When returning multiple references from PsiReferenceProvider#getReferencesByElement, the highlighting effect is gone

Answered

The BNF structure looks like this:

positive-prop ::= prop-name prop-args? {
    pin = 1
    implements="net.fallingangel.jimmerdto.psi.mixin.DTONamedElement"
    mixin="net.fallingangel.jimmerdto.psi.mixin.impl.DTONamedElementImpl"
    methods=[getPsi getName setName]
}
prop-name ::= IDENTIFIER | "null"
prop-args ::= "(" value ("," value)* ")"
value ::= CHAR_CONSTANT | STRING_CONSTANT

The implementation of PsiReferenceProvider#getReferencesByElement is as follows:

override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
    registrar.registerReferenceProvider(
        psiElement(DTONamedElement::class.java),
        object : PsiReferenceProvider() {
            override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
                element as DTOPositiveProp
                val propArgs = element.propArgs
                @Suppress("IfThenToElvis")
                return if (propArgs == null) {
                    arrayOf(DTOReference(element, element.propName.textRangeInParent))
                } else {
                    propArgs.valueList
                            .map {
                                val valueStart = it.textRangeInParent.startOffset
                                val argStart = it.parent.textRangeInParent.startOffset
                                val valueInArgStart = argStart + valueStart
                                DTOValueReference(it, element, TextRange(valueInArgStart, valueInArgStart + it.textLength))
                            }
                            .toTypedArray()
                }
            }
        },
    )
}

The DTOValueReference is implemented as follows:

class DTOValueReference(private val value: DTOValue, element: PsiElement, textRange: TextRange) : PsiReferenceBase<PsiElement>(element, textRange) {
    override fun resolve(): PsiElement? {
        val project = value.project
        val entityFile = value.virtualFile.entityFile(project) ?: throw IllegalStateException()
        val propName = value.text
        val prop = value.parent.parent as DTOPositiveProp

        return when (entityFile.language) {
            Language.Java -> {
                val clazz = prop.psiClass()
                clazz.methods().find { it.name == propName }
            }

            Language.Kotlin -> {
                val clazz = prop.ktClass()
                clazz.properties().find { it.name == propName }
            }
        }
    }
}

The actual runtime case where one DTOValueReference is returned:

The actual runtime case where more than one DTOValueReference is returned:

As you can see, when one DTOValueReference is returned, it's exactly as expected; when more than one DTOValueReference is returned, the highlighting of each DTOValue is lost, but other things such as documentation and reference parsing are as expected.

0
4 comments

Hi,

This looks suspicious to me:

class DTOValueReference(private val value: DTOValue, element: PsiElement, textRange: TextRange) : PsiReferenceBase<PsiElement>(element, textRange) {
  ...
}

This is a reference for a value, but you pass an element instead of a value to the PsiReferenceBase constructor.

0

Yes... it's quite possible that my parsing element is "value," while the "element" is a parent of "value". I'll give it a try!

0

Here's the situation:
My BNF structure is as follows: value belongs to prop-args, and prop-args belong to positive-prop.
My logic is this: value needs to be resolved as DTOValueReference, and positive-prop also needs to be resolved as DTOReference. Herein lies a problem:
When registering structures in registerReferenceProvider, if the pattern is set for positive-prop, whether I write a separate registerReferenceProvider for value or include “prop or value” in the same pattern, the actual runtime behavior is that clicking on prop-name triggers parsing for prop; clicking on value also triggers parsing for prop, causing confusion in logic. This is why I created a base class for elements (DTONamedElement) and two reference classes (DTOReference, DTOValueReference), and why I handled the reference for value specially in PsiReferenceProvider.
However, I have now thought of a potential solution, which is to stop creating reference parsing for prop and instead only do it for prop-name. This way, name and value would not have a parent-child relationship, which should prevent the confusion in reference parsing when clicking on elements.
I am going to try this new approach.

0

Exactly, that's how it is! The issue has been resolved, thank you, Karol Lewandowski!

0

Please sign in to leave a comment.