- 声明式地添加语言。
[[contributes.languages]]提供文件识别、注释、缩进、语言服务器和调试器, 不需要写代码。[[contributes.snippets]]提供实时模板。 - 为 IDE 没有语法的语言提供语法。 可选的
syntax-provider导出返回高亮、结构和折叠。 - 编辑器提供者(provider),适用于任何语言,内置语言也可以:补全、悬停、跳转到定义、行号槽标记、
语义 token、嵌入提示、格式化、代码透镜等。在
[[contributes.providers]]下声明。
声明提供者
activation。
宿主只调用你声明过的类型(kind)。提供者需要代码(wasm),但不需要额外权限:文档会随调用一起传入。
在提供者里调用的宿主函数仍按原有规则检查权限。
多个导出可以一次注册:
register_plugin!(MyPlugin, providers, hover, language_features)。
每个 trait 方法都有返回空结果的默认实现,只实现声明过的即可。
最小示例
Document:绝对路径 path、编辑器语言 id language、版本号 version,
以及包含未保存修改的完整 text。位置使用从 0 开始的行号和 UTF-16 列号;
openidea_plugin::text::offset 和 text::position 用于与字节偏移互相转换。
时间预算
编辑器提供者运行在编辑路径上:- 超过约 50 ms 的结果会被丢弃,500 ms 时仍在运行的调用会被中断。同一类型的所有插件同时被调用, 各自都有完整的预算。
- 提供者与插件的命令、事件、视图共用同一个实例:能看到同一份内存,但也要排在它们后面。排在长命令之后的调用 会像慢调用一样在 50 ms 时被丢弃,所以命令和事件处理要尽量短。
inline-completion不受 50 ms 限制(可能要访问网络),但用户继续输入时会立即取消。 可以检查Task::is_cancelled,或者交给宿主中断。formatting和代码操作的延迟解析使用 5 秒的命令预算。
补全、内联补全、导航与行号槽标记
这些都是providers::Providers(WIT providers)的方法:
completion(doc, at, trigger)返回CompletionItem,与语言服务器的结果合并显示。kind(CompletionKind)决定图标。补全项可以是snippet($1、${1:name}、$0),可以替换replace范围, 按filter_text而不是标签匹配,按sort_text排序,带documentation,还能用additional_edits修改文档其他位置(比如加 import)。inline_completion(doc, at)在光标处返回幽灵文本(InlineSuggestion { insert_text, replace })。 可参考 AI Completion。definition(doc, at)返回Location列表,与语言服务器的结果合并,用于跳转到声明(⌘B/Ctrl+B)。line_markers(doc)返回行号槽图标,带tooltip和可选的点击跳转目标target。
definition、line-markers 和 completion。
标记图标。 icon 可以是图标名(包括已注册的插件图标)、别名 navigate(默认)、implemented、
implementing、overridden、overriding、run,或者声明为标记图标的包内 SVG(API 1.3):
icon: Some("icons/resource.svg".into()) 即可使用。标记 SVG 最大 64 KiB,与视图图标一样经过净化;
未知图标会回退为导航图标。如果点击时要执行命令而不是跳转,请用下文的 gutter-actions。
语义 token
semantic-tokens(API 1.3)为声明语言的文档着色,也包括插件没有贡献的语言,比如 php:
悬停
hover(API 1.3)在悬停提示中追加 Markdown。宿主同时询问语言服务器和插件,先显示服务器的内容,
再用分隔线依次显示各插件的内容。Hover::new(markdown).range(range) 指明悬停描述的文本范围(服务器没给范围时使用)。
每个插件最多 64 KiB Markdown。
嵌入提示
inlay-hints(API 1.3)在代码中显示简短标签:
range 是文档的可见部分,范围外的提示会被丢弃。kind(Type、Parameter 或 Other)决定由哪个
嵌入提示 设置控制显示。.padding_left() 和 .padding_right() 添加空格。标签截断到 256 字节,
提示文字截断到 1 KiB,每次调用最多 10,000 个。
更多语言功能(API 1.4)
声明engines.openidea = "^1.4" 后可以使用下列类型,实现 language_features::LanguageFeatures
并注册 language_features:
下面是 Blame Annotations
示例中提供命令入口的代码透镜:
gutter-actions。
所有这些功能共同的规则:
- 语言服务器缺失或不可用时,插件结果照常生效;服务器的其他错误仍按错误处理。
document-symbol.parent是返回的扁平列表中的下标。父节点必须排在前面并包含子节点的范围; 无效的父节点会变成根节点,层级最多 64 层。- 每个列表最多 10,000 项,每个字符串最多 64 KiB;无效范围会被丢弃。格式化编辑必须有效且互不重叠。
- 禁用已选定的格式化插件后,会报告为不可用,而不是悄悄换用其他格式化工具。
嵌入语言(injections)
injections(API 1.4)标出宿主文档中属于另一种语言的范围,比如字符串字面量里的 SQL。
为宿主语言声明这个类型,实现 Injections 并注册 injections:
- 用嵌入语言的解析器和当前配色方案为每个范围着色,编辑后 300 ms 刷新。
- 把补全、悬停、诊断以及 API 1.4 的各项功能转给嵌入语言的插件提供者。传入的是一份虚拟文档: 保留原路径和坐标,范围外的文本替换为空白。
- 如果嵌入语言配置了语言服务器,还会把补全、悬停、签名帮助、导航、引用、高亮和代码操作转给它; 每个范围有一份独立的内存中同级文档(不会写入磁盘)。格式化和重命名仍由宿主语言的服务器处理。
为插件语言提供语法
对于插件贡献、但 IDE 没有 tree-sitter 语法的语言,可以导出syntax-provider(world plugin-syntax)。
这里的偏移是文本中的 UTF-8 字节范围,由 IDE 负责转换。
activation = ["on-language:<id>"],让组件在打开第一个文件时加载。结果会被校验:裁剪到文本范围内,
重叠部分丢弃。完整示例见 Solidity。
贡献一门语言
languages/<id>.toml 相同):
- 插件语言和内置语言一样加入语言注册表:文件识别(
extensions、filenames、first-line)、注释、缩进、ui-mode、framework-ui-modes,以及其中声明的language-servers和debugger。 - 每个语言服务器或调试器命令都会变成一个可选的
process:<program>权限。拒绝它不影响插件其他部分。 - 插件不能覆盖内置或用户定义:它们的 id、扩展名和文件名优先。用户仍可以在 设置 › 文件类型 中把文件映射到插件语言。
- 在远程项目中,语言定义会发送到服务器,由服务器在那边启动语言服务器。
openidea-plugin new my-lang --template language 可以创建一个纯数据的语言插件。
代码片段
prefix 和 body 可以是字符串或字符串数组(body 各行以换行连接)。$1、${1:default} 和结尾的 $0
会成为联动的实时模板字段;\$ 表示字面量美元符号。片段会出现在补全中,可按 Tab 展开,并以只读形式按插件分组列在
设置 › 编辑器 › 实时模板 中。不需要代码或权限。限制:每个文件 1 MiB、1,000 个条目、每条 32 个前缀、每个 body 64 KiB。
测试
在本机运行时 SDK 调用的是模拟宿主,所以可以在cargo test 中直接调用 trait 方法。
MockHost::provider_document() 返回模拟编辑器当前未保存的快照。参见 测试。