Add entries in goto declaration or usages

Answered

Hi,

I have a custom web service tooling. An IDL is written in Java to define beans and services, and used to generate beans and client proxies in multiple languages.

In JavaScript for exemple, currently when I run “goto declaration or usage” on a generated client proxy method, it goes to generated code of the class as expected. I would like to add a new entry, and IDE to open the menu asking which point to go between native JS class or IDL java class, like it does when running it on a method and you choose which usage to go.

I know how to find PsiElements where to go, but is there a way to inject alternative declarations so the IDE ask user where to go on “Ctrl + click” ?

I currently did it implementing an Action with a new menu entry in “Go to” menu, but I would like it a bit more integrated as we usually don't care navigating to generated code.

0
2 comments

Hi Julien,

You can contribute references to elements from other languages with com.intellij.psi.PsiReferenceContributor. But there is a catch - element for which you would like to contribute references must implement com.intellij.psi.ContributedReferenceHost.

You can use the PSI viewer to find out which element you want to contribute references to: https://plugins.jetbrains.com/docs/intellij/explore-api.html#internalMode

Then, check if it implements com.intellij.psi.ContributedReferenceHost. If it does, then implement a contributor that will provide references that resolve to additional elements.

0

From what I understand, nothing in JavaScript or in Java implements com.intellij.psi.ContributedReferenceHost ? It would mean there is not way achieve that goal in JavaScript and Java using PsiReferenceContributor ?

With following test, I'm able to have Ctrl+Click on a JavaScript constructor that navigate to expected Java class definition. But it breaks JS import features and I guess many other things. I would like to add a new alternative goto after the native one, not to replace the native one. Is there a way to fix it ?

package com.test.contributor;

import com.intellij.lang.javascript.psi.JSNewExpression;
import com.intellij.lang.javascript.psi.JSReferenceExpression;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

public class TestReferenceContributor extends PsiReferenceContributor {
	@Override
	public void registerReferenceProviders(@NotNull final PsiReferenceRegistrar registrar) {
		registrar.registerReferenceProvider(
				PlatformPatterns.psiElement(JSReferenceExpression.class),
				new PsiReferenceProvider() {
					@Override
					public PsiReference @NotNull [] getReferencesByElement(
							@NotNull PsiElement element,
							@NotNull ProcessingContext context) {
						final JSReferenceExpression referenceExpression = (JSReferenceExpression) element;
						System.out.println("Reference " + referenceExpression.getText());
						if (referenceExpression.getParent() != null
								&& referenceExpression.getParent() instanceof JSNewExpression jsNewExpression
								&& referenceExpression.getText().endsWith("ClientProxy")) {
							final List<PsiReference> refs = new ArrayList<>();

							// Find IDL service definition corresponding to ClientProxy
							final String qualifiedClientProxyName = "com.demo.test.TestServiceClientProxy";
							final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(element.getProject());
							final var idlService = javaPsiFacade.findClass(
									qualifiedClientProxyName.substring(0, qualifiedClientProxyName.length() - "ClientProxy".length()),
									GlobalSearchScope.everythingScope(element.getProject()));
							
							System.out.println("Found " + idlService.getQualifiedName() + " - ref " + idlService.getReference());
							// refs.add(idlService.getReference()); // null
							refs.add(new PsiReference() {
								@Override
								public @NotNull PsiElement getElement() {
									return referenceExpression;
								}
								@Override
								public @NotNull TextRange getRangeInElement() {
									return null;
								}
								@Override
								public @Nullable PsiElement resolve() {
									return idlService;
								}
								@Override
								public @NotNull @NlsSafe String getCanonicalText() {
									return idlService.getQualifiedName();
								}
								@Override
								public PsiElement handleElementRename(@NotNull final String newElementName) throws IncorrectOperationException {
									return null;
								}
								@Override
								public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException {
									return null;
								}
								@Override
								public boolean isReferenceTo(@NotNull final PsiElement element) {
									return false;
								}
								@Override
								public boolean isSoft() {
									return false;
								}
							});
							System.out.println(refs);
							return refs.toArray(new PsiReference[0]);
						}

						return PsiReference.EMPTY_ARRAY;
					}
				});
	}
}


 

0

Please sign in to leave a comment.