diff --git a/src/main/gen/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReference.java b/src/main/gen/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReference.java index 165c520..c1d3407 100644 --- a/src/main/gen/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReference.java +++ b/src/main/gen/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReference.java @@ -2,7 +2,13 @@ package de.platon42.intellij.plugins.m68k.psi; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; public interface M68kSymbolReference extends PsiElement { + @NotNull + String getSymbolName(); + + boolean isLocalLabelRef(); + } diff --git a/src/main/gen/de/platon42/intellij/plugins/m68k/psi/impl/M68kSymbolReferenceImpl.java b/src/main/gen/de/platon42/intellij/plugins/m68k/psi/impl/M68kSymbolReferenceImpl.java index 51ff280..a8a09ef 100644 --- a/src/main/gen/de/platon42/intellij/plugins/m68k/psi/impl/M68kSymbolReferenceImpl.java +++ b/src/main/gen/de/platon42/intellij/plugins/m68k/psi/impl/M68kSymbolReferenceImpl.java @@ -1,14 +1,15 @@ // This is a generated file. Not intended for manual editing. package de.platon42.intellij.plugins.m68k.psi.impl; -import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElementVisitor; +import de.platon42.intellij.plugins.m68k.psi.M68kPsiImplUtil; import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference; +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReferenceMixin; import de.platon42.intellij.plugins.m68k.psi.M68kVisitor; import org.jetbrains.annotations.NotNull; -public class M68kSymbolReferenceImpl extends ASTWrapperPsiElement implements M68kSymbolReference { +public class M68kSymbolReferenceImpl extends M68kSymbolReferenceMixin implements M68kSymbolReference { public M68kSymbolReferenceImpl(@NotNull ASTNode node) { super(node); @@ -24,4 +25,15 @@ public class M68kSymbolReferenceImpl extends ASTWrapperPsiElement implements M68 else super.accept(visitor); } + @Override + @NotNull + public String getSymbolName() { + return M68kPsiImplUtil.getSymbolName(this); + } + + @Override + public boolean isLocalLabelRef() { + return M68kPsiImplUtil.isLocalLabelRef(this); + } + } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/M68kIcons.kt b/src/main/java/de/platon42/intellij/plugins/m68k/M68kIcons.kt index 5e2e35c..f4c5ed9 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/M68kIcons.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/M68kIcons.kt @@ -1,7 +1,11 @@ package de.platon42.intellij.plugins.m68k +import com.intellij.icons.AllIcons import com.intellij.openapi.util.IconLoader object M68kIcons { val FILE = IconLoader.getIcon("/icons/FileType_m68k.svg", javaClass) + val LOCAL_LABEL = AllIcons.Nodes.AbstractMethod + val GLOBAL_LABEL = AllIcons.Nodes.Method + val SYMBOL_DEF = AllIcons.Nodes.Constant } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf b/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf index 3404a34..8e48ce7 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf +++ b/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf @@ -153,6 +153,8 @@ GlobalLabel ::= GLOBAL_LABEL_DEF COLON* { extends = Label implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement" mixin = "de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabelMixin" +// elementTypeFactory = "de.platon42.intellij.plugins.m68k.stubs.M68kStubElementTypeFactory.stubFactory" +// stubClass = "de.platon42.intellij.plugins.m68k.stubs.M68kGlobalLabelStub" methods = [getName setName getNameIdentifier] } @@ -179,7 +181,13 @@ private PreprocessorOperand ::= expr private PlainOperands ::= STRINGLIT (SEPARATOR STRINGLIT)* -SymbolReference ::= SYMBOL // TODO This should probably be a ILazyParseableElementType, no idea how to implement that yet +// TODO This should probably be a ILazyParseableElementType, no idea how to implement that yet +SymbolReference ::= SYMBOL { + mixin = "de.platon42.intellij.plugins.m68k.psi.M68kSymbolReferenceMixin" + methods = [getSymbolName isLocalLabelRef] +// implements = "com.intellij.model.psi.PsiExternalReferenceHost" +} + ProgramCounterReference ::= CURRENT_PC_SYMBOL DataRegister ::= DREG { 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 88e6386..ee04dfd 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 @@ -2,7 +2,12 @@ package de.platon42.intellij.plugins.m68k.psi import com.intellij.extapi.psi.ASTWrapperPsiElement import com.intellij.lang.ASTNode +import de.platon42.intellij.plugins.m68k.M68kIcons +import javax.swing.Icon abstract class M68kLocalLabelMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLocalLabel { + override fun getIcon(flags: Int): Icon? { + return M68kIcons.LOCAL_LABEL + } } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kNamedElement.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kNamedElement.kt index beeb317..00e54c5 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kNamedElement.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kNamedElement.kt @@ -1,6 +1,6 @@ -package de.platon42.intellij.plugins.m68k.psi; +package de.platon42.intellij.plugins.m68k.psi -import com.intellij.psi.PsiNameIdentifierOwner; +import com.intellij.psi.NavigatablePsiElement +import com.intellij.psi.PsiNameIdentifierOwner -public interface M68kNamedElement extends PsiNameIdentifierOwner { -} +interface M68kNamedElement : PsiNameIdentifierOwner, NavigatablePsiElement \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt index d70a148..49bef8a 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt @@ -45,7 +45,6 @@ object M68kPsiImplUtil { // Symbol Definition - @JvmStatic fun getName(element: M68kSymbolDefinition): String? = element.firstChild.text @@ -62,4 +61,14 @@ object M68kPsiImplUtil { @JvmStatic fun getNameIdentifier(element: M68kSymbolDefinition): PsiElement = element.firstChild + + // Symbol Reference + @JvmStatic + fun getSymbolName(element: M68kSymbolReference): String = element.firstChild.text + + @JvmStatic + fun isLocalLabelRef(element: M68kSymbolReference): Boolean { + val text = element.firstChild.text + return text.startsWith('.') || text.endsWith('$') + } } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt new file mode 100644 index 0000000..c58c4fd --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt @@ -0,0 +1,13 @@ +package de.platon42.intellij.plugins.m68k.psi + +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiReference +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry + +abstract class M68kSymbolReferenceMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolReference { + + override fun getReferences(): Array { + return ReferenceProvidersRegistry.getReferencesFromProviders(this) + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt new file mode 100644 index 0000000..473dc24 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt @@ -0,0 +1,58 @@ +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.util.PsiTreeUtil +import com.intellij.util.SmartList +import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel +import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel +import de.platon42.intellij.plugins.m68k.psi.M68kStatement +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference + +class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantReferenceBase(element, TextRange(0, element.textLength)) { + + override fun multiResolve(incompleteCode: Boolean): Array { + val refName = myElement.symbolName + + return findLocalLabels { it.name == refName } + .map { PsiElementResolveResult(it) } + .toTypedArray() + } + + override fun resolve(): PsiElement? { + val resolveResults = multiResolve(false) + return resolveResults.singleOrNull()?.element + } + + override fun getVariants(): Array { + return findLocalLabels { true } + .map { LookupElementBuilder.createWithIcon(it) } + .toTypedArray() + } + + private fun findLocalLabels(predicate: (M68kLocalLabel) -> Boolean): List { + val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!! + val results: MutableList = SmartList() + // go backward + var currentStatement = PsiTreeUtil.getPrevSiblingOfType(statement, M68kStatement::class.java) + while (currentStatement != null) { + val child = currentStatement.firstChild + if (child is M68kGlobalLabel) break + if (child is M68kLocalLabel && predicate.invoke(child)) results.add(child) + currentStatement = PsiTreeUtil.getPrevSiblingOfType(currentStatement, M68kStatement::class.java) + } + // go forward + currentStatement = statement + while (currentStatement != null) { + val child = currentStatement.firstChild + if (child is M68kGlobalLabel) break + if (child is M68kLocalLabel && predicate.invoke(child)) results.add(child) + currentStatement = PsiTreeUtil.getNextSiblingOfType(currentStatement, M68kStatement::class.java) + } + return results + } +} \ 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 2f92964..abf58ba 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 @@ -3,26 +3,23 @@ package de.platon42.intellij.plugins.m68k.refs import com.intellij.patterns.PlatformPatterns import com.intellij.psi.* import com.intellij.util.ProcessingContext -import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel -import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel -import de.platon42.intellij.plugins.m68k.psi.M68kRefExpr +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference class M68kReferenceContributor : PsiReferenceContributor() { - override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { - registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kRefExpr::class.java), object : PsiReferenceProvider() { - override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { - return emptyArray() - } - }) - registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kGlobalLabel::class.java), object : PsiReferenceProvider() { - override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { - return emptyArray() - } - }) - registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kLocalLabel::class.java), object : PsiReferenceProvider() { - override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { - return emptyArray() - } - }) + + companion object { + val localLabelReferenceProvider = LocalLabelReferenceProvider() } -} \ No newline at end of file + + override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { + registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider) + } + + class LocalLabelReferenceProvider : PsiReferenceProvider() { + override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { + val symbolReference = element as M68kSymbolReference + if (!symbolReference.isLocalLabelRef) return emptyArray() + return arrayOf(M68kLocalLabelReference(symbolReference)) + } + } +} 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 a2764b4..0ae7c6a 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 @@ -1,60 +1,59 @@ -package de.platon42.intellij.plugins.m68k.scanner; +package de.platon42.intellij.plugins.m68k.scanner -import com.intellij.lang.cacheBuilder.DefaultWordsScanner; -import com.intellij.lang.cacheBuilder.WordsScanner; -import com.intellij.lang.findUsages.FindUsagesProvider; -import com.intellij.psi.PsiElement; -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.M68kTypes; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.lang.cacheBuilder.DefaultWordsScanner +import com.intellij.lang.cacheBuilder.WordsScanner +import com.intellij.lang.findUsages.FindUsagesProvider +import com.intellij.psi.PsiElement +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 org.jetbrains.annotations.Nls +import org.jetbrains.annotations.NonNls -public class M68kFindUsagesProvider implements FindUsagesProvider { +class M68kFindUsagesProvider : FindUsagesProvider { - @Nullable - @Override - public WordsScanner getWordsScanner() { - return new DefaultWordsScanner(new M68kLexer(new M68kLexerPrefs()), // FIXME Oh no! More Prefs! - TokenSet.create(M68kTypes.SYMBOLDEF, M68kTypes.GLOBAL_LABEL_DEF, M68kTypes.LOCAL_LABEL_DEF, M68kTypes.SYMBOL), - TokenSet.create(M68kTypes.COMMENT), - TokenSet.create(M68kTypes.STRINGLIT), - TokenSet.EMPTY); + override fun getWordsScanner(): WordsScanner { + return DefaultWordsScanner( + M68kLexer(M68kLexerPrefs()), // FIXME Oh no! More Prefs! + TokenSet.create(M68kTypes.SYMBOLDEF, M68kTypes.GLOBAL_LABEL_DEF, M68kTypes.LOCAL_LABEL_DEF, M68kTypes.SYMBOL), + TokenSet.create(M68kTypes.COMMENT), + TokenSet.create(M68kTypes.STRINGLIT), + TokenSet.EMPTY + ) } - @Override - public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { - return psiElement instanceof PsiNamedElement; + override fun canFindUsagesFor(psiElement: PsiElement): Boolean { + return psiElement is PsiNamedElement } - @Override - public @Nullable @NonNls String getHelpId(@NotNull PsiElement psiElement) { - return null; + override fun getHelpId(psiElement: PsiElement): @NonNls String? { + return null } - @Override - public @Nls @NotNull String getType(@NotNull PsiElement element) { - if (element instanceof M68kGlobalLabel) { - return "global label"; + override fun getType(element: PsiElement): @Nls String { + return when (element) { + is M68kGlobalLabel -> "global label" + is M68kLocalLabel -> "local label" + is M68kSymbolDefinition -> "symbol definition" + else -> "" } - return ""; } - @Override - public @Nls @NotNull String getDescriptiveName(@NotNull PsiElement element) { - if (element instanceof M68kGlobalLabel) { - return ((M68kGlobalLabel) element).getName(); + override fun getDescriptiveName(element: PsiElement): @Nls String { + return when (element) { + is M68kGlobalLabel -> element.name!! + is M68kLocalLabel -> element.name!! + is M68kSymbolDefinition -> element.parent.text + else -> "" } - return ""; } - @Override - public @Nls @NotNull String getNodeText(@NotNull PsiElement element, boolean useFullName) { - return getDescriptiveName(element); + override fun getNodeText(element: PsiElement, useFullName: Boolean): @Nls String { + return getDescriptiveName(element) } -} +} \ No newline at end of file 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 new file mode 100644 index 0000000..959c11a --- /dev/null +++ b/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kReferenceContributorTest.kt @@ -0,0 +1,37 @@ +package de.platon42.intellij.plugins.m68k.refs + +import com.intellij.psi.PsiElement +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.jupiter.TestDataSubPath +import de.platon42.intellij.plugins.m68k.AbstractM68kTest +import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@TestDataPath("src/test/resources/references") +@TestDataSubPath("labels") +@ExtendWith(LightCodeInsightExtension::class) +internal class M68kReferenceContributorTest : AbstractM68kTest() { + + @Test + internal fun reference_to_dot_local_label(@MyFixture myFixture: CodeInsightTestFixture) { +// val reference = myFixture.getReferenceAtCaretPositionWithAssertion("dot_local_label.asm") +// assertThat(reference.element).isInstanceOf(M68kSym) + myFixture.configureByFile("dot_local_label.asm") + assertThat(myFixture.elementAtCaret).isInstanceOf(M68kLocalLabel::class.java) + .extracting(PsiElement::getText).isEqualTo(".loop") + } + + @Test + internal fun reference_to_multiple_conditional_local_label_dollar(@MyFixture myFixture: CodeInsightTestFixture) { + val reference = myFixture.getReferenceAtCaretPositionWithAssertion("multiple_conditional_local_label_dollar.asm") + assertThat(reference.element).isInstanceOf(M68kLocalLabel::class.java) + myFixture.configureByFile("multiple_conditional_local_label_dollar.asm") + assertThat(myFixture.elementAtCaret).isInstanceOf(M68kLocalLabel::class.java) + .extracting(PsiElement::getText).isEqualTo("loop$") + } +} \ No newline at end of file diff --git a/src/test/resources/references/labels/dot_local_label.asm b/src/test/resources/references/labels/dot_local_label.asm new file mode 100644 index 0000000..5c37221 --- /dev/null +++ b/src/test/resources/references/labels/dot_local_label.asm @@ -0,0 +1,10 @@ + +main + tst.w d1 + beq.s .skip + moveq.l #42,d0 +.loop move.l d0,(a0)+ + dbra d0,.loop +.skip subq.w #1,d1 + beq.s .loop + rts diff --git a/src/test/resources/references/labels/multiple_conditional_local_label_dollar.asm b/src/test/resources/references/labels/multiple_conditional_local_label_dollar.asm new file mode 100644 index 0000000..bae7bf4 --- /dev/null +++ b/src/test/resources/references/labels/multiple_conditional_local_label_dollar.asm @@ -0,0 +1,14 @@ + +main + tst.w d1 + beq.s .skip + moveq.l #42,d0 + IFEQ DATAWIDTH=4 +loop$ move.l d0,(a0)+ + ELSE +loop$ move.w d0,(a0)+ + ENDC + dbra d0,loop$ +.skip subq.w #1,d1 + beq.s loop$ + rts