Skip to content

Commit

Permalink
Merge pull request #35 from marcelkliemannel/develop
Browse files Browse the repository at this point in the history
4.1.0
  • Loading branch information
marcelkliemannel authored Dec 25, 2023
2 parents ba60f5a + c00e7bf commit d6b83e6
Show file tree
Hide file tree
Showing 15 changed files with 854 additions and 470 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@

### Fixed

## 4.1.0 - 2023-12-25

### Changed

- For lambda classes, a fallback to the parent class is now made if the lambda class file is not found. This is necessary because, for example, the Kotlin compiler often optimises lambda code and does not create an extra class.
- The Open Class Files mechanism has been optimised for performance by using read access to the PSI data model (IntelliJ's source code model) only when it is actually needed.

## 4.0.0 - 2023-12-17

### Added
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pluginGroup=dev.turingcomplete
pluginVersion=4.0.0
pluginVersion=4.1.0
pluginSinceBuild=233
pluginUntilBuild=
# LATEST-EAP-SNAPSHOT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dev.turingcomplete.intellijbytecodeplugin.common.ClassFile
import dev.turingcomplete.intellijbytecodeplugin.common.ClassFileContext
import dev.turingcomplete.intellijbytecodeplugin.common.SourceFile.CompilableSourceFile
import dev.turingcomplete.intellijbytecodeplugin.common._internal.AsyncUtils
import dev.turingcomplete.intellijbytecodeplugin.openclassfiles._internal.ClassFileCandidates
import dev.turingcomplete.intellijbytecodeplugin.openclassfiles._internal.ClassFilesPreparatorService
import dev.turingcomplete.intellijbytecodeplugin.openclassfiles._internal.ClassFilesPreparatorService.ClassFilePreparationTask
import dev.turingcomplete.intellijbytecodeplugin.view.ByteCodeView
Expand Down Expand Up @@ -52,7 +53,8 @@ internal class ClassFileTab(
override fun reParseClassNodeContext() {
val sourceFile = classFile.sourceFile
if (sourceFile is CompilableSourceFile) {
val classFilePreparationTask = ClassFilePreparationTask(classFile.file.toNioPath(), sourceFile)
val classFileCandidates = ClassFileCandidates.fromAbsolutePaths(classFile.file.toNioPath())
val classFilePreparationTask = ClassFilePreparationTask(classFileCandidates, sourceFile)
project.getService(ClassFilesPreparatorService::class.java)
.prepareClassFiles(listOf(classFilePreparationTask), centerComponentContainer) {
classFile = it
Expand Down Expand Up @@ -135,19 +137,16 @@ internal class ClassFileTab(
private class ByteCodeViewTabs(classFileContext: ClassFileContext, parentDisposable: Disposable)
: TabbedPaneWrapper(parentDisposable) {

private val byteCodeViews: List<ByteCodeView>
var selectedByteCodeViewIndex: Int
private val byteCodeViews: List<ByteCodeView> = ByteCodeView.EP.extensions.mapIndexed { index, byteCodeViewCreator ->
val selected = index == 0
val classFileView = byteCodeViewCreator.create(classFileContext)
addTab(classFileView.title, null, classFileView.createComponent(selected), null)
Disposer.register(parentDisposable, classFileView)
classFileView
}
var selectedByteCodeViewIndex: Int = 0

init {
byteCodeViews = ByteCodeView.EP.extensions.mapIndexed { index, byteCodeViewCreator ->
val selected = index == 0
val classFileView = byteCodeViewCreator.create(classFileContext)
addTab(classFileView.title, null, classFileView.createComponent(selected), null)
Disposer.register(parentDisposable, classFileView)
classFileView
}
selectedByteCodeViewIndex = 0

addChangeListener {
if (selectedByteCodeViewIndex != selectedIndex) {
selectedByteCodeViewIndex = selectedIndex
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.turingcomplete.intellijbytecodeplugin.common

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
Expand All @@ -20,33 +21,28 @@ class ByteCodeAnalyserOpenClassFileService(val project: Project) {
// -- Exposed Methods --------------------------------------------------------------------------------------------- //

fun openPsiFiles(psiFiles: List<PsiFile>) {
project.getService(ClassFilesFinderService::class.java)
.findByPsiFiles(psiFiles)
.let { handleResult(it) }
run { service -> service.findByPsiFiles(psiFiles) }
}

fun openPsiElements(psiElements: Map<PsiElement, PsiFile?>) {
psiElements.map { (psiElement, psiElementOriginPsiFile) ->
project.getService(ClassFilesFinderService::class.java)
.findByPsiElement(psiElement, psiElementOriginPsiFile)
.let { handleResult(it) }
}
fun openPsiElements(psiElementToPsiElementOriginPsiFile: Map<PsiElement, PsiFile?>) {
run { service -> service.findByPsiElements(psiElementToPsiElementOriginPsiFile) }
}

fun openVirtualFiles(files: List<VirtualFile>) {
project.getService(ClassFilesFinderService::class.java)
.findByVirtualFiles(files)
.let { handleResult(it) }
run { service -> service.findByVirtualFiles(files) }
}

internal fun openClassFiles(classFiles: List<ClassFile>) {
project.getService(ClassFilesFinderService::class.java)
.findByClassFiles(classFiles)
.let { handleResult(it) }
run { service -> service.findByClassFiles(classFiles) }
}

// -- Private Methods --------------------------------------------------------------------------------------------- //

private fun run(findBy: (ClassFilesFinderService) -> Result) {
val result = findBy(project.getService(ClassFilesFinderService::class.java))
handleResult(result)
}

private fun handleResult(result: Result) {
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ByteCodeToolWindowFactory.TOOL_WINDOW_ID)
?: throw IllegalStateException("Could not find tool window '${ByteCodeToolWindowFactory.TOOL_WINDOW_ID}'")
Expand All @@ -69,7 +65,9 @@ class ByteCodeAnalyserOpenClassFileService(val project: Project) {
else {
"The following errors occurred: ${result.errors.joinToString(prefix = "<ul>", postfix = "</ul>") { "<li>$it</li>" }}"
}
Messages.showErrorDialog(project, errorMessage, "Analyse Class Files")
ApplicationManager.getApplication().invokeLater {
Messages.showErrorDialog(project, errorMessage, "Analyse Class Files")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package dev.turingcomplete.intellijbytecodeplugin.openclassfiles._internal

import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import dev.turingcomplete.intellijbytecodeplugin.common._internal.joinAsNaturalLanguage
import org.jsoup.internal.StringUtil.StringJoiner
import java.nio.file.Path
import kotlin.io.path.relativeToOrNull

sealed class ClassFileCandidates private constructor(val primaryPath: Path, private val fallbackPaths: List<Path> = emptyList()) {
// -- Properties -------------------------------------------------------------------------------------------------- //
// -- Initialization ---------------------------------------------------------------------------------------------- //
// -- Exported Methods -------------------------------------------------------------------------------------------- //

fun allPaths() = listOf(primaryPath) + fallbackPaths

fun formatNotFoundError(postfix: String = "cannot be found.", project: Project? = null): String =
StringJoiner(" ").apply {
add("Class file")
add("'${formatPath(primaryPath, project)}'")
if (fallbackPaths.isNotEmpty()) {
add("or possible fallback class file${if (fallbackPaths.size >= 2) "s" else ""}")
add(fallbackPaths.joinAsNaturalLanguage { "'${formatPath(it, project)}'" })
}
add(postfix)
}.complete()

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ClassFileCandidates) return false

if (primaryPath != other.primaryPath) return false
if (fallbackPaths != other.fallbackPaths) return false

return true
}

override fun hashCode(): Int {
var result = primaryPath.hashCode()
result = 31 * result + fallbackPaths.hashCode()
return result
}

override fun toString(): String {
return "ClassFileCandidates(primaryPath=$primaryPath, fallbackPaths=$fallbackPaths)"
}

// -- Private Methods --------------------------------------------------------------------------------------------- //

private fun formatPath(path: Path, project: Project?): String {
val projectDir = project?.guessProjectDir()?.toNioPath()
return if (projectDir != null) {
path.relativeToOrNull(projectDir)?.toString() ?: path.toString()
}
else {
path.toString()
}
}

// -- Inner Type -------------------------------------------------------------------------------------------------- //

class RelativeClassFileCandidates(primaryPath: Path, fallbackPaths: List<Path>)
: ClassFileCandidates(primaryPath, fallbackPaths)

// -- Inner Type -------------------------------------------------------------------------------------------------- //

class AbsoluteClassFileCandidates(primaryPath: Path, fallbackPaths: List<Path>)
: ClassFileCandidates(primaryPath, fallbackPaths)

// -- Companion Object -------------------------------------------------------------------------------------------- //

companion object {

fun fromRelativePaths(vararg paths: Path): RelativeClassFileCandidates {
assert(paths.all { !it.isAbsolute })
assert(paths.isNotEmpty())

val fallbackPaths = if (paths.size > 1) paths.sliceArray(1 until paths.size) else emptyArray()
return RelativeClassFileCandidates(primaryPath = paths.first(), fallbackPaths = fallbackPaths.toList())
}

fun fromAbsolutePaths(vararg paths: Path): AbsoluteClassFileCandidates {
assert(paths.all { it.isAbsolute })
assert(paths.isNotEmpty())

val fallbackPaths = if (paths.size > 1) paths.sliceArray(1 until paths.size) else emptyArray()
return AbsoluteClassFileCandidates(primaryPath = paths.first(), fallbackPaths = fallbackPaths.toList())
}
}
}
Loading

0 comments on commit d6b83e6

Please sign in to comment.