Added local label reference (not final yet), but referencing kind of works.

This commit is contained in:
Chris Hodges 2021-07-19 12:08:47 +02:00
parent 6073cd86ac
commit 66fa728a45
14 changed files with 244 additions and 72 deletions

View File

@ -2,7 +2,13 @@
package de.platon42.intellij.plugins.m68k.psi; package de.platon42.intellij.plugins.m68k.psi;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
public interface M68kSymbolReference extends PsiElement { public interface M68kSymbolReference extends PsiElement {
@NotNull
String getSymbolName();
boolean isLocalLabelRef();
} }

View File

@ -1,14 +1,15 @@
// This is a generated file. Not intended for manual editing. // This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi.impl; package de.platon42.intellij.plugins.m68k.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor; 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.M68kSymbolReference;
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReferenceMixin;
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor; import de.platon42.intellij.plugins.m68k.psi.M68kVisitor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class M68kSymbolReferenceImpl extends ASTWrapperPsiElement implements M68kSymbolReference { public class M68kSymbolReferenceImpl extends M68kSymbolReferenceMixin implements M68kSymbolReference {
public M68kSymbolReferenceImpl(@NotNull ASTNode node) { public M68kSymbolReferenceImpl(@NotNull ASTNode node) {
super(node); super(node);
@ -24,4 +25,15 @@ public class M68kSymbolReferenceImpl extends ASTWrapperPsiElement implements M68
else super.accept(visitor); else super.accept(visitor);
} }
@Override
@NotNull
public String getSymbolName() {
return M68kPsiImplUtil.getSymbolName(this);
}
@Override
public boolean isLocalLabelRef() {
return M68kPsiImplUtil.isLocalLabelRef(this);
}
} }

View File

@ -1,7 +1,11 @@
package de.platon42.intellij.plugins.m68k package de.platon42.intellij.plugins.m68k
import com.intellij.icons.AllIcons
import com.intellij.openapi.util.IconLoader import com.intellij.openapi.util.IconLoader
object M68kIcons { object M68kIcons {
val FILE = IconLoader.getIcon("/icons/FileType_m68k.svg", javaClass) 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
} }

View File

@ -153,6 +153,8 @@ GlobalLabel ::= GLOBAL_LABEL_DEF COLON* {
extends = Label extends = Label
implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement" implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement"
mixin = "de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabelMixin" 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] methods = [getName setName getNameIdentifier]
} }
@ -179,7 +181,13 @@ private PreprocessorOperand ::= expr
private PlainOperands ::= STRINGLIT (SEPARATOR STRINGLIT)* 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 ProgramCounterReference ::= CURRENT_PC_SYMBOL
DataRegister ::= DREG { DataRegister ::= DREG {

View File

@ -2,7 +2,12 @@ package de.platon42.intellij.plugins.m68k.psi
import com.intellij.extapi.psi.ASTWrapperPsiElement import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import de.platon42.intellij.plugins.m68k.M68kIcons
import javax.swing.Icon
abstract class M68kLocalLabelMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLocalLabel { abstract class M68kLocalLabelMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLocalLabel {
override fun getIcon(flags: Int): Icon? {
return M68kIcons.LOCAL_LABEL
}
} }

View File

@ -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
}

View File

@ -45,7 +45,6 @@ object M68kPsiImplUtil {
// Symbol Definition // Symbol Definition
@JvmStatic @JvmStatic
fun getName(element: M68kSymbolDefinition): String? = element.firstChild.text fun getName(element: M68kSymbolDefinition): String? = element.firstChild.text
@ -62,4 +61,14 @@ object M68kPsiImplUtil {
@JvmStatic @JvmStatic
fun getNameIdentifier(element: M68kSymbolDefinition): PsiElement = element.firstChild 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('$')
}
} }

View File

@ -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<PsiReference> {
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
}

View File

@ -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<M68kSymbolReference>(element, TextRange(0, element.textLength)) {
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
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<Any> {
return findLocalLabels { true }
.map { LookupElementBuilder.createWithIcon(it) }
.toTypedArray()
}
private fun findLocalLabels(predicate: (M68kLocalLabel) -> Boolean): List<M68kLocalLabel> {
val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!!
val results: MutableList<M68kLocalLabel> = 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
}
}

View File

