自定义语言支持¶
IntelliJ Platform 的语言能力由平台通用部分和语言特定部分共同组成。为一门新语言开发插件时,你主要实现语言特定部分,平台会提供编辑器、项目模型、检查框架、补全框架、导航框架等基础设施。
开发顺序¶
一个自定义语言插件通常按这个顺序推进:
- 注册文件类型。
- 实现 Lexer。
- 实现 Parser 与 PSI。
- 支持语法高亮与错误高亮。
- 实现引用解析与导航。
- 实现补全。
- 实现格式化、查找用法、重命名、安全删除。
- 增加检查、意图动作、文档、结构视图、Inlay Hint 等编辑器体验。
不要一开始就做所有 IDE 能力。先让文件被识别、能解析、能定位 PSI,再逐步叠加功能。
注册文件类型¶
文件类型让 IDE 知道哪些文件属于你的语言:
<extensions defaultExtensionNs="com.intellij">
<fileType
name="Example"
language="Example"
implementationClass="com.example.ExampleFileType"
fieldName="INSTANCE"
extensions="example"/>
</extensions>
文件类型类通常包含名称、描述、默认扩展名和图标。
Lexer、Parser 与 PSI¶
语言插件的核心是把文本变成结构:
- Lexer:把字符流拆成 token。
- Parser:把 token 组织成语法树。
- PSI:在语法树之上提供语义化 API。
简单语言可以使用 Grammar-Kit 生成 Parser 和 PSI 样板。复杂语言可能需要手写部分解析逻辑或桥接已有解析器。
引用解析¶
引用解析决定 IDE 能否从使用位置跳到声明位置:
class ExampleReference(element: PsiElement, range: TextRange) :
PsiReferenceBase<PsiElement>(element, range) {
override fun resolve(): PsiElement? {
return findDeclaration(element.project, value)
}
override fun getVariants(): Array<Any> {
return collectDeclarations(element.project).toTypedArray()
}
}
resolve() 支持跳转和重命名基础能力;getVariants() 可为基础补全提供候选项。
补全与导航¶
基础补全可以来自 Reference,复杂补全使用 CompletionContributor。导航能力通常围绕:
PsiNamedElementPsiReferenceChooseByNameContributorGotoDeclarationHandler- Stub Index
当项目变大后,不要全项目遍历 PSI 查声明,应通过索引查找。
重构支持¶
最常见的重构能力:
- Rename:声明和引用都要正确更新。
- Find Usages:引用查找结果要准确。
- Safe Delete:删除前检查是否仍被引用。
这些能力通常建立在稳定的 PSI、Reference 和索引之上。
编辑器体验¶
可逐步补充:
- Syntax Highlighting:词法级颜色。
- Annotator / ExternalAnnotator:语义级高亮。
- Formatter:格式化和缩进。
- DocumentationProvider:悬浮文档。
- StructureViewBuilder:结构视图。
- ParameterInfoHandler:参数提示。
- InlayHintsProvider:内嵌提示。
与 LSP 的关系¶
如果已有成熟 Language Server,可以考虑接入 LSP;如果你需要深度集成 IntelliJ 的重构、索引、项目模型、检查和 UI,则原生语言插件通常更可控。