IntelliJ Platform SDK DevGuide

10. Reference Contributor

引用是实现自定义语言支持中最重要和最棘手的部分之一.

解析引用意味着能够从元素的使用转变为声明,完成,重命名重构,查找用法等.

每个可以重命名或引用的元素都需要实现com.intellij.psi.PsiNamedElement接口.

10.1.

定义一个名为element的基类

package com.simpleplugin.psi; import com.intellij.psi.PsiNameIdentifierOwner; public interface SimpleNamedElement extends PsiNameIdentifierOwner { }
package com.simpleplugin.psi.impl; import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; import com.simpleplugin.psi.SimpleNamedElement; import org.jetbrains.annotations.NotNull; public abstract class SimpleNamedElementImpl extends ASTWrapperPsiElement implements SimpleNamedElement { public SimpleNamedElementImpl(@NotNull ASTNode node) { super(node); } }

10.2.

为生成的PSI元素定义辅助方法

由于我们需要在PSI类中实现新方法,我们应该在SimplePsiImplUtil类中定义它们:

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; } } // ... }

请注意,SimpleElementFactory类将显示为错误. 我们接下来会创建它.

10.3.

定义元素工厂

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); } }

10.4.

更新语法并重新生成解析器

现在我们需要对语法文件进行相应的更改并重新生成解析器和PSI类.

property ::= (KEY? SEPARATOR VALUE?) | KEY {mixin="com.simpleplugin.psi.impl.SimpleNamedElementImpl" implements="com.simpleplugin.psi.SimpleNamedElement" methods=[getKey getValue getName setName getNameIdentifier]}

不要忘记重新生成解析器! 右键单击Simple.bnf文件并选择_Generate Parser Code_.

10.5.

定义参考

现在我们需要定义一个引用类来解析它的用法属性.

package com.simpleplugin; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.simpleplugin.psi.SimpleProperty; import org.jetbrains.annotations.*; import java.util.*; public class SimpleReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference { private String key; public SimpleReference(@NotNull PsiElement element, TextRange textRange) { super(element, textRange); key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset()); } @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { Project project = myElement.getProject(); final List<SimpleProperty> properties = SimpleUtil.findProperties(project, key); List<ResolveResult> results = new ArrayList<ResolveResult>(); for (SimpleProperty property : properties) { results.add(new PsiElementResolveResult(property)); } return results.toArray(new ResolveResult[results.size()]); } @Nullable @Override public PsiElement resolve() { ResolveResult[] resolveResults = multiResolve(false); return resolveResults.length == 1 ? resolveResults[0].getElement() : null; } @NotNull @Override public Object[] getVariants() { Project project = myElement.getProject(); List<SimpleProperty> properties = SimpleUtil.findProperties(project); List<LookupElement> variants = new ArrayList<LookupElement>(); for (final SimpleProperty property : properties) { if (property.getKey() != null && property.getKey().length() > 0) { variants.add(LookupElementBuilder.create(property). withIcon(SimpleIcons.FILE). withTypeText(property.getContainingFile().getName()) ); } } return variants.toArray(); } }

10.6.

定义参考贡献者

引用参与者允许您提供从其他语言(如Java)中的元素到您所用语言中的元素的引用.

让我们为每个属性的使用贡献一个参考.

package com.simpleplugin; import com.intellij.openapi.util.TextRange; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.*; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; public class SimpleReferenceContributor extends PsiReferenceContributor { @Override public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiLiteralExpression.class), new PsiReferenceProvider() { @NotNull @Override public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; if (value != null && value.startsWith("simple" + ":")) { return new PsiReference[]{ new SimpleReference(element, new TextRange(8, value.length() + 1))}; } return PsiReference.EMPTY_ARRAY; } }); } }

10.7.

注册参考贡献者

<psi.referenceContributor implementation="com.simpleplugin.SimpleReferenceContributor"/>

10.8.

运行该项目

如您所见,IDE现在解析了该属性并提供了完成功能.

参考贡献者

根据定义和用法重命名重构.

重命名

10.9.

定义重构支持提供程序

为了允许就地重构,我们应该在重构支持提供程序中明确指定它.

package com.simpleplugin; import com.intellij.lang.refactoring.RefactoringSupportProvider; import com.intellij.psi.PsiElement; import com.simpleplugin.psi.SimpleProperty; public class SimpleRefactoringSupportProvider extends RefactoringSupportProvider { @Override public boolean isMemberInplaceRenameAvailable(PsiElement element, PsiElement context) { return element instanceof SimpleProperty; } }

10.10.

注册重构支持提供程序

<lang.refactoringSupport language="Simple" implementationClass="com.simpleplugin.SimpleRefactoringSupportProvider"/>

10.11.

运行该项目

原位重命名

Last modified: 11 May 2019