Code Insight 细节专题:Inlay、Documentation、Intentions 与 Templates¶
代码补全、检查与 Quick Fix 覆盖了 Code Insight 的主入口。本章补齐官方目录中更细的能力:Inlay Hints、Documentation、Intentions、Inspection Options、Live Templates 和 Postfix Templates。它们共同决定插件是否像 IDE 原生能力,而不是只在编辑器里显示一条 warning。
核对日期:2026-06-18。
能力选择¶
| 目标 | 首选能力 | 适合场景 |
|---|---|---|
| 在代码旁显示轻量信息 | Inlay Hints / Code Vision | 参数名、推断类型、引用数、作者、指标 |
| 展示符号说明 | Documentation Target API | Quick Documentation、hover doc、文档工具窗口 |
| 提供上下文操作 | Intention Action | 光标处可触发的代码转换或辅助动作 |
| 报告代码问题并修复 | Inspection + Quick Fix | 可持续分析、可配置 severity 和 scope |
| 插入重复代码模式 | Live Templates | 用户主动输入缩写或 Surround With |
| 基于表达式后缀展开 | Postfix Templates | expr.if、expr.var、expr.notnull 一类代码生成 |
不要把所有编辑器增强都做成 inspection。Inspection 会进入检查配置、批量分析和 problem view;只在光标处出现的动作更适合 intention;只提供视觉提示的能力更适合 inlay 或 code vision;纯代码片段生成更适合 templates。
Inlay Hints¶
Inlay hints 是编辑器中的轻量内嵌信息。官方按展示方式分为:
- inline:显示在代码 token 之间。
- block:显示在代码块上方或行末,常用于 Code Vision。
常见 API 选择:
| API | 状态与用途 |
|---|---|
InlayParameterHintsProvider |
简单参数名提示,只能展示字符串 inline hint |
Declarative InlayHintsProvider |
2023.1+ 推荐的 inline hint API,UI 无关,适配不同 frontend |
DaemonBoundCodeVisionProvider |
Code Vision 与 PSI 相关时使用,PSI 变化后自动重新计算 |
CodeVisionProvider |
Code Vision 与 PSI 无关时使用,例如 VCS 或外部指标 |
Legacy InlayHintsProvider |
需要自定义 presentation/behavior 时使用;inline 场景优先考虑 declarative API |
Declarative inlay provider 注册思路:
<extensions defaultExtensionNs="com.intellij">
<codeInsight.declarativeInlayProvider
language="JAVA"
providerId="example.implicit.types"
implementationClass="com.example.ExampleInlayProvider"/>
</extensions>
预览文件必须放在资源目录:
src/main/resources/inlayProviders/example.implicit.types/preview.java
其中目录名要和 providerId 一致。预览里的提示用官方 markup:
var value /*<# String #>*/ = "text";
实现原则:
- Inlay 内容必须短,不替代 Quick Documentation。
- 不在 provider 中做昂贵全项目搜索;需要引用数这类指标时使用 Code Vision,并做好缓存/取消。
- 对 PSI 相关 Code Vision 使用
DaemonBoundCodeVisionProvider。 - Settings | Editor | Inlay Hints 中要有可理解的名称、说明和 preview。
- 用 UI Inspector 检查已有 inlay 的 provider 和设置项。
Documentation Target API¶
自 IntelliJ Platform 2023.1 起,官方推荐使用 Documentation Target API。旧 DocumentationProvider API 已被标为 deprecated,但自定义语言教程仍保留旧 API 以兼容迁移。
三类入口:
| EP | 用途 |
|---|---|
DocumentationTargetProvider |
根据当前 editor offset 和 PsiFile 返回文档目标 |
PsiDocumentationTargetProvider |
根据 PSI element 返回文档目标 |
SymbolDocumentationTargetProvider |
根据 Symbol API 返回文档目标 |
核心对象是 DocumentationTarget:
computeDocumentation()返回 HTML 文档,可异步。computeDocumentationHint()返回 hover 或 quick navigate 的短提示。createPointer()用于跨 read action 恢复目标;PSI 失效时应返回null。
旧 API 对照:
旧 DocumentationProvider |
新 API 思路 |
|---|---|
generateDoc() |
DocumentationTarget.computeDocumentation() |
getQuickNavigateInfo() |
computeDocumentationHint() |
getCustomDocumentationElement() |
Target provider 中选择正确目标 |
generateHoverDoc() |
target 的 hint/doc 分流 |
getDocumentationElementForLookupItem() |
completion lookup item 应能映射到 PSI/Symbol target |
注册旧 API 时常见形式:
<extensions defaultExtensionNs="com.intellij">
<lang.documentationProvider
language="Example"
implementationClass="com.example.ExampleDocumentationProvider"/>
</extensions>
若要跨语言提供文档,例如在 Java string literal 里解析自定义 key,需要使用更通用的 com.intellij.documentationProvider,并在 getCustomDocumentationElement() 或新 target provider 中自行解析引用。
文档内容建议:
- 第一屏展示类型/签名/定义位置。
- 详细说明和示例放在 content/sections。
- 外部文档 URL 可以提供,但不要让网络失败影响本地 Quick Documentation。
- HTML 要简洁,避免依赖复杂脚本或外部资源。
- 大文档或远程文档使用异步
DocumentationResult。
Intentions 与 Preview¶
Intention 是用户按 Alt+Enter 时出现的上下文动作。它和 Quick Fix 的差别在于:Intention 不一定来自问题报告,也不一定表示代码有错。
典型实现:
class ConvertExampleIntention : PsiElementBaseIntentionAction() {
override fun getFamilyName(): String = "Convert example"
override fun getText(): String = "Convert example syntax"
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean {
return findTarget(element) != null
}
override fun invoke(project: Project, editor: Editor?, element: PsiElement) {
val target = findTarget(element) ?: return
WriteCommandAction.runWriteCommandAction(project) {
// Replace target PSI with new PSI.
}
}
}
资源文件约定:
src/main/resources/intentionDescriptions/ConvertExampleIntention/description.html
src/main/resources/intentionDescriptions/ConvertExampleIntention/before.example.template
src/main/resources/intentionDescriptions/ConvertExampleIntention/after.example.template
如果类名会被混淆,或多模块打包可能造成资源重名,应在 plugin.xml 的 <intentionAction> 中声明 descriptionDirectoryName。不需要 before/after preview 时可设置 skipBeforeAfter。
Preview 要点:
- 平台会在非物理文件副本和 headless editor 中尝试执行 intention/quick fix,生成 diff。
- 默认 preview 适合只修改当前文件、无需用户输入、无外部副作用的动作。
- 如果修改其他文件、弹出 UI、启动外部进程或依赖真实 editor,应 override
generatePreview()。 - 可返回
IntentionPreviewInfo.Html、CustomDiff、DIFF或EMPTY。 - 使用
checkPreviewAndLaunchAction()、checkIntentionPreviewHtml()、getIntentionPreviewText()测试 preview。
常见 preview 失败原因:
- action 持有真实
PsiElement并修改它,而不是 remap 到文件副本。 - 在 preview 中调用
Application.invokeLater()。 - 依赖
PsiDocumentManager.getDocument(psiFile),但非物理文件不支持该路径。 - 修改当前文件之外的 references/search 结果。
- 对 mock editor 未实现的能力做假设,例如复杂 folding 操作。
Inspection Options¶
如果 inspection 需要配置项,2023.1+ 优先使用 declarative options,而不是 Swing panel。
推荐入口:
class ExampleInspection : LocalInspectionTool() {
@JvmField
var ignoreGeneratedCode: Boolean = true
override fun getOptionsPane(): OptPane {
return pane(
checkbox("ignoreGeneratedCode", "Ignore generated code")
)
}
}
Declarative options 的优势:
- 平台统一渲染,符合 UI guidelines。
- 可以在 inspection panel 以外的地方读取和修改。
- 对不同 frontend 更友好。
- bind id 可被 DevKit resolve,减少字段重命名后的隐藏错误。
复杂场景可以通过 OptionController 自定义读写逻辑。仍需要 Swing 时,使用 createOptionsPanel(),但注意:从 2023.1 起,如果 getOptionsPane() 返回非空面板,createOptionsPanel() 会被忽略。
Live Templates¶
Live Templates 适合用户主动输入缩写后展开代码,或在 Surround With 中包裹选区。可由用户创建、导出,再随插件打包。
典型资源:
<templateSet group="Example">
<template
name="ex"
value="example($ARG$)$END$"
description="Create example call"
toReformat="true"
toShortenFQNames="true">
<variable name="ARG" expression="complete()" defaultValue=""value"" alwaysStopAt="true"/>
<context>
<option name="EXAMPLE_CODE" value="true"/>
</context>
</template>
</templateSet>
注册:
<extensions defaultExtensionNs="com.intellij">
<defaultLiveTemplates file="/liveTemplates/Example.xml"/>
<liveTemplateContext
contextId="EXAMPLE_CODE"
implementation="com.example.ExampleTemplateContext"/>
</extensions>
TemplateContextType 决定模板在哪里可用:
class ExampleTemplateContext : TemplateContextType("EXAMPLE_CODE", "Example code") {
override fun isInContext(templateActionContext: TemplateActionContext): Boolean {
return templateActionContext.file.fileType == ExampleFileType
}
}
Live Template XML 关键属性:
| 属性 | 作用 |
|---|---|
name |
用户输入的缩写 |
value |
展开内容,变量用 $NAME$ |
shortcut |
TAB、SPACE、ENTER、CUSTOM、NONE |
toReformat |
插入后按 code style 格式化 |
toShortenFQNames |
插入后尝试缩短全限定名/导入 |
resource-bundle + key |
本地化 description |
自定义函数本质是 macro。实现 MacroBase 并注册:
<extensions defaultExtensionNs="com.intellij">
<liveTemplateMacro implementation="com.example.TitleCaseMacro"/>
</extensions>
实现建议:
- 先在 IDE 中手工创建和导出模板,再打包进插件,减少 XML 错误。
- 优先复用内置 template contexts,不要为已有语言重复定义上下文。
- description 应说明用户看到的动作,不写实现细节。
- 多模块打包时确保 template 文件路径唯一。
Postfix Templates¶
Postfix Templates 在 completion 过程中出现。用户输入表达式后缀,例如 value.notnull,平台询问当前语言的 PostfixTemplateProvider,筛选适用模板并展开。
注册:
<extensions defaultExtensionNs="com.intellij">
<codeInsight.template.postfixTemplateProvider
language="Example"
implementationClass="com.example.ExamplePostfixTemplateProvider"/>
</extensions>
简单模板可直接继承 PostfixTemplate,实现:
isApplicable():当前 context 是否可用。expand():如何改写 editor/document/PSI。
更复杂场景:
| 需求 | 推荐基类/机制 |
|---|---|
| 多个表达式可选 | PostfixTemplateWithExpressionSelector |
| 可用 Live Template 语法表达 | StringBasedPostfixTemplate |
| 用户可在 Settings 中编辑 | editable postfix template 相关基类 |
| 复用 Surround With | SurroundPostfixTemplateBase |
资源文件必须提供描述和 before/after 示例:
src/main/resources/postfixTemplates/IntroduceVariablePostfixTemplate/description.html
src/main/resources/postfixTemplates/IntroduceVariablePostfixTemplate/before.example.template
src/main/resources/postfixTemplates/IntroduceVariablePostfixTemplate/after.example.template
示例中可以用 <spot> 标记重点区域:
<spot>items</spot>.notnull
也可以用 $key 表示当前 postfix key。多模块打包时同样要避免 description 目录重名。
测试矩阵¶
| 能力 | 最低测试 |
|---|---|
| Inlay Hints | provider 开关、preview 文件、PSI 改变后刷新、Dumb Mode 行为 |
| Documentation | offset/PSI/Symbol target、hover、lookup item documentation、失效 PSI pointer |
| Intentions | isAvailable、invoke、description、before/after、preview |
| Inspection Options | 默认值、持久化、options UI、quick fix 修改配置 |
| Live Templates | context 生效、变量跳转顺序、reformat/shorten、Surround With |
| Postfix Templates | applicability、多个表达式选择、description 示例、展开后 caret |