PsiReference only resolving for values in YAML file

Answered

I've implemented a plugin that simply looks up files that have the name of a given string's text. For example, if there exists a file called foo_character.yaml, then in my other YAML files, CTRL + Clicking a string called foo_character would jump to the foo_character.yaml file. The plugin is working as intended for values in YAML, but not keys. I suppose it's because the existing YAML plugin conflicts with this as it also has some behavior that happens when you CTRL + Click a key. How could I go about resolving this?

Example files:

foo_character.yaml

---
name: foo_character
displayName: Foo Character

another_yaml_file.yaml

---
name: another_yaml_file
params:
foo_character: 1

yaml_that_works.yaml

---
name: yaml_that_works
params:
0: foo_character

CTRL + Clicking the foo_character in yaml_that_works jumps to foo_character.yaml. CTRL + Clicking the foo_character in another_yaml_file.yaml will not, however, jump to foo_character.yaml. After debugging the code, the reference contributor is indeed resolving the reference correctly.

Here is the code for my reference that resolves it. If the properties value is printed out in multiresolve, it will correctly find the PsiFile in both cases.

internal class GameCacheConfigFileReference(
    element: PsiElement,
    textRange: TextRange,
) : PsiReferenceBase<PsiElement>(element, textRange), PsiPolyVariantReference {

    private val key = element.text.substring(textRange.startOffset, textRange.endOffset)

    override fun resolve(): PsiElement? {
        val resolveResults = multiResolve(false)
        return if (resolveResults.size == 1) resolveResults[0].element else null
    }

    override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
        val project = myElement.project
        val properties = FilenameIndex.getFilesByName(project, "$key.yaml", GlobalSearchScope.allScope(project))
        val results = arrayListOf<ResolveResult>()
        for (property in properties) {
            results.add(PsiElementResolveResult(property))
        }
        return results.toTypedArray()
    }
}

Here is the reference contributor that I made.

internal class GameCacheConfigReferenceContributor : PsiReferenceContributor() {

    override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
        registrar.registerReferenceProvider(PlatformPatterns.psiElement(), GameCacheConfigFileReferenceProvider(), PsiReferenceRegistrar.DEFAULT_PRIORITY)
    }

    private class GameCacheConfigFileReferenceProvider : PsiReferenceProvider() {

        override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
            val text = element.text

            val words = text.split("\\s".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            if (words.size != 1) {
                return PsiReference.EMPTY_ARRAY
            }
            return arrayOf(GameCacheConfigFileReference(element, TextRange.allOf(words[0])))
        }
    }
}

How could I make the keys in YAML files behave correctly?

0
2 comments

So I suppose ultimately the issue comes down to how can I have my psi references be used when there is a find usage provider conflicting with it?

0

Hi Arham,

Could you please clarify whether your reference resolve is called when you click on the key or not? You say it is resolved when you debug - is it called then when you Ctrl+click it?

Regarding the GameCacheConfigReferenceContributor - why do you match all the elements (PlatformPatterns.psiElement())? Does this contributor contribute references for a key only? If yes, I suggest something cleaner, like matching the first PsiElement in YAMLKeyValue or another approach explicitly using actual YAML elements you want to cover. Currently, it's unclear to me what references are created and how many.

How is your contributor registered in plugin.xml? Does it have an order parameter? Did you try making it the first on the list?

0

Please sign in to leave a comment.