title |
---|
10. Reference Contributor |
References is one of the most important and tricky parts in the implementation of a custom language support. Resolving references means the ability to go from the usage of an element to its declaration, completion, rename refactoring, find usages, etc.
Every element which can be renamed or referenced needs to implement com.intellij.psi.PsiNamedElement
interface.
{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/SimpleNamedElement.java %}
{% include /code_samples/simple_language_plugin/src/com/simpleplugin/psi/impl/SimpleNamedElementImpl.java %}
Since we need to implement new methods in PSI class, we should define them in the SimplePsiImplUtil
class:
public class SimplePsiImplUtil {
// ...
public static String getName(SimpleProperty element) {
return getKey(element);
}
public static PsiElement setName(SimpleProperty element, String newName) {
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
if (keyNode != null) {
SimpleProperty property = SimpleElementFactory.createProperty(element.getProject(), newName);
ASTNode newKeyNode = property.getFirstChild().getNode();
element.getNode().replaceChild(keyNode, newKeyNode);
}
return element;
}
public static PsiElement getNameIdentifier(SimpleProperty element) {
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
if (keyNode != null) {
return keyNode.getPsi();
} else {
return null;
}
}
// ...
}
Note that the SimpleElementFactory
class will show as an error. We'll create it next.
package com.simpleplugin.psi;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.simpleplugin.SimpleFileType;
public class SimpleElementFactory {
public static SimpleProperty createProperty(Project project, String name) {
final SimpleFile file = createFile(project, name);
return (SimpleProperty) file.getFirstChild();
}
public static SimpleFile createFile(Project project, String text) {
String name = "dummy.simple";
return (SimpleFile) PsiFileFactory.getInstance(project).
createFileFromText(name, SimpleFileType.INSTANCE, text);
}
}
Now we need to make corresponding changes to the grammar file and regenerate parser and PSI classes.
property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.SimpleNamedElementImpl"
implements="com.simpleplugin.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier]}
Don't forget to regenerate the parser! Right click on the Simple.bnf
file and select Generate Parser Code.
Now we need to define a reference class to resolve a property from it's usage.
{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleReference.java %}
A reference contributor allows you to provide references from elements in other languages such as Java to elements in your language. Let's contribute a reference to each usage of a property.
{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleReferenceContributor.java %}
<psi.referenceContributor implementation="com.simpleplugin.SimpleReferenceContributor"/>
As you see the IDE now resolves the property and provides completion.
Rename refactoring available from definition and usages.
To allow in-place refactoring we should specify it explicitly in a refactoring support provider.
{% include /code_samples/simple_language_plugin/src/com/simpleplugin/SimpleRefactoringSupportProvider.java %}
<lang.refactoringSupport language="Simple" implementationClass="com.simpleplugin.SimpleRefactoringSupportProvider"/>