跳转至

代码补全、检查与 Quick Fix

编辑器功能是插件最常见的价值入口。本章覆盖三类常用能力:代码补全、代码检查、Quick Fix。

代码补全

IntelliJ Platform 中常见补全有两种实现方式。

方式 适合场景 入口
Reference Completion 基于引用解析的基础补全 PsiReference.getVariants()
Contributor Completion 更可控的补全、关键词补全、类型匹配补全 CompletionContributor

CompletionContributor 示例

class MyCompletionContributor : CompletionContributor() {
    init {
        extend(
            CompletionType.BASIC,
            PlatformPatterns.psiElement().withLanguage(JavaLanguage.INSTANCE),
            object : CompletionProvider<CompletionParameters>() {
                override fun addCompletions(
                    parameters: CompletionParameters,
                    context: ProcessingContext,
                    result: CompletionResultSet
                ) {
                    result.addElement(
                        LookupElementBuilder.create("myKeyword")
                            .withTypeText("example")
                            .bold()
                    )
                }
            }
        )
    }
}

注册:

<extensions defaultExtensionNs="com.intellij">
  <completion.contributor
      language="JAVA"
      implementationClass="com.example.MyCompletionContributor"/>
</extensions>

补全建议:

  • 不依赖索引的补全可以标记为 Dumb Aware。
  • 显示项使用 LookupElementBuilder,可以设置图标、尾部文本、类型文本和插入处理器。
  • 模式匹配针对光标附近的叶子 PSI Element;匹配父节点时使用 withParent()withSuperParent()

代码检查

代码检查用于静态分析,不实际执行用户代码。局部检查通常一次处理一个文件,并可在用户编辑时实时运行。

最小组成:

  • LocalInspectionTool 或特定语言的基类。
  • PSI Visitor,用于遍历目标语法节点。
  • ProblemsHolder,用于登记问题。
  • 可选 LocalQuickFix,用于修复问题。
  • inspectionDescriptions/<ShortName>.html,用于设置页展示说明。

LocalInspectionTool 示例

class MyStringComparisonInspection : LocalInspectionTool() {
    override fun buildVisitor(
        holder: ProblemsHolder,
        isOnTheFly: Boolean
    ): PsiElementVisitor {
        return object : JavaElementVisitor() {
            override fun visitBinaryExpression(expression: PsiBinaryExpression) {
                val op = expression.operationTokenType
                if (op == JavaTokenType.EQEQ || op == JavaTokenType.NE) {
                    holder.registerProblem(
                        expression.operationSign,
                        "String comparison should use equals()",
                        ReplaceWithEqualsQuickFix()
                    )
                }
            }
        }
    }
}

注册:

<extensions defaultExtensionNs="com.intellij">
  <localInspection
      language="JAVA"
      shortName="MyStringComparison"
      displayName="String comparison uses =="
      groupName="Probable bugs"
      enabledByDefault="true"
      level="WARNING"
      implementationClass="com.example.MyStringComparisonInspection"/>
</extensions>

Quick Fix

Quick Fix 应尽量小而确定。它接收问题位置,在 Write Command 中修改 PSI。

class ReplaceWithEqualsQuickFix : LocalQuickFix {
    override fun getFamilyName(): String = "Replace with equals()"

    override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
        val element = descriptor.psiElement ?: return
        WriteCommandAction.runWriteCommandAction(project) {
            // create replacement PSI and replace the old expression
        }
    }
}

检查描述文件

路径约定:

src/main/resources/inspectionDescriptions/MyStringComparison.html

HTML 内容应解释:

  • 检查发现什么问题。
  • 为什么这是问题。
  • Quick Fix 会做什么。
  • 哪些情况可能误报。

参考来源