theY4Kman / idea-sp

IntelliJ SourcePawn Plugin
13 stars 4 forks source link

Implement references #7

Open theY4Kman opened 3 years ago

theY4Kman commented 3 years ago

Brief

Under the hood, IDEA parses symbols from source code into a tree of PsiElements. Each PsiElement can have references attached to it, which power vital features such as Find Usages and Go To Declaration. idea-sp will need a solid set of references to aid SourcePawn developers.

Details

See the Reference Contributor section of the Custom Language Support Tutorial for more info

A reference is implemented as a subclass of PsiReferenceBase, and stores a record of the PsiElement it's attached to (and any other relevant details). The reference is responsible for implementing the resolve() method, which returns the PsiElement the reference refers to.

References are attached to elements using a sort of selector — think CSS. Basically, a matcher is built, which acts upon a PSI tree to find matching elements and pass them to a reference provider. Here's an example from my pytest-imp plugin:

registrar.registerReferenceProvider(
    psiElement(PyStringLiteralExpression::class.java)
        .withParent(psiElement(PyArgumentList::class.java)
            .withParent(psiElement(PyCallExpression::class.java)
                .with(object : PatternCondition<PyCallExpression>("callIsLambdaFixture") {
                    override fun accepts(call: PyCallExpression, context: ProcessingContext?) = call.isLambdaFixture()
                })
            )),
    LambdaFixtureStringReferenceProvider,
    PsiReferenceRegistrar.HIGHER_PRIORITY + 10)

A reference provider is a subclass of PsiReferenceProvider. It's sole responsibility is to extract relevant details from matching PsiElements, instantiate zero or more instances of a PsiReferenceBase child class, and return them.

So, if we implement these few things:

we get Find Usages and Go To Declaration almost for free.

Requirements

Now, I'd expect things to get a little hairy later on with multiple linked files, macros, #if/def, etc — but for now, I think the important things to get right are:

Stretch Goals

The reference system is pretty darn flexible. It's capable of attaching references even to string literals, and much more. It would be cool, but not essential, if we could implement:

theY4Kman commented 3 years ago

Sooooo, it turns out a reference contributor is for contributing references in other languages. To implement references for our own language, we just override PsiElement.getReference(). I had a go at getting just some basic, intra-file references going, and it works pretty well, even if the code is at yet inelegant

idea-sp-references