diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kGlobalLabelMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kGlobalLabelMixin.kt index b87e282..95fa06b 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kGlobalLabelMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kGlobalLabelMixin.kt @@ -1,8 +1,19 @@ package de.platon42.intellij.plugins.m68k.psi import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.ide.projectView.PresentationData import com.intellij.lang.ASTNode +import com.intellij.navigation.ItemPresentation +import de.platon42.intellij.plugins.m68k.M68kIcons +import javax.swing.Icon abstract class M68kGlobalLabelMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kGlobalLabel { + override fun getPresentation(): ItemPresentation? { + return PresentationData(name, containingFile?.name, getIcon(0), null) + } + + override fun getIcon(flags: Int): Icon? { + return M68kIcons.GLOBAL_LABEL + } } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLocalLabelMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLocalLabelMixin.kt index ee04dfd..82f02a0 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLocalLabelMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLocalLabelMixin.kt @@ -1,12 +1,18 @@ package de.platon42.intellij.plugins.m68k.psi import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.ide.projectView.PresentationData import com.intellij.lang.ASTNode +import com.intellij.navigation.ItemPresentation import de.platon42.intellij.plugins.m68k.M68kIcons import javax.swing.Icon abstract class M68kLocalLabelMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLocalLabel { + override fun getPresentation(): ItemPresentation? { + return PresentationData(name, containingFile?.name, getIcon(0), null) + } + override fun getIcon(flags: Int): Icon? { return M68kIcons.LOCAL_LABEL } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLookupUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLookupUtil.kt new file mode 100644 index 0000000..a6b5de8 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kLookupUtil.kt @@ -0,0 +1,43 @@ +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.util.PsiTreeUtil +import de.platon42.intellij.plugins.m68k.M68kFileType + +object M68kLookupUtil { + + fun findAllGlobalLabels(project: Project): List { + return FileTypeIndex.getFiles(M68kFileType.INSTANCE, GlobalSearchScope.allScope(project)).asSequence() + .map { PsiManager.getInstance(project).findFile(it) as M68kFile } + .flatMap { + val results: MutableList = ArrayList() + var currentStatement = it.firstChild + while (currentStatement != null) { + val child = currentStatement.firstChild + if (child is M68kGlobalLabel) results.add(child) + currentStatement = PsiTreeUtil.getNextSiblingOfType(currentStatement, M68kStatement::class.java) + } + results + } + .toList() + } + + fun findAllSymbolDefinitions(project: Project): List { + return FileTypeIndex.getFiles(M68kFileType.INSTANCE, GlobalSearchScope.allScope(project)).asSequence() + .map { PsiManager.getInstance(project).findFile(it) as M68kFile } + .flatMap { + val results: MutableList = ArrayList() + var currentStatement = it.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) + } + results + } + .toList() + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolDefinitionMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolDefinitionMixin.kt index 2da37d8..5fdcb94 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolDefinitionMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolDefinitionMixin.kt @@ -1,8 +1,19 @@ package de.platon42.intellij.plugins.m68k.psi import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.ide.projectView.PresentationData import com.intellij.lang.ASTNode +import com.intellij.navigation.ItemPresentation +import de.platon42.intellij.plugins.m68k.M68kIcons +import javax.swing.Icon abstract class M68kSymbolDefinitionMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolDefinition { + override fun getPresentation(): ItemPresentation? { + return PresentationData(name, containingFile?.name, getIcon(0), null) + } + + override fun getIcon(flags: Int): Icon? { + return M68kIcons.SYMBOL_DEF + } } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kGlobalLabelSymbolReference.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kGlobalLabelSymbolReference.kt new file mode 100644 index 0000000..1307289 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kGlobalLabelSymbolReference.kt @@ -0,0 +1,66 @@ +package de.platon42.intellij.plugins.m68k.refs + +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementResolveResult +import com.intellij.psi.PsiPolyVariantReferenceBase +import com.intellij.psi.ResolveResult +import com.intellij.psi.impl.source.resolve.ResolveCache +import com.intellij.psi.search.GlobalSearchScope +import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel +import de.platon42.intellij.plugins.m68k.psi.M68kLookupUtil +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference + +class M68kGlobalLabelSymbolReference(element: M68kSymbolReference) : + PsiPolyVariantReferenceBase(element, TextRange(0, element.textLength)) { + + companion object { + val INSTANCE = Resolver() + + fun findGlobalLabels(element: M68kSymbolReference, predicate: (M68kGlobalLabel) -> Boolean): List { + return M68kLookupUtil.findAllGlobalLabels(element.project).filter(predicate) + } + + fun findSymbolDefinitions(element: M68kSymbolReference, predicate: (M68kSymbolDefinition) -> Boolean): List { + return M68kLookupUtil.findAllSymbolDefinitions(element.project).filter(predicate) + } + + private fun getCurrentFileSearchScope(element: PsiElement): GlobalSearchScope { + return GlobalSearchScope.fileScope(element.containingFile.originalFile) + } + } + + class Resolver : ResolveCache.PolyVariantResolver { + override fun resolve(ref: M68kGlobalLabelSymbolReference, incompleteCode: Boolean): Array { + val refName = ref.element.symbolName + + val globalLabelMatches: Array = findGlobalLabels(ref.myElement) { it.name == refName } + .map { PsiElementResolveResult(it) } + .toTypedArray() + if (globalLabelMatches.isNotEmpty()) return globalLabelMatches + return findSymbolDefinitions(ref.myElement) { it.name == refName } + .map { PsiElementResolveResult(it) } + .toTypedArray() + } + } + + override fun multiResolve(incompleteCode: Boolean): Array { + return ResolveCache.getInstance(element.project) + .resolveWithCaching(this, INSTANCE, false, incompleteCode) + } + + override fun resolve(): PsiElement? { + val resolveResults = multiResolve(false) + return resolveResults.singleOrNull()?.element + } + + override fun getVariants(): Array { + return listOf(findGlobalLabels(element) { true }, findSymbolDefinitions(element) { true }).asSequence() + .flatten() + .map { LookupElementBuilder.createWithIcon(it) } + .toList() + .toTypedArray() + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributor.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributor.kt index abf58ba..81c92a6 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributor.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributor.kt @@ -9,10 +9,12 @@ class M68kReferenceContributor : PsiReferenceContributor() { companion object { val localLabelReferenceProvider = LocalLabelReferenceProvider() + val globalLabelReferenceProvider = GlobalLabelSymbolReferenceProvider() } override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider) + registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), globalLabelReferenceProvider) } class LocalLabelReferenceProvider : PsiReferenceProvider() { @@ -22,4 +24,12 @@ class M68kReferenceContributor : PsiReferenceContributor() { return arrayOf(M68kLocalLabelReference(symbolReference)) } } + + class GlobalLabelSymbolReferenceProvider : PsiReferenceProvider() { + override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { + val symbolReference = element as M68kSymbolReference + if (symbolReference.isLocalLabelRef) return emptyArray() + return arrayOf(M68kGlobalLabelSymbolReference(symbolReference)) + } + } } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kSymbolDefinitionReference.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kSymbolDefinitionReference.kt new file mode 100644 index 0000000..3398602 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kSymbolDefinitionReference.kt @@ -0,0 +1,55 @@ +package de.platon42.intellij.plugins.m68k.refs + +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementResolveResult +import com.intellij.psi.PsiPolyVariantReferenceBase +import com.intellij.psi.ResolveResult +import com.intellij.psi.impl.source.resolve.ResolveCache +import com.intellij.psi.search.GlobalSearchScope +import de.platon42.intellij.plugins.m68k.psi.M68kLookupUtil +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference + +class M68kSymbolDefinitionReference(element: M68kSymbolReference) : + PsiPolyVariantReferenceBase(element, TextRange(0, element.textLength)) { + + companion object { + val INSTANCE = Resolver() + + fun findSymbolDefinitions(element: M68kSymbolReference, predicate: (M68kSymbolDefinition) -> Boolean): List { + return M68kLookupUtil.findAllSymbolDefinitions(element.project).filter(predicate) + } + + private fun getCurrentFileSearchScope(element: PsiElement): GlobalSearchScope { + return GlobalSearchScope.fileScope(element.containingFile.originalFile) + } + } + + class Resolver : ResolveCache.PolyVariantResolver { + override fun resolve(ref: M68kSymbolDefinitionReference, incompleteCode: Boolean): Array { + val refName = ref.element.symbolName + + return findSymbolDefinitions(ref.myElement) { it.name == refName } + .map { PsiElementResolveResult(it) } + .toTypedArray() + } + } + + override fun multiResolve(incompleteCode: Boolean): Array { + return ResolveCache.getInstance(element.project) + .resolveWithCaching(this, INSTANCE, false, incompleteCode) + } + + override fun resolve(): PsiElement? { + val resolveResults = multiResolve(false) + return resolveResults.singleOrNull()?.element + } + + override fun getVariants(): Array { + return findSymbolDefinitions(element) { true } + .map { LookupElementBuilder.createWithIcon(it) } + .toTypedArray() + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/scanner/M68kFindUsagesProvider.kt b/src/main/java/de/platon42/intellij/plugins/m68k/scanner/M68kFindUsagesProvider.kt index 0ae7c6a..542d178 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/scanner/M68kFindUsagesProvider.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/scanner/M68kFindUsagesProvider.kt @@ -8,10 +8,7 @@ import com.intellij.psi.PsiNamedElement import com.intellij.psi.tree.TokenSet import de.platon42.intellij.plugins.m68k.lexer.M68kLexer import de.platon42.intellij.plugins.m68k.lexer.M68kLexerPrefs -import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel -import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel -import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition -import de.platon42.intellij.plugins.m68k.psi.M68kTypes +import de.platon42.intellij.plugins.m68k.psi.* import org.jetbrains.annotations.Nls import org.jetbrains.annotations.NonNls @@ -40,6 +37,7 @@ class M68kFindUsagesProvider : FindUsagesProvider { is M68kGlobalLabel -> "global label" is M68kLocalLabel -> "local label" is M68kSymbolDefinition -> "symbol definition" + is M68kSymbolReference -> "symbol reference" else -> "" } } @@ -49,6 +47,7 @@ class M68kFindUsagesProvider : FindUsagesProvider { is M68kGlobalLabel -> element.name!! is M68kLocalLabel -> element.name!! is M68kSymbolDefinition -> element.parent.text + is M68kSymbolReference -> element.symbolName else -> "" } } diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributorTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributorTest.kt index 7b5cac7..ac1b9a8 100644 --- a/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributorTest.kt +++ b/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributorTest.kt @@ -8,7 +8,9 @@ import de.platon42.intellij.jupiter.MyFixture import de.platon42.intellij.jupiter.TestDataPath import de.platon42.intellij.jupiter.TestDataSubPath import de.platon42.intellij.plugins.m68k.AbstractM68kTest +import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -36,4 +38,38 @@ internal class M68kReferenceContributorTest : AbstractM68kTest() { .extracting { (it as LookupElementBuilder).lookupString } .containsExactlyInAnyOrderElementsOf(listOf("loop$", "loop$", ".skip")) } + + @Test + internal fun reference_to_global_label_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) { + val file = myFixture.configureByFile("global_labels.asm") + assertThat(myFixture.elementAtCaret).isInstanceOf(M68kGlobalLabel::class.java) + .extracting(PsiElement::getText).isEqualTo("main") + + val reference = file.findReferenceAt(myFixture.editor.caretModel.offset - 1)!! + assertThat(reference).isInstanceOf(M68kGlobalLabelSymbolReference::class.java) + assertThat(reference.variants).hasOnlyElementsOfType(LookupElementBuilder::class.java) + .extracting { (it as LookupElementBuilder).lookupString } + .containsExactlyInAnyOrder("main", "init", "exit") + + myFixture.renameElementAtCaret("intro_main") + + myFixture.checkResultByFile("global_labels_after_rename.asm") + } + + @Test + internal fun reference_to_symbol_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) { + val file = myFixture.configureByFile("symbol_assignment.asm") + assertThat(myFixture.elementAtCaret).isInstanceOf(M68kSymbolDefinition::class.java) + .extracting(PsiElement::getText).isEqualTo("PIC_HEIGTH") + + myFixture.renameElementAtCaret("PIC_HEIGHT") + + val reference = file.findReferenceAt(myFixture.editor.caretModel.offset)!! + assertThat(reference).isInstanceOf(M68kGlobalLabelSymbolReference::class.java) + assertThat(reference.variants).hasOnlyElementsOfType(LookupElementBuilder::class.java) + .extracting { (it as LookupElementBuilder).lookupString } + .containsExactlyInAnyOrder("main", "init", "exit", "PIC_WIDTH", "PIC_HEIGHT") + + myFixture.checkResultByFile("symbol_assignment_after_rename.asm") + } } \ No newline at end of file diff --git a/src/test/resources/references/labels/global_labels.asm b/src/test/resources/references/labels/global_labels.asm new file mode 100644 index 0000000..c61a926 --- /dev/null +++ b/src/test/resources/references/labels/global_labels.asm @@ -0,0 +1,13 @@ + bsr init + bsr main + bsr exit + rts + +init moveq.l #-1,d0 + rts + +main moveq.l #0,d0 + rts + +exit illegal + rts \ No newline at end of file diff --git a/src/test/resources/references/labels/global_labels_after_rename.asm b/src/test/resources/references/labels/global_labels_after_rename.asm new file mode 100644 index 0000000..566d229 --- /dev/null +++ b/src/test/resources/references/labels/global_labels_after_rename.asm @@ -0,0 +1,13 @@ + bsr init + bsr intro_main + bsr exit + rts + +init moveq.l #-1,d0 + rts + +intro_main moveq.l #0,d0 + rts + +exit illegal + rts \ No newline at end of file diff --git a/src/test/resources/references/labels/symbol_assignment.asm b/src/test/resources/references/labels/symbol_assignment.asm new file mode 100644 index 0000000..e299528 --- /dev/null +++ b/src/test/resources/references/labels/symbol_assignment.asm @@ -0,0 +1,17 @@ +PIC_WIDTH = 320 +PIC_HEIGTH equ 256 + + bsr init + bsr main + bsr exit + rts + +init move.w #PIC_WIDTH,d0 + move.w #PIC_HEIGTH,d1 + rts + +main moveq.l #0,d0 + rts + +exit illegal + rts \ No newline at end of file diff --git a/src/test/resources/references/labels/symbol_assignment_after_rename.asm b/src/test/resources/references/labels/symbol_assignment_after_rename.asm new file mode 100644 index 0000000..a2e1c97 --- /dev/null +++ b/src/test/resources/references/labels/symbol_assignment_after_rename.asm @@ -0,0 +1,17 @@ +PIC_WIDTH = 320 +PIC_HEIGHT equ 256 + + bsr init + bsr main + bsr exit + rts + +init move.w #PIC_WIDTH,d0 + move.w #PIC_HEIGHT,d1 + rts + +main moveq.l #0,d0 + rts + +exit illegal + rts \ No newline at end of file