PsiReferenceContributor, Find Usages, and Go to Declaration

I'm working on a plugin for a framework that uses JSON for configuration.  Here's an example configuration file:

"players": [
{ "id": "AA11111", "name": "Player A"},
{ "id": "BB22222", "name": "Player B"},
{ "id": "CC33333", "name": "Player C"},
{ "id": "DD44444", "name": "Player D"},
{ "id": "EE55555", "name": "Player E"}
"teams": [
{ "name": "Alpha Team", "members": [ "AA11111", "BB22222", "CC33333" ] },
{ "name": "Bravo Team", "members": [ "CC33333", "DD44444", "EE55555" ] }

Under the "players" array, if I do a "Find Usages" on one of the IDs, such as "CC33333", I'd like the references within "teams" to be identified.  And if I do a "Go to Declaration" on one of those references in the "teams" array, I'd like the cursor to go up to the corresponding element in the "players" array.

From what I've read, PsiReferenceContributor seems to be the piece I need to develop to achieve that functionality.  I wrote a quick implementation as a proof of concept -- here's the code:

package com.example.ipe;

import com.intellij.json.psi.JsonArray;
import com.intellij.json.psi.JsonObject;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonStringLiteral;
import com.intellij.json.psi.impl.JsonPsiImplUtils;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.patterns.PlatformPatterns.psiFile;
import static com.intellij.patterns.StandardPatterns.string;

public class ExampleReferenceContributor extends PsiReferenceContributor {
public void registerReferenceProviders(@NotNull final PsiReferenceRegistrar registrar) {
final PsiElementPattern.Capture<PsiElement> pattern = psiElement()
registrar.registerReferenceProvider(pattern, new PsiReferenceProvider() {
public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull final ProcessingContext context) {
final JsonStringLiteral idElement = (JsonStringLiteral)element;
final String id = JsonPsiImplUtils.getValue(idElement);
final JsonObject root = (JsonObject)element.getContainingFile().getFirstChild();

final PsiReference[] refs = root.getPropertyList().stream()
.filter(p -> p.getName().equals("teams"))
.map(p -> (JsonArray)p.getValue())
.flatMap(p -> p.getValueList().stream())
.map(p -> (JsonObject)p)
.flatMap(p -> p.getPropertyList().stream())
.filter(p -> p.getName().equals("members"))
.map(p -> (JsonArray)p.getValue())
.flatMap(m -> m.getValueList().stream())
.map(m -> (JsonStringLiteral)m)
.filter(m -> JsonPsiImplUtils.getValue(m).equals(id))
.map(ref -> new PsiReferenceBase.Immediate<>(ref, element))

return refs;

I've also added the contributor to plugin.xml:

<extensions defaultExtensionNs="com.intellij">
  • When I try "Find Usages", nothing happens -- it does not even say "Cannot search for usages" as it usually would if I provided no implementation.  
  • When I try "Go to Declaration", I get "Cannot find declaration to go to".

I tried setting a breakpoint on "return refs;", and I see that the correct references are indeed being returned from that method.

I must be missing something -- what am I doing wrong?


1 comment
Comment actions Permalink
Official comment

Hello Dave, looks like here: 

.map(ref -> new PsiReferenceBase.Immediate<>(ref, element))

you should change the arguments places: first argument is for reference element, and second is where to resolve

Please sign in to leave a comment.