跳转到主要内容
插件可以在三个层次上扩展编辑器:
  1. 声明式地添加语言。 [[contributes.languages]] 提供文件识别、注释、缩进、语言服务器和调试器, 不需要写代码。[[contributes.snippets]] 提供实时模板。
  2. 为 IDE 没有语法的语言提供语法。 可选的 syntax-provider 导出返回高亮、结构和折叠。
  3. 编辑器提供者(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。
MyBatis Navigator 在 Java 和 XML 中使用了 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:
每个 token 覆盖一行中的一段,带 LSP token 类型和修饰符(WIT 中为 kebab-case 字符串)。插件 token 与语言服务器的 合并,重叠处以插件为准,所以配色方案会像对待服务器 token 一样为它们着色。每次编辑后都会重新请求;每个文档最多 65,536 个。

悬停

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 示例中提供命令入口的代码透镜:
Test Runner 演示了 gutter-actions。 所有这些功能共同的规则:
  • 语言服务器缺失或不可用时,插件结果照常生效;服务器的其他错误仍按错误处理。
  • document-symbol.parent 是返回的扁平列表中的下标。父节点必须排在前面并包含子节点的范围; 无效的父节点会变成根节点,层级最多 64 层。
  • 每个列表最多 10,000 项,每个字符串最多 64 KiB;无效范围会被丢弃。格式化编辑必须有效且互不重叠。
  • 禁用已选定的格式化插件后,会报告为不可用,而不是悄悄换用其他格式化工具。

嵌入语言(injections)

injections(API 1.4)标出宿主文档中属于另一种语言的范围,比如字符串字面量里的 SQL。 为宿主语言声明这个类型,实现 Injections 并注册 injections:
宿主会这样处理这些范围:
  • 用嵌入语言的解析器和当前配色方案为每个范围着色,编辑后 300 ms 刷新。
  • 把补全、悬停、诊断以及 API 1.4 的各项功能转给嵌入语言的插件提供者。传入的是一份虚拟文档: 保留原路径和坐标,范围外的文本替换为空白。
  • 如果嵌入语言配置了语言服务器,还会把补全、悬停、签名帮助、导航、引用、高亮和代码操作转给它; 每个范围有一份独立的内存中同级文档(不会写入磁盘)。格式化和重命名仍由宿主语言的服务器处理。
限制:每个文档最多 1,000 个互不重叠的范围、32 种嵌入语言;超过 2 MiB 的文档不处理;嵌入不会递归。

为插件语言提供语法

对于插件贡献、但 IDE 没有 tree-sitter 语法的语言,可以导出 syntax-provider(world plugin-syntax)。 这里的偏移是文本中的 UTF-8 字节范围,由 IDE 负责转换。
它运行在编辑路径上,每个文档版本只调用一次(有缓存),所以要快;通常手写一个词法分析器即可。 加上 activation = ["on-language:<id>"],让组件在打开第一个文件时加载。结果会被校验:裁剪到文本范围内, 重叠部分丢弃。完整示例见 Solidity。

贡献一门语言

文件使用 IDE 自己的 语言定义格式(与仓库中的 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() 返回模拟编辑器当前未保存的快照。参见 测试。