Changed LookupUtil to use stub indexes instead of iterating over global labels and symbol definitions. Added basic test for Structure View.

This commit is contained in:
Chris Hodges 2021-07-26 10:50:22 +02:00
parent 89ecaeb613
commit 3165d99fc2
6 changed files with 141 additions and 30 deletions

View File

@ -36,7 +36,6 @@ it's "good enough" to get started, and I can return to demo coding with its curr
## Known issues
- Performance is not optimal yet (no stub index).
- No referencing of macro invocations and macro definitions.
- `Find Usages` always shows _"Unclassified"_ though it shouldn't.
- Macro definitions may cause syntax errors when using backslash arguments.
@ -55,9 +54,14 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- Formatter + Code Style Settings
- Register use analysis (but this only makes sense after macro evaluation)
## Recommendations
Currently, I would suggest using the fabulous [Browse Word at Caret Plugin](https://plugins.jetbrains.com/plugin/201-browsewordatcaret)
to highlight the same address and data registers while editing (see new `View -> Highlight Word at Caret` menu item).
## Development notice
This plugin has been written in Kotlin 1.5.
This plugin has been written in Kotlin 1.5 using Grammar-Kit.
It is probably the only plugin (besides [Cajon](https://github.com/chrisly42/cajon-plugin) from the same author) that uses JUnit 5 Jupiter for unit testing so
far (or at least the only one I'm aware of ;) ). The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing, and it took me quite a while to
@ -65,6 +69,12 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
## Changelog
### V0.2 (unreleased)
- Added (same) icon for plugin as for file type.
- Performance improvement: Use Word-Index for global labels and symbols instead of iterating over the file.
- Performance improvement: Use Stub-Index for global labels and symbols.
### V0.1 (20-Jul-21)
- Initial public release.

View File

@ -46,6 +46,12 @@ intellij {
patchPluginXml {
setChangeNotes("""
<h4>V0.2 (xx-Jul-21)</h4>
<ul>
<li>Added (same) icon for plugin as for file type.
<li>Performance improvement: Use Word-Index for global labels and symbols instead of iterating over the file.
<li>Performance improvement: Use Stub-Index for global labels and symbols.
</ul>
<h4>V0.1 (20-Jul-21)</h4>
<ul>
<li>Initial public release.

View File

@ -1,28 +1,41 @@
package de.platon42.intellij.plugins.m68k.psi
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.M68kFileType
import de.platon42.intellij.plugins.m68k.stubs.M68kGlobalLabelStubIndex
import de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex
object M68kLookupUtil {
fun findAllGlobalLabels(project: Project): List<M68kGlobalLabel> {
return getFiles(project)
.flatMap(::findAllGlobalLabels)
.toList()
val results: MutableList<M68kGlobalLabel> = ArrayList()
StubIndex.getInstance().processAllKeys(M68kGlobalLabelStubIndex.KEY, project)
{
results.addAll(StubIndex.getElements(M68kGlobalLabelStubIndex.KEY, it, project, GlobalSearchScope.allScope(project), M68kGlobalLabel::class.java))
true
}
return results
}
fun findAllGlobalLabels(file: M68kFile): List<M68kGlobalLabel> {
val results: MutableList<M68kGlobalLabel> = ArrayList()
var currentStatement = file.firstChild
while (currentStatement != null) {
val child = currentStatement.firstChild
if (child is M68kGlobalLabel) results.add(child)
currentStatement = PsiTreeUtil.getNextSiblingOfType(currentStatement, M68kStatement::class.java)
}
StubIndex.getInstance().processAllKeys(
M68kGlobalLabelStubIndex.KEY,
{
results.addAll(
StubIndex.getElements(
M68kGlobalLabelStubIndex.KEY,
it,
file.project,
GlobalSearchScope.fileScope(file),
M68kGlobalLabel::class.java
)
)
true
}, GlobalSearchScope.fileScope(file), null
)
return results
}
@ -40,23 +53,40 @@ object M68kLookupUtil {
}
fun findAllSymbolDefinitions(project: Project): List<M68kSymbolDefinition> {
return getFiles(project)
.flatMap(::findAllSymbolDefinitions)
.toList()
}
fun findAllSymbolDefinitions(file: M68kFile): List<M68kSymbolDefinition> {
val results: MutableList<M68kSymbolDefinition> = ArrayList()
var currentStatement = file.firstChild
while (currentStatement != null) {
val child = currentStatement.firstChild
if (child is M68kAssignment) results.add(child.firstChild as M68kSymbolDefinition)
currentStatement = PsiTreeUtil.getNextSiblingOfType(currentStatement, M68kStatement::class.java)
StubIndex.getInstance().processAllKeys(M68kSymbolDefinitionStubIndex.KEY, project)
{
results.addAll(
StubIndex.getElements(
M68kSymbolDefinitionStubIndex.KEY,
it,
project,
GlobalSearchScope.allScope(project),
M68kSymbolDefinition::class.java
)
)
true
}
return results
}
private fun getFiles(project: Project) =
FileTypeIndex.getFiles(M68kFileType.INSTANCE, GlobalSearchScope.allScope(project)).asSequence()
.map { PsiManager.getInstance(project).findFile(it) as M68kFile }
fun findAllSymbolDefinitions(file: M68kFile): List<M68kSymbolDefinition> {
val results: MutableList<M68kSymbolDefinition> = ArrayList()
StubIndex.getInstance().processAllKeys(
M68kSymbolDefinitionStubIndex.KEY,
{
results.addAll(
StubIndex.getElements(
M68kSymbolDefinitionStubIndex.KEY,
it,
file.project,
GlobalSearchScope.fileScope(file),
M68kSymbolDefinition::class.java
)
)
true
}, GlobalSearchScope.fileScope(file), null
)
return results
}
}

View File

@ -4,6 +4,7 @@ import com.intellij.ide.structureView.StructureViewTreeElement
import com.intellij.ide.util.treeView.smartTree.TreeElement
import com.intellij.navigation.ItemPresentation
import com.intellij.psi.NavigatablePsiElement
import com.intellij.refactoring.suggested.startOffset
import de.platon42.intellij.plugins.m68k.psi.M68kFile
import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kLookupUtil
@ -18,8 +19,10 @@ class M68kStructureViewElement(private val myElement: NavigatablePsiElement) : S
return when (myElement) {
is M68kFile -> {
listOf(
M68kLookupUtil.findAllSymbolDefinitions(myElement),
M68kLookupUtil.findAllGlobalLabels(myElement)
M68kLookupUtil.findAllSymbolDefinitions(myElement).sortedBy { it.startOffset },
M68kLookupUtil.findAllGlobalLabels(myElement).sortedBy { it.startOffset },
// M68kLookupUtil.findAllSymbolDefinitions(myElement).sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name!! }),
// M68kLookupUtil.findAllGlobalLabels(myElement).sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name!! })
)
.flatten()
.map(::M68kStructureViewElement)

View File

@ -0,0 +1,37 @@
package de.platon42.intellij.plugins.m68k.structureview
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.plugins.m68k.AbstractM68kTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@TestDataPath("src/test/resources/structureview")
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kStructureViewTest : AbstractM68kTest() {
@Test
internal fun do_basic_verification_of_resulting_structure_view(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByFile("basic_example.asm")
myFixture.testStructureView {
val tree = it.tree
PlatformTestUtil.waitWhileBusy(tree)
PlatformTestUtil.assertTreeEqual(
tree, """-basic_example.asm
PIC_WIDTH
PIC_HEIGHT
DEBUG_LEVEL
entry
-init
.looph
.loopw
main
exit
"""
)
}
}
}

View File

@ -0,0 +1,25 @@
PIC_WIDTH = 320
PIC_HEIGHT equ 256
DEBUG_LEVEL set 10
entry:
bsr init
bsr main
bsr exit
rts
init
move.w #PIC_HEIGHT,d1
.looph move.w #PIC_WIDTH,d0
.loopw clr.b (a0)+
subq.w #1,d0
bne.s .loopw
subq.w #1,d1
bne.s .looph
rts
main moveq.l #0,d0
rts
exit illegal
rts