JS String literal to Json property ReferenceContributor

Answered

Hi!

I'm having a bit of difficulty wrapping my head around why my ReferenceContributor doesn't work.

There are translation json files in my project in which property names act as i18n keys. These keys are then used as String literals within JS properties in many JavaScript files.

What I would like to do is to be able to jump to the proper json properties from the JS String literals by Ctrl+Click, etc.

My current implementation is the following:

public class TranslationReferenceContributor extends PsiReferenceContributor {

@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(JSLiteralExpression.class),
new PsiReferenceProvider() {
@Override
public @NotNull PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (element instanceof JSLiteralExpression && ((JSLiteralExpression) element).isStringLiteral()) {
if (element.getParent() instanceof JSProperty) {
//Other checks to determine the context
//...

Set<JsonProperty> matchingJsonProperties = new HashSet<>();
//Collect all JsonProperty PSI elements into matchingJsonProperties whose names match the JS String literal

//Create a JsonPropertyNameReference for each JsonProperty element
return matchingJsonProperties.stream().map(JsonPropertyNameReference::new).toArray(PsiReference[]::new);
}
}

return PsiReference.EMPTY_ARRAY;
}
}
}
}

However when the contributor is executed I get the following exception (and in turn no references at all):

reference com.intellij.json.psi.impl.JsonPropertyNameReference@5899cb70 was created for JSLiteralExpression but target JsonProperty, provider com.myproject.TranslationReferenceContributor$1@7f207dae
at com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl.assertReferenceUnderlyingElement(ReferenceProvidersRegistryImpl.java:194)
at com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl.mapNotEmptyReferencesFromProviders(ReferenceProvidersRegistryImpl.java:168)
at com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl.doGetReferencesFromProviders(ReferenceProvidersRegistryImpl.java:145)
at com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry.lambda$getReferencesFromProviders$0(ReferenceProvidersRegistry.java:40)

I can't really see what I am missing either from implementation or conceptual aspect, even after looking into other ReferenceContributor implementations. My understanding is that

registrar.registerReferenceProvider(PlatformPatterns.psiElement(JSLiteralExpression.class)

determines the place (a JSLiteralExpression in this case) where I would invoke Ctrl+Click, Ctrl+B, etc., while the type of the reference created (JsonPropertyNameReference) in getReferencesByElement determines the place I would jump to.

The contributor is currently registered in plugin.xml as

<psi.referenceContributor
implementation="com.myproject.TranslationReferenceContributor"/>

I would really appreciate some insight on this.

3 comments
Comment actions Permalink

com.intellij.json.psi.impl.JsonPropertyNameReference cannot be created for anything else but com.intellij.json.psi.JsonProperty (see its constructor), but you try to bind it to JSLiteralExpression.
I'd recommend copy/pasting it instead of trying to reuse.

 

If possible, also register <psi.referenceContributor> with "language" attribute for performance.

0
Comment actions Permalink

Hi Yann,

Thank you, now I'm starting to see what's happening, and as it turned out I missed some of the devguide documentation, so I did some catching up.

So my current understanding is that the ReferenceContributor merely creates a references, but the Reference object itself is the one that describes what element(s) the current one is linked to by its resolve(), multiResolve(), etc. methods. Thus besides my ReferenceContributor I also need a custom Reference class that will handle the linking. (I guess it is kind of what you meant by copy/pasting.)

Now my question is, are the Ctrl+B/Ctrl-Click shortcuts the ones that supposed to trigger the call to resolve() and multiResolve()? If not, then how are they handled between the triggering the shortcut key and calling the resolution methods?

0
Comment actions Permalink

Yes, basically you can copy/paste

JsonPropertyNameReference

or just create your own PsiReference implementation.

The shortcuts will ultimatively use the target element(s) provided by your PsiReference (resolve/multiResolve).

0

Please sign in to leave a comment.