Custom Language: References in same file

Answered

Hello everyone,

I'm stuck with the following problem since several days and could not get an answer from the documentation or other posts.

I have a really simple use cases in my custom language:

- Everything happens in the same language file -> no references between files needed
- I have a declaration of an ID and a usage of this ID (I think this is the most simple way for references but I can't figure out how to do it)

How do I find the usages when CTRL-Clicking on the declaration and how do I get to the declaration when clicking on the usage?

The PsiElement which holds the ID in the declaration extends the PsiNamendElement interface.
The PsiElement which holds the ID in the usage overrides the method 'getReference' which in turn returns a PsiReference containing the PsiElement of the ID in the declaration.

The 'resolve' method of the PsiReference is never called, but it seems that the 'getReference' Method is called in an endless loop.

I also tried to implement the 'getReference / getReferences' methods in the NamendElement which return references to the PsiElement of the ID in the usage, but that did not work either.

Another try was to use ReferenceContributors as shown in the tutorial, but I read that these are (normally) not needed.

I just can't get this simple problem to work.

Hope anyone can help me out

 

public class MyLangReference extends PsiReferenceBase<PsiElement> {
public MyLangReference(@NotNull PsiElement element, TextRange textRange) {
super(element, textRange);
System.out.println("I think I am called in some infinite loop");
}

@Override
public @Nullable PsiElement resolve () {
System.out.println ("I am never called");
return null;
}
}

public class MyLangIDUsageImpl extends ASTWrapperPsiElement implements MyLangIDUsage {

public MyLangIDUsageImpl(@NotNull ASTNode node) {
super(node);
}

// ...

// return the declaration id
@Override public PsiReference getReference () {
MyLangID id = MyLangUtil.findID (getProject (), getText ());
if (id == null) return null;
return new MyLangReference (id, id.getTextRange ());
}
}
1
8 comments

Hi,

The PsiElement which holds the ID in the usage overrides the method 'getReference' which in turn returns a PsiReference containing the PsiElement of the ID in the declaration.

You shouldn't precalculate the declaration when creating a reference. You should calculate the declaration element only when resolve is called, so lazily.

The 'resolve' method of the PsiReference is never called, but it seems that the 'getReference' Method is called in an endless loop.

What endless loop do you mean?

The information you provided is pretty general, and I'm not able to find the reason why your references do not work. I suggest preparing a minimal reproducible plugin example and the test project or sharing your actual sources.

I also suggest checking a code sample for the Simple language plugin: https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin

and tutorial: https://plugins.jetbrains.com/docs/intellij/reference-contributor.html

1

I am not quite sure what you mean by that, because I calculate the element only when 'getReference' is called.

This is the problem. It is a performance issue as PsiElement.getReference() may be called many times even if resolving the declaration is not needed (and you can observe it by looking at printed message - these are not called in a loop but from several platform mechanisms). It should be as cheap as possible and never do resolving.

In getReference(), you should only create a reference instance. The reference instance (note that your PsiElement can be a PsiReference too, and you can simply return "this" from getReference()) should be responsible for calculating the declaration (in the resolve() method using the current PsiElement, not declaration) and in your case, you provide the reference instance with the declaration element calculated upfront, without need.

1

Why wouldn't it matter what getReference() returns? It returns a PsiReference instance that resolves the declaration. If it's a PsiElement being a PsiReference at the same time, or separate PsiReference object, doesn't matter. 

The resolve() is called when declaration resolution is needed, so e.g., when go to the declaration, when you find for usages (declaration is resolved and then usages are found), in inspections if they need the information about the declaration, and in many other features.

I suggest reading https://plugins.jetbrains.com/docs/intellij/references-and-resolve.html#implementing-resolve-logic carefully, and playing with the code (e.g. sample I mentioned), to see how it works.

1

Hi, thanks for your reply.

You shouldn't precalculate the declaration when creating a reference. You should calculate the declaration element only when resolve is called, so lazily.

I am not quite sure what you mean by that, because I calculate the element only when 'getReference' is called.

By endless loop I mean, that the following statement is executed multiple times per second.

System.out.println("I think I am called in some infinite loop");

I will try to setup a minimal working example later.

0

Thank you. I think I understand it better know, but I assume that it basically doesn’t matter what the getReference method returns as the logic is in the resolve method? That feels kind of wrong

And could you explain at what point the resolve method is called? I think this is the last part where I struggle

0

Ok thank you, I think I got it now.

0

Sorry I feel so dumb but I guess I didn't got it :D

Do I have to implement something that resolve() is called? Do I have to implement e.g. the PsiScopeProcesor which calls somehow resolve()?

If I have to implement some interface, where do I have to register it and how is it called?

For me it is like a black box because I don't know which functionality is given by default and what has to be implemented manually. I guess mainly because of my proper understanding of english texts.

0

Hi,

Did you try to go through the documentation, tutorial, and code sample I mentioned? I really see no point in repeating what's there.

Please invest some time in checking the resources. If you have further questions, feel free to use Slack or this forum.

If something is unclear in the docs, please use the feedback widget at the bottom of the page and let us know what exactly is unclear. Thanks.

0

Please sign in to leave a comment.