@ -3,26 +3,23 @@ package de.platon42.intellij.plugins.m68k.refs
import com.intellij.patterns.PlatformPatterns import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kRefExpr
class M68kReferenceContributor : PsiReferenceContributor() { class M68kReferenceContributor : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kRefExpr::class.java), object : PsiReferenceProvider() { companion object {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> { val localLabelReferenceProvider = LocalLabelReferenceProvider()
return emptyArray()
}
})
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kGlobalLabel::class.java), object : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return emptyArray()
}
})
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kLocalLabel::class.java), object : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return emptyArray()
}
})
} }
}
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider)
}
class LocalLabelReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
val symbolReference = element as M68kSymbolReference
if (!symbolReference.isLocalLabelRef) return emptyArray()
return arrayOf(M68kLocalLabelReference(symbolReference))
}
}
}

View File

@ -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.DefaultWordsScanner
import com.intellij.lang.cacheBuilder.WordsScanner; import com.intellij.lang.cacheBuilder.WordsScanner
import com.intellij.lang.findUsages.FindUsagesProvider; import com.intellij.lang.findUsages.FindUsagesProvider
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiNamedElement
import com.intellij.psi.tree.TokenSet; import com.intellij.psi.tree.TokenSet
import de.platon42.intellij.plugins.m68k.lexer.M68kLexer; import de.platon42.intellij.plugins.m68k.lexer.M68kLexer
import de.platon42.intellij.plugins.m68k.lexer.M68kLexerPrefs; import de.platon42.intellij.plugins.m68k.lexer.M68kLexerPrefs
import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel; import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kTypes; import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel
import org.jetbrains.annotations.Nls; import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition
import org.jetbrains.annotations.NonNls; import de.platon42.intellij.plugins.m68k.psi.M68kTypes
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NonNls
public class M68kFindUsagesProvider implements FindUsagesProvider { class M68kFindUsagesProvider : FindUsagesProvider {
@Nullable override fun getWordsScanner(): WordsScanner {
@Override return DefaultWordsScanner(
public WordsScanner getWordsScanner() { M68kLexer(M68kLexerPrefs()), // FIXME Oh no! More Prefs!
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.SYMBOLDEF, M68kTypes.GLOBAL_LABEL_DEF, M68kTypes.LOCAL_LABEL_DEF, M68kTypes.SYMBOL), TokenSet.create(M68kTypes.COMMENT),
TokenSet.create(M68kTypes.COMMENT), TokenSet.create(M68kTypes.STRINGLIT),
TokenSet.create(M68kTypes.STRINGLIT), TokenSet.EMPTY
TokenSet.EMPTY); )
} }
@Override override fun canFindUsagesFor(psiElement: PsiElement): Boolean {
public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { return psiElement is PsiNamedElement
return psiElement instanceof PsiNamedElement;
} }
@Override override fun getHelpId(psiElement: PsiElement): @NonNls String? {
public @Nullable @NonNls String getHelpId(@NotNull PsiElement psiElement) { return null
return null;
} }
@Override override fun getType(element: PsiElement): @Nls String {
public @Nls @NotNull String getType(@NotNull PsiElement element) { return when (element) {
if (element instanceof M68kGlobalLabel) { is M68kGlobalLabel -> "global label"
return "global label"; is M68kLocalLabel -> "local label"
is M68kSymbolDefinition -> "symbol definition"
else -> ""
} }
return "";
} }
@Override override fun getDescriptiveName(element: PsiElement): @Nls String {
public @Nls @NotNull String getDescriptiveName(@NotNull PsiElement element) { return when (element) {
if (element instanceof M68kGlobalLabel) { is M68kGlobalLabel -> element.name!!
return ((M68kGlobalLabel) element).getName(); is M68kLocalLabel -> element.name!!
is M68kSymbolDefinition -> element.parent.text
else -> ""
} }
return "";
} }
@Override override fun getNodeText(element: PsiElement, useFullName: Boolean): @Nls String {
public @Nls @NotNull String getNodeText(@NotNull PsiElement element, boolean useFullName) { return getDescriptiveName(element)
return getDescriptiveName(element);
} }
} }

View File

@ -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$")
}
}

View File

@ -0,0 +1,10 @@
main
tst.w d1
beq.s .skip
moveq.l #42,d0
.loop move.l d0,(a0)+
dbra d0,.l<caret>oop
.skip subq.w #1,d1
beq.s .loop
rts

View File

@ -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,l<caret>oop$
.skip subq.w #1,d1
beq.s loop$
rts