组成部分
宿主内部如何组织,见贡献者文档中的 插件宿主。
World、导入与导出
插件 API 是一个 WIT 包。基础 worldplugin 列出组件可以导入的宿主 interface(插件调用它们),以及组件必须导出的插件 interface(IDE 调用它们):
- 导入:
log、ui、storage、fs、workspace、editor、decorations、invocation、vcs、commit、events、tasks、diagnostics、net、secrets、process、ipc、output、auth、command-runner、views、run、run-targets、database、mcp、settings。 - 导出:
lifecycle(activate、deactivate)、commands(执行命令)、event-handler(处理一批事件)。
plugin 并多一个导出。只有清单声明了对应的贡献时,插件才需要导出它:
在 Rust 里把参数列出来即可:
register_plugin!(MyPlugin, providers, views)。每个可选 world 还有一个只含导出的孪生 world(syntax、editor-providers、views-provider 等),由组件工具与 plugin 合并。
双向调用
-
IDE → 插件(导出):用户执行命令时 IDE 调用
commands.run;有订阅的事件时调用event-handler.handle并传入一批事件;编辑器需要补全、悬停等时调用 provider 函数;绘制视图时调用view-provider.render。 -
插件 → IDE(导入):在上述任一调用中,插件可以调用
editor.text-editor.document、workspace.project.read-file、net.fetch等宿主函数。缺少能力时宿主函数返回error::denied("<capability>"),插件继续运行,可以降级处理。 1.x 的所有调用都是同步的:宿主调用返回前插件实例一直阻塞;编辑器 provider 每次调用都会收到完整的文档文本。位置坐标是从 0 开始的行号和 UTF-16 列,与编辑器和 LSP 一致(syntax provider 使用 UTF-8 字节偏移,由 IDE 转换)。
沙箱
每个插件都有自己的 wasmtimeStore:
- 没有预打开的目录、没有环境变量、没有网络,也不继承 stdin/stdout;
- WASI 只提供时钟和随机数;
- stderr 会被捕获:
eprintln!的输出写进 IDE 日志,panic 的信息会随崩溃一起显示。
<iframe sandbox="allow-scripts"> 中,从 IDE 自己的 openidea-plugin URL scheme 加载:opaque origin,没有网络、Cookie 或存储,没有 IDE API,只能加载页面自己的文件。页面通过 postMessage 与插件代码通信。
限制
每次调用插件都有上限:
等待用户操作(选择器、输入框、确认框)的时间不计入时间限制。各个宿主函数还有自己的上限(文件大小、句柄数、响应大小等),详见
plugins/README.md。
线程与崩溃
每个活跃插件运行在自己的线程上(一个持有该插件运行时的 actor)。调用方把任务排进队列并等待回复,所以同一实例内的调用永远不会并发。编辑器调用方可以在软截止时间放弃等待,而调用本身继续执行到硬截止时间;任何线程都可以取消调用。墙钟限制通过 wasmtime 的 epoch 中断实现。 panic、trap、权限违规或超出限制都会丢弃该实例。用户只会收到一次提示,下次需要时插件重新启动;连续崩溃时重启前的等待时间逐次翻倍(1 秒、2 秒……最长 60 秒)。连续崩溃三次后,插件在本次会话中被标记为 失败,设置 › 插件 中会显示原因;点 重启、关闭再打开,或 从磁盘重新加载 都能让它重新启动。只要有一次调用正常返回,计数就会清零。插件崩溃不会影响 IDE 本身。 设置 › 插件 会显示每个插件的激活次数、近期调用延迟(p95)、崩溃次数、超时的编辑器结果和空闲回收次数。远程项目
插件代码在项目所在的机器上运行。窗口打开远程项目时,桌面端会把已启用的代码插件及其授权发送给openidea-server,由它在文件旁边运行:文件读取、搜索、进程、网络请求和诊断都作用于远程机器,process: 程序也在那里解析。密钥始终留在桌面端的钥匙串中,服务器在请求需要时向桌面端索取。远程插件的视图、通知、状态栏项和终端照常显示在窗口中。
目前的限制:项目插件(.openidea/plugins)还不能在远程项目中运行;插件在服务器上的存储初始为空。