diff --git a/README.md b/README.md index 8f94ada..f80c2b3 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ -# Lithe 插件开发 +# Lithe 插件开发 / Lithe plugin development -> English: The entry point for building [Lithe](https://github.com/1lck/Lithe-IDEA) plugins: -> guides, the full plugin contract, the official SDKs, and example plugins. Writing, -> testing, and publishing a plugin happens in your own repository; you never need to -> clone the Lithe repository or send it a pull request. +[中文](#中文) · [English](#english) -写插件、测试插件、发布插件,都在**你自己的仓库**里完成。不需要克隆 Lithe 主仓库,也不需要给它提 PR。 +## 中文 + +写插件、测试插件、发布插件,都在**你自己的仓库**里完成。不需要克隆 [Lithe](https://github.com/1lck/Lithe-IDEA) 主仓库,也不需要给它提 PR。 | 想做什么 | 看哪里 | | --- | --- | @@ -16,9 +15,10 @@ | 需要自己的后端进程 | [插件后端 SDK](#插件后端-sdk可选) | | 接入宿主日志、排查插件故障 | [插件接入统一日志](guides/plugin-logging.md) | | 参考现成插件 | [示例插件](#示例插件) | +| 查 API-01–44 现在能用什么、每种能力和协议有哪些版本、各宿主支持到哪一版 | [插件参考](reference/README.md) | | 查字段的准确含义 | [插件契约](#插件契约) | -## 插件是什么 +### 插件是什么 一个 Lithe 插件就是一个目录:`plugin.json` 声明插件提供什么,其余文件是它用到的资源,例如语言服务器、脚本和图标。 @@ -37,7 +37,7 @@ Lithe 读取并校验 `plugin.json`,然后自己启动其中声明的外部进 - 包内路径只用 `/`,不能有 `..`、绝对路径或符号链接。 - 插件包安装后是只读的。缓存和状态使用 Lithe 通过启动模板传入的 `${stateDir}`、`${workspaceStateDir}`;诊断日志使用 SDK 与宿主统一入口。确需原生文件日志时,先确认目标宿主支持 `${logDir}`,详见[插件接入统一日志](guides/plugin-logging.md)。 -## 五步做出第一个插件 +### 五步做出第一个插件 1. **复制模板。** 以 [Lua 插件](https://github.com/Lithe-IDEA/lithe-plugin-lua) 为起点,它是 Lithe Store 的示例和模板。 2. **改 `plugin.json`。** 换成你自己的 ID、名称、语言和语言服务器。先按[下一节](#在编辑器里校验-pluginjson)配置编辑器,写错字段时会直接提示。 @@ -47,7 +47,7 @@ Lithe 读取并校验 `plugin.json`,然后自己启动其中声明的外部进 需要运行、测试或构建工具集成时,再读 [构建、运行与测试接入指南](guides/build-tool-integration.md)。 -### 选 API 等级 +#### 选 API 等级 `pluginAPIVersion` 决定 Lithe 需要支持哪些功能才能安装你的插件。选能满足需求的最低等级,老版本 Lithe 也能装。 @@ -60,11 +60,11 @@ Lithe 读取并校验 `plugin.json`,然后自己启动其中声明的外部进 三个版本号不要混淆:`schemaVersion` 永远是 `2`,表示清单格式;`pluginAPIVersion` 是上表的等级;`version` 是你自己插件的版本。 -API 3 及以上必须在 `requires` 里逐项列出实际用到的能力。例如只用声明式构建系统时写 `"requires": { "buildSystems": 1 }`,不要多写。 +API 3 及以上必须在 `requires` 里逐项列出实际用到的能力。例如只用声明式构建系统时写 `"requires": { "buildSystems": 1 }`,不要多写。每个能力有哪些版本、各宿主支持到哪一版,见[能力与协议版本对照](reference/capabilities-and-versions.md)。 API 4 需要 Lithe 明确支持 API 4:旧版 Lithe 在插件市场里会提示升级,而不是安装。目前只有 macOS 宿主实现了调试适配器。 -## 在编辑器里校验 plugin.json +### 在编辑器里校验 plugin.json 契约里的两份 JSON Schema 地址固定: @@ -96,7 +96,7 @@ JetBrains IDE:设置 → Languages & Frameworks → Schemas and DTDs → JSON Schema 只检查 JSON 结构。ID 引用、权限、激活事件、模板变量这类规则由 Lithe 在安装时检查,规则写在[插件契约](#插件契约)里。 -## 插件后端 SDK(可选) +### 插件后端 SDK(可选) 只用语言服务器、运行和测试的插件不需要后端。需要构建后端(`buildProviders`)、插件函数,或给插件自己的网页配一个后端进程(`backend`)时,用官方 SDK 写:协议、取消、进程清理和错误处理由 SDK 负责,你只写处理函数。 @@ -106,13 +106,13 @@ Schema 只检查 JSON 结构。ID 引用、权限、激活事件、模板变量 | [Java](sdk/java/README.md) | Java 8+ | `sdk-java-v<版本>` | `lithe-plugin-sdk-java-<版本>.jar` 和 `.sha256` | shade 进后端 JAR | | [Rust](sdk/rust/README.md) | Rust 1.87+ | `sdk-rust-v<版本>` | `lithe-plugin-sdk-rust-<版本>.crate` 和 `.sha256` | 用 Git 依赖锁定标签(`Cargo.lock` 记录 commit),或解压后作为 `path` 依赖;编译成每个平台各一份的插件后端可执行文件 | -SDK 的源码都在本仓库的 `sdk/` 下,发布在[本仓库的 Releases](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases) 里,不在 npm、Maven 或 crates.io 上。Rust SDK 目前只实现插件后端(`backend`):插件函数和宿主服务(插件函数协议 1–3);构建后端请用 Node.js 或 Java SDK,VCS 后端请用 Node.js SDK。Java SDK 0.1.0 是搬到这里之前从 Lithe 主仓库发布的,[那个 Release](https://github.com/1lck/Lithe-IDEA/releases/tag/sdk-java-v0.1.0) 继续可用,已经锁定它的插件不用改。 +SDK 的源码都在本仓库的 `sdk/` 下,发布在[本仓库的 Releases](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases) 里,不在 npm、Maven 或 crates.io 上。Rust SDK 目前只实现插件后端(`backend`):插件函数和宿主服务(插件函数协议 1–3);构建后端请用 Node.js 或 Java SDK,VCS 后端请用 Node.js SDK。各 SDK 实现的协议版本见[宿主与官方 SDK 对照](reference/capabilities-and-versions.md#宿主与官方-sdk-对照)。Java SDK 0.1.0 是搬到这里之前从 Lithe 主仓库发布的,[那个 Release](https://github.com/1lck/Lithe-IDEA/releases/tag/sdk-java-v0.1.0) 继续可用,已经锁定它的插件不用改。 规则只有一条:**在构建时下载,并按你锁定的 SHA-256(或 Git commit)校验;运行时不下载任何东西。** 插件包安装后没有 `npm install` 步骤,也必须能离线运行。 [Maven 插件](https://github.com/Lithe-IDEA/lithe-plugin-maven) 的 `sdk.lock.json` 是锁定 Java SDK 的现成例子。 -## 示例插件 +### 示例插件 | 插件 | API | 示范什么 | | --- | --- | --- | @@ -124,39 +124,52 @@ SDK 的源码都在本仓库的 `sdk/` 下,发布在[本仓库的 Releases](ht | [Cargo](https://github.com/Lithe-IDEA/lithe-plugin-cargo) | 3 | 用 Node.js SDK 写的构建后端和插件设置 | | [LLDB](https://github.com/Lithe-IDEA/lithe-plugin-lldb) | 4 | 调试适配器的参考实现;用 Xcode 自带的 `lldb-dap` 调试 C、C++、Swift 和 Rust,含 `executable` 查找和调试控制台补全 | -## 教程 +### 教程 | 教程 | 内容 | | --- | --- | | [LSP 插件完整生命周期](guides/lsp-plugin-lifecycle.md) | 从 Lua 模板开始:清单、服务器打包、激活、状态目录、停止、升级与卸载 | | [插件接入统一日志](guides/plugin-logging.md) | 三 SDK 的安全失败边界、受管诊断通道、macOS v2 / Windows v1 状态,以及 `${logDir}` 的本地会话生命周期 | -| [原生构建工作流(开发中)](guides/native-build-workflows.md) | Node/Java SDK 的构建执行、精确测试、运行准备,以及未提交源码的真实 Core 验证 | -| [构建、运行与测试接入](guides/build-tool-integration.md) | 可用的执行提供者示例、工程后端边界,以及开发中的 API 3 状态 | +| [原生构建工作流](guides/native-build-workflows.md) | Node/Java SDK 的构建执行、精确测试、运行准备,以及未提交源码的真实 Core 验证 | +| [构建、运行与测试接入](guides/build-tool-integration.md) | 执行提供者示例、工程后端边界,以及 API 3 构建系统的现状和已发布示例 | | [编写调试适配器插件](guides/debug-adapter-plugin.md) | API 4:从最小清单到启动方式、先编译再调试的 `executable`、调试控制台补全,以及预发布与转正 | -| [插件后端:工作区上下文与宿主服务(开发中)](guides/plugin-backend-workspace.md) | `backend` 2 与协议 3:免页面启动、工作区上下文、纯后端命令(`ui` 4)、文档/文件/窗口宿主方法、宿主通知、失败码,以及协议 3 只增不改时后端要遵守的规则 | +| [插件后端:工作区上下文与宿主服务](guides/plugin-backend-workspace.md) | `backend` 2 与协议 3:免页面启动、工作区上下文、纯后端命令(`ui` 4)、文档/文件/窗口宿主方法、宿主通知、失败码,以及协议 3 只增不改时后端要遵守的规则 | +| [插件自有的原生终端](guides/native-terminals.md) | 插件函数协议 3 的 `terminal/*`:创建、显示、关闭、重开和退出通知(macOS、本机工作区) | 收录和上架的规则由 Lithe Store 维护:[发布社区插件](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)讲怎么打包、提交收录和发布新版本,[版本追踪与更新](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/versioning-and-updates.md)讲 Lithe 怎么选择可安装的版本。 -## 插件契约 +### 插件参考 + +[`reference/`](reference/README.md) 按功能整理契约,中英双语: + +| 页面 | 内容 | +| --- | --- | +| [插件 API 总表](reference/api-catalog.md) | API-01–44 每一项现在能用的方法、尚未提供的部分和对应功能页 | +| [能力与协议版本对照](reference/capabilities-and-versions.md) | 插件 API 级别、`requires` 能力版本、进程协议版本、文档格式版本,按 Core、macOS、Windows 和官方 SDK 对照 | +| [插件协议更新记录](reference/protocol-changelog.md) | 每个协议、能力和 API 是哪天、哪个 PR 改的,以及每个版本首次出现在哪个 PR;只有中文 | +| [功能页](reference/README.md#功能页) | 15 个功能各一页:版本对照、字段、宿主现状、契约章节、示例与用例 | -插件契约规定每个字段的含义、校验规则和错误码,完整内容在本仓库的 [`contract/v1/`](contract/v1/README.md): +### 插件契约 + +插件契约规定每个字段的含义、校验规则和错误码,完整内容在本仓库的 [`contract/v1/`](contract/v1/README.md),以英文为准: | 文件 | 内容 | | --- | --- | | [`README.md`](contract/v1/README.md) | 字段、API 等级、能力版本、权限和错误码的完整说明 | | [`plugin-manifest.schema.json`](contract/v1/plugin-manifest.schema.json) | `plugin.json` 的 JSON Schema | | [`plugin-index.schema.json`](contract/v1/plugin-index.schema.json) | `lithe-plugin-index.json` 的 JSON Schema | -| [`toolchains.md`](contract/v1/toolchains.md) | 插件提供的工具链:工具定义、版本管理器提供方、查询与回复校验、目录固定值和四层解析。目前只有 macOS 设置页以只读方式接入,构建、运行和语言服务器还不使用解析结果 | -| [`proposals/toolchains/`](contract/v1/proposals/toolchains/README.md) | 工具链的完整设计草案,包括还没实现的部分;里面的示例只用于评审,不能直接安装 | -| [`fixtures/`](contract/v1/fixtures) | 标准用例:合法和非法的清单、命令规划、构建规划、插件函数协议、市场索引,以及 Lithe 对每个用例的预期结果 | +| [`native-ui.schema.json`](contract/v1/native-ui.schema.json) | `ui` 3 宿主渲染的原生内容的 JSON Schema | +| [`toolchains/`](contract/v1/toolchains/README.md) | 插件提供的工具链:工具定义、版本管理器提供方、查询与回复校验、固定值(pin)和四层解析。Core 已实现;macOS 设置页以只读方式显示结果,macOS 构建和提供者运行另有独立的解析适配器,已有的 Java 和语言服务器仍用原来的方式;Windows 不支持 | +| [`toolchains/proposal/`](contract/v1/toolchains/proposal/README.md) | 工具链的完整设计草案,包括还没实现的部分;对应示例在 `fixtures/toolchains/proposal/`(含 Java 工具定义和 mise 提供方),只用于评审,不能直接安装 | +| [`fixtures/`](contract/v1/fixtures) | 标准用例,按功能分目录(`manifest`、`languages`、`backend`、`build-systems`、`build-providers`、`marketplace` 等),和 [reference 功能页](reference/README.md#功能页)一一对应:合法和非法的清单、命令和构建规划、插件函数协议、市场索引,以及 Lithe 对每个用例的预期结果 | -fixture 按演示的能力命名,例如 `build-command-stdout.plugin.json` 演示“解码命令的标准输出”。它们借 Cargo、Go、Gradle、Maven 等真实工具作为输入,但不是这些插件本身;要参考完整插件,看[示例插件](#示例插件)。 +fixture 放在 `fixtures/<功能>/` 下,文件按演示的能力命名,例如 `build-systems/build-command-stdout.plugin.json` 演示“解码命令的标准输出”。它们借 Cargo、Go、Gradle、Maven 等真实工具作为输入,但不是这些插件本身;要参考完整插件,看[示例插件](#示例插件)。每个目录的文件和用例数量见对应功能页的“示例与用例”。 -契约由 Lithe 的核心代码统一执行,各平台的校验结果相同。它在 Lithe 主仓库的 `shared/contracts/plugins/v1/` 定义,和执行它的代码一起修改;本仓库的 `contract/v1/` 是固定版本的逐字节副本,`contract.lock.json` 记录来源 commit 和每个文件的 SHA-256。副本可能包含还没进入正式版 Lithe 的能力,发布插件前以目标版本实际支持为准。 +契约由 Lithe 的核心代码统一执行,各平台的校验结果相同。它在 Lithe 主仓库的 `shared/contracts/plugins/v1/` 定义,和执行它的代码一起修改;本仓库的 `contract/v1/` 是固定版本的逐字节副本,`contract.lock.json` 记录来源 commit 和每个文件的 SHA-256。本仓库的指南和[版本对照](reference/capabilities-and-versions.md)以主仓库最新 `preview` 为准;副本和指南可能包含还没进入 Lithe 稳定版的能力,发布插件前以目标版本实际支持为准。 对契约有疑问或建议,到 [Lithe 主仓库提 issue](https://github.com/1lck/Lithe-IDEA/issues)。 -## 维护本仓库 +### 维护本仓库 不要手改 `contract/` 下的文件。契约在主仓库合并后,用同步脚本把整个目录同步过来,新增、删除、修改的文件都会跟上,锁文件同时更新: @@ -175,7 +188,9 @@ node scripts/contract.mjs sync --revision <主仓库的完整 commit> 联网命令会调用 GitHub API;设置 `GITHUB_TOKEN` 可以避开匿名请求的频率限制。CI 在每次提交时运行前两项检查和测试,每周一运行一次 `drift`。主仓库的契约有变化时,每周检查会失败并提示重新同步。 -### SDK +契约变化后,同时检查 [`reference/`](reference/README.md) 里受影响的功能页和版本表:它们是手写的对照,不会随同步自动更新。每次同步还要在[插件协议更新记录](reference/protocol-changelog.md)里补上新合入的 PR,做法见该页的“维护”一节。 + +#### SDK | 命令 | 作用 | | --- | --- | @@ -187,8 +202,216 @@ node scripts/contract.mjs sync --revision <主仓库的完整 commit> 端到端测试需要 Rust、Node.js 20.3+、Java 和 Maven。Rust Core 在主仓库,脚本会把锁文件锁定的那个主仓库 commit 稀疏检出到 `.artifacts/lithe`(只取 `rust/` 和 `shared/`),把 `sdk/e2e` 复制进它的 `rust/lithe-core/tests/` 再运行,所以 SDK 测的是和契约副本同一个版本的 Core。`LITHE_PLUGIN_SDK_JAVA_RUNTIME` 指定运行 Java 示例的 Java,`LITHE_PLUGIN_SDK_RUST_SAMPLE` 可以指定已编译好的 Rust 示例后端(否则脚本用 Cargo 编译);已有主仓库本地克隆时可以加 `--source <克隆目录>`。CI 每次提交跑锁定版本,每周一再跑一次主仓库 `preview` 的最新版本,Core 改动影响 SDK 时会提前发现。 -发版:先改版本号(Node.js 改 `sdk/node/package.json`;Java 运行 `mvn -f sdk/java/pom.xml versions:set -DnewVersion=<版本> -DgenerateBackupPoms=false`,三个 pom 一起改;Rust 改 `sdk/rust/Cargo.toml` 的 `workspace.package.version` 并更新 `Cargo.lock`),合并后推送对应标签 `sdk-node-v<版本>`、`sdk-java-v<版本>` 或 `sdk-rust-v<版本>`。发版工作流会检查标签和版本一致,测试通过后把包和 `.sha256` 发到本仓库的 Releases。 +发版:先改版本号(Node.js 改 `sdk/node/package.json`;Java 运行 `mvn -f sdk/java/pom.xml versions:set -DnewVersion=<版本> -DgenerateBackupPoms=false`,三个 pom 一起改;Rust 改 `sdk/rust/Cargo.toml` 的 `workspace.package.version` 并更新 `Cargo.lock`),合并后推送对应标签 `sdk-node-v<版本>`、`sdk-java-v<版本>` 或 `sdk-rust-v<版本>`。发版工作流会检查标签和版本一致,测试通过后把包和 `.sha256` 发到本仓库的 Releases。发版后同步更新[SDK 发版历史](reference/capabilities-and-versions.md#sdk-发版历史)。 + +### 许可证 + +[Apache-2.0](LICENSE) + +--- + +## English + +Writing, testing, and publishing a plugin all happen in **your own repository**. You never need to clone the [Lithe](https://github.com/1lck/Lithe-IDEA) repository or send it a pull request. + +| To | Go to | +| --- | --- | +| See what a plugin looks like | [What a plugin is](#what-a-plugin-is) | +| Build your first plugin | [Your first plugin in five steps](#your-first-plugin-in-five-steps) | +| Follow a complete walkthrough | [Guides](#guides) | +| Get completion and validation while writing `plugin.json` | [Validate in your editor](#validate-pluginjson-in-your-editor) | +| Run a backend process of your own | [Plugin backend SDKs](#plugin-backend-sdks-optional) | +| Connect to host logging and diagnose plugin failures | [Plugin logging](guides/plugin-logging.md) | +| Learn from existing plugins | [Example plugins](#example-plugins) | +| See what API-01–44 offer today, every version of each capability and protocol, and which version each host supports | [Plugin reference](reference/README.md) | +| Look up the exact meaning of a field | [Plugin contract](#plugin-contract) | + +### What a plugin is + +A Lithe plugin is a directory: `plugin.json` declares what the plugin provides, and the other files are the resources it uses, such as language servers, scripts, and icons. + +```text +io.github..zig-support/ +├── plugin.json +└── … # resources referenced by plugin.json +``` + +Lithe reads and validates `plugin.json` and then starts the external processes it declares itself. Plugin code is never loaded into the Lithe process, so the same package does not need rebuilding for each Lithe version. + +Community plugins must follow these rules: + +- `id` uses your own reverse domain name, such as `io.github..zig-support`. `dev.lithe` and `dev.lithe.*` are reserved for official plugins. +- `vendor.signatureRequirement` is `"none"`. Community plugins carry no Lithe signature; before installing, users see and confirm the complete programs, permissions, and source. +- Package paths use only `/`, with no `..`, absolute paths, or symbolic links. +- An installed package is read-only. Caches and state go to `${stateDir}` and `${workspaceStateDir}`, which Lithe passes through launch templates; diagnostic logs go through the shared SDK and host path. If you really need native file logs, first check that the target host supports `${logDir}`; see [Plugin logging](guides/plugin-logging.md). + +### Your first plugin in five steps + +1. **Copy the template.** Start from the [Lua plugin](https://github.com/Lithe-IDEA/lithe-plugin-lua), Lithe Store's example and template. +2. **Edit `plugin.json`.** Use your own ID, name, language, and language server. Set up your editor as in the [next section](#validate-pluginjson-in-your-editor) first, so mistyped fields are flagged as you write. +3. **Prepare every file at build time.** Language servers and backends go into the package when you build it; nothing is downloaded at run time. See the [LSP plugin lifecycle guide](guides/lsp-plugin-lifecycle.md). +4. **Try it in Lithe.** During development, use "Install Plugin from Disk…" with a zip or directory (you still confirm the install review; Lithe installs a snapshot, so reinstall after each change). Before publishing, create a Release in your repository, put `lithe-plugin-index.json` at the repository root, and add your repository under "Manage Plugin Repositories…" to walk through the real user install flow. +5. **Publish.** To list the plugin in every Lithe user's marketplace, follow the [Lithe Store publishing guide](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md); new versions are tracked automatically once listed. + +When you need run, test, or build tool integration, read the [build, run, and test integration guide](guides/build-tool-integration.md). + +#### Choosing an API level + +`pluginAPIVersion` decides which features Lithe must support to install your plugin. Choose the lowest level that meets your needs so older Lithe versions can install it too. + +| Level | Adds | Examples | +| --- | --- | --- | +| `1` | Languages, language servers (packaged or a command the user installed), run and test | Lua, Go, PHP | +| `2` | Adds: launching with a Node.js or Java runtime, launch templates, plugin settings, tool windows and commands | — | +| `3` | Adds: per-capability versions (`requires`), declarative build systems, build backends, VCS providers | Gradle, Maven, Cargo | +| `4` | Adds: debug adapters (`debugAdapters`, DAP processes), launching an adapter with a selected runtime | LLDB (prerelease) | + +Keep the three version numbers apart: `schemaVersion` is always `2` and names the manifest format; `pluginAPIVersion` is the level above; `version` is your plugin's own version. + +At API 3 and above, `requires` must list exactly the capabilities you use. For example, a plugin that only uses declarative build systems writes `"requires": { "buildSystems": 1 }` and nothing more. For every version of each capability and which version each host supports, see [Capability and protocol versions](reference/capabilities-and-versions.md). + +API 4 needs a Lithe that explicitly supports API 4: older versions show an update prompt in the marketplace instead of installing. Only the macOS host implements debug adapters today. + +### Validate plugin.json in your editor + +The contract's two JSON Schemas have fixed addresses: + +| File | Schema | +| --- | --- | +| `plugin.json` | `https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-manifest.schema.json` | +| `lithe-plugin-index.json` | `https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-index.schema.json` | + +> **Do not put `"$schema"` in `plugin.json`.** Lithe refuses unknown manifest fields, so adding it makes the plugin uninstallable. Associate the schema by file name in your editor instead. + +VS Code: add this to `.vscode/settings.json` in your plugin repository: + +```json +{ + "json.schemas": [ + { + "fileMatch": ["**/plugin.json"], + "url": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-manifest.schema.json" + }, + { + "fileMatch": ["**/lithe-plugin-index.json"], + "url": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-index.schema.json" + } + ] +} +``` + +JetBrains IDEs: Settings → Languages & Frameworks → Schemas and DTDs → JSON Schema Mappings; add the URL above with the file pattern `plugin.json`. + +A schema checks only JSON structure. Rules such as ID references, permissions, activation events, and template variables are checked by Lithe at install time and are written in the [plugin contract](#plugin-contract). + +### Plugin backend SDKs (optional) + +A plugin that uses only language servers, run, and test needs no backend. When you need a build backend (`buildProviders`), plugin functions, or a backend process for the plugin's own web page (`backend`), write it with an official SDK: the SDK handles the protocol, cancellation, process cleanup, and errors, and you write only the handlers. + +| SDK | Runtime | Release tag | Download | How it goes into the plugin | +| --- | --- | --- | --- | --- | +| [Node.js](sdk/node/README.md) | Node.js 20.3+ | `sdk-node-v` | `lithe-plugin-sdk-node-.tgz` and `.sha256` | Unpack it, copy `package/` into the plugin package, and import it by relative path | +| [Java](sdk/java/README.md) | Java 8+ | `sdk-java-v` | `lithe-plugin-sdk-java-.jar` and `.sha256` | Shade it into the backend JAR | +| [Rust](sdk/rust/README.md) | Rust 1.87+ | `sdk-rust-v` | `lithe-plugin-sdk-rust-.crate` and `.sha256` | Pin the tag as a Git dependency (`Cargo.lock` records the commit), or unpack it as a `path` dependency; compile one backend executable per platform | + +All SDK sources live under `sdk/` in this repository and are published in [this repository's Releases](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases), not on npm, Maven, or crates.io. The Rust SDK implements only the plugin backend (`backend`): plugin functions and host services (plugin function protocol 1–3); write build backends with the Node.js or Java SDK and VCS backends with the Node.js SDK. For the protocol versions each SDK implements, see [Host and official SDKs](reference/capabilities-and-versions.md#host-and-official-sdks). Java SDK 0.1.0 was released from the Lithe repository before the move; [that Release](https://github.com/1lck/Lithe-IDEA/releases/tag/sdk-java-v0.1.0) stays available, and plugins pinned to it need no change. + +There is one rule: **download at build time and verify against the SHA-256 (or Git commit) you pinned; download nothing at run time.** An installed package has no `npm install` step and must run offline. + +The [Maven plugin](https://github.com/Lithe-IDEA/lithe-plugin-maven)'s `sdk.lock.json` is a ready example of pinning the Java SDK. + +### Example plugins + +| Plugin | API | Demonstrates | +| --- | --- | --- | +| [Lua](https://github.com/Lithe-IDEA/lithe-plugin-lua) | 1 | **Template**; a packaged language server (LuaLS) and running the current file | +| [Go](https://github.com/Lithe-IDEA/lithe-plugin-go) | 1 | The user's own gopls installation, running the main package and `go test` | +| [PHP](https://github.com/Lithe-IDEA/lithe-plugin-php) | 1 | The user's own Intelephense installation (its license forbids bundling), PHPUnit | +| [Gradle](https://github.com/Lithe-IDEA/lithe-plugin-gradle) | 3 | A purely declarative build system with no backend | +| [Maven](https://github.com/Lithe-IDEA/lithe-plugin-maven) | 3 | A build backend written with the Java SDK | +| [Cargo](https://github.com/Lithe-IDEA/lithe-plugin-cargo) | 3 | A build backend and plugin settings written with the Node.js SDK | +| [LLDB](https://github.com/Lithe-IDEA/lithe-plugin-lldb) | 4 | Reference debug adapter; debugs C, C++, Swift, and Rust with Xcode's `lldb-dap`, including `executable` lookup and debug console completion | + +### Guides + +| Guide | Covers | +| --- | --- | +| [LSP plugin lifecycle](guides/lsp-plugin-lifecycle.md) | Starting from the Lua template: manifest, server packaging, activation, state directories, shutdown, upgrade, and uninstall | +| [Plugin logging](guides/plugin-logging.md) | The safe failure boundary of the three SDKs, the managed diagnostics channel, macOS v2 and Windows v1 status, and the local session lifecycle of `${logDir}` | +| [Native build workflows](guides/native-build-workflows.md) | Build execution, exact tests, and run preparation in the Node and Java SDKs, and verifying uncommitted sources against a real Core | +| [Build, run, and test integration](guides/build-tool-integration.md) | Execution provider examples, the project backend boundary, and the current status and published examples of API 3 build systems | +| [Writing a debug adapter plugin](guides/debug-adapter-plugin.md) | API 4: from a minimal manifest to launch modes, compile-then-debug `executable`, debug console completion, and prerelease to stable | +| [Plugin backend: workspace contexts and host services](guides/plugin-backend-workspace.md) | `backend` 2 and protocol 3: launching without a page, workspace contexts, backend-only commands (`ui` 4), document, file, and window host methods, host notifications, failure codes, and the rules a backend follows while protocol 3 only adds | +| [Plugin-owned native terminals](guides/native-terminals.md) | `terminal/*` in plugin function protocol 3: create, show, close, reopen, and exit notifications (macOS, local workspaces) | + +Lithe Store maintains the listing rules: [Publishing community plugins](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md) covers packaging, submitting a listing, and releasing new versions, and [Version tracking and updates](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/versioning-and-updates.md) covers how Lithe chooses an installable version. + +### Plugin reference + +[`reference/`](reference/README.md) organizes the contract by feature, in Chinese and English: + +| Page | Covers | +| --- | --- | +| [Plugin API catalog](reference/api-catalog.md) | For each of API-01–44, the methods usable today, what is still missing, and its feature page | +| [Capability and protocol versions](reference/capabilities-and-versions.md) | Plugin API levels, `requires` capability versions, process protocol versions, and document format versions, compared across Core, macOS, Windows, and the official SDKs | +| [Plugin protocol changelog](reference/protocol-changelog.md) | When, and in which PR, each protocol, capability, and API changed, and the PR that introduced each version; Chinese only | +| [Feature pages](reference/README.md#feature-pages) | One page for each of 15 features: versions compared, fields, host status, contract sections, examples and cases | + +### Plugin contract + +The plugin contract defines the meaning, validation rules, and error codes of every field. It lives in [`contract/v1/`](contract/v1/README.md) of this repository, and its English text is normative: + +| File | Contents | +| --- | --- | +| [`README.md`](contract/v1/README.md) | Complete description of fields, API levels, capability versions, permissions, and error codes | +| [`plugin-manifest.schema.json`](contract/v1/plugin-manifest.schema.json) | JSON Schema for `plugin.json` | +| [`plugin-index.schema.json`](contract/v1/plugin-index.schema.json) | JSON Schema for `lithe-plugin-index.json` | +| [`native-ui.schema.json`](contract/v1/native-ui.schema.json) | JSON Schema for host-rendered native content (`ui` 3) | +| [`toolchains/`](contract/v1/toolchains/README.md) | Plugin-owned toolchains: tool definitions, version manager providers, query and reply validation, pins, and four-layer resolution. Core implements it; the macOS settings pages show results read-only, macOS Build and Provider Run have a separate resolution adapter, and existing Java and language-server consumers keep their previous behavior; Windows does not support it | +| [`toolchains/proposal/`](contract/v1/toolchains/proposal/README.md) | The complete toolchain design draft, including unimplemented parts; its examples live in `fixtures/toolchains/proposal/` (including a Java tool definition and a mise provider), are for review only, and cannot be installed | +| [`fixtures/`](contract/v1/fixtures) | Conformance cases in one directory per feature (`manifest`, `languages`, `backend`, `build-systems`, `build-providers`, `marketplace`, and so on), matching the [reference feature pages](reference/README.md#feature-pages): valid and invalid manifests, command and build plans, plugin function protocol, marketplace indexes, and Lithe's expected result for each case | + +Fixtures live under `fixtures//` and are named for the capability they demonstrate; for example `build-systems/build-command-stdout.plugin.json` demonstrates decoding a command's standard output. They use real tools such as Cargo, Go, Gradle, and Maven as inputs but are not those plugins; for complete plugins, see the [example plugins](#example-plugins). Each feature page's "Examples and cases" section lists its files and case counts. + +Lithe's core code enforces the contract, so every platform validates the same way. It is defined in `shared/contracts/plugins/v1/` of the Lithe repository and changes together with the code that enforces it; `contract/v1/` here is a byte-for-byte copy of a pinned revision, and `contract.lock.json` records the source commit and each file's SHA-256. The guides and [version tables](reference/capabilities-and-versions.md) here track the latest `preview` of the Lithe repository; the copy and the guides may contain capabilities not yet in a stable Lithe release, so check what your target version supports before publishing. + +For questions or suggestions about the contract, [open an issue in the Lithe repository](https://github.com/1lck/Lithe-IDEA/issues). + +### Maintaining this repository + +Do not edit files under `contract/` by hand. After the contract merges in the Lithe repository, sync the whole directory with the sync script; added, removed, and changed files all follow, and the lock file updates with them: + +```bash +node scripts/contract.mjs sync --revision +``` + +With a local clone of the Lithe repository, add `--checkout ` to read from it. Other commands: + +| Command | Purpose | +| --- | --- | +| `node scripts/contract.mjs verify` | Offline check: lock file format, SHA-256s, that each schema's `$id` equals its address in this repository, that relative links in the contract Markdown stay inside the copy, and that `contract/` has no unlocked files | +| `node scripts/contract.mjs verify --upstream` | Online check: the copy matches the contract directory at the pinned Lithe commit exactly, listing extra, missing, or differing files | +| `node scripts/contract.mjs drift` | Online check: whether the contract on the Lithe repository's `preview` branch (where changes merge; the default branch `main` updates only at release) has changed | +| `node --test scripts/*.test.mjs` | Tests for the sync script itself | + +Online commands call the GitHub API; set `GITHUB_TOKEN` to avoid anonymous rate limits. CI runs the first two checks and the tests on every commit and runs `drift` every Monday. When the Lithe repository's contract changes, the weekly check fails and asks for a resync. + +After a contract change, also check the affected feature pages and version tables in [`reference/`](reference/README.md): they are hand-written comparisons and do not update with the sync. Each sync also adds the newly merged PRs to the [plugin protocol changelog](reference/protocol-changelog.md), as its maintenance section describes. + +#### SDKs + +| Command | Purpose | +| --- | --- | +| `node --test sdk/node/test/*.test.mjs` | Node.js SDK unit tests | +| `mvn -B -ntp -f sdk/java/pom.xml verify -Djvm=` | Java SDK unit tests; `-Djvm` selects the Java that runs them (CI uses Java 8) | +| `cargo test --locked --manifest-path sdk/rust/Cargo.toml` | Rust SDK unit tests (CI uses Rust 1.87, plus stable for `cargo fmt` and `cargo clippy`) | +| `node scripts/sdk-e2e.mjs` | End-to-end tests: each SDK's sample backend against the real Lithe Rust Core. Node.js and Java run the same build backend scenarios in `sdk/e2e`, Rust runs the plugin backend scenario (`plugin_sdk/backend.rs`), and the test answers credential requests in place of the host | +| `node scripts/sdk-e2e.mjs --ref preview` | The same, against the latest Core on the Lithe repository's `preview` branch | + +End-to-end tests need Rust, Node.js 20.3+, Java, and Maven. Rust Core lives in the Lithe repository: the script sparse-checks out the commit pinned in the lock file to `.artifacts/lithe` (only `rust/` and `shared/`), copies `sdk/e2e` into its `rust/lithe-core/tests/`, and runs it there, so the SDKs are tested against the same Core version as the contract copy. `LITHE_PLUGIN_SDK_JAVA_RUNTIME` selects the Java that runs the Java sample, and `LITHE_PLUGIN_SDK_RUST_SAMPLE` can point at a prebuilt Rust sample backend (otherwise the script builds it with Cargo); with a local Lithe clone, add `--source `. CI runs the pinned version on every commit and the latest `preview` every Monday, so Core changes that affect the SDKs show up early. + +Releasing: first bump the version (Node.js in `sdk/node/package.json`; for Java run `mvn -f sdk/java/pom.xml versions:set -DnewVersion= -DgenerateBackupPoms=false`, which updates all three poms; for Rust change `workspace.package.version` in `sdk/rust/Cargo.toml` and update `Cargo.lock`), then after merging push the tag `sdk-node-v`, `sdk-java-v`, or `sdk-rust-v`. The release workflow checks that the tag matches the version and, once tests pass, publishes the package and its `.sha256` to this repository's Releases. After a release, update the [SDK release history](reference/capabilities-and-versions.md#sdk-release-history). -## 许可证 +### License [Apache-2.0](LICENSE) diff --git a/contract.lock.json b/contract.lock.json index 6861d1e..e410e91 100644 --- a/contract.lock.json +++ b/contract.lock.json @@ -3,160 +3,220 @@ "source": { "repository": "https://github.com/1lck/Lithe-IDEA", "directory": "shared/contracts/plugins/v1", - "revision": "7a24126eca3a26775daeb84db38ae04bec938d1a" + "revision": "644afdaac9087b276cb8f7902658b85ae1792d6d" }, "files": [ { "path": "README.md", - "sha256": "ceb7d20d4c47fa48154813727c5c8e06563cf0fbdcd7d62ed45f36cb7c6913c3" + "sha256": "8bae09cb1277181d6598c9835a42f6fb9c2db27d2e4d0e60dd5e45e80d487e5d" }, { - "path": "fixtures/backend-workspace.plugin.json", + "path": "fixtures/backend/backend-workspace.plugin.json", "sha256": "6eef7a0735e4cd0c66b32a1075ad8f689554e26f929d3a1f436249026e06037b" }, { - "path": "fixtures/backend.plugin.json", + "path": "fixtures/backend/backend.plugin.json", "sha256": "c83d455cd1981407e903f28b6bfdb38309715856499feba5d76af85a097e1977" }, { - "path": "fixtures/build-command-output-file.plugin.json", - "sha256": "14cbcc03070fc9632a756d5afd025680e7ea77f7763c36d83a2bd86cae2b12d7" + "path": "fixtures/backend/command-backend.plugin.json", + "sha256": "791f11f3b9d59d5909a9ca94788bddb7f7493b7610361553d5733d7aed18df22" }, { - "path": "fixtures/build-command-stdout.plugin.json", - "sha256": "cb24aedb59cdcec6b358dbeb95213143a4fc229aea8973ddd3f154f09335b9dc" + "path": "fixtures/backend/function-protocol.json", + "sha256": "4341a2145d495431f4017ae2aed95214c512c14c5efb866c5a1178912c66006b" }, { - "path": "fixtures/build-debug-preparation.json", - "sha256": "73d3362fac7302c5698b440536821784350fbf6eb267b94d144994d403f0efed" + "path": "fixtures/backend/plugin-logging.json", + "sha256": "ada9fe4b6a030466fc3ed8819683e645007720cf2310b709ff0c1873c80025a5" }, { - "path": "fixtures/build-execution-protocol.json", - "sha256": "556779c92b7b43970ff882cce6fd278dbe0d16476403778a9240d61c4a15b3da" + "path": "fixtures/backend/terminal-backend.plugin.json", + "sha256": "664669aa36b8b3af464495048f76cb1154c38407c816e7997447f372c139fff1" }, { - "path": "fixtures/build-host-integration.plugin.json", - "sha256": "36361a98a86de1c6c9c4f0694accab3adadefd53f344b4ac3a85b8906d749b13" + "path": "fixtures/build-providers/build-configuration.json", + "sha256": "6dd6cbed70f9952f6787b44df2064ce6c8ceefc392d1ec831d4562c395799b48" }, { - "path": "fixtures/build-model-consumer.plugin.json", - "sha256": "c41ac0f6fa6600f5bc0ee29d7d89f8257d9f9fec9add8d4c5ac448ad753177f5" + "path": "fixtures/build-providers/build-connection-debug-options.json", + "sha256": "f82897b2e1f2c74974b6fcd43c0f2684517696ee9ecd35bb85f24d327ccd9e41" }, { - "path": "fixtures/build-model-producer.plugin.json", - "sha256": "2bed5e883cd21d37599cdaf69f2663b903e1149b58a7f33f7c01658ec081a455" + "path": "fixtures/build-providers/build-debug-preparation.json", + "sha256": "73d3362fac7302c5698b440536821784350fbf6eb267b94d144994d403f0efed" }, { - "path": "fixtures/build-plans.json", - "sha256": "b1ebdab5d1e7d0b6f5fc948335581c41814dac83937b8452722ad5da6836d6ab" + "path": "fixtures/build-providers/build-execution-protocol.json", + "sha256": "556779c92b7b43970ff882cce6fd278dbe0d16476403778a9240d61c4a15b3da" + }, + { + "path": "fixtures/build-providers/build-provider-configuration.plugin.json", + "sha256": "103b65e5dcc261ce21345d846be090ee66cb1511554af0677766789ebbb9dfd3" }, { - "path": "fixtures/build-provider-debug-preparation.plugin.json", + "path": "fixtures/build-providers/build-provider-debug-preparation.plugin.json", "sha256": "07db3ced65ab5f73318de59c8857b48f7b68d731bae928bc2954e3e47f585170" }, { - "path": "fixtures/build-provider-execution.plugin.json", + "path": "fixtures/build-providers/build-provider-execution.plugin.json", "sha256": "6978c19d7f4353c5dadc833ac723dad76c001aae2c92286d4dd4a9ff2bd67101" }, { - "path": "fixtures/build-provider-language-server.plugin.json", + "path": "fixtures/build-providers/build-provider-language-server.plugin.json", "sha256": "fc6fae12c07715ad5017484cf9a17905d51ffa7ee40ccf4200613b720644f2ce" }, { - "path": "fixtures/build-provider-run-preparation.plugin.json", + "path": "fixtures/build-providers/build-provider-preparation.plugin.json", + "sha256": "30d6aa8714fe3373e538eafe02797434a1272e56b0804a6ae05a914e498502da" + }, + { + "path": "fixtures/build-providers/build-provider-reports.plugin.json", + "sha256": "73e5f13cbb70fe9e99c59dfba9a421823ea137c20de1c1b472cdf0a4537538a0" + }, + { + "path": "fixtures/build-providers/build-provider-run-options.plugin.json", + "sha256": "38ffa12489e44247a75c2290117fc18e716574d76c33b893c04531f6387fbc64" + }, + { + "path": "fixtures/build-providers/build-provider-run-preparation.plugin.json", "sha256": "14fca4d8fba7a2bb66f3d6fb7ae81c745bc323d44d9958d8fdfc5c1adbcc7ac1" }, { - "path": "fixtures/build-provider-tests.plugin.json", + "path": "fixtures/build-providers/build-provider-selectors.plugin.json", + "sha256": "4837369deb8697eef5ce79180cd1518ce7a2929ec013cbef96f28700924ee7c5" + }, + { + "path": "fixtures/build-providers/build-provider-tests.plugin.json", "sha256": "071cef77007fbde34e1ff971c10bbe693e9275f10de1c070801baf880c4e19f8" }, { - "path": "fixtures/build-provider.plugin.json", + "path": "fixtures/build-providers/build-provider.plugin.json", "sha256": "bbb19c3d201be64af5262be549fe92822c14e77efae616e64c4f32ddd56b2758" }, { - "path": "fixtures/build-run-preparation.json", - "sha256": "3609997b65e0322ae16756f1af376c041c55aa1a20187cad0c5a9bf1444a10cd" + "path": "fixtures/build-providers/build-run-options.json", + "sha256": "b128cb04b6075acc1ec5102d13be17f6592652728a3c9e04c86e14e7a02f2161" }, { - "path": "fixtures/build-static-file.plugin.json", - "sha256": "0380748f151cf2b154133dac5447c8665801f16bf005314ab7ac1d4d11475c92" + "path": "fixtures/build-providers/build-run-preparation.json", + "sha256": "3609997b65e0322ae16756f1af376c041c55aa1a20187cad0c5a9bf1444a10cd" }, { - "path": "fixtures/build-test-discovery.json", + "path": "fixtures/build-providers/build-test-discovery.json", "sha256": "62384b0d2c31cd2fed877a421dd90eb1fff1e7d991e1622567004bc2eace5dd6" }, { - "path": "fixtures/build-test-protocol.json", + "path": "fixtures/build-providers/build-test-durations.json", + "sha256": "4c0fef0f83ddba412ac70f35b2b30586a6cec911ccc83c3ced8572abfd77600e" + }, + { + "path": "fixtures/build-providers/build-test-protocol.json", "sha256": "a67b9b6448aff53792cb791b0fa6994fcb0e94729a5c5a85a524398ebe6e58fd" }, { - "path": "fixtures/build-tool-path.plugin.json", - "sha256": "685144e417bbf01b013144dec109d51b56922f98811e9ea6eda34da26749165c" + "path": "fixtures/build-systems/build-command-output-file.plugin.json", + "sha256": "14cbcc03070fc9632a756d5afd025680e7ea77f7763c36d83a2bd86cae2b12d7" }, { - "path": "fixtures/command-backend.plugin.json", - "sha256": "791f11f3b9d59d5909a9ca94788bddb7f7493b7610361553d5733d7aed18df22" + "path": "fixtures/build-systems/build-command-stdout.plugin.json", + "sha256": "cb24aedb59cdcec6b358dbeb95213143a4fc229aea8973ddd3f154f09335b9dc" }, { - "path": "fixtures/command-plans.json", - "sha256": "d268b4d3966c73031b1876c11cec9a2f6ee6dcc27b8bee687c9a349c147610f4" + "path": "fixtures/build-systems/build-host-integration.plugin.json", + "sha256": "36361a98a86de1c6c9c4f0694accab3adadefd53f344b4ac3a85b8906d749b13" + }, + { + "path": "fixtures/build-systems/build-instance-options.plugin.json", + "sha256": "21d3864e3ad5e0f41f0726193599dee7683384c254c824cb7a592961a6e85bf1" + }, + { + "path": "fixtures/build-systems/build-model-consumer.plugin.json", + "sha256": "c41ac0f6fa6600f5bc0ee29d7d89f8257d9f9fec9add8d4c5ac448ad753177f5" + }, + { + "path": "fixtures/build-systems/build-model-producer.plugin.json", + "sha256": "2bed5e883cd21d37599cdaf69f2663b903e1149b58a7f33f7c01658ec081a455" }, { - "path": "fixtures/debug-adapter-language-server.plugin.json", + "path": "fixtures/build-systems/build-plans.json", + "sha256": "b1ebdab5d1e7d0b6f5fc948335581c41814dac83937b8452722ad5da6836d6ab" + }, + { + "path": "fixtures/build-systems/build-static-file.plugin.json", + "sha256": "0380748f151cf2b154133dac5447c8665801f16bf005314ab7ac1d4d11475c92" + }, + { + "path": "fixtures/build-systems/build-tool-path.plugin.json", + "sha256": "685144e417bbf01b013144dec109d51b56922f98811e9ea6eda34da26749165c" + }, + { + "path": "fixtures/build-systems/build-toolchains.plugin.json", + "sha256": "4cf0e02549ea24f30d245adb423dffddc0a51fba1c835fae600dc89468d23c3d" + }, + { + "path": "fixtures/debug-adapters/debug-adapter-language-server.plugin.json", "sha256": "c29d1e94a69840a63317e5d4b15a0a9cd8267a980fa2b21c37528bc08bb3ee96" }, { - "path": "fixtures/debug-adapter-rejections.json", + "path": "fixtures/debug-adapters/debug-adapter-rejections.json", "sha256": "ec6023c48236ec3f59f8c8c7bfd4cefea0180f14e11fd4823fb2e48ac36567a6" }, { - "path": "fixtures/debug-adapter-remote-attach.plugin.json", + "path": "fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json", "sha256": "66b4e0326f3b99027865869742ec98afd7da0df6133bc0a23115749edde13e3b" }, { - "path": "fixtures/debug-adapter.plugin.json", + "path": "fixtures/debug-adapters/debug-adapter.plugin.json", "sha256": "81038febbde31e0554451789106f8fc255e87038c8185602ce0c7e918f90ff78" }, { - "path": "fixtures/editor-actions.plugin.json", + "path": "fixtures/editor/editor-actions.plugin.json", "sha256": "ca86471b63a15e6f12102777c59c300a6da7690ac13e02835e43abf41f26eb0b" }, { - "path": "fixtures/function-protocol.json", - "sha256": "4341a2145d495431f4017ae2aed95214c512c14c5efb866c5a1178912c66006b" + "path": "fixtures/keymap-import/keymap-import-provider.plugin.json", + "sha256": "03d3843f30edd82fd02652a6f800eca9d7afcddc7b9d2a0bd1b3726c2dfb8eae" + }, + { + "path": "fixtures/languages/command-plans.json", + "sha256": "d268b4d3966c73031b1876c11cec9a2f6ee6dcc27b8bee687c9a349c147610f4" }, { - "path": "fixtures/grammars/http.monarch.json", + "path": "fixtures/languages/grammars/http.monarch.json", "sha256": "2fb9c325feb966d0d6569a77a2a8493b0e43e13b7cd64f9b30548ff826f9cdea" }, { - "path": "fixtures/hello-lithe.plugin.json", - "sha256": "e56a4c2df075c14f059eefd73e916215f6e1594f047617c76b518fa21cc2a065" + "path": "fixtures/languages/language-grammar.plugin.json", + "sha256": "9c55a3ea5f075328fca87c845f04f680e35fb283a39283c742fb5d435be05faa" + }, + { + "path": "fixtures/languages/language-host-integration.plugin.json", + "sha256": "507e8134a08ab665c0e9393b90e3b56e54c0d2b81af2e95de947a82e399f4c14" }, { - "path": "fixtures/invalid-manifests.json", - "sha256": "3859c99928158e44fc1f9f25caff5289ed3c2f8e3af200108ab17051915246c2" + "path": "fixtures/languages/language-packaged-server.plugin.json", + "sha256": "fd0e27a4cb3642c2094c70e4124f95c55711e0228fd77bfddc869d02fac3d7d6" }, { - "path": "fixtures/keymap-import-provider.plugin.json", - "sha256": "03d3843f30edd82fd02652a6f800eca9d7afcddc7b9d2a0bd1b3726c2dfb8eae" + "path": "fixtures/languages/language-system-command.plugin.json", + "sha256": "4dbdd38bbe23003da61210296f986d94313b23a98be4d97b1a628570c4375501" }, { - "path": "fixtures/language-grammar.plugin.json", - "sha256": "9c55a3ea5f075328fca87c845f04f680e35fb283a39283c742fb5d435be05faa" + "path": "fixtures/languages/node-runtime.plugin.json", + "sha256": "3cbc80303dca796302653982d3f55fdc2072aea69a2517286a06e73fbc3c4499" }, { - "path": "fixtures/language-host-integration.plugin.json", - "sha256": "507e8134a08ab665c0e9393b90e3b56e54c0d2b81af2e95de947a82e399f4c14" + "path": "fixtures/languages/runtime-versions.json", + "sha256": "a19c1c057ab54a2b24a4a0e5a0d7764f0b9063bb6dd356baf19f5f6e37a76e19" }, { - "path": "fixtures/language-packaged-server.plugin.json", - "sha256": "fd0e27a4cb3642c2094c70e4124f95c55711e0228fd77bfddc869d02fac3d7d6" + "path": "fixtures/manifest/hello-lithe.plugin.json", + "sha256": "e56a4c2df075c14f059eefd73e916215f6e1594f047617c76b518fa21cc2a065" }, { - "path": "fixtures/language-system-command.plugin.json", - "sha256": "4dbdd38bbe23003da61210296f986d94313b23a98be4d97b1a628570c4375501" + "path": "fixtures/manifest/invalid-manifests.json", + "sha256": "4f1b2c72d9b9156686f347ae083ba145d10319dcabfefe4c9eef6854858bb02a" }, { "path": "fixtures/marketplace/index-cases.json", @@ -179,148 +239,136 @@ "sha256": "88a1b00fc443a65ca5faa8ac519e93de4e6abca8297abb9045613317747f41b5" }, { - "path": "fixtures/native-controls.plugin.json", - "sha256": "fc160a33d801d44222d20d6c79b1726e8e9dceb929fa99695a7990b3f4cb57cb" - }, - { - "path": "fixtures/native-form.plugin.json", - "sha256": "082dd621eadad410a0aadc08e35e213e6818853ae4c18a2d38ce7c6f5bd8946d" + "path": "fixtures/package-trust/publisher-bundled.plugin.json", + "sha256": "195c3f7c9712f44711602c059e32af88b3e7b7285b600cffd38f116e5b1150eb" }, { - "path": "fixtures/native-panel.plugin.json", - "sha256": "b35c69c85f4e53f4ccd31e320e2f91387dde1935e10bde5b13019fd6e925fe1d" + "path": "fixtures/package-trust/signature-vectors.json", + "sha256": "94dd446142eee59dd1d8422b004912161c13e9e281c8a754d8bb1f632fb8e6fa" }, { - "path": "fixtures/node-runtime.plugin.json", - "sha256": "3cbc80303dca796302653982d3f55fdc2072aea69a2517286a06e73fbc3c4499" + "path": "fixtures/project-generators/project-generator.plugin.json", + "sha256": "87477231929291d6e6be351cf993f99971b3ddb3b81cce3f668df6e3a07f18a3" }, { - "path": "fixtures/plugin-logging.json", - "sha256": "ada9fe4b6a030466fc3ed8819683e645007720cf2310b709ff0c1873c80025a5" + "path": "fixtures/project-generators/project-plans.json", + "sha256": "ca8a469a844678af11f3ad5781a057e4ec987cea2bd42e7e7cba2fa90c2a13f2" }, { - "path": "fixtures/project-generator-templates/.gitattributes", + "path": "fixtures/project-generators/templates/.gitattributes", "sha256": "5ab88980b2cd4db0455bb2d5c3703db6668d28f254c41a92a6feff64604af9f7" }, { - "path": "fixtures/project-generator-templates/app/README.md", + "path": "fixtures/project-generators/templates/app/README.md", "sha256": "c02eaa65ce4ba3a06c92c8fc5825508347437a5f3a2b1870e11f6510947e4fb7" }, { - "path": "fixtures/project-generator-templates/app/dot-gitignore", + "path": "fixtures/project-generators/templates/app/dot-gitignore", "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, { - "path": "fixtures/project-generator-templates/main/Main.java", + "path": "fixtures/project-generators/templates/main/Main.java", "sha256": "5435c9be433e0a1cadbeccc701451b21d53d8519e1516c58960af944fe0bf616" }, { - "path": "fixtures/project-generator.plugin.json", - "sha256": "1e5598cfe85e62711c8f5e4090dc229df732380b75d519a0e95f7a8345740c5b" - }, - { - "path": "fixtures/project-plans.json", - "sha256": "14c6fa8b073732f93b85b30e4fa9e8384ce094f2fb6fe833e41476f12c8d2132" - }, - { - "path": "fixtures/publisher-bundled.plugin.json", - "sha256": "195c3f7c9712f44711602c059e32af88b3e7b7285b600cffd38f116e5b1150eb" + "path": "fixtures/settings/settings-v2.plugin.json", + "sha256": "83d4c7faf7bfbb9c474639bfc0ef42dae8cd7d370d388f8527589f2afed12981" }, { - "path": "fixtures/runtime-versions.json", - "sha256": "a19c1c057ab54a2b24a4a0e5a0d7764f0b9063bb6dd356baf19f5f6e37a76e19" + "path": "fixtures/toolchains/proposal/java-tool-definition.json", + "sha256": "23408937de3c5d77712657e8753d23d7cd07d91e73d5c3d0788d3ae283f56134" }, { - "path": "fixtures/settings-v2.plugin.json", - "sha256": "83d4c7faf7bfbb9c474639bfc0ef42dae8cd7d370d388f8527589f2afed12981" + "path": "fixtures/toolchains/proposal/local.json", + "sha256": "0d24ed31d1b330cd45147274747206dada3dc660deae66eb72acdf359e2092ba" }, { - "path": "fixtures/terminal-backend.plugin.json", - "sha256": "664669aa36b8b3af464495048f76cb1154c38407c816e7997447f372c139fff1" + "path": "fixtures/toolchains/proposal/manifest-rejections.json", + "sha256": "de2ebb472c31eb8e5ff3fd9217572311dc65df1bb62a17736e4192d105576674" }, { - "path": "fixtures/tool-definition.plugin.json", - "sha256": "06b94a1886edcaa8ade3ae1462d01aa6e54e7ec3104ae28d0b796f2a303fd90a" + "path": "fixtures/toolchains/proposal/mise-provider.json", + "sha256": "55f9b992e8d4988afb3e8574025af2e593c71c799a98bd9b237b9d01c5fc474c" }, { - "path": "fixtures/toolchain-provider.plugin.json", - "sha256": "87a7c83ecf5e1983953fd29705237ff9c1b9480d22416aec7e85293a2342600a" + "path": "fixtures/toolchains/proposal/query-cases.json", + "sha256": "d1a7c3e8b8df18cba3d40cca437b712a787b8935d6c8eb7b567e74736d42c203" }, { - "path": "fixtures/vcs-provider.plugin.json", - "sha256": "7d0c85b6e0531a28e877050c2da15470d0bfe5ccd4c98215e7824d4445d977a9" + "path": "fixtures/toolchains/proposal/resolution-cases.json", + "sha256": "8292f41dc287e0d96638a3583324354f106cc0bd149c91ab76f80525893c5cab" }, { - "path": "fixtures/web-panel.plugin.json", - "sha256": "1ccaf5bb58e0296b8d244e6951d62c1f8e9e35e2e2bd467d4368fc31d7f42fd8" + "path": "fixtures/toolchains/tool-definition.plugin.json", + "sha256": "06b94a1886edcaa8ade3ae1462d01aa6e54e7ec3104ae28d0b796f2a303fd90a" }, { - "path": "native-ui.schema.json", - "sha256": "62c6e49ec274c52efb70cc3562f261e11d4dfb8cc59c8e13f7774321266f2837" + "path": "fixtures/toolchains/toolchain-provider.plugin.json", + "sha256": "87a7c83ecf5e1983953fd29705237ff9c1b9480d22416aec7e85293a2342600a" }, { - "path": "plugin-index.schema.json", - "sha256": "6771cb0e8c1ba101e6fc90b436de1b74f057ff0d58cc90f92e669edc9a102fc6" + "path": "fixtures/ui/native-controls.plugin.json", + "sha256": "fc160a33d801d44222d20d6c79b1726e8e9dceb929fa99695a7990b3f4cb57cb" }, { - "path": "plugin-manifest.schema.json", - "sha256": "7ed0714e4d6120dc4098410db07ca5286f64677b05f03b7532f0ca73bdda2e5d" + "path": "fixtures/ui/native-form.plugin.json", + "sha256": "082dd621eadad410a0aadc08e35e213e6818853ae4c18a2d38ce7c6f5bd8946d" }, { - "path": "proposals/toolchains/README.md", - "sha256": "eeec7c53075056649d103af78fa9c5b2e0a28bc60c8049d4ae2a9d3ec15e17e7" + "path": "fixtures/ui/native-panel.plugin.json", + "sha256": "b35c69c85f4e53f4ccd31e320e2f91387dde1935e10bde5b13019fd6e925fe1d" }, { - "path": "proposals/toolchains/fixtures/java-tool-definition.json", - "sha256": "23408937de3c5d77712657e8753d23d7cd07d91e73d5c3d0788d3ae283f56134" + "path": "fixtures/ui/web-panel.plugin.json", + "sha256": "1ccaf5bb58e0296b8d244e6951d62c1f8e9e35e2e2bd467d4368fc31d7f42fd8" }, { - "path": "proposals/toolchains/fixtures/local.json", - "sha256": "0d24ed31d1b330cd45147274747206dada3dc660deae66eb72acdf359e2092ba" + "path": "fixtures/vcs/vcs-provider.plugin.json", + "sha256": "7d0c85b6e0531a28e877050c2da15470d0bfe5ccd4c98215e7824d4445d977a9" }, { - "path": "proposals/toolchains/fixtures/manifest-rejections.json", - "sha256": "de2ebb472c31eb8e5ff3fd9217572311dc65df1bb62a17736e4192d105576674" + "path": "native-ui.schema.json", + "sha256": "62c6e49ec274c52efb70cc3562f261e11d4dfb8cc59c8e13f7774321266f2837" }, { - "path": "proposals/toolchains/fixtures/mise-provider.json", - "sha256": "55f9b992e8d4988afb3e8574025af2e593c71c799a98bd9b237b9d01c5fc474c" + "path": "plugin-index.schema.json", + "sha256": "6771cb0e8c1ba101e6fc90b436de1b74f057ff0d58cc90f92e669edc9a102fc6" }, { - "path": "proposals/toolchains/fixtures/query-cases.json", - "sha256": "d1a7c3e8b8df18cba3d40cca437b712a787b8935d6c8eb7b567e74736d42c203" + "path": "plugin-manifest.schema.json", + "sha256": "3320d28b7c77d628164b25125a9c08f6fba86a4b4e261a37f052dd0d9502f819" }, { - "path": "proposals/toolchains/fixtures/resolution-cases.json", - "sha256": "8292f41dc287e0d96638a3583324354f106cc0bd149c91ab76f80525893c5cab" + "path": "toolchains/README.md", + "sha256": "65b98b081650aac5b1208ab823c56ec5d335a2300c76867e6c814fca08b5b50e" }, { - "path": "proposals/toolchains/local.schema.json", - "sha256": "a27683a7d4086af4e24cfae220af4d2089bb7c14cb0f9fbd7a83e3ba7a193a51" + "path": "toolchains/proposal/README.md", + "sha256": "b7b324bc688a218c94f7cbceb425a8976f024ac2ac18d049aac747887b99496b" }, { - "path": "proposals/toolchains/manifest-contributions.schema.json", - "sha256": "9b66f66bfeb5ad32b7415c084048f95ad54d7b1628bd89ce146dfcfc4c746ebf" + "path": "toolchains/proposal/local.schema.json", + "sha256": "e875e0f90b2481f3b548c0f72be3fd6bd88ba6993f42011cb4a0f7441373d436" }, { - "path": "proposals/toolchains/manifest.md", - "sha256": "c8074693b61fa55cdecd8cda923e9c21a1be923093ef1f00acafb99844afec82" + "path": "toolchains/proposal/manifest-contributions.schema.json", + "sha256": "6189f332f2bfab7d3e6ed527c6a0aed046ab71b137d9c8455584a7ac7eeb4b78" }, { - "path": "proposals/toolchains/protocol.md", - "sha256": "5218ad632fcdc09b317dc2b248e68528fedc9afd030cd2e3d276f1c34b4a851d" + "path": "toolchains/proposal/manifest.md", + "sha256": "77e4e8a8e1b4d67c548edc9242afaa0f071466c431ea7e290c25993df218defb" }, { - "path": "proposals/toolchains/query.schema.json", - "sha256": "4cb8093ee80d99527fb05d4cf7aa86a55f74711e9bf9e61139db3681d7361219" + "path": "toolchains/proposal/protocol.md", + "sha256": "5def30935a4e34da4f857f262a356d16df9311df504d9f535b6392bb853e90a9" }, { - "path": "proposals/toolchains/resolution.schema.json", - "sha256": "c0ce78a4df543e2bae06877ae9a49c425a4fb81f5af3ff4626b064c8063b288c" + "path": "toolchains/proposal/query.schema.json", + "sha256": "053aebb0a0a05f1dc8c9e8cf82c35f6e34dbf20b3fd5b20706c6ecab6741f58c" }, { - "path": "toolchains.md", - "sha256": "0d9e19ea676836d469e3342f01d1e0eef60c6cd321f3671e1b5db1235690c39f" + "path": "toolchains/proposal/resolution.schema.json", + "sha256": "8c2084680ff062b1a415cacd6ca72e4da4b5ed85055dce6a3cf403adc489692b" } ] } diff --git a/contract/v1/README.md b/contract/v1/README.md index ab35b71..54f53f4 100644 --- a/contract/v1/README.md +++ b/contract/v1/README.md @@ -8,23 +8,32 @@ not need to be rebuilt for each Lithe release. This directory is the cross-platform source of truth: -| File | Purpose | +| Path | Purpose | | --- | --- | | `plugin-manifest.schema.json` | JSON shape of `plugin.json` | -| `fixtures/*.plugin.json` | Valid example manifests, named after the capability they exercise rather than a published plugin: `language-*` (packaged, system-command, and host-integration language servers; `node-runtime` for a discovered runtime), `hello-lithe` and `web-panel` (UI), `settings-v2`, and `build-*` (level 3 build systems and the two forms of a build provider) | -| `fixtures/tool-definition.plugin.json`, `fixtures/toolchain-provider.plugin.json` | Production-shape Core fixtures for tool definitions and a packaged toolchain provider; require explicit new host capabilities, not yet supplied by native products | -| `fixtures/keymap-import-provider.plugin.json` | Two independent shortcut import providers sharing one packaged backend; native source access is not implemented yet | -| `fixtures/invalid-manifests.json` | Edits to a valid manifest and the rejection code every host must report | -| `fixtures/command-plans.json` | Golden run/test command expansions and language-server launch plans | -| `fixtures/runtime-versions.json` | Golden outcomes of the launch runtime version check | -| `fixtures/build-plans.json` | Golden build-system plans and decoded outputs | -| `fixtures/function-protocol.json` | Golden plugin-function negotiation, registration, invocation, outcome, progress, and refusal cases | -| `fixtures/project-generator.plugin.json`, `fixtures/project-generator-templates/` | Project generators: template files, a scaffolding command, and an archive download | -| `fixtures/project-plans.json` | Golden `project.*` form evaluations, generation plans, and name checks | +| `native-ui.schema.json` | JSON shape of native UI documents rendered by the host | | `plugin-index.schema.json` | JSON shape of a marketplace source index | -| `fixtures/marketplace/*.json` | Marketplace index, availability, source URL, and install review golden cases | -| `toolchains.md` | Implemented Core foundation: plugin definitions/provider declarations, verified-origin registry, reply validation, directory pins, snapshot resolution, version matching and discovery plans; native hosts are not connected | -| `proposals/toolchains/` | Broader design draft, including unimplemented query transport, native lifecycle/persistence, consumer migration, settings UI and manager plugins; examples are review projections, not installable manifests | +| `fixtures/` | Valid example manifests and golden cases, one folder per contract area (below). Manifests are named after the capability they exercise rather than a published plugin. Case files name their base manifest by file name, so file names are unique across the folders | +| `toolchains/README.md` | Implemented Core foundation: plugin definitions/provider declarations, verified-origin registry, reply validation, directory pins, snapshot resolution, version matching and discovery plans; macOS uses it for its read-only settings pages and for Build and run tools, Windows is not connected | +| `toolchains/proposal/` | Broader design draft, including unimplemented query transport, native lifecycle/persistence, consumer migration, settings UI and manager plugins; examples are review projections, not installable manifests | + +| Fixture folder | Contents | +| --- | --- | +| `fixtures/manifest/` | `hello-lithe.plugin.json`, the smallest UI plugin, and `invalid-manifests.json`: edits to a valid manifest and the rejection code every host must report | +| `fixtures/languages/` | Language support: packaged, system-command, and host-integration language servers, `node-runtime` for a discovered runtime, and `language-grammar` with `grammars/`; `command-plans.json` holds golden run/test command expansions and language-server launch plans, `runtime-versions.json` the golden outcomes of the launch runtime version check | +| `fixtures/backend/` | Plugin backends (`backend`, `backend-workspace`, `command-backend`, `terminal-backend`); `function-protocol.json` holds golden plugin-function negotiation, registration, invocation, outcome, progress, and refusal cases, `plugin-logging.json` the golden `litheLogging` negotiation and notifications | +| `fixtures/editor/` | Editor actions served by a backend | +| `fixtures/ui/` | Native controls, forms, and panels, and a web panel | +| `fixtures/settings/` | Plugin settings declared with `settings: 2` | +| `fixtures/build-systems/` | Level 3 build systems (`build-*`: static file, command output, host integration, instance options, model producer and consumer, tool path, toolchains); `build-plans.json` holds golden build-system plans and decoded outputs | +| `fixtures/build-providers/` | Build providers (`build-provider*`) and the golden cases of their configuration, run options, run and debug preparation, execution, and test protocols | +| `fixtures/debug-adapters/` | Debug adapters (standalone, served by a language server, remote attach) and `debug-adapter-rejections.json` | +| `fixtures/vcs/` | A version control provider | +| `fixtures/keymap-import/` | Two independent shortcut import providers sharing one packaged backend | +| `fixtures/project-generators/` | Project generators: template files (`templates/`), a scaffolding command, and an archive download; `project-plans.json` holds golden `project.*` form evaluations, generation plans, and name checks | +| `fixtures/toolchains/` | Production-shape fixtures for tool definitions and a packaged toolchain provider; they require `toolDefinitions` and `toolchainProviders`, which only macOS advertises. `proposal/` holds the examples and cases of the toolchains draft | +| `fixtures/package-trust/` | `publisher-bundled.plugin.json` and `signature-vectors.json`: public cross-platform vectors for the package publisher signature: the RFC 8032 test key, a fixed package with non-ASCII file names, its canonical payload, RFC 8032 deterministic signature, and content digest. Both platforms must build the same canonical payload and digest and accept the recorded signature; signers that add randomness, such as CryptoKit, check the signature by verification instead of byte comparison | +| `fixtures/marketplace/` | Marketplace index, availability, source URL, and install review golden cases | Validation and template expansion are implemented once in Rust Core (`rust/lithe-core/src/plugins/`, with build systems in @@ -43,15 +52,16 @@ fields are rejected; editors associate the schema by file name instead. ## Plugin-owned toolchains (Core foundation) -The [toolchains draft](proposals/toolchains/README.md) records the replacement +The [toolchains draft](toolchains/proposal/README.md) records the replacement design discussed in [PR #1581](https://github.com/1lck/Lithe-IDEA/pull/1581#issuecomment-6541366112). All tool definitions belong to enabled plugins; the host supplies only generic query, validation, resolution, persistence, and presentation mechanisms. It does not add a built-in tool-definition table or interpret version-manager configuration formats. -The [implemented Core contract](toolchains.md) now accepts `toolDefinitions: 1` -and `toolchainProviders: 1` declarations and provides six `toolchains.*` commands. +The [implemented Core contract](toolchains/README.md) now accepts `toolDefinitions: 1` +and `toolchainProviders: 1` declarations and provides the `toolchains.*` commands +listed there. `executableResolution: 2` applies to tool-definition candidates only; it does not admit new sources into existing Build candidate sets. Definitions are entirely plugin-owned, with reserved short IDs checked against host-verified official @@ -60,11 +70,13 @@ provenance when constructing the registry. macOS advertises these capabilities for its read-only toolchain settings pages. The native adapter scans declarations, probes checked executables, queries plugin backends and watches their inputs; Core consumes captured facts and owns validation -and resolution. Windows does not advertise the capabilities. Saving pins, +and resolution. A separate fresh resolution serves Build tools and run plans +whose `toolchain` executable names a plugin-defined tool rather than a host +toolchain; settings detections never grant launch authority. Windows does not advertise the capabilities. Saving pins, `toolchainConsumers`, generic runtime launches and live backend activation remain -unsupported, and current Java/Maven/Build/Run selection is unchanged. Version-manager +unsupported, and Java and language-server consumers keep their previous adapters. Version-manager implementations such as mise belong to their own plugin packages. Proposal -envelopes remain separate from production manifests. See [the implemented slice](toolchains.md). +envelopes remain separate from production manifests. See [the implemented slice](toolchains/README.md). ## Shortcut import providers (Core foundation) @@ -124,10 +136,10 @@ and provider, revokes them when the import or session ends, and rejects files replaced or changed after capture. A new snapshot for that provider revokes the previous one. Errors never disclose absolute paths. -This is a foundation, not an available product extension: macOS implements the -scoped reader, but provider discovery, the product import workflow and Windows -source access are not implemented. Hosts must not advertise this capability -until they implement the complete workflow. No existing SDK advertises it. +macOS advertises `keymapImportProviders` 1 and implements the scoped reader. +Windows has no import source reader and does not advertise the capability; a +host advertises it only with the complete import workflow. The official Node, +Java and Rust SDKs implement `litheKeymapImport` 1. ## Package layout @@ -150,7 +162,7 @@ symbolic links. A host treats an installed package as read-only. | `id` | Stable plugin ID: lowercase letters, digits, `.` and `-`. | | `displayName` | Name shown in plugin management. | | `version` | Plugin version, `major.minor.patch`. Independent of the Lithe version. | -| `pluginAPIVersion` | Declarative plugin API level of the manifest format. Hosts list the levels they support; Lithe's own version is never used for compatibility. This contract defines levels `1`, `2`, and `3`; see [API levels](#api-levels). | +| `pluginAPIVersion` | Declarative plugin API level of the manifest format. Hosts list the levels they support; Lithe's own version is never used for compatibility. This contract defines levels `1`, `2`, `3`, and `4`; see [API levels](#api-levels). | | `requires` | Level 3 only, and required there: every capability the manifest uses, with the version of that capability's contract it was written against; see [Capability versions](#capability-versions-level-3). | | `vendor` | `id`, `displayName`, and `signatureRequirement` (see Trust). | | `permissions` | Capabilities the plugin may ask the host to perform. | @@ -161,8 +173,8 @@ symbolic links. A host treats an installed package as read-only. | `settings` | User-adjustable plugin options (level 2 and later). | | `ui` | Host-rendered tool windows and commands (level 2 and later). | | `buildSystems` | Project-level build tools (level 3). | -| `toolDefinitions` | Plugin-owned tool policy ([Core foundation](toolchains.md)); requires `toolDefinitions: 1` and `executableResolution: 2`; macOS hosts read-only discovery. | -| `toolchainProviders` | Backend fact-provider declarations ([Core foundation](toolchains.md)); requires `toolchainProviders: 1`, backend/server-process capabilities and `toolchains.provide`. | +| `toolDefinitions` | Plugin-owned tool policy ([Core foundation](toolchains/README.md)); requires `toolDefinitions: 1` and `executableResolution: 2`; macOS hosts read-only discovery and resolves Build and run tools. | +| `toolchainProviders` | Backend fact-provider declarations ([Core foundation](toolchains/README.md)); requires `toolchainProviders: 1`, backend/server-process capabilities and `toolchains.provide`. | | `keymapImportProviders` | Independent shortcut import providers; requires `keymapImportProviders: 1`, `backend: 2`, `serverProcess: 1` and `keymaps.provide`. macOS provides access to explicitly selected sources; Windows does not yet advertise this capability. | Unknown fields are refused (`plugin.manifest.invalid`) so an older host @@ -238,7 +250,7 @@ activation event, and a plugin may consist of grammars alone. (`@a.b` resolves to the longest declared prefix, as in Monarch), `cases` guards naming an undeclared attribute, and `nextEmbedded` values other than `@pop` or a language ID. The fixture - [`fixtures/grammars/http.monarch.json`](fixtures/grammars/http.monarch.json) + [`fixtures/languages/grammars/http.monarch.json`](fixtures/languages/grammars/http.monarch.json) is a complete example. - `configuration` sets `lineComment`, `blockComment` (open and close), `brackets`, and `autoClosingPairs`. Each delimiter is 1 to 16 bytes without @@ -298,7 +310,7 @@ prints to stderr (hosts capture it together with stdout), and `--version` for `node`. Java 8 and older report `1.`, which is read as ``. The reported version is the first dotted number in the probe output, ignoring lines that start with `Picked up ` (a JVM announcing `JAVA_TOOL_OPTIONS` -before its version); see `fixtures/runtime-versions.json`. +before its version); see `fixtures/languages/runtime-versions.json`. #### Host integrations (API 2) @@ -431,7 +443,33 @@ Malformed or unnegotiated input produces one category-only the rejected payload. Ingress admits at most 600 facts per minute per session, then uses the existing bounded Core handoff to the host's single writer; overload increments its dropped counter. No polling timer or extra writer is -created. Fixture: `fixtures/plugin-logging.json`. +created. Fixture: `fixtures/backend/plugin-logging.json`. + +Declarative contributions have no plugin process to send `lithe/log`, so whoever +executes the declared work records the same `plugins.operation.finished` and +`plugins.operation.failed` events on the same `plugins//host` channel. A +declarative plugin writes no logging code. Each start of a `languageServers[]` or +`debugAdapters[]` entry is one `languageServer/start` or `debugAdapter/start` +operation, from launch resolution until `initialize` succeeds. Each run of a +`buildSystems[]` model or dependencies command source is one `lithe/build/model` or +`lithe/build/dependencies` operation, using the SDK request names. A failure +carries one `code`: + +| Code | Meaning | +| --- | --- | +| `host.planFailed` | The declared command could not be planned | +| `host.executableUnavailable` | The declared executable or launch input could not be resolved | +| `host.launchFailed` | The resolved process could not be started | +| `host.initializeFailed` | A started server or adapter failed its `initialize` handshake | +| `host.timedOut` | The attempt exceeded its deadline | +| `host.exitedEarly` | A server or adapter exited before `initialize` succeeded | +| `host.commandFailed` | A build command exited unsuccessfully | +| `host.outputInvalid` | A build command's output is not a valid document | + +User cancellation and superseded attempts record nothing. Plugin output, paths, +and error text never appear in these facts. The executing owner and the +host-facing command are specified as `diagnostics.recordPluginOperation` in +[`rust-core-api.md`](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/rust-core-api.md). Severity follows the actual result. An operation that failed for the user is ERROR, a recovery or fallback is WARN, and a useful successful user operation @@ -495,7 +533,7 @@ a plugin place its options anywhere in the host's settings without the host knowing the plugin. Hosts keep only generic registration points: a page per language and per build system, sections, one control per setting type, a project/global layer switch, and buttons. What appears there comes from the -manifest. Fixture: `fixtures/settings-v2.plugin.json`. +manifest. Fixture: `fixtures/settings/settings-v2.plugin.json`. ```json "settings": [ @@ -626,20 +664,21 @@ hosts. Level 3 versions each section on its own. A level 3 manifest lists in | `languages` | `languages[]` | `1`, `2` (adds [`grammar` and `configuration`](#grammars-and-editor-configuration-languages-2)) | | `packageTrust` | `vendor.bundledSignatureRequirement`: origin-scoped trust for a host-sealed bundled copy of a publisher-signed plugin | `1` | | `languageServers` | `languageServers[]` apart from their launch fields | `1`, `2` (adds [`workspaceConfiguration`](#workspace-configuration-languageservers-2)) | -| `serverProcess` | The launch fields of language servers, of build providers with a process of their own, of VCS providers, and of the plugin backend: `launch`, `arguments`, `validationArguments`, `runtimeDependencies`, `initializationOptions` | `1` | +| `serverProcess` | The launch fields of language servers, of build providers with a process of their own, of VCS providers, and of the plugin backend: `launch`, `arguments`, `validationArguments`, `runtimeDependencies`, `initializationOptions` | `1`, `2` (adds `runSelectedRuntime` for [debug adapters](#api-level-4-debug-adapters); level 4; v2 Core only, macOS and Windows advertise `1`) | | `executionProviders` | `executionProviders[]` | `1` | | `settings` | `settings[]` and `settingActions[]` | `1`, `2` (adds [placement, scope, effect, page buttons](#settings-v2-placement-scope-effect-and-page-buttons)) | | `ui` | `ui` | `1`, `2` (web content), `3` (native controls), `4` ([backend-only commands](#backend-only-commands-ui-4)) | -| `buildSystems` | Declarative build detection, models, and command plans | `1` | -| `buildProviders` | `buildSystems[].provider`, including own-process and shared language-server providers; v2 tasks, v3 tests, v4 run, v5 native debug preparation, v6 read-only model inputs | `1`, `2`, `3`, `4`, `5`, `6` | +| `buildSystems` | Declarative build detection, models, and command plans; v2 executable setting adapter; v3 instance-scoped options; v4 declarative preparation roles | `1`, `2`, `3`, `4` | +| `buildProviders` | `buildSystems[].provider`, including own-process and shared language-server providers; v2 tasks, v3 tests, v4 run, v5 native debug preparation, v6 read-only model inputs, v7 native configuration, v8 inline test reports, v9 opaque semantic test selectors, v10 typed run options and service/task preparation, v11 registry connection debugging | `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11` | | `buildModelInputs` | Optional public model sharing between enabled workspace plugins | `1` | | `vcsProviders` | `vcsProviders[]` | `1` | | `debugAdapters` | [`debugAdapters[]`](#api-level-4-debug-adapters); v2 adds target arguments/environment | `1`, `2` | | `projectGenerators` | [`projectGenerators[]`](#project-generators): entries of the New Project window | `1` | | `backend` | [`backend`](#plugin-backend): the plugin's own long-lived process | `1`, `2` (adds [start without a page and workspace contexts](#backend-2-workspace-contexts)) | -| `toolDefinitions` | [Tool identities, layouts, discovery and four-layer candidates](toolchains.md) | `1` (Core; macOS read-only settings) | -| `toolchainProviders` | [Provider declarations and permission-bound answer validation](toolchains.md) | `1` (Core; macOS read-only settings) | -| `executableResolution` | [Shared executable candidates](#shared-executable-candidates-executableresolution-1); v2 adds tool-definition candidates without changing existing Build candidate sets | `1`, `2` (v2 Core; macOS read-only settings) | +| `toolDefinitions` | [Tool identities, layouts, discovery and four-layer candidates](toolchains/README.md) | `1` (Core; macOS settings pages, Build and run tools) | +| `toolchainProviders` | [Provider declarations and permission-bound answer validation](toolchains/README.md) | `1` (Core; macOS settings pages, Build and run tools) | +| `executableResolution` | [Shared executable candidates](#shared-executable-candidates-executableresolution-12): candidate ordering and strict selection; v2 adds tool-definition candidates and canonical project/subproject toolchain registry bindings without changing existing Build candidate sets | `1`, `2` (v2 Core; macOS settings pages, Build and run tools) | +| `keymapImportProviders` | `keymapImportProviders[]`: independent shortcut import providers | `1` (macOS; Windows does not advertise it yet) | Version 1 of every capability means what its section means at level 2 (runtime launches, launch templates, `initializationOptions`, and so on), so @@ -684,8 +723,8 @@ in `plugins.declarative.validate`, `plugins.marketplace.validateIndex`, and - `hostCapabilities` has no effect on levels 1 and 2. The macOS host sends its capability table with validation, marketplace filtering -and install review. It advertises `buildSystems: [1, 2]` and -`buildProviders: [1, 2, 3, 4, 5, 6]`; native Provider models, dependencies, tasks, tests and launch preparation +and install review. It advertises `buildSystems: [1, 2, 3, 4]` and +`buildProviders: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]`; native Provider models, dependencies, tasks, tests and launch preparation share workspace-owned sessions. Backend and VCS support use their own entries in the same table. Core validates supported versions without a host table, but admission must use the actual host table. Windows does not yet advertise @@ -780,7 +819,7 @@ even inside the host seal. The optional declaration alone grants no trust. Hosts without `packageTrust: 1` filter this version before download and reject its manifest; no new application-version field is needed. An older manifest without the declaration keeps its existing policy. The valid example is -`fixtures/publisher-bundled.plugin.json`. +`fixtures/package-trust/publisher-bundled.plugin.json`. A host pins the signature requirement of its official plugin IDs so a package cannot downgrade itself. Selecting a file from disk never overrides that pin, @@ -986,9 +1025,16 @@ They are excluded as `plugin-disk-install-state`. No runtime write targets the signed app bundle or Windows installation resources, so these paths do not change code signatures or Sparkle differential-update baselines. -Windows currently shares the schemas and Rust validation only; it has no -implementation of this declarative ZIP/directory snapshot and approval flow. -Its existing worker-package import is a separate format. The feature matrix +Windows has a narrower disk import. It copies a ZIP or unpacked directory into +a private snapshot under the application's local data directory +(`plugin-temp/lithe-plugin-disk-`), reviews it with +`plugins.marketplace.installReview`, and installs only after the user confirms. +Confirmation re-runs the review and refuses a snapshot whose `plugin.json` +changed after review. It does not verify publisher signatures, does not compute +or recheck a digest of the other package files, and does not record approved +programs or provenance; its installation record holds only the ID, active +version and enabled state. It has no marketplace install path. Its existing +worker-package import is a separate format. The feature matrix therefore keeps Windows `partial` and macOS runtime verification `pending`; static checks and Node resource tests alone do not verify the UI confirmation, complete program rendering, cleanup, or bundle hashes in a real workflow. @@ -1007,7 +1053,7 @@ a project file, `build-command-stdout` decodes a command's standard output, `build-host-integration` hands the model to a host integration. They use Cargo, Go, Gradle, and Maven as realistic inputs; they are not those plugins. `build-provider` and `build-provider-language-server` show the two -forms of a build provider, and `fixtures/build-plans.json` is the golden list +forms of a build provider, and `fixtures/build-systems/build-plans.json` is the golden list of plans and decoded outputs every host gets from them. A build system needs `pluginAPIVersion` 3 with `buildSystems` in `requires`, @@ -1035,6 +1081,20 @@ declaration are relative, use `/`, and stay inside their base directory | `layout` | `excluded` directories relative to each project, added to every project as `excluded` content roots. | | `languageImport` | The project import Lithe's Java language server performs for this build system: `kind` `maven` or `gradle`, and `options` mapping its settings to the plugin's own option IDs. `maven` has `profiles` (`multiSelect`), `settingsFile` (`file`) and `localRepository` (`directory`); `gradle` has `offline` (`boolean`) and `gradleUserHome` (`directory`). `build.languageImport` turns the user's values into the `mavenContext` or `javaGradle` input of `lsp.startServer`; settings without an option keep the language server's defaults. | +With `requires.buildSystems: 3`, an option may declare `scope: "instance"`. +Omitted scope (or `"workspace"`) keeps workspace/build-system storage. Instance +values are independently persisted by normalized detected root: changing or +resetting `a` cannot change `b`, even when both accept the same choices. Explicit +`false`, `""`, and `[]` remain stored user values. The host merges only the +matching scope before detection, static run generation, model/dependency/task +requests, synchronization fingerprints, and language import. Instance data cannot +override workspace options, and workspace values cannot silently override an +instance option. When an existing option moves to instance scope, macOS copies its shared value +to each detected or previously stored root, preserving explicit instance values +and empty markers. It removes the shared representation only after readback, then +redetects static models. Failure retains the source and blocks publishing defaults +until retry; scope is not a fallback chain. See `fixtures/build-systems/build-instance-options.plugin.json`. + ### Presentation Each enabled build system gets a tool window of its own, titled with its @@ -1157,7 +1217,7 @@ same invocation resolver; explicit invalid tool/runtime selection is a failure, never permission for independent discovery. It must reject stale project facts rather than guess a target. -The host offers `litheBuild.versions: [6, 5, 4, 3, 2, 1]`. Version 2 adds task execution +The host offers `litheBuild.versions: [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]`. Version 2 adds task execution and its progress notification to the two version 1 methods. A process that chooses version 1 cannot receive execution; unsupported negotiation fails explicitly. A plugin that needs this behavior declares capability version 2 @@ -1190,8 +1250,8 @@ Provider session owns children and writable workspace state; no page, additional runtime, directory or cross-plugin private cache is required. Only public typed results may cross domain boundaries. -Canonical examples: `fixtures/build-provider-execution.plugin.json` and -`fixtures/build-execution-protocol.json`. Protocol v2 is a construction +Canonical examples: `fixtures/build-providers/build-provider-execution.plugin.json` and +`fixtures/build-providers/build-execution-protocol.json`. Protocol v2 is a construction primitive for #1495; it does not by itself migrate Maven or Java consumers. ### Provider test execution (buildProviders 3) @@ -1213,7 +1273,7 @@ widening the test run. Paths, scopes and declared filters are validated before the request is sent. The host's report project list only resolves report globs; it is not a second backend project model. -Only a process choosing protocol version 3 or 4 receives `lithe/build/test`. +Only a process choosing protocol version 3 or newer receives `lithe/build/test`. Versions 1 and 2 explicitly refuse tests. Version 3 retains every version 2 task behavior. Tests use the same Core-assigned operation identity, `lithe/build/progress`, tool hint, bounded `{ exitCode, output }` result, @@ -1224,8 +1284,8 @@ system has one. Configuration validity is retained through diagnostic and report decoding; late results cannot update a changed workspace/configuration. Java/Maven migration remains separate. No new resource/cache location is introduced. -Canonical examples: `fixtures/build-provider-tests.plugin.json` and -`fixtures/build-test-protocol.json`. +Canonical examples: `fixtures/build-providers/build-provider-tests.plugin.json` and +`fixtures/build-providers/build-test-protocol.json`. #### Test discovery @@ -1234,7 +1294,7 @@ Provider tests may additionally declare `discovery: true`. This requires both `build.testDiscoveryPlan` accepts the common plan fields and optional `refresh` and `tool`, returning a request for the whole instance: `lithe/build/discoverTests` with its effective options. Project/output-file -selection is refused. This method also requires protocol version 3 or 4 and uses +selection is refused. This method also requires protocol version 3 or newer and uses the existing bounded request/cancellation lifecycle, without execution progress. The provider returns `{ "format": "litheTestDiscovery", "version": 1, @@ -1259,7 +1319,7 @@ package changes and workspace teardown revoke the cached list and in-flight discovery. A native action must revalidate the selected ID against the current list before submitting its exact selectors to `lithe/build/test`; stale toolbar selections cannot run. The backend must reject a selector whose facts -have become stale externally. Canonical example: `fixtures/build-test-discovery.json`. +have become stale externally. Canonical example: `fixtures/build-providers/build-test-discovery.json`. ### Provider run preparation (buildProviders 4) @@ -1311,7 +1371,78 @@ Unknown fields are ignored. The host calls `build.decodeRunPreparation` with its workspace root, instance root and raw result; any malformed plan fails as `plugin.build.invalidOutput` before executable resolution. -#### Native debug preparation (version 5) +#### Typed run options and service/task preparation (version 10) + +Version 10 retains versions 1–9 and extends the same `lithe/build/prepareRun` +method. A provider run may declare `execution: "service"` or `"task"` only +with `requires.buildProviders: 10`. It still uses `fromTargets.tags`, never +host command or extension templates. Preparation ends before the host starts +the process; the host owns stdout/stderr, process identity, Stop and cleanup. +This version does not introduce provider-owned background services. + +The optional `launchOptions` object contains required `execution` +(`application`, `service` or `task`), `runtimeArguments` and `arguments` +(string arrays), and `environment` (a string map). Optional `entrypoint` is an +opaque nonempty string of at most 4,096 UTF-8 bytes without NUL; the owning +provider validates its meaning. Optional `workingDirectory` is a nonempty +workspace-relative directory, `.` allowed, without backslashes, absolute +paths, empty components or `..`. Unknown option fields are refused. Argument +and environment bounds match the launch result above, including empty argv +values. Options describe resolved user choices; providers must preserve +argument boundaries and cannot silently ignore unsupported choices. + +`execution` must match the run declaration (default `application`). Service +and task preparation require `launchOptions`; legacy application preparation +without it retains version 4 behavior. Hosts must reject options before +sending them to a session that negotiated a version below 10. Both SDKs +likewise reject unsupported or malformed options before invoking a handler. +Build options, resolved tool hints and native settings remain their existing +sources; launch options do not create a second profile or tool configuration. + +The host supplies `workspaceRoot` to `build.prepareRunPlan` when options contain +an explicit working directory. Core validates it against the canonical workspace, +including aliases, before preparation. The host passes the same options to +`build.decodeRunPreparation`; the result must use exactly that directory. +Only this explicit override may lie outside the selected instance. Without +an override the original instance containment rule still applies. Other +launch result validation and cancellation/stale-result behavior are unchanged. +Canonical fixtures: `fixtures/build-providers/build-provider-run-options.plugin.json` and +`fixtures/build-providers/build-run-options.json`. + +#### Registry connection debugging (buildProviders 11) + +Version 11 retains versions 1–10. A provider service run may declare +`debug: {"configurationType":"example"}`. This is a public DebugAdapter Registry +alias, not another plugin's private language server or a local adapter ID. +The host selects one currently enabled adapter advertising the type and `attach`, +respecting an explicit qualified adapter selection. Missing or ambiguous entries +fail before starting the provider process. It sends: + +```json +{"debug":{"adapterID":"dev.example/debug","configurationType":"example", + "target":{"kind":"connection","host":"127.0.0.1","port":51235}}} +``` + +These are the exact three debug fields and three target fields. The host owns +the port (integer 1–65535), process, readiness and cleanup. Explicit null, unknown +fields, remote hosts and application/task execution are rejected. The type must +match the selected run declaration. Both manifest and process agreement must +support v11; older SDKs reject before invoking a handler. The provider uses its +upstream engine to suspend the service until attachment; it owns runtime-specific +flags and fails if it cannot honor the request. It returns the ordinary portable +Run process plan, without selecting an endpoint or returning DAP arguments. + +The host retains the adapter's qualified identity, package and registry generation +through preparation, readiness and attach. Disable, replacement or stale input +revokes the operation and cleans its owned process. Adapter configuration is +produced by existing `debug.adapters.startArguments` with a `connection` target; +the selected registered adapter owns its transport. In particular, +`tcpLanguageServer` may refer only to a language server declared by that adapter's +own plugin. No built-in language debugger fallback is permitted. +This does not change same-package native `prepareDebug` (v5). +Canonical fixture: `fixtures/build-providers/build-connection-debug-options.json`. + +### Native debug preparation (version 5) A provider run may additionally declare `debug: {"adapter":"native"}` with `requires.buildProviders: 5` and `requires.debugAdapters: 2`. `adapter` is a @@ -1350,8 +1481,8 @@ its own permissions and package process declaration. The launch configuration re operation; the Debug host owns launched sessions and plugin withdrawal cleanup. Saved Run configurations keep their existing selectors and prepare again for Debug. -Fixtures: [native declaration](fixtures/build-provider-debug-preparation.plugin.json) -and [native result](fixtures/build-debug-preparation.json). This does not provide +Fixtures: [native declaration](fixtures/build-providers/build-provider-debug-preparation.plugin.json) +and [native result](fixtures/build-providers/build-debug-preparation.json). This does not provide Java/JDT preparation, long-running services or cross-plugin collaboration. @@ -1368,8 +1499,84 @@ Every restart re-resolves the enabled instance/model and sends a fresh preparati Missing plugins/targets/models fail explicitly; there is no command fallback. Saving and running work before detector-generated Run configurations exist. Service coordination, debug target registration and Java/Maven migration remain -separate. See `fixtures/build-provider-run-preparation.plugin.json` -and `fixtures/build-run-preparation.json`. +separate. See `fixtures/build-providers/build-provider-run-preparation.plugin.json` +and `fixtures/build-providers/build-run-preparation.json`. + +### Optional native configuration (buildProviders 7) + +An own-process provider may opt into `buildSystems[].configuration: {"source":"provider"}` +with `requires.buildProviders: 7`. Shared language-server providers cannot opt in. +`build.configurationPlan` uses the same selected instance/options/environment/tool +snapshot as model loading and emits `lithe/build/configuration`. It has no project, +output file, or mutation parameters. Wire versions below 7 refuse the method. +The negotiated v7 server feature is `litheBuildConfiguration: true`; earlier methods +retain their existing admission rules. Configuration reads are bounded requests +with the existing cancellation, timeout, workspace and package ownership. + +`build.decodeConfiguration` validates `result` and returns `configuration`: +`{"format":"litheBuildConfiguration","version":1,"sources":[...]}`. There are at most +8 sources, each with a unique provider-owned `id`, `scope` (`installation`, `user`, +`workspace`), file `uri`, exact byte `revision` (`missing` or `sha256:` plus 64 lower-case +hex digits), `writable`, and up to 64 `fields`. +Installation sources are always read-only. Two scopes may alias one URI only when +both are read-only and have identical revision and fields. File URIs +cannot carry credentials, queries or fragments. + +Each field contains `id`, `kind` (`text`, `file`, `directory`, `boolean`, `stringList`), +`value` (raw native value, or null for absent), `effectiveValue`, `effectiveSource` +(an upstream technical origin ID). IDs are opaque bindings interpreted by the +provider; there is no document-format, XPath, XML root or element binding in the +host contract. Fields cannot duplicate or expose credential/secret fields; +values and identifiers are bounded and kind-checked. Full native documents, secret +values, credential-bearing URLs and private keys must never enter this response, +logs or normal controls. Source discovery, interpolation, precedence and effective +values belong to the selected upstream engine; Core does not infer them. + +The native host populates raw/inherited values, displays scope and target URI, +maintains drafts, and requires explicit Save. Opening, cancelling, losing focus +and leaving a page do not write native sources. Passive page reads refuse a +project-owned executable (such as a cold Wrapper), and cannot silently delegate +failed automatic tool discovery to the Provider. An explicit Read using project +tool action authorizes that tooling; explicit Save also owns its required read-back. The confirmed edit is +`{sourceID, revision, values:{:}}`; null removes only +that native override. Unchanged inherited values must not create overrides. + +`build.configurationEditPlan` takes the same instance/tool/options context, +`snapshot`, confirmed `edits`, and a fresh, empty, **already existing** `outputFile` +inside the host's isolated workspace state. It validates the bounded field IDs, +types and revision, then plans `lithe/build/editConfiguration` (protocol 7). +The provider owns its native format and uses an upstream/mature reader/writer; +Maven XML, JSON and TOML do not add host format cases. The provider verifies the +source revision and writes replacement bytes only to that private staging file. +Open without CREATE or following a symlink, validate that it is the empty staging +file before writing, and never truncate another file. A cancelled operation cannot +recreate removed staging. +Preserve unrelated content, format-supported comments and secrets privately. + +The reply is only `{format:"litheConfigurationEdit", version:1, sourceID, +revision, changed}`. `build.decodeConfigurationEdit` binds this receipt to the +same snapshot/draft and rejects extra fields. Full documents and private bytes +must never appear in RPC, UI, logs or model inputs. The native adapter validates +the staged bytes against the receipt SHA-256 before publication. A no-op creates +no source or native override. Providers never write the final source through +this workflow; publication remains the host's guarded atomic transaction. +The output uses the existing private outputs lifecycle, is never reusable across +worktrees, and is deleted on success, failure, cancellation, workspace/window +close and plugin revocation. A late backend may finish its already-open file +handle, but cannot resurrect a removed file. Installation resources stay read-only. + +The platform adapter denies app/plugin installation roots, validates the selected +file and canonical target, preserves metadata and performs guarded atomic publication +against the original bytes. Before commit the operation must still own its workspace, +package, settings and monotonic option revision. Conflicts retain the draft. After +save the host invalidates build/test/language import facts and re-queries the owning +provider; read-back must identify the saved source and exact published revision. +A failed read-back is not a successful effective-value publication. Native probes, +output files and mutable provider caches live only in workspace/platform state and +are not cross-worktree reusable. Installed packages remain byte-for-byte unchanged. + +Fixtures: [declaration](fixtures/build-providers/build-provider-configuration.plugin.json) and +[native facts](fixtures/build-providers/build-configuration.json). ### Optional read-only model inputs (buildModelInputs 1) @@ -1441,8 +1648,48 @@ functions, grants permissions, leases a process or resolves a package version. There is no general plugin-dependency/export API. Backend authors must handle unavailable and non-ready slots explicitly. Language integration and existing Java/Maven business migration remain separate work. Shared fixtures: -`fixtures/build-model-producer.plugin.json` and -`fixtures/build-model-consumer.plugin.json`. +`fixtures/build-systems/build-model-producer.plugin.json` and +`fixtures/build-systems/build-model-consumer.plugin.json`. + +### Inline provider test reports (buildProviders 8) + +A provider test may declare `results: { "decoder": "provider" }` with +`requires.buildProviders: 8`. Core adds `testResults: true` to the test request; +wire versions below 8 refuse it before invocation. The reply includes +`testResults: { "format": "litheTestResults", "version": 1, "tests": [...] }` +next to `exitCode` and `output`. Ordinary execute replies may include the same +report when the task also ran tests. Missing required or malformed reports fail +explicitly; there is no host report-file fallback. + +Each case has `suite`, `case`, `status` (`passed`, `failed`, `error`, `skipped`), +optional workspace-relative `project` and source `path`, one-based `line`, +`durationMillis`, `message`, and `invocations` (default 1). A report contains at +most 10,000 cases and 8 MiB UTF-8 JSON. Paths remain inside the selected build +root, durations are nonnegative safe integers, and messages are bounded. Core +validates and aggregates by project/suite/case, so identical names in distinct +projects remain distinct; it computes summary/success from the cases. Repeated +case durations are summed only if every invocation has a duration and the total +is a safe integer; otherwise the aggregate duration is omitted. + +An optional `coverage: [{ "project": "module", "suite"?: "Suite", "case"?: "method" }]` +records completed scopes. A missing case covers the entire suite; a missing suite +covers the project (and forbids case). Scopes use canonical workspace-relative +projects inside the instance root, at most 10,000 entries, and bounded nonempty +suite/case names. Core sorts and deduplicates them. Hosts may replace old outcomes +within those scopes; without coverage they replace only the returned exact cases. +Providers must not claim a complete suite when upstream filters or interruption +could have limited execution. Opaque selectors are never parsed by hosts to infer +coverage. + +The provider owns report discovery, freshness and upstream format semantics. +It must preserve reports from failing executions and reject unreadable/oversized +reports instead of returning empty success. The host passes the reply to +`build.decodeTestResults` as `request.result` with the same run/configuration +identity, checks ownership again after decode, and only presents the result. +Cancellation or configuration changes revoke late reports just like late output. +The report is operation state in a platform-provided writable directory, never +installed package state or a cross-worktree cache. Fixture: +`fixtures/build-providers/build-provider-reports.plugin.json`. ### Build providers @@ -1474,10 +1721,12 @@ with `buildSystemID`. For a provider source, `build.modelPlan` and | --- | --- | | `buildSystemID`, `root` | The instance. A model request always covers the whole instance. | | `options` | Effective option values by option ID. | -| `optionArguments`, `environment` | What the options contribute, for a provider that runs the tool. | +| `optionArguments`, `environment` | Arguments from options, and selected runtime/tool environment merged with option contributions (options win). The host does not forward its complete environment. Providers use this environment for every invocation and cache identity. | | `project` | Dependencies only: `{ name, path }` of the selected project, or `null` for the workspace. | | `refresh` | Optional; `true` when the user asked to reload (Reload, Retry, a page's `build.refresh`) or the host reloads after a `sync` file changed. The provider must then recompute instead of answering from a cache or snapshot of its own. Absent or `false` for a first load, which a provider may answer from a snapshot it still trusts. | -| `tool` | Optional; `{ executable }`, the absolute path of the build tool the host resolved from the build system's `invocation` the same way a task run does (project wrapper, environment variable, `PATH`, the user's override), so the provider need not repeat the lookup. Absent when the host could not resolve it; the provider then looks the tool up itself. It is never a reason to fail the request. | +| `tool` | Optional; `{ executable }`, the absolute path of the build tool the host resolved from the build system's `invocation` the same way a task run does (project wrapper, environment variable, `PATH`, the user's override), so the provider need not repeat the lookup. Absent when the host could not resolve it; the provider then looks the tool up itself. An explicit toolchain or path selection that cannot resolve fails before the Provider request; it cannot be dropped to trigger independent lookup. | + +The Core-only `tool` hint may include a bounded `environment` string map. Core merges it into the existing Provider `environment` parameter, with option contributions taking precedence; the wire `tool` remains `{ executable }`. This also applies to native configuration, tasks and tests. The backend process runtime and the selected build runtime are independent. The host passes `refresh` and `tool` in the `build.modelPlan` and `build.dependencyPlan` requests; Core validates `tool.executable` as an @@ -1496,12 +1745,15 @@ The manifest only says how to start the provider and which sources use it. Everything else is negotiated in the LSP `initialize` exchange, so a provider can add optional data within a negotiated protocol version: -- The host sends `capabilities.experimental.litheBuild: { "versions": [6, 5, 4, 3, 2, 1] }`, +- The host sends `capabilities.experimental.litheBuild: { "versions": [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] }`, the build-provider protocol versions it speaks. Version 1 is the two requests above with the params and result formats of this section; version 2 adds typed task execution; version 3 adds typed test execution and discovery; version 4 adds application run preparation; version 5 adds native debug preparation; - version 6 accepts the host's explicitly declared read-only model inputs. + version 6 accepts the host's explicitly declared read-only model inputs; + version 7 adds source-backed native configuration; version 8 adds inline test reports; + version 9 adds opaque semantic test selectors; version 10 adds typed run options and + service/task run preparation; version 11 adds registry connection debugging. - The provider answers `capabilities.experimental.litheBuild: { "version": 1 }` with the version it chose from that list. A provider that answers no version, or one the host did not offer, is not used for build requests; a @@ -1696,14 +1948,14 @@ takes a connection (`maxPathMappings` is 0 without mappings, 1 for `first`, and 16 otherwise) and `null` otherwise, so a host knows whether to offer the host-and-port form and the mapping list, and how many rows the list may hold. -The cases in `fixtures/debug-adapter-rejections.json` state the stable code and +The cases in `fixtures/debug-adapters/debug-adapter-rejections.json` state the stable code and field for each refusal and the layer that reports it: `schema`, `coreManifest`, `corePlan` (one start: stale registration or runtime selection, a target missing a fact a binding reads; the request defaults to `launch` and `context.request` sets `attach`), and `coreInstall` (a `parametersSchema` that is missing or links outside the package). -`fixtures/debug-adapter.plugin.json` is a valid example, and -`fixtures/debug-adapter-remote-attach.plugin.json` declares six adapters that +`fixtures/debug-adapters/debug-adapter.plugin.json` is a valid example, and +`fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json` declares six adapters that differ in how they take a connection. ## Plugin backend @@ -1724,8 +1976,8 @@ language, or workspace: launch fields, with the same templates and install review; the review lists it as `packagedBackend`, `systemCommandBackend`, or `runtimeBackend`. It needs `pluginAPIVersion` 3, `requires.backend: 1`, `requires.serverProcess: - 1`, and the `backend.launch` permission. `fixtures/backend.plugin.json` is - the example; the backend cases of `fixtures/invalid-manifests.json` are the + 1`, and the `backend.launch` permission. `fixtures/backend/backend.plugin.json` is + the example; the backend cases of `fixtures/manifest/invalid-manifests.json` are the rejections. - In `backend` 1 only the plugin's own [web pages](#web-content-ui-2) call it, so a backend without a web tool window is `plugin.manifest.invalid`. @@ -1748,7 +2000,7 @@ language, or workspace: `requires.backend: 2` keeps every rule of version 1 and changes when the process starts and what it learns about workspaces. -`fixtures/backend-workspace.plugin.json` is the example. +`fixtures/backend/backend-workspace.plugin.json` is the example. - It may start without a page. It needs at least one way to start: a web tool window (first page call, as in version 1), a `ui.commands[]` entry @@ -2189,7 +2441,7 @@ notifications. `versionConflict`, or `{ retryAfterMilliseconds }` with `rateLimited`); it reaches a version 3 backend as `error.data.details` and is dropped otherwise. Details that are not an object, or larger, are dropped too; - the failure itself still reaches the backend. `fixtures/function-protocol.json` (`hostFailures`) + the failure itself still reaches the backend. `fixtures/backend/function-protocol.json` (`hostFailures`) lists the mapping. | Code | JSON-RPC | Meaning | @@ -2260,7 +2512,7 @@ then never sees them. and serialized params are at most `limits.notificationBytes`. Core refuses any other method, a service the session is not granted, malformed params, a session that is not plugin-owned, or one that is not ready, with - `invalid_request`. `fixtures/function-protocol.json` + `invalid_request`. `fixtures/backend/function-protocol.json` (`hostNotifications`) holds the cases. ### Extending version 3 @@ -2299,7 +2551,7 @@ So that a version 3 backend works with every host of version 3: (`plugin.permission.unknown`), so a permission is never granted or ignored silently. -`fixtures/function-protocol.json` (`hostRequests`) holds the +`fixtures/backend/function-protocol.json` (`hostRequests`) holds the unsupported-option cases. ### Public document, file and window methods (protocol version 3) @@ -2389,7 +2641,7 @@ or omitted `toolWindows` array. Such a command requires `backend:2`, the existin `ui.contribute` and `backend.launch` permissions, and its `onCommand:` event. Versions 1-3 retain their window-required behavior. An old host rejects ui 4 at installation, rather than accepting a command it cannot execute. See -`fixtures/command-backend.plugin.json`. +`fixtures/backend/command-backend.plugin.json`. The command appears through the ordinary workbench command contributions and sends `commands/didRun` without opening a dummy tool window. Commands arriving @@ -2430,7 +2682,7 @@ workspace context: cannot discover a backend that has not started. Backend installation still needs restart. Standalone backends have an application-scoped enablement module, without a fake UI or language provider; safe mode/quarantine/disable still veto - their startup. Example package: `fixtures/editor-actions.plugin.json`. + their startup. Example package: `fixtures/editor/editor-actions.plugin.json`. - A selector has 1 to 32 literal names/extensions; extensions accept an optional leading dot. Matching is case-insensitive. At most 32 bindings per plugin across contexts; duplicate registration IDs in a context are refused. @@ -2490,7 +2742,7 @@ workspace context: context, and failure codes and details, all of which it never sees. - `requires.backend: 1` packages keep their meaning; a new behavior of the backend capability is `backend` 2, never a silent change of version 1. -- `fixtures/function-protocol.json` holds the golden cases, including the +- `fixtures/backend/function-protocol.json` holds the golden cases, including the host requests (`hostRequests`), call contexts (`invocationContexts`), host failure codes (`hostFailures`), and host notifications (`hostNotifications`). Rust Core tests run them, and a scripted backend covers routing, result, failure, cancellation, @@ -2633,8 +2885,8 @@ is Windows-only behavior outside v1. `pluginAPIVersion: 2` adds optional `ui` contributions and the `ui.contribute` permission. API level 1 language packages continue to work. UI-only packages may omit `languages`; every package must still declare a language provider or a -nonempty UI catalog. See `fixtures/hello-lithe.plugin.json` and the UI cases in -`fixtures/invalid-manifests.json`. +nonempty UI catalog. See `fixtures/manifest/hello-lithe.plugin.json` and the UI cases in +`fixtures/manifest/invalid-manifests.json`. - `ui.toolWindows`: 1–64 entries with stable `id`, `title`, initial `placement` (`left`, `bottom` or `right`), integer `order` (default 0), and plain `text`. @@ -2685,8 +2937,8 @@ that wants its own UI can show its own page in a tool window instead of permission, which the install review lists like every permission. `requires.ui: 2` without a web window is `plugin.manifest.invalid`, so a text-only plugin stays installable on hosts that run only `ui` 1. -- `entry` is a package-relative `.html` path. `fixtures/web-panel.plugin.json` - is the example; the web cases of `fixtures/invalid-manifests.json` are the +- `entry` is a package-relative `.html` path. `fixtures/ui/web-panel.plugin.json` + is the example; the web cases of `fixtures/manifest/invalid-manifests.json` are the rejections. The window shell stays the host's: dock, title, activity-bar entry, order, @@ -2814,9 +3066,12 @@ The design source is the [proposed public Extension Contract and Provider API](h Current runtime behavior is recorded in the [first-stage workbench UI Note](https://github.com/1lck/Lithe-IDEA/blob/preview/.agents/notes/implemented/architecture/2026-10-12-workbench-ui-contributions.md). ### Current Windows availability -Windows can validate API level 2 through Rust Core but its UI adapter has not -implemented this contract yet. Its marketplace must filter unsupported API -levels until that adapter is available. +Windows advertises `ui` 1 and 2: it renders host tool windows, commands and +package web content. It does not advertise `ui` 3 (native controls) or 4 +(backend-only commands), so Core refuses packages that require them there. +Windows installs declarative packages from disk only and has no marketplace +browser yet. Core's `plugins.marketplace.validateIndex` marks level 2 versions +without `requires` available only on `darwin-*` platforms. ### Existing community store and Lua template @@ -2834,10 +3089,11 @@ remain valid. To add a host-rendered view to that template: existing package checksum, user install review and content digest still apply; UI does not bypass them. 5. Set the new Store version entry's `pluginAPIVersion` to 2. The index schema, - release archive format and SHA-256 rules stay unchanged. Only list platforms - with an implemented API level 2 UI host (currently macOS). + release archive format and SHA-256 rules stay unchanged. Only list `darwin-*` + platforms: `plugins.marketplace.validateIndex` marks a level 2 version without + `requires` unsupported on every other platform. -`fixtures/hello-lithe.plugin.json` is a contract fixture in the reserved test +`fixtures/manifest/hello-lithe.plugin.json` is a contract fixture in the reserved test namespace, not a ready-to-publish community package. Change its ID and all references to your own namespace before using it in Lithe Store. @@ -2908,7 +3164,7 @@ macOS 动作的可见、可用、勾选和激活中状态由宿主计算,不 仍可取消显示。多个窗口同时请求同一插件模块时只构造一次;禁用或关闭会取消 并清理在途激活,旧的菜单或命令引用不能重新调用已禁用的贡献。 -#### Shared executable candidates (`executableResolution` 1) +#### Shared executable candidates (`executableResolution` 1/2) Declare executable sources in `invocation.executables`, in preference order. Array order is the recommended form; no custom path setting is required for a @@ -2989,7 +3245,26 @@ remains sufficient). Legacy manifests do not need the new capability; hosts without it reject new-style plugins before decoding unknown fields. Published 0.1.0/0.1.1 archives remain immutable; migration is a new plugin version. -See [`build-tool-path.plugin.json`](fixtures/build-tool-path.plugin.json). +See [`build-tool-path.plugin.json`](fixtures/build-systems/build-tool-path.plugin.json). + +With `executableResolution: 2`, a candidate may use +`{ "kind": "toolchain", "toolchainID": "org.example.compiler:compiler" }`. +Core accepts the same short/qualified ID grammar as tool definitions and adds the +normalized instance `root`. The macOS adapter resolves installed definitions +against fresh native evidence and directory pins. Existing host registry IDs +remain only for their unmigrated consumers. Unavailable selections are terminal, +including when a prerequisite runtime fails first; they cannot silently fall +back to Provider discovery. +Host-managed `java`, `node` or `go` dependencies may select one such candidate, +while retaining Core's version probe. Maven declares its namespace-owned tool +and the official Java definition for its Java prerequisite; no Maven-specific +JDK registry entry is added. Its backend bootstrap remains a separate consumer. +Passive import performs no version or runtime probes; a definition that requires +verification is not reported as an already verified import executable. +A toolchain that resolves to a project file remains project code, +including through a symlink, and native configuration requires explicit Read +before running it. See [`build-toolchains.plugin.json`](fixtures/build-systems/build-toolchains.plugin.json). + ### Host-rendered native content (`ui` 3, native schema 1) @@ -2999,7 +3274,7 @@ ui 3 and rejects these packages before installing them. The independently versioned [native schema](native-ui.schema.json) has no style, script, HTML or host-action fields. The host renders every control with its shared theme. -See [native-panel.plugin.json](fixtures/native-panel.plugin.json) and the runnable +See [native-panel.plugin.json](fixtures/ui/native-panel.plugin.json) and the runnable [Node SDK Sample Explorer](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/sdk/node/examples/sample-build/plugin.json). `native.kind` is `tree`, `list` or `form`; `nodes` contains literal `id`, `label` and optional `children`. IDs are stable, case-sensitive ASCII letters/digits plus @@ -3058,7 +3333,7 @@ A pending call disables all fields. Values use the existing 64 KiB per-plugin, per-window, per-workspace state store; removed fields, invalid types and removed options are discarded when restoring. Provider replies never overwrite local fields. Form declarations and choices are static in this version. -See [native-form.plugin.json](fixtures/native-form.plugin.json) and +See [native-form.plugin.json](fixtures/ui/native-form.plugin.json) and [host UI acceptance fixture](https://github.com/1lck/Lithe-IDEA/blob/preview/macos/Tests/Fixtures/native-ui-lab/README.md) for acceptance. An optional `source` names this plugin's `buildSystemID` and a function returning @@ -3100,9 +3375,55 @@ multi-instance selectors, global function commands, native renderer factories and Windows rendering remain follow-up work under #1209; this subset does not claim completion of that whole issue. +### Opaque semantic test selectors (buildProviders 9) + +Provider test declarations may advertise `selectorKinds`, up to 16 distinct public +namespace identifiers (120 bytes each). These require `buildProviders: 9`. A test +request may then carry `selectorKind` and `selectors` instead of `suite`/`case`. +The namespace must be declared; at most 64 nonempty, control-free UTF-8 values of +4096 bytes each are allowed. An empty list explicitly selects all tests within the +request's normal project/workspace scope. Values remain opaque to the host and +Core: only the owning language engine supplies semantic identities and the build +backend translates them into its upstream runner's selection syntax. Providers +must reject identities they cannot represent exactly, rather than widen execution. + +The wire session must explicitly negotiate version 9. Core and both SDKs reject +these fields on older versions before dispatch; ignoring a filter is unsafe even +when version 8 inline reports are supported. The operation identity, source/model +and configuration validity, cancellation, progress, and inline report rules still +apply. The shared example is `fixtures/build-providers/build-provider-selectors.plugin.json`. + +### Declarative project preparation (buildSystems 4) + +A project task may declare `preparation: { "purpose": "run", "sourceRoles": +["source", "generatedSource"] }` with `requires.buildSystems: 4`. Purpose is +an opaque workflow identifier; `run` identifies preparation before resolving a +language launch. Roles are the canonical model content-root kinds. A provider +can declare a different task for `testSource`/`generatedTestSource`, with any +private task IDs. Hosts never prescribe task names or infer a role from a file +extension, build descriptor, class name or missing model entry. + +Bindings are unique by purpose/role within each build system. Preparation +requires a project-scoped task, task execution, no argument prompt, and no +`fromModel`. Empty, overlapping, duplicate or unknown roles are rejected. +Hosts select the deepest owning project and its deepest content root from the +current model, then require exactly one declared matching task. Missing or +ambiguous declarations fail; they do not fall back to a conventional task ID. +The selected ID travels through the existing execute plan and backend method. +An older host rejects the capability before invocation. No new RPC version is +needed, so buildProviders 8–10 are unchanged. Shared fixture: +`fixtures/build-providers/build-provider-preparation.plugin.json`. + +The build backend owns filtering, custom resource directories, profiles and +reactor semantics. Hosts await successful completion, retain one operation +identity through cancellation/configuration changes, and only then resolve and +launch Java. Per-run option/tool overrides are ephemeral host inputs to the +existing validated plan; they never mutate saved build defaults or claim that +the persistent model reflects a different configuration. + ### Plugin-owned native terminals (function protocol 3) -[`terminal-backend.plugin.json`](fixtures/terminal-backend.plugin.json) is the +[`terminal-backend.plugin.json`](fixtures/backend/terminal-backend.plugin.json) is the minimal manifest. `terminal.create` grants `lithe/host/terminal/create`, `show`, `close`, `reopen`, `dispose`, and `onExit`. It requires `backend:2`; discover the individual methods through `getCapabilities` before using them. macOS implements this diff --git a/contract/v1/fixtures/backend-workspace.plugin.json b/contract/v1/fixtures/backend/backend-workspace.plugin.json similarity index 100% rename from contract/v1/fixtures/backend-workspace.plugin.json rename to contract/v1/fixtures/backend/backend-workspace.plugin.json diff --git a/contract/v1/fixtures/backend.plugin.json b/contract/v1/fixtures/backend/backend.plugin.json similarity index 100% rename from contract/v1/fixtures/backend.plugin.json rename to contract/v1/fixtures/backend/backend.plugin.json diff --git a/contract/v1/fixtures/command-backend.plugin.json b/contract/v1/fixtures/backend/command-backend.plugin.json similarity index 100% rename from contract/v1/fixtures/command-backend.plugin.json rename to contract/v1/fixtures/backend/command-backend.plugin.json diff --git a/contract/v1/fixtures/function-protocol.json b/contract/v1/fixtures/backend/function-protocol.json similarity index 100% rename from contract/v1/fixtures/function-protocol.json rename to contract/v1/fixtures/backend/function-protocol.json diff --git a/contract/v1/fixtures/plugin-logging.json b/contract/v1/fixtures/backend/plugin-logging.json similarity index 100% rename from contract/v1/fixtures/plugin-logging.json rename to contract/v1/fixtures/backend/plugin-logging.json diff --git a/contract/v1/fixtures/terminal-backend.plugin.json b/contract/v1/fixtures/backend/terminal-backend.plugin.json similarity index 100% rename from contract/v1/fixtures/terminal-backend.plugin.json rename to contract/v1/fixtures/backend/terminal-backend.plugin.json diff --git a/contract/v1/fixtures/build-providers/build-configuration.json b/contract/v1/fixtures/build-providers/build-configuration.json new file mode 100644 index 0000000..c1944cd --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-configuration.json @@ -0,0 +1,34 @@ +{ + "format": "litheBuildConfiguration", + "version": 1, + "sources": [ + { + "id": "user", + "scope": "user", + "uri": "file:///fixture/user/settings.xml", + "revision": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "writable": true, + "fields": [ + { + "id": "localRepository", + "kind": "directory", + "value": null, + "effectiveValue": "/fixture/repository", + "effectiveSource": "installation" + }, + { + "id": "activeProfiles", + "kind": "stringList", + "value": [ + "ci" + ], + "effectiveValue": [ + "ci", + "offline" + ], + "effectiveSource": "commandLine" + } + ] + } + ] +} diff --git a/contract/v1/fixtures/build-providers/build-connection-debug-options.json b/contract/v1/fixtures/build-providers/build-connection-debug-options.json new file mode 100644 index 0000000..e3beac1 --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-connection-debug-options.json @@ -0,0 +1,46 @@ +{ + "format": "litheBuildRunOptions", + "version": 1, + "launchOptions": { + "execution": "service", + "runtimeArguments": [ + "-Dmessage=hello world", + "-Dquote=\"value\"", + "-Dpath=C:\\Users\\example" + ], + "arguments": [ + "--message=hello world", + "雪", + "" + ], + "environment": { + "EXAMPLE": "hello world" + }, + "workingDirectory": "tools", + "entrypoint": "example.Main", + "debug": { + "adapterID": "dev.lithe.plugin.java-support/java", + "configurationType": "java", + "target": { + "kind": "connection", + "host": "127.0.0.1", + "port": 51235 + } + } + }, + "result": { + "executable": { + "kind": "command", + "value": "engine" + }, + "arguments": [ + "serve", + "--root", + "app" + ], + "workingDirectory": "tools", + "environment": { + "EXAMPLE": "hello world" + } + } +} diff --git a/contract/v1/fixtures/build-debug-preparation.json b/contract/v1/fixtures/build-providers/build-debug-preparation.json similarity index 100% rename from contract/v1/fixtures/build-debug-preparation.json rename to contract/v1/fixtures/build-providers/build-debug-preparation.json diff --git a/contract/v1/fixtures/build-execution-protocol.json b/contract/v1/fixtures/build-providers/build-execution-protocol.json similarity index 100% rename from contract/v1/fixtures/build-execution-protocol.json rename to contract/v1/fixtures/build-providers/build-execution-protocol.json diff --git a/contract/v1/fixtures/build-providers/build-provider-configuration.plugin.json b/contract/v1/fixtures/build-providers/build-provider-configuration.plugin.json new file mode 100644 index 0000000..ea4d15d --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-provider-configuration.plugin.json @@ -0,0 +1,178 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-provider-configuration", + "displayName": "Native Configuration Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildProviders": 7, + "buildSystems": 1, + "serverProcess": 1, + "settings": 1 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "languageServer.launch", + "process.build" + ], + "activationEvents": [ + "onBuildSystem:gradle" + ], + "languages": [], + "settings": [ + { + "id": "providerHeapMB", + "type": "integer", + "title": "Provider memory", + "description": "Upper bound of the Gradle provider's heap.", + "default": 1024, + "minimum": 256, + "maximum": 8192, + "unit": "MB" + } + ], + "buildSystems": [ + { + "id": "gradle", + "displayName": "Gradle", + "detection": { + "markers": [ + "build.gradle", + "build.gradle.kts", + "settings.gradle", + "settings.gradle.kts" + ], + "nesting": "outermost", + "priority": 60 + }, + "invocation": { + "executables": [ + { + "kind": "projectFile", + "path": "gradlew", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "macos", + "linux" + ] + }, + { + "kind": "projectFile", + "path": "gradlew.bat", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "windows" + ] + }, + { + "kind": "command", + "command": "gradle" + } + ], + "runtimeDependencies": [ + { + "id": "java", + "minimumVersion": "17" + } + ], + "globalArguments": [ + "--console=plain", + "${optionArguments}" + ] + }, + "provider": { + "launch": { + "kind": "runtime", + "tool": "java", + "minimumVersion": "17", + "runtimeArguments": [ + "-Xmx${setting:providerHeapMB}m" + ], + "entry": "provider/gradle-tooling-provider.jar" + }, + "arguments": [ + "--stdio", + "--cache", + "${workspaceStateDir}/tooling" + ], + "initializationOptions": { + "initScript": "${pluginDir}/provider/lithe-model.init.gradle" + }, + "executesProjectCode": true, + "mayUseNetwork": true + }, + "options": [ + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + } + ], + "model": { + "source": "provider" + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "workspace", + "project", + "target" + ], + "source": "provider", + "arguments": [] + }, + { + "id": "custom", + "displayName": "Custom", + "group": "other", + "scopes": [ + "workspace" + ], + "source": "provider", + "requiresUserArguments": true, + "arguments": [] + } + ], + "dependencies": { + "source": "provider", + "scopes": [ + "workspace", + "project" + ] + }, + "sync": { + "watch": [ + "build.gradle", + "build.gradle.kts", + "gradle.properties", + "gradle/libs.versions.toml", + "settings.gradle", + "settings.gradle.kts" + ] + }, + "layout": { + "excluded": [ + ".gradle", + "build" + ] + }, + "configuration": { + "source": "provider" + } + } + ] +} diff --git a/contract/v1/fixtures/build-provider-debug-preparation.plugin.json b/contract/v1/fixtures/build-providers/build-provider-debug-preparation.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider-debug-preparation.plugin.json rename to contract/v1/fixtures/build-providers/build-provider-debug-preparation.plugin.json diff --git a/contract/v1/fixtures/build-provider-execution.plugin.json b/contract/v1/fixtures/build-providers/build-provider-execution.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider-execution.plugin.json rename to contract/v1/fixtures/build-providers/build-provider-execution.plugin.json diff --git a/contract/v1/fixtures/build-provider-language-server.plugin.json b/contract/v1/fixtures/build-providers/build-provider-language-server.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider-language-server.plugin.json rename to contract/v1/fixtures/build-providers/build-provider-language-server.plugin.json diff --git a/contract/v1/fixtures/build-providers/build-provider-preparation.plugin.json b/contract/v1/fixtures/build-providers/build-provider-preparation.plugin.json new file mode 100644 index 0000000..7da03c3 --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-provider-preparation.plugin.json @@ -0,0 +1,186 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-provider-preparation", + "displayName": "Declarative Preparation Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildProviders": 2, + "buildSystems": 4, + "serverProcess": 1, + "settings": 1 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "languageServer.launch", + "process.build" + ], + "activationEvents": [ + "onBuildSystem:gradle" + ], + "languages": [], + "settings": [ + { + "id": "providerHeapMB", + "type": "integer", + "title": "Provider memory", + "description": "Upper bound of the Gradle provider's heap.", + "default": 1024, + "minimum": 256, + "maximum": 8192, + "unit": "MB" + } + ], + "buildSystems": [ + { + "id": "gradle", + "displayName": "Gradle", + "detection": { + "markers": [ + "build.gradle", + "build.gradle.kts", + "settings.gradle", + "settings.gradle.kts" + ], + "nesting": "outermost", + "priority": 60 + }, + "invocation": { + "executables": [ + { + "kind": "projectFile", + "path": "gradlew", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "macos", + "linux" + ] + }, + { + "kind": "projectFile", + "path": "gradlew.bat", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "windows" + ] + }, + { + "kind": "command", + "command": "gradle" + } + ], + "runtimeDependencies": [ + { + "id": "java", + "minimumVersion": "17" + } + ], + "globalArguments": [ + "--console=plain", + "${optionArguments}" + ] + }, + "provider": { + "launch": { + "kind": "runtime", + "tool": "java", + "minimumVersion": "17", + "runtimeArguments": [ + "-Xmx${setting:providerHeapMB}m" + ], + "entry": "provider/gradle-tooling-provider.jar" + }, + "arguments": [ + "--stdio", + "--cache", + "${workspaceStateDir}/tooling" + ], + "initializationOptions": { + "initScript": "${pluginDir}/provider/lithe-model.init.gradle" + }, + "executesProjectCode": true, + "mayUseNetwork": true + }, + "options": [ + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + } + ], + "model": { + "source": "provider" + }, + "tasks": [ + { + "id": "prepare-assets", + "displayName": "Prepare assets", + "group": "build", + "scopes": [ + "project" + ], + "source": "provider", + "preparation": { + "purpose": "run", + "sourceRoles": [ + "source", + "generatedSource" + ] + }, + "arguments": [] + }, + { + "id": "prepare-specs", + "displayName": "Prepare test assets", + "group": "build", + "scopes": [ + "project" + ], + "source": "provider", + "preparation": { + "purpose": "run", + "sourceRoles": [ + "testSource", + "generatedTestSource" + ] + }, + "arguments": [] + } + ], + "dependencies": { + "source": "provider", + "scopes": [ + "workspace", + "project" + ] + }, + "sync": { + "watch": [ + "build.gradle", + "build.gradle.kts", + "gradle.properties", + "gradle/libs.versions.toml", + "settings.gradle", + "settings.gradle.kts" + ] + }, + "layout": { + "excluded": [ + ".gradle", + "build" + ] + } + } + ] +} diff --git a/contract/v1/fixtures/build-providers/build-provider-reports.plugin.json b/contract/v1/fixtures/build-providers/build-provider-reports.plugin.json new file mode 100644 index 0000000..4b7fc7d --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-provider-reports.plugin.json @@ -0,0 +1,192 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-provider-reports", + "displayName": "Build Provider Tests Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildProviders": 8, + "buildSystems": 1, + "serverProcess": 1, + "settings": 1 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "languageServer.launch", + "process.build" + ], + "activationEvents": [ + "onBuildSystem:gradle" + ], + "languages": [], + "settings": [ + { + "id": "providerHeapMB", + "type": "integer", + "title": "Provider memory", + "description": "Upper bound of the Gradle provider's heap.", + "default": 1024, + "minimum": 256, + "maximum": 8192, + "unit": "MB" + } + ], + "buildSystems": [ + { + "id": "gradle", + "displayName": "Gradle", + "detection": { + "markers": [ + "build.gradle", + "build.gradle.kts", + "settings.gradle", + "settings.gradle.kts" + ], + "nesting": "outermost", + "priority": 60 + }, + "invocation": { + "executables": [ + { + "kind": "projectFile", + "path": "gradlew", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "macos", + "linux" + ] + }, + { + "kind": "projectFile", + "path": "gradlew.bat", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "windows" + ] + }, + { + "kind": "command", + "command": "gradle" + } + ], + "runtimeDependencies": [ + { + "id": "java", + "minimumVersion": "17" + } + ], + "globalArguments": [ + "--console=plain", + "${optionArguments}" + ] + }, + "provider": { + "launch": { + "kind": "runtime", + "tool": "java", + "minimumVersion": "17", + "runtimeArguments": [ + "-Xmx${setting:providerHeapMB}m" + ], + "entry": "provider/gradle-tooling-provider.jar" + }, + "arguments": [ + "--stdio", + "--cache", + "${workspaceStateDir}/tooling" + ], + "initializationOptions": { + "initScript": "${pluginDir}/provider/lithe-model.init.gradle" + }, + "executesProjectCode": true, + "mayUseNetwork": true + }, + "options": [ + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + } + ], + "model": { + "source": "provider" + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "workspace", + "project", + "target" + ], + "source": "provider", + "arguments": [] + }, + { + "id": "custom", + "displayName": "Custom", + "group": "other", + "scopes": [ + "workspace" + ], + "source": "provider", + "requiresUserArguments": true, + "arguments": [] + } + ], + "dependencies": { + "source": "provider", + "scopes": [ + "workspace", + "project" + ] + }, + "sync": { + "watch": [ + "build.gradle", + "build.gradle.kts", + "gradle.properties", + "gradle/libs.versions.toml", + "settings.gradle", + "settings.gradle.kts" + ] + }, + "layout": { + "excluded": [ + ".gradle", + "build" + ] + }, + "test": { + "source": "provider", + "scopes": [ + "workspace", + "project", + "target" + ], + "filters": [ + "suite", + "case" + ], + "arguments": [], + "results": { + "decoder": "provider" + }, + "discovery": true + } + } + ] +} diff --git a/contract/v1/fixtures/build-providers/build-provider-run-options.plugin.json b/contract/v1/fixtures/build-providers/build-provider-run-options.plugin.json new file mode 100644 index 0000000..12b980f --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-provider-run-options.plugin.json @@ -0,0 +1,209 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-provider-run-options", + "displayName": "Provider Run Options Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildProviders": 10, + "buildSystems": 1, + "serverProcess": 1, + "settings": 1 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "languageServer.launch", + "process.build" + ], + "activationEvents": [ + "onBuildSystem:gradle" + ], + "languages": [], + "settings": [ + { + "id": "providerHeapMB", + "type": "integer", + "title": "Provider memory", + "description": "Upper bound of the Gradle provider's heap.", + "default": 1024, + "minimum": 256, + "maximum": 8192, + "unit": "MB" + } + ], + "buildSystems": [ + { + "id": "gradle", + "displayName": "Gradle", + "detection": { + "markers": [ + "build.gradle", + "build.gradle.kts", + "settings.gradle", + "settings.gradle.kts" + ], + "nesting": "outermost", + "priority": 60 + }, + "invocation": { + "executables": [ + { + "kind": "projectFile", + "path": "gradlew", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "macos", + "linux" + ] + }, + { + "kind": "projectFile", + "path": "gradlew.bat", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "windows" + ] + }, + { + "kind": "command", + "command": "gradle" + } + ], + "runtimeDependencies": [ + { + "id": "java", + "minimumVersion": "17" + } + ], + "globalArguments": [ + "--console=plain", + "${optionArguments}" + ] + }, + "provider": { + "launch": { + "kind": "runtime", + "tool": "java", + "minimumVersion": "17", + "runtimeArguments": [ + "-Xmx${setting:providerHeapMB}m" + ], + "entry": "provider/gradle-tooling-provider.jar" + }, + "arguments": [ + "--stdio", + "--cache", + "${workspaceStateDir}/tooling" + ], + "initializationOptions": { + "initScript": "${pluginDir}/provider/lithe-model.init.gradle" + }, + "executesProjectCode": true, + "mayUseNetwork": true + }, + "options": [ + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + } + ], + "model": { + "source": "provider" + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "workspace", + "project", + "target" + ], + "source": "provider", + "arguments": [] + }, + { + "id": "custom", + "displayName": "Custom", + "group": "other", + "scopes": [ + "workspace" + ], + "source": "provider", + "requiresUserArguments": true, + "arguments": [] + } + ], + "dependencies": { + "source": "provider", + "scopes": [ + "workspace", + "project" + ] + }, + "sync": { + "watch": [ + "build.gradle", + "build.gradle.kts", + "gradle.properties", + "gradle/libs.versions.toml", + "settings.gradle", + "settings.gradle.kts" + ] + }, + "layout": { + "excluded": [ + ".gradle", + "build" + ] + }, + "test": { + "source": "provider", + "scopes": [ + "workspace", + "project", + "target" + ], + "filters": [ + "suite", + "case" + ], + "arguments": [], + "results": { + "decoder": "junitXml", + "reports": [ + "build/test-results/**/*.xml" + ] + }, + "discovery": true + }, + "run": [ + { + "id": "service", + "source": "provider", + "name": "Run Service", + "fromTargets": { + "tags": [ + "application" + ] + }, + "arguments": [], + "execution": "service" + } + ] + } + ] +} diff --git a/contract/v1/fixtures/build-provider-run-preparation.plugin.json b/contract/v1/fixtures/build-providers/build-provider-run-preparation.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider-run-preparation.plugin.json rename to contract/v1/fixtures/build-providers/build-provider-run-preparation.plugin.json diff --git a/contract/v1/fixtures/build-providers/build-provider-selectors.plugin.json b/contract/v1/fixtures/build-providers/build-provider-selectors.plugin.json new file mode 100644 index 0000000..e36cd3b --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-provider-selectors.plugin.json @@ -0,0 +1,195 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-provider-selectors", + "displayName": "Build Provider Tests Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildProviders": 9, + "buildSystems": 1, + "serverProcess": 1, + "settings": 1 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "languageServer.launch", + "process.build" + ], + "activationEvents": [ + "onBuildSystem:gradle" + ], + "languages": [], + "settings": [ + { + "id": "providerHeapMB", + "type": "integer", + "title": "Provider memory", + "description": "Upper bound of the Gradle provider's heap.", + "default": 1024, + "minimum": 256, + "maximum": 8192, + "unit": "MB" + } + ], + "buildSystems": [ + { + "id": "gradle", + "displayName": "Gradle", + "detection": { + "markers": [ + "build.gradle", + "build.gradle.kts", + "settings.gradle", + "settings.gradle.kts" + ], + "nesting": "outermost", + "priority": 60 + }, + "invocation": { + "executables": [ + { + "kind": "projectFile", + "path": "gradlew", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "macos", + "linux" + ] + }, + { + "kind": "projectFile", + "path": "gradlew.bat", + "requires": [ + "gradle/wrapper/gradle-wrapper.properties" + ], + "platforms": [ + "windows" + ] + }, + { + "kind": "command", + "command": "gradle" + } + ], + "runtimeDependencies": [ + { + "id": "java", + "minimumVersion": "17" + } + ], + "globalArguments": [ + "--console=plain", + "${optionArguments}" + ] + }, + "provider": { + "launch": { + "kind": "runtime", + "tool": "java", + "minimumVersion": "17", + "runtimeArguments": [ + "-Xmx${setting:providerHeapMB}m" + ], + "entry": "provider/gradle-tooling-provider.jar" + }, + "arguments": [ + "--stdio", + "--cache", + "${workspaceStateDir}/tooling" + ], + "initializationOptions": { + "initScript": "${pluginDir}/provider/lithe-model.init.gradle" + }, + "executesProjectCode": true, + "mayUseNetwork": true + }, + "options": [ + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + } + ], + "model": { + "source": "provider" + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "workspace", + "project", + "target" + ], + "source": "provider", + "arguments": [] + }, + { + "id": "custom", + "displayName": "Custom", + "group": "other", + "scopes": [ + "workspace" + ], + "source": "provider", + "requiresUserArguments": true, + "arguments": [] + } + ], + "dependencies": { + "source": "provider", + "scopes": [ + "workspace", + "project" + ] + }, + "sync": { + "watch": [ + "build.gradle", + "build.gradle.kts", + "gradle.properties", + "gradle/libs.versions.toml", + "settings.gradle", + "settings.gradle.kts" + ] + }, + "layout": { + "excluded": [ + ".gradle", + "build" + ] + }, + "test": { + "source": "provider", + "scopes": [ + "workspace", + "project", + "target" + ], + "filters": [ + "suite", + "case" + ], + "arguments": [], + "results": { + "decoder": "provider" + }, + "discovery": true, + "selectorKinds": [ + "org.example.semantic" + ] + } + } + ] +} diff --git a/contract/v1/fixtures/build-provider-tests.plugin.json b/contract/v1/fixtures/build-providers/build-provider-tests.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider-tests.plugin.json rename to contract/v1/fixtures/build-providers/build-provider-tests.plugin.json diff --git a/contract/v1/fixtures/build-provider.plugin.json b/contract/v1/fixtures/build-providers/build-provider.plugin.json similarity index 100% rename from contract/v1/fixtures/build-provider.plugin.json rename to contract/v1/fixtures/build-providers/build-provider.plugin.json diff --git a/contract/v1/fixtures/build-providers/build-run-options.json b/contract/v1/fixtures/build-providers/build-run-options.json new file mode 100644 index 0000000..b8644d8 --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-run-options.json @@ -0,0 +1,37 @@ +{ + "format": "litheBuildRunOptions", + "version": 1, + "launchOptions": { + "execution": "service", + "runtimeArguments": [ + "-Dmessage=hello world", + "-Dquote=\"value\"", + "-Dpath=C:\\Users\\example" + ], + "arguments": [ + "--message=hello world", + "雪", + "" + ], + "environment": { + "EXAMPLE": "hello world" + }, + "workingDirectory": "tools", + "entrypoint": "example.Main" + }, + "result": { + "executable": { + "kind": "toolchain", + "value": "project-maven" + }, + "arguments": [ + "--file", + "app/pom.xml", + "framework:run" + ], + "workingDirectory": "tools", + "environment": { + "EXAMPLE": "hello world" + } + } +} diff --git a/contract/v1/fixtures/build-run-preparation.json b/contract/v1/fixtures/build-providers/build-run-preparation.json similarity index 100% rename from contract/v1/fixtures/build-run-preparation.json rename to contract/v1/fixtures/build-providers/build-run-preparation.json diff --git a/contract/v1/fixtures/build-test-discovery.json b/contract/v1/fixtures/build-providers/build-test-discovery.json similarity index 100% rename from contract/v1/fixtures/build-test-discovery.json rename to contract/v1/fixtures/build-providers/build-test-discovery.json diff --git a/contract/v1/fixtures/build-providers/build-test-durations.json b/contract/v1/fixtures/build-providers/build-test-durations.json new file mode 100644 index 0000000..5e156fe --- /dev/null +++ b/contract/v1/fixtures/build-providers/build-test-durations.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "cases": [ + { + "name": "sum", + "values": [ + 10, + 20 + ], + "durationMillis": 30 + }, + { + "name": "zero", + "values": [ + 0, + 0 + ], + "durationMillis": 0 + }, + { + "name": "missing-first", + "values": [ + null, + 20 + ], + "durationMillis": null + }, + { + "name": "missing-second", + "values": [ + 10, + null + ], + "durationMillis": null + }, + { + "name": "unsafe-total", + "values": [ + 9007199254740990, + 2 + ], + "durationMillis": null + } + ] +} diff --git a/contract/v1/fixtures/build-test-protocol.json b/contract/v1/fixtures/build-providers/build-test-protocol.json similarity index 100% rename from contract/v1/fixtures/build-test-protocol.json rename to contract/v1/fixtures/build-providers/build-test-protocol.json diff --git a/contract/v1/fixtures/build-command-output-file.plugin.json b/contract/v1/fixtures/build-systems/build-command-output-file.plugin.json similarity index 100% rename from contract/v1/fixtures/build-command-output-file.plugin.json rename to contract/v1/fixtures/build-systems/build-command-output-file.plugin.json diff --git a/contract/v1/fixtures/build-command-stdout.plugin.json b/contract/v1/fixtures/build-systems/build-command-stdout.plugin.json similarity index 100% rename from contract/v1/fixtures/build-command-stdout.plugin.json rename to contract/v1/fixtures/build-systems/build-command-stdout.plugin.json diff --git a/contract/v1/fixtures/build-host-integration.plugin.json b/contract/v1/fixtures/build-systems/build-host-integration.plugin.json similarity index 100% rename from contract/v1/fixtures/build-host-integration.plugin.json rename to contract/v1/fixtures/build-systems/build-host-integration.plugin.json diff --git a/contract/v1/fixtures/build-systems/build-instance-options.plugin.json b/contract/v1/fixtures/build-systems/build-instance-options.plugin.json new file mode 100644 index 0000000..9aa8cf6 --- /dev/null +++ b/contract/v1/fixtures/build-systems/build-instance-options.plugin.json @@ -0,0 +1,312 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-instance-options", + "displayName": "Instance Options Build Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildSystems": 3 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "process.build" + ], + "activationEvents": [ + "onBuildSystem:cargo" + ], + "languages": [], + "buildSystems": [ + { + "id": "cargo", + "displayName": "Cargo", + "detection": { + "markers": [ + "Cargo.toml" + ] + }, + "invocation": { + "executables": [ + { + "kind": "command", + "command": "cargo" + } + ], + "version": { + "arguments": [ + "--version" + ], + "pattern": "cargo (\\d+\\.\\d+\\.\\d+)" + }, + "minimumVersion": "1.70" + }, + "options": [ + { + "id": "release", + "displayName": "Release Profile", + "kind": "boolean", + "arguments": [ + "--release" + ], + "scope": "instance" + }, + { + "id": "features", + "displayName": "Features", + "kind": "multiSelect", + "choicesFromModel": true, + "arguments": [ + "--features", + "${value}" + ], + "scope": "instance" + }, + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + }, + { + "id": "settingsFile", + "displayName": "Settings File", + "kind": "file", + "scope": "instance", + "arguments": [ + "--config", + "${value}" + ] + }, + { + "id": "cacheDirectory", + "displayName": "Cache Directory", + "kind": "directory", + "scope": "instance", + "arguments": [ + "--cache", + "${value}" + ] + }, + { + "id": "label", + "displayName": "Label", + "kind": "text", + "scope": "instance", + "arguments": [ + "--label", + "${value}" + ] + } + ], + "model": { + "source": "static", + "file": "Cargo.toml", + "decoder": "toml", + "map": { + "projects": [ + { + "each": "/package", + "name": "/name", + "properties": { + "version": "/version", + "edition": "/edition" + }, + "targets": [ + { + "each": "$/bin/*", + "name": "/name", + "tags": [ + "application" + ] + }, + { + "fallback": true, + "whenFile": "src/main.rs", + "name": "/name", + "tags": [ + "application" + ] + }, + { + "each": "$/test/*", + "name": "/name", + "tags": [ + "test" + ] + }, + { + "each": "$/example/*", + "name": "/name", + "tags": [ + "example" + ] + } + ], + "contentRoots": [ + { + "path": { + "value": "src" + }, + "kind": "source" + }, + { + "path": { + "value": "tests" + }, + "kind": "testSource" + } + ] + } + ], + "optionValues": { + "features": { + "each": "/features/*", + "value": "@key" + } + } + } + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "project" + ], + "arguments": [ + "build", + "--message-format=short", + "${optionArguments}" + ] + }, + { + "id": "check", + "displayName": "Check", + "group": "verification", + "scopes": [ + "project" + ], + "arguments": [ + "check", + "--message-format=short", + "${optionArguments}" + ] + }, + { + "id": "clean", + "displayName": "Clean", + "group": "build", + "scopes": [ + "project" + ], + "arguments": [ + "clean" + ] + }, + { + "id": "example", + "displayName": "Run Example", + "group": "run", + "execution": "application", + "scopes": [ + "target" + ], + "targetTags": [ + "example" + ], + "arguments": [ + "run", + "--example", + "${target.name}", + "${optionArguments}" + ] + } + ], + "run": [ + { + "id": "binary", + "fromTargets": { + "tags": [ + "application" + ] + }, + "arguments": [ + "run", + "--bin", + "${target.name}", + "${optionArguments}" + ], + "extension": { + "bin": "${target.name}" + } + } + ], + "test": { + "scopes": [ + "project", + "target" + ], + "targetTags": [ + "test" + ], + "filters": [ + "case" + ], + "arguments": [ + "test", + "${optionArguments}", + { + "value": "--test", + "when": "targetSelected" + }, + { + "value": "${target.name}", + "when": "targetSelected" + }, + { + "value": "--", + "when": "caseSelected" + }, + { + "value": "${test.case}", + "when": "caseSelected" + }, + { + "value": "--exact", + "when": "caseSelected" + } + ] + }, + "diagnostics": { + "decoder": "lineMatcher", + "patterns": [ + { + "regex": "^([^\\s:][^:]*\\.rs):(\\d+):(\\d+): (error|warning)(?:\\[\\w+\\])?: (.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + }, + "sync": { + "watch": [ + "Cargo.toml", + "Cargo.lock" + ] + }, + "layout": { + "excluded": [ + "target" + ] + } + } + ] +} diff --git a/contract/v1/fixtures/build-model-consumer.plugin.json b/contract/v1/fixtures/build-systems/build-model-consumer.plugin.json similarity index 100% rename from contract/v1/fixtures/build-model-consumer.plugin.json rename to contract/v1/fixtures/build-systems/build-model-consumer.plugin.json diff --git a/contract/v1/fixtures/build-model-producer.plugin.json b/contract/v1/fixtures/build-systems/build-model-producer.plugin.json similarity index 100% rename from contract/v1/fixtures/build-model-producer.plugin.json rename to contract/v1/fixtures/build-systems/build-model-producer.plugin.json diff --git a/contract/v1/fixtures/build-plans.json b/contract/v1/fixtures/build-systems/build-plans.json similarity index 100% rename from contract/v1/fixtures/build-plans.json rename to contract/v1/fixtures/build-systems/build-plans.json diff --git a/contract/v1/fixtures/build-static-file.plugin.json b/contract/v1/fixtures/build-systems/build-static-file.plugin.json similarity index 100% rename from contract/v1/fixtures/build-static-file.plugin.json rename to contract/v1/fixtures/build-systems/build-static-file.plugin.json diff --git a/contract/v1/fixtures/build-tool-path.plugin.json b/contract/v1/fixtures/build-systems/build-tool-path.plugin.json similarity index 100% rename from contract/v1/fixtures/build-tool-path.plugin.json rename to contract/v1/fixtures/build-systems/build-tool-path.plugin.json diff --git a/contract/v1/fixtures/build-systems/build-toolchains.plugin.json b/contract/v1/fixtures/build-systems/build-toolchains.plugin.json new file mode 100644 index 0000000..386c84a --- /dev/null +++ b/contract/v1/fixtures/build-systems/build-toolchains.plugin.json @@ -0,0 +1,329 @@ +{ + "schemaVersion": 2, + "id": "dev.lithe.fixture.build-toolchains", + "displayName": "Canonical Toolchain Build Fixture", + "version": "0.1.0", + "pluginAPIVersion": 3, + "requires": { + "buildSystems": 3, + "executableResolution": 2 + }, + "vendor": { + "id": "org.example", + "displayName": "Example", + "signatureRequirement": "publisherPackage" + }, + "permissions": [ + "process.build" + ], + "activationEvents": [ + "onBuildSystem:cargo" + ], + "languages": [], + "buildSystems": [ + { + "id": "cargo", + "displayName": "Cargo", + "detection": { + "markers": [ + "Cargo.toml" + ] + }, + "invocation": { + "executables": [ + { + "kind": "toolchain", + "toolchainID": "dev.lithe.fixture.build-toolchains:compiler" + } + ], + "runtimeDependencies": [ + { + "id": "vm", + "minimumVersion": "8", + "version": { + "arguments": ["--version"], + "pattern": "VM (\\d+(?:\\.\\d+)*)" + }, + "executables": [ + { + "kind": "toolchain", + "toolchainID": "dev.lithe.fixture.build-toolchains:vm" + } + ] + } + ], + "version": { + "arguments": [ + "--version" + ], + "pattern": "Fixture (\\d+\\.\\d+)" + }, + "minimumVersion": "1.0" + }, + "options": [ + { + "id": "release", + "displayName": "Release Profile", + "kind": "boolean", + "arguments": [ + "--release" + ], + "scope": "instance" + }, + { + "id": "features", + "displayName": "Features", + "kind": "multiSelect", + "choicesFromModel": true, + "arguments": [ + "--features", + "${value}" + ], + "scope": "instance" + }, + { + "id": "offline", + "displayName": "Offline", + "kind": "boolean", + "arguments": [ + "--offline" + ] + }, + { + "id": "settingsFile", + "displayName": "Settings File", + "kind": "file", + "scope": "instance", + "arguments": [ + "--config", + "${value}" + ] + }, + { + "id": "cacheDirectory", + "displayName": "Cache Directory", + "kind": "directory", + "scope": "instance", + "arguments": [ + "--cache", + "${value}" + ] + }, + { + "id": "label", + "displayName": "Label", + "kind": "text", + "scope": "instance", + "arguments": [ + "--label", + "${value}" + ] + } + ], + "model": { + "source": "static", + "file": "Cargo.toml", + "decoder": "toml", + "map": { + "projects": [ + { + "each": "/package", + "name": "/name", + "properties": { + "version": "/version", + "edition": "/edition" + }, + "targets": [ + { + "each": "$/bin/*", + "name": "/name", + "tags": [ + "application" + ] + }, + { + "fallback": true, + "whenFile": "src/main.rs", + "name": "/name", + "tags": [ + "application" + ] + }, + { + "each": "$/test/*", + "name": "/name", + "tags": [ + "test" + ] + }, + { + "each": "$/example/*", + "name": "/name", + "tags": [ + "example" + ] + } + ], + "contentRoots": [ + { + "path": { + "value": "src" + }, + "kind": "source" + }, + { + "path": { + "value": "tests" + }, + "kind": "testSource" + } + ] + } + ], + "optionValues": { + "features": { + "each": "/features/*", + "value": "@key" + } + } + } + }, + "tasks": [ + { + "id": "build", + "displayName": "Build", + "group": "build", + "scopes": [ + "project" + ], + "arguments": [ + "build", + "--message-format=short", + "${optionArguments}" + ] + }, + { + "id": "check", + "displayName": "Check", + "group": "verification", + "scopes": [ + "project" + ], + "arguments": [ + "check", + "--message-format=short", + "${optionArguments}" + ] + }, + { + "id": "clean", + "displayName": "Clean", + "group": "build", + "scopes": [ + "project" + ], + "arguments": [ + "clean" + ] + }, + { + "id": "example", + "displayName": "Run Example", + "group": "run", + "execution": "application", + "scopes": [ + "target" + ], + "targetTags": [ + "example" + ], + "arguments": [ + "run", + "--example", + "${target.name}", + "${optionArguments}" + ] + } + ], + "run": [ + { + "id": "binary", + "fromTargets": { + "tags": [ + "application" + ] + }, + "arguments": [ + "run", + "--bin", + "${target.name}", + "${optionArguments}" + ], + "extension": { + "bin": "${target.name}" + } + } + ], + "test": { + "scopes": [ + "project", + "target" + ], + "targetTags": [ + "test" + ], + "filters": [ + "case" + ], + "arguments": [ + "test", + "${optionArguments}", + { + "value": "--test", + "when": "targetSelected" + }, + { + "value": "${target.name}", + "when": "targetSelected" + }, + { + "value": "--", + "when": "caseSelected" + }, + { + "value": "${test.case}", + "when": "caseSelected" + }, + { + "value": "--exact", + "when": "caseSelected" + } + ] + }, + "diagnostics": { + "decoder": "lineMatcher", + "patterns": [ + { + "regex": "^([^\\s:][^:]*\\.rs):(\\d+):(\\d+): (error|warning)(?:\\[\\w+\\])?: (.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + }, + "sync": { + "watch": [ + "Cargo.toml", + "Cargo.lock" + ] + }, + "layout": { + "excluded": [ + "target" + ] + } + } + ] +} diff --git a/contract/v1/fixtures/debug-adapter-language-server.plugin.json b/contract/v1/fixtures/debug-adapters/debug-adapter-language-server.plugin.json similarity index 100% rename from contract/v1/fixtures/debug-adapter-language-server.plugin.json rename to contract/v1/fixtures/debug-adapters/debug-adapter-language-server.plugin.json diff --git a/contract/v1/fixtures/debug-adapter-rejections.json b/contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json similarity index 100% rename from contract/v1/fixtures/debug-adapter-rejections.json rename to contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json diff --git a/contract/v1/fixtures/debug-adapter-remote-attach.plugin.json b/contract/v1/fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json similarity index 100% rename from contract/v1/fixtures/debug-adapter-remote-attach.plugin.json rename to contract/v1/fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json diff --git a/contract/v1/fixtures/debug-adapter.plugin.json b/contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json similarity index 100% rename from contract/v1/fixtures/debug-adapter.plugin.json rename to contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json diff --git a/contract/v1/fixtures/editor-actions.plugin.json b/contract/v1/fixtures/editor/editor-actions.plugin.json similarity index 100% rename from contract/v1/fixtures/editor-actions.plugin.json rename to contract/v1/fixtures/editor/editor-actions.plugin.json diff --git a/contract/v1/fixtures/keymap-import-provider.plugin.json b/contract/v1/fixtures/keymap-import/keymap-import-provider.plugin.json similarity index 100% rename from contract/v1/fixtures/keymap-import-provider.plugin.json rename to contract/v1/fixtures/keymap-import/keymap-import-provider.plugin.json diff --git a/contract/v1/fixtures/command-plans.json b/contract/v1/fixtures/languages/command-plans.json similarity index 100% rename from contract/v1/fixtures/command-plans.json rename to contract/v1/fixtures/languages/command-plans.json diff --git a/contract/v1/fixtures/grammars/http.monarch.json b/contract/v1/fixtures/languages/grammars/http.monarch.json similarity index 100% rename from contract/v1/fixtures/grammars/http.monarch.json rename to contract/v1/fixtures/languages/grammars/http.monarch.json diff --git a/contract/v1/fixtures/language-grammar.plugin.json b/contract/v1/fixtures/languages/language-grammar.plugin.json similarity index 100% rename from contract/v1/fixtures/language-grammar.plugin.json rename to contract/v1/fixtures/languages/language-grammar.plugin.json diff --git a/contract/v1/fixtures/language-host-integration.plugin.json b/contract/v1/fixtures/languages/language-host-integration.plugin.json similarity index 100% rename from contract/v1/fixtures/language-host-integration.plugin.json rename to contract/v1/fixtures/languages/language-host-integration.plugin.json diff --git a/contract/v1/fixtures/language-packaged-server.plugin.json b/contract/v1/fixtures/languages/language-packaged-server.plugin.json similarity index 100% rename from contract/v1/fixtures/language-packaged-server.plugin.json rename to contract/v1/fixtures/languages/language-packaged-server.plugin.json diff --git a/contract/v1/fixtures/language-system-command.plugin.json b/contract/v1/fixtures/languages/language-system-command.plugin.json similarity index 100% rename from contract/v1/fixtures/language-system-command.plugin.json rename to contract/v1/fixtures/languages/language-system-command.plugin.json diff --git a/contract/v1/fixtures/node-runtime.plugin.json b/contract/v1/fixtures/languages/node-runtime.plugin.json similarity index 100% rename from contract/v1/fixtures/node-runtime.plugin.json rename to contract/v1/fixtures/languages/node-runtime.plugin.json diff --git a/contract/v1/fixtures/runtime-versions.json b/contract/v1/fixtures/languages/runtime-versions.json similarity index 100% rename from contract/v1/fixtures/runtime-versions.json rename to contract/v1/fixtures/languages/runtime-versions.json diff --git a/contract/v1/fixtures/hello-lithe.plugin.json b/contract/v1/fixtures/manifest/hello-lithe.plugin.json similarity index 100% rename from contract/v1/fixtures/hello-lithe.plugin.json rename to contract/v1/fixtures/manifest/hello-lithe.plugin.json diff --git a/contract/v1/fixtures/invalid-manifests.json b/contract/v1/fixtures/manifest/invalid-manifests.json similarity index 99% rename from contract/v1/fixtures/invalid-manifests.json rename to contract/v1/fixtures/manifest/invalid-manifests.json index 2a20bb4..5ca1ec5 100644 --- a/contract/v1/fixtures/invalid-manifests.json +++ b/contract/v1/fixtures/manifest/invalid-manifests.json @@ -825,7 +825,7 @@ { "name": "future capability version", "base": "build-static-file.plugin.json", - "edits": [{ "op": "set", "path": "/requires/buildSystems", "value": 3 }], + "edits": [{ "op": "set", "path": "/requires/buildSystems", "value": 5 }], "expectedCode": "plugin.api.unsupported" }, { diff --git a/contract/v1/fixtures/publisher-bundled.plugin.json b/contract/v1/fixtures/package-trust/publisher-bundled.plugin.json similarity index 100% rename from contract/v1/fixtures/publisher-bundled.plugin.json rename to contract/v1/fixtures/package-trust/publisher-bundled.plugin.json diff --git a/contract/v1/fixtures/package-trust/signature-vectors.json b/contract/v1/fixtures/package-trust/signature-vectors.json new file mode 100644 index 0000000..70e0d2b --- /dev/null +++ b/contract/v1/fixtures/package-trust/signature-vectors.json @@ -0,0 +1,40 @@ +{ + "schemaVersion": 1, + "description": "Public cross-platform test vectors for the plugin package signature. macOS PluginPackageSignature tests consume them; Windows does not verify package signatures yet, and its verifier must consume the same vectors. The Ed25519 key is the RFC 8032 test key; it must never sign real packages. Regenerating requires updating every platform test that consumes them in the same change.", + "ed25519": { + "seedHex": "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + "publicKeyHex": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", + "messageEmptySignatureHex": "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b" + }, + "packageFiles": { + "plugin.json": "plugin manifest bytes", + "language-server/bin/server.js": "console.log('server');\n", + "插件/服务器.js": "console.log('非 ASCII 路径');\n", + "日本語/README.md": "# サンプル\n", + "assets/заметки.txt": "заметки\n" + }, + "canonicalPayload": { + "keyID": "lithe-test-v1", + "pluginID": "dev.example.plugin", + "pluginVersion": "0.3.0", + "files": { + "plugin.json": "1ec2dc0f2dacb3b0c43dd9dc6035731249094d080f60394236f68450420e6a29", + "language-server/bin/server.js": "fca7a5298331fc1e306876075edf8f47b791b200b182c841dc250c54d5138760", + "插件/服务器.js": "481e4738eca92d68a48ef69591c5ecb0afd2529a0eba37b489665cd31b457ad5", + "日本語/README.md": "667ec9c9c320a9dee9f64ce35daf3ec27cf5bc06aaa0625253c788301bd5f7af", + "assets/заметки.txt": "d303fa2a84fb9a7d0c0e50cf1c27ff33f8c90cb53d366138929934f4e2e758fb" + }, + "payloadBase64": "c2NoZW1hVmVyc2lvbj0xCmFsZ29yaXRobT1lZDI1NTE5CmtleUlEPWxpdGhlLXRlc3QtdjEKcGx1Z2luSUQ9WkdWMkxtVjRZVzF3YkdVdWNHeDFaMmx1CnBsdWdpblZlcnNpb249TUM0ekxqQQpmaWxlPVlYTnpaWFJ6TDlDMzBMRFF2TkMxMFlMUXV0QzRMblI0ZEE9ZDMwM2ZhMmE4NGZiOWE3ZDBjMGU1MGNmMWMyN2ZmMzNmOGM5MGNiNTNkMzY2MTM4OTI5OTM0ZjRlMmU3NThmYgpmaWxlPWJHRnVaM1ZoWjJVdGMyVnlkbVZ5TDJKcGJpOXpaWEoyWlhJdWFuTT1mY2E3YTUyOTgzMzFmYzFlMzA2ODc2MDc1ZWRmOGY0N2I3OTFiMjAwYjE4MmM4NDFkYzI1MGM1NGQ1MTM4NzYwCmZpbGU9Y0d4MVoybHVMbXB6YjI0PTFlYzJkYzBmMmRhY2IzYjBjNDNkZDlkYzYwMzU3MzEyNDkwOTRkMDgwZjYwMzk0MjM2ZjY4NDUwNDIwZTZhMjkKZmlsZT01by1TNUx1MkwtYWNqZVdLb2VXWnFDNXFjdz00ODFlNDczOGVjYTkyZDY4YTQ4ZWY2OTU5MWM1ZWNiMGFmZDI1MjlhMGViYTM3YjQ4OTY2NWNkMzFiNDU3YWQ1CmZpbGU9NXBlbDVweXM2S3FlTDFKRlFVUk5SUzV0WkE9NjY3ZWM5YzljMzIwYTlkZWU5ZjY0Y2UzNWRhZjNlYzI3Y2Y1YmMwNmFhYTA2MjUyNTNjNzg4MzAxYmQ1ZjdhZgo=", + "signatureBase64": "lFd79pLu83LDn4D8N0cSAtm3jzHGNLIVGBAfS0v4Wzfnim4DLcCOKITqS8v5iOGP951whGhZIZxqdMZULHy+Cw==" + }, + "contentDigest": { + "files": { + "plugin.json": "1ec2dc0f2dacb3b0c43dd9dc6035731249094d080f60394236f68450420e6a29", + "language-server/bin/server.js": "fca7a5298331fc1e306876075edf8f47b791b200b182c841dc250c54d5138760", + "插件/服务器.js": "481e4738eca92d68a48ef69591c5ecb0afd2529a0eba37b489665cd31b457ad5", + "日本語/README.md": "667ec9c9c320a9dee9f64ce35daf3ec27cf5bc06aaa0625253c788301bd5f7af", + "assets/заметки.txt": "d303fa2a84fb9a7d0c0e50cf1c27ff33f8c90cb53d366138929934f4e2e758fb" + }, + "digest": "4dcdacc3e80f23932029638487a3074301d95a6721151a03a010d8633fe55a61" + } +} diff --git a/contract/v1/fixtures/project-generator.plugin.json b/contract/v1/fixtures/project-generators/project-generator.plugin.json similarity index 96% rename from contract/v1/fixtures/project-generator.plugin.json rename to contract/v1/fixtures/project-generators/project-generator.plugin.json index 548eed5..2add876 100644 --- a/contract/v1/fixtures/project-generator.plugin.json +++ b/contract/v1/fixtures/project-generators/project-generator.plugin.json @@ -27,8 +27,8 @@ {"buildSystemID": "maven", "when": {"field": "buildSystem", "equals": "maven"}} ], "steps": [ - {"kind": "files", "source": "project-generator-templates/app"}, - {"kind": "files", "source": "project-generator-templates/main", + {"kind": "files", "source": "templates/app"}, + {"kind": "files", "source": "templates/main", "target": "src/${field.groupId|package|path}", "when": [{"field": "buildSystem", "equals": "maven"}, {"field": "sample", "equals": true}]} ], diff --git a/contract/v1/fixtures/project-plans.json b/contract/v1/fixtures/project-generators/project-plans.json similarity index 99% rename from contract/v1/fixtures/project-plans.json rename to contract/v1/fixtures/project-generators/project-plans.json index b4a16fe..f35b509 100644 --- a/contract/v1/fixtures/project-plans.json +++ b/contract/v1/fixtures/project-generators/project-plans.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "description": "Golden answers of the project.* commands for project-generator.plugin.json. generationPlan cases read templates with the fixtures directory as the package path.", + "description": "Golden answers of the project.* commands for project-generator.plugin.json. generationPlan cases read templates with fixtures/project-generators as the package path.", "cases": [ { "name": "form defaults", diff --git a/contract/v1/fixtures/project-generator-templates/.gitattributes b/contract/v1/fixtures/project-generators/templates/.gitattributes similarity index 100% rename from contract/v1/fixtures/project-generator-templates/.gitattributes rename to contract/v1/fixtures/project-generators/templates/.gitattributes diff --git a/contract/v1/fixtures/project-generator-templates/app/README.md b/contract/v1/fixtures/project-generators/templates/app/README.md similarity index 100% rename from contract/v1/fixtures/project-generator-templates/app/README.md rename to contract/v1/fixtures/project-generators/templates/app/README.md diff --git a/contract/v1/fixtures/project-generator-templates/app/dot-gitignore b/contract/v1/fixtures/project-generators/templates/app/dot-gitignore similarity index 100% rename from contract/v1/fixtures/project-generator-templates/app/dot-gitignore rename to contract/v1/fixtures/project-generators/templates/app/dot-gitignore diff --git a/contract/v1/fixtures/project-generator-templates/main/Main.java b/contract/v1/fixtures/project-generators/templates/main/Main.java similarity index 100% rename from contract/v1/fixtures/project-generator-templates/main/Main.java rename to contract/v1/fixtures/project-generators/templates/main/Main.java diff --git a/contract/v1/fixtures/settings-v2.plugin.json b/contract/v1/fixtures/settings/settings-v2.plugin.json similarity index 100% rename from contract/v1/fixtures/settings-v2.plugin.json rename to contract/v1/fixtures/settings/settings-v2.plugin.json diff --git a/contract/v1/proposals/toolchains/fixtures/java-tool-definition.json b/contract/v1/fixtures/toolchains/proposal/java-tool-definition.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/java-tool-definition.json rename to contract/v1/fixtures/toolchains/proposal/java-tool-definition.json diff --git a/contract/v1/proposals/toolchains/fixtures/local.json b/contract/v1/fixtures/toolchains/proposal/local.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/local.json rename to contract/v1/fixtures/toolchains/proposal/local.json diff --git a/contract/v1/proposals/toolchains/fixtures/manifest-rejections.json b/contract/v1/fixtures/toolchains/proposal/manifest-rejections.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/manifest-rejections.json rename to contract/v1/fixtures/toolchains/proposal/manifest-rejections.json diff --git a/contract/v1/proposals/toolchains/fixtures/mise-provider.json b/contract/v1/fixtures/toolchains/proposal/mise-provider.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/mise-provider.json rename to contract/v1/fixtures/toolchains/proposal/mise-provider.json diff --git a/contract/v1/proposals/toolchains/fixtures/query-cases.json b/contract/v1/fixtures/toolchains/proposal/query-cases.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/query-cases.json rename to contract/v1/fixtures/toolchains/proposal/query-cases.json diff --git a/contract/v1/proposals/toolchains/fixtures/resolution-cases.json b/contract/v1/fixtures/toolchains/proposal/resolution-cases.json similarity index 100% rename from contract/v1/proposals/toolchains/fixtures/resolution-cases.json rename to contract/v1/fixtures/toolchains/proposal/resolution-cases.json diff --git a/contract/v1/fixtures/tool-definition.plugin.json b/contract/v1/fixtures/toolchains/tool-definition.plugin.json similarity index 100% rename from contract/v1/fixtures/tool-definition.plugin.json rename to contract/v1/fixtures/toolchains/tool-definition.plugin.json diff --git a/contract/v1/fixtures/toolchain-provider.plugin.json b/contract/v1/fixtures/toolchains/toolchain-provider.plugin.json similarity index 100% rename from contract/v1/fixtures/toolchain-provider.plugin.json rename to contract/v1/fixtures/toolchains/toolchain-provider.plugin.json diff --git a/contract/v1/fixtures/native-controls.plugin.json b/contract/v1/fixtures/ui/native-controls.plugin.json similarity index 100% rename from contract/v1/fixtures/native-controls.plugin.json rename to contract/v1/fixtures/ui/native-controls.plugin.json diff --git a/contract/v1/fixtures/native-form.plugin.json b/contract/v1/fixtures/ui/native-form.plugin.json similarity index 100% rename from contract/v1/fixtures/native-form.plugin.json rename to contract/v1/fixtures/ui/native-form.plugin.json diff --git a/contract/v1/fixtures/native-panel.plugin.json b/contract/v1/fixtures/ui/native-panel.plugin.json similarity index 100% rename from contract/v1/fixtures/native-panel.plugin.json rename to contract/v1/fixtures/ui/native-panel.plugin.json diff --git a/contract/v1/fixtures/web-panel.plugin.json b/contract/v1/fixtures/ui/web-panel.plugin.json similarity index 100% rename from contract/v1/fixtures/web-panel.plugin.json rename to contract/v1/fixtures/ui/web-panel.plugin.json diff --git a/contract/v1/fixtures/vcs-provider.plugin.json b/contract/v1/fixtures/vcs/vcs-provider.plugin.json similarity index 100% rename from contract/v1/fixtures/vcs-provider.plugin.json rename to contract/v1/fixtures/vcs/vcs-provider.plugin.json diff --git a/contract/v1/plugin-manifest.schema.json b/contract/v1/plugin-manifest.schema.json index 6c6f572..a418847 100644 --- a/contract/v1/plugin-manifest.schema.json +++ b/contract/v1/plugin-manifest.schema.json @@ -38,11 +38,11 @@ "properties": { "backend": { "description": "Required exactly when backend is present: the plugin's own process. Version 1 serves only its web pages. Version 2 may also start on onWorkspaceOpen or a plugin command, receives workspace contexts through plugin-function protocol 3, and may be granted workspace.metadata.", "enum": [1, 2] }, "buildModelInputs": {"description": "Optional public model sharing between already enabled workspace plugins; no installation, private function, or process authority. Required exactly when modelSharing or modelInputs is used. Consumers require buildProviders 6. Version 1 producers cannot also consume inputs.", "enum": [1]}, - "buildProviders": { "description": "Required exactly when any buildSystems[].provider is present, including own-process and shared language-server providers. Declares host support for Provider orchestration, independent of declarative buildSystems and serverProcess launch support; version 2 adds task execution, version 3 test execution/discovery, version 4 application run preparation, and version 5 native debug preparation; version 6 optional read-only model inputs.", "enum": [1, 2, 3, 4, 5, 6] }, - "buildSystems": { "description": "Declarative build detection, models, and command plans; does not imply buildProviders support.", "enum": [1, 2] }, + "buildProviders": { "description": "Required exactly when any buildSystems[].provider is present, including own-process and shared language-server providers. Declares host support for Provider orchestration, independent of declarative buildSystems and serverProcess launch support; version 2 adds task execution, version 3 test execution/discovery, version 4 application run preparation, and version 5 native debug preparation; version 6 optional read-only model inputs; version 7 non-sensitive native configuration sources; version 8 inline provider test reports; version 9 opaque semantic test selectors; version 10 typed run options and service/task preparation; version 11 registry-selected adapter and host-owned connection target for service debugging.", "enum": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] }, + "buildSystems": { "description": "Declarative build detection, models, and command plans; v2 adds the executable setting adapter, v3 instance-scoped options, v4 declarative preparation roles; does not imply buildProviders support.", "enum": [1, 2, 3, 4] }, "debugAdapters": { "description": "Required exactly when debugAdapters is non-empty. Version 2 adds bounded target argument and environment bindings. The DAP session itself is negotiated by the adapter in initialize.", "enum": [1, 2] }, "executableResolution": { "enum": [1, 2], "description": "Shared executable candidate ordering and strict selection." }, - "executionProviders": { "enum": [1] }, + "executionProviders": { "enum": [1] }, "languageServers": { "description": "Required exactly when languageServers is non-empty. Version 2 adds languageServers[].workspaceConfiguration.", "enum": [1, 2] }, "languages": { "description": "Version 2 adds languages[].grammar and languages[].configuration and is required exactly when one of them is used.", "enum": [1, 2] }, "packageTrust": { "description": "Required exactly when vendor.bundledSignatureRequirement is present.", "enum": [1] }, @@ -1955,6 +1955,78 @@ } } ] + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "kind", + "toolchainID" + ], + "properties": { + "kind": { + "const": "toolchain" + }, + "toolchainID": { + "type": "string", + "maxLength": 192, + "pattern": "^(?:[a-z0-9]+(?:[.-][a-z0-9]+)*:)?[a-z][a-z0-9-]{0,63}$" + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "priority": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "insertBefore": { + "type": "string", + "minLength": 1 + }, + "insertAfter": { + "type": "string", + "minLength": 1 + }, + "platforms": { + "type": "array", + "items": { + "enum": [ + "macos", + "linux", + "windows" + ] + } + } + }, + "allOf": [ + { + "not": { + "required": [ + "priority", + "insertBefore" + ] + } + }, + { + "not": { + "required": [ + "priority", + "insertAfter" + ] + } + }, + { + "not": { + "required": [ + "insertBefore", + "insertAfter" + ] + } + } + ] } ] }, @@ -2292,6 +2364,7 @@ "invocation" ], "properties": { + "configuration": {"type": "object", "additionalProperties": false, "required": ["source"], "properties": {"source": {"const": "provider"}}, "description": "Format-independent native configuration facts and provider-owned staged editing; requires buildProviders 7 and an owned provider."}, "modelSharing": {"type": "object", "additionalProperties": false, "properties": {"options": {"type": "array", "maxItems": 32, "uniqueItems": true, "items": {"type": "string"}}}, "description": "Opt into publishing normalized projects, targets and content roots, plus only these explicitly shared ordinary build option values. Never tasks, run plans, settings or optionValues."}, "modelInputs": {"type": "array", "minItems": 1, "maxItems": 8, "uniqueItems": true, "items": {"type": "object", "additionalProperties": false, "required": ["pluginID", "buildSystemID"], "properties": {"pluginID": {"type": "string"}, "buildSystemID": {"type": "string"}}}, "description": "Exact optional producer slots, sorted by Core. Missing or disabled producers are unavailable and never installed or activated automatically."}, "id": { @@ -2403,7 +2476,7 @@ "$ref": "#/$defs/buildVersionProbe" } }, - "description": "java, node, and go are host-managed and take only id and minimumVersion; any other runtime declares its executables and version probe." + "description": "java, node, and go use host version probes. With executableResolution 2 they may select one toolchain candidate; otherwise they take only id and minimumVersion. Other runtimes declare their executables and version probe." } }, "version": { @@ -2454,6 +2527,10 @@ ] }, "default": {}, + "scope": { + "description": "Instance scope requires buildSystems 3; omitted means workspace. Hosts persist each detected root independently.", + "enum": ["workspace", "instance"] + }, "choices": { "type": "array", "items": { @@ -2667,6 +2744,19 @@ "description": "Omitted expands argv in Core; provider sends lithe/build/execute. Provider tasks require buildProviders 2, empty arguments, and task execution.", "enum": ["provider"] }, + "preparation": { + "description": "buildSystems 4: automatic project preparation selected by workflow purpose and canonical model source role. Bindings must be unique within one build system; no prompted or fromModel tasks.", + "type": "object", + "additionalProperties": false, + "required": ["purpose", "sourceRoles"], + "properties": { + "purpose": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9._-]*$", "maxLength": 120 }, + "sourceRoles": { + "type": "array", "minItems": 1, "maxItems": 6, "uniqueItems": true, + "items": { "enum": ["source", "generatedSource", "resource", "testSource", "generatedTestSource", "testResource"] } + } + } + }, "requiresUserArguments": { "type": "boolean" }, @@ -2698,12 +2788,16 @@ ], "properties": { "debug": { - "description": "Native executable debug preparation (buildProviders 5); same-package debugAdapters 2 launch adapter with typed program/cwd/args/env bindings.", - "type": "object", "additionalProperties": false, "required": ["adapter"], - "properties": { "adapter": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$", "maxLength": 64 } } + "description": "Same-package native launch adapter (v5), or a public registry configurationType for connection attach (v11 service runs).", + "type": "object", "additionalProperties": false, + "oneOf": [{"required": ["adapter"]}, {"required": ["configurationType"]}], + "properties": { + "adapter": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$", "maxLength": 64 }, + "configurationType": { "type": "string", "minLength": 1, "maxLength": 256 } + } }, "source": { - "description": "Provider-owned application preparation (buildProviders 4), fromTargets only, without host command templates.", + "description": "Provider-owned application preparation (buildProviders 4) or service/task preparation (buildProviders 10), fromTargets only, without host command templates.", "enum": ["provider"] }, "id": { @@ -2804,6 +2898,11 @@ "arguments" ], "properties": { + "selectorKinds": { + "type": "array", "maxItems": 16, "uniqueItems": true, + "description": "Public opaque semantic selector namespaces; provider tests and buildProviders 9 required.", + "items": {"type": "string", "maxLength": 120, "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]*$"} + }, "discovery": { "type": "boolean", "description": "Opt in to lithe/build/discoverTests; requires provider test source and suite/case filters." @@ -2873,6 +2972,18 @@ "const": "hostIntegration" } } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "decoder" + ], + "properties": { + "decoder": { + "const": "provider" + } + } } ] } diff --git a/contract/v1/toolchains.md b/contract/v1/toolchains/README.md similarity index 82% rename from contract/v1/toolchains.md rename to contract/v1/toolchains/README.md index af255a1..23b03ef 100644 --- a/contract/v1/toolchains.md +++ b/contract/v1/toolchains/README.md @@ -8,11 +8,17 @@ reuses each enabled provider's plugin backend, sends `toolchains/query` through `lsp.toolchainRequest`, passes every raw reply with native facts to `decodeReply`, runs the definition's version probe on executables Core returns from `candidates`, and shows the result. Any change to a watched input reloads the -whole snapshot. Nothing is saved, and no build, run or language-server launch -uses the result yet. Windows is not connected. -The [broader proposal](proposals/toolchains/README.md) still describes unsupported +whole snapshot. Nothing is saved by these pages. The pinned Stage D integration +adds a separate fresh-resolution adapter for macOS Build and Provider Run; +settings detections are never used as launch permits. Existing Java and +language-server consumers still use their previous adapters. Windows has no +toolchain adapter and does not advertise `toolDefinitions` or +`toolchainProviders`, so Core refuses packages that require them there. +Publisher signatures provide independent origin evidence when present; legacy +unsigned records never do. +The [broader proposal](proposal/README.md) still describes unsupported consumer migration, native lifecycle, persistence and manager-plugin work. -The active shape is [plugin-manifest.schema.json](plugin-manifest.schema.json), +The active shape is [plugin-manifest.schema.json](../plugin-manifest.schema.json), not the proposal envelopes. ## Manifest admission and registry @@ -23,6 +29,9 @@ not the proposal envelopes. `backend.launch` / `toolchains.provide`. Capability lists are exact: missing or unused capabilities reject the manifest. A definition/provider alone is a valid contribution. Old executable candidate sets retain their strict source types. +Build consumers may also require executableResolution 2 for their existing +`toolchain` candidate without contributing a definition; they do not gain +definition-owned candidate fields. Definitions own IDs, labels/icons, `executableLayouts`, ordered `executables`, `version`, `homeLayouts`, `discovery`, `homeVariable`, `commands`, `wrappers`, @@ -47,6 +56,16 @@ execution. Short definition IDs require independently verified official origin; community definitions use `:`. Community providers may answer short IDs. No host-owned tool table, name alias or manager parser is added. +A native Build consumer that directly executes a tool currently accepts only +`["${executable}", "${arguments}"]`: its tool context carries a program and +separate Build arguments. Extra launch flags or `${home}` tokens are refused +before probing, rather than dropped. An installation dependency instead consumes +checked version evidence and declared exports; it does not instantiate the tool's +entry-launch recipe. For example a runtime can declare a JAR recipe while a build +engine consumes only that runtime's installation and environment. Such a dependency +is not published as a direct executable binding. Entry processes continue to use +their own declared process recipe; supporting more direct templates remains pending. + ## Two installation routes A definition receives installations through either route, or both. Neither is @@ -113,6 +132,10 @@ the host-owned session, not a provider claim. `platform` is `macos`, `windows` o generation,providerID,authority:{kind:"local"},context,workspace,tools}}`. `context` is `{contextID,generation}` or `null`; workspace queries require it. `workspace` is `{rootID,root,directory}` or `null` for global-only queries. +The JSON-RPC `id` is a positive integer or a nonempty string of at most 128 +UTF-8 bytes without control characters; the reply preserves its exact type and +value. Core's LSP transport uses string IDs. Numeric `1` and string `"1"` are +distinct identities and cannot acknowledge each other's request. `directory` is normalized workspace-relative, with `.` for the opened root. Tools are unique and sorted. This envelope is validated as inert data; no query is dispatched and no protocol capability is negotiated by this command. @@ -142,7 +165,7 @@ remote authority, unsafe paths and malformed error mappings reject it. The snapshot contains `{installations,statements,watch,diagnostics}`; normalized entries carry qualified `:` provenance and project source directory. JSON-RPC errors are checked against the distinct semantic/numeric map -in the [protocol proposal](proposals/toolchains/protocol.md#stable-failure-mapping). +in the [protocol proposal](proposal/protocol.md#stable-failure-mapping). Cancellation, deadlines, cache invalidation and whole-generation requery remain native-client implementation work, not behavior performed by this decoder. @@ -168,7 +191,7 @@ Saving, conflict protection and `.lithe/.gitignore` updates are not implemented. platform,workspaceRoot,filesystem}` and accepts `workspaceRoots`, `filesystemCaseSensitive`, `local`, `minimumVersion`, `probes`, `queries`, `systemInstallations`, `globalDefault`, `environment`, `commandHomes`, -`projectCodeAllowed` and `providerChoices`. `local` defaults to an empty v1 +`projectCodeAllowed`, `providerChoices` and `fixed`. `local` defaults to an empty v1 document. Probes are host observations `{executable,output,exitCode}` keyed by the canonical executable a selection reports; system installations are `{tool,home}`. Shell environment home variables and `commandHomes` come from the native login-shell @@ -192,6 +215,18 @@ that could not answer. Safety, identity and cancellation failures are not converted to missing tools. A valid explicit pin does not need lower-layer provider answers at all. +A consumer may supply `fixed:{home}` or `fixed:{executable}` for one operation. +Each object has exactly one field, an absolute native path bounded to 4,096 UTF-8 +bytes, without traversal or control characters. It contains no version, environment, +provider or execution grant. This explicit choice precedes directory pins and fails +terminally if invalid; lower scans and providers cannot replace it. Installation +choices use the definition's layouts and exports. An executable choice preserves +its lexical launcher path, has no inferred home, and omits home exports and PATH +entries. It requires a fresh successful version probe even without a minimum. +A definition whose launch requires `${home}` cannot use that choice. A lexical or +canonical executable inside the workspace requires the current explicit action +before it becomes a probe target. Directory pins and packages stay unchanged. + Four layers apply: fixed nearest pin; nearest project source (wrapper > environment > declaration, native reader before compatible reader of the same source); application global default > unambiguous/chosen provider global > login shell; @@ -229,12 +264,16 @@ are 256 roots, 4096 entries and 64 ancestors for the host adapter. automatic layers without resolving a directory. It requires `{registry,tool, platform,workspaceRoot,filesystem}` and accepts `workspaceRoots`, `filesystemCaseSensitive`, `minimumVersion`, `probes`, `systemInstallations`, -`providerInstallations` and `environment`, with the same meaning and checks as in +`providerInstallations`, `environment`, `fixed` and `projectCodeAllowed`, with the same meaning and checks as in `resolve`. `providerInstallations` (at most 512) are `{tool,home,provider}` rows a host copies from `decodeReply` snapshots; `provider` must name a registry provider `pluginID:providerID` declaring the tool, and the row keeps that attribution when a scan reports the same canonical home. The output is `{tool,shell,installations,skipped,selected,conflicts}`: +A checked `fixed` choice without a version contributes a `probeFailed` skipped row +with its executable for the native probe. An unauthorized workspace program never +contributes such a target. This candidate report itself grants no authority. + - `shell` lists each declared home variable present in `environment` as `{candidateID,variable,home,executable?,version?|reason?}`, checked through @@ -254,8 +293,8 @@ workspace root are refused before any probe, so a project program is never run to read its version. The macOS host bounds probes by count, concurrency and total time, and reports skipped work as diagnostics rather than a missing tool. -Production-shape examples are [tool-definition.plugin.json](fixtures/tool-definition.plugin.json) -and [toolchain-provider.plugin.json](fixtures/toolchain-provider.plugin.json). +Production-shape examples are [tool-definition.plugin.json](../fixtures/toolchains/tool-definition.plugin.json) +and [toolchain-provider.plugin.json](../fixtures/toolchains/toolchain-provider.plugin.json). Core acceptance vectors are in [toolchain_definitions.rs](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/tests/toolchain_definitions.rs), with sibling pins/query/resolution suites. Passing them is not native product diff --git a/contract/v1/proposals/toolchains/README.md b/contract/v1/toolchains/proposal/README.md similarity index 98% rename from contract/v1/proposals/toolchains/README.md rename to contract/v1/toolchains/proposal/README.md index 73b6019..e250eed 100644 --- a/contract/v1/proposals/toolchains/README.md +++ b/contract/v1/toolchains/proposal/README.md @@ -1,7 +1,7 @@ # Plugin-injected toolchains (draft) > **Status: broader proposal; a Core-only foundation is now implemented.** -> The implemented subset is specified in [the active contract](../../toolchains.md). +> The implemented subset is specified in [the active contract](../README.md). > This directory remains a review artifact for > [PR #1581's replacement plan](https://github.com/1lck/Lithe-IDEA/pull/1581#issuecomment-6541366112). > It is not an extension of the currently accepted `plugin.json` format. @@ -46,7 +46,7 @@ not authorize replacing unrelated host commands. | [query.schema.json](query.schema.json) | Query request/reply shapes | | [resolution.schema.json](resolution.schema.json) | Resolver outcomes and structured reasons | | [local.schema.json](local.schema.json) | Proposed extension of the machine-local document with directory pins | -| `fixtures/` | Illustrative contributions, queries, resolution outcomes, pins, and rejections | +| `fixtures/toolchains/proposal/` | Illustrative contributions, queries, resolution outcomes, pins, and rejections | Schemas describe JSON shape. Signature verification, duplicate-definition registration, enabled-plugin ownership, path containment, executable evidence, diff --git a/contract/v1/proposals/toolchains/local.schema.json b/contract/v1/toolchains/proposal/local.schema.json similarity index 97% rename from contract/v1/proposals/toolchains/local.schema.json rename to contract/v1/toolchains/proposal/local.schema.json index 0727b88..481d584 100644 --- a/contract/v1/proposals/toolchains/local.schema.json +++ b/contract/v1/toolchains/proposal/local.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/proposals/toolchains/local.schema.json", + "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/toolchains/proposal/local.schema.json", "title": "Proposed local toolchain pins extension (not implemented)", "description": "Review-only complete document shape for .lithe/toolchains/local.json. The active toolchain-local-v1.schema.json remains unchanged and does not accept pins.", "type": "object", diff --git a/contract/v1/proposals/toolchains/manifest-contributions.schema.json b/contract/v1/toolchains/proposal/manifest-contributions.schema.json similarity index 99% rename from contract/v1/proposals/toolchains/manifest-contributions.schema.json rename to contract/v1/toolchains/proposal/manifest-contributions.schema.json index 20c8906..a86dde1 100644 --- a/contract/v1/proposals/toolchains/manifest-contributions.schema.json +++ b/contract/v1/toolchains/proposal/manifest-contributions.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/proposals/toolchains/manifest-contributions.schema.json", + "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/toolchains/proposal/manifest-contributions.schema.json", "title": "PROPOSAL: toolchain contribution projections, not a plugin manifest", "description": "A standalone review envelope for proposed manifest fields. Passing this schema does not make a plugin installable and does not prove signature, registry, filesystem or executable safety. See manifest.md.", "type": "object", diff --git a/contract/v1/proposals/toolchains/manifest.md b/contract/v1/toolchains/proposal/manifest.md similarity index 98% rename from contract/v1/proposals/toolchains/manifest.md rename to contract/v1/toolchains/proposal/manifest.md index fae3ab1..2f45a1c 100644 --- a/contract/v1/proposals/toolchains/manifest.md +++ b/contract/v1/toolchains/proposal/manifest.md @@ -1,7 +1,7 @@ # Toolchain manifest contributions (proposal) **Status: full proposal, not a claim of native host support.** The implemented -Core subset is specified by [the active contract](../../toolchains.md). This +Core subset is specified by [the active contract](../README.md). This document follows the [final scope of PR #1581](https://github.com/1lck/Lithe-IDEA/pull/1581#issuecomment-6541366112). It is a host contract draft, not a plugin author tutorial. The current manifest @@ -63,7 +63,7 @@ pretend that an unimplemented launch adapter supports the draft. ## Tool definitions -The [Java-support-style projection](fixtures/java-tool-definition.json) supplies +The [Java-support-style projection](../../fixtures/toolchains/proposal/java-tool-definition.json) supplies the tool name and every executable policy. Its short ID is valid only in an independently official installation context; the fixture itself is not signed. @@ -144,7 +144,7 @@ rejected candidate never leak into the next candidate's environment. ## One candidate resolver, four product layers The draft extends the -[existing candidate model](../../README.md#shared-executable-candidates-executableresolution-1) +[existing candidate model](../../README.md#shared-executable-candidates-executableresolution-12) rather than building a second runtime resolver: - `setting` gains mandatory `layer: "project" | "global"` in this projection. @@ -194,7 +194,7 @@ query results and reason shapes; no current `skip` consumer changes in this stag ## Providers, file watching, and bootstrap -The [mise projection](fixtures/mise-provider.json) declares `id`, `displayName`, +The [mise projection](../../fixtures/toolchains/proposal/mise-provider.json) declares `id`, `displayName`, `tools`, and `watchFiles`. `watchFiles` is optional: without it a provider reports installations only and any returned statement is rejected. That shape lets the plugin defining a tool answer for its own installations when declarative @@ -301,7 +301,7 @@ usage. The native host separately proves verified signature provenance, package containment, canonical paths, executable status, workspace exclusion, cancellation and limits. None of those checks can be replaced by JSON Schema. -[`manifest-rejections.json`](fixtures/manifest-rejections.json) separates these +[`manifest-rejections.json`](../../fixtures/toolchains/proposal/manifest-rejections.json) separates these phases. Each case applies RFC 6902 `patch` to its `base`; `context` is **host test input**, never a plugin field. `phase: "schema"` must fail schema validation; `registry` and `hostTrust` cases intentionally remain valid JSON shape and describe diff --git a/contract/v1/proposals/toolchains/protocol.md b/contract/v1/toolchains/proposal/protocol.md similarity index 99% rename from contract/v1/proposals/toolchains/protocol.md rename to contract/v1/toolchains/proposal/protocol.md index dd80653..532cc44 100644 --- a/contract/v1/proposals/toolchains/protocol.md +++ b/contract/v1/toolchains/proposal/protocol.md @@ -2,7 +2,7 @@ **Status: broader proposal; the macOS settings pages query providers read-only, and SDK support is not implemented.** The Core-only validation/resolution subset is specified by -[the active contract](../../toolchains.md). These files remain review vectors, +[the active contract](../README.md). These files remain review vectors, not an advertised native capability or an SDK guide. The existing local-toolchain writer/schema is unchanged. The design follows the [latest #1581 review](https://github.com/1lck/Lithe-IDEA/pull/1581#issuecomment-6541366112). diff --git a/contract/v1/proposals/toolchains/query.schema.json b/contract/v1/toolchains/proposal/query.schema.json similarity index 95% rename from contract/v1/proposals/toolchains/query.schema.json rename to contract/v1/toolchains/proposal/query.schema.json index 4f1fa55..a2c7f60 100644 --- a/contract/v1/proposals/toolchains/query.schema.json +++ b/contract/v1/toolchains/proposal/query.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/proposals/toolchains/query.schema.json", + "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/toolchains/proposal/query.schema.json", "title": "Proposed toolchain query protocol v1 (not implemented)", "description": "Review-only JSON-RPC request, successful response or error response. Semantic validation and authority checks are specified in protocol.md.", "oneOf": [ @@ -334,8 +334,10 @@ "const": "2.0" }, "id": { - "type": "integer", - "minimum": 1 + "oneOf": [ + { "type": "integer", "minimum": 1 }, + { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[^\\u0000-\\u001f\\u007f]*$" } + ] }, "method": { "const": "toolchains/query" @@ -358,8 +360,10 @@ "const": "2.0" }, "id": { - "type": "integer", - "minimum": 1 + "oneOf": [ + { "type": "integer", "minimum": 1 }, + { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[^\\u0000-\\u001f\\u007f]*$" } + ] }, "result": { "$ref": "#/$defs/result" @@ -379,8 +383,10 @@ "const": "2.0" }, "id": { - "type": "integer", - "minimum": 1 + "oneOf": [ + { "type": "integer", "minimum": 1 }, + { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[^\\u0000-\\u001f\\u007f]*$" } + ] }, "error": { "type": "object", diff --git a/contract/v1/proposals/toolchains/resolution.schema.json b/contract/v1/toolchains/proposal/resolution.schema.json similarity index 99% rename from contract/v1/proposals/toolchains/resolution.schema.json rename to contract/v1/toolchains/proposal/resolution.schema.json index 623c67c..2a101c2 100644 --- a/contract/v1/proposals/toolchains/resolution.schema.json +++ b/contract/v1/toolchains/proposal/resolution.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/proposals/toolchains/resolution.schema.json", + "$id": "https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/toolchains/proposal/resolution.schema.json", "title": "Proposed shared toolchain resolution result v1 (not implemented)", "description": "Review-only normalized resolver output. Host-owned snapshots and native filesystem evidence are inputs; providers never produce this object directly.", "type": "object", diff --git a/guides/build-tool-integration.md b/guides/build-tool-integration.md index dec1e88..9ba0ad1 100644 --- a/guides/build-tool-integration.md +++ b/guides/build-tool-integration.md @@ -1,29 +1,36 @@ -# 为语言插件接入构建、运行与测试 +# 为语言插件接入构建、运行与测试 / Adding build, run, and test to a language plugin + +[中文](#中文) · [English](#english) + +## 中文 LSP 服务器负责语言分析,构建工具负责项目导入、编译、依赖解析与产物。 一个实用的语言插件应把这两条链路分别验证,再验证它们在保存、切换项目和停止时能配合工作。 先完成[LSP 插件生命周期教程](lsp-plugin-lifecycle.md),再按本篇增加执行能力。 -本篇的可操作示例以 macOS 声明式插件宿主为范围;Windows 仍有独立扩展实现, -不能凭 Core 校验通过推断已有相同的运行/测试宿主能力。 +本篇的可操作示例以 macOS 宿主为范围。Windows 宿主的能力表不声明 `executionProviders`、 +`buildSystems` 和 `buildProviders`,构建插件在 Windows 上安装前即被拒绝; +不能凭 Core 校验通过推断 Windows 已有相同的运行/测试能力。 -## 1. 先选择当前能发布的接入方式 +### 1. 先选择当前能发布的接入方式 -本文于 2026-10-17 对照 Lithe 主仓库及 Lua 模板核对。API 3 的构建系统接口仍在开发; -下面区分已支持的声明与 Core 中尚未形成产品闭环的接口。发布前还要核实目标宿主版本。 +API 3 的构建系统(`buildSystems`)和构建提供者(`buildProviders`)已经在 Core 和 macOS 宿主中实现, +Windows 不支持。各能力的版本和宿主对照见[构建系统](../reference/domains/build-systems.md)、 +[构建提供者](../reference/domains/build-providers.md)和[版本对照](../reference/capabilities-and-versions.md)。 +发布前仍要核实目标宿主版本支持你声明的能力版本。 | 需求 | 当前接入方式 | 能做什么、边界在哪里 | | --- | --- | --- | | 补全、跳转、诊断 | API 1/2 `languageServers` | 按 LSP 提供能力,不自动声明编译和测试 | -| 运行文件或固定项目命令 | API 1/2 `executionProviders[].run` | 命令与 argv 模板,工作目录为工作区根;不提供通用目标选择或构建模型 | -| 运行整个工程、文件所在范围或指定测试 | API 1/2 `executionProviders[].test` | 测试项目标记、文件发现和命令规划;不是完整语义测试发现与结构化结果协议 | +| 运行文件或固定项目命令 | `executionProviders[].run` | 命令与 argv 模板,工作目录为工作区根;不提供通用目标选择或构建模型 | +| 运行整个工程、文件所在范围或指定测试 | `executionProviders[].test` | 测试项目标记、文件发现和命令规划;不是完整语义测试发现与结构化结果协议 | | 官方 Java 项目的运行/测试/调试 | 官方 Java 插件的 API 2 `hostIntegration: jdt` | 宿主已有 JDT 集成,不能复制为社区通用扩展点 | -| 通用 Build 工具窗口、目标与依赖模型、增量同步 | API 3 `buildSystems`,开发中 | Core 验证/规划接口预览,尚不能作为市场可安装的社区构建插件发布 | +| 通用 Build 工具窗口、工程模型、任务、依赖与同步 | API 3 `buildSystems`;模型需要程序计算时再加 `buildProviders` | Core 和 macOS 已实现;官方 Gradle、Maven、Cargo 插件已按 API 3 发布;Windows 在安装前拒绝构建插件 | 当需求只是 `lua <当前文件>`、`go test ./...` 这类固定命令,可以使用现有执行提供者。 当需求需要 Maven/Gradle/Cargo 的模块、profile、target、classpath、依赖树或任务动态发现, -应使用成熟构建后端和完整模型;不要把一个 `run` 命令包装成已经完成工程集成。 +应使用 API 3 构建系统和成熟构建后端给出的完整模型;不要把一个 `run` 命令包装成已经完成工程集成。 -## 2. 给 Lua 模板加上运行能力 +### 2. 给 Lua 模板加上运行能力 在生命周期教程的最小清单中,替换下面三个顶层字段,其他字段保留: @@ -62,10 +69,10 @@ LSP 服务器负责语言分析,构建工具负责项目导入、编译、依 | `${testName:regex}` | 将用例名的正则特殊字符转义,适合测试筛选参数 | 不要在数组里写 `"lua ${file} && echo done"`、额外 shell 引号或依赖变量替换; -那只是一个普通参数。路径中的空格也不需要作者再加引号。API 1/2 执行提供者没有可自行 +那只是一个普通参数。路径中的空格也不需要作者再加引号。执行提供者没有可自行 设置的 `cwd`、`${projectDir}` 或通用构建设置字段,工作目录固定是工作区根。 -## 3. 用 Go 说明三个测试范围 +### 3. 用 Go 说明三个测试范围 下面是一个 **Go 语言插件**的顶层字段片段。它来自现有 Go 提供者的命令结构; 需要与声明 `go` 语言及其服务器的清单合并,不能直接粘贴到 Lua 插件中使用。 @@ -109,12 +116,12 @@ LSP 服务器负责语言分析,构建工具负责项目导入、编译、依 - 用例范围有选中文件时限定包,没有文件时使用 `./...`;引用 `${file}` 或 `${fileDirectory}` 的用例参数必须用 `when: "fileSelected"` 保护。 - 声明式发现返回工作区和匹配文件,不会仅凭上述字段解析任意框架的测试函数、 - 测试结果树或调试入口。要得到这些能力,需要对应后端及宿主消费支持。 + 测试结果树或调试入口。要得到这些能力,需要 API 3 构建系统和构建提供者。 这里的 `go run <文件>` 也只运行选中文件,不能替代需要多个源码文件的包运行。 -需求超出模板能力时,应如实说明边界,或等待完整构建模型接入。 +需求超出模板能力时,应如实说明边界,或改用第 5 节的构建系统。 -## 4. 将项目事实留给成熟后端 +### 4. 将项目事实留给成熟后端 先检查你选用的语言服务器或项目后端是否已经支持工程导入、构建命令和运行目标。 例如 Java 的模块、classpath、测试/调试目标应使用相应 Java 后端给出的事实, @@ -135,15 +142,24 @@ LSP 服务器负责语言分析,构建工具负责项目导入、编译、依 Gradle daemon 状态、Maven 仓库或 Cargo target 等应由对应工具放到其用户缓存或工作区, 不能写入插件包或应用安装目录。每个后台进程都要有明确的停止与清理策略。 -## 5. API 3 构建系统接口:目前能研究什么 +### 5. 需要完整工程模型时:API 3 构建系统 + +`buildSystems` 用数据描述一个项目级构建工具:识别、可执行文件、选项、模型、任务、运行入口和同步。 +Core 校验声明、检测实例、解码模型、展开命令行;macOS 宿主执行这些计划,并显示 Build 工具窗口。 +模型只能由程序计算时(例如 Gradle Tooling API),在 `buildSystems[].provider` 声明一个 +[构建提供者](../reference/domains/build-providers.md),用 `litheBuild` 协议回答模型、任务、测试和运行准备请求。 +字段和版本见[构建系统](../reference/domains/build-systems.md); +运行原生程序、调试准备和终端见[原生构建工作流](native-build-workflows.md)。 -**当前状态:开发中的 Core 接口,不是可发布的完整宿主功能。** -截至本次核对,已提交基线支持 API 1/2;开发工作区新增 API 3 的清单验证与纯规划模块。 -macOS/Windows 尚无通用 Build 工具窗口对它的完整消费链路,市场可安装版本选择也暂时跳过 -API 3。因此不要把自己的正式插件升到 API 3,再让用户通过安装市场获得尚未实现的能力。 +可以直接参考的已发布插件: -如果你参与 Core/宿主开发,可以阅读开发版本中的 `buildSystems` 契约及 Cargo、Go、 -Gradle、Maven fixtures,并用下列问题检查设计。它们是接口评估项,不能替代产品验收: +| 插件 | 做法 | +| --- | --- | +| [Gradle](https://github.com/Lithe-IDEA/lithe-plugin-gradle) | 纯声明式构建系统,不需要后端 | +| [Maven](https://github.com/Lithe-IDEA/lithe-plugin-maven) | 用 Java SDK 写的构建后端 | +| [Cargo](https://github.com/Lithe-IDEA/lithe-plugin-cargo) | 用 Node.js SDK 写的构建后端和插件设置 | + +设计自己的构建系统时,先用下列问题检查。它们是设计检查项,不能替代产品验收: | 模型部分 | 需要回答的问题 | | --- | --- | @@ -154,17 +170,19 @@ Gradle、Maven fixtures,并用下列问题检查设计。它们是接口评估 | 任务 | 支持哪些范围、是否需要用户参数、是否执行项目代码或访问网络? | | 同步 | 哪些配置变更触发重导入,如何取消旧请求并拒绝过期结果? | | 测试与诊断 | 发现、执行、结果映射由谁提供,路径如何对应用户源码? | -| 生命周期 | 谁启动进程、监督超时、终止任务并清理状态,宿主是否已经消费这些计划? | +| 生命周期 | 谁启动进程、监督超时、终止任务并清理状态? | -Core 的计划接口本身不启动进程、不监视文件、不持久化选择,也不会显示界面。 -即使 fixture 能生成正确命令,宿主仍须实现执行、权限/确认、取消、状态、结果和工具窗口。 +Core 的计划接口本身不启动进程、不监视文件、不持久化选择,也不会显示界面; +执行、权限/确认、取消、状态、结果和工具窗口都由宿主负责。fixture 能生成正确命令, +只说明声明和规划正确,仍要在真实宿主里验收。 不要把内部 dispatcher 命令写成社区插件可以调用的宿主脚本 API。 -`languageServers[].hostIntegration` 的 JDT 集成与开发中的构建 `hostIntegration` 是不同接口, -都不是社区作者任意命名即可扩展的机制。现有 JDT 路径限官方插件; -不要复制官方 ID 或修改 `vendor` 来绕过限制。未来正式开放的能力以目标版本契约和宿主为准。 +`languageServers[].hostIntegration` 的 JDT 集成(API 2)与构建系统 `model.source: "hostIntegration"` +是不同接口,都只限官方插件:语言服务器侧只有 `jdt`,构建侧目前只有 `maven`。 +它们都不是社区作者任意命名即可扩展的机制;不要复制官方 ID 或修改 `vendor` 来绕过限制。 +社区构建插件用 `static`、`command` 或 `provider` 模型。 -## 6. 验证完整工作流 +### 6. 验证完整工作流 打包与清单校验完成后,还应在目标平台、实际宿主中完成: @@ -186,3 +204,228 @@ Core 的计划接口本身不启动进程、不监视文件、不持久化选择 相关入口:[生命周期教程](lsp-plugin-lifecycle.md) · [发布社区插件](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md) · [版本追踪](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/versioning-and-updates.md) · [Lithe 插件契约](../contract/v1/README.md)。 + +--- + +## English + +A language server handles language analysis; a build tool handles project import, compilation, +dependency resolution, and artifacts. A useful language plugin verifies each chain on its own, +then verifies that they work together on save, on project switch, and on stop. +Finish the [LSP plugin lifecycle tutorial](lsp-plugin-lifecycle.md) first, then add execution with this guide. +The runnable examples here target the macOS host. The Windows host's capability table does not +advertise `executionProviders`, `buildSystems`, or `buildProviders`, and Windows rejects build +plugins before install; passing Core validation does not mean Windows has the same run and test support. + +### 1. Pick an integration you can publish today + +API 3 build systems (`buildSystems`) and build providers (`buildProviders`) are implemented in +Core and the macOS host; Windows does not support them. For the versions of each capability +and how the hosts compare, see [build systems](../reference/domains/build-systems.md), +[build providers](../reference/domains/build-providers.md), and the +[version tables](../reference/capabilities-and-versions.md). +Before publishing, still confirm that the target host version supports every capability version you declare. + +| Need | Integration | What it does and where it stops | +| --- | --- | --- | +| Completion, navigation, diagnostics | API 1/2 `languageServers` | Features come from LSP; compile and test are not declared automatically | +| Run a file or a fixed project command | `executionProviders[].run` | A command and argv template; the working directory is the workspace root; no general target selection or build model | +| Run the whole project, a file's scope, or a named test | `executionProviders[].test` | Test-project markers, file discovery, and command planning; not a full semantic test-discovery or structured-results protocol | +| Run, test, and debug official Java projects | The official Java plugin's API 2 `hostIntegration: jdt` | The host already has the JDT integration; it cannot be copied as a general community extension point | +| A general Build tool window, project model, tasks, dependencies, and sync | API 3 `buildSystems`, plus `buildProviders` when a program must compute the model | Implemented in Core and macOS; the official Gradle, Maven, and Cargo plugins are published at API 3; Windows rejects build plugins before install | + +When all you need is a fixed command such as `lua ` or `go test ./...`, an +execution provider is enough. When you need Maven/Gradle/Cargo modules, profiles, targets, +classpaths, dependency trees, or dynamic task discovery, use an API 3 build system with the +full model a mature build backend reports; do not wrap a single `run` command and call it project integration. + +### 2. Add run support to the Lua template + +In the lifecycle tutorial's minimal manifest, replace these three top-level fields and keep the rest: + +```json +{ + "permissions": ["languageServer.launch", "process.run"], + "activationEvents": ["onLanguage:lua", "onRun:lua"], + "executionProviders": [ + { + "languageID": "lua", + "run": { + "executable": { "command": "lua" }, + "arguments": ["${file}", "${userArguments}"] + } + } + ] +} +``` + +This is exactly what the [Lua template](https://github.com/Lithe-IDEA/lithe-plugin-lua) does. +Users must install a Lua interpreter themselves; the bundled LuaLS is not an interpreter. +`process.run` declares the run permission and `onRun:lua` declares activation on run; neither can be omitted. +A test declaration likewise needs `process.test` and `onTest:`. + +`executable` is exactly one of two things: a bare `command` that the host finds on its tool path, +or a project `toolchain` ID the host already supports, such as Go's `project-go`. +A plugin cannot invent a toolchain ID and expect the host to implement it. + +Every argument is a separate argv element and never goes through a shell: + +| Template | Meaning and scope | +| --- | --- | +| `${file}` | Workspace-relative path; the host prefixes `./` when it starts with `-`; valid for run, file tests, and test-case tests | +| `${fileDirectory}` | The file's directory: `.` for the root, `./…` otherwise | +| `${userArguments}` | Run only; must be a whole element on its own and expands to the user's argument array | +| `${testName}` | The selected test-case name, trimmed | +| `${testName:regex}` | The test-case name with regex metacharacters escaped, for test-filter arguments | + +Do not write `"lua ${file} && echo done"`, extra shell quotes, or anything that relies on variable +substitution; that is just one ordinary argument. Paths with spaces need no extra quoting either. +Execution providers have no author-settable `cwd`, no `${projectDir}`, and no general build-settings +field; the working directory is always the workspace root. + +### 3. Three test scopes, using Go + +Below are the top-level fields of a **Go language plugin**. They follow the command structure of +the existing Go provider; merge them into a manifest that declares the `go` language and its server. +They cannot be pasted into the Lua plugin as is. + +```json +{ + "permissions": ["languageServer.launch", "process.run", "process.test"], + "activationEvents": ["onLanguage:go", "onRun:go", "onTest:go"], + "executionProviders": [ + { + "languageID": "go", + "run": { + "executable": { "toolchain": "project-go" }, + "arguments": ["run", "${file}", "${userArguments}"] + }, + "test": { + "frameworkID": "go", + "workspaceLabel": "All Go Tests", + "projectMarkers": ["go.mod", "go.work"], + "testFilePatterns": ["*_test.go"], + "executable": { "toolchain": "project-go" }, + "workspaceArguments": ["test", "./..."], + "fileArguments": ["test", "${fileDirectory}"], + "testCaseArguments": [ + "test", + { "value": "${fileDirectory}", "when": "fileSelected" }, + { "value": "./...", "when": "noFileSelected" }, + "-run", + "^${testName:regex}$" + ] + } + } + ] +} +``` + +- A workspace is a test project only if it has at least one of the `projectMarkers`. + `testFilePatterns` are case-insensitive file-name patterns that support only `*`; they are not regexes or arbitrary globs. +- The workspace scope runs `go test ./...`. The file scope runs the file's package, which is Go's + unit of testing; it does not promise to run only that file. +- The test-case scope narrows to the package when a file is selected and uses `./...` when none is. + Test-case arguments that reference `${file}` or `${fileDirectory}` must be guarded with `when: "fileSelected"`. +- Declarative discovery returns the workspace and matching files. These fields alone do not parse + any framework's test functions, result trees, or debug entries; for those you need an API 3 build system and build provider. + +`go run ` here also runs only the selected file and cannot replace running a package made of several source files. +When a need goes beyond what the templates can express, state the limit honestly, or switch to the build system in section 5. + +### 4. Leave project facts to a mature backend + +First check whether the language server or project backend you chose already supports project import, +build commands, and run targets. For Java, for example, modules, classpaths, and test/debug targets +should come from what the Java backend reports; do not have the plugin read build files again and +assemble its own classpath that will drift. Other languages should reuse their mature toolchains too. + +A complete project workflow must confirm this order: + +```text +save document → wait for the backend to receive and sync the project change → build the target + → check the build result → get the run path/artifact from this build → launch the chosen entry +``` + +A failed build must not go on to run an old artifact; after a project switch the old model must not +launch the new project; after a cancel, a late result must not overwrite the next operation. +These duties need a real backend working with the host; declaring a command does not do them for you. + +When a build tool resolves project dependencies normally, it may access the network and write the +user's tool caches; that is different from downloading a language server when the plugin starts. +Language servers and bundled runtimes must be prepared at packaging time. Project dependencies, +build outputs, Gradle daemon state, the Maven repository, Cargo targets, and so on belong in the +tool's user cache or the workspace, never in the plugin package or the application install directory. +Every background process needs a clear stop and cleanup policy. + +### 5. When you need the full project model: API 3 build systems + +`buildSystems` describes a project-level build tool as data: detection, executables, options, model, +tasks, run entries, and sync. Core validates the declaration, detects instances, decodes the model, +and expands command lines; the macOS host executes those plans and shows the Build tool window. +When only a program can compute the model (the Gradle Tooling API, for example), declare a +[build provider](../reference/domains/build-providers.md) in `buildSystems[].provider`; it answers +model, task, test, and run-preparation requests over the `litheBuild` protocol. +For fields and versions see [build systems](../reference/domains/build-systems.md); +for running native programs, debug preparation, and terminals see [native build workflows](native-build-workflows.md). + +Published plugins you can learn from: + +| Plugin | Approach | +| --- | --- | +| [Gradle](https://github.com/Lithe-IDEA/lithe-plugin-gradle) | A purely declarative build system with no backend | +| [Maven](https://github.com/Lithe-IDEA/lithe-plugin-maven) | A build backend written with the Java SDK | +| [Cargo](https://github.com/Lithe-IDEA/lithe-plugin-cargo) | A build backend and plugin settings written with the Node.js SDK | + +When designing your own build system, check it against these questions first. +They are a design checklist and do not replace product acceptance: + +| Model part | Questions to answer | +| --- | --- | +| Detection | How are root projects, nested modules, and wrappers found, and how are generated directories excluded? | +| Tools and runtimes | How is a wrapper or system command chosen, and how is the required JDK or other runtime found and verified? | +| Options | How do profiles, targets, modes, and platforms become explicit arguments and environment instead of shell concatenation? | +| Project model | Which mature backend supplies modules, targets, dependencies, and run entries, and how are they decoded and validated? | +| Tasks | Which scopes are supported, do they need user arguments, and do they run project code or access the network? | +| Sync | Which configuration changes trigger a re-import, and how are old requests cancelled and stale results rejected? | +| Tests and diagnostics | Who provides discovery, execution, and result mapping, and how do paths map back to the user's sources? | +| Lifecycle | Who starts processes, enforces timeouts, terminates tasks, and cleans up state? | + +Core's planning interface does not start processes, watch files, persist selections, or show any UI; +execution, permissions and confirmation, cancellation, status, results, and the tool window are the host's job. +A fixture that produces the right command only shows that the declaration and plan are correct; +you still have to accept it in a real host. +Do not present internal dispatcher commands as a host scripting API that community plugins can call. + +The JDT integration in `languageServers[].hostIntegration` (API 2) and the build-system +`model.source: "hostIntegration"` are different interfaces, and both are limited to official plugins: +the language-server side has only `jdt`, and the build side currently has only `maven`. +Neither is a mechanism community authors can extend by picking a name; do not copy an official ID +or change `vendor` to get around the limit. Community build plugins use the `static`, `command`, or `provider` models. + +### 6. Verify the complete workflow + +After packaging and manifest validation, also do the following on the target platform in a real host: + +1. Prepare single-file, multi-file, nested-project, and space-in-path projects, and state which ones the plugin supports. +2. Run or test after saving a change and confirm the current file and target are used; break the source + and confirm the error is visible. A full build integration must stop an old artifact from launching. +3. Cover the workspace, file, and named-test scopes, and check the actual command, working directory, output, and exit code. +4. When the interpreter is missing, the toolchain version is wrong, the wrapper is not executable, + or a dependency download fails, the error must point to the cause. +5. Cancel a long task and run again; switch projects and close the workspace, and confirm no old + tasks, locks, or backend processes remain. If the host or tool cannot do this yet, report the limit + instead of only claiming support in the docs. +6. After disabling or uninstalling the language plugin, check that run, test, and LSP for that language + are all in the right state. The plugin owns the languages it declares; do not assume missing + features fall back to a built-in host implementation. +7. Clean up the apps and helper processes the acceptance run started, then verify restart, upgrade, + and uninstall with the [LSP lifecycle checklist](lsp-plugin-lifecycle.md#7-treat-stop-failure-and-uninstall-as-acceptance-criteria). + +Command-planning tests prove the arguments are right, process smoke tests prove the tool can start, +and only acceptance on real projects proves the user workflow holds. Release notes must state the +platforms, host, language server, and build tool versions you verified. + +Related: [lifecycle tutorial](lsp-plugin-lifecycle.md) · [publishing a community plugin](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md) · +[version tracking](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/versioning-and-updates.md) · +[Lithe plugin contract](../contract/v1/README.md). diff --git a/guides/debug-adapter-plugin.md b/guides/debug-adapter-plugin.md index 824f5f6..c5a65ef 100644 --- a/guides/debug-adapter-plugin.md +++ b/guides/debug-adapter-plugin.md @@ -1,4 +1,8 @@ -# 编写调试适配器插件(API 4) +# 编写调试适配器插件(API 4) / Writing a debug adapter plugin (API 4) + +[中文](#中文) · [English](#english) + +## 中文 这篇指南面向想让 Lithe 调试某种语言或运行时的插件作者。调试适配器插件只需要一份 `plugin.json`:声明一个遵循 [DAP(Debug Adapter Protocol)](https://microsoft.github.io/debug-adapter-protocol/)的进程怎么启动,Lithe 负责断点、 @@ -8,10 +12,10 @@ 字段的权威定义在[契约](../contract/v1/README.md#api-level-4-debug-adapters),这里只讲怎么用、为什么这样设计。发布步骤见 [发布社区插件](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)。 -> **宿主支持。** 调试适配器是 API 4,需要所用 Lithe 明确支持 API 4。目前只有 macOS 宿主实现了调试适配器; -> Windows 宿主还没有。不要只凭应用版本号推断支持情况,以[契约](../contract/v1/README.md)和发行说明为准。 +> **宿主支持。** 调试适配器是 API 4,需要所用 Lithe 明确支持 API 4。macOS 宿主支持 API 4 和调试适配器; +> Windows 宿主还没有实现调试适配器。各能力版本以按最新 preview 核对的[版本对照](../reference/capabilities-and-versions.md)和[契约](../contract/v1/README.md)为准,稳定版是否已包含看发行说明。 -## 1. 插件和宿主各自负责什么 +### 1. 插件和宿主各自负责什么 | 对象 | 插件作者负责 | Lithe 负责 | | --- | --- | --- | @@ -23,7 +27,7 @@ 插件不加载进 Lithe 进程。清单是纯数据:没有解析器、没有工作流、没有宿主集成,也不能传 `initializationOptions`,写了会被拒绝。 -## 2. 最小清单 +### 2. 最小清单 ```json { @@ -69,7 +73,7 @@ - `targetBindings` 把调试目标的事实填进适配器的启动参数:`program` ← 目标路径,`cwd` ← 工作目录,`processId` ← 进程 ID。只填 `arguments` 里没写的字段,不会覆盖你显式写出的默认值。目标缺少绑定要读的事实时,Lithe 会拒绝这次启动并说明缺什么。 - `configurationTypes` 声明适配器对应的 `launch.json` `type`,用于导入用户已有的 VS Code 风格配置。 -## 3. 怎么启动适配器 +### 3. 怎么启动适配器 `launch.process` 有三种常见写法: @@ -83,7 +87,7 @@ 插件包和 Lithe 的安装目录运行时是只读的,适配器不能往里面写日志、索引或状态,否则会破坏代码签名和增量更新。macOS 上宿主把适配器的状态目录放在 Application Support 的 `debug-adapters/<适配器 id>` 下。 -## 4. 调试要先编译的语言:`executable` +### 4. 调试要先编译的语言:`executable` DAP 适配器大多直接接受“要运行的程序”,但 C、C++、Swift 这类语言的用户打开的是**源文件**,真正要调试的是编译产物。 `configurations.launch.executable` 就是为这种情况设计的: @@ -112,7 +116,7 @@ DAP 适配器大多直接接受“要运行的程序”,但 C、C++、Swift 设置的 `description` 是教用户怎么产生这个程序的地方,写清楚构建命令(例如 `clang -g main.c -o main`)和适配器本身的安装方式。LLDB 插件的 `program` 设置就是这样写的。设置的写法见契约的 `settings` 一节,`scope: "project"`、`effect: "nextRun"` 适合这类“下次运行生效”的路径。 -## 5. 调试控制台:提示符、补全和 `console` +### 5. 调试控制台:提示符、补全和 `console` Lithe 的调试控制台有输入行和补全弹窗。补全来自适配器的 DAP `completions` 请求;`console` 补充两件事: @@ -121,27 +125,176 @@ Lithe 的调试控制台有输入行和补全弹窗。补全来自适配器的 D 两者至少声明一个。适配器能实时补全时,不要把整个命令集再抄一遍。 -## 6. 本地验证 +### 6. 本地验证 -1. 对照契约示例 `contract/v1/fixtures/debug-adapter.plugin.json`,并用[编辑器 Schema](../README.md#在编辑器里校验-pluginjson) 检查字段。 -2. `contract/v1/fixtures/debug-adapter-rejections.json` 列出每种拒绝的稳定错误码、出错字段和报告它的层(`schema`、`coreManifest`、`corePlan`、`coreInstall`),写测试时可以直接对照。 +1. 对照契约示例 [`contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json`](../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json),并用[编辑器 Schema](../README.md#在编辑器里校验-pluginjson) 检查字段。 +2. [`contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json`](../contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json) 列出每种拒绝的稳定错误码、出错字段和报告它的层(`schema`、`coreManifest`、`corePlan`、`coreInstall`),写测试时可以直接对照。 3. 在支持 API 4 的 Lithe 里安装插件,打开一个目标文件,设置断点并启动调试。适配器被拒绝请求时,Lithe 会显示适配器自己的解释,所以适配器返回的错误文字要写得让用户能看懂。 4. 在设置页确认 `executable` 找到的路径被回显,再把它改成一个不存在的文件,确认得到的是错误而不是悄悄换文件。 -## 7. 安装后是否需要重启 +### 7. 安装后是否需要重启 只贡献调试适配器(以及它声明的语言和设置)的插件,在支持热激活的 Lithe 里安装、更新、回退、卸载、启用、停用后**不需要重启**。是否免重启由宿主按它实际能在运行期登记和撤回的贡献类型决定,不是 `plugin.json` 里的字段,所以插件作者不需要也不能声明。带有其他贡献(语言服务器、构建系统、界面等)的插件仍然在重启后生效。 -## 8. 发布 +### 8. 发布 调试适配器插件需要 API 4,只有支持 API 4 的 Lithe 才能安装。旧版 Lithe 在插件市场里会提示升级,而不是安装。 -- 在 Lithe 稳定版支持 API 4 之前,把你的 GitHub Release 标为**预发布**。Lithe Store 的自动追踪只收正式 Release,预发布需要提交一个手工的索引条目,写法参考 Maven、Gradle 和 LLDB 插件的 0.1.0。 -- 稳定版支持 API 4 后,把 Release 转为正式版,追踪器会自动提交后续版本的索引更新。 +- 插件声明的能力版本,或所用 SDK 要求的协议(例如 0.3.0 起的 SDK 要求宿主提供 `litheLogging` 1),还没进入 Lithe 稳定版时,把 GitHub Release 标为**预发布**。Lithe Store 的自动追踪只收正式 Release,预发布需要提交一个手工的索引条目,写法参考 Maven、Gradle 和 LLDB 插件的 0.1.0。 +- 稳定版支持插件需要的全部能力和协议后,把 Release 转为正式版,追踪器会自动提交后续版本的索引更新。 - 发布后不要替换或删除资产;索引固定了它的 SHA-256。 -## 9. 当前边界 +### 9. 当前边界 - 只有 macOS 宿主实现了调试适配器。 -- 宿主调试入口目前只发起 `launch` 请求;`attach` 可以在清单里声明,但宿主入口还没有完成,不要依赖。 +- 宿主的调试入口对插件适配器只发起 `launch` 请求。`attach` 可以在清单里声明,目前只在构建提供者服务运行的连接调试(`buildProviders` 11)中由宿主选用;用户手动填写主机和端口的远程 attach 还没有宿主入口,不要依赖。 - 适配器声明是纯数据,没有自定义界面、没有宿主集成点;需要调试之外的能力(构建、测试、运行配置)请另用对应的贡献类型。 + +--- + +## English + +This guide is for plugin authors who want Lithe to debug a language or runtime. A debug adapter plugin needs only a `plugin.json`: it declares how to start a process that speaks the +[Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/), and Lithe provides the breakpoint, stepping, variable, call stack, and debug console UI and manages the session. The running example is the official +[LLDB plugin](https://github.com/Lithe-IDEA/lithe-plugin-lldb), which debugs C, C++, Objective-C, Rust, and Swift with the `lldb-dap` that ships with Xcode. + +The normative field definitions are in the [contract](../contract/v1/README.md#api-level-4-debug-adapters); this guide covers only how to use them and why they are designed this way. For publishing steps, see +[Publishing community plugins](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md). + +> **Host support.** Debug adapters are API 4 and need a Lithe that explicitly supports API 4. The macOS host supports API 4 and debug adapters; +> the Windows host does not implement debug adapters yet. For each capability version, go by the [version tables](../reference/capabilities-and-versions.md), which track the latest preview, and the [contract](../contract/v1/README.md); the release notes say whether a stable release includes it. + +### 1. What the plugin and the host each own + +| Item | The plugin author owns | Lithe owns | +| --- | --- | --- | +| Debug adapter | Choosing it, its license, the pinned version, the launch entry | Starting the process, DAP communication, stopping the session | +| Program to debug | Explaining how it is produced (build command, compiler flags) | Showing the path actually found in settings and passing it to the adapter | +| Debugging UI | Nothing to write | Breakpoints, variables, call stack, debug console and completion | +| Permissions | Declaring `debug.launch` | Asking the user to confirm at install time | +| Activation | Declaring `onDebug:` | Starting the adapter only when a debug session needs it | + +Plugins are not loaded into the Lithe process. The manifest is pure data: no resolvers, no workflows, no host integrations, and no `initializationOptions`; writing one is rejected. + +### 2. A minimal manifest + +```json +{ + "schemaVersion": 2, + "id": "com.example.demo-debug", + "displayName": "Demo Debugger", + "version": "0.1.0", + "pluginAPIVersion": 4, + "requires": { "debugAdapters": 1, "languages": 1, "serverProcess": 1 }, + "vendor": { "id": "com.example", "displayName": "Example", "signatureRequirement": "none" }, + "permissions": ["debug.launch"], + "activationEvents": ["onDebug:demo"], + "languages": [ + { "id": "demo", "displayName": "Demo", "fileExtensions": ["demo"] } + ], + "debugAdapters": [ + { + "id": "demo", + "displayName": "Demo", + "requests": ["launch"], + "selectors": [{ "targetKinds": ["file"], "languageIDs": ["demo"] }], + "configurations": { + "launch": { + "arguments": { "stopOnEntry": false }, + "targetBindings": { "program": "target.path", "cwd": "target.workingDirectory" } + } + }, + "launch": { + "process": { "launch": { "kind": "systemCommand", "commands": ["demo-dap"] } }, + "transport": "stdio" + } + } + ] +} +``` + +Key points: + +- `pluginAPIVersion` is `4`, and `requires` must list exactly the capabilities you use (`debugAdapters` is `1`). +- Every adapter needs an `onDebug:` activation event and the `debug.launch` permission. `debug.attach` is needed (and allowed) only when the adapter lists `attach`. +- `selectors` decide on which targets Lithe picks this adapter automatically: `targetKinds` are target kinds and `languageIDs` are languages. Without `selectors`, the adapter can only be chosen explicitly by the user. +- `languages[]` declares the languages the adapter serves. If another plugin already provides the language (for example Rust), your declaration only adds the ability to debug it and merges in the file extensions you declare; the language name, language ID, and language server stay the same, and installing the debugger does not restart a running language server. +- `targetBindings` fills facts about the debug target into the adapter's launch arguments: `program` ← the target path, `cwd` ← the working directory, `processId` ← the process ID. Only fields missing from `arguments` are filled; defaults you wrote explicitly are never overwritten. When the target lacks a fact a binding reads, Lithe refuses the launch and says what is missing. +- `configurationTypes` declares the `launch.json` `type` values the adapter matches, used to import the user's existing VS Code-style configurations. + +### 3. Launching the adapter + +`launch.process` has three common forms: + +| Form | When to use it | +| --- | --- | +| `{"launch": {"kind": "packaged", "path": …}}` | The adapter's license allows distribution and you can provide a build for each platform; the executable installs with the plugin package | +| `{"launch": {"kind": "systemCommand", "commands": ["lldb-dap"]}}` | The adapter is already installed on the user's machine (for example with the Xcode command line tools); Lithe resolves the command name with its own tool discovery (PATH, project toolchains, user overrides), which on macOS also covers the Xcode toolchain | +| `{"launch": {"kind": "runSelectedRuntime", "tool": "python"}}` | The adapter is a Python, Java, or Node.js program started with the runtime the user selected; this is the only use of `serverProcess` 2 | + +`launch.transport` is `stdio` or TCP (`tcpAnnounced`: the adapter prints its port in its output; `tcpPort`: a fixed port). TCP allows only loopback addresses. + +The plugin package and the Lithe install directory are read-only at run time. An adapter must not write logs, indexes, or state there, or it breaks code signing and delta updates. On macOS the host puts the adapter's state directory under `debug-adapters/` in Application Support. + +### 4. Languages that compile before debugging: `executable` + +Most DAP adapters take "the program to run" directly, but in languages such as C, C++, and Swift the user opens a **source file** while the thing to debug is the compiled output. +`configurations.launch.executable` is designed for this case: + +```json +"configurations": { + "launch": { + "targetBindings": { "program": "target.executable", "cwd": "target.workingDirectory" }, + "executable": { + "setting": "program", + "candidates": [ + "${fileDirectory}/${fileBasenameNoExtension}", + "${workspaceFolder}/${fileBasenameNoExtension}", + "${workspaceFolder}/build/${fileBasenameNoExtension}" + ] + } + } +} +``` + +- `setting` is the ID of a `file` setting you declare in `settings[]`. When the user picks a path on the settings page, that path is used. +- When the user has picked nothing, Lithe tries `candidates` in order (at most 8; each must start with `${workspaceFolder}/` or `${fileDirectory}/`, may use `${fileBasenameNoExtension}`, and must not contain `..`) and uses the first runnable file it finds. +- The path it finds is **shown back in that setting**, so the user can see which file will actually be debugged. +- A user-picked path that is not a runnable file is an error; Lithe does **not** quietly fall back to the candidates. +- `executable` and `targetBindings.program: "target.executable"` must be declared together. + +The setting's `description` is where you teach the user how to produce the program: give the build command (for example `clang -g main.c -o main`) and how to install the adapter itself. The LLDB plugin's `program` setting is written this way. For writing settings, see the contract's `settings` section; `scope: "project"` and `effect: "nextRun"` suit a path like this that takes effect on the next run. + +### 5. The debug console: prompt, completion, and `console` + +Lithe's debug console has an input line and a completion popup. Completions come from the adapter's DAP `completions` request; `console` adds two things: + +- `commandPrefix`: 1 to 8 characters with no spaces. Some adapters treat a bare word as an expression (lldb-dap does), so completing the first word mixes in language keywords such as `long`. With a declared prefix (lldb-dap uses a backtick `` ` ``), Lithe adds it in front of the input only when asking the adapter for completions and moves the adapter's answers back, so the user never sees it. +- `commands[]`: when the adapter does not support `completions`, or its live answers miss some commands, list the debugger commands (`name`, with an optional one-line `description`). Names are inserted as written, must be unique, and must not have padding spaces; descriptions are shown as written and are not translated. + +Declare at least one of the two. When the adapter completes live, do not copy its whole command set as well. + +### 6. Local verification + +1. Compare with the contract example [`contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json`](../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json) and check fields with the [editor schema](../README.md#validate-pluginjson-in-your-editor). +2. [`contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json`](../contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json) lists, for each rejection, the stable error code, the offending field, and the layer that reports it (`schema`, `coreManifest`, `corePlan`, `coreInstall`); compare against it directly when writing tests. +3. Install the plugin in a Lithe that supports API 4, open a target file, set a breakpoint, and start debugging. When the adapter rejects a request, Lithe shows the adapter's own explanation, so write the adapter's error text for users to understand. +4. On the settings page, confirm that the path `executable` found is shown, then change it to a file that does not exist and confirm you get an error rather than a quietly swapped file. + +### 7. Whether installing needs a restart + +A plugin that contributes only debug adapters (and the languages and settings it declares) needs **no restart** after install, update, rollback, uninstall, enable, or disable in a Lithe that supports hot activation. The host decides this from the contribution types it can actually register and withdraw at run time; it is not a `plugin.json` field, so plugin authors neither need to nor can declare it. Plugins with other contributions (language servers, build systems, UI, and so on) still take effect after a restart. + +### 8. Publishing + +A debug adapter plugin needs API 4 and installs only on a Lithe that supports API 4. Older Lithe versions show an update prompt in the marketplace instead of installing. + +- Mark your GitHub Release as a **prerelease** while a capability version the plugin declares, or a protocol its SDK requires (for example, SDKs from 0.3.0 require the host to offer `litheLogging` 1), is not yet in a stable Lithe release. Lithe Store's automatic tracking only picks up full Releases, so a prerelease needs a hand-written index entry; follow the 0.1.0 entries of the Maven, Gradle, and LLDB plugins. +- Once a stable release supports every capability and protocol the plugin needs, convert the Release to a full release, and the tracker submits index updates for later versions automatically. +- Do not replace or delete assets after publishing; the index pins their SHA-256. + +### 9. Current limits + +- Only the macOS host implements debug adapters. +- The host's debug entry issues only `launch` requests to plugin adapters. `attach` can be declared in the manifest; today the host chooses it only for connection debugging of build-provider service runs (`buildProviders` 11). Remote attach with a user-entered host and port has no host entry yet, so do not rely on it. +- An adapter declaration is pure data, with no custom UI and no host integration points; for capabilities beyond debugging (build, test, run configurations), use the matching contribution types. diff --git a/guides/lsp-plugin-lifecycle.md b/guides/lsp-plugin-lifecycle.md index 9bfcbfb..0c76552 100644 --- a/guides/lsp-plugin-lifecycle.md +++ b/guides/lsp-plugin-lifecycle.md @@ -1,16 +1,21 @@ -# 从零构建一个完整生命周期的 LSP 插件 +# 从零构建一个完整生命周期的 LSP 插件 / Building an LSP plugin with a complete lifecycle + +[中文](#中文) · [English](#english) + +## 中文 这篇教程面向想给 Lithe 增加语言支持的插件作者。我们用可运行的 [Lua Support 模板](https://github.com/Lithe-IDEA/lithe-plugin-lua)走完选型、清单、语言服务器打包、 安装与激活、日常运行、停止、升级和卸载;构建、运行与测试的接入另见 [构建工具接入指南](build-tool-integration.md)。发布步骤见[发布社区插件](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)。 -本文的安装与验收路径以 **macOS 的社区插件市场(Lithe 0.6.1 起)**为基线。 -API 2 示例需要所用宿主明确支持 API 2;不要只凭应用版本号推断支持情况。 -索引能描述 Windows/Linux 的包,不代表对应宿主已经实现相同的插件安装与运行流程。 -Lua 模板目前只提供 macOS arm64、x64 两个包。 +本文以主仓库最新 `preview` 的 **macOS 宿主**为基线:社区插件可以从插件市场源安装,也可以从磁盘安装。 +Windows 宿主目前只能从磁盘安装声明式插件包,没有插件市场浏览器,不支持运行提供者(`executionProviders`), +也不向启动计划提供 `logDirectory`,所以用到 `${logDir}` 的启动在 Windows 上会被拒绝。 +索引能描述 Windows/Linux 的包,不代表对应宿主已经实现相同的安装与运行流程。 +Lua 模板目前只提供 macOS arm64、x64 两个包。各能力版本见[版本对照](../reference/capabilities-and-versions.md)。 -## 1. 先确定插件与宿主各自负责什么 +### 1. 先确定插件与宿主各自负责什么 LSP(Language Server Protocol,语言服务器协议)让编辑器通过独立进程获得补全、诊断、 跳转等能力。Lithe 的声明式插件用 JSON 描述要启动的程序;宿主读取清单、发现运行时并管理 @@ -18,7 +23,7 @@ LSP(Language Server Protocol,语言服务器协议)让编辑器通过独 ```text 开发与构建:固定上游版本 → 下载并校验 → 组装各平台插件包 → 发布 zip → 索引收录 -用户安装:下载 zip → 校验摘要与清单 → 确认程序及权限 → 安装并记录完整性摘要 +用户安装:下载 zip(或从磁盘选择包)→ 校验摘要与清单 → 确认程序及权限 → 安装并记录完整性摘要 正常运行:启用 → 打开对应语言的文件 → 启动服务器 → 初始化 LSP → 编辑与诊断 结束使用:禁用/关闭工作区 → 宿主停止会话和进程;更新/卸载 → 替换或移除包 ``` @@ -29,14 +34,14 @@ LSP(Language Server Protocol,语言服务器协议)让编辑器通过独 | 语言服务器 | 上游选型、许可、固定版本、启动入口、正确参数 | 校验声明、按需启动、LSP 通信与停止 | | 运行时 | 声明依赖,或在允许的 API 中随包提供 | 按声明发现、检查并启动运行时 | | 运行与测试 | 声明命令、参数和测试范围 | 生成计划、执行与展示结果 | -| 可写数据 | 指定上游使用的日志、索引和状态路径 | 为 API 2 状态模板提供平台可写目录 | +| 可写数据 | 指定上游使用的日志、索引和状态路径 | 为 API 2 启动模板提供平台可写目录 | | 发布与维护 | 各平台产物、版本升级、兼容与回退验证 | 下载校验、安装审查、插件启停 | 先选成熟且许可允许分发的语言服务器。项目导入、依赖解析、符号和模块模型应尽量由该语言的 成熟后端提供。不要为了“接上 LSP”再复制一套工程模型。LSP 支持的具体功能仍取决于服务器 能力与宿主支持;安装服务器不会自动带来构建、测试、DAP 调试或自定义界面。 -## 2. 跑通原版模板,再改成自己的插件 +### 2. 跑通原版模板,再改成自己的插件 本地需要 Git、Node.js 22+、`tar`、`zip` 和 `unzip`。打包工具使用 Node.js, 不代表目标语言服务器需要 Node.js;LuaLS 是平台相关的原生程序。 @@ -45,11 +50,11 @@ LSP(Language Server Protocol,语言服务器协议)让编辑器通过独 git clone https://github.com/Lithe-IDEA/lithe-plugin-lua.git cd lithe-plugin-lua node scripts/package.mjs -unzip -l dist/lua-support-0.3.0-darwin-arm64.zip -shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip +unzip -l dist/lua-support-0.3.1-darwin-arm64.zip +shasum -a 256 dist/lua-support-0.3.1-darwin-arm64.zip ``` -这里的 `0.3.0` 对应本教程使用的模板版本;上游更新后,产物名以模板清单和脚本输出为准。 +这里的 `0.3.1` 是本文核对时的模板版本;上游更新后,产物名以模板清单和脚本输出为准。 脚本会重新生成 `dist/`,下载两个 macOS 架构的 LuaLS、核对固定 SHA-256,并输出每个 zip 的 SHA-256。Linux CI 可以组装 macOS 包,但这只证明打包成功,不能替代在目标 Mac 上运行。 @@ -64,7 +69,7 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip ├── scripts/package.mjs # 下载、校验、解包、组装 zip └── .github/workflows/ # PR 校验与标签发布 -产物 lua-support-0.3.0-darwin-arm64.zip +产物 lua-support-0.3.1-darwin-arm64.zip └── io.github.lithe-idea.lua-support/ ├── plugin.json ├── bin/lithe-lua-language-server @@ -73,6 +78,9 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip └── … # 上游依赖、脚本和许可文件 ``` +模板清单是 API 2:语言服务器之外还声明了运行 `lua <当前文件>` 的执行提供者(`process.run` 权限和 +`onRun:lua` 事件),启动器的第一个参数是宿主传入的 `${logDir}`。 + 派生自己的仓库后,同步修改以下位置: 1. 包目录名、`plugin.json` 的 `id`、显示名、版本与 `vendor`,使用你自己的稳定反向域名。 @@ -82,11 +90,11 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip 4. 启动器中的包内入口、参数和缓存命名空间。不能继续写入 Lua 模板的缓存目录。 5. CI 中读取清单的目录名、Release 说明、README、许可证与第三方声明。 -不要只换包目录名:模板 CI 中也有原插件 ID,启动器中也有状态路径。 +不要只换包目录名:模板 CI 中也有原插件 ID,启动器中也有缓存路径。 -## 3. 写一个最小、完整的 LSP 清单 +### 3. 写一个最小、完整的 LSP 清单 -下面是 API 1 的纯 LSP 示例。它沿用模板的目录布局,先只接入 LuaLS;如要运行 Lua 程序, +下面是一个纯 LSP 示例。它沿用模板的目录布局和启动器,先只接入 LuaLS;如要运行 Lua 程序, 再按[构建工具指南](build-tool-integration.md)添加运行提供者。示例中的作者和 ID 要换成自己的。 ```json @@ -95,7 +103,7 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip "id": "io.github.alice.lua-support", "displayName": "Lua Support", "version": "0.1.0", - "pluginAPIVersion": 1, + "pluginAPIVersion": 2, "vendor": { "id": "io.github.alice", "displayName": "Alice", @@ -119,16 +127,17 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip "kind": "packaged", "path": "bin/lithe-lua-language-server" }, - "arguments": [], + "arguments": ["${logDir}"], "validationArguments": [] } ] } ``` -三个版本不要混淆:`schemaVersion: 2` 是清单格式,`pluginAPIVersion: 1` 是宿主需要支持的 +三个版本不要混淆:`schemaVersion: 2` 是清单格式,`pluginAPIVersion: 2` 是宿主需要支持的 插件 API 等级,`version: "0.1.0"` 是你的插件版本。上游 LuaLS 的版本另记在构建输入中。 -只声明实际需要的最低 API;API 1 的服务器参数是字面量,不展开 API 2 模板。 +这里用 API 2,是因为参数里的 `${logDir}` 是 API 2 的启动模板;只声明实际需要的最低 API。 +API 1 的服务器参数是字面量,宿主原样传给进程、不展开模板,这时启动器要自己选择日志和缓存目录。 `languages[].id`、`languageServers[].languageID` 和 `onLanguage:` 必须对应。 声明服务器需要 `languageServer.launch` 权限及激活事件;每个语言最多一个服务器。 @@ -137,7 +146,7 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip 启用只表示允许激活,打开项目中相应语言的文档才触发服务器启动。 文件后缀不等于 LSP 的文档标识,必要时单独设置 `languageIdentifier`。 -### 选择启动方式 +#### 选择启动方式 | 方式 | 适用场景 | 作者要做的事 | | --- | --- | --- | @@ -165,7 +174,7 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip 启动时下载服务器的变通方案。运行时依赖与主入口是两回事:例如随包服务器调用 Go 时, `runtimeDependencies` 让宿主发现 Go 并传递环境;API 1 支持 `node`、`go`,API 2 增加 `java`。 -## 4. 在构建阶段准备完整语言服务器 +### 4. 在构建阶段准备完整语言服务器 沿用模板的处理顺序: @@ -181,11 +190,11 @@ shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip 更换语言服务器时不能只替换下载地址,必须同步检查解压布局、入口、依赖、探测方式及许可。 模板中的归档处理适配 LuaLS 的 tar.gz;其他格式需要在自己的打包脚本中明确实现。 -社区插件使用自己的 ID 和 `vendor.signatureRequirement: "none"`,通过第三方市场源的 -摘要校验、用户确认和加载时内容复查安装。不要伪造 Lithe 发布者签名,也不要使用保留的 -`dev.lithe.*` 命名空间。官方包的发布者签名是另一条信任路径,签名必须在完整组装后完成。 +社区插件使用自己的 ID 和 `vendor.signatureRequirement: "none"`,通过市场源的 +摘要校验或从磁盘安装时的私有快照、用户确认和加载时内容复查安装。不要伪造 Lithe 发布者签名, +也不要使用保留的 `dev.lithe.*` 命名空间。官方包的发布者签名是另一条信任路径,签名必须在完整组装后完成。 -## 5. 将可写状态与安装文件分开 +### 5. 将可写状态与安装文件分开 Lithe 会在加载时复查社区包内容。服务器如果将日志、索引、锁或生成文件写回插件目录, 下次加载就可能失败;写入应用安装包还会破坏应用签名与增量更新基线。 @@ -193,11 +202,14 @@ Lithe 会在加载时复查社区包内容。服务器如果将日志、索引 | 数据 | 放在哪里 | 需要注意的生命周期 | | --- | --- | --- | | 服务器、运行时、默认配置、许可证 | 只读插件包 | 随插件版本安装和替换 | -| 日志、可重建缓存 | 平台用户缓存目录 | 设置容量/轮转策略,记录清理方法 | -| 项目索引、锁、项目相关状态 | 按工作区隔离的可写目录 | 不同项目、工作树不能共用同一份可变索引 | +| 上游原生日志 | API 2 的 `${logDir}`(宿主提供的本次会话日志目录) | 由宿主的日志存储管理,不写结构化事件,见[插件日志](plugin-logging.md) | +| 可重建缓存 | `${stateDir}`,或平台用户缓存目录 | 设置容量策略,记录清理方法 | +| 项目索引、锁、项目相关状态 | `${workspaceStateDir}` 等按工作区隔离的可写目录 | 不同项目、工作树不能共用同一份可变索引 | | 用户源码与构建产物 | 用户工作区或构建工具指定目录 | 卸载插件不应误删用户工程 | -API 1 的 Lua 模板通过启动器设置缓存目录,并使用 `exec` 将启动器替换为真实服务器。 +Lua 模板的启动器把宿主传入的 `${logDir}` 交给 LuaLS 的 `--logpath`,把生成的库定义放在 +`~/Library/Caches/io.github.lithe-idea.lua-support/meta`(设置了 `XDG_CACHE_HOME` 时放在它下面), +再用 `exec` 将启动器替换为真实服务器。 不要把服务器放到后台、`nohup` 或自行创建常驻守护进程,否则宿主难以完整停止它。 标准输出只承载 LSP 的 JSON-RPC 帧,启动器提示与日志应写标准错误或日志文件。 @@ -205,10 +217,10 @@ API 1 的 Lua 模板通过启动器设置缓存目录,并使用 `exec` 将启 需要说明残留内容及安全删除方法。若自己的服务器维护工作区索引,还要增加稳定的工作区隔离, 不要直接复用模板的单一缓存目录保存所有项目索引。 -### API 2:让宿主提供状态目录 +#### API 2:让宿主提供状态目录 -目标宿主支持 API 2 时,可以将上面的 Lua 示例升到 `pluginAPIVersion: 2`, -把 `languageServers` 替换为以下值,直接运行包内 LuaLS 并传入宿主状态目录: +也可以去掉启动器,把上面示例的 `languageServers` 替换为以下值,直接运行包内 LuaLS +并传入宿主管理的目录: ```json [ @@ -220,7 +232,7 @@ API 1 的 Lua 模板通过启动器设置缓存目录,并使用 `exec` 将启 "path": "language-server/bin/lua-language-server" }, "arguments": [ - "--logpath=${stateDir}", + "--logpath=${logDir}", "--metapath=${workspaceStateDir}" ], "validationArguments": [] @@ -228,9 +240,11 @@ API 1 的 Lua 模板通过启动器设置缓存目录,并使用 `exec` 将启 ] ``` -`${stateDir}` 是宿主为该插件服务器管理的可写状态目录,`${workspaceStateDir}` 进一步 +`${logDir}` 是宿主显式提供的本次会话插件日志目录;宿主不提供 `logDirectory` 时 +(目前的 Windows 宿主就是这样),用到它的启动以 `plugin.launch.invalidRequest` 被拒绝。 +`${stateDir}` 是宿主为该插件服务器管理的可写状态目录,卸载插件时删除;`${workspaceStateDir}` 进一步 按工作区隔离。`${pluginDir}` 是只读包根路径;`${arch}` 的值是 `arm64` 或 `x86_64`, -与 Release 资产里的 `x64` 拼写不同。状态模板不能放进 `validationArguments`。 +与 Release 资产里的 `x64` 拼写不同。状态和日志模板不能放进 `validationArguments`。 不要将这些模板写进 API 1 的参数,宿主会按原样传给进程。 如果服务器需要 Node.js/Java,API 2 的 `runtime` 可免去启动器。以下是服务器声明中的 @@ -253,7 +267,7 @@ API 2 也支持 `initializationOptions` 和声明式 `settings`。只传上游 `${setting:}` 只能引用已声明设置,值必须符合类型和范围。设置是插件级、跨工作区共享, 修改后宿主重启相应服务器。详细格式见[正式契约](../contract/v1/README.md#plugin-settings-api-2)。 -## 6. 本地验证、安装与激活 +### 6. 本地验证、安装与激活 先运行模板现有 CI 中的两层检查:`node scripts/package.mjs` 组装产物,随后用 Lithe Store 的 `readPackageManifests` 和 `checkManifest` 检查包路径、ID、版本和社区信任声明。 @@ -279,17 +293,24 @@ JS 派生模板后,把这里读取 `plugin.json` 的目录名也换成自己的 ID。 如果错误来自跨引用、模板上下文或 API 等级,还需对照契约并通过目标宿主的安装校验。 -要在正式版 Lithe 中验收社区包,使用[发布流程](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)在自己的公开 GitHub 仓库 -发布 zip,再准备自己仓库根目录的 `lithe-plugin-index.json`。索引格式与 Store 相同, -下载 URL 指向该仓库的 Release 资产,摘要固定最终 zip;不必先申请进入公共 Store。 -在 Lithe 的“管理插件仓库…”中加入你的仓库 URL,即可通过自己的源安装测试。 -不要把正式版的“从磁盘安装”当作绕过社区来源与确认流程的开发入口;`none` 社区包走市场源。 -开发宿主的本地包例外属于单独信任路径,不能替代社区安装验收。 +macOS 宿主上有两条安装 `none` 社区包的路径,两条都会显示安装审查,由用户确认: + +- **从磁盘安装**:在插件管理中选择“从磁盘安装插件…”,选中 `dist/` 里的 zip 或解压后的包目录。 + 宿主校验清单后保存一份私有快照,并把它标为发布者与源码仓库都未经验证的社区插件。 + 它适合开发中反复验证,但不检查索引条目和 Release 资产的摘要。 +- **自己的插件源**:按[发布流程](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)在自己的公开 GitHub 仓库 + 发布 zip,再准备仓库根目录的 `lithe-plugin-index.json`。索引格式与 Store 相同, + 下载 URL 指向该仓库的 Release 资产,摘要固定最终 zip;不必先申请进入公共 Store。 + 在 Lithe 的“管理插件仓库…”中加入你的仓库 URL,即可通过自己的源安装,走的是用户真正会用的路径。 + +发布前至少用自己的插件源完整安装一次。开发构建会把未签名、但要求发布者签名的包按本地开发包处理, +这属于单独的信任路径,不能替代社区安装验收。 安装前先禁用与你的插件声明同一语言的其他插件,避免提供者冲突。然后: -1. 在插件市场选择你的源和版本,确认显示的程序、权限、来源都符合预期。 - 安装或更新后按界面提示**重启 Lithe**,让新版本进入本次启动的插件图。 +1. 在插件市场选择你的源和版本(或从磁盘选择包),确认显示的程序、权限、来源都符合预期。 + 语言服务器插件安装或更新后,按界面提示**重启 Lithe**,让新版本进入本次启动的插件图; + 宿主只对仅含调试适配器或语法的插件免重启(见第 7 节)。 2. 打开一个小项目中的 `.lua` 文件。应当出现补全、悬停、跳转和诊断; 不要只检查进程“已运行”。 3. 保存修改,确认诊断更新;关闭并重开文件,确认没有重复服务器和重复诊断。 @@ -316,12 +337,12 @@ print(greet("Lithe")) 悬停查看 `greet` 的签名,跳到定义,再将调用参数改成数字,观察类型诊断。 具体提示随 LuaLS 版本和项目配置变化,验收时应记录所用版本和配置。 -## 7. 把停止、失败和卸载也当作功能验收 +### 7. 把停止、失败和卸载也当作功能验收 声明式插件没有让作者注册任意 `activate()` / `deactivate()` 回调的机制。 宿主管理会话、进程和停用;作者负责让服务器保持前台运行、遵守 LSP 生命周期, -并把所有子进程和状态设计成可以停止、重启和隔离。当前 Core 的强停路径面向直接子进程, -不能假定会自动终止服务器任意派生的整棵进程树。下面是发布前需要执行的验收, +并把所有子进程和状态设计成可以停止、重启和隔离。在 macOS 上,Core 的强停只针对直接子进程, +不能假定会自动终止服务器任意派生的整棵进程树;Windows 宿主按进程树结束。下面是发布前需要执行的验收, 不是声明后即可免测的保证。 | 操作或故障 | 要检查的结果 | @@ -336,19 +357,20 @@ print(greet("Lithe")) | 卸载与重新安装 | 卸载先停用,重启后删除包和宿主管理的插件状态;自定义缓存有明确说明 | | 篡改安装包内一个文件后重新加载 | 内容复查拒绝加载,不能靠服务器自行修改清单“修复” | -已在本次启动中注册的声明式插件可即时启用/禁用;本次启动后新装的插件要等重启。 -安装、更新、回滚及卸载的文件/版本转换按界面提示在下次启动完成, +已在本次启动中注册的插件可即时启用/禁用。安装、更新、回滚、恢复和卸载是否免重启由宿主判断: +只有贡献全部是调试适配器或语法、且没有后端的插件,才在运行中立即生效;语言服务器属于启动时构建的 +模块图,所以 LSP 插件的这些文件/版本转换都按界面提示在下次启动完成, 卸载请求会先停用已注册插件。验收需要覆盖重启前和重启后的状态。 验收完成后关闭测试启动的应用、服务器和子进程。不要把关闭编辑器标签等同于卸载服务器, 也不要假定崩溃后一定会自动重试或所有平台都有相同恢复策略。发现生命周期问题时, 在插件仓库记录日志、宿主版本、包版本、架构和复现步骤;宿主进程管理问题再提交到 Lithe。 -## 8. 发布、更新与回退演练 +### 8. 发布、更新与回退演练 按[发布社区插件](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md)递增插件版本、发布正式 Release 并首次提交收录。 模板的 Release 工作流会校验 `v<版本>` 与清单一致;原版示例是 -`node scripts/package.mjs --expect-tag v0.3.0`,派生后使用自己的版本。 +`node scripts/package.mjs --expect-tag v0.3.1`,派生后使用自己的版本。 每次更新服务器或运行时,都要更新固定输入和许可,重建对应平台包,走完交互及生命周期验收。 发布后的 zip 不可替换,同一版本不能重用;修复要发布新版本。新增可运行程序会触发新的 @@ -358,13 +380,13 @@ print(greet("Lithe")) - 保留旧版本资产和索引记录,确保旧宿主仍有兼容版本可装。 - 新旧服务器使用不兼容索引格式时,隔离状态或提供安全重建;不能让旧版读损坏状态。 -- macOS 的回滚功能使用安装记录中保留的上一版本,重新验证该包并在重启后切换, - 不提供任意历史版本选择,也不会恢复服务器索引或用户数据。应实际演练该路径。 +- macOS 的回滚功能使用安装记录中保留的上一版本,重新验证该包;LSP 插件在重启后切换。 + 它不提供任意历史版本选择,也不会恢复服务器索引或用户数据。应实际演练该路径。 - 当前版本有问题时,发布更高版本的修复包,必要时在新包里恢复上一个稳定服务器; 同时申请撤回问题版本,不能只依赖用户机器恰好保留上一版本。 - 卸载重装也要验证。源码、用户构建产物与自定义缓存的处理必须说明,避免误删用户数据。 -## 9. 按失败阶段排查 +### 9. 按失败阶段排查 | 现象 | 优先检查 | | --- | --- | @@ -373,6 +395,7 @@ print(greet("Lithe")) | `plugin.manifest.invalid` | 未知字段、schema/API 混淆、ID/引用、API 2 字段误放到 API 1 | | `plugin.permission.undeclared` / `plugin.activation.invalid` | 提供者、权限与事件是否一一对应 | | `plugin.launch.missing` | 相对路径、大小写、打包完整性与可执行权限 | +| `plugin.launch.invalidRequest` | 用到 `${logDir}` 或 `${workspaceStateDir}`,但宿主没有提供对应目录 | | `plugin.runtime.missing` | 运行时发现、最低版本、Dock 启动环境 | | 有进程但无法补全 | stdio 是否被日志污染、LSP 握手、文档语言标识与上游配置 | | 用过一次后拒绝加载 | 服务器是否向包内写了日志、索引、锁或生成文件 | @@ -382,4 +405,413 @@ print(greet("Lithe")) 字段全集、API 等级和稳定错误码以 Lithe 的 [插件契约](../contract/v1/README.md)及 [清单 Schema](../contract/v1/plugin-manifest.schema.json)为准。 -契约副本固定在 Lithe 主仓库的某个提交(见 [`contract.lock.json`](../contract.lock.json)),可能包含还没发布的能力,发布前以目标宿主实际支持为准。 +契约副本固定在 Lithe 主仓库的某个提交(见 [`contract.lock.json`](../contract.lock.json)),本文的宿主现状按主仓库最新 `preview` 核对; +两者都可能包含还没进入 Lithe 稳定版的能力,发布前以目标宿主实际支持为准。 + +--- + +## English + +This tutorial is for plugin authors who want to add language support to Lithe. Using the runnable +[Lua Support template](https://github.com/Lithe-IDEA/lithe-plugin-lua), it walks through choosing a server, the manifest, packaging the language server, +installation and activation, daily operation, shutdown, upgrade, and uninstall. For build, run, and test, see the +[build tool integration guide](build-tool-integration.md). For publishing, see [Publishing a community plugin](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md). + +The baseline is the **macOS host** on the latest `preview` of the Lithe repository: a community plugin installs from a marketplace source or from disk. +The Windows host currently installs declarative packages from disk only, has no marketplace browser, does not support execution providers (`executionProviders`), +and supplies no `logDirectory` to launch plans, so a launch that uses `${logDir}` is rejected on Windows. +That an index can describe Windows or Linux packages does not mean the corresponding host implements the same install and run flow. +The Lua template ships only macOS arm64 and x64 packages. For capability versions, see the [version tables](../reference/capabilities-and-versions.md). + +### 1. Decide what the plugin owns and what the host owns + +LSP (Language Server Protocol) lets an editor get completion, diagnostics, navigation, and more +from a separate process. A Lithe declarative plugin describes the program to start in JSON; the host reads the manifest, discovers runtimes, and manages +the process and the LSP session. You do not write a Swift or JavaScript entry point inside the host. + +```text +Develop and build: pin upstream version → download and verify → assemble per-platform packages → publish zips → index entry +User install: download zip (or pick a package on disk) → verify checksum and manifest → confirm programs and permissions → install and record integrity digests +Normal operation: enable → open a file in the language → start the server → initialize LSP → edit and diagnose +Ending use: disable / close workspace → host stops sessions and processes; update / uninstall → replace or remove the package +``` + +| Concern | The plugin author owns | Lithe owns | +| --- | --- | --- | +| Language detection | File extensions, full file names, project markers | Matching files and choosing a provider | +| Language server | Choosing upstream, license, pinned version, entry point, correct arguments | Validating declarations, starting on demand, LSP communication and shutdown | +| Runtime | Declaring dependencies, or shipping one where the API allows | Discovering, checking, and starting runtimes as declared | +| Run and test | Declaring commands, arguments, and test scopes | Building plans, executing, and showing results | +| Writable data | The log, index, and state paths the upstream uses | Platform-writable directories for API 2 launch templates | +| Release and maintenance | Per-platform artifacts, version upgrades, compatibility and rollback checks | Download verification, install review, enabling and stopping plugins | + +Pick a mature language server whose license allows redistribution. Project import, dependency resolution, and symbol and module models should come from +the language's mature backend wherever possible. Do not duplicate a project model just to "hook up LSP". What LSP features you get still depends on the server's +capabilities and on host support; installing a server does not bring build, test, DAP debugging, or custom UI. + +### 2. Run the original template, then make it your own plugin + +You need Git, Node.js 22+, `tar`, `zip`, and `unzip`. The packaging tool uses Node.js; +that does not mean the target language server needs Node.js. LuaLS is a platform-specific native program. + +```bash +git clone https://github.com/Lithe-IDEA/lithe-plugin-lua.git +cd lithe-plugin-lua +node scripts/package.mjs +unzip -l dist/lua-support-0.3.1-darwin-arm64.zip +shasum -a 256 dist/lua-support-0.3.1-darwin-arm64.zip +``` + +`0.3.1` is the template version when this page was checked; after upstream updates, take the artifact names from the template manifest and the script output. +The script regenerates `dist/`, downloads LuaLS for both macOS architectures, checks the pinned SHA-256 values, and prints the SHA-256 of each zip. +Linux CI can assemble the macOS packages, but that only proves packaging works; it does not replace running on a target Mac. + +How the template source maps to the artifacts: + +```text +Source repository +├── io.github.lithe-idea.lua-support/ +│ ├── plugin.json +│ └── bin/lithe-lua-language-server # launcher, keeps its executable bit +├── language-server.json # build input: version, per-platform URLs and SHA-256 +├── scripts/package.mjs # download, verify, unpack, assemble zips +└── .github/workflows/ # PR checks and tag releases + +Artifact lua-support-0.3.1-darwin-arm64.zip +└── io.github.lithe-idea.lua-support/ + ├── plugin.json + ├── bin/lithe-lua-language-server + └── language-server/ # the complete upstream resources, not just the executable + ├── bin/lua-language-server + └── … # upstream dependencies, scripts, and license files +``` + +The template manifest is API 2: besides the language server it declares an execution provider that runs `lua ` (the `process.run` permission and +the `onRun:lua` event), and the launcher's first argument is the `${logDir}` the host passes in. + +After forking, change all of these together: + +1. The package directory name and, in `plugin.json`, the `id`, display name, version, and `vendor`, using your own stable reverse domain. +2. `PACKAGE_DIR` and `ASSET_PREFIX` in `scripts/package.mjs`, and the resource checks for your server. +3. The version, per-platform URLs, digests, and license information in `language-server.json`. It is the template script's own input, + not a unified download API the host interprets; if you change its structure, change the packaging script too. +4. The in-package entry, arguments, and cache namespace in the launcher. Do not keep writing into the Lua template's cache directory. +5. The directory name CI reads the manifest from, the Release notes, the README, the license, and third-party notices. + +Do not change only the package directory name: the template's CI also contains the original plugin ID, and the launcher contains its cache path. + +### 3. Write a minimal, complete LSP manifest + +Below is an LSP-only example. It keeps the template's layout and launcher and wires up only LuaLS; to run Lua programs, +add an execution provider following the [build tool guide](build-tool-integration.md). Replace the author and ID with your own. + +```json +{ + "schemaVersion": 2, + "id": "io.github.alice.lua-support", + "displayName": "Lua Support", + "version": "0.1.0", + "pluginAPIVersion": 2, + "vendor": { + "id": "io.github.alice", + "displayName": "Alice", + "signatureRequirement": "none" + }, + "permissions": ["languageServer.launch"], + "activationEvents": ["onLanguage:lua"], + "languages": [ + { + "id": "lua", + "displayName": "Lua", + "fileExtensions": ["lua"], + "projectFileNames": [".luarc.json", ".luarc.jsonc"] + } + ], + "languageServers": [ + { + "languageID": "lua", + "languageIdentifier": "lua", + "launch": { + "kind": "packaged", + "path": "bin/lithe-lua-language-server" + }, + "arguments": ["${logDir}"], + "validationArguments": [] + } + ] +} +``` + +Keep the three versions apart: `schemaVersion: 2` is the manifest format, `pluginAPIVersion: 2` is the plugin API level the host must support, +and `version: "0.1.0"` is your plugin's version. The upstream LuaLS version lives in the build input. +The example uses API 2 because `${logDir}` in its arguments is an API 2 launch template; declare only the lowest API you actually need. +In API 1, server arguments are literals that the host passes unchanged without expanding templates, so the launcher must pick its own log and cache directories. + +`languages[].id`, `languageServers[].languageID`, and `onLanguage:` must match. +A server needs the `languageServer.launch` permission and an activation event; each language has at most one server. +The event set must match the actual providers exactly; do not add unused events. Permissions declare capabilities; +they are not an operating-system sandbox that limits an external program's disk or network access. +Enabling only allows activation; opening a document of the language in a project starts the server. +A file extension is not the LSP document identifier; set `languageIdentifier` separately when needed. + +#### Choosing a launch kind + +| Kind | When to use it | What the author does | +| --- | --- | --- | +| API 1 `packaged` | A native server or launcher shipped in the package | Declare the relative path, package every resource, and keep the executable bit | +| API 1 `systemCommand` | A server the user is explicitly expected to install | Declare bare command candidates and a correct probe, and document the install requirement | +| API 2 `runtime` | A packaged JS file or JAR started by Node.js or Java | Declare the packaged entry and the discovered runtime's minimum version, or declare a packaged runtime | + +For example, this is an alternative server declaration in `languageServers[]` that expects the user to have installed `gopls`; +the full manifest must also declare the `go` language and the matching permission and event: + +```json +{ + "languageID": "go", + "launch": { "kind": "systemCommand", "commands": ["gopls"] }, + "arguments": ["serve"], + "validationArguments": ["version"], + "runtimeDependencies": [{ "tool": "go" }] +} +``` + +Probe arguments must exit with code 0 on their own; never use the long-running LSP start arguments. If the upstream has no suitable probe, +use an empty array as the Lua template does, and let the LSP handshake decide. Passing the probe still does not mean completion works. + +When shipping a server, prefer a pinned, reproducible one. `systemCommand` does not install tools for the user, and it is not a workaround for +downloading a server at launch. Runtime dependencies are separate from the main entry: for example, when a packaged server calls Go, +`runtimeDependencies` makes the host discover Go and pass the environment. API 1 supports `node` and `go`; API 2 adds `java`. + +### 4. Prepare the complete language server at build time + +Follow the template's order: + +1. Pin the upstream release version and the HTTPS archive URL for each platform and architecture, and check the license and redistribution terms. +2. Commit the archive SHA-256 values. Confirm them from the upstream's checksum data or from a reviewed download; + never download at build time and treat a freshly computed digest as the expected value. +3. Verify the digest after downloading, then unpack into a temporary assembly directory. Re-verify on a cache hit too; fail immediately on a mismatch. +4. Copy every file the server needs: binaries, scripts, resources, dynamic libraries, licenses; check that the declared entry exists. +5. Check that the archive has no symbolic links, paths escaping the root, or absolute paths from the build machine, and keep the executable bit on the launcher and binaries. +6. Produce one zip per platform, then compute the SHA-256 of each final zip for the marketplace index to pin. + +The upstream archive digest and the final zip digest serve different purposes: the first guarantees the build input, the second that users get the same released package. +Switching language servers is more than swapping the download URL; recheck the unpacked layout, entry, dependencies, probe, and license. +The template's archive handling fits LuaLS's tar.gz; other formats need explicit handling in your own packaging script. + +A community plugin uses its own ID and `vendor.signatureRequirement: "none"`, and installs through a marketplace source's +checksum or, from disk, a private snapshot, plus user confirmation and a content recheck at load. Do not forge a Lithe publisher signature, +and do not use the reserved `dev.lithe.*` namespace. Official packages' publisher signatures are a separate trust path, and signing must happen after full assembly. + +### 5. Keep writable state apart from installed files + +Lithe rechecks a community package's contents at load. If the server writes logs, indexes, locks, or generated files back into the plugin directory, +the next load may fail; writing into the application bundle also breaks the app signature and the delta-update baseline. + +| Data | Where it goes | Lifecycle to consider | +| --- | --- | --- | +| Server, runtime, default configuration, licenses | The read-only plugin package | Installed and replaced with the plugin version | +| Native upstream logs | API 2 `${logDir}` (the current-session log directory the host supplies) | Managed by the host's log storage; not structured events, see [plugin logging](plugin-logging.md) | +| Rebuildable caches | `${stateDir}`, or the platform user cache directory | Set a size policy and document how to clean up | +| Project indexes, locks, project state | A per-workspace writable directory such as `${workspaceStateDir}` | Different projects and worktrees must not share one mutable index | +| User source and build output | The user's workspace or the directory the build tool chooses | Uninstalling the plugin must not delete user projects | + +The Lua template's launcher passes the host's `${logDir}` to LuaLS's `--logpath`, keeps generated library definitions in +`~/Library/Caches/io.github.lithe-idea.lua-support/meta` (under `XDG_CACHE_HOME` when set), +and then uses `exec` to replace itself with the real server. +Do not background the server, use `nohup`, or create your own daemon, or the host cannot stop it completely. +Standard output carries only LSP JSON-RPC frames; launcher messages and logs go to standard error or a log file. + +The template's custom cache path is the launcher's own convention, and **you cannot assume the host cleans it up on uninstall**. +Document what remains and how to delete it safely. If your server keeps workspace indexes, add stable per-workspace isolation; +do not reuse the template's single cache directory for every project's index. + +#### API 2: let the host provide state directories + +You can also drop the launcher: replace `languageServers` in the example above with the following to run the packaged LuaLS directly +with host-managed directories: + +```json +[ + { + "languageID": "lua", + "languageIdentifier": "lua", + "launch": { + "kind": "packaged", + "path": "language-server/bin/lua-language-server" + }, + "arguments": [ + "--logpath=${logDir}", + "--metapath=${workspaceStateDir}" + ], + "validationArguments": [] + } +] +``` + +`${logDir}` is the current-session plugin log directory the host supplies explicitly; when the host supplies no `logDirectory` +(as the Windows host does today), a launch that uses it is rejected with `plugin.launch.invalidRequest`. +`${stateDir}` is a writable state directory the host keeps for this plugin's server and removes on uninstall; `${workspaceStateDir}` is further +isolated per workspace. `${pluginDir}` is the read-only package root; `${arch}` is `arm64` or `x86_64`, +spelled differently from the `x64` in Release asset names. State and log templates are not allowed in `validationArguments`. +Do not put these templates in API 1 arguments; the host passes them to the process unchanged. + +If the server needs Node.js or Java, an API 2 `runtime` launch removes the need for a launcher. This is the `launch` fragment of a server declaration; +replace the entry with your real resource, and keep server-specific arguments in `arguments`: + +```json +{ + "kind": "runtime", + "tool": "node", + "minimumVersion": "18.17", + "entry": "language-server/server.js" +} +``` + +The host discovers and checks the runtime; do not also declare the same launch tool in `runtimeDependencies`. +A `runtime` launch declares no `validationArguments`; the host probes the runtime. With `packagedRuntime` instead, +which excludes `minimumVersion`, the plugin ships the runtime, its dependencies, and licenses, and must be verified on each target architecture. + +API 2 also supports `initializationOptions` and declarative `settings`. Pass only options the upstream really supports; +`${setting:}` may reference only declared settings, and values must match their type and range. Settings are per plugin and shared across workspaces; +after a change, the host restarts the affected servers. For the format, see the [contract](../contract/v1/README.md#plugin-settings-api-2). + +### 6. Verify locally, install, and activate + +First run the two checks in the template's CI: `node scripts/package.mjs` assembles the artifacts, then Lithe Store's +`readPackageManifests` and `checkManifest` check package paths, the ID, the version, and the community trust declaration. +See the template's [CI workflow](https://github.com/Lithe-IDEA/lithe-plugin-lua/blob/main/.github/workflows/ci.yml). +These checks do not start the server and are not the full Core semantic manifest validation. +From the plugin repository root you can check the artifacts the same way CI does; pick an unused name for the sibling directory: + +```bash +git clone --depth 1 https://github.com/Lithe-IDEA/Lithe-store.git ../lithe-store-validation +node --input-type=module <<'JS' +import { readdirSync, readFileSync } from "node:fs"; +import { readPackageManifests, checkManifest } from "../lithe-store-validation/scripts/validate-index.mjs"; +const manifest = JSON.parse(readFileSync("io.github.lithe-idea.lua-support/plugin.json", "utf8")); +for (const name of readdirSync("dist")) { + const { manifests, unsafe } = readPackageManifests("dist/" + name, manifest.id); + const problems = [...unsafe, ...checkManifest(manifests, { id: manifest.id }, manifest)]; + if (problems.length) throw new Error(name + ": " + problems.join("; ")); + console.log("validated " + name); +} +JS +``` + +After forking, change the directory this reads `plugin.json` from to your own ID. +If an error comes from cross-references, template contexts, or the API level, also check the contract and pass the target host's install validation. + +The macOS host has two paths for installing a `none` community package, and both show an install review for the user to confirm: + +- **Install from disk**: in plugin management choose "Install Plugin from Disk…" and pick a zip from `dist/` or an unpacked package directory. + The host validates the manifest, keeps a private snapshot, and marks it as a community plugin whose publisher and source repository are unverified. + It suits repeated checks during development, but it does not check an index entry or the Release asset's checksum. +- **Your own plugin source**: following the [publishing guide](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md), publish the zips in your own public GitHub repository, + then add a `lithe-plugin-index.json` at the repository root. The index format is the same as the Store's; + the download URLs point at that repository's Release assets, and the digests pin the final zips. You do not need to apply to the public Store first. + Add your repository URL in Lithe's "Manage Plugin Repositories…" to install from your source, which is the path users will actually take. + +Before publishing, install from your own plugin source at least once end to end. Development builds treat an unsigned package that requires a publisher signature +as a local development package; that is a separate trust path and does not replace community install acceptance. + +Before installing, disable other plugins that declare the same language as yours, to avoid provider conflicts. Then: + +1. In the plugin marketplace, choose your source and version (or pick the package on disk), and confirm the programs, permissions, and source shown are what you expect. + After installing or updating a language server plugin, **restart Lithe** when prompted so the new version joins this launch's plugin graph; + the host skips the restart only for plugins with nothing but debug adapters or grammars (see section 7). +2. Open a `.lua` file in a small project. You should see completion, hover, go to definition, and diagnostics; + do not just check that the process "is running". +3. Save a change and confirm diagnostics update; close and reopen the file, and confirm there is no duplicate server and no duplicate diagnostics. +4. Launch Lithe from the Dock and try again, so you do not depend on a PATH inherited by chance from a terminal launch. +5. If you add run or test, verify them separately; LuaLS working does not mean the user's `lua` is installed. + +You should also be able to explain the protocol order after start: the host sends `initialize`, the server returns its capabilities and then receives +`initialized`, documents sync through `didOpen`, `didChange`, `didSave`, and `didClose` per the negotiated capabilities, +and completion requests and diagnostics travel over the same session. A normal stop is `shutdown`, then `exit`; when initialization does not finish or shutdown +times out, the host may force-stop the process. The current macOS defaults are 30 seconds for initialization and requests, and 2 seconds for shutdown. +Cancellation reaches the server as `$/cancelRequest`; the server may still finish the computation, so do not treat cancellation as the process having exited. + +Use this file for a minimal interactive check: + +```lua +---@param name string +local function greet(name) + return "Hello, " .. name +end + +print(greet("Lithe")) +``` + +Hover `greet` to see its signature, go to its definition, then change the call's argument to a number and watch for the type diagnostic. +The exact messages vary with the LuaLS version and project configuration, so record both when you check. + +### 7. Treat stop, failure, and uninstall as acceptance criteria + +Declarative plugins have no mechanism for authors to register arbitrary `activate()` / `deactivate()` callbacks. +The host manages sessions, processes, and deactivation; the author keeps the server in the foreground, follows the LSP lifecycle, +and designs every child process and all state to be stoppable, restartable, and isolated. On macOS, Core's force-stop targets the direct child only, +so you cannot assume it ends an arbitrary process tree the server spawns; the Windows host ends the process tree. The following are checks to run before publishing, +not guarantees you get by declaring something. + +| Action or failure | What to check | +| --- | --- | +| Install or enable only, without opening the language | The language server does not start just because it was installed | +| Edit, save, close and reopen files | Document sync and diagnostics work, with no duplicate sessions | +| Switch quickly between two projects or workspaces | Old results do not leak into the new project; indexes and locks do not collide | +| The server exits unexpectedly or fails to initialize | The host shows a failure you can trace; recovery leaves no duplicate processes | +| Node.js, Go, or Java missing or too old | An actionable runtime error, with no download at launch | +| Change an API 2 setting | New sessions use the new value, and the old process exits | +| Disable the plugin, close the workspace, quit Lithe | The sessions and processes stop, with no launcher or helper child left behind | +| Uninstall and reinstall | Uninstall deactivates first; after restart the package and host-managed plugin state are removed; custom caches are documented | +| Tamper with one file in the installed package and reload | The content recheck refuses to load it; the server must not "repair" the manifest itself | + +A plugin registered in this launch can be enabled and disabled immediately. Whether install, update, rollback, restore, and uninstall skip the restart is the host's decision: +only a plugin whose contributions are all debug adapters or grammars, and which has no backend, takes effect while Lithe runs. Language servers belong to the module graph +built at launch, so for an LSP plugin these file and version transitions finish at the next launch when prompted, +and an uninstall request deactivates a registered plugin first. Acceptance must cover the state before and after the restart. + +When the checks are done, close the app, servers, and child processes you started for testing. Do not treat closing an editor tab as uninstalling the server, +and do not assume a crash always triggers an automatic retry or that every platform recovers the same way. When you find a lifecycle problem, +record the logs, host version, package version, architecture, and reproduction steps in the plugin repository; file host process-management problems with Lithe. + +### 8. Rehearse publishing, updates, and rollback + +Following [Publishing a community plugin](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/publishing.md), bump the plugin version, publish a full Release, and submit the first index entry. +The template's Release workflow checks that `v` matches the manifest; for the original it is +`node scripts/package.mjs --expect-tag v0.3.1`. Use your own version after forking. + +Every time you update the server or runtime, update the pinned inputs and licenses, rebuild the affected platform packages, and run the interactive and lifecycle checks again. +A published zip cannot be replaced, and a version cannot be reused; ship fixes as a new version. Adding a runnable program triggers a new +install confirmation; see [Version tracking and updates](https://github.com/Lithe-IDEA/Lithe-store/blob/main/docs/versioning-and-updates.md). + +Before publishing, rehearse "old version → new version → back to the old logic" at least once: + +- Keep the old version's assets and index records, so older hosts still have a compatible version to install. +- When the old and new servers use incompatible index formats, isolate the state or rebuild it safely; never let the old version read corrupted state. +- macOS rollback uses the previous version kept in the installation record and verifies that package again; for an LSP plugin the switch happens after restart. + It offers no choice of arbitrary past versions, and it does not restore server indexes or user data. Rehearse this path for real. +- When the current version is broken, publish a higher fix version, restoring the last stable server in the new package if needed, + and ask for the broken version to be withdrawn; do not rely on users' machines happening to keep the previous version. +- Verify uninstall and reinstall too. Document what happens to source, user build output, and custom caches, so user data is not deleted by mistake. + +### 9. Troubleshoot by failure stage + +| Symptom | Check first | +| --- | --- | +| Not visible in the marketplace, or not installable | Source URL, an asset for the current platform, whether the host supports the `pluginAPIVersion` | +| `plugin.archive.checksumMismatch` | Whether a Release asset was replaced; whether the index pins the final zip's digest | +| `plugin.manifest.invalid` | Unknown fields, schema/API confusion, IDs and references, API 2 fields in an API 1 manifest | +| `plugin.permission.undeclared` / `plugin.activation.invalid` | Whether providers, permissions, and events match one to one | +| `plugin.launch.missing` | Relative path, letter case, packaging completeness, and the executable bit | +| `plugin.launch.invalidRequest` | A launch uses `${logDir}` or `${workspaceStateDir}`, but the host did not supply that directory | +| `plugin.runtime.missing` | Runtime discovery, the minimum version, the Dock launch environment | +| A process runs but there is no completion | Whether logs pollute stdio, the LSP handshake, the document language identifier, upstream configuration | +| Refuses to load after one use | Whether the server wrote logs, indexes, locks, or generated files into the package | +| Processes remain after disabling | Whether the launcher uses `exec`, backgrounds itself, or leaves helper processes behind | +| Completion works but build or test does not | Whether a separate run, test, or build integration was actually declared and verified | + +For the full field set, API levels, and stable error codes, go by Lithe's +[plugin contract](../contract/v1/README.md) and the +[manifest schema](../contract/v1/plugin-manifest.schema.json). +The contract copy is pinned to a commit of the Lithe repository (see [`contract.lock.json`](../contract.lock.json)), and the host status on this page is checked against the latest `preview` of that repository; +both may include capabilities not yet in a stable Lithe release, so check what your target host supports before publishing. diff --git a/guides/native-build-workflows.md b/guides/native-build-workflows.md index 2faf785..ee8ba4e 100644 --- a/guides/native-build-workflows.md +++ b/guides/native-build-workflows.md @@ -1,10 +1,14 @@ -# 原生构建、测试与运行准备(开发中) +# 原生构建、测试与运行准备 / Native build, test, and run preparation + +[中文](#中文) · [English](#english) + +## 中文 插件可以只提供工作区后端,不创建网页。用户从原生工具窗口执行构建、发现与运行测试、准备应用启动;宿主继续管理工作区、取消、输出和应用进程。 -本文对应 Lithe #1495 的宿主改动,契约副本已经同步。Node SDK 和 Java SDK 从 0.2.0 起包含这些入口,更早的 SDK 不包含它们。不要手改契约副本或向旧版本宿主隐瞒 `requires.buildProviders`。 +这些入口对应 `buildProviders` 2–6,macOS 宿主已经实现;Windows 宿主不支持构建提供者。Node SDK 和 Java SDK 从 0.2.0 起包含这些入口,更早的 SDK 不包含它们。不要手改契约副本或向旧版本宿主隐瞒 `requires.buildProviders`。 -## 选择入口 +### 选择入口 | 工作 | Node SDK | Java SDK | 最低 `buildProviders` / 进程协议 | | --- | --- | --- | --- | @@ -18,7 +22,7 @@ 处理器必须在 `listen` 前注册。模型、依赖和测试发现回复由 SDK 补齐对应 `format` 和 `version: 1`;执行回复是 `{ exitCode, output }`,启动准备回复是完整的可移植计划。Core 继续检查选择器、工作区路径和大小限制。 -## 所有权与清理 +### 所有权与清理 插件从其选定的上游引擎读取当前工程、测试和运行事实。发现结果中的每个用例都必须能通过同一套件、用例、工程与目标选择器精确执行;处理器重新核对选择,不把失效用例当成“运行全部”。不要在宿主或 SDK 里再扫描 Java 注解、拼 classpath 或解释 Maven 模型。 @@ -32,7 +36,7 @@ 状态写入宿主提供的可写目录;安装后的插件包只读。`prepareRun` 的结果不能变成跨重启缓存的 classpath 或命令:宿主保存入口选择器,重启重新准备。 -## 可运行示例 +### 可运行示例 两套示例使用相同的虚构构建引擎,工程事实来自工作区的 `lithe-sample.json`。这是协议教学用的引擎,不是 Maven/Java 迁移成果。 @@ -58,7 +62,7 @@ `app` 目录需存在,测试工具需可执行。示例重新读取配置、核对工程/目标和测试,并解析真实路径以拒绝工作区外目录;实际插件应调用其上游工程引擎,而非复制这份虚构引擎。 -## 验证 +### 验证 ```sh node --test sdk/node/test/*.test.mjs @@ -79,17 +83,113 @@ node scripts/sdk-e2e.mjs --source /path/to/Lithe-IDEA --working-tree --build-wor `--working-tree` 只把 `rust/`、`shared/` 的暂存、未暂存和未跟踪源码复制到一次性 Core 检出,不修改源仓库,也不复制忽略的构建缓存。该模式报告基线 commit 加工作区改动,不能把输出里的基线 SHA 当成完整已提交源码。 -`--build-workflows` 显式增加两套新协议场景,供合并前验证;契约镜像同步到包含 `buildProviders: 5` 后,默认验证也自动包含它们。每周的 preview 检查显式运行新场景。新的测试通过真实 Core 规划与解码,覆盖构建非零退出码、精确筛选、运行前构建、准备计划、进度、取消后的工具清理、并行工作区隔离,以及包含 SDK/JAR 的完整包文件不变。它不代替原生界面、真实 Maven/JDT 工程或 Windows 实机验证。 +契约镜像已包含 `buildProviders` 6,所以默认验证就会运行原生构建工作流场景;`--build-workflows` 用来在较旧的镜像或宿主分支上显式加入它们,每周的 preview 检查也显式加了这个参数。新的测试通过真实 Core 规划与解码,覆盖构建非零退出码、精确筛选、运行前构建、准备计划、进度、取消后的工具清理、并行工作区隔离,以及包含 SDK/JAR 的完整包文件不变。它不代替原生界面、真实 Maven/JDT 工程或 Windows 实机验证。 若复用预构建 Java 示例,分别设置 `LITHE_PLUGIN_SDK_JAVA_SAMPLE` 与 `LITHE_PLUGIN_SDK_JAVA_NATIVE_SAMPLE`。编译使用 JDK 21,产物面向 Java 8;可用 `LITHE_PLUGIN_SDK_JAVA_RUNTIME` 指定实际运行时。 调试适配器、长期服务和跨插件协作有各自的宿主契约;这些处理器不开放跨包私有函数或反向 `lithe/*` 调用。 - -## 原生程序调试准备 +### 原生程序调试准备 运行声明加 `debug: {"adapter":"native"}`,同一插件提供 `native` 适配器;manifest 要求 `buildProviders: 5` 与 `debugAdapters: 2`。适配器声明 launch/executable,使用 program、cwd、args、env 的目标绑定。不要用跨包 ID,也不要在后端返回 DAP 进程、端口或任意调试参数。 `prepareDebug` 先完成构建,再返回 `{program, arguments, workingDirectory, environment}`。程序是当前实例内的已有文件,路径从工作区起算;宿主校验路径和可执行权限,再复用现有 DebugAdapter Registry。取消、错误和过期结果都不能启动调试。Node 和 Java 的 sample-native-build 从虚构引擎的 `run.debugProgram` 读取程序,参数使用当前请求的 userArguments;示例的 `example-dap` 是占位适配器命令,真实插件应打包符合其许可的成熟适配器。 端到端示例验证两个 SDK 的真实进程、运行前构建、原生调试准备前构建、Core 路径解码、进度及失败不返回目标。它不启动真实调试器,也不代表 Java/JDT、跨插件协作或 Windows 已完成。 + +--- + +## English + +A plugin can provide only a workspace backend and create no web page. Users build, discover and run tests, and prepare application launches from native tool windows; the host keeps managing the workspace, cancellation, output, and application processes. + +These entry points are `buildProviders` 2–6, which the macOS host implements; the Windows host does not support build providers. The Node SDK and Java SDK include these entry points from 0.2.0; earlier SDKs do not. Do not edit the contract copy by hand, and do not hide `requires.buildProviders` from older hosts. + +### Choosing an entry point + +| Work | Node SDK | Java SDK | Minimum `buildProviders` / process protocol | +| --- | --- | --- | --- | +| Execute a bounded task | `backend.build.execute` | `onExecute` | 2 | +| Run exact tests | `backend.build.test` | `onTest` | 3 | +| Discover runnable cases | `backend.build.discoverTests` | `onDiscoverTests` | 3 | +| Prepare an application launch | `backend.build.prepareRun` | `onPrepareRun` | 4 | +| Prepare native program debugging | `backend.build.prepareDebug` | `onPrepareDebug` | 5 | + +The manifest's capability versions are checked at install time, and at startup the SDK picks the highest version shared with what the host offers. Without the matching protocol, a new method is rejected before it reaches the handler; it is never rerouted to a private function or expanded into a different host command. Read the result with `backend.buildVersion` in Node or `backend.negotiated().buildVersion()` in Java. + +Register handlers before `listen`. For model, dependency, and test discovery replies, the SDK fills in the matching `format` and `version: 1`; an execution reply is `{ exitCode, output }`, and a launch preparation reply is a complete portable plan. Core still checks selectors, workspace paths, and size limits. + +### Ownership and cleanup + +The plugin reads the current project, test, and run facts from the upstream engine it chose. Every case in a discovery result must be exactly runnable through the same suite, case, project, and target selectors; the handler checks the selection again and never treats a stale case as "run all". Do not scan Java annotations, assemble a classpath, or interpret a Maven model in the host or SDK. + +Build and test handlers start tools through the call context's `run`; cancellation or the end of the call ends the tool tree it owns. A tool that returns a non-zero code normally is still an execution result; an exception is a protocol or preparation failure. Do not start processes that outlive the call, and do not replace these domain entry points with a private function. + +Run preparation first waits for upstream sync and any required build, then returns `executable`, the raw `arguments` array, a workspace-relative `workingDirectory`, and `environment`. `executable.kind` is only `command` (a bare command name the host resolves) or `toolchain` (a host toolchain identifier). The handler does not start a long-running application; the host validates the plan, then starts and stops the application and shows its output. A build failure, cancellation, or stale result must not start the application. + +Core reports a backend rejection of an execute or test request as `plugin.build.failed`. An RPC error from run preparation keeps the generic LSP `serverError` classification; it is not an execution result with an exit code and does not use the private-function `pluginErrorCode`. In both failures the host must not start the application or fall back to an old command. + +Execute, test, run preparation, and debug preparation contexts carry an `operationID` assigned by Core, and progress carries it automatically. Node uses `context.progress({ message, percentage, output })`; Java uses `context.progress(message, percentage, output)`. Messages are limited to 4096 UTF-8 bytes, each output chunk to 64 KiB, and percentages are clamped to 0–100; reports after cancellation or completion are dropped. Model, dependency, and discovery requests have no execution progress. + +State goes into the writable directories the host provides; an installed plugin package is read-only. A `prepareRun` result must not become a classpath or command cached across restarts: the host stores the entry selector and prepares again after a restart. + +### Runnable examples + +Both examples use the same fictional build engine, whose project facts come from `lithe-sample.json` in the workspace. It is an engine for teaching the protocol, not a result of migrating Maven or Java. + +- The [Node example](../sdk/node/examples/sample-native-build/backend/main.mjs) and its [manifest](../sdk/node/examples/sample-native-build/plugin.json). During development it imports the SDK from source; for distribution, put `src/` into `backend/vendor/` and change the import to `./vendor/index.mjs`. The end-to-end tests package and verify that layout. +- The [Java example](../sdk/java/examples/sample-native-build/src/main/java/org/example/samplenativebuild/SampleNativeBuild.java) and its [manifest](../sdk/java/examples/sample-native-build/plugin.json). Run `mvn -B -ntp -f sdk/java/pom.xml package` and put `sample-native-build/target/sample-native-build.jar` into the package's `backend/`. The SDK is already shaded into the same JAR. + +The example project contains a build command, exact cases, and application arguments, for example: + +```json +{ + "projects": [{ + "name": "app", "path": "app", + "build": ["node", "-e", "console.log('built')"], + "tests": [{ "suite": "Suite", "case": "exact case", + "command": ["node", "-e", "console.log('passed')", "--"] }], + "run": { "command": "node", + "arguments": ["-e", "console.log(process.argv.slice(1))", "--"] } + }] +} +``` + +Model paths are relative to the build instance, while project and target selectors in later requests are relative to the whole workspace; the example converts between the two explicitly and never treats a nested instance as the root project. + +The `app` directory must exist and the test tool must be executable. The example rereads its configuration, checks the project, target, and test, and resolves real paths to reject directories outside the workspace. A real plugin should call its upstream project engine instead of copying this fictional one. + +### Verification + +```sh +node --test sdk/node/test/*.test.mjs +npm ci --prefix sdk/node --ignore-scripts +npm run --prefix sdk/node test:types +mvn -B -ntp -f sdk/java/pom.xml verify +node --test scripts/*.test.mjs + +# Compatibility scenarios for the pinned contract. +node scripts/sdk-e2e.mjs + +# When the host branch is committed but not yet merged. +node scripts/sdk-e2e.mjs --source /path/to/Lithe-IDEA --ref branch --build-workflows + +# Explicitly use uncommitted host sources; cannot be combined with --ref. +node scripts/sdk-e2e.mjs --source /path/to/Lithe-IDEA --working-tree --build-workflows +``` + +`--working-tree` copies only the staged, unstaged, and untracked sources under `rust/` and `shared/` into a throwaway Core checkout. It does not modify the source repository or copy ignored build caches. This mode reports the base commit plus working-tree changes, so the base SHA in its output does not stand for complete committed sources. + +The contract mirror already contains `buildProviders` 6, so the default run includes the native build workflow scenarios; `--build-workflows` adds them explicitly against an older mirror or a host branch, and the weekly preview check passes it explicitly too. The new tests plan and decode through a real Core and cover a non-zero build exit code, exact filtering, build before run, preparation plans, progress, tool cleanup after cancellation, isolation between parallel workspaces, and an unchanged complete package including the SDK and JAR. They do not replace verification of the native UI, real Maven/JDT projects, or real Windows machines. + +To reuse prebuilt Java examples, set `LITHE_PLUGIN_SDK_JAVA_SAMPLE` and `LITHE_PLUGIN_SDK_JAVA_NATIVE_SAMPLE`. They compile with JDK 21 and target Java 8; `LITHE_PLUGIN_SDK_JAVA_RUNTIME` selects the runtime that actually runs them. + +Debug adapters, long-running services, and cross-plugin collaboration have host contracts of their own; these handlers open no cross-package private functions or reverse `lithe/*` calls. + +### Native program debug preparation + +Add `debug: {"adapter":"native"}` to the run declaration and provide a `native` adapter from the same plugin; the manifest requires `buildProviders: 5` and `debugAdapters: 2`. The adapter declares launch/executable and uses the target bindings for program, cwd, args, and env. Do not use a cross-package ID, and do not return a DAP process, port, or arbitrary debug arguments from the backend. + +`prepareDebug` finishes the build first and then returns `{program, arguments, workingDirectory, environment}`. The program is an existing file inside the current instance, with a path relative to the workspace; the host checks the path and execute permission and then reuses the existing DebugAdapter Registry. Cancellation, errors, and stale results must not start debugging. The Node and Java sample-native-build examples read the program from the fictional engine's `run.debugProgram` and use the current request's userArguments as arguments. The examples' `example-dap` is a placeholder adapter command; a real plugin should package a mature adapter whose license allows it. + +The end-to-end example verifies real processes for both SDKs, build before run, build before native debug preparation, Core path decoding, progress, and that failures return no target. It does not start a real debugger and does not mean Java/JDT, cross-plugin collaboration, or Windows is done. diff --git a/guides/native-terminals.md b/guides/native-terminals.md index 4bbf552..b385d65 100644 --- a/guides/native-terminals.md +++ b/guides/native-terminals.md @@ -1,4 +1,92 @@ -# Open a native Lithe terminal from a plugin +# 从插件打开 Lithe 原生终端 / Open a native Lithe terminal from a plugin + +[中文](#中文) · [English](#english) + +## 中文 + +这些 SDK 封装从 Node.js SDK 0.3.0、Java SDK 0.3.0 和 Rust SDK 0.2.0 开始提供,并且需要宿主声明终端方法。仓库里的契约副本锁定在已合并的宿主改动 [Lithe #1653](https://github.com/1lck/Lithe-IDEA/pull/1653),即 `f64d262c48f1ff5ad1778b301138596493a09ad5`。副本由 `scripts/contract.mjs sync` 生成,不手改。规则见[原生终端契约](../contract/v1/README.md#plugin-owned-native-terminals-function-protocol-3)。 + +交互式客户端请使用宿主的集成终端。声明 `requires.backend: 2` 和权限 `terminal.create`;网页调用自己后端的函数,后端带着该函数的工作区上下文调用宿主。插件不需要实现终端模拟器,不需要通过插件函数转发按键,也不需要成为构建或运行提供者。 + +先用 `host.supports` 探测 `lithe/host/terminal/create` 和 `reopen`。旧宿主和 Windows 宿主可能没有这些方法。首个实现只支持 macOS 上的本机工作区,并明确拒绝远程 authority。宿主检查调用方、显示执行确认,并管理 PTY、渲染、焦点、尺寸调整和有界的进程树关闭。传 `show:false` 时,create 和 reopen 保留当前的终端选择,不展开终端面板,也不请求输入焦点。需要让新标签页显示出来时,调用 `show`。 + +### Node.js 示例 + +```js +const host = context.host; +const workspace = context.workspaceContext; +if (!workspace || !await host.supports('lithe/host/terminal/reopen')) { + throw new Error('This host has no native terminal API.'); +} +const terminal = await host.terminal.create(workspace, { + title: 'Interactive client', + executable: 'example-client', + arguments: ['--interactive'], + retainedContextBytes: 16 * 1024, +}); +// 退出订阅需要比这次调用活得更久时,把它放在后端生命周期的 host 上。 +const events = await backend.host.terminal.onExit(workspace, terminal.terminalID, event => { + console.error(event.generation, event.exitCode, event.reason); +}); +await host.terminal.close(workspace, { terminalID: terminal.terminalID, retainedContextBytes: 4096 }); +await host.terminal.reopen(workspace, { terminalID: terminal.terminalID }); +await events.dispose(); +await host.terminal.dispose(workspace, { terminalID: terminal.terminalID }); +``` + +只传契约支持的字段。尤其是 close 要传 `{terminalID, retainedContextBytes:4096}`,不要把结果里的元数据复制进请求参数;宿主会拒绝未知字段。所有 SDK 都不会在取消后自动重试启动。 + +### Rust 示例 + +```rust,ignore +let workspace = context.workspace_context().ok_or_else(|| + lithe_plugin_sdk::PluginError::new("example.noWorkspace", "Open a workspace first."))?; +let host = context.host(); +if !host.supports("lithe/host/terminal/reopen")? { + return Err(lithe_plugin_sdk::PluginError::new( + "example.unavailable", "This host has no native terminal API.").into()); +} +let terminal = host.terminal(); +let opened = terminal.create(workspace, serde_json::json!({ + "executable": "example-client", "arguments": ["--interactive"], + "retainedContextBytes": 4096 +}))?; +let id = opened["terminalID"].as_str().ok_or_else(|| + lithe_plugin_sdk::FunctionError::internal("Host returned no terminalID"))?; +terminal.close(workspace, id, Some(4096))?; +terminal.reopen(workspace, id)?; +terminal.dispose(workspace, id)?; +``` + +Rust 还提供 `terminal.on_exit(workspace,id)`,返回 SDK 已有的 `Subscription`,用有界的 `recv_timeout` 接收,并显式调用 `dispose`。 + +### Java 接口 + +`host.terminal()` 提供 `create(context,params)`、`show(context,params)`、`close(context,params)`、`reopen(context,params)`、`dispose(context,params)` 和 `onExit(context,params)`。参数是普通的 JSON Map。前五个返回 `CompletableFuture>`(show 和 dispose 返回 null);`onExit` 返回 `CompletableFuture`。上下文、取消、宿主失败和订阅路由与其他协议 3 方法使用同一通道。 + +### 启动与重开的语义 + +`executable` 是 PATH 中的裸命令名或绝对路径;`arguments` 保持 argv 数组。可选的 `cwd` 是工作区内的 `{rootID,path}` 目录资源,不能离开工作区。`environment` 是字符串覆盖值或表示删除的 null 组成的映射,不是 shell 赋值语句。不要把凭据放进标题、参数或日志。输入和输出都留在原生终端里;没有公开的 `sendText`、读取终端输出或枚举用户终端的 API。 + +`close` 移除标签页并停止进程,保留启动配置和已显示输出末尾的一段有界纯文本。`retainedContextBytes` 是 UTF-8 字节预算:默认 16384,最大 262144,0 表示不保留历史。可以在 create 时设置,也可以在 close 时覆盖。为了不截断 Unicode 字符,实际保留的字节数可能更少。显示或回显过的密钥可能出现在这段历史里,这类会话请设为 0。原始转义序列和 stdin 事件都不保留。 + +`reopen` 用同样的 executable、argv、cwd 和 environment 启动一个**新进程**,启动前先把保留的那段输出恢复到显示中。它不会重放输入、恢复进程内存或找回 shell 变量。它保留 `terminalID` 并递增 `generation`;事件带有 generation,旧进程的退出不会被误认为新进程的退出。退出订阅在 reopen 之后继续有效。进程自然退出和用户关闭标签页之后也可以 reopen。这两条路径都会先完成有界的原生清理,只保留选定的快照,并释放原生会话和渲染缓冲区,然后才通知订阅者。清理期间句柄处于忙碌状态;退出订阅者收到事件后可以立即 reopen。字节预算为 0 时,任何退出路径都不保留历史。 + +`dispose` 忘掉句柄、启动设置和历史;会话仍在运行时先关闭它。每个插件最多保留 8 个句柄(包括已关闭的),所以不会再 reopen 的句柄要 dispose。后端或会话重启、插件停用或更新、关闭或替换工作区,也会释放这些句柄。上下文和历史只在内存中,从不写入已安装的插件包。 + +Rust 示例的 `terminal.cycle` 函数和 Core 端到端场景覆盖协议的往返。该场景由测试提供宿主应答;真实的原生 PTY 输入、输出和历史由 Lithe 自己的 `PluginTerminalNativeTests` 验证。 + +终端生命周期场景现在默认针对锁定的宿主运行: + +```sh +node scripts/sdk-e2e.mjs +``` + +示例在已安装的清单里声明了 `terminal.create`,所以测试拿到的授权与已安装插件相同。没有这个权限的旧宿主会拒绝该示例清单;没有声明它的其他插件保持原有兼容性。 + +--- + +## English These SDK wrappers ship from Node.js SDK 0.3.0, Java SDK 0.3.0, and Rust SDK 0.2.0, and require a host that advertises the terminal methods. The checked-in contract mirror is pinned to @@ -22,7 +110,7 @@ With `show:false`, create/reopen preserve the current terminal selection and do not reveal the terminal panel or request input focus. Call `show` when the new tab should become visible. -## Node.js +### Node.js ```js const host = context.host; @@ -51,7 +139,7 @@ retainedContextBytes:4096}` for close rather than copying result metadata into request params; the host refuses unknown fields. No SDK retries a launch after cancellation automatically. -## Rust +### Rust ```rust,ignore let workspace = context.workspace_context().ok_or_else(|| @@ -76,7 +164,7 @@ terminal.dispose(workspace, id)?; Rust also exposes `terminal.on_exit(workspace,id)` as an existing SDK `Subscription`, with bounded `recv_timeout` and explicit `dispose`. -## Java +### Java `host.terminal()` exposes `create(context,params)`, `show(context,params)`, `close(context,params)`, `reopen(context,params)`, `dispose(context,params)` and @@ -85,7 +173,7 @@ Rust also exposes `terminal.on_exit(workspace,id)` as an existing SDK `CompletableFuture`. Context, cancellation, host failures and subscription routing use the same channel as the other protocol-3 methods. -## Launch and reopen semantics +### Launch and reopen semantics `executable` is a bare PATH command or absolute path; `arguments` stays an argv array. Optional `cwd` is a workspace `{rootID,path}` directory resource and diff --git a/guides/plugin-backend-workspace.md b/guides/plugin-backend-workspace.md index 5a22fb5..fa93324 100644 --- a/guides/plugin-backend-workspace.md +++ b/guides/plugin-backend-workspace.md @@ -1,17 +1,22 @@ -# 插件后端:工作区上下文与宿主服务(backend 2、协议 3,开发中) +# 插件后端:工作区上下文与宿主服务(backend 2、协议 3) / Plugin backend: workspace contexts and host services (backend 2, protocol 3) + +[中文](#中文) · [English](#english) + +## 中文 这篇指南面向写插件后端(`backend`)的作者。目标读者已经能让自己的网页调用后端函数(`backend` 1), 现在想让后端在没有页面时也能启动、知道用户打开了哪些工作区、响应命令,并按需调用宿主能力。 字段与错误码的权威定义在[契约](../contract/v1/README.md#backend-2-workspace-contexts),这里只讲怎么用、为什么这样设计。 -> **宿主支持。** `requires.backend: 2` 和插件函数协议 3 还在 Lithe `preview` 分支上开发,尚未进入稳定版。 -> 稳定版宿主只提供 `backend: [1]` 时会以 `plugin.api.unsupported` 拒绝 `backend` 2 的插件,所以老版本 Lithe -> 不会启动一个期待工作区上下文的进程。三个官方 SDK 都实现了协议 3:Node.js SDK 0.2.0、Java SDK 0.2.0 和 Rust SDK 0.1.0 +> **宿主支持。** `requires.backend: 2` 和插件函数协议 3 由 macOS 宿主实现;Windows 宿主不提供 `backend`。 +> 只提供 `backend: [1]` 的旧宿主会以 `plugin.api.unsupported` 拒绝 `backend` 2 的插件,所以老版本 Lithe +> 不会启动一个期待工作区上下文的进程。三个官方 SDK 都实现了协议 3:Node.js SDK 0.2.0、Java SDK 0.2.0 和 Rust SDK 0.1.0 起 > (见各自 README 的 Host services:[Node.js](../sdk/node/README.md#host-services)、[Java](../sdk/java/README.md#host-services)、 > [Rust](../sdk/rust/README.md#host-services))。用其他语言或更早的 SDK 需要自己处理下文的请求和通知。 +> 各宿主支持到哪一版,见按最新 preview 核对的[版本对照](../reference/capabilities-and-versions.md)。 -## 1. 三个版本号各管什么 +### 1. 三个版本号各管什么 | 写在哪里 | 值 | 决定什么 | | --- | --- | --- | @@ -21,7 +26,7 @@ 清单只做声明,宿主读清单决定装不装、什么时候启动;进程起来以后能做什么,靠协商和 `getCapabilities` 判断。 -## 2. 最小清单 +### 2. 最小清单 ```json { @@ -51,14 +56,14 @@ - 仍然拒绝 `${workspaceStateDir}`:一个插件只有一个后端进程,被所有窗口共享,工作区不是进程状态。 每个工作区的状态目录从 `getContext` 的 `stateDirectory` 取。 -## 3. 启动时机 +### 3. 启动时机 - `onWorkspaceOpen` 不会拖慢打开工作区。宿主在窗口显示之后逐个启动这些后端;`initialize` 超时的后端会被停止, 并像其他启动失败一样报告,不会挡住工作区。 - 后端在 Lithe 所在的机器上运行,远程工作区也一样。 - 激活只看插件是否已安装、启用、通过审查。Lithe 目前没有工作区信任状态;会运行或修改东西的操作,由宿主逐次询问用户。 -## 4. 协商协议 3 +### 4. 协商协议 3 宿主在 `initialize` 里提供 `capabilities.experimental.litheFunctions: { "versions": [1, 2, 3] }`。后端从中选自己实现的最高版本作答: @@ -68,7 +73,7 @@ 只回答你真正实现了的版本。回答 `2` 的后端继续按协议 2 工作,永远看不到协议 3 新增的通知、上下文字段和失败码。 -## 5. 先问宿主能做什么 +### 5. 先问宿主能做什么 协商到 3 以后,第一件事是调 `lithe/host/getCapabilities`(不带参数或 `{}`): @@ -103,7 +108,7 @@ 上例是声明了 `workspace.metadata` 的会话。`host`、`subscriptions`、`commands` 每个插件会话都有;`services` 只列本次会话可用的方法, 没声明的权限、宿主没有实现的服务都不会出现。数值以实际返回为准。**按 `services` 判断能不能调用,不要按协议版本号推断**,见第 10 节。 -## 6. 工作区上下文 +### 6. 工作区上下文 一个上下文就是“某个窗口里打开的某个工作区”: @@ -137,7 +142,7 @@ 页面调用后端函数时,宿主会按页面所在窗口填好上下文:协议 3 的后端在 `lithe/function/invoke` 的 `params.context` 里收到 `{ contextID, generation }`,直接拿它调 `getContext`。 -## 7. 宿主通知 +### 7. 宿主通知 宿主用 JSON-RPC 通知告诉后端发生了变化,后端不应答: @@ -151,7 +156,7 @@ `didRun` 只是事件,不代表命令要做的事已经成功;宿主也不会为了送达它而打开一个空工具窗口。 -### 纯后端命令(`ui` 4) +#### 纯后端命令(`ui` 4) ```json "requires": { "backend": 2, "serverProcess": 1, "ui": 4 }, @@ -166,9 +171,9 @@ 这样的命令要求 `backend` 2、`ui.contribute` 与 `backend.launch` 权限,以及对应的 `onCommand:`。 不认识 `ui` 4 的旧宿主会在安装时拒绝插件,不会装上一个执行不了的命令。完整示例见 -[`fixtures/command-backend.plugin.json`](../contract/v1/fixtures/command-backend.plugin.json)。 +[`fixtures/backend/command-backend.plugin.json`](../contract/v1/fixtures/backend/command-backend.plugin.json)。 -## 8. 文档、文件与窗口方法 +### 8. 文档、文件与窗口方法 协议 3 还开放了一组公共宿主方法,每组对应一个新权限。目前只有 macOS 宿主实现;Windows 宿主不提供 `backend`。 `getCapabilities.services` 只列出“清单权限推出的授权”与“本宿主已实现的方法”的交集,所以照第 10 节第 1 条先查再调。 @@ -198,7 +203,9 @@ - 后台没有挂载编辑器的文档可以列出和读取,但编辑与选区可能返回 `busy`。 - 消息有频率和排队上限(每插件 10 秒 5 条、最多 3 条待答),用户回答前请求一直挂起。 -## 9. 宿主失败与错误码 +协议 3 还包括插件自有的编辑器动作(`editor.actions`)和原生终端(`terminal.create`),分别见[编辑器、文档与预览](../reference/domains/editor.md)和[从插件打开 Lithe 原生终端](native-terminals.md)。 + +### 9. 宿主失败与错误码 协议 3 的宿主失败带类型:JSON-RPC 号码与 `error.data.code` 一一对应,号码永不改义,部分失败还带 `error.data.details`。 @@ -223,7 +230,7 @@ `-32000` 到 `-32009` 留给后端自己的失败,宿主号码从 `-32010` 起。 -## 10. 协议 3 只增不改:后端要遵守的四条 +### 10. 协议 3 只增不改:后端要遵守的四条 从协议 3 起,宿主再开放新能力(新服务及其方法和通知、可选参数、结果字段、错误码)都在协议 3 内新增, 不再升协议版本;只有会让已有后端读错的改动(删除或改名、改字段含义或类型、把可选参数改成必填、改错误码号码) @@ -239,11 +246,266 @@ 新服务总是配一个新的清单权限。旧宿主不认识这个权限时,会在安装时以 `plugin.permission.unknown` 拒绝整个插件包, 权限不会被悄悄授予或忽略。 -## 11. 验证 +### 11. 验证 -- 用契约里的 [`fixtures/function-protocol.json`](../contract/v1/fixtures/function-protocol.json) 核对你的实现: +- 用契约里的 [`fixtures/backend/function-protocol.json`](../contract/v1/fixtures/backend/function-protocol.json) 核对你的实现: `hostRequests` 有不认识的选项等请求用例,`invocationContexts` 有调用上下文,`hostFailures` 有失败码映射, `hostNotifications` 有通知用例。 -- 清单示例是 [`fixtures/backend-workspace.plugin.json`](../contract/v1/fixtures/backend-workspace.plugin.json); - 纯后端命令的示例是 [`fixtures/command-backend.plugin.json`](../contract/v1/fixtures/command-backend.plugin.json); - `fixtures/invalid-manifests.json` 里的 backend 用例是会被拒绝的写法。 +- 清单示例是 [`fixtures/backend/backend-workspace.plugin.json`](../contract/v1/fixtures/backend/backend-workspace.plugin.json); + 纯后端命令的示例是 [`fixtures/backend/command-backend.plugin.json`](../contract/v1/fixtures/backend/command-backend.plugin.json); + [`fixtures/manifest/invalid-manifests.json`](../contract/v1/fixtures/manifest/invalid-manifests.json) 里的 backend 用例是会被拒绝的写法。 + +--- + +## English + +This guide is for authors writing a plugin backend (`backend`). It assumes your web page can already call backend functions (`backend` 1), +and you now want the backend to start without a page, know which workspaces the user has open, respond to commands, and call host services when needed. + +The [contract](../contract/v1/README.md#backend-2-workspace-contexts) is normative for fields and error codes; this guide covers how to use them and why they are designed this way. + +> **Host support.** The macOS host implements `requires.backend: 2` and plugin-function protocol 3; the Windows host has no `backend`. +> An older host that offers only `backend: [1]` refuses a `backend` 2 plugin with `plugin.api.unsupported`, so an older Lithe +> never starts a process that expects workspace contexts. All three official SDKs implement protocol 3 from Node.js SDK 0.2.0, Java SDK 0.2.0, and Rust SDK 0.1.0 +> (see Host services in each README: [Node.js](../sdk/node/README.md#host-services), [Java](../sdk/java/README.md#host-services), +> [Rust](../sdk/rust/README.md#host-services)). With another language or an earlier SDK, handle the requests and notifications below yourself. +> For how far each host supports each version, see the [version tables](../reference/capabilities-and-versions.md), which track the latest preview. + +### 1. What each of the three version numbers controls + +| Where | Value | Controls | +| --- | --- | --- | +| `requires.backend` | `1` or `2` | **When** the backend process starts, and whether it may declare `workspace.metadata` and `onWorkspaceOpen` | +| `litheFunctions.version` in the `initialize` answer | `1`, `2`, or `3` | The **wire protocol** rules once the process runs: `2` adds host services (credentials); `3` adds capability discovery, workspace contexts, host notifications, and typed failure codes | +| `getCapabilities.services` | Runtime query result | Which host methods this session **can actually call**, and which notifications it receives | + +The manifest only declares. The host reads it to decide whether to install and when to start; what the running process can do is decided by negotiation and `getCapabilities`. + +### 2. Minimal manifest + +```json +{ + "schemaVersion": 2, + "id": "io.github..orders", + "displayName": "Orders", + "version": "1.0.0", + "pluginAPIVersion": 3, + "requires": { "backend": 2, "serverProcess": 1 }, + "vendor": { "id": "io.github.", "displayName": "", "signatureRequirement": "none" }, + "permissions": ["backend.launch", "workspace.metadata"], + "activationEvents": ["onWorkspaceOpen"], + "languages": [], + "backend": { + "launch": { "kind": "packaged", "path": "bin/backend" }, + "arguments": ["--state", "${stateDir}"] + } +} +``` + +- `backend` 2 needs at least one way to start: a web tool window (the first page call, as in `backend` 1), a command in `ui.commands[]` + (`onCommand:` also starts the backend), or `onWorkspaceOpen`. Having none is `plugin.manifest.invalid`. +- For commands without a tool window, declare `requires.ui: 4`: commands may omit `toolWindowID`, and `toolWindows` may be empty or omitted; + see section 7. Commands under `ui` 1–3 must still belong to a tool window. +- `workspace.metadata` only tells the backend the open workspaces' names, roots, IDs, authority (local or remote), and each workspace's private state directory; + it reads no file contents. Do not declare it unless you need this information; installation review lists it to the user. +- `${workspaceStateDir}` is still refused: a plugin has one backend process shared by every window, so a workspace is not process state. + Take each workspace's state directory from `stateDirectory` in `getContext`. + +### 3. When the backend starts + +- `onWorkspaceOpen` does not slow down opening a workspace. The host starts these backends one by one after the window appears; a backend whose `initialize` times out is stopped + and reported like any other start failure, without blocking the workspace. +- The backend runs on the machine that runs Lithe, for remote workspaces too. +- Activation only checks that the plugin is installed, enabled, and approved in review. Lithe has no workspace trust state yet; the host asks the user each time before an operation runs or changes something. + +### 4. Negotiating protocol 3 + +The host offers `capabilities.experimental.litheFunctions: { "versions": [1, 2, 3] }` in `initialize`. The backend answers with the highest version it implements: + +```json +"experimental": { "litheFunctions": { "version": 3, "functions": [{ "id": "orders.refresh" }] } } +``` + +Answer only versions you actually implement. A backend that answers `2` keeps working under protocol 2 and never sees the notifications, context fields, and failure codes added in protocol 3. + +### 5. Ask the host what it can do first + +Once protocol 3 is negotiated, first call `lithe/host/getCapabilities` (no params, or `{}`): + +```json +{ + "protocolVersion": 3, + "backendAuthority": "local", + "services": { + "commands": { "methods": [], "notifications": ["lithe/host/commands/didRun"] }, + "host": { "methods": ["lithe/host/getCapabilities"], "notifications": [] }, + "subscriptions": { + "methods": ["lithe/host/subscriptions/dispose"], + "notifications": ["lithe/host/subscriptions/notify"] + }, + "workspace": { + "methods": ["lithe/host/workspace/listContexts", "lithe/host/workspace/getContext", "lithe/host/workspace/getFolders"], + "notifications": ["lithe/host/workspace/didChangeContexts"] + } + }, + "limits": { + "pendingHostRequests": 16, + "functionResultBytes": 1048576, + "hostRequestBytes": 1048576, + "hostResultBytes": 1048576, + "errorMessageBytes": 1024, + "errorDetailsBytes": 4096, + "notificationBytes": 1048576 + } +} +``` + +The example is a session that declared `workspace.metadata`. Every plugin session has `host`, `subscriptions`, and `commands`; `services` lists only the methods this session may use, +so undeclared permissions and services the host does not implement never appear. Treat the numbers as whatever the host returns. **Decide what you can call from `services`, not from the protocol version**; see section 10. + +### 6. Workspace contexts + +A context is "one workspace open in one window": + +```json +{ + "contextID": "w-3f2a9c", + "generation": 2, + "name": "orders", + "authority": { "kind": "local" }, + "folders": [{ "rootID": "r0c1d2e3f4a5b6c7d", "name": "orders", "path": "/Users/me/orders" }], + "stateDirectory": "/Users/me/Library/Application Support/Lithe/PluginState//backend/workspaces/" +} +``` + +| Field | How to use it | +| --- | --- | +| `contextID` | Stable while the window shows this workspace. When the window switches to another workspace, the old context closes and the new one gets a new ID, so state cached by ID never leaks into another workspace | +| `generation` | Increments when the same workspace's roots or authority change. A request with a stale `generation`, or for a closed context, gets `plugin.host.contextExpired`; call `listContexts` again | +| `rootID` | Stable for the same location across restarts and does not reveal the path; derived cryptographically per plugin, so two plugins see different IDs for the same folder | +| `path` | Present only for local workspaces. A remote workspace (`authority.kind` is `remote`) has no `path`, and the local directory the host keeps for it is only an empty placeholder | +| `stateDirectory` | Private to the plugin and the workspace, outside the plugin package and the app bundle. Only `getContext` returns it, creating it at that moment; `listContexts` creates no directory | + +Common requests: + +| Request | Params | Result | +| --- | --- | --- | +| `lithe/host/workspace/listContexts` | none | `{ revision, contexts }`; entries have no `stateDirectory` | +| `lithe/host/workspace/getContext` | `{ context: { contextID, generation } }` | The full context | +| `lithe/host/workspace/getFolders` | `{ context: { contextID, generation } }` | `{ folders }` | + +When a page calls a backend function, the host fills in the context of the page's window: a protocol 3 backend receives `{ contextID, generation }` +in `params.context` of `lithe/function/invoke` and can pass it straight to `getContext`. + +### 7. Host notifications + +The host tells the backend about changes with JSON-RPC notifications; the backend does not answer them: + +| Notification | Params | When it arrives | +| --- | --- | --- | +| `lithe/host/workspace/didChangeContexts` | `{ revision }` | A context opened, closed, or changed; call `listContexts` again | +| `lithe/host/commands/didRun` | `{ commandID, context? }` | The user ran one of the plugin's own commands. If the backend is not running and declares `onCommand:`, the host starts it first and delivers the notification once it is ready | +| `lithe/host/subscriptions/notify` | `{ subscriptionID, sequence, event }` | An event of a subscription. `sequence` starts at 1 per subscription and increments by one, so gaps reveal losses; cancel with `lithe/host/subscriptions/dispose` | + +A backend that starts late gets no replay of earlier `didChangeContexts`; call `listContexts` once after starting. + +`didRun` is only an event and does not mean the command's work succeeded; the host also never opens an empty tool window just to deliver it. + +#### Backend-only commands (`ui` 4) + +```json +"requires": { "backend": 2, "serverProcess": 1, "ui": 4 }, +"permissions": ["backend.launch", "ui.contribute"], +"activationEvents": ["onCommand:io.github..orders.refresh"], +"ui": { + "commands": [ + { "id": "io.github..orders.refresh", "title": "Refresh Orders", "placements": ["commandPalette"] } + ] +} +``` + +Such a command requires `backend` 2, the `ui.contribute` and `backend.launch` permissions, and the matching `onCommand:`. +An older host that does not know `ui` 4 refuses the plugin at installation, so it never installs a command it cannot run. For a complete example, see +[`fixtures/backend/command-backend.plugin.json`](../contract/v1/fixtures/backend/command-backend.plugin.json). + +### 8. Document, file, and window methods + +Protocol 3 also opens a set of public host methods, each group behind a new permission. Only the macOS host implements them; the Windows host has no `backend`. +`getCapabilities.services` lists only the intersection of "grants derived from the manifest's permissions" and "methods this host implements", so check before calling, as rule 1 of section 10 says. + +| Method (prefix `lithe/host/`) | Permission | Purpose | +| --- | --- | --- | +| `documents/list`, `documents/getSnapshot`, `editor/getActive`, `editor/getSelections` | `documents.read` | List open documents; read unsaved editor content, the active tab, and selections | +| `editor/applyEdits`, `editor/replaceSelections`, `editor/insertText` | `editor.edit` | Edits with `expectedVersion`; a batch is one atomic operation and one undo step | +| `workspace/readDirectory`, `workspace/stat`, `workspace/readFile` | `workspace.files.read` | Read directories and files on disk | +| `workspace/watch` | `workspace.files.watch` | Subscribe to file changes; events arrive through `subscriptions/notify` | +| `workspace/findFiles`, `workspace/findText` | `workspace.search` | Search by file name or content, paged | +| `editor/open`, `editor/reveal` | `editor.open` | Open a file and go to a location | +| `preview/openHTML` | `preview.open` | Open an HTML file in the built-in preview | +| `browser/openExternal` | `browser.open` | Open an HTTP(S) link or HTML file in the system browser | +| `window/showInformation`, `window/showWarning`, `window/showError` | `window.messages` | Show a message with up to 3 buttons and return which one the user chose | + +The [contract](../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) is normative for params, results, and limits. When writing a backend: + +- Every method takes `context: { contextID, generation }`. Files are named by `{ rootID, path }`, where `path` uses `/` and is relative to + a root returned by `getFolders`. These methods refuse remote workspaces explicitly (`authorityUnsupported`). +- `workspace.metadata` only gives workspace information and **does not grant file reads**; declare `workspace.files.read` separately to read files. +- Positions `{ line, column }` start at 1 and count UTF-16 units; ranges are half-open. +- One read returns at most 128 KiB: an oversized snapshot gets `limitExceeded`, and a file read marks truncation with `isTruncated`. A batch of edits or selections has at most 1,000 items. +- Cancellation or timeout **does not mean rollback**: an edit or open may already have taken effect, so do not replay changes automatically. On `versionConflict`, reread and recompute; + on `contextExpired`, find the original target again instead of switching to the focused window; `busy` and `rateLimited` may be retried a bounded number of times; + `permissionDenied` and `readOnly` need the user. +- Documents open in the background without a mounted editor can be listed and read, but edits and selections may return `busy`. +- Messages have rate and queue limits (5 per 10 seconds per plugin, at most 3 awaiting an answer), and the request stays pending until the user answers. + +Protocol 3 also includes the plugin's own editor actions (`editor.actions`) and native terminals (`terminal.create`); see [Editor, documents, and previews](../reference/domains/editor.md) and [Open a native Lithe terminal from a plugin](native-terminals.md). + +### 9. Host failures and error codes + +Protocol 3 host failures are typed: each JSON-RPC number maps one-to-one to an `error.data.code`, a number never changes meaning, and some failures carry `error.data.details`. + +| `error.data.code` | Number | What a backend usually does | +| --- | --- | --- | +| `plugin.host.unavailable` | `-32601` | The host has no such method; check `getCapabilities` first | +| `plugin.host.invalidParams` | `-32602` | Malformed params; fix the code | +| `plugin.host.failed` | `-32603` | Any other failure; report it to the user | +| `plugin.host.cancelled` | `-32800` | You cancelled it yourself with `$/cancelRequest` | +| `plugin.host.permissionDenied` | `-32010` | The permission is not declared, or the user refused | +| `plugin.host.contextExpired` | `-32011` | Call `listContexts` again, then retry | +| `plugin.host.authorityUnsupported` | `-32012` | The service does not support remote workspaces, and the host will not run it locally instead | +| `plugin.host.versionConflict` | `-32013` | The resource version changed; `details.currentVersion` gives the current version | +| `plugin.host.limitExceeded` | `-32014` | A limit was exceeded, for example more pending requests than `limits.pendingHostRequests` | +| `plugin.host.timeout` | `-32015` | The host timed out | +| `plugin.host.notFound` | `-32016` | The resource does not exist | +| `plugin.host.readOnly` | `-32017` | The resource or workspace is not writable | +| `plugin.host.unsupportedOption` | `-32018` | The host does not know a field in the request; `details.option` names it, so drop it and retry | +| `plugin.host.preconditionFailed` | `-32019` | A required state does not hold, for example the user did not confirm | +| `plugin.host.busy` | `-32020` | The resource is in use; retry later | +| `plugin.host.rateLimited` | `-32021` | Too many requests; wait `details.retryAfterMilliseconds` when given | + +`-32000` through `-32009` are reserved for the backend's own failures; host numbers start at `-32010`. + +### 10. Protocol 3 only grows: four rules for backends + +From protocol 3 on, new host capabilities (new services with their methods and notifications, optional params, result fields, error codes) are added within protocol 3 +without raising the protocol version; only changes that would make an existing backend misread something (removing or renaming, changing a field's meaning or type, making an optional param required, changing an error number) +lead to protocol 4. So a plugin that implements protocol 3 once works on every future protocol 3 host, provided it follows these four rules: + +1. **Call only methods listed in `getCapabilities.services`.** Any other method gets `-32601` `plugin.host.unavailable`. +2. **Ignore unknown fields and notifications.** Ignore extra fields in results, notifications, `details`, and `limits`, and any unknown `lithe/host/*` notification. +3. **An unsupported new option gets an explicit answer.** If a request carries a field the host does not know, Core answers + `-32018` `plugin.host.unsupportedOption` with `details: { "option": "" }` before the host sees the request. The host never silently ignores it; + drop the option and retry, or tell the user to update Lithe. +4. **Treat unknown numbers as host failures.** A newer host may bring error codes you have not seen; branch on `error.data.code` when you know it, and otherwise treat it as a general failure. + +A new service always comes with a new manifest permission. An older host that does not know the permission refuses the whole plugin package at installation with `plugin.permission.unknown`, +so the permission is never silently granted or ignored. + +### 11. Verification + +- Check your implementation against the contract's [`fixtures/backend/function-protocol.json`](../contract/v1/fixtures/backend/function-protocol.json): + `hostRequests` has request cases such as unknown options, `invocationContexts` has invocation contexts, `hostFailures` has the failure code mapping, + and `hostNotifications` has notification cases. +- The manifest example is [`fixtures/backend/backend-workspace.plugin.json`](../contract/v1/fixtures/backend/backend-workspace.plugin.json); + the backend-only command example is [`fixtures/backend/command-backend.plugin.json`](../contract/v1/fixtures/backend/command-backend.plugin.json); + the backend cases in [`fixtures/manifest/invalid-manifests.json`](../contract/v1/fixtures/manifest/invalid-manifests.json) are the forms that are refused. diff --git a/guides/plugin-logging.md b/guides/plugin-logging.md index 364e87b..7f48939 100644 --- a/guides/plugin-logging.md +++ b/guides/plugin-logging.md @@ -1,12 +1,16 @@ -# 插件接入统一日志 +# 插件接入统一日志 / Plugin logging + +[中文](#中文) · [English](#english) + +## 中文 新版本插件使用 `litheLogging` 1 与 `lithe/log` 发送受限的操作事实,由宿主的共享事件目录、脱敏、队列和文件写入器落盘。三个 SDK 不再提供自由文本 `log` 接口,也没有 stderr 回退。插件无需实现 v2 文件格式、文件轮转、脱敏器或后台写入线程。 宿主必须在 initialize 中提供 `capabilities.experimental.litheLogging.versions: [1]`,SDK 回复 `capabilities.experimental.litheLogging.version: 1`。缺少协议时,SDK 拒绝初始化;不自动退回旧日志。初始化尚未协商时的失败只通过原有 RPC 错误与宿主进程生命周期记录,不能提前发送未协商的日志通知。 -本文的 API 从 Node.js SDK 0.3.0、Java SDK 0.3.0 和 Rust SDK 0.2.0 开始提供。插件在构建输入中锁定对应的 release tag 与 SHA-256,不要引用开发机的绝对 path 依赖。用这些版本构建的插件只能在提供 `litheLogging` 1 的 Lithe 版本上启动。 +本文的 API 从 Node.js SDK 0.3.0、Java SDK 0.3.0 和 Rust SDK 0.2.0 开始提供。插件在构建输入中锁定对应的 release tag 与 SHA-256,不要引用开发机的绝对 path 依赖。用这些版本构建的插件只能在提供 `litheLogging` 1 的 Lithe 版本上启动,主仓库最新 `preview` 已提供。还要在不提供它的 Lithe 版本上运行的插件,继续用 Node.js/Java SDK 0.2.0 或 Rust SDK 0.1.0;用新版本 SDK 构建的插件,在 Lithe 稳定版提供 `litheLogging` 1 之前把 GitHub Release 标为预发布。 -## 每种事实与级别 +### 每种事实与级别 | `kind` | v2 目录事件 | 级别与用途 | | --- | --- | --- | @@ -21,7 +25,7 @@ 通道由宿主从已校验的安装插件 ID 得到 `plugins//host`,插件不能更改通道或拼接路径。上下文诊断优先携带宿主原始 `operationID`,缺少合法值时沿用当前请求原始 JSON-RPC ID(安全字符串或整数的十进制字符串),wire 名为 `operationId`,只保留最长 128 字节的受控 ASCII 标识;无真实身份时省略。通知没有请求 ID,不生成回退身份。后台共享任务可以使用 backend/handle 的无 op 诊断,不能伪造一个操作 ID。 -## 三个 SDK 的用法 +### 三个 SDK 的用法 ```js backend.function('project.refresh', async (params, context) => { @@ -55,7 +59,7 @@ SDK 在函数与受管 build/VCS 请求的结果边界自动发送一次终态 Rust SDK 用一次安装的 panic hook 包装器,仅在线程处于 SDK 拥有的 catch 边界时屏蔽原始 payload 输出,随后发送固定 `panic` 类别。其他线程和边界外的 panic 继续执行原 hook。不要把用户内容放进 panic,插件也不要在 SDK 启动后替换该 hook 来恢复正文输出。 -## 上游诊断与文件位置 +### 上游诊断与文件位置 上游语言服务器的 stderr、`window/logMessage` 继续由宿主接收;它们不是插件自己的结构化日志入口。插件不得把源码、协议载荷、SQL、HTTP body 或构建输出复制进上游诊断,以为脱敏与截断能使其安全。控制台输出走已有 Build、Run、Test、VCS 或插件界面结果。 @@ -70,10 +74,92 @@ macOS 的日志窗口与导出读取 v2。Windows 的持久化仍是 v1,新的 - 原生文件不自动变成 v2 时间线事件,不能再把整个文件打印进 stderr。 - app bundle、插件包、Windows 安装目录运行时只读;日志与锁文件不得改变签名或 Sparkle 增量更新基线。 -## 最小验证 +### 最小验证 1. 用已有有界协议测试确认 logging v1 协商、四类事实、级别、原始操作关联、成功抑制、取消和非法参数拒绝。 2. 触发真实失败,确认原有用户错误码与提示保持不变,诊断只有固定类别,不含请求正文、原始异常或堆栈。 3. 使用真实 Core 查询 `diagnostics.drainCoreEvents`,核对目录事件与 `op`;不要把 v1 poll 中的旧 detail 当作新结构化记录。 4. 在目标宿主中安装并执行对应工作流,核对日志窗口。SDK 组件通过不能替代产品验收。 5. 原生日志位置有变化时比较工作流前后安装目录清单或哈希,确认发行资源不变;结束后清理本轮进程。 + +--- + +## English + +Current plugins use `litheLogging` 1 and `lithe/log` to send restricted operation facts. The host's shared event catalog, redaction, queue, and file writer persist them. The three SDKs no longer offer a free-text `log` interface and have no stderr fallback. A plugin does not implement the v2 file format, file rotation, a redactor, or a background writer thread. + +The host must offer `capabilities.experimental.litheLogging.versions: [1]` in initialize, and the SDK replies with `capabilities.experimental.litheLogging.version: 1`. When the protocol is missing, the SDK refuses to initialize; it does not fall back to the old logging. A failure before initialization has negotiated logging is recorded only through the existing RPC error and the host's process lifecycle; the plugin must not send a log notification before negotiation. + +The APIs in this guide ship from Node.js SDK 0.3.0, Java SDK 0.3.0, and Rust SDK 0.2.0. Pin the matching release tag and SHA-256 in your build inputs; do not depend on an absolute path on a development machine. Plugins built with these versions start only on Lithe versions that offer `litheLogging` 1, which the latest `preview` of the Lithe repository does. For a plugin that must also run on Lithe versions without it, stay on Node.js/Java SDK 0.2.0 or Rust SDK 0.1.0; for a plugin built with the newer SDKs, mark its GitHub Release as a prerelease until a stable Lithe release offers `litheLogging` 1. + +### Facts and levels + +| `kind` | v2 catalog event | Level and use | +| --- | --- | --- | +| `operationFinished` | `plugins.operation.finished` | INFO, the real successful end of a key user operation | +| `operationFailed` | `plugins.operation.failed` | ERROR, a failure that affects the request's result; the SDK sends it once at the boundary that owns the result | +| `degraded` | `plugins.operation.fallback_selected` | WARN, a failure that was handled by choosing a fallback | +| `recovered` | `plugins.operation.recovery_finished` | WARN, a real recovery after an earlier failure | + +`operationFinished` and `operationFailed` must carry a `durationMs` measured with a monotonic clock. Failure, degradation, and recovery use a controlled `code`; success has no `code`. The optional `count` and `durationMs` are integers from 0 to 9007199254740991. The API does not accept raw messages, stacks, requests, URLs, SQL, command output, or field dictionaries. + +`operation` accepts only function IDs registered with this SDK or closed protocol operations; a registered function ID longer than 64 bytes is reported as `function-call`. The public protocol operations are initialize, shutdown, notification, function-call, and result-encoding, the `lithe/build/*` and `lithe/vcs/*` methods the SDK knows, and the shortcut import request `lithe/keymap/import` (all three SDKs report its end state automatically). Arbitrary user text is not accepted even when its characters are valid. `code` is an ASCII category identifier of at most 64 bytes, not an error message. + +The host derives the channel `plugins//host` from the validated installed plugin ID; the plugin cannot change the channel or build a path. Contextual diagnostics carry the host's original `operationID` first; without a valid one they reuse the current request's original JSON-RPC ID (a safe string, or an integer as a decimal string). The wire name is `operationId`, and only a controlled ASCII identifier of at most 128 bytes is kept; it is omitted when there is no real identity. Notifications have no request ID, and no fallback identity is generated for them. Shared background work can use the backend or handle diagnostics without an operation, but must not invent an operation ID. + +### Using the three SDKs + +```js +backend.function('project.refresh', async (params, context) => { + // Call only in a real degraded branch that decides to continue; never pass exception text to the log. + context.diagnostics.degraded('project.refresh', 'cache_unavailable', 1); + return refreshWithoutCache(params); +}); +// High-frequency reads or polling produce no INFO success noise; failures are still recorded automatically. +backend.function('project.inspect', (params) => inspectProject(params)); +backend.suppressSuccessDiagnostics('project.inspect'); +backend.diagnostics.recovered('project.refresh', 'cache_restored'); +``` + +```java +context.diagnostics().degraded("lithe/build/model", "snapshot_unavailable", 1L); +backend.diagnostics().recovered("lithe/build/model", "snapshot_restored"); +backend.suppressSuccessDiagnostics("lithe/build/dependencies"); +``` + +```rust +context.diagnostics().degraded("query.run", "rows_truncated", Some(1))?; +handle.diagnostics().recovered("query.run", "connection_restored", None)?; +backend.suppress_success_diagnostics("query.inspect")?; +``` + +The operation passed to `degraded` and `recovered` must be registered, so put static examples after registration has actually finished. A Rust error return means only that the call's arguments are invalid. Protocol writes reuse the existing writer, and a failed diagnostic send never turns a successful user operation into a failure. There is no extra writer or background queue. + +A single entry point that mixes user operations with high-frequency reads can call `context.suppressSuccessDiagnostics()` (in Rust, `context.suppress_success_diagnostics()`) for the current call. That decision affects only this call's success fact, not other calls or failures. + +The SDK sends one end state automatically at the result boundary of functions and managed build and VCS requests. The notification goes out before the RPC response so the host can relate it to the still-active request. Do not send a failure from a handler and then return or throw the same failure. Cancellation produces no failure event, and a normally absent cleanup object should not produce a WARN. High-frequency reads, previews, statistics, and polling turn off success facts with `suppressSuccessDiagnostics` / `suppress_success_diagnostics`; failures and real degradations are kept. + +The Rust SDK installs a panic hook wrapper once. It suppresses the raw payload output only while the thread is inside a catch boundary the SDK owns, and then sends the fixed `panic` category. Panics on other threads and outside the boundary still run the original hook. Do not put user content in a panic, and do not replace the hook after the SDK starts to restore message output. + +### Upstream diagnostics and file locations + +The host still receives an upstream language server's stderr and `window/logMessage`; they are not the plugin's own structured logging path. A plugin must not copy source code, protocol payloads, SQL, HTTP bodies, or build output into upstream diagnostics in the belief that redaction and truncation make them safe. Console output goes through the existing Build, Run, Test, VCS, or plugin UI results. + +On macOS the log window and export read v2. Windows still persists v1: the new Core intake and catalog events do not mean the Windows log window has migrated. Declare a platform verified only after running the workflow on that target platform. + +Use `${logDir}` only when an upstream tool really can write only to files. The host explicitly provides the current session's `plugins//files/`, created through the writable storage adapter; it is never derived from the plugin's install path or the app bundle: + +- The log directory belongs only to the current session. It holds no databases, indexes, downloads, or workspace state and is not shared across worktrees. +- Caches and state keep using `${stateDir}` and `${workspaceStateDir}`. +- When log storage is unavailable, the host does not provide the variable. Remote processes cannot use a local log directory; a launch plan that references `${logDir}` without an absolute `logDirectory` fails. +- A plugin that needs no native files should not add this launch dependency. Check that the target host and platform support it before changing the launch template. +- Native files do not become v2 timeline events automatically, and the whole file must not be printed to stderr instead. +- The app bundle, plugin packages, and the Windows install directory are read-only at run time; logs and lock files must not change the signature or the Sparkle delta update baseline. + +### Minimum verification + +1. Use the existing bounded protocol tests to confirm logging v1 negotiation, the four facts, levels, original operation correlation, success suppression, cancellation, and rejection of invalid arguments. +2. Trigger a real failure and confirm that the existing user error code and message are unchanged and that diagnostics contain only fixed categories, with no request body, raw exception, or stack. +3. Query `diagnostics.drainCoreEvents` against a real Core and check the catalog events and `op`; do not mistake the old detail in a v1 poll for a new structured record. +4. Install the plugin in the target host, run the matching workflow, and check the log window. Passing SDK component tests does not replace product acceptance. +5. When the native log location changes, compare the install directory listing or hashes before and after the workflow to confirm the release resources are unchanged, and clean up this run's processes afterwards. diff --git a/reference/README.md b/reference/README.md new file mode 100644 index 0000000..1b9ecd4 --- /dev/null +++ b/reference/README.md @@ -0,0 +1,95 @@ +# 插件参考 / Plugin reference + +[中文](#中文) · [English](#english) + +## 中文 + +这里按功能整理插件契约:每个编号的 API 现在能用什么,每个能力、协议和格式有哪些版本,Core、macOS、Windows 和官方 SDK 各自支持到哪一版。规范以英文的 [`contract/v1/README.md`](../contract/v1/README.md) 为准,本目录只做导航和对照;两边不一致时以契约为准,并请提交问题。 + +### 从哪里开始 + +| 想知道 | 看这里 | +| --- | --- | +| API-01–44 每一项现在能用什么、还缺什么 | [插件 API 总表](api-catalog.md) | +| 插件 API 级别、`requires` 能力版本、进程协议版本、文档格式版本,以及各宿主和 SDK 支持到哪一版 | [能力与协议版本对照](capabilities-and-versions.md) | +| 每个协议、能力和 API 是哪天、哪个 PR 改的 | [插件协议更新记录](protocol-changelog.md)(只有中文) | +| 某个功能的字段、版本、宿主现状、契约章节和示例 | 下方的功能页 | +| 一步步做出一个插件 | [开发指南](../guides/) | + +### 功能页 + +| 功能页 | 内容 | API 编号 | 能力(`requires`) | +| --- | --- | --- | --- | +| [清单与打包](domains/manifest.md) | `plugin.json` 字段、插件 API 级别、权限与激活事件、包目录 | 不在编号内 | — | +| [语言、语言服务器与运行/测试](domains/languages.md) | 语言、语法、语言服务器、进程启动、运行和测试命令 | API-25 | `languages`、`languageServers`、`serverProcess`、`executionProviders` | +| [插件后端与插件函数](domains/backend.md) | 后端进程、函数协议、宿主方法、工作区上下文、文件、凭据、终端 | API-01、04–08、20–22、24、37、40、41 | `backend`、`serverProcess` 1 | +| [编辑器、文档与预览](domains/editor.md) | 文档和选区、版本化编辑、打开与定位、HTML 预览、编辑器动作 | API-09、10、15、16、27 | `backend` 2 | +| [界面](domains/ui.md) | 工具窗口、命令、包内网页、原生控件、后端命令、消息框 | API-02、03、13、14、17 | `ui` | +| [插件设置](domains/settings.md) | 声明式设置、放置位置、作用域、生效方式、设置页按钮 | API-19 | `settings` | +| [声明式构建系统](domains/build-systems.md) | 构建检测、模型、命令规划、项目准备角色 | API-29 | `buildSystems` | +| [构建提供者](domains/build-providers.md) | `litheBuild` 协议:同步、任务、测试、运行和调试准备、模型输入 | API-29 | `buildProviders`、`buildModelInputs` | +| [调试适配器](domains/debug-adapters.md) | DAP 适配器声明、启动和传输 | API-30 | `debugAdapters`、`serverProcess` 2 | +| [版本控制提供者](domains/vcs.md) | VCS 提供者进程和协议 | API-31 | `vcsProviders` | +| [项目生成器](domains/project-generators.md) | 新建项目的模板和步骤 | API-33 | `projectGenerators` | +| [工具链](domains/toolchains.md) | 工具定义、可执行文件候选、工具链提供者、Core 命令 | API-23 | `toolDefinitions`、`toolchainProviders`、`executableResolution` | +| [快捷键导入](domains/keymap-import.md) | 从其他编辑器导入快捷键的提供者 | 不在编号内 | `keymapImportProviders` | +| [包信任](domains/package-trust.md) | 签名要求、签名文件格式、随应用发布的插件 | 不在编号内 | `packageTrust` | +| [插件市场](domains/marketplace.md) | 索引来源和格式、可用性判断、安装审查和确认 | 不在编号内 | — | + +计划中、还没有方法的 API(API-11、12、18、26、28、32、34–36、38、39、42–44)只在[总表](api-catalog.md#计划中的-api)列出,没有功能页。 + +### 每个功能页的结构 + +1. 用途和摘要表:能力 ID、版本、Core 命令或协议方法、Windows 现状、官方 SDK。 +2. 版本对照:每个版本新增什么,Core、macOS、Windows 是否支持。 +3. 字段、方法、错误码等细节表。 +4. 契约章节:链接到 `contract/v1/README.md` 的对应小节。 +5. 示例与用例:`contract/v1/fixtures/` 里的文件和用例数量。 +6. 相关指南。 + +--- + +## English + +This directory organizes the plugin contract by feature: what each numbered API offers today, which versions each capability, protocol, and format has, and which version Core, macOS, Windows, and the official SDKs support. The English [`contract/v1/README.md`](../contract/v1/README.md) is normative; this directory only navigates and compares. When they disagree the contract wins, and please file an issue. + +### Where to start + +| To find | Go to | +| --- | --- | +| What each of API-01–44 offers today and what is missing | [Plugin API catalog](api-catalog.md) | +| Plugin API levels, `requires` capability versions, process protocol versions, document format versions, and which version each host and SDK supports | [Capability and protocol versions](capabilities-and-versions.md) | +| When, and in which PR, each protocol, capability, and API changed | [Plugin protocol changelog](protocol-changelog.md) (Chinese only) | +| A feature's fields, versions, host status, contract sections, and examples | The feature pages below | +| Building a plugin step by step | [Guides](../guides/) | + +### Feature pages + +| Page | Covers | API numbers | Capabilities (`requires`) | +| --- | --- | --- | --- | +| [Manifest and packaging](domains/manifest.md) | `plugin.json` fields, plugin API levels, permissions and activation events, package layout | Not numbered | — | +| [Languages, language servers, run and test](domains/languages.md) | Languages, grammars, language servers, process launch, run and test commands | API-25 | `languages`, `languageServers`, `serverProcess`, `executionProviders` | +| [Plugin backend and plugin functions](domains/backend.md) | Backend process, function protocol, host methods, workspace contexts, files, credentials, terminals | API-01, 04–08, 20–22, 24, 37, 40, 41 | `backend`, `serverProcess` 1 | +| [Editor, documents, and previews](domains/editor.md) | Documents and selections, versioned edits, open and reveal, HTML preview, editor actions | API-09, 10, 15, 16, 27 | `backend` 2 | +| [UI](domains/ui.md) | Tool windows, commands, package web content, native controls, backend commands, message boxes | API-02, 03, 13, 14, 17 | `ui` | +| [Plugin settings](domains/settings.md) | Declarative settings, placement, scope, apply behavior, settings page actions | API-19 | `settings` | +| [Declarative build systems](domains/build-systems.md) | Build detection, models, command planning, project preparation roles | API-29 | `buildSystems` | +| [Build providers](domains/build-providers.md) | The `litheBuild` protocol: sync, tasks, tests, run and debug preparation, model inputs | API-29 | `buildProviders`, `buildModelInputs` | +| [Debug adapters](domains/debug-adapters.md) | DAP adapter declarations, launch, and transports | API-30 | `debugAdapters`, `serverProcess` 2 | +| [VCS providers](domains/vcs.md) | VCS provider processes and protocol | API-31 | `vcsProviders` | +| [Project generators](domains/project-generators.md) | New-project templates and steps | API-33 | `projectGenerators` | +| [Toolchains](domains/toolchains.md) | Tool definitions, executable candidates, toolchain providers, Core commands | API-23 | `toolDefinitions`, `toolchainProviders`, `executableResolution` | +| [Shortcut import](domains/keymap-import.md) | Providers that import shortcuts from other editors | Not numbered | `keymapImportProviders` | +| [Package trust](domains/package-trust.md) | Signature requirements, the signature file format, plugins bundled with the application | Not numbered | `packageTrust` | +| [Marketplace](domains/marketplace.md) | Index sources and format, availability, install review and confirmation | Not numbered | — | + +Planned APIs that have no methods yet (API-11, 12, 18, 26, 28, 32, 34–36, 38, 39, 42–44) are listed only in the [catalog](api-catalog.md#planned-apis) and have no feature page. + +### Layout of each feature page + +1. Purpose and a summary table: capability IDs, versions, Core commands or protocol methods, Windows status, official SDKs. +2. Versions compared: what each version adds, and whether Core, macOS, and Windows support it. +3. Detail tables for fields, methods, error codes, and so on. +4. Contract sections: links to the matching sections of `contract/v1/README.md`. +5. Examples and cases: files in `contract/v1/fixtures/` and their case counts. +6. Related guides. diff --git a/reference/api-catalog.md b/reference/api-catalog.md new file mode 100644 index 0000000..32af3ef --- /dev/null +++ b/reference/api-catalog.md @@ -0,0 +1,247 @@ +# 插件 API 总表(API-01–44) / Plugin API catalog (API-01–44) + +[中文](#中文) · [English](#english) + +## 中文 + +Lithe 的插件公共 API 按 [API 实施计划](https://github.com/1lck/Lithe-IDEA/blob/preview/docs/development/macos-plugin-api-plan.md)统一编号为 API-01 到 API-44。本页列出每个编号**现在能用什么**、**还没有什么**,并把能用的部分链接到契约章节和功能页。参数、返回值、错误码以 [`contract/v1/README.md`](../contract/v1/README.md) 为准;版本号见[能力与协议版本对照](capabilities-and-versions.md)。按主仓库最新 `preview` 核对,日期 2026-11-05。 + +读这张表前先知道三件事: + +- **宿主方法只有 macOS 实现。** 所有 `lithe/host/*` 方法由 Rust Core 校验、macOS 宿主执行。Windows 没有插件后端宿主,也不声明 `backend` 能力。清单里的声明式能力(语言、设置、界面等)在 Windows 上的支持见[版本对照页](capabilities-and-versions.md#能力版本requires)。 +- **运行时以 `getCapabilities` 为准。** 某个方法出现在本表,不代表当前宿主一定提供;后端调用前先查 `lithe/host/getCapabilities` 返回的 `services`。 +- **计划中的条目没有可用方法名。** 第二张表只描述要做的能力,不列候选方法名;在契约正式写入之前,不要按猜测的名字调用。 + +状态含义: + +| 状态 | 含义 | +| --- | --- | +| 已公开 | 方法已进入 Core 白名单并由 macOS 宿主分发,插件按权限可以调用 | +| 既有能力 | 在这次编号之前就已存在的契约能力,继续可用 | +| 部分完成 | 一部分已经可用,表中写明缺口 | +| 进行中 | 正在实现,尚不可用 | +| 未做 | 尚未开放;不代表 IDE 内部没有相关功能 | + +### 现在能用的 API + +| ID | 名称 | 状态 | 现在能用 | 尚未提供 | 功能页 | +| --- | --- | --- | --- | --- | --- | +| API-01 | 能力发现、激活与订阅 | 已公开 | [`getCapabilities`](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3)、`backend` 2 的 `onWorkspaceOpen` 激活、`subscriptions/dispose` | 新安装的后端仍需重启后才能启动 | [插件后端](domains/backend.md) | +| API-02 | 命令 | 部分完成 | [`ui` 4 不打开工具窗口的命令](../contract/v1/README.md#backend-only-commands-ui-4)和 `commands/didRun` 通知 | 运行期动态注册命令、带返回结果的执行 | [界面](domains/ui.md) | +| API-03 | 菜单、快捷键、上下文键、状态栏 | 部分完成 | 已有的命令放置位置(`ui.commands`) | 通用菜单、快捷键贡献、上下文键、状态栏 | [界面](domains/ui.md) | +| API-04 | 工作区与文档事件 | 部分完成 | `workspace/didChangeContexts`、`workspace/watch` 的文件事件 | 文档、选区、活动编辑器的变化事件 | [插件后端](domains/backend.md) | +| API-05 | 工作区上下文 | 已公开 | [`workspace/listContexts`、`getContext`、`getFolders`](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3),带 `contextID`/`generation` 与 `rootID` | — | [插件后端](domains/backend.md) | +| API-06 | 文件读取 | 已公开 | [`workspace/readDirectory`、`stat`、`readFile`](../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3);每次最多读 128 KiB | — | [插件后端](domains/backend.md) | +| API-07 | 文件监听 | 已公开 | `workspace/watch` 加 `subscriptions/notify`、`dispose`;上下文或会话结束时回收,溢出时要求重扫 | — | [插件后端](domains/backend.md) | +| API-08 | 搜索 | 部分完成 | `workspace/findFiles`、`findText`,分页、可取消 | 只搜磁盘内容,不含未保存的修改;匹配的精确范围目前是 `null` | [插件后端](domains/backend.md) | +| API-09 | 文档与选区读取 | 已公开 | `documents/list`、`documents/getSnapshot`、`editor/getActive`、`editor/getSelections`,包含未保存状态、版本、编码、换行符 | — | [编辑器](domains/editor.md) | +| API-10 | 版本化编辑 | 已公开 | `editor/applyEdits`、`replaceSelections`、`insertText`;校验版本,拒绝输入法组字中和只读文档,一批编辑是一次撤销 | — | [编辑器](domains/editor.md) | +| API-13 | 原生视图数据源 | 部分完成 | [`ui` 3 原生 Tree、List、Form](../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1) | 独立的后端数据源、懒加载 | [界面](domains/ui.md) | +| API-14 | 网页消息 | 部分完成 | [包内网页](../contract/v1/README.md#web-content-ui-2)的桥接、状态和生命周期 | 通用的页面双向消息 | [界面](domains/ui.md) | +| API-15 | 打开与定位 | 部分完成 | `editor/open`、`editor/reveal`,返回 `documentID` 和版本 | 传入范围时只把光标放到起点;不能替换预览标签 | [编辑器](domains/editor.md) | +| API-16 | HTML 预览与外部浏览器 | 已公开 | `preview/openHTML`(`editorAndPreview` 或 `previewOnly`)、`browser/openExternal`(HTTP(S) 地址或已保存的 HTML) | — | [编辑器](domains/editor.md) | +| API-17 | 消息、选择、输入 | 部分完成 | `window/showInformation`、`showWarning`、`showError`,带按钮和取消 | 通用的快速选择、输入框、经授权的文件句柄 | [界面](domains/ui.md) | +| API-19 | 设置 | 部分完成 | [声明式设置 `settings` 1、2](../contract/v1/README.md#plugin-settings-api-2) | 运行期读取、查看来源、修改设置和设置变化事件 | [设置](domains/settings.md) | +| API-20 | 存储 | 部分完成 | 插件级 `${stateDir}`、后端和工作区的可写目录(`stateDirectory`)、网页状态 | 统一的键值存储、配额 | [插件后端](domains/backend.md) | +| API-21 | 凭据 | 既有能力 | [协议 2 的 `credentials/get`、`set`、`delete`](../contract/v1/README.md#host-services-protocol-version-2),按插件隔离 | — | [插件后端](domains/backend.md) | +| API-22 | 输出、日志、进度 | 部分完成 | [插件函数的进度和取消](../contract/v1/README.md#progress);面向开发者的[结构化诊断日志 `litheLogging` 1](../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | 独立的输出面板、面向用户的日志和进度 API | [插件后端](domains/backend.md) | +| API-23 | 工具链与受管进程 | 部分完成 | 进程声明(`serverProcess`)、运行时解析、[共享可执行文件候选](../contract/v1/README.md#shared-executable-candidates-executableresolution-12)、工具定义、工具链提供者(macOS) | 后端可调用的通用工具链和进程服务;保存固定(pin)、`toolchainConsumers`、Windows 支持 | [工具链](domains/toolchains.md) | +| API-24 | 终端 | 部分完成 | [插件自有原生终端](../contract/v1/README.md#plugin-owned-native-terminals-function-protocol-3) `terminal/create`、`show`、`close`、`reopen`、`dispose`、`onExit`(macOS、本机工作区) | 向终端发送文本、读取终端输出;Windows | [插件后端](domains/backend.md) | +| API-25 | 语言、语法、LSP | 部分完成 | [语言](../contract/v1/README.md#languages)与[语言服务器](../contract/v1/README.md#language-servers);[`languages` 2 的包内 Monarch 语法和编辑器配置](../contract/v1/README.md#grammars-and-editor-configuration-languages-2) | 运行期注册或替换高亮提供者、独立的语言语义提供者 | [语言](domains/languages.md) | +| API-27 | 编辑器动作、主题、装饰 | 部分完成 | [独立编辑器动作 `editor/registerActions`](../contract/v1/README.md#independent-editor-actions-protocol-version-3),绑定普通函数和文件范围,不依赖 LSP、构建或运行 | 主题、代码片段、其他装饰 | [编辑器](domains/editor.md) | +| API-29 | 构建、任务、测试、运行准备 | 部分完成 | [`buildSystems` 1–4](../contract/v1/README.md#api-level-3-build-systems)、[`buildProviders` 1–11](../contract/v1/README.md#build-providers)、`buildModelInputs` 1 | 开发指南还不完整 | [声明式构建](domains/build-systems.md)、[构建提供者](domains/build-providers.md) | +| API-30 | 调试适配器 | 既有能力 | [`debugAdapters` 1、2](../contract/v1/README.md#api-level-4-debug-adapters) 与宿主的 DAP 会话 | — | [调试适配器](domains/debug-adapters.md) | +| API-31 | VCS 提供者 | 既有能力 | [`vcsProviders` 1](../contract/v1/README.md#api-level-3-vcs-providers) | 不含内置 Git 的公共读取(见 API-42) | [VCS](domains/vcs.md) | +| API-33 | 项目生成器 | 既有能力 | [`projectGenerators` 1](../contract/v1/README.md#project-generators) | — | [项目生成器](domains/project-generators.md) | +| API-37 | 环境信息与语言包 | 部分完成 | `getCapabilities` 返回协议版本、可用方法、限制和后端运行位置(`backendAuthority`) | 语言包、环境和主题变化事件 | [插件后端](domains/backend.md) | +| API-40 | 工作区 authority | 部分完成 | 工作区上下文带 `local` 或 `remote`;文件和编辑器方法对远程上下文明确返回 `plugin.host.authorityUnsupported` | 远程执行 | [插件后端](domains/backend.md) | +| API-41 | 激活事件与启动预算 | 部分完成 | [`onWorkspaceOpen`](../contract/v1/README.md#permissions-and-activation)、`onCommand`、后台两路并发启动、`initialize` 超时、有界通知队列 | 按工作区内容匹配激活、全局冷启动预算 | [插件后端](domains/backend.md) | + +### 计划中的 API + +| ID | 名称 | 状态 | 计划提供 | +| --- | --- | --- | --- | +| API-11 | 保存与文件变更 | 未做 | 保存、另存为、关闭,以及工作区文件的创建、改名、删除 | +| API-12 | 多文件编辑、虚拟文档、diff | 未做 | 跨文件编辑、按 URI scheme 提供的只读虚拟文档、对比视图 | +| API-18 | 剪贴板、Finder | 未做 | 剪贴板读写、在 Finder 中显示 | +| API-26 | 诊断集合 | 未做 | 后端推送的诊断集合,带文档版本;现有的 LSP 诊断不属于这个 API | +| API-28 | 自定义编辑器 | 未做 | 插件自有文档的编辑器,含保存、撤销、恢复 | +| API-32 | 服务 | 未做 | 通用服务提供者(仍是提案) | +| API-34 | IDE/AI/MCP | 未做 | 插件可用的 IDE、AI、MCP 能力与授权 | +| API-35 | 插件间调用与依赖 | 进行中 | 第一阶段:经 Core 中转调用另一个插件的函数(不是安装依赖);之后是依赖与导出、版本求解 | +| API-36 | WASM | 未做 | WASM/WASI 运行时 | +| API-38 | 受管网络 | 未做 | 宿主代为执行的网络请求和下载 | +| API-39 | 身份验证 | 未做 | 身份验证会话和提供者 | +| API-42 | 内置 Git 只读快照 | 未做 | 内置 Git 状态的只读快照和变化事件 | +| API-43 | 保存前事件 | 未做 | 保存前事件,以及有时限的保存前编辑 | +| API-44 | Agent 侧贡献 | 未做 | 插件贡献 Skills、Hooks、MCP,并单独审查授权 | + +清单、市场、包信任、快捷键导入是安装和打包层面的契约,不在 API-01–44 编号内,见各自的功能页:[清单](domains/manifest.md)、[市场](domains/marketplace.md)、[包信任](domains/package-trust.md)、[快捷键导入](domains/keymap-import.md)。 + +### 已公开的宿主方法 + +后端向宿主发送的请求,前缀都是 `lithe/host/`。“协议”列是最低的 `litheFunctions` 版本。除 `getCapabilities`、`subscriptions/dispose`、`workspace/listContexts` 外,协议 3 的方法都要带 `context: {contextID, generation}`。除 `credentials.store` 外,下表的权限都要求清单声明 `backend` 2,否则安装时拒绝。 + +| 方法 | 权限 | 协议 | API | +| --- | --- | --- | --- | +| `credentials/get`、`credentials/set`、`credentials/delete` | `credentials.store` | 2 | API-21 | +| `getCapabilities`、`subscriptions/dispose` | 每个插件会话都有 | 3 | API-01、API-37 | +| `workspace/listContexts`、`workspace/getContext`、`workspace/getFolders` | `workspace.metadata` | 3 | API-05 | +| `workspace/readDirectory`、`workspace/stat`、`workspace/readFile` | `workspace.files.read` | 3 | API-06 | +| `workspace/watch` | `workspace.files.watch` | 3 | API-07 | +| `workspace/findFiles`、`workspace/findText` | `workspace.search` | 3 | API-08 | +| `documents/list`、`documents/getSnapshot`、`editor/getActive`、`editor/getSelections` | `documents.read` | 3 | API-09 | +| `editor/applyEdits`、`editor/replaceSelections`、`editor/insertText` | `editor.edit` | 3 | API-10 | +| `editor/open`、`editor/reveal` | `editor.open` | 3 | API-15 | +| `preview/openHTML` | `preview.open` | 3 | API-16 | +| `browser/openExternal` | `browser.open` | 3 | API-16 | +| `window/showInformation`、`window/showWarning`、`window/showError` | `window.messages` | 3 | API-17 | +| `editor/registerActions` | `editor.actions`(同时需要 `documents.read`) | 3 | API-27 | +| `terminal/create`、`show`、`close`、`reopen`、`dispose`、`onExit` | `terminal.create` | 3 | API-24 | + +宿主发给后端的通知(后端不回复): + +| 通知 | 授予条件 | API | +| --- | --- | --- | +| `lithe/host/workspace/didChangeContexts` | `workspace.metadata` | API-04、API-05 | +| `lithe/host/subscriptions/notify` | 每个插件会话都有 | API-07、API-24 | +| `lithe/host/commands/didRun` | 每个插件会话都有 | API-02 | + +### 限制 + +| 项目 | 上限 | +| --- | --- | +| 单次文件读取、文档快照 | 128 KiB 源文本或字节 | +| 一批编辑或选区 | 1,000 项 | +| 目录分页 | 默认 200,最多 1,000 | +| 搜索分页 | 默认 100,最多 200(搜索总结果 200) | +| 机器请求的宿主时限 | 30 秒(等待用户选择的消息不计) | +| 窗口消息 | 每个插件每 10 秒 5 条,每个插件最多 3 条待回答,每个窗口最多 10 条;正文 2,000 字符,最多 3 个按钮,每个 40 字符 | +| 未回答的宿主请求 | 每个会话 16 个 | +| 通知队列 | 每个会话 128 条 | +| 插件自有终端 | 每个插件最多保留 8 个句柄 | + +失败时的错误码和处理方式见契约的 [Outcomes and error codes](../contract/v1/README.md#outcomes-and-error-codes) 与 [Protocol 3 宿主失败码表](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3)。取消或超时**不保证回滚**:编辑可能已经生效,不要自动重放。 + +--- + +## English + +Lithe numbers its public plugin APIs API-01 to API-44 in the [API implementation plan](https://github.com/1lck/Lithe-IDEA/blob/preview/docs/development/macos-plugin-api-plan.md). This page lists, for each ID, **what is usable now** and **what is still missing**, and links the usable parts to contract sections and domain pages. [`contract/v1/README.md`](../contract/v1/README.md) is normative for params, results, and error codes; versions are in [Capability and protocol versions](capabilities-and-versions.md). Checked against the latest `preview` of the Lithe repository on 2026-11-05. + +Three things to know before reading the tables: + +- **Host methods are implemented on macOS only.** Rust Core checks every `lithe/host/*` method and the macOS host executes it. Windows has no plugin backend host and does not advertise the `backend` capability. For declarative manifest capabilities (languages, settings, UI, and so on) on Windows, see the [version page](capabilities-and-versions.md#capability-versions-requires). +- **At runtime, `getCapabilities` decides.** A method listed here is not necessarily offered by the current host; check `services` from `lithe/host/getCapabilities` before calling it. +- **Planned entries have no usable method names.** The second table only describes the capability to come and lists no candidate method names; until the contract defines them, do not call guessed names. + +Status meanings: + +| Status | Meaning | +| --- | --- | +| Public | The method is on Core's allowlist and dispatched by the macOS host; a plugin with the permission can call it | +| Existing | A contract capability that predates the numbering and stays available | +| Partial | Part of it is usable; the table names the gaps | +| In progress | Being implemented, not usable yet | +| Not started | Not open yet; this does not mean the IDE lacks the feature internally | + +### APIs usable now + +| ID | Name | Status | Usable now | Not yet | Domain page | +| --- | --- | --- | --- | --- | --- | +| API-01 | Capability discovery, activation, subscriptions | Public | [`getCapabilities`](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3), `onWorkspaceOpen` activation of `backend` 2, `subscriptions/dispose` | A newly installed backend still needs a restart before it can start | [Plugin backend](domains/backend.md) | +| API-02 | Commands | Partial | [`ui` 4 commands without a tool window](../contract/v1/README.md#backend-only-commands-ui-4) and the `commands/didRun` notification | Registering commands at runtime, result-bearing execution | [UI](domains/ui.md) | +| API-03 | Menus, keybindings, context keys, status bar | Partial | Existing command placements (`ui.commands`) | General menus, keybinding contributions, context keys, status bar | [UI](domains/ui.md) | +| API-04 | Workspace and document events | Partial | `workspace/didChangeContexts`, file events from `workspace/watch` | Document, selection, and active editor change events | [Plugin backend](domains/backend.md) | +| API-05 | Workspace contexts | Public | [`workspace/listContexts`, `getContext`, `getFolders`](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) with `contextID`/`generation` and `rootID` | — | [Plugin backend](domains/backend.md) | +| API-06 | File reads | Public | [`workspace/readDirectory`, `stat`, `readFile`](../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3); at most 128 KiB per read | — | [Plugin backend](domains/backend.md) | +| API-07 | File watching | Public | `workspace/watch` with `subscriptions/notify` and `dispose`; reclaimed when the context or session ends, with a rescan request on overflow | — | [Plugin backend](domains/backend.md) | +| API-08 | Search | Partial | `workspace/findFiles` and `findText`, paged and cancellable | Searches disk content only, not unsaved changes; the exact match range is `null` for now | [Plugin backend](domains/backend.md) | +| API-09 | Documents and selections | Public | `documents/list`, `documents/getSnapshot`, `editor/getActive`, `editor/getSelections`, with dirty state, version, encoding, and line ending | — | [Editor](domains/editor.md) | +| API-10 | Versioned edits | Public | `editor/applyEdits`, `replaceSelections`, `insertText`; version checks, refusal during IME composition and in read-only documents, one undo step per batch | — | [Editor](domains/editor.md) | +| API-13 | Native view data sources | Partial | [`ui` 3 native Tree, List, Form](../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1) | An independent backend data source, lazy loading | [UI](domains/ui.md) | +| API-14 | Webview messaging | Partial | The [package web page](../contract/v1/README.md#web-content-ui-2) bridge, state, and lifecycle | General two-way page messaging | [UI](domains/ui.md) | +| API-15 | Open and reveal | Partial | `editor/open` and `editor/reveal`, returning `documentID` and version | A range only places the caret at its start; no preview-tab replacement | [Editor](domains/editor.md) | +| API-16 | HTML preview and external browser | Public | `preview/openHTML` (`editorAndPreview` or `previewOnly`), `browser/openExternal` (HTTP(S) URLs or saved HTML) | — | [Editor](domains/editor.md) | +| API-17 | Messages, quick pick, input | Partial | `window/showInformation`, `showWarning`, `showError` with buttons and dismissal | General quick pick, input box, authorized file handles | [UI](domains/ui.md) | +| API-19 | Settings | Partial | [Declarative settings `settings` 1 and 2](../contract/v1/README.md#plugin-settings-api-2) | Reading, inspecting the source of, and updating settings at runtime, and settings change events | [Settings](domains/settings.md) | +| API-20 | Storage | Partial | The plugin-wide `${stateDir}`, writable backend and workspace directories (`stateDirectory`), web page state | A unified key-value store, quotas | [Plugin backend](domains/backend.md) | +| API-21 | Credentials | Existing | [Protocol 2 `credentials/get`, `set`, `delete`](../contract/v1/README.md#host-services-protocol-version-2), isolated per plugin | — | [Plugin backend](domains/backend.md) | +| API-22 | Output, log, progress | Partial | [Plugin function progress and cancellation](../contract/v1/README.md#progress); developer-facing [structured diagnostics `litheLogging` 1](../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | A dedicated output panel, user-facing log and progress APIs | [Plugin backend](domains/backend.md) | +| API-23 | Toolchains and managed processes | Partial | Process declarations (`serverProcess`), runtime resolution, [shared executable candidates](../contract/v1/README.md#shared-executable-candidates-executableresolution-12), tool definitions, toolchain providers (macOS) | A general toolchain and process service backends can call; saving pins, `toolchainConsumers`, Windows support | [Toolchains](domains/toolchains.md) | +| API-24 | Terminals | Partial | [Plugin-owned native terminals](../contract/v1/README.md#plugin-owned-native-terminals-function-protocol-3) `terminal/create`, `show`, `close`, `reopen`, `dispose`, `onExit` (macOS, local workspaces) | Sending text to a terminal, reading its output; Windows | [Plugin backend](domains/backend.md) | +| API-25 | Languages, grammars, LSP | Partial | [Languages](../contract/v1/README.md#languages) and [language servers](../contract/v1/README.md#language-servers); [packaged Monarch grammars and editor configuration in `languages` 2](../contract/v1/README.md#grammars-and-editor-configuration-languages-2) | Registering or replacing highlighting providers at runtime, independent language semantic providers | [Languages](domains/languages.md) | +| API-27 | Editor actions, themes, decorations | Partial | [Independent editor actions `editor/registerActions`](../contract/v1/README.md#independent-editor-actions-protocol-version-3), binding ordinary functions to file ranges without LSP, build, or run | Themes, snippets, other decorations | [Editor](domains/editor.md) | +| API-29 | Build, tasks, tests, run preparation | Partial | [`buildSystems` 1–4](../contract/v1/README.md#api-level-3-build-systems), [`buildProviders` 1–11](../contract/v1/README.md#build-providers), `buildModelInputs` 1 | The developer guides are incomplete | [Build systems](domains/build-systems.md), [Build providers](domains/build-providers.md) | +| API-30 | Debug adapters | Existing | [`debugAdapters` 1 and 2](../contract/v1/README.md#api-level-4-debug-adapters) and the host's DAP sessions | — | [Debug adapters](domains/debug-adapters.md) | +| API-31 | VCS providers | Existing | [`vcsProviders` 1](../contract/v1/README.md#api-level-3-vcs-providers) | Public reads of built-in Git are not included (see API-42) | [VCS](domains/vcs.md) | +| API-33 | Project generators | Existing | [`projectGenerators` 1](../contract/v1/README.md#project-generators) | — | [Project generators](domains/project-generators.md) | +| API-37 | Environment info and l10n | Partial | `getCapabilities` returns the protocol version, usable methods, limits, and where the backend runs (`backendAuthority`) | Language packs, environment and theme change events | [Plugin backend](domains/backend.md) | +| API-40 | Workspace authority | Partial | Workspace contexts carry `local` or `remote`; file and editor methods refuse a remote context with `plugin.host.authorityUnsupported` | Remote execution | [Plugin backend](domains/backend.md) | +| API-41 | Activation events and startup budget | Partial | [`onWorkspaceOpen`](../contract/v1/README.md#permissions-and-activation), `onCommand`, two concurrent background starts, `initialize` timeout, bounded notification queues | Activation by workspace content, a global cold-start budget | [Plugin backend](domains/backend.md) | + +### Planned APIs + +| ID | Name | Status | Planned | +| --- | --- | --- | --- | +| API-11 | Save and file operations | Not started | Save, save as, close, and creating, renaming, and deleting workspace files | +| API-12 | Multi-file edits, content providers, diff | Not started | Cross-file edits, read-only virtual documents provided per URI scheme, a diff view | +| API-18 | Clipboard and Finder | Not started | Reading and writing the clipboard, revealing in Finder | +| API-26 | Diagnostics collections | Not started | Diagnostics collections pushed by a backend, with document versions; existing LSP diagnostics are not this API | +| API-28 | Custom editors | Not started | Editors for plugin-owned documents, with save, undo, and restore | +| API-32 | Services | Not started | General service providers (still a proposal) | +| API-34 | IDE, AI, MCP | Not started | IDE, AI, and MCP capabilities for plugins, with authorization | +| API-35 | Cross-plugin calls and dependencies | In progress | Phase one: calling another plugin's function through Core (not an install dependency); then dependencies and exports, version solving | +| API-36 | WASM | Not started | A WASM/WASI runtime | +| API-38 | Managed network | Not started | Network requests and downloads run by the host | +| API-39 | Authentication | Not started | Authentication sessions and providers | +| API-42 | Built-in Git read-only snapshot | Not started | A read-only snapshot of built-in Git state and change events | +| API-43 | Will-save events | Not started | Will-save events and time-bounded edits before save | +| API-44 | Agent contributions | Not started | Skills, hooks, and MCP contributed by plugins, with separate authorization review | + +The manifest, marketplace, package trust, and shortcut import are installation and packaging contracts outside API-01–44; see their domain pages: [Manifest](domains/manifest.md), [Marketplace](domains/marketplace.md), [Package trust](domains/package-trust.md), [Shortcut import](domains/keymap-import.md). + +### Public host methods + +Requests a backend sends to the host, all prefixed `lithe/host/`. The “Protocol” column is the lowest `litheFunctions` version. Apart from `getCapabilities`, `subscriptions/dispose`, and `workspace/listContexts`, every protocol 3 method takes `context: {contextID, generation}`. Apart from `credentials.store`, every permission below requires the manifest to declare `backend` 2, or installation refuses it. + +| Method | Permission | Protocol | API | +| --- | --- | --- | --- | +| `credentials/get`, `credentials/set`, `credentials/delete` | `credentials.store` | 2 | API-21 | +| `getCapabilities`, `subscriptions/dispose` | Every plugin-owned session | 3 | API-01, API-37 | +| `workspace/listContexts`, `workspace/getContext`, `workspace/getFolders` | `workspace.metadata` | 3 | API-05 | +| `workspace/readDirectory`, `workspace/stat`, `workspace/readFile` | `workspace.files.read` | 3 | API-06 | +| `workspace/watch` | `workspace.files.watch` | 3 | API-07 | +| `workspace/findFiles`, `workspace/findText` | `workspace.search` | 3 | API-08 | +| `documents/list`, `documents/getSnapshot`, `editor/getActive`, `editor/getSelections` | `documents.read` | 3 | API-09 | +| `editor/applyEdits`, `editor/replaceSelections`, `editor/insertText` | `editor.edit` | 3 | API-10 | +| `editor/open`, `editor/reveal` | `editor.open` | 3 | API-15 | +| `preview/openHTML` | `preview.open` | 3 | API-16 | +| `browser/openExternal` | `browser.open` | 3 | API-16 | +| `window/showInformation`, `window/showWarning`, `window/showError` | `window.messages` | 3 | API-17 | +| `editor/registerActions` | `editor.actions` (also needs `documents.read`) | 3 | API-27 | +| `terminal/create`, `show`, `close`, `reopen`, `dispose`, `onExit` | `terminal.create` | 3 | API-24 | + +Notifications the host sends to a backend (the backend answers none): + +| Notification | Granted by | API | +| --- | --- | --- | +| `lithe/host/workspace/didChangeContexts` | `workspace.metadata` | API-04, API-05 | +| `lithe/host/subscriptions/notify` | Every plugin-owned session | API-07, API-24 | +| `lithe/host/commands/didRun` | Every plugin-owned session | API-02 | + +### Limits + +| Item | Limit | +| --- | --- | +| One file read or document snapshot | 128 KiB of source text or bytes | +| One batch of edits or selections | 1,000 entries | +| Directory pages | 200 by default, at most 1,000 | +| Search pages | 100 by default, at most 200 (200 search results in total) | +| Host deadline for machine requests | 30 seconds (messages waiting on the user are exempt) | +| Window messages | 5 per plugin per 10 seconds, at most 3 pending per plugin and 10 per window; 2,000 text characters, at most 3 buttons of 40 characters | +| Unanswered host requests | 16 per session | +| Notification queue | 128 entries per session | +| Plugin-owned terminals | At most 8 retained handles per plugin | + +For failure codes and how to handle them, see the contract's [Outcomes and error codes](../contract/v1/README.md#outcomes-and-error-codes) and the [protocol 3 host failure table](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3). Cancellation or timeout **does not promise rollback**: an edit may already have taken effect, so do not replay it automatically. diff --git a/reference/capabilities-and-versions.md b/reference/capabilities-and-versions.md new file mode 100644 index 0000000..e018dd0 --- /dev/null +++ b/reference/capabilities-and-versions.md @@ -0,0 +1,319 @@ +# 能力与协议版本对照 / Capability and protocol versions + +[中文](#中文) · [English](#english) + +## 中文 + +本页把插件会碰到的所有版本号放在一起:每个能力、每个进程协议、每个文档格式的**全部版本**,以及 Rust Core、macOS、Windows、三个官方 SDK 各自支持到哪一版。规范以 [`contract/v1/README.md`](../contract/v1/README.md) 为准;本页与契约不一致时以契约为准,并请提 Issue。本页以主仓库最新 `preview` 为基准:Core、macOS、Windows 列按 `preview` 上的代码核对(2026-11-05,`644afdaac`),契约链接对应 [`contract.lock.json`](../contract.lock.json) 锁定的修订。Lithe 稳定版可能还没有其中的部分版本,发布插件前以目标版本的发行说明为准。 + +### 四类版本号 + +| 版本号 | 写在哪里 | 谁检查 | 不满足时 | +| --- | --- | --- | --- | +| 插件 API 级别 `pluginAPIVersion` | `plugin.json` | Core | 安装审查拒绝 | +| 能力版本 `requires.<能力>` | `plugin.json`(级别 3 起必填) | Core 的支持表,再和宿主的 `hostCapabilities` 取交集 | `plugin.api.unsupported`,`field` 为 `requires.<能力>`;市场不提供这个插件版本 | +| 进程协议版本 `experimental.lithe*` | 不写进清单;进程启动时在 LSP `initialize` 里协商 | 宿主给出版本列表,后端从中选一个 | 该协议不启用,例如不向这个进程发构建请求 | +| 文档格式版本 | 各文档自己的 `schemaVersion` 或 `version` 字段 | Core | 解析失败 | + +能力版本决定**能不能安装**;协议版本决定**进程启动后能说什么**。两者分开演进:后端支持一个新的协议版本,不需要提高清单里的任何版本号。 + +发布规则(契约 [API levels](../contract/v1/README.md#api-levels) 一节): + +- 级别和版本只增不改,已发布的版本不改变含义,宿主一直支持旧版本。 +- 插件只声明自己用到的**最低**级别和每个能力的**最低**版本。没用到新内容就提高版本,只会让旧宿主装不上。 +- 提高级别或能力版本必须发新的插件版本;市场索引保留上一个旧要求的版本,旧宿主仍有版本可装。 +- 支持某个级别或版本的宿主先发布,声明它的插件后发布。 + +### 插件 API 级别 + +| 级别 | 新增 | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| `1` | 语言、`packaged` 和 `systemCommand` 语言服务器、`node` 和 `go` 运行时依赖、运行和测试提供者 | ✅ | ✅ | ✅ | +| `2` | 用包内或已发现运行时启动(`runtime`)、宿主集成、`java` 运行时、启动模板、`initializationOptions`、插件设置、第一阶段工作台 UI(`ui`) | ✅ | ✅ | 由 Core 校验¹ | +| `3` | 按能力声明版本(`requires`)、`buildSystems`、`vcsProviders` | ✅ | ✅ | 按下表的 Windows 列准入 | +| `4` | `debugAdapters`、`serverProcess` 2(`runSelectedRuntime`) | ✅ | ✅ | 按下表的 Windows 列准入 | + +- Core:`SUPPORTED_PLUGIN_API_VERSIONS = [1, 2, 3, 4]`([`declarative.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/plugins/declarative.rs))。macOS:`PluginManifest.supportedDeclarativeAPIVersions = [1, 2, 3, 4]`。 +- 级别 1、2 不看宿主能力表;级别 3 起,安装和市场筛选使用宿主能力表。 +- ¹ Windows 通过 Core 校验级别 2 的清单。按契约 [Current Windows availability](../contract/v1/README.md#current-windows-availability),Windows 声明 `ui` 1、2(工具窗口、命令、包内网页),不声明 3、4;级别 3 起的插件按宿主能力表准入。市场的 `plugins.marketplace.validateIndex` 目前只在 `darwin-*` 平台把级别 2 的版本标为可用,同一版本在 Windows 上显示 `plugin.index.apiUnsupported`。 + +### 能力版本(`requires`) + +每一行是一个能力的一个版本。Core 列是 Core 能校验的版本([`capabilities.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/plugins/capabilities.rs) 的 `SUPPORTED_CAPABILITIES`),macOS 和 Windows 列是宿主真正能运行并在安装时声明的版本([macOS `PluginManifest.hostCapabilities`](https://github.com/1lck/Lithe-IDEA/blob/preview/macos/Sources/LitheModuleAPI/Plugins/PluginTypes.swift)、[Windows `host_capabilities()`](https://github.com/1lck/Lithe-IDEA/blob/preview/windows/tauri/src-tauri/src/plugin_host/mod.rs))。Core 能校验不等于宿主能运行。 + +| 能力 | 版本 | 这一版新增 | Core | macOS | Windows | 功能页 | +| --- | --- | --- | --- | --- | --- | --- | +| `languages` | 1 | `languages[]`:语言 ID、文件扩展名等 | ✅ | ✅ | ✅ | [语言](domains/languages.md) | +| `languages` | 2 | [`grammar` 与 `configuration`](../contract/v1/README.md#grammars-and-editor-configuration-languages-2):包内 Monarch 语法和编辑器配置 | ✅ | ✅ | — | [语言](domains/languages.md) | +| `languageServers` | 1 | `languageServers[]`(启动字段之外的部分) | ✅ | ✅ | ✅ | [语言](domains/languages.md) | +| `languageServers` | 2 | [`workspaceConfiguration`](../contract/v1/README.md#workspace-configuration-languageservers-2):向语言服务器推送工作区配置 | ✅ | ✅ | ✅ | [语言](domains/languages.md) | +| `serverProcess` | 1 | 进程启动字段:`launch`、`arguments`、`validationArguments`、`runtimeDependencies`、`initializationOptions`;语言服务器、自有进程的构建提供者、VCS 提供者、插件后端共用 | ✅ | ✅ | ✅ | [语言](domains/languages.md) | +| `serverProcess` | 2 | `runSelectedRuntime`,供[调试适配器](../contract/v1/README.md#api-level-4-debug-adapters)使用;需要级别 4 | ✅ | — | — | [调试适配器](domains/debug-adapters.md) | +| `executionProviders` | 1 | `executionProviders[]`:运行和测试提供者 | ✅ | ✅ | — | [语言](domains/languages.md) | +| `settings` | 1 | `settings[]` 与 `settingActions[]` | ✅ | ✅ | ✅ | [设置](domains/settings.md) | +| `settings` | 2 | [放置位置、作用域、生效方式、页面按钮](../contract/v1/README.md#settings-v2-placement-scope-effect-and-page-buttons) | ✅ | ✅ | ✅ | [设置](domains/settings.md) | +| `ui` | 1 | [第一阶段工作台 UI](../contract/v1/README.md#api-level-2-first-stage-host-owned-workbench-ui):宿主渲染的工具窗口和命令 | ✅ | ✅ | ✅ | [界面](domains/ui.md) | +| `ui` | 2 | [包内网页](../contract/v1/README.md#web-content-ui-2) | ✅ | ✅ | ✅ | [界面](domains/ui.md) | +| `ui` | 3 | [宿主渲染的原生控件](../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1)(原生 UI 文档 schema 1) | ✅ | ✅ | — | [界面](domains/ui.md) | +| `ui` | 4 | [不打开工具窗口的后端命令](../contract/v1/README.md#backend-only-commands-ui-4),需要 `backend` 2 | ✅ | ✅ | — | [插件后端](domains/backend.md) | +| `buildSystems` | 1 | [声明式构建](../contract/v1/README.md#api-level-3-build-systems):检测、选项、项目模型、任务、运行入口、测试、诊断、依赖、同步 | ✅ | ✅ | — | [声明式构建](domains/build-systems.md) | +| `buildSystems` | 2 | 可执行文件设置适配 | ✅ | ✅ | — | [声明式构建](domains/build-systems.md) | +| `buildSystems` | 3 | 实例级选项 | ✅ | ✅ | — | [声明式构建](domains/build-systems.md) | +| `buildSystems` | 4 | [声明式项目准备角色](../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | ✅ | ✅ | — | [声明式构建](domains/build-systems.md) | +| `buildProviders` | 1 | [构建提供者](../contract/v1/README.md#build-providers):后端回答模型和依赖请求,可以是自有进程或共享语言服务器 | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 2 | [任务执行](../contract/v1/README.md#provider-task-execution-buildproviders-2) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 3 | [测试执行](../contract/v1/README.md#provider-test-execution-buildproviders-3)与[测试发现](../contract/v1/README.md#test-discovery) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 4 | [应用运行准备](../contract/v1/README.md#provider-run-preparation-buildproviders-4) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 5 | [原生调试准备](../contract/v1/README.md#native-debug-preparation-version-5) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 6 | [只读模型输入](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 7 | [基于真实来源的原生配置](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 8 | [内联测试报告](../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 9 | [不透明语义测试选择器](../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 10 | [类型化运行选项与服务/任务运行准备](../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildProviders` | 11 | [注册表连接调试](../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `buildModelInputs` | 1 | [已启用工作区插件之间共享公开模型](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1)(可选) | ✅ | ✅ | — | [构建提供者](domains/build-providers.md) | +| `vcsProviders` | 1 | [`vcsProviders[]`](../contract/v1/README.md#api-level-3-vcs-providers) | ✅ | ✅ | — | [VCS](domains/vcs.md) | +| `debugAdapters` | 1 | [`debugAdapters[]`](../contract/v1/README.md#api-level-4-debug-adapters);需要级别 4 | ✅ | ✅ | — | [调试适配器](domains/debug-adapters.md) | +| `debugAdapters` | 2 | 调试目标的参数和环境变量 | ✅ | ✅ | — | [调试适配器](domains/debug-adapters.md) | +| `projectGenerators` | 1 | [`projectGenerators[]`](../contract/v1/README.md#project-generators):新建项目窗口里的条目 | ✅ | ✅ | — | [项目生成器](domains/project-generators.md) | +| `backend` | 1 | [插件后端](../contract/v1/README.md#plugin-backend):插件自己的长驻进程,从插件页面启动 | ✅ | ✅ | — | [插件后端](domains/backend.md) | +| `backend` | 2 | [无页面启动和工作区上下文](../contract/v1/README.md#backend-2-workspace-contexts) | ✅ | ✅ | — | [插件后端](domains/backend.md) | +| `packageTrust` | 1 | `vendor.bundledSignatureRequirement`:随宿主发布的发布者签名插件副本按来源信任 | ✅ | ✅ | — | [包信任](domains/package-trust.md) | +| `toolDefinitions` | 1 | [工具身份、布局、发现和四层候选](../contract/v1/toolchains/README.md) | ✅ | ✅(只读设置、构建与运行工具) | — | [工具链](domains/toolchains.md) | +| `toolchainProviders` | 1 | [工具链提供者声明和按权限校验应答](../contract/v1/toolchains/README.md) | ✅ | ✅(只读设置、构建与运行工具) | — | [工具链](domains/toolchains.md) | +| `executableResolution` | 1 | [共享可执行文件候选](../contract/v1/README.md#shared-executable-candidates-executableresolution-12):候选排序和严格选择 | ✅ | ✅ | — | [工具链](domains/toolchains.md) | +| `executableResolution` | 2 | 工具定义候选、项目/子项目工具链注册表绑定;不改变已有构建候选集 | ✅ | ✅(只读设置、构建与运行工具) | — | [工具链](domains/toolchains.md) | +| `keymapImportProviders` | 1 | [快捷键导入提供者](../contract/v1/README.md#shortcut-import-providers-core-foundation) | ✅ | ✅ | — | [快捷键导入](domains/keymap-import.md) | + +`requires` 必须**恰好**列出用到的能力:漏一个或多列一个都是 `plugin.manifest.invalid`。每个语言服务器、每个自有进程的构建提供者、每个 VCS 提供者和 `backend` 都要带上 `serverProcess`。 + +### 进程协议版本 + +这些协议在进程启动时协商,不写进清单。宿主在 `initialize` 的 `capabilities.experimental.<协议>` 里给出 `{ "versions": [...] }`,后端回答 `{ "version": N }`。回答了宿主没给出的版本,或格式不对,该协议整个不启用。 + +| 协议 | 版本 | 这一版新增 | 宿主向谁提供 | 契约章节 | +| --- | --- | --- | --- | --- | +| `litheFunctions` | 1 | 插件函数:注册、撤回、调用、结果、进度、取消;不提供宿主服务,所有 `lithe/host/*` 请求返回 `plugin.host.unavailable` | 插件拥有的会话 | [Plugin functions](../contract/v1/README.md#plugin-functions) | +| `litheFunctions` | 2 | 宿主服务:凭据 `credentials/get`、`set`、`delete` | 同上 | [Host services](../contract/v1/README.md#host-services-protocol-version-2) | +| `litheFunctions` | 3 | 工作区上下文与能力发现、宿主通知、公共文档/文件/窗口方法、独立编辑器动作、插件自有原生终端 | 同上 | [Protocol 3](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | +| `litheBuild` | 1 | `lithe/build/model` 与 `lithe/build/dependencies` | 构建提供者 | [Build providers](../contract/v1/README.md#build-providers) | +| `litheBuild` | 2 | 类型化任务执行(`lithe/build/execute`) | 同上 | [Task execution](../contract/v1/README.md#provider-task-execution-buildproviders-2) | +| `litheBuild` | 3 | 类型化测试执行和发现(`lithe/build/test`、`lithe/build/discoverTests`) | 同上 | [Test execution](../contract/v1/README.md#provider-test-execution-buildproviders-3) | +| `litheBuild` | 4 | 应用运行准备(`lithe/build/prepareRun`) | 同上 | [Run preparation](../contract/v1/README.md#provider-run-preparation-buildproviders-4) | +| `litheBuild` | 5 | 原生调试准备(`lithe/build/prepareDebug`) | 同上 | [Debug preparation](../contract/v1/README.md#native-debug-preparation-version-5) | +| `litheBuild` | 6 | 接收宿主显式声明的只读模型输入 | 同上 | [Model inputs](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | +| `litheBuild` | 7 | 基于真实来源的原生配置 | 同上 | [Native configuration](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | +| `litheBuild` | 8 | 内联测试报告 | 同上 | [Inline test reports](../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | +| `litheBuild` | 9 | 不透明语义测试选择器 | 同上 | [Test selectors](../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | +| `litheBuild` | 10 | 类型化运行选项与服务/任务运行准备 | 同上 | [Run options](../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | +| `litheBuild` | 11 | 注册表连接调试 | 同上 | [Connection debugging](../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | +| `litheVcs` | 1 | VCS 提供者的能力和请求 | VCS 提供者 | [VCS providers](../contract/v1/README.md#api-level-3-vcs-providers) | +| `litheKeymapImport` | 1 | 后端从声明的提供者中选出要启用的子集;`lithe/keymap/import` | 声明了 `keymapImportProviders` 的插件会话 | [Shortcut import](../contract/v1/README.md#shortcut-import-providers-core-foundation) | +| `litheToolchains` | 1 | `toolchains/query`;后端从声明的提供者中选出子集 | 工具链提供者(macOS;Windows 未接入) | [Toolchains protocol](../contract/v1/toolchains/proposal/protocol.md) | +| `litheLogging` | 1 | 结构化诊断通知 `lithe/log` | 每个插件拥有的进程 | [Structured diagnostics](../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | + +- `litheBuild` 各版本新增的内容与同号的 `buildProviders` 能力版本一致。 +- `litheFunctions` 3 内部还有两个子版本号:宿主服务版本 `HOST_SERVICES_VERSION = 2`、上下文版本 `CONTEXT_VERSION = 3`,SDK 用它们判断能调用哪些宿主方法;运行时以 `lithe/host/getCapabilities` 的返回为准。 +- 宿主侧:上表的协议都由 Rust Core 实现;macOS 运行全部会话类型。Windows 没有插件后端宿主,也不运行构建提供者和 VCS 提供者。 + +### 宿主与官方 SDK 对照 + +“Core 提供”是宿主在 `initialize` 里给出的版本列表;SDK 列是该 SDK 能回答的版本。后端会选两边都支持的最高版本。 + +| 协议 | Core 提供 | Node SDK 0.3.0 | Java SDK 0.3.0 | Rust SDK 0.2.0 | +| --- | --- | --- | --- | --- | +| `litheFunctions` | 1、2、3 | 3 | 3 | 3、2、1 | +| `litheBuild` | 1–11 | 1–6 | 1–6 | — | +| `litheVcs` | 1 | 1 | — | — | +| `litheKeymapImport` | 1 | 1 | 1 | 1 | +| `litheToolchains` | 1 | — | — | — | +| `litheLogging` | 1 | 1(必需) | 1(必需) | 1(必需) | + +- Node 和 Java SDK 的构建方法按协商结果启用:`model`、`dependencies` 需要 1,`execute` 需要 2,`test`、`discoverTests` 需要 3,`prepareRun` 需要 4,`prepareDebug` 需要 5。版本 7–11 的内容目前没有 SDK 封装。 +- 当前 SDK 要求宿主提供 `litheLogging` 1,否则拒绝初始化;用这些 SDK 构建的后端只能在提供该协议的 Lithe 版本中启动。 + +### SDK 发版历史 + +| 日期 | 版本 | `litheFunctions` | `litheBuild` | `litheVcs` | `litheKeymapImport` | `litheLogging` | 主要变化 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 2026-10-20 | [Java 0.1.0](https://github.com/1lck/Lithe-IDEA/releases/tag/sdk-java-v0.1.0) | 1 | 1 | — | — | — | 首个版本,从主仓库发布 | +| 2026-10-23 | [Node 0.1.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.1.0) | 1 | 1 | — | — | — | 首个版本 | +| 2026-11-02 | [Node 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.2.0) | 3 | 1–6 | 1 | — | — | 函数协议 2、3;构建协议 2–6 的类型化处理器;VCS 提供者 | +| 2026-11-02 | [Java 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-java-v0.2.0) | 3 | 1–6 | — | — | — | 函数协议 2、3;构建协议 2–6 的类型化处理器 | +| 2026-11-02 | [Rust 0.1.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-rust-v0.1.0) | 3、2、1 | — | — | — | — | 首个版本 | +| 2026-11-05 | [Node 0.3.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.3.0) | 3 | 1–6 | 1 | 1 | 1(必需) | 快捷键导入提供者、原生终端、类型化诊断;移除自由文本 `log` | +| 2026-11-05 | [Java 0.3.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-java-v0.3.0) | 3 | 1–6 | — | 1 | 1(必需) | 同 Node 0.3.0 | +| 2026-11-05 | [Rust 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-rust-v0.2.0) | 3、2、1 | — | — | 1 | 1(必需) | 同 Node 0.3.0 | + +### 文档格式版本 + +| 文档 | 版本字段 | 当前版本 | 定义 | +| --- | --- | --- | --- | +| 插件清单 `plugin.json` | `schemaVersion` | 2 | [`plugin-manifest.schema.json`](../contract/v1/plugin-manifest.schema.json) | +| 市场索引 | `schemaVersion` | 1 | [`plugin-index.schema.json`](../contract/v1/plugin-index.schema.json) | +| 原生 UI 文档(`ui` 3) | `schemaVersion` | 1 | [`native-ui.schema.json`](../contract/v1/native-ui.schema.json) | +| 构建模型 | `format: "litheBuildModel"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| 依赖树 | `format: "litheDependencies"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| 测试结果 | `format: "litheTestResults"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| 测试发现 | `format: "litheTestDiscovery"` | 1 | [Test discovery](../contract/v1/README.md#test-discovery) | +| 原生构建配置 | `format: "litheBuildConfiguration"` | 1 | [Native configuration](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | +| 本机工具链文档 `.lithe/toolchains/local.json` | `version` | 1 | [Pins and snapshot resolution](../contract/v1/toolchains/README.md#pins-and-snapshot-resolution);读取不会创建文件,保存固定尚未实现 | + +--- + +## English + +This page puts every version number a plugin meets in one place: **every version** of each capability, each process protocol, and each document format, and how far Rust Core, macOS, Windows, and the three official SDKs support each. [`contract/v1/README.md`](../contract/v1/README.md) is normative; where this page disagrees with it, the contract wins, and please open an issue. This page tracks the latest `preview` of the Lithe repository: the Core, macOS, and Windows columns are checked against the code on `preview` (2026-11-05, `644afdaac`), and contract links follow the revision pinned in [`contract.lock.json`](../contract.lock.json). A stable Lithe release may lack some of these versions, so check your target version's release notes before publishing. + +### Four kinds of version + +| Version | Where it is written | Who checks it | When it is not met | +| --- | --- | --- | --- | +| Plugin API level `pluginAPIVersion` | `plugin.json` | Core | The install review refuses the package | +| Capability version `requires.` | `plugin.json` (required from level 3) | Core's support table, intersected with the host's `hostCapabilities` | `plugin.api.unsupported` with `field: "requires."`; the marketplace does not offer that plugin version | +| Process protocol version `experimental.lithe*` | Not in the manifest; negotiated in LSP `initialize` when the process starts | The host offers a list, the backend picks one | The protocol is not enabled, for example no build request goes to that process | +| Document format version | The document's own `schemaVersion` or `version` field | Core | Decoding fails | + +A capability version decides **whether a plugin installs**; a protocol version decides **what a running process can say**. They evolve separately: a backend that supports a new protocol version does not raise any version in its manifest. + +Publishing rules (contract section [API levels](../contract/v1/README.md#api-levels)): + +- Levels and versions only add. A released version never changes meaning, and hosts keep supporting older versions. +- A plugin declares the **lowest** level, and for each capability the **lowest** version, it needs. Raising one without using what it adds only cuts off older hosts. +- Raising a level or capability version needs a new plugin version; the index keeps the last version with the previous requirements so older hosts still have one to install. +- A host release that supports a level or version ships before any plugin that declares it. + +### Plugin API levels + +| Level | Adds | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| `1` | Languages, `packaged` and `systemCommand` language servers, `node` and `go` runtime dependencies, run and test providers | ✅ | ✅ | ✅ | +| `2` | `runtime` launches with a packaged or discovered runtime, host integrations, the `java` runtime, launch templates, `initializationOptions`, plugin settings, first-stage workbench UI (`ui`) | ✅ | ✅ | Validated by Core¹ | +| `3` | Per-capability versions (`requires`), `buildSystems`, `vcsProviders` | ✅ | ✅ | Admitted by the Windows column below | +| `4` | `debugAdapters`, `serverProcess` 2 (`runSelectedRuntime`) | ✅ | ✅ | Admitted by the Windows column below | + +- Core: `SUPPORTED_PLUGIN_API_VERSIONS = [1, 2, 3, 4]` ([`declarative.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/plugins/declarative.rs)). macOS: `PluginManifest.supportedDeclarativeAPIVersions = [1, 2, 3, 4]`. +- Levels 1 and 2 ignore the host capability table; from level 3, installation and marketplace filtering use it. +- ¹ Windows validates level 2 manifests through Core. Per the contract's [Current Windows availability](../contract/v1/README.md#current-windows-availability), Windows advertises `ui` 1 and 2 (tool windows, commands, package web content) and not 3 or 4; plugins at level 3 and above are admitted by the host capability table. The marketplace's `plugins.marketplace.validateIndex` currently marks level 2 versions available only on `darwin-*` platforms; on Windows the same version shows `plugin.index.apiUnsupported`. + +### Capability versions (`requires`) + +Each row is one version of one capability. The Core column is what Core can validate (`SUPPORTED_CAPABILITIES` in [`capabilities.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/plugins/capabilities.rs)); the macOS and Windows columns are what each host actually runs and advertises at installation ([macOS `PluginManifest.hostCapabilities`](https://github.com/1lck/Lithe-IDEA/blob/preview/macos/Sources/LitheModuleAPI/Plugins/PluginTypes.swift), [Windows `host_capabilities()`](https://github.com/1lck/Lithe-IDEA/blob/preview/windows/tauri/src-tauri/src/plugin_host/mod.rs)). Validating a capability is not running it. + +| Capability | Version | Adds | Core | macOS | Windows | Domain page | +| --- | --- | --- | --- | --- | --- | --- | +| `languages` | 1 | `languages[]`: language IDs, file extensions, and so on | ✅ | ✅ | ✅ | [Languages](domains/languages.md) | +| `languages` | 2 | [`grammar` and `configuration`](../contract/v1/README.md#grammars-and-editor-configuration-languages-2): packaged Monarch grammars and editor configuration | ✅ | ✅ | — | [Languages](domains/languages.md) | +| `languageServers` | 1 | `languageServers[]` apart from their launch fields | ✅ | ✅ | ✅ | [Languages](domains/languages.md) | +| `languageServers` | 2 | [`workspaceConfiguration`](../contract/v1/README.md#workspace-configuration-languageservers-2): workspace configuration pushed to the server | ✅ | ✅ | ✅ | [Languages](domains/languages.md) | +| `serverProcess` | 1 | Launch fields `launch`, `arguments`, `validationArguments`, `runtimeDependencies`, `initializationOptions`, shared by language servers, own-process build providers, VCS providers, and the plugin backend | ✅ | ✅ | ✅ | [Languages](domains/languages.md) | +| `serverProcess` | 2 | `runSelectedRuntime` for [debug adapters](../contract/v1/README.md#api-level-4-debug-adapters); needs level 4 | ✅ | — | — | [Debug adapters](domains/debug-adapters.md) | +| `executionProviders` | 1 | `executionProviders[]`: run and test providers | ✅ | ✅ | — | [Languages](domains/languages.md) | +| `settings` | 1 | `settings[]` and `settingActions[]` | ✅ | ✅ | ✅ | [Settings](domains/settings.md) | +| `settings` | 2 | [Placement, scope, effect, page buttons](../contract/v1/README.md#settings-v2-placement-scope-effect-and-page-buttons) | ✅ | ✅ | ✅ | [Settings](domains/settings.md) | +| `ui` | 1 | [First-stage workbench UI](../contract/v1/README.md#api-level-2-first-stage-host-owned-workbench-ui): host-rendered tool windows and commands | ✅ | ✅ | ✅ | [UI](domains/ui.md) | +| `ui` | 2 | [Package web content](../contract/v1/README.md#web-content-ui-2) | ✅ | ✅ | ✅ | [UI](domains/ui.md) | +| `ui` | 3 | [Host-rendered native controls](../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1) (native UI schema 1) | ✅ | ✅ | — | [UI](domains/ui.md) | +| `ui` | 4 | [Backend-only commands](../contract/v1/README.md#backend-only-commands-ui-4) without a tool window; needs `backend` 2 | ✅ | ✅ | — | [Plugin backend](domains/backend.md) | +| `buildSystems` | 1 | [Declarative builds](../contract/v1/README.md#api-level-3-build-systems): detection, options, project model, tasks, run entries, tests, diagnostics, dependencies, sync | ✅ | ✅ | — | [Build systems](domains/build-systems.md) | +| `buildSystems` | 2 | Executable setting adapter | ✅ | ✅ | — | [Build systems](domains/build-systems.md) | +| `buildSystems` | 3 | Instance-scoped options | ✅ | ✅ | — | [Build systems](domains/build-systems.md) | +| `buildSystems` | 4 | [Declarative project preparation roles](../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | ✅ | ✅ | — | [Build systems](domains/build-systems.md) | +| `buildProviders` | 1 | [Build providers](../contract/v1/README.md#build-providers): a backend answers model and dependency requests, in its own process or a shared language server | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 2 | [Task execution](../contract/v1/README.md#provider-task-execution-buildproviders-2) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 3 | [Test execution](../contract/v1/README.md#provider-test-execution-buildproviders-3) and [test discovery](../contract/v1/README.md#test-discovery) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 4 | [Application run preparation](../contract/v1/README.md#provider-run-preparation-buildproviders-4) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 5 | [Native debug preparation](../contract/v1/README.md#native-debug-preparation-version-5) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 6 | [Read-only model inputs](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 7 | [Source-backed native configuration](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 8 | [Inline test reports](../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 9 | [Opaque semantic test selectors](../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 10 | [Typed run options and service/task run preparation](../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildProviders` | 11 | [Registry connection debugging](../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `buildModelInputs` | 1 | [Optional public model sharing between enabled workspace plugins](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | ✅ | ✅ | — | [Build providers](domains/build-providers.md) | +| `vcsProviders` | 1 | [`vcsProviders[]`](../contract/v1/README.md#api-level-3-vcs-providers) | ✅ | ✅ | — | [VCS](domains/vcs.md) | +| `debugAdapters` | 1 | [`debugAdapters[]`](../contract/v1/README.md#api-level-4-debug-adapters); needs level 4 | ✅ | ✅ | — | [Debug adapters](domains/debug-adapters.md) | +| `debugAdapters` | 2 | Debug target arguments and environment | ✅ | ✅ | — | [Debug adapters](domains/debug-adapters.md) | +| `projectGenerators` | 1 | [`projectGenerators[]`](../contract/v1/README.md#project-generators): entries of the New Project window | ✅ | ✅ | — | [Project generators](domains/project-generators.md) | +| `backend` | 1 | [Plugin backend](../contract/v1/README.md#plugin-backend): the plugin's own long-lived process, started from its pages | ✅ | ✅ | — | [Plugin backend](domains/backend.md) | +| `backend` | 2 | [Start without a page, and workspace contexts](../contract/v1/README.md#backend-2-workspace-contexts) | ✅ | ✅ | — | [Plugin backend](domains/backend.md) | +| `packageTrust` | 1 | `vendor.bundledSignatureRequirement`: origin-scoped trust for a host-sealed bundled copy of a publisher-signed plugin | ✅ | ✅ | — | [Package trust](domains/package-trust.md) | +| `toolDefinitions` | 1 | [Tool identities, layouts, discovery, and four-layer candidates](../contract/v1/toolchains/README.md) | ✅ | ✅ (read-only settings, Build and run tools) | — | [Toolchains](domains/toolchains.md) | +| `toolchainProviders` | 1 | [Provider declarations and permission-bound answer validation](../contract/v1/toolchains/README.md) | ✅ | ✅ (read-only settings, Build and run tools) | — | [Toolchains](domains/toolchains.md) | +| `executableResolution` | 1 | [Shared executable candidates](../contract/v1/README.md#shared-executable-candidates-executableresolution-12): candidate ordering and strict selection | ✅ | ✅ | — | [Toolchains](domains/toolchains.md) | +| `executableResolution` | 2 | Tool-definition candidates and project/subproject toolchain registry bindings, without changing existing build candidate sets | ✅ | ✅ (read-only settings, Build and run tools) | — | [Toolchains](domains/toolchains.md) | +| `keymapImportProviders` | 1 | [Shortcut import providers](../contract/v1/README.md#shortcut-import-providers-core-foundation) | ✅ | ✅ | — | [Shortcut import](domains/keymap-import.md) | + +`requires` lists **exactly** the capabilities a manifest uses: leaving one out or listing an unused one is `plugin.manifest.invalid`. Every language server, own-process build provider, VCS provider, and `backend` also needs `serverProcess`. + +### Process protocol versions + +These protocols are negotiated when a process starts and never appear in the manifest. The host offers `{ "versions": [...] }` under `capabilities.experimental.` in `initialize`; the backend answers `{ "version": N }`. An answer with a version the host did not offer, or a malformed answer, leaves the whole protocol disabled. + +| Protocol | Version | Adds | Offered to | Contract section | +| --- | --- | --- | --- | --- | +| `litheFunctions` | 1 | Plugin functions: register, withdraw, invoke, results, progress, cancellation; no host services, every `lithe/host/*` request is `plugin.host.unavailable` | Plugin-owned sessions | [Plugin functions](../contract/v1/README.md#plugin-functions) | +| `litheFunctions` | 2 | Host services: credentials `credentials/get`, `set`, `delete` | Same | [Host services](../contract/v1/README.md#host-services-protocol-version-2) | +| `litheFunctions` | 3 | Workspace contexts and capability discovery, host notifications, public document/file/window methods, independent editor actions, plugin-owned native terminals | Same | [Protocol 3](../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | +| `litheBuild` | 1 | `lithe/build/model` and `lithe/build/dependencies` | Build providers | [Build providers](../contract/v1/README.md#build-providers) | +| `litheBuild` | 2 | Typed task execution (`lithe/build/execute`) | Same | [Task execution](../contract/v1/README.md#provider-task-execution-buildproviders-2) | +| `litheBuild` | 3 | Typed test execution and discovery (`lithe/build/test`, `lithe/build/discoverTests`) | Same | [Test execution](../contract/v1/README.md#provider-test-execution-buildproviders-3) | +| `litheBuild` | 4 | Application run preparation (`lithe/build/prepareRun`) | Same | [Run preparation](../contract/v1/README.md#provider-run-preparation-buildproviders-4) | +| `litheBuild` | 5 | Native debug preparation (`lithe/build/prepareDebug`) | Same | [Debug preparation](../contract/v1/README.md#native-debug-preparation-version-5) | +| `litheBuild` | 6 | Accepts the host's explicitly declared read-only model inputs | Same | [Model inputs](../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | +| `litheBuild` | 7 | Source-backed native configuration | Same | [Native configuration](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | +| `litheBuild` | 8 | Inline test reports | Same | [Inline test reports](../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | +| `litheBuild` | 9 | Opaque semantic test selectors | Same | [Test selectors](../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | +| `litheBuild` | 10 | Typed run options and service/task run preparation | Same | [Run options](../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | +| `litheBuild` | 11 | Registry connection debugging | Same | [Connection debugging](../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | +| `litheVcs` | 1 | VCS provider capabilities and requests | VCS providers | [VCS providers](../contract/v1/README.md#api-level-3-vcs-providers) | +| `litheKeymapImport` | 1 | The backend selects a subset of its declared providers; `lithe/keymap/import` | Plugin sessions with declared `keymapImportProviders` | [Shortcut import](../contract/v1/README.md#shortcut-import-providers-core-foundation) | +| `litheToolchains` | 1 | `toolchains/query`; the backend selects a subset of its declared providers | Toolchain providers (macOS; Windows not connected) | [Toolchains protocol](../contract/v1/toolchains/proposal/protocol.md) | +| `litheLogging` | 1 | Structured diagnostic notification `lithe/log` | Every plugin-owned process | [Structured diagnostics](../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | + +- Each `litheBuild` version adds what the `buildProviders` capability version of the same number adds. +- `litheFunctions` 3 carries two sub-versions: host services `HOST_SERVICES_VERSION = 2` and contexts `CONTEXT_VERSION = 3`, which SDKs use to decide which host methods they may call; at runtime, `lithe/host/getCapabilities` is authoritative. +- Hosts: Rust Core implements every protocol above, and macOS runs every session kind. Windows has no plugin backend host and runs no build or VCS providers. + +### Host and official SDKs + +“Core offers” is the list the host sends in `initialize`; the SDK columns are the versions each SDK can answer. A backend picks the highest version both sides support. + +| Protocol | Core offers | Node SDK 0.3.0 | Java SDK 0.3.0 | Rust SDK 0.2.0 | +| --- | --- | --- | --- | --- | +| `litheFunctions` | 1, 2, 3 | 3 | 3 | 3, 2, 1 | +| `litheBuild` | 1–11 | 1–6 | 1–6 | — | +| `litheVcs` | 1 | 1 | — | — | +| `litheKeymapImport` | 1 | 1 | 1 | 1 | +| `litheToolchains` | 1 | — | — | — | +| `litheLogging` | 1 | 1 (required) | 1 (required) | 1 (required) | + +- The Node and Java SDKs enable each build method from the negotiated version: `model` and `dependencies` need 1, `execute` 2, `test` and `discoverTests` 3, `prepareRun` 4, `prepareDebug` 5. Versions 7–11 have no SDK wrappers yet. +- The current SDKs require the host to offer `litheLogging` 1 and refuse to initialize otherwise, so a backend built on them starts only in a Lithe release that offers it. + +### SDK release history + +| Date | Release | `litheFunctions` | `litheBuild` | `litheVcs` | `litheKeymapImport` | `litheLogging` | Main changes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 2026-10-20 | [Java 0.1.0](https://github.com/1lck/Lithe-IDEA/releases/tag/sdk-java-v0.1.0) | 1 | 1 | — | — | — | First release, published from the Lithe repository | +| 2026-10-23 | [Node 0.1.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.1.0) | 1 | 1 | — | — | — | First release | +| 2026-11-02 | [Node 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.2.0) | 3 | 1–6 | 1 | — | — | Function protocols 2 and 3; typed handlers for build protocols 2–6; VCS providers | +| 2026-11-02 | [Java 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-java-v0.2.0) | 3 | 1–6 | — | — | — | Function protocols 2 and 3; typed handlers for build protocols 2–6 | +| 2026-11-02 | [Rust 0.1.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-rust-v0.1.0) | 3, 2, 1 | — | — | — | — | First release | +| 2026-11-05 | [Node 0.3.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-node-v0.3.0) | 3 | 1–6 | 1 | 1 | 1 (required) | Shortcut import providers, native terminals, typed diagnostics; free-text `log` removed | +| 2026-11-05 | [Java 0.3.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-java-v0.3.0) | 3 | 1–6 | — | 1 | 1 (required) | Same as Node 0.3.0 | +| 2026-11-05 | [Rust 0.2.0](https://github.com/Lithe-IDEA/lithe-plugin-docs/releases/tag/sdk-rust-v0.2.0) | 3, 2, 1 | — | — | 1 | 1 (required) | Same as Node 0.3.0 | + +### Document format versions + +| Document | Version field | Current version | Definition | +| --- | --- | --- | --- | +| Plugin manifest `plugin.json` | `schemaVersion` | 2 | [`plugin-manifest.schema.json`](../contract/v1/plugin-manifest.schema.json) | +| Marketplace index | `schemaVersion` | 1 | [`plugin-index.schema.json`](../contract/v1/plugin-index.schema.json) | +| Native UI document (`ui` 3) | `schemaVersion` | 1 | [`native-ui.schema.json`](../contract/v1/native-ui.schema.json) | +| Build model | `format: "litheBuildModel"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| Dependency tree | `format: "litheDependencies"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| Test results | `format: "litheTestResults"` | 1 | [Model and outputs](../contract/v1/README.md#model-and-outputs) | +| Test discovery | `format: "litheTestDiscovery"` | 1 | [Test discovery](../contract/v1/README.md#test-discovery) | +| Native build configuration | `format: "litheBuildConfiguration"` | 1 | [Native configuration](../contract/v1/README.md#optional-native-configuration-buildproviders-7) | +| Machine-local toolchain document `.lithe/toolchains/local.json` | `version` | 1 | [Pins and snapshot resolution](../contract/v1/toolchains/README.md#pins-and-snapshot-resolution); reading never creates the file, and saving pins is not implemented | diff --git a/reference/domains/backend.md b/reference/domains/backend.md new file mode 100644 index 0000000..4ac2693 --- /dev/null +++ b/reference/domains/backend.md @@ -0,0 +1,141 @@ +# 插件后端与插件函数 / Plugin backend and plugin functions + +[中文](#中文) · [English](#english) + +## 中文 + +插件后端是插件自己的长驻进程,用来运行网页做不到的事:数据库驱动、网络客户端、文件格式解析、调用宿主服务。它只服务插件自己,不属于任何构建系统、语言或工作区,也不回答 `lithe/build/*`、VCS 等领域请求。后端与宿主之间说插件函数协议(`litheFunctions`),从版本 2 起可以按权限请求宿主服务。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `backend`(与 `serverProcess` 的启动字段相同) | +| 能力版本 | `backend` 1–2、`serverProcess` 1;`ui` 4 的后端命令需要 `backend` 2,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheFunctions` 1–3、`litheLogging` 1,见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | API-01、04、05、06、07、08、20、21、22、24、37、40、41,见 [API 总表](../api-catalog.md#现在能用的-api) | +| 权限 | `backend.launch`;宿主服务按方法另加权限(`credentials.store`、`workspace.metadata`、`workspace.files.read`、`workspace.files.watch`、`workspace.search`、`terminal.create` 等),见[已公开的宿主方法](../api-catalog.md#已公开的宿主方法) | +| 激活事件 | `backend` 1:插件网页第一次调用 `functions.*` 时启动。`backend` 2 另可用 `onCommand:` 或可选的 `onWorkspaceOpen` | +| Windows | 没有插件后端宿主,不声明 `backend` | +| 官方 SDK | [Node](../../sdk/node/README.md)、[Java](../../sdk/java/README.md)、[Rust](../../sdk/rust/README.md);各 SDK 支持的协议版本见[宿主与官方 SDK 对照](../capabilities-and-versions.md#宿主与官方-sdk-对照) | + +### 版本对照 + +| 版本 | 新增 | 契约章节 | +| --- | --- | --- | +| `backend` 1 | 每个插件一个长驻进程,只有插件自己的网页能调用;没有网页工具窗口的 `backend` 1 会被拒绝 | [Plugin backend](../../contract/v1/README.md#plugin-backend) | +| `backend` 2 | 可以不依赖网页启动(网页、`ui.commands` 或 `onWorkspaceOpen` 至少一种);所有工作区共享一个进程,每次调用显式带工作区上下文 | [Backend 2: workspace contexts](../../contract/v1/README.md#backend-2-workspace-contexts) | +| `litheFunctions` 1 | 注册、撤回、调用、结果、进度、取消;不提供宿主服务 | [Plugin functions](../../contract/v1/README.md#plugin-functions) | +| `litheFunctions` 2 | 宿主服务:凭据 `credentials/get`、`set`、`delete` | [Host services (protocol version 2)](../../contract/v1/README.md#host-services-protocol-version-2) | +| `litheFunctions` 3 | 工作区上下文与能力发现、宿主通知、公共文件/文档/窗口方法、独立编辑器动作、插件自有原生终端 | [Protocol 3](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | +| `litheLogging` 1 | 结构化诊断通知 `lithe/log`;当前官方 SDK 要求宿主提供 | [Structured plugin diagnostics](../../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Plugin backend](../../contract/v1/README.md#plugin-backend) | 后端声明、状态目录、启动与停止时机、按平台发布 | +| [Backend 2: workspace contexts](../../contract/v1/README.md#backend-2-workspace-contexts) | 启动方式、`onWorkspaceOpen` 不阻塞工作区、`workspace.metadata` | +| [Plugin functions](../../contract/v1/README.md#plugin-functions) | 协议总览 | +| [Capability ownership](../../contract/v1/README.md#capability-ownership) | 插件函数不能代替领域契约 | +| [Declarative or backend implementation](../../contract/v1/README.md#declarative-or-backend-implementation) | 什么时候用声明式数据、什么时候写后端 | +| [Negotiation](../../contract/v1/README.md#negotiation) | `initialize` 里的版本协商 | +| [References and IDs](../../contract/v1/README.md#references-and-ids) | 函数引用和 ID 规则 | +| [Registration and withdrawal](../../contract/v1/README.md#registration-and-withdrawal) | 注册和撤回函数 | +| [Invocation](../../contract/v1/README.md#invocation)、[Progress](../../contract/v1/README.md#progress) | 调用、结果、进度、取消 | +| [Host services (protocol version 2)](../../contract/v1/README.md#host-services-protocol-version-2) | 凭据服务 | +| [Workspace contexts and capability discovery (protocol version 3)](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | `getCapabilities`、上下文 `contextID`/`generation`、宿主失败码 | +| [Host notifications (protocol version 3)](../../contract/v1/README.md#host-notifications-protocol-version-3) | `workspace/didChangeContexts`、`subscriptions/notify`、`commands/didRun` | +| [Extending version 3](../../contract/v1/README.md#extending-version-3) | 协议 3 如何不升版本地增加方法 | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | 文件读取、监听、搜索与窗口消息(文档和编辑器方法见[编辑器](editor.md)) | +| [Plugin-owned native terminals (function protocol 3)](../../contract/v1/README.md#plugin-owned-native-terminals-function-protocol-3)、[Close, retained context and reopen](../../contract/v1/README.md#close-retained-context-and-reopen) | 插件自有原生终端 | +| [Structured plugin diagnostics (logging protocol 1)](../../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | `lithe/log` 的字段和限制 | +| [Compatibility](../../contract/v1/README.md#compatibility)、[Official backend SDK](../../contract/v1/README.md#official-backend-sdk) | 版本兼容规则与官方 SDK | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | 结果与错误码 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`backend/backend.plugin.json`](../../contract/v1/fixtures/backend/backend.plugin.json) | `backend` 1:网页工具窗口调用后端,使用凭据服务 | +| [`backend/backend-workspace.plugin.json`](../../contract/v1/fixtures/backend/backend-workspace.plugin.json) | `backend` 2:`onWorkspaceOpen` 启动,`workspace.metadata` | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | `backend` 2 加 `ui` 4:不打开工具窗口的命令,读文档并编辑 | +| [`backend/terminal-backend.plugin.json`](../../contract/v1/fixtures/backend/terminal-backend.plugin.json) | `backend` 2:插件自有原生终端的最小清单 | +| [`backend/function-protocol.json`](../../contract/v1/fixtures/backend/function-protocol.json) | 插件函数协议的标准报文:协商、注册、调用、结果、进度、42 个宿主请求、18 个宿主失败、10 个宿主通知 | +| [`backend/plugin-logging.json`](../../contract/v1/fixtures/backend/plugin-logging.json) | `litheLogging` 1:提供与回答、通知、拒绝用例、级别 | + +后端相关的拒绝用例在 [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json)。 + +### 相关指南 + +- [插件后端:工作区上下文与宿主服务](../../guides/plugin-backend-workspace.md) +- [从插件打开原生终端](../../guides/native-terminals.md) +- [插件接入统一日志](../../guides/plugin-logging.md) + +--- + +## English + +A plugin backend is the plugin's own long-lived process. It runs what a web page cannot: database drivers, network clients, file-format parsers, calls to host services. It serves only its plugin, belongs to no build system, language, or workspace, and never answers domain requests such as `lithe/build/*` or VCS. Backend and host speak the plugin-function protocol (`litheFunctions`); from version 2 on, the backend may request host services its permissions grant. + +| Item | Value | +| --- | --- | +| Manifest field | `backend` (the same launch fields as `serverProcess`) | +| Capability versions | `backend` 1–2, `serverProcess` 1; `ui` 4 backend commands need `backend` 2; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheFunctions` 1–3, `litheLogging` 1; see [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | API-01, 04, 05, 06, 07, 08, 20, 21, 22, 24, 37, 40, 41; see the [API catalog](../api-catalog.md#apis-usable-now) | +| Permissions | `backend.launch`; host services need their own permission per method (`credentials.store`, `workspace.metadata`, `workspace.files.read`, `workspace.files.watch`, `workspace.search`, `terminal.create`, …); see [public host methods](../api-catalog.md#public-host-methods) | +| Activation | `backend` 1 starts on the first `functions.*` call from the plugin's page. `backend` 2 may also start on `onCommand:` or the optional `onWorkspaceOpen` | +| Windows | No plugin backend host; `backend` is not advertised | +| Official SDKs | [Node](../../sdk/node/README.md), [Java](../../sdk/java/README.md), [Rust](../../sdk/rust/README.md); protocol versions per SDK are in [Host and official SDKs](../capabilities-and-versions.md#host-and-official-sdks) | + +### Versions compared + +| Version | Adds | Contract section | +| --- | --- | --- | +| `backend` 1 | One long-lived process per plugin, called only by the plugin's own web pages; a `backend` 1 without a web tool window is refused | [Plugin backend](../../contract/v1/README.md#plugin-backend) | +| `backend` 2 | Starts without a page (a web window, `ui.commands`, or `onWorkspaceOpen`, at least one); one process shared by every workspace, each call names its workspace context | [Backend 2: workspace contexts](../../contract/v1/README.md#backend-2-workspace-contexts) | +| `litheFunctions` 1 | Registration, withdrawal, invocation, results, progress, cancellation; no host services | [Plugin functions](../../contract/v1/README.md#plugin-functions) | +| `litheFunctions` 2 | Host services: credentials `credentials/get`, `set`, `delete` | [Host services (protocol version 2)](../../contract/v1/README.md#host-services-protocol-version-2) | +| `litheFunctions` 3 | Workspace contexts and capability discovery, host notifications, public file/document/window methods, independent editor actions, plugin-owned native terminals | [Protocol 3](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | +| `litheLogging` 1 | Structured diagnostics notification `lithe/log`; current official SDKs require the host to offer it | [Structured plugin diagnostics](../../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | + +### Contract sections + +| Section | Content | +| --- | --- | +| [Plugin backend](../../contract/v1/README.md#plugin-backend) | Declaration, state directory, when it starts and stops, per-platform archives | +| [Backend 2: workspace contexts](../../contract/v1/README.md#backend-2-workspace-contexts) | Start routes, `onWorkspaceOpen` never blocks the workspace, `workspace.metadata` | +| [Plugin functions](../../contract/v1/README.md#plugin-functions) | Protocol overview | +| [Capability ownership](../../contract/v1/README.md#capability-ownership) | Plugin functions never replace a domain contract | +| [Declarative or backend implementation](../../contract/v1/README.md#declarative-or-backend-implementation) | When to use declarative data and when to write a backend | +| [Negotiation](../../contract/v1/README.md#negotiation) | Version negotiation in `initialize` | +| [References and IDs](../../contract/v1/README.md#references-and-ids) | Function references and ID rules | +| [Registration and withdrawal](../../contract/v1/README.md#registration-and-withdrawal) | Registering and withdrawing functions | +| [Invocation](../../contract/v1/README.md#invocation), [Progress](../../contract/v1/README.md#progress) | Calls, results, progress, cancellation | +| [Host services (protocol version 2)](../../contract/v1/README.md#host-services-protocol-version-2) | Credentials service | +| [Workspace contexts and capability discovery (protocol version 3)](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | `getCapabilities`, `contextID`/`generation` contexts, host failure codes | +| [Host notifications (protocol version 3)](../../contract/v1/README.md#host-notifications-protocol-version-3) | `workspace/didChangeContexts`, `subscriptions/notify`, `commands/didRun` | +| [Extending version 3](../../contract/v1/README.md#extending-version-3) | How protocol 3 gains methods without a new version | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | File reads, watches, search, and window messages (document and editor methods are on [Editor](editor.md)) | +| [Plugin-owned native terminals (function protocol 3)](../../contract/v1/README.md#plugin-owned-native-terminals-function-protocol-3), [Close, retained context and reopen](../../contract/v1/README.md#close-retained-context-and-reopen) | Plugin-owned native terminals | +| [Structured plugin diagnostics (logging protocol 1)](../../contract/v1/README.md#structured-plugin-diagnostics-logging-protocol-1) | `lithe/log` fields and limits | +| [Compatibility](../../contract/v1/README.md#compatibility), [Official backend SDK](../../contract/v1/README.md#official-backend-sdk) | Compatibility rules and the official SDKs | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | Outcomes and error codes | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`backend/backend.plugin.json`](../../contract/v1/fixtures/backend/backend.plugin.json) | `backend` 1: a web tool window calls the backend, which uses the credentials service | +| [`backend/backend-workspace.plugin.json`](../../contract/v1/fixtures/backend/backend-workspace.plugin.json) | `backend` 2: starts on `onWorkspaceOpen`, uses `workspace.metadata` | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | `backend` 2 with `ui` 4: a command without a tool window that reads and edits documents | +| [`backend/terminal-backend.plugin.json`](../../contract/v1/fixtures/backend/terminal-backend.plugin.json) | `backend` 2: the smallest manifest for plugin-owned native terminals | +| [`backend/function-protocol.json`](../../contract/v1/fixtures/backend/function-protocol.json) | Canonical plugin-function messages: negotiation, registration, invocation, results, progress, 42 host requests, 18 host failures, 10 host notifications | +| [`backend/plugin-logging.json`](../../contract/v1/fixtures/backend/plugin-logging.json) | `litheLogging` 1: offer and answer, notifications, rejections, levels | + +Backend rejections are in [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json). + +### Related guides + +- [Plugin backend: workspace contexts and host services](../../guides/plugin-backend-workspace.md) +- [Open a native Lithe terminal from a plugin](../../guides/native-terminals.md) +- [Plugin logging](../../guides/plugin-logging.md) diff --git a/reference/domains/build-providers.md b/reference/domains/build-providers.md new file mode 100644 index 0000000..4aea009 --- /dev/null +++ b/reference/domains/build-providers.md @@ -0,0 +1,151 @@ +# 构建提供者(litheBuild) / Build providers (litheBuild) + +[中文](#中文) · [English](#english) + +## 中文 + +有些构建工具只能通过自己的 API 给出项目模型(Gradle Tooling API、BEAM 里的 Mix)。这类插件在 `buildSystems[].provider` 声明一个构建提供者:一个和语言服务器同类的长驻 LSP 基础协议进程,回答 `lithe/build/*` 请求。从版本 2 起,任务、测试、运行准备、调试准备、原生配置也可以交给它。宿主仍然负责启动和停止进程、显示界面、运行最终的程序。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `buildSystems[].provider`,以及各声明里的 `source: "provider"` | +| 能力版本 | `buildProviders` 1–11;每个提供者都需要 `buildSystems`,自有进程的提供者还需要 `serverProcess` 1,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheBuild` 1–11:宿主在 `initialize` 里提供 `capabilities.experimental.litheBuild: {"versions": [11, …, 1]}`,提供者回答选中的 `{"version": n}`,见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | [API-29](../api-catalog.md#现在能用的-api):构建、任务、测试、运行准备 | +| 权限 | `process.build`;自有进程的提供者加 `languageServer.launch`;调试准备加 `debug.launch` | +| 激活事件 | `onBuildSystem:`;提供者在第一次由用户操作触发的构建请求时启动 | +| Windows | 宿主不声明 `buildProviders` | +| 官方 SDK | [Node](../../sdk/node/README.md) 和 [Java](../../sdk/java/README.md) 实现 `litheBuild` 1–6;[Rust](../../sdk/rust/README.md) 不实现 | + +### 两种形式 + +| 形式 | 字段 | 说明 | +| --- | --- | --- | +| 共享 | `{"languageServer": "", "executesProjectCode", "mayUseNetwork"}` | 请求发给同一插件里该语言的语言服务器会话 | +| 自有进程 | 语言服务器的启动字段(`launch`、`arguments`、`validationArguments`、`runtimeDependencies`、`initializationOptions`)加 `executesProjectCode`、`mayUseNetwork` | 安装审查列为 `packagedBuildProvider`、`systemBuildProvider` 或 `runtimeBuildProvider`;原生配置(版本 7)只能用这种形式 | + +### 版本对照 + +清单里的 `requires.buildProviders` 和 `initialize` 里协商的 `litheBuild` 用同一套版本号。每个版本都保留之前所有版本;提供者回答的版本低于某个功能所需版本时,宿主在调用前拒绝该功能。 + +| 版本 | 新增 | 方法 | 契约章节 | 示例 | Node / Java SDK | +| --- | --- | --- | --- | --- | --- | +| 1 | 项目模型和依赖:`model.source`、`dependencies.source` 为 `provider` | `lithe/build/model`、`lithe/build/dependencies` | [Build providers](../../contract/v1/README.md#build-providers) | `build-provider`、`build-provider-language-server` | 支持 | +| 2 | 任务执行:`tasks[].source: "provider"` | `lithe/build/execute`、`lithe/build/progress` | [Provider task execution (buildProviders 2)](../../contract/v1/README.md#provider-task-execution-buildproviders-2) | `build-provider-execution`、`build-execution-protocol.json` | 支持 | +| 3 | 测试执行和测试发现:`test.source: "provider"` | `lithe/build/test`、`lithe/build/discoverTests` | [Provider test execution (buildProviders 3)](../../contract/v1/README.md#provider-test-execution-buildproviders-3)、[Test discovery](../../contract/v1/README.md#test-discovery) | `build-provider-tests`、`build-test-protocol.json`、`build-test-discovery.json` | 支持 | +| 4 | 应用运行准备:`run[].source: "provider"`,只限应用目标 | `lithe/build/prepareRun` | [Provider run preparation (buildProviders 4)](../../contract/v1/README.md#provider-run-preparation-buildproviders-4) | `build-provider-run-preparation`、`build-run-preparation.json` | 支持 | +| 5 | 原生调试准备:运行声明 `debug: {"adapter": "native"}`,同时需要 `debugAdapters` 2 和级别 4 | `lithe/build/prepareDebug` | [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | `build-provider-debug-preparation`、`build-debug-preparation.json` | 支持 | +| 6 | 接收宿主显式声明的只读模型输入(`modelInputs`,同时需要 `buildModelInputs` 1) | 沿用版本 1 的方法 | [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | `build-systems/build-model-consumer` | 支持 | +| 7 | 以来源为准的原生配置:`configuration: {"source": "provider"}`,只限自有进程 | `lithe/build/configuration`、`lithe/build/editConfiguration` | [Optional native configuration (buildProviders 7)](../../contract/v1/README.md#optional-native-configuration-buildproviders-7) | `build-provider-configuration`、`build-configuration.json` | 不支持 | +| 8 | 内联测试报告:`results: {"decoder": "provider"}`,回复里带 `testResults` | 沿用 `lithe/build/test`、`execute` | [Inline provider test reports (buildProviders 8)](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | `build-provider-reports`、`build-test-durations.json`(重复用例耗时求和) | 不支持 | +| 9 | 不透明的语义测试选择器:`selectorKinds`,请求带 `selectorKind`、`selectors` | 沿用 `lithe/build/test` | [Opaque semantic test selectors (buildProviders 9)](../../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | `build-provider-selectors` | 不支持 | +| 10 | 类型化运行选项,服务和任务的运行准备:`execution: "service"` 或 `"task"` | 扩展 `lithe/build/prepareRun` | [Typed run options and service/task preparation (version 10)](../../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | `build-provider-run-options`、`build-run-options.json` | 不支持 | +| 11 | 注册表连接调试:服务运行声明 `debug: {"configurationType": "…"}`,由宿主选择已启用的 attach 适配器 | 沿用 `lithe/build/prepareRun` | [Registry connection debugging (buildProviders 11)](../../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | `build-connection-debug-options.json` | 不支持 | + +未注明目录的示例都在 `fixtures/build-providers/` 下。版本 11 没有单独的清单示例。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Build providers](../../contract/v1/README.md#build-providers) | 两种形式、请求参数、`initialize` 协商、进程隔离和生命周期 | +| [Provider task execution (buildProviders 2)](../../contract/v1/README.md#provider-task-execution-buildproviders-2) | 任务执行、进度、取消 | +| [Provider test execution (buildProviders 3)](../../contract/v1/README.md#provider-test-execution-buildproviders-3) | 测试执行、报告、测试发现 | +| [Provider run preparation (buildProviders 4)](../../contract/v1/README.md#provider-run-preparation-buildproviders-4) | 运行准备,以及版本 10、11 的扩展 | +| [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | 原生调试准备和适配器绑定 | +| [Optional native configuration (buildProviders 7)](../../contract/v1/README.md#optional-native-configuration-buildproviders-7) | 原生配置的读取和分阶段编辑 | +| [Inline provider test reports (buildProviders 8)](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | 回复里的 `litheTestResults` 报告 | +| [Opaque semantic test selectors (buildProviders 9)](../../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | 语义测试选择器 | +| [Model and outputs](../../contract/v1/README.md#model-and-outputs) | 提供者结果要符合的模型、诊断、依赖格式 | + +### 文档格式 + +| 格式 | 版本 | 用在 | 示例 | +| --- | --- | --- | --- | +| `litheBuildModel` | 1 | 版本 1 的模型结果 | [`build-plans.json`](../../contract/v1/fixtures/build-systems/build-plans.json) | +| `litheTestResults` | 1 | 版本 3 的测试报告、版本 8 的内联报告 | 契约正文内联示例,见 [Inline provider test reports](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8);重复用例的耗时求和规则见 [`build-test-durations.json`](../../contract/v1/fixtures/build-providers/build-test-durations.json) | +| `litheTestDiscovery` | 1 | 版本 3 的测试发现 | [`build-test-discovery.json`](../../contract/v1/fixtures/build-providers/build-test-discovery.json) | +| `litheBuildConfiguration` | 1 | 版本 7 的原生配置 | [`build-configuration.json`](../../contract/v1/fixtures/build-providers/build-configuration.json) | +| `launchOptions` 对象 | — | 版本 10、11 的运行选项;示例文件外层标为 `format: "litheBuildRunOptions"`, `version: 1` | [`build-run-options.json`](../../contract/v1/fixtures/build-providers/build-run-options.json)、[`build-connection-debug-options.json`](../../contract/v1/fixtures/build-providers/build-connection-debug-options.json) | + +所有格式版本见[文档格式版本](../capabilities-and-versions.md#文档格式版本)。 + +### 相关指南 + +- [原生构建、测试与运行准备](../../guides/native-build-workflows.md) +- [为语言插件接入构建、运行与测试](../../guides/build-tool-integration.md) + +--- + +## English + +Some build tools expose their project model only through their own API (the Gradle Tooling API, Mix inside the BEAM). Such a plugin declares a build provider in `buildSystems[].provider`: a long-lived LSP base-protocol process of the same kind as a language server, which answers `lithe/build/*` requests. From version 2 on, tasks, tests, run preparation, debug preparation, and native configuration can be handed to it too. The host still starts and stops the process, draws the UI, and runs the final program. + +| Item | Value | +| --- | --- | +| Manifest fields | `buildSystems[].provider`, and `source: "provider"` in individual declarations | +| Capability versions | `buildProviders` 1–11; every provider needs `buildSystems`, and an own-process provider also needs `serverProcess` 1; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheBuild` 1–11: the host offers `capabilities.experimental.litheBuild: {"versions": [11, …, 1]}` in `initialize`, and the provider answers the chosen `{"version": n}`; see [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | [API-29](../api-catalog.md#apis-usable-now): build, tasks, tests, run preparation | +| Permissions | `process.build`; `languageServer.launch` for an own-process provider; `debug.launch` for debug preparation | +| Activation events | `onBuildSystem:`; a provider starts on the first build request, which only a user action sends | +| Windows | `buildProviders` is not advertised | +| Official SDKs | [Node](../../sdk/node/README.md) and [Java](../../sdk/java/README.md) implement `litheBuild` 1–6; [Rust](../../sdk/rust/README.md) does not | + +### Two forms + +| Form | Fields | Notes | +| --- | --- | --- | +| Shared | `{"languageServer": "", "executesProjectCode", "mayUseNetwork"}` | Requests go to that language's server session in the same plugin | +| Own process | The language-server launch fields (`launch`, `arguments`, `validationArguments`, `runtimeDependencies`, `initializationOptions`) plus `executesProjectCode` and `mayUseNetwork` | Listed in the install review as `packagedBuildProvider`, `systemBuildProvider`, or `runtimeBuildProvider`; native configuration (version 7) requires this form | + +### Versions compared + +`requires.buildProviders` in the manifest and the `litheBuild` version negotiated in `initialize` share one numbering. Each version keeps every earlier one; when the provider answers a version below what a feature needs, the host refuses that feature before calling it. + +| Version | Adds | Methods | Contract section | Example | Node / Java SDK | +| --- | --- | --- | --- | --- | --- | +| 1 | Project model and dependencies: `model.source` and `dependencies.source` set to `provider` | `lithe/build/model`, `lithe/build/dependencies` | [Build providers](../../contract/v1/README.md#build-providers) | `build-provider`, `build-provider-language-server` | Yes | +| 2 | Task execution: `tasks[].source: "provider"` | `lithe/build/execute`, `lithe/build/progress` | [Provider task execution (buildProviders 2)](../../contract/v1/README.md#provider-task-execution-buildproviders-2) | `build-provider-execution`, `build-execution-protocol.json` | Yes | +| 3 | Test execution and discovery: `test.source: "provider"` | `lithe/build/test`, `lithe/build/discoverTests` | [Provider test execution (buildProviders 3)](../../contract/v1/README.md#provider-test-execution-buildproviders-3), [Test discovery](../../contract/v1/README.md#test-discovery) | `build-provider-tests`, `build-test-protocol.json`, `build-test-discovery.json` | Yes | +| 4 | Application run preparation: `run[].source: "provider"`, application targets only | `lithe/build/prepareRun` | [Provider run preparation (buildProviders 4)](../../contract/v1/README.md#provider-run-preparation-buildproviders-4) | `build-provider-run-preparation`, `build-run-preparation.json` | Yes | +| 5 | Native debug preparation: a run declares `debug: {"adapter": "native"}`; also needs `debugAdapters` 2 and level 4 | `lithe/build/prepareDebug` | [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | `build-provider-debug-preparation`, `build-debug-preparation.json` | Yes | +| 6 | Accepts read-only model inputs the host declares explicitly (`modelInputs`; also needs `buildModelInputs` 1) | The version 1 methods | [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | `build-systems/build-model-consumer` | Yes | +| 7 | Source-backed native configuration: `configuration: {"source": "provider"}`, own-process providers only | `lithe/build/configuration`, `lithe/build/editConfiguration` | [Optional native configuration (buildProviders 7)](../../contract/v1/README.md#optional-native-configuration-buildproviders-7) | `build-provider-configuration`, `build-configuration.json` | No | +| 8 | Inline test reports: `results: {"decoder": "provider"}`, replies carry `testResults` | `lithe/build/test`, `execute` | [Inline provider test reports (buildProviders 8)](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | `build-provider-reports`, `build-test-durations.json` (summing repeated case durations) | No | +| 9 | Opaque semantic test selectors: `selectorKinds`; requests carry `selectorKind` and `selectors` | `lithe/build/test` | [Opaque semantic test selectors (buildProviders 9)](../../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | `build-provider-selectors` | No | +| 10 | Typed run options; service and task run preparation: `execution: "service"` or `"task"` | Extends `lithe/build/prepareRun` | [Typed run options and service/task preparation (version 10)](../../contract/v1/README.md#typed-run-options-and-servicetask-preparation-version-10) | `build-provider-run-options`, `build-run-options.json` | No | +| 11 | Registry connection debugging: a service run declares `debug: {"configurationType": "…"}` and the host picks an enabled attach adapter | `lithe/build/prepareRun` | [Registry connection debugging (buildProviders 11)](../../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | `build-connection-debug-options.json` | No | + +Examples without a directory are in `fixtures/build-providers/`. Version 11 has no manifest example of its own. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Build providers](../../contract/v1/README.md#build-providers) | The two forms, request params, `initialize` negotiation, process isolation and lifecycle | +| [Provider task execution (buildProviders 2)](../../contract/v1/README.md#provider-task-execution-buildproviders-2) | Task execution, progress, cancellation | +| [Provider test execution (buildProviders 3)](../../contract/v1/README.md#provider-test-execution-buildproviders-3) | Test execution, reports, discovery | +| [Provider run preparation (buildProviders 4)](../../contract/v1/README.md#provider-run-preparation-buildproviders-4) | Run preparation, and the version 10 and 11 extensions | +| [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | Native debug preparation and adapter bindings | +| [Optional native configuration (buildProviders 7)](../../contract/v1/README.md#optional-native-configuration-buildproviders-7) | Reading and staged editing of native configuration | +| [Inline provider test reports (buildProviders 8)](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8) | `litheTestResults` reports in replies | +| [Opaque semantic test selectors (buildProviders 9)](../../contract/v1/README.md#opaque-semantic-test-selectors-buildproviders-9) | Semantic test selectors | +| [Model and outputs](../../contract/v1/README.md#model-and-outputs) | The model, diagnostics, and dependency formats a provider result must follow | + +### Document formats + +| Format | Version | Used by | Example | +| --- | --- | --- | --- | +| `litheBuildModel` | 1 | Version 1 model results | [`build-plans.json`](../../contract/v1/fixtures/build-systems/build-plans.json) | +| `litheTestResults` | 1 | Version 3 test reports, version 8 inline reports | Inline in the contract, see [Inline provider test reports](../../contract/v1/README.md#inline-provider-test-reports-buildproviders-8); the rule for summing repeated case durations is in [`build-test-durations.json`](../../contract/v1/fixtures/build-providers/build-test-durations.json) | +| `litheTestDiscovery` | 1 | Version 3 test discovery | [`build-test-discovery.json`](../../contract/v1/fixtures/build-providers/build-test-discovery.json) | +| `litheBuildConfiguration` | 1 | Version 7 native configuration | [`build-configuration.json`](../../contract/v1/fixtures/build-providers/build-configuration.json) | +| `launchOptions` object | — | Version 10 and 11 run options; the fixture files wrap it as `format: "litheBuildRunOptions"`, `version: 1` | [`build-run-options.json`](../../contract/v1/fixtures/build-providers/build-run-options.json), [`build-connection-debug-options.json`](../../contract/v1/fixtures/build-providers/build-connection-debug-options.json) | + +All format versions are in [document format versions](../capabilities-and-versions.md#document-format-versions). + +### Related guides + +- [Native build, test, and run preparation](../../guides/native-build-workflows.md) +- [Build, run, and test for language plugins](../../guides/build-tool-integration.md) diff --git a/reference/domains/build-systems.md b/reference/domains/build-systems.md new file mode 100644 index 0000000..dfbc975 --- /dev/null +++ b/reference/domains/build-systems.md @@ -0,0 +1,155 @@ +# 声明式构建系统 / Declarative build systems + +[中文](#中文) · [English](#english) + +## 中文 + +`buildSystems` 用数据描述一个项目级构建工具(Cargo、Go、Gradle、Maven 等):怎么识别项目根目录、找哪个可执行文件、怎么得到项目模型、有哪些任务和运行入口、哪些文件变化要重新同步。所有构建工具共用同一套模型和命令:Core 校验声明、检测实例、解码模型、展开每一条命令行,宿主只执行 Core 规划好的进程并把输出交回。所以社区插件加一个构建工具不需要改宿主,宿主代码也不按构建系统 ID 分支。模型需要程序计算时,再加一个[构建提供者](build-providers.md)。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `buildSystems[]`:`detection`、`invocation`、`options`、`model`、`tasks`、`run`、`sync`、`layout`、`languageImport`、`modelSharing`、`modelInputs` | +| 能力版本 | `buildSystems` 1–4;相关的 `executableResolution` 1–2、`buildModelInputs` 1、`settings` 2,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | 没有进程协议;`model.source: "provider"` 时走 [`litheBuild`](build-providers.md) | +| API 编号 | [API-29](../api-catalog.md#现在能用的-api):构建、任务、测试、运行准备 | +| 权限 | `process.build` | +| 激活事件 | `onBuildSystem:` | +| Windows | 宿主不声明 `buildSystems`,构建插件在 Windows 上安装前被拒绝 | + +### 版本对照 + +| 版本 | 新增 | 契约章节 | 示例 | +| --- | --- | --- | --- | +| `buildSystems` 1 | 识别、可执行文件候选、选项、模型(`static`、`command` 标准输出、`command` 输出文件、`hostIntegration`、`provider`)、任务、运行入口、同步、布局、Java 语言服务器的项目导入 | [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) | `build-static-file`、`build-command-stdout`、`build-command-output-file`、`build-host-integration` | +| `buildSystems` 2 | `invocation.executableSetting`。只为已发布的 Cargo 0.1.1 预览版保留,已弃用;新插件用 `executableResolution` 1 加 `buildSystems` 1 | [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | — | +| `buildSystems` 3 | 选项可以声明 `scope: "instance"`,按检测到的每个根目录分别保存 | [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) 字段表之后 | `build-instance-options` | +| `buildSystems` 4 | 项目任务的 `preparation`:按用途(如 `run`)和内容根角色绑定运行前要执行的任务;宿主不猜任务名 | [Declarative project preparation (buildSystems 4)](../../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | `build-providers/build-provider-preparation` | +| `executableResolution` 1 | 可执行文件候选的第五种来源 `setting`,以及 `id`、`priority` 等排序元数据;配置的路径失败时不回退 | [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | `build-tool-path` | +| `executableResolution` 2 | 工具定义候选,以及项目/子项目的工具链绑定(Core;macOS 只读设置页) | 同上,以及[工具链](toolchains.md) | `build-toolchains` | +| `buildModelInputs` 1 | 生产者用 `modelSharing` 公开只读模型(项目、目标、内容根),消费者用 `modelInputs` 读取;消费者还需要 `buildProviders` 6 | [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | `build-model-producer`、`build-model-consumer` | + +### 模型来源对照 + +| `model.source` | 怎么得到模型 | 示例 | +| --- | --- | --- | +| `static` | 读取项目里的文件 | `build-static-file`(Cargo) | +| `command`(标准输出) | 运行命令,解码标准输出 | `build-command-stdout`(Go) | +| `command`(输出文件) | 运行命令,解码它写出的文件 | `build-command-output-file`(Gradle) | +| `hostIntegration` | 交给宿主集成;只限官方插件,目前只有 `maven`,其他是 `plugin.integration.unsupported` | `build-host-integration`(Maven) | +| `provider` | 由插件的构建提供者回答 `lithe/build/model` | [构建提供者](build-providers.md) | + +这些示例以 Cargo、Go、Gradle、Maven 为真实输入,但不是那些插件本身。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) | 字段表:识别、可执行文件、选项、模型、任务、运行、同步、布局、项目导入 | +| [Presentation](../../contract/v1/README.md#presentation) | 宿主绘制的构建窗口,插件不需要自带界面 | +| [Templates](../../contract/v1/README.md#templates) | 命令行模板变量 | +| [Model and outputs](../../contract/v1/README.md#model-and-outputs) | 统一模型、诊断、测试结果和依赖的格式 | +| [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | 插件之间共享只读模型 | +| [Declarative project preparation (buildSystems 4)](../../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | 运行前准备任务 | +| [Shared executable candidates (`executableResolution` 1/2)](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | 可执行文件候选、排序、严格选择,以及 `buildSystems` 2 的兼容说明 | +| [Host integrations](../../contract/v1/README.md#host-integrations) | 构建系统的宿主集成 | +| [Commands](../../contract/v1/README.md#commands) | `build.*` Core 命令:检测、规划、解码、同步指纹 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`build-systems/build-static-file.plugin.json`](../../contract/v1/fixtures/build-systems/build-static-file.plugin.json) | `static` 模型(Cargo) | +| [`build-systems/build-command-stdout.plugin.json`](../../contract/v1/fixtures/build-systems/build-command-stdout.plugin.json) | 命令标准输出模型(Go) | +| [`build-systems/build-command-output-file.plugin.json`](../../contract/v1/fixtures/build-systems/build-command-output-file.plugin.json) | 命令输出文件模型(Gradle) | +| [`build-systems/build-host-integration.plugin.json`](../../contract/v1/fixtures/build-systems/build-host-integration.plugin.json) | 宿主集成模型(Maven) | +| [`build-systems/build-instance-options.plugin.json`](../../contract/v1/fixtures/build-systems/build-instance-options.plugin.json) | `buildSystems` 3:按实例保存的选项 | +| [`build-systems/build-tool-path.plugin.json`](../../contract/v1/fixtures/build-systems/build-tool-path.plugin.json) | `executableResolution` 1:用 `file` 设置选择构建工具 | +| [`build-systems/build-toolchains.plugin.json`](../../contract/v1/fixtures/build-systems/build-toolchains.plugin.json) | `buildSystems` 3 加 `executableResolution` 2:工具链绑定 | +| [`build-systems/build-model-producer.plugin.json`](../../contract/v1/fixtures/build-systems/build-model-producer.plugin.json) | `buildModelInputs` 1:公开模型的生产者 | +| [`build-systems/build-model-consumer.plugin.json`](../../contract/v1/fixtures/build-systems/build-model-consumer.plugin.json) | `buildModelInputs` 1 加 `buildProviders` 6:读取模型的消费者 | +| [`build-systems/build-plans.json`](../../contract/v1/fixtures/build-systems/build-plans.json) | 65 个 `build.*` 标准答案:每个宿主从上面的声明得到的计划和解码结果 | +| [`build-providers/build-provider-preparation.plugin.json`](../../contract/v1/fixtures/build-providers/build-provider-preparation.plugin.json) | `buildSystems` 4:运行前准备任务 | + +构建相关的拒绝用例在 [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json)。 + +### 相关指南 + +- [为语言插件接入构建、运行与测试](../../guides/build-tool-integration.md) +- [原生构建、测试与运行准备](../../guides/native-build-workflows.md) + +--- + +## English + +`buildSystems` describes a project-level build tool (Cargo, Go, Gradle, Maven, …) as data: how to recognize a project root, which executable to use, how to get the project model, which tasks and run entries exist, and which file changes require a re-sync. Every build tool shares the same model and commands: Core validates the declaration, detects instances, decodes the model, and expands every command line; the host only runs the processes Core planned and passes their output back. A community plugin therefore adds a build tool without any host change, and no host code branches on a build-system ID. When the model must be computed by a program, add a [build provider](build-providers.md). + +| Item | Value | +| --- | --- | +| Manifest fields | `buildSystems[]`: `detection`, `invocation`, `options`, `model`, `tasks`, `run`, `sync`, `layout`, `languageImport`, `modelSharing`, `modelInputs` | +| Capability versions | `buildSystems` 1–4; related `executableResolution` 1–2, `buildModelInputs` 1, `settings` 2; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | No process protocol; `model.source: "provider"` uses [`litheBuild`](build-providers.md) | +| API IDs | [API-29](../api-catalog.md#apis-usable-now): build, tasks, tests, run preparation | +| Permissions | `process.build` | +| Activation events | `onBuildSystem:` | +| Windows | `buildSystems` is not advertised; build plugins are refused before installation | + +### Versions compared + +| Version | Adds | Contract section | Example | +| --- | --- | --- | --- | +| `buildSystems` 1 | Detection, executable candidates, options, models (`static`, `command` stdout, `command` output file, `hostIntegration`, `provider`), tasks, run entries, sync, layout, Java language-server project import | [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) | `build-static-file`, `build-command-stdout`, `build-command-output-file`, `build-host-integration` | +| `buildSystems` 2 | `invocation.executableSetting`. Kept only for the published Cargo 0.1.1 preview and deprecated; new plugins use `executableResolution` 1 with `buildSystems` 1 | [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | — | +| `buildSystems` 3 | Options may declare `scope: "instance"`, stored separately per detected root | After the field table in [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) | `build-instance-options` | +| `buildSystems` 4 | Project-task `preparation`: binds the task to run first by purpose (such as `run`) and content-root role; the host never guesses task names | [Declarative project preparation (buildSystems 4)](../../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | `build-providers/build-provider-preparation` | +| `executableResolution` 1 | A fifth executable source, `setting`, plus ordering metadata such as `id` and `priority`; a configured path that fails never falls back | [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | `build-tool-path` | +| `executableResolution` 2 | Tool-definition candidates and project/subproject toolchain bindings (Core; macOS read-only settings) | Same, and [Toolchains](toolchains.md) | `build-toolchains` | +| `buildModelInputs` 1 | A producer publishes a read-only model (projects, targets, content roots) with `modelSharing`; a consumer reads it with `modelInputs` and also needs `buildProviders` 6 | [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | `build-model-producer`, `build-model-consumer` | + +### Model sources compared + +| `model.source` | How the model is obtained | Example | +| --- | --- | --- | +| `static` | Read a project file | `build-static-file` (Cargo) | +| `command` (stdout) | Run a command and decode its standard output | `build-command-stdout` (Go) | +| `command` (output file) | Run a command and decode the file it writes | `build-command-output-file` (Gradle) | +| `hostIntegration` | Handed to a host integration; official plugins only, currently only `maven`, otherwise `plugin.integration.unsupported` | `build-host-integration` (Maven) | +| `provider` | The plugin's build provider answers `lithe/build/model` | [Build providers](build-providers.md) | + +The examples use Cargo, Go, Gradle, and Maven as realistic inputs; they are not those plugins. + +### Contract sections + +| Section | Content | +| --- | --- | +| [API level 3: build systems](../../contract/v1/README.md#api-level-3-build-systems) | Field table: detection, executables, options, model, tasks, run, sync, layout, project import | +| [Presentation](../../contract/v1/README.md#presentation) | The host-drawn build window; the plugin brings no UI | +| [Templates](../../contract/v1/README.md#templates) | Command-line template variables | +| [Model and outputs](../../contract/v1/README.md#model-and-outputs) | The shared model, diagnostics, test results, and dependency formats | +| [Optional read-only model inputs (buildModelInputs 1)](../../contract/v1/README.md#optional-read-only-model-inputs-buildmodelinputs-1) | Sharing read-only models between plugins | +| [Declarative project preparation (buildSystems 4)](../../contract/v1/README.md#declarative-project-preparation-buildsystems-4) | Tasks that run before a launch | +| [Shared executable candidates (`executableResolution` 1/2)](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | Executable candidates, ordering, strict selection, and `buildSystems` 2 compatibility | +| [Host integrations](../../contract/v1/README.md#host-integrations) | Host integrations for build systems | +| [Commands](../../contract/v1/README.md#commands) | `build.*` Core commands: detection, plans, decoding, sync fingerprints | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`build-systems/build-static-file.plugin.json`](../../contract/v1/fixtures/build-systems/build-static-file.plugin.json) | `static` model (Cargo) | +| [`build-systems/build-command-stdout.plugin.json`](../../contract/v1/fixtures/build-systems/build-command-stdout.plugin.json) | Command stdout model (Go) | +| [`build-systems/build-command-output-file.plugin.json`](../../contract/v1/fixtures/build-systems/build-command-output-file.plugin.json) | Command output-file model (Gradle) | +| [`build-systems/build-host-integration.plugin.json`](../../contract/v1/fixtures/build-systems/build-host-integration.plugin.json) | Host-integration model (Maven) | +| [`build-systems/build-instance-options.plugin.json`](../../contract/v1/fixtures/build-systems/build-instance-options.plugin.json) | `buildSystems` 3: per-instance options | +| [`build-systems/build-tool-path.plugin.json`](../../contract/v1/fixtures/build-systems/build-tool-path.plugin.json) | `executableResolution` 1: a `file` setting selects the build tool | +| [`build-systems/build-toolchains.plugin.json`](../../contract/v1/fixtures/build-systems/build-toolchains.plugin.json) | `buildSystems` 3 with `executableResolution` 2: toolchain bindings | +| [`build-systems/build-model-producer.plugin.json`](../../contract/v1/fixtures/build-systems/build-model-producer.plugin.json) | `buildModelInputs` 1: a model producer | +| [`build-systems/build-model-consumer.plugin.json`](../../contract/v1/fixtures/build-systems/build-model-consumer.plugin.json) | `buildModelInputs` 1 with `buildProviders` 6: a model consumer | +| [`build-systems/build-plans.json`](../../contract/v1/fixtures/build-systems/build-plans.json) | 65 golden `build.*` results: the plans and decoded outputs every host gets from the declarations above | +| [`build-providers/build-provider-preparation.plugin.json`](../../contract/v1/fixtures/build-providers/build-provider-preparation.plugin.json) | `buildSystems` 4: tasks that run before a launch | + +Build rejections are in [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json). + +### Related guides + +- [Build, run, and test for language plugins](../../guides/build-tool-integration.md) +- [Native build, test, and run preparation](../../guides/native-build-workflows.md) diff --git a/reference/domains/debug-adapters.md b/reference/domains/debug-adapters.md new file mode 100644 index 0000000..829d4ca --- /dev/null +++ b/reference/domains/debug-adapters.md @@ -0,0 +1,163 @@ +# 调试适配器 / Debug adapters + +[中文](#中文) · [English](#english) + +## 中文 + +插件在 `debugAdapters[]` 里声明自己实现的调试适配器。每个适配器是一个 DAP 进程,宿主启动它,并通过 stdio 或本机回环 TCP 端口和它通信;也可以是插件语言服务器里内置的调试服务(例如运行在 JDT LS 里的 Java Debug Server)。声明只是数据:没有解析器、工作流、宿主集成或 `initializationOptions`,出现这些字段会被拒绝。调试界面、断点、变量、控制台都归宿主。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `debugAdapters[]` | +| 能力版本 | `debugAdapters` 1–2,需要级别 4;`launch.process` 用到的 `serverProcess` 1,`runSelectedRuntime` 需要 `serverProcess` 2。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | DAP(Debug Adapter Protocol),没有 Lithe 专用协议 | +| API 编号 | [API-30](../api-catalog.md#现在能用的-api):调试适配器 | +| 权限 | `debug.launch`;任何适配器支持 `attach` 时加 `debug.attach`(远程 attach 不另设权限);`tcpLanguageServer` 依赖同一插件的语言服务器,所以还要有 `languageServers` 和 `languageServer.launch` | +| 激活事件 | 每个适配器一个 `onDebug:` | +| Windows | 不支持;Windows 宿主不声明 `debugAdapters` | + +### 版本对照 + +| 版本 | 最低级别 | 新增 | macOS | Windows | +| --- | --- | --- | --- | --- | +| `debugAdapters` 1 | 4 | `debugAdapters[]` 的全部字段:`launch`/`attach` 请求、自动匹配 `selectors`、`configurationTypes`、默认参数与 `targetBindings`(`program`、`processId`、`cwd`、`connection`)、`executable` 候选、`capabilities.disabled`、`console`、四种传输 | 支持 | 不支持 | +| `debugAdapters` 2 | 4 | `targetBindings` 新增有界的 `args` ← `target.arguments`、`env` ← `target.environment` | 支持 | 不支持 | +| `serverProcess` 2 | 4 | `launch.process` 可以写成 `{"launch": {"kind": "runSelectedRuntime", "tool": "python" \| "java" \| "node"}}`,由宿主用用户选中的运行时启动适配器;这是 `serverProcess` 2 唯一的用途 | 只有 Core 支持,macOS 宿主只声明 `serverProcess` 1 | 不支持 | +| `buildProviders` 5 | 4 | 构建提供者为原生调试准备目标,运行声明 `debug: {"adapter": "native"}`;同时需要 `debugAdapters` 2,见[构建提供者](build-providers.md) | 支持 | 不支持 | +| `buildProviders` 11 | 3 | 服务运行声明 `debug: {"configurationType": "…"}`,宿主选一个已启用、支持 `attach` 和该类型的适配器连接 | 支持 | 不支持 | + +低于级别 4 的清单不能声明 `debugAdapters`;级别 3 的清单不能要求 `debugAdapters` 或 `serverProcess` 2。 + +### 传输方式 + +| `launch.transport` | 端点 | 需要 `launch.process` | +| --- | --- | --- | +| `stdio` | 适配器的标准输入输出 | 是 | +| `tcpAnnounced` | 适配器在输出流里宣布端口:`endpoint` 带 `stream`、单行 `prefix`、`value`(`port` 或 `hostPort`) | 是 | +| `tcpPort` | 固定端口 `endpoint.port` | 是 | +| `tcpLanguageServer` | 同一插件的语言服务器:`endpoint` 带 `languageID` 和 `command`;宿主每次会话发 `workspace/executeCommand`,服务器回答回环端口 | 否,且不能有 | + +所有 TCP 端点只能是本机回环地址。`tcpLanguageServer` 的命令是安装和更新审查的一部分,命令改变时会重新询问用户。 + +### 远程 attach + +连接另一台机器上的被调试程序(带 `-agentlib:jdwp` 的 JVM、Node inspector、gdbserver)时,适配器仍在本机运行,只有被调试程序在远端。`attach` 配置的 `targetBindings.connection` 把用户确认的主机、端口、路径映射写进适配器的 `arguments`: + +| 字段 | 说明 | +| --- | --- | +| `host` + `port` | 一起声明,值是 `arguments` 里的路径(1–4 段,如 `connect.host`) | +| `address` | 单个 `host:port` 字符串,IPv6 用方括号;可以代替 `host` + `port`,也可以同时写 | +| `pathMappings.shape: "objects"` | 对象列表,`localKey`、`remoteKey` 命名每个对象的键 | +| `pathMappings.shape: "first"` | 只有一对根目录的适配器;目标多于一对时拒绝,不截断 | +| `pathMappings.shape: "dictionary"` | 一个对象,`keyedBy` 决定哪一侧做键 | +| `pathMappings.shape: "pairs"` | 二元数组列表,`order` 为 `remoteLocal` 或 `localRemote` | + +Core 只把 `localhost`、`127.0.0.0/8`、`::1` 判为本机;其他主机都是 `remote: true`,宿主每次都请用户确认,默认不记住。`connection` 不声明外部 DAP 端点或远程执行适配器(例如 `ssh host gdb -i=dap`),也不提供代理、隧道、端口转发。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [API level 4: debug adapters](../../contract/v1/README.md#api-level-4-debug-adapters) | 全部字段、绑定、`executable` 候选、控制台、传输 | +| [Remote attach](../../contract/v1/README.md#remote-attach) | `connection` 绑定、四种路径映射、目标格式、本机判定、拒绝层次 | +| [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | 构建提供者准备原生调试目标 | +| [Registry connection debugging (buildProviders 11)](../../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | 服务运行按配置类型选择 attach 适配器 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`debug-adapters/debug-adapter.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json) | 合法示例:两个适配器(`example`、`native`),`settings` 2 的 `file` 设置选择要调试的程序 | +| [`debug-adapters/debug-adapter-language-server.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-language-server.plugin.json) | `tcpLanguageServer`:调试服务在插件的语言服务器里 | +| [`debug-adapters/debug-adapter-remote-attach.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json) | 六个适配器,分别演示 `connection` 的不同写法:`flat`(平铺的 `host`、`port`)、`nested`(嵌套路径加 `objects`)、`scalar`(`first`)、`joined`(`address`)、`keyed`(`dictionary`)、`paired`(`pairs`) | +| [`debug-adapters/debug-adapter-rejections.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json) | 76 个拒绝用例,每个写明稳定错误码、字段和报告层次(`schema`、`coreManifest`、`corePlan`、`coreInstall`) | +| [`build-providers/build-provider-debug-preparation.plugin.json`](../../contract/v1/fixtures/build-providers/build-provider-debug-preparation.plugin.json) | `buildProviders` 5 加 `debugAdapters` 2 | +| [`build-providers/build-debug-preparation.json`](../../contract/v1/fixtures/build-providers/build-debug-preparation.json) | `lithe/build/prepareDebug` 的结果和拒绝用例 | +| [`build-providers/build-connection-debug-options.json`](../../contract/v1/fixtures/build-providers/build-connection-debug-options.json) | `buildProviders` 11 的服务运行调试选项 | +| [Lithe-IDEA/lithe-plugin-lldb](https://github.com/Lithe-IDEA/lithe-plugin-lldb) | 官方 lldb-dap 调试插件(预发布),使用 `executable` 候选和 `console.commandPrefix` | + +### 相关指南 + +- [编写调试适配器插件(API 4)](../../guides/debug-adapter-plugin.md) +- [原生构建、测试与运行准备](../../guides/native-build-workflows.md) + +--- + +## English + +A plugin declares the debug adapters it implements in `debugAdapters[]`. Each adapter is a DAP process the host starts and talks to over stdio or a loopback TCP port, or a debug server built into the plugin's language server (such as the Java Debug Server inside JDT LS). The declaration is data only: there is no resolver, workflow, host integration, or `initializationOptions`, and any such field is refused. The debug UI, breakpoints, variables, and console belong to the host. + +| Item | Value | +| --- | --- | +| Manifest field | `debugAdapters[]` | +| Capability versions | `debugAdapters` 1–2, at level 4; `serverProcess` 1 for `launch.process`, and `serverProcess` 2 for `runSelectedRuntime`. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | DAP (Debug Adapter Protocol); no Lithe-specific protocol | +| API IDs | [API-30](../api-catalog.md#apis-usable-now): debug adapters | +| Permissions | `debug.launch`; `debug.attach` when any adapter serves `attach` (remote attach has no separate permission); `tcpLanguageServer` relies on a language server of the same plugin, so it also needs `languageServers` and `languageServer.launch` | +| Activation events | One `onDebug:` per adapter | +| Windows | Not supported; the Windows host does not advertise `debugAdapters` | + +### Versions compared + +| Version | Lowest level | Adds | macOS | Windows | +| --- | --- | --- | --- | --- | +| `debugAdapters` 1 | 4 | Every `debugAdapters[]` field: `launch`/`attach` requests, automatic `selectors`, `configurationTypes`, default arguments and `targetBindings` (`program`, `processId`, `cwd`, `connection`), `executable` candidates, `capabilities.disabled`, `console`, the four transports | Yes | No | +| `debugAdapters` 2 | 4 | Bounded `args` ← `target.arguments` and `env` ← `target.environment` in `targetBindings` | Yes | No | +| `serverProcess` 2 | 4 | `launch.process` may be `{"launch": {"kind": "runSelectedRuntime", "tool": "python" \| "java" \| "node"}}`, so the host starts the adapter with the runtime the user selected; this is the only use of `serverProcess` 2 | Core only; the macOS host advertises `serverProcess` 1 | No | +| `buildProviders` 5 | 4 | A build provider prepares a native debug target, and a run declares `debug: {"adapter": "native"}`; also needs `debugAdapters` 2; see [build providers](build-providers.md) | Yes | No | +| `buildProviders` 11 | 3 | A service run declares `debug: {"configurationType": "…"}` and the host connects an enabled adapter that serves `attach` and that type | Yes | No | + +A manifest below level 4 may not declare `debugAdapters`, and a level 3 manifest may not require `debugAdapters` or `serverProcess` 2. + +### Transports + +| `launch.transport` | Endpoint | Needs `launch.process` | +| --- | --- | --- | +| `stdio` | The adapter's standard input and output | Yes | +| `tcpAnnounced` | The adapter announces its port on an output stream: `endpoint` has `stream`, a one-line `prefix`, and `value` (`port` or `hostPort`) | Yes | +| `tcpPort` | A fixed `endpoint.port` | Yes | +| `tcpLanguageServer` | A language server of the same plugin: `endpoint` has `languageID` and `command`; for each session the host sends `workspace/executeCommand` and the server answers a loopback port | No, and it must not have one | + +Every TCP endpoint is loopback only. The `tcpLanguageServer` command is part of the install and update review, and changing it asks the user again. + +### Remote attach + +When the debuggee runs on another machine (a JVM with `-agentlib:jdwp`, a Node inspector, gdbserver), the adapter still runs locally and only the debuggee is remote. The `targetBindings.connection` of an `attach` configuration writes the host, port, and path mappings the user confirmed into the adapter's `arguments`: + +| Field | Notes | +| --- | --- | +| `host` + `port` | Declared together; values are paths into `arguments` (1–4 segments, such as `connect.host`) | +| `address` | One `host:port` string, IPv6 in brackets; replaces `host` + `port` or sits beside them | +| `pathMappings.shape: "objects"` | A list of objects; `localKey` and `remoteKey` name each object's keys | +| `pathMappings.shape: "first"` | For an adapter with a single pair of roots; a target with more is refused, not truncated | +| `pathMappings.shape: "dictionary"` | One object; `keyedBy` chooses which side is the key | +| `pathMappings.shape: "pairs"` | A list of two-element arrays; `order` is `remoteLocal` or `localRemote` | + +Core treats only `localhost`, `127.0.0.0/8`, and `::1` as local; every other host is `remote: true`, and the host asks the user to confirm it each time without remembering the answer by default. `connection` declares no external DAP endpoint or remote adapter execution (such as `ssh host gdb -i=dap`), and no proxy, tunnel, or port forwarding. + +### Contract sections + +| Section | Content | +| --- | --- | +| [API level 4: debug adapters](../../contract/v1/README.md#api-level-4-debug-adapters) | Every field, bindings, `executable` candidates, console, transports | +| [Remote attach](../../contract/v1/README.md#remote-attach) | The `connection` binding, the four mapping shapes, the target format, local detection, refusal layers | +| [Native debug preparation (version 5)](../../contract/v1/README.md#native-debug-preparation-version-5) | Build providers preparing native debug targets | +| [Registry connection debugging (buildProviders 11)](../../contract/v1/README.md#registry-connection-debugging-buildproviders-11) | Service runs choosing an attach adapter by configuration type | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`debug-adapters/debug-adapter.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json) | Valid example: two adapters (`example`, `native`), and a `settings` 2 `file` setting picks the program to debug | +| [`debug-adapters/debug-adapter-language-server.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-language-server.plugin.json) | `tcpLanguageServer`: the debug server lives in the plugin's language server | +| [`debug-adapters/debug-adapter-remote-attach.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-remote-attach.plugin.json) | Six adapters, one per way of taking a `connection`: `flat` (top-level `host` and `port`), `nested` (nested paths with `objects`), `scalar` (`first`), `joined` (`address`), `keyed` (`dictionary`), `paired` (`pairs`) | +| [`debug-adapters/debug-adapter-rejections.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter-rejections.json) | 76 refusal cases, each with its stable code, field, and reporting layer (`schema`, `coreManifest`, `corePlan`, `coreInstall`) | +| [`build-providers/build-provider-debug-preparation.plugin.json`](../../contract/v1/fixtures/build-providers/build-provider-debug-preparation.plugin.json) | `buildProviders` 5 with `debugAdapters` 2 | +| [`build-providers/build-debug-preparation.json`](../../contract/v1/fixtures/build-providers/build-debug-preparation.json) | `lithe/build/prepareDebug` results and refusal cases | +| [`build-providers/build-connection-debug-options.json`](../../contract/v1/fixtures/build-providers/build-connection-debug-options.json) | `buildProviders` 11 service-run debug options | +| [Lithe-IDEA/lithe-plugin-lldb](https://github.com/Lithe-IDEA/lithe-plugin-lldb) | The official lldb-dap debug plugin (prerelease), using `executable` candidates and `console.commandPrefix` | + +### Related guides + +- [Writing a debug adapter plugin (API 4)](../../guides/debug-adapter-plugin.md) +- [Native build, test, and run preparation](../../guides/native-build-workflows.md) diff --git a/reference/domains/editor.md b/reference/domains/editor.md new file mode 100644 index 0000000..b8a508a --- /dev/null +++ b/reference/domains/editor.md @@ -0,0 +1,101 @@ +# 编辑器、文档与预览 / Editor, documents, and previews + +[中文](#中文) · [English](#english) + +## 中文 + +插件后端通过插件函数协议 3 读取打开的文档和选区、做版本化编辑、打开和定位文件、打开 HTML 预览或外部浏览器,并在编辑器里注册自己的动作。这些都是宿主方法,由 Rust Core 按权限校验、macOS 宿主执行;插件不直接操作编辑器。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | 没有专用字段;用 `backend` 加下面的权限 | +| 能力版本 | `backend` 2,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheFunctions` 3,见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | API-09、10、15、16、27,见 [API 总表](../api-catalog.md#现在能用的-api) | +| 权限 | `documents.read`、`editor.edit`、`editor.open`、`preview.open`、`browser.open`、`editor.actions`(同时需要 `documents.read`),见[已公开的宿主方法](../api-catalog.md#已公开的宿主方法) | +| 激活事件 | 与后端相同:`onWorkspaceOpen`、`onCommand:` 或网页调用 | +| Windows | 不支持;Windows 没有插件后端宿主 | + +### 方法对照 + +| 方法 | 权限 | API | 说明 | +| --- | --- | --- | --- | +| `documents/list`、`documents/getSnapshot`、`editor/getActive`、`editor/getSelections` | `documents.read` | API-09 | 包含未保存状态、版本、编码、换行符;快照最多 128 KiB | +| `editor/applyEdits`、`editor/replaceSelections`、`editor/insertText` | `editor.edit` | API-10 | 校验文档版本;输入法组字中和只读文档会被拒绝;一批编辑是一次撤销 | +| `editor/open`、`editor/reveal` | `editor.open` | API-15 | 返回 `documentID` 和版本;传入范围时只把光标放到起点 | +| `preview/openHTML` | `preview.open` | API-16 | `editorAndPreview` 或 `previewOnly` | +| `browser/openExternal` | `browser.open` | API-16 | HTTP(S) 地址或已保存的 HTML | +| `editor/registerActions` | `editor.actions` + `documents.read` | API-27 | 先用 `getCapabilities` 发现,再按工作区上下文注册;动作绑定普通函数和文件范围,不依赖 LSP、构建或运行 | + +所有方法都要带 `context: {contextID, generation}`;远程工作区上下文返回 `plugin.host.authorityUnsupported`。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | 文档、编辑、打开、预览、浏览器方法的参数、限制和失败码 | +| [Independent editor actions (protocol version 3)](../../contract/v1/README.md#independent-editor-actions-protocol-version-3) | `editor/registerActions` 的注册、范围、调用和撤回 | +| [Workspace contexts and capability discovery (protocol version 3)](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | `contextID`/`generation` 与 `getCapabilities` | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | 结果与错误码 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`editor/editor-actions.plugin.json`](../../contract/v1/fixtures/editor/editor-actions.plugin.json) | `backend` 2 加 `editor.actions`、`documents.read`:`onWorkspaceOpen` 启动后注册编辑器动作 | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | `ui` 4 命令读取文档并编辑 | +| [`backend/function-protocol.json`](../../contract/v1/fixtures/backend/function-protocol.json) | `editor/applyEdits`、`editor/registerActions` 的标准请求与回复,以及 `versionConflict`、`readOnly` 等宿主失败码 | + +### 相关指南 + +- [插件后端:工作区上下文与宿主服务](../../guides/plugin-backend-workspace.md) + +--- + +## English + +Through plugin-function protocol 3, a plugin backend reads open documents and selections, applies versioned edits, opens and reveals files, opens an HTML preview or an external browser, and registers its own editor actions. These are host methods: Rust Core checks permissions and the macOS host executes them. The plugin never drives the editor directly. + +| Item | Value | +| --- | --- | +| Manifest field | None of its own; use `backend` with the permissions below | +| Capability versions | `backend` 2; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheFunctions` 3; see [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | API-09, 10, 15, 16, 27; see the [API catalog](../api-catalog.md#apis-usable-now) | +| Permissions | `documents.read`, `editor.edit`, `editor.open`, `preview.open`, `browser.open`, `editor.actions` (also needs `documents.read`); see [public host methods](../api-catalog.md#public-host-methods) | +| Activation | As for the backend: `onWorkspaceOpen`, `onCommand:`, or a page call | +| Windows | Not supported; Windows has no plugin backend host | + +### Methods compared + +| Method | Permission | API | Notes | +| --- | --- | --- | --- | +| `documents/list`, `documents/getSnapshot`, `editor/getActive`, `editor/getSelections` | `documents.read` | API-09 | Includes unsaved state, version, encoding, line endings; snapshots up to 128 KiB | +| `editor/applyEdits`, `editor/replaceSelections`, `editor/insertText` | `editor.edit` | API-10 | Checks the document version; refused during IME composition and on read-only documents; one batch is one undo step | +| `editor/open`, `editor/reveal` | `editor.open` | API-15 | Returns `documentID` and version; a range only places the caret at its start | +| `preview/openHTML` | `preview.open` | API-16 | `editorAndPreview` or `previewOnly` | +| `browser/openExternal` | `browser.open` | API-16 | An HTTP(S) URL or saved HTML | +| `editor/registerActions` | `editor.actions` + `documents.read` | API-27 | Discover it with `getCapabilities`, then register per workspace context; actions bind ordinary functions and file scopes, independent of LSP, build, or run | + +Every method takes `context: {contextID, generation}`; a remote workspace context returns `plugin.host.authorityUnsupported`. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | Parameters, limits, and failure codes for document, edit, open, preview, and browser methods | +| [Independent editor actions (protocol version 3)](../../contract/v1/README.md#independent-editor-actions-protocol-version-3) | Registering, scoping, invoking, and withdrawing `editor/registerActions` | +| [Workspace contexts and capability discovery (protocol version 3)](../../contract/v1/README.md#workspace-contexts-and-capability-discovery-protocol-version-3) | `contextID`/`generation` and `getCapabilities` | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | Outcomes and error codes | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`editor/editor-actions.plugin.json`](../../contract/v1/fixtures/editor/editor-actions.plugin.json) | `backend` 2 with `editor.actions` and `documents.read`: registers editor actions after `onWorkspaceOpen` | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | A `ui` 4 command that reads and edits documents | +| [`backend/function-protocol.json`](../../contract/v1/fixtures/backend/function-protocol.json) | Canonical requests and replies for `editor/applyEdits` and `editor/registerActions`, plus host failure codes such as `versionConflict` and `readOnly` | + +### Related guides + +- [Plugin backend: workspace contexts and host services](../../guides/plugin-backend-workspace.md) diff --git a/reference/domains/keymap-import.md b/reference/domains/keymap-import.md new file mode 100644 index 0000000..ccf5d9f --- /dev/null +++ b/reference/domains/keymap-import.md @@ -0,0 +1,111 @@ +# 快捷键导入 / Shortcut import + +[中文](#中文) · [English](#english) + +## 中文 + +插件可以把其他编辑器的快捷键配置转换成 Lithe 的快捷键。插件声明 1–8 个导入提供者,说明怎样识别某个编辑器的配置目录、要读哪些文件;用户在导入窗口里选中目录后,宿主只把声明过的文件按块交给插件后端,后端返回规范化的映射,由 Core 对照宿主的动作目录校验。插件拿不到绝对路径,也读不到用户没选中的目录。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `keymapImportProviders[]`:`{id, displayName, recognition, inputs}` | +| 能力版本 | `keymapImportProviders` 1,同时需要 `backend` 2 和 `serverProcess` 1,级别 3。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheKeymapImport` 1:Core 只向声明了提供者的插件会话提供 `experimental.litheKeymapImport: {versions: [1], providers}`,后端回答 `{version: 1, providers}`(非空、无重复的子集),见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | 不在 API-01–44 内(安装和导入层面的契约),见 [API 总表](../api-catalog.md#计划中的-api)下方的说明 | +| 权限 | `backend.launch`、`keymaps.provide`、`keymaps.readSelectedSources` | +| 激活事件 | 没有;宿主只在用户发起导入时启动后端 | +| 后端启动 | 包内可执行文件或包内运行时,不能依赖需要发现的运行时 | +| Windows | 不支持;Windows 宿主不声明 `keymapImportProviders`,也没有读取导入源的实现 | +| 官方 SDK | [Node](../../sdk/node/README.md#shortcut-import-providers) 0.3.0、[Java](../../sdk/java/README.md#shortcut-import-providers) 0.3.0、[Rust](../../sdk/rust/README.md#shortcut-import-providers) 0.2.0 起实现 `litheKeymapImport` 1 | + +### 版本对照 + +| 版本 | 新增 | Core | macOS | Windows | Node / Java / Rust SDK | +| --- | --- | --- | --- | --- | --- | +| `keymapImportProviders` 1 / `litheKeymapImport` 1 | 提供者声明、识别探针、输入选择、受限读取方法、`lithe/keymap/import` 请求、结果校验 | 支持 | 支持 | 不支持 | 0.3.0 / 0.3.0 / 0.2.0 起支持 | + +### 声明与读取 + +| 部分 | 规则 | +| --- | --- | +| `id` | 插件内唯一,小写字母开头,只含小写 ASCII 字母、数字和连字符,最多 64 字节 | +| `recognition` | 1–16 个只看元数据的探针:`{kind: "file" \| "directory", path}` 匹配所选目录下的条目,`{kind: "root", base: "userConfig", components}` 匹配平台用户配置目录(macOS 的 Application Support、Windows 的 Roaming AppData)下的确切路径,最后一段可以是 `*`。识别不授权读内容 | +| `inputs` | 1–16 个选择器:`{kind: "file", path}` 选一个相对文件;`{kind: "directory", path, extension}` 选该目录下指定扩展名的直接子文件,不递归,最多 64 个匹配 | +| 读取限制 | 拒绝符号链接和离开所选目录的路径;单个文件最多 1 MiB,一次快照最多 8 MiB,每块最多 128 KiB | +| 宿主方法 | `lithe/host/keymapImport/listInputs` 返回 `{files: [{id, path, byteCount}]}`;`lithe/host/keymapImport/readChunk` 按 `fileID`、`offset`、`maximumBytes` 返回 base64 的 `{data}` | +| 导入请求 | `lithe/keymap/import` 只带 `{providerID, snapshotID}`;快照是宿主持有的不透明引用,导入或会话结束时撤销,捕获后被替换或修改的文件会被拒绝 | + +提供者负责理解源格式,不能把有条件或多键序列的规则悄悄变成全局快捷键。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Shortcut import providers (Core foundation)](../../contract/v1/README.md#shortcut-import-providers-core-foundation) | 清单字段、识别、输入、协商、宿主读取方法 | +| [Normalized shortcut import validation](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/rust-core-api.md#normalized-shortcut-import-validation) | Core 命令、结果校验规则、限制和示例(在 Lithe 主仓库的 Rust Core API 文档) | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`keymap-import/keymap-import-provider.plugin.json`](../../contract/v1/fixtures/keymap-import/keymap-import-provider.plugin.json) | 两个提供者:`first` 用文件探针和两个 `userConfig` 根探针(含 `*`)、读一个文件;`second` 用目录探针、读一个文件加一个目录下的 `.xml` | +| [Lithe-IDEA/lithe-plugin-keymap-import](https://github.com/Lithe-IDEA/lithe-plugin-keymap-import) | 官方快捷键导入插件(预发布) | + +### 相关指南 + +- [插件后端:工作区上下文与宿主服务](../../guides/plugin-backend-workspace.md) + +--- + +## English + +A plugin can convert another editor's shortcut configuration into Lithe shortcuts. The plugin declares one to eight import providers that say how to recognize an editor's configuration directory and which files to read. After the user selects a directory in the import window, the host passes only the declared files, in chunks, to the plugin backend; the backend returns normalized mappings, which Core validates against the host's action catalog. The plugin never sees absolute paths and cannot read a directory the user did not select. + +| Item | Value | +| --- | --- | +| Manifest field | `keymapImportProviders[]`: `{id, displayName, recognition, inputs}` | +| Capability versions | `keymapImportProviders` 1, together with `backend` 2 and `serverProcess` 1, at level 3. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheKeymapImport` 1: Core offers `experimental.litheKeymapImport: {versions: [1], providers}` only to a plugin session that declares providers, and the backend answers `{version: 1, providers}` (a nonempty, duplicate-free subset); see [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | Outside API-01–44 (an installation and import contract); see the note under the [API catalog](../api-catalog.md#planned-apis) | +| Permissions | `backend.launch`, `keymaps.provide`, `keymaps.readSelectedSources` | +| Activation events | None; the host starts the backend only when the user starts an import | +| Backend launch | A packaged executable or packaged runtime, without discovered runtime dependencies | +| Windows | Not supported; the Windows host does not advertise `keymapImportProviders` and has no import source reader | +| Official SDKs | [Node](../../sdk/node/README.md#shortcut-import-providers) 0.3.0, [Java](../../sdk/java/README.md#shortcut-import-providers) 0.3.0, and [Rust](../../sdk/rust/README.md#shortcut-import-providers) 0.2.0 and later implement `litheKeymapImport` 1 | + +### Versions compared + +| Version | Adds | Core | macOS | Windows | Node / Java / Rust SDK | +| --- | --- | --- | --- | --- | --- | +| `keymapImportProviders` 1 / `litheKeymapImport` 1 | Provider declarations, recognition probes, input selectors, scoped read methods, the `lithe/keymap/import` request, result validation | Yes | Yes | No | From 0.3.0 / 0.3.0 / 0.2.0 | + +### Declaration and reading + +| Part | Rules | +| --- | --- | +| `id` | Unique in the plugin; starts with a lowercase letter; lowercase ASCII letters, digits, and hyphens only; at most 64 bytes | +| `recognition` | 1–16 metadata-only probes: `{kind: "file" \| "directory", path}` matches an entry beneath the selected directory, and `{kind: "root", base: "userConfig", components}` matches an exact path beneath the platform's user configuration directory (macOS Application Support, Windows roaming AppData), whose last component may be `*`. Recognition never authorizes reading contents | +| `inputs` | 1–16 selectors: `{kind: "file", path}` selects one relative file; `{kind: "directory", path, extension}` selects that directory's immediate children with the given extension, never recursively, at most 64 matches | +| Read limits | Symlinks and paths leaving the selected directory are refused; at most 1 MiB per file, 8 MiB per snapshot, and 128 KiB per chunk | +| Host methods | `lithe/host/keymapImport/listInputs` returns `{files: [{id, path, byteCount}]}`; `lithe/host/keymapImport/readChunk` takes `fileID`, `offset`, and `maximumBytes` and returns base64 `{data}` | +| Import request | `lithe/keymap/import` carries only `{providerID, snapshotID}`; the snapshot is an opaque host-owned reference, revoked when the import or session ends, and files replaced or changed after capture are refused | + +Providers own the source formats and must not silently promote conditional or multi-stroke rules to global shortcuts. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Shortcut import providers (Core foundation)](../../contract/v1/README.md#shortcut-import-providers-core-foundation) | Manifest fields, recognition, inputs, negotiation, host read methods | +| [Normalized shortcut import validation](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/rust-core-api.md#normalized-shortcut-import-validation) | Core commands, result validation, limits, and fixtures (in the Rust Core API document of the Lithe main repository) | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`keymap-import/keymap-import-provider.plugin.json`](../../contract/v1/fixtures/keymap-import/keymap-import-provider.plugin.json) | Two providers: `first` uses a file probe and two `userConfig` root probes (one with `*`) and reads one file; `second` uses a directory probe and reads one file plus the `.xml` files of a directory | +| [Lithe-IDEA/lithe-plugin-keymap-import](https://github.com/Lithe-IDEA/lithe-plugin-keymap-import) | The official shortcut import plugin (prerelease) | + +### Related guides + +- [Plugin backend: workspace contexts and host services](../../guides/plugin-backend-workspace.md) diff --git a/reference/domains/languages.md b/reference/domains/languages.md new file mode 100644 index 0000000..7c052f4 --- /dev/null +++ b/reference/domains/languages.md @@ -0,0 +1,119 @@ +# 语言、语言服务器与运行/测试 / Languages, language servers, run and test + +[中文](#中文) · [English](#english) + +## 中文 + +让 Lithe 认识一种语言:识别文件,用包内 Monarch 语法高亮,启动 LSP 语言服务器,提供“运行当前文件”和测试。全部是数据声明,宿主负责进程生命周期、LSP 会话和停止进程,插件不需要写宿主代码。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `languages`、`languageServers`、`executionProviders` | +| 能力版本 | `languages` 1–2、`languageServers` 1–2、`serverProcess` 1–2、`executionProviders` 1,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | LSP;所有插件进程都可以协商 [`litheLogging` 1](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | [API-25](../api-catalog.md#现在能用的-api):语言、语法、LSP | +| 权限 | `languageServer.launch`;声明 `run`、`test` 时加 `process.run`、`process.test` | +| 激活事件 | `onLanguage:`、`onRun:`、`onTest:` | +| Windows | 级别 3 清单按宿主能力表准入:`languages` 1、`languageServers` 1–2、`serverProcess` 1。不支持 `languages` 2 的语法,也不声明 `executionProviders` | + +### 各级别的变化 + +| 版本 | 新增 | +| --- | --- | +| 级别 1 | 语言、`packaged` 和 `systemCommand` 语言服务器、`node` 和 `go` 运行时依赖、运行和测试提供者 | +| 级别 2 | `runtime` 启动(包内运行时或宿主发现的运行时)、`java` 运行时、启动模板、`initializationOptions`、宿主集成(只限官方插件) | +| `languages` 2 | 包内 Monarch 语法 `grammar` 与编辑器配置 `configuration`;只有语法的插件不需要任何进程 | +| `languageServers` 2 | `workspaceConfiguration`:Core 按设置展开后回答 `workspace/configuration`,`immediate` 设置变化时不重启即可推送 | +| `serverProcess` 2 | `runSelectedRuntime`,只给调试适配器用,见[调试适配器](debug-adapters.md) | + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Languages](../../contract/v1/README.md#languages) | 语言 ID、扩展名、文件名、项目标记、依赖路径 | +| [Grammars and editor configuration (`languages` 2)](../../contract/v1/README.md#grammars-and-editor-configuration-languages-2) | Monarch 语法的上限和拒绝规则、注释与括号配置、只在可终止的 worker 中运行 | +| [Language servers](../../contract/v1/README.md#language-servers) | 启动方式、参数、探测参数、运行时依赖 | +| [Runtime launch (API 2)](../../contract/v1/README.md#runtime-launch-api-2) | 用 `java` 或 `node` 启动包内入口文件,可以自带运行时 | +| [Host integrations (API 2)](../../contract/v1/README.md#host-integrations-api-2) | 数据表达不了的行为由宿主代码实现,只限官方插件按名称声明 | +| [Launch templates (API 2)](../../contract/v1/README.md#launch-templates-api-2) | `${pluginDir}`、`${stateDir}`、`${workspaceStateDir}` 等模板变量 | +| [Workspace configuration (`languageServers` 2)](../../contract/v1/README.md#workspace-configuration-languageservers-2) | `workspaceConfiguration` 的展开与推送 | +| [Execution providers](../../contract/v1/README.md#execution-providers) | `run`、`test` 的可执行文件、参数模板、测试发现 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`languages/language-packaged-server.plugin.json`](../../contract/v1/fixtures/languages/language-packaged-server.plugin.json) | 级别 1:包内语言服务器加运行/测试提供者(PHP) | +| [`languages/language-system-command.plugin.json`](../../contract/v1/fixtures/languages/language-system-command.plugin.json) | 级别 1:用系统命令启动的语言服务器加运行/测试提供者(Go) | +| [`languages/node-runtime.plugin.json`](../../contract/v1/fixtures/languages/node-runtime.plugin.json) | 级别 2:用 `node` 运行时启动,带插件设置 | +| [`languages/language-host-integration.plugin.json`](../../contract/v1/fixtures/languages/language-host-integration.plugin.json) | 级别 2:声明宿主集成的语言服务器(Java) | +| [`languages/language-grammar.plugin.json`](../../contract/v1/fixtures/languages/language-grammar.plugin.json) | 级别 3、`languages` 2:只有语法、不启动进程的插件 | +| [`languages/grammars/http.monarch.json`](../../contract/v1/fixtures/languages/grammars/http.monarch.json) | 完整的 Monarch 语法示例 | +| [`languages/command-plans.json`](../../contract/v1/fixtures/languages/command-plans.json) | 35 个标准答案:运行/测试模板和语言服务器启动计划的展开结果 | +| [`languages/runtime-versions.json`](../../contract/v1/fixtures/languages/runtime-versions.json) | 12 个标准答案:`plugins.declarative.checkRuntimeVersion` 的运行时版本判断 | + +语言相关的拒绝用例在 [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json)。 + +### 相关指南 + +- [从零构建一个完整生命周期的 LSP 插件](../../guides/lsp-plugin-lifecycle.md) +- [为语言插件接入构建、运行与测试](../../guides/build-tool-integration.md) + +--- + +## English + +Teach Lithe a language: recognize its files, highlight them with a packaged Monarch grammar, start an LSP language server, and offer "run current file" and tests. Everything is declared as data. The host owns the process lifecycle, the LSP session, and stopping the process; the plugin writes no host code. + +| Item | Value | +| --- | --- | +| Manifest fields | `languages`, `languageServers`, `executionProviders` | +| Capability versions | `languages` 1–2, `languageServers` 1–2, `serverProcess` 1–2, `executionProviders` 1; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | LSP; every plugin process may negotiate [`litheLogging` 1](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | [API-25](../api-catalog.md#apis-usable-now): languages, grammars, LSP | +| Permissions | `languageServer.launch`; `process.run` and `process.test` when `run` or `test` is declared | +| Activation events | `onLanguage:`, `onRun:`, `onTest:` | +| Windows | Level 3 manifests are admitted by the host table: `languages` 1, `languageServers` 1–2, `serverProcess` 1. No `languages` 2 grammars, and `executionProviders` is not advertised | + +### What each version adds + +| Version | Adds | +| --- | --- | +| Level 1 | Languages, `packaged` and `systemCommand` language servers, `node` and `go` runtime dependencies, run and test providers | +| Level 2 | `runtime` launches (packaged or host-discovered runtime), the `java` runtime, launch templates, `initializationOptions`, host integrations (official plugins only) | +| `languages` 2 | Packaged Monarch `grammar` and editor `configuration`; a grammar-only plugin needs no process | +| `languageServers` 2 | `workspaceConfiguration`: Core expands it from settings, answers `workspace/configuration`, and pushes `immediate` setting changes without a restart | +| `serverProcess` 2 | `runSelectedRuntime`, used only by debug adapters; see [Debug adapters](debug-adapters.md) | + +### Contract sections + +| Section | Content | +| --- | --- | +| [Languages](../../contract/v1/README.md#languages) | Language IDs, extensions, file names, project markers, dependency paths | +| [Grammars and editor configuration (`languages` 2)](../../contract/v1/README.md#grammars-and-editor-configuration-languages-2) | Monarch limits and rejections, comment and bracket configuration, worker-only execution | +| [Language servers](../../contract/v1/README.md#language-servers) | Launch kinds, arguments, probe arguments, runtime dependencies | +| [Runtime launch (API 2)](../../contract/v1/README.md#runtime-launch-api-2) | Start a packaged entry with `java` or `node`, optionally with a packaged runtime | +| [Host integrations (API 2)](../../contract/v1/README.md#host-integrations-api-2) | Behavior data cannot express lives in host code; only official plugins opt in by name | +| [Launch templates (API 2)](../../contract/v1/README.md#launch-templates-api-2) | Template variables such as `${pluginDir}`, `${stateDir}`, `${workspaceStateDir}` | +| [Workspace configuration (`languageServers` 2)](../../contract/v1/README.md#workspace-configuration-languageservers-2) | Expanding and pushing `workspaceConfiguration` | +| [Execution providers](../../contract/v1/README.md#execution-providers) | `run` and `test` executables, argument templates, test discovery | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`languages/language-packaged-server.plugin.json`](../../contract/v1/fixtures/languages/language-packaged-server.plugin.json) | Level 1: packaged language server with run and test providers (PHP) | +| [`languages/language-system-command.plugin.json`](../../contract/v1/fixtures/languages/language-system-command.plugin.json) | Level 1: system-command language server with run and test providers (Go) | +| [`languages/node-runtime.plugin.json`](../../contract/v1/fixtures/languages/node-runtime.plugin.json) | Level 2: launched with the `node` runtime, with plugin settings | +| [`languages/language-host-integration.plugin.json`](../../contract/v1/fixtures/languages/language-host-integration.plugin.json) | Level 2: a language server that declares a host integration (Java) | +| [`languages/language-grammar.plugin.json`](../../contract/v1/fixtures/languages/language-grammar.plugin.json) | Level 3, `languages` 2: a grammar-only plugin that starts no process | +| [`languages/grammars/http.monarch.json`](../../contract/v1/fixtures/languages/grammars/http.monarch.json) | A complete Monarch grammar | +| [`languages/command-plans.json`](../../contract/v1/fixtures/languages/command-plans.json) | 35 golden expansions of run/test templates and language-server launch plans | +| [`languages/runtime-versions.json`](../../contract/v1/fixtures/languages/runtime-versions.json) | 12 golden outcomes of `plugins.declarative.checkRuntimeVersion` | + +Language rejections are in [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json). + +### Related guides + +- [Building an LSP plugin end to end](../../guides/lsp-plugin-lifecycle.md) +- [Build, run, and test for language plugins](../../guides/build-tool-integration.md) diff --git a/reference/domains/manifest.md b/reference/domains/manifest.md new file mode 100644 index 0000000..eae861b --- /dev/null +++ b/reference/domains/manifest.md @@ -0,0 +1,177 @@ +# 清单与打包 / Manifest and packaging + +[中文](#中文) · [English](#english) + +## 中文 + +每个插件包的根目录有一份 `plugin.json`。它说明插件是谁、按哪个插件 API 级别和能力版本编写、需要哪些权限、在什么时候启动。宿主遇到不认识的字段会拒绝整个包,而不是忽略其中一部分。其他功能页只讲各自的清单字段,本页讲所有插件共用的部分。 + +| 项目 | 内容 | +| --- | --- | +| 文件 | `plugin.json`([`plugin-manifest.schema.json`](../../contract/v1/plugin-manifest.schema.json));发布者签名 `lithe-plugin-signature.json`(见[包信任](package-trust.md)) | +| 基础字段 | `schemaVersion`(固定为 `2`)、`id`、`displayName`、`version`、`pluginAPIVersion`、`requires`、`vendor`、`permissions`、`activationEvents` | +| 版本 | `pluginAPIVersion` 1–4,见[插件 API 级别](../capabilities-and-versions.md#插件-api-级别)。基础字段只随 `pluginAPIVersion` 变化;级别 3 起各功能字段按[能力版本](../capabilities-and-versions.md#能力版本requires)单独变化 | +| API 编号 | 不在 API-01–44 内,属于安装和打包层面的契约 | +| Windows | 级别 1 可用;级别 2 由 Core 校验;级别 3 起按 Windows 宿主能力表准入 | + +### 清单字段归属 + +| 清单字段 | 最低级别 | `requires` 里的能力 | 功能页 | +| --- | --- | --- | --- | +| `languages` | 1 | `languages` | [语言](languages.md) | +| `languageServers` | 1 | `languageServers`、`serverProcess` | [语言](languages.md) | +| `executionProviders` | 1 | `executionProviders` | [语言](languages.md) | +| `settings`、`settingActions` | 2 | `settings` | [设置](settings.md) | +| `ui` | 2 | `ui` | [界面](ui.md) | +| `buildSystems` | 3 | `buildSystems`,可选 `executableResolution`、`buildModelInputs` | [声明式构建](build-systems.md) | +| `buildSystems[].provider` | 3 | `buildProviders`,自有进程时加 `serverProcess` | [构建提供者](build-providers.md) | +| `vcsProviders` | 3 | `vcsProviders`、`serverProcess` | [VCS](vcs.md) | +| `backend` | 3 | `backend`、`serverProcess` | [插件后端](backend.md) | +| `projectGenerators` | 3 | `projectGenerators` | [项目生成器](project-generators.md) | +| `toolDefinitions`、`toolchainProviders` | 3 | `toolDefinitions`、`toolchainProviders`、`executableResolution` | [工具链](toolchains.md) | +| `keymapImportProviders` | 3 | `keymapImportProviders`、`backend`、`serverProcess` | [快捷键导入](keymap-import.md) | +| `vendor.bundledSignatureRequirement` | 3 | `packageTrust` | [包信任](package-trust.md) | +| `debugAdapters` | 4 | `debugAdapters`,`runSelectedRuntime` 需要 `serverProcess` 2 | [调试适配器](debug-adapters.md) | + +级别 1、2 的清单不写 `requires`;级别 3 起必须写,而且要**恰好**列出用到的能力。 + +### 权限与激活事件 + +| 权限 | 什么时候必须声明 | 功能页 | +| --- | --- | --- | +| `languageServer.launch` | 有 `languageServers` 或 `vcsProviders`,或构建提供者启动自己的进程 | [语言](languages.md)、[VCS](vcs.md)、[构建提供者](build-providers.md) | +| `process.run`、`process.test` | 有提供者声明了 `run`、`test` | [语言](languages.md) | +| `process.build` | `buildSystems` 不为空 | [声明式构建](build-systems.md) | +| `process.vcs` | `vcsProviders` 不为空 | [VCS](vcs.md) | +| `debug.launch`、`debug.attach` | 有 `debugAdapters`;有适配器在 `requests` 里列了 `attach` 时才加 `debug.attach` | [调试适配器](debug-adapters.md) | +| `project.generate` | `projectGenerators` 不为空 | [项目生成器](project-generators.md) | +| `ui.contribute`、`ui.web` | 声明了 `ui`;有工具窗口显示网页时加 `ui.web` | [界面](ui.md) | +| `backend.launch` | 声明了 `backend` | [插件后端](backend.md) | +| `keymaps.provide`、`keymaps.readSelectedSources` | `keymapImportProviders` 不为空 | [快捷键导入](keymap-import.md) | +| `toolchains.provide` | 声明了 `toolchainProviders` | [工具链](toolchains.md) | +| `credentials.store`、`workspace.*`、`documents.read`、`editor.*`、`preview.open`、`browser.open`、`window.messages`、`terminal.create` | 后端调用对应的宿主方法 | [API 总表](../api-catalog.md#已公开的宿主方法) | + +| 激活事件 | 含义 | +| --- | --- | +| `onLanguage:` | 项目里打开该语言的文档时启动语言服务器 | +| `onRun:`、`onTest:` | 用户运行该语言的文件、列出或运行测试时激活运行/测试提供者 | +| `onBuildSystem:` | 构建系统第一次计划、建模或解码之前激活 | +| `onVcsRoot:` | 工作区里检测到该 VCS 的根目录后启动 VCS 提供者 | +| `onDebug:` | 调试会话需要该适配器时启动(级别 4) | +| `onView:`、`onCommand:` | 打开工具窗口、执行命令时激活界面模块;`ui` 4 的后端命令也用 `onCommand` 启动后端 | +| `onWorkspaceOpen` | 工作区打开后启动 `backend` 2 进程;唯一可选的事件 | + +每个提供者都要有自己的事件,每个事件都要对应一个提供者。没有“每次启动都运行”的事件(`onStartupFinished`、`*`),声明了会被拒绝为 `plugin.activation.invalid`。启用插件只是允许激活,事件发生之前什么都不会启动。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Package layout](../../contract/v1/README.md#package-layout) | 包的目录结构;路径规则;不允许符号链接;安装后的包只读 | +| [Manifest](../../contract/v1/README.md#manifest) | 全部顶层字段;未知字段一律拒绝(`plugin.manifest.invalid`) | +| [API levels](../../contract/v1/README.md#api-levels) | 级别 1–4 各自新增的内容 | +| [Capability versions (level 3)](../../contract/v1/README.md#capability-versions-level-3) | `requires`、宿主能力表 `hostCapabilities`、跨级别和版本的发布规则 | +| [Permissions and activation](../../contract/v1/README.md#permissions-and-activation) | 权限表与激活事件表 | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | 结果与稳定错误码 | +| [Windows mapping](../../contract/v1/README.md#windows-mapping) | 旧版 Windows 扩展清单到本契约的字段映射 | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | 把 Lua 社区模板从级别 1 升到级别 2 的步骤 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`manifest/hello-lithe.plugin.json`](../../contract/v1/fixtures/manifest/hello-lithe.plugin.json) | 最小的级别 2 界面插件:一个工具窗口、一个命令。ID 在保留的测试命名空间里,不能直接发布 | +| [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json) | 174 个拒绝用例,覆盖所有功能。每个用例给某个合法 fixture 打补丁,并写明稳定的拒绝码和字段 | + +### 相关指南 + +- [从零构建一个完整生命周期的 LSP 插件](../../guides/lsp-plugin-lifecycle.md):清单、打包、状态目录、安装、发布 +- [插件接入统一日志](../../guides/plugin-logging.md) + +--- + +## English + +Every plugin package has a `plugin.json` at its root. It says who the plugin is, which plugin API level and capability versions it was written against, which permissions it needs, and when it starts. A host that meets an unknown field refuses the whole package instead of ignoring part of it. The other domain pages cover their own manifest fields; this page covers what every plugin shares. + +| Item | Value | +| --- | --- | +| Files | `plugin.json` ([`plugin-manifest.schema.json`](../../contract/v1/plugin-manifest.schema.json)); publisher signature `lithe-plugin-signature.json` (see [Package trust](package-trust.md)) | +| Base fields | `schemaVersion` (always `2`), `id`, `displayName`, `version`, `pluginAPIVersion`, `requires`, `vendor`, `permissions`, `activationEvents` | +| Versions | `pluginAPIVersion` 1–4; see [Plugin API levels](../capabilities-and-versions.md#plugin-api-levels). Base fields change only with `pluginAPIVersion`; from level 3 on, each domain field changes with its own [capability version](../capabilities-and-versions.md#capability-versions-requires) | +| API IDs | Outside API-01–44; this is an installation and packaging contract | +| Windows | Level 1 works; level 2 is validated by Core; from level 3 on, admission follows the Windows host capability table | + +### Which page owns each manifest field + +| Manifest field | Lowest level | Capabilities in `requires` | Domain page | +| --- | --- | --- | --- | +| `languages` | 1 | `languages` | [Languages](languages.md) | +| `languageServers` | 1 | `languageServers`, `serverProcess` | [Languages](languages.md) | +| `executionProviders` | 1 | `executionProviders` | [Languages](languages.md) | +| `settings`, `settingActions` | 2 | `settings` | [Settings](settings.md) | +| `ui` | 2 | `ui` | [UI](ui.md) | +| `buildSystems` | 3 | `buildSystems`, optionally `executableResolution` and `buildModelInputs` | [Declarative build systems](build-systems.md) | +| `buildSystems[].provider` | 3 | `buildProviders`, plus `serverProcess` for an own process | [Build providers](build-providers.md) | +| `vcsProviders` | 3 | `vcsProviders`, `serverProcess` | [VCS](vcs.md) | +| `backend` | 3 | `backend`, `serverProcess` | [Plugin backend](backend.md) | +| `projectGenerators` | 3 | `projectGenerators` | [Project generators](project-generators.md) | +| `toolDefinitions`, `toolchainProviders` | 3 | `toolDefinitions`, `toolchainProviders`, `executableResolution` | [Toolchains](toolchains.md) | +| `keymapImportProviders` | 3 | `keymapImportProviders`, `backend`, `serverProcess` | [Shortcut import](keymap-import.md) | +| `vendor.bundledSignatureRequirement` | 3 | `packageTrust` | [Package trust](package-trust.md) | +| `debugAdapters` | 4 | `debugAdapters`; `runSelectedRuntime` needs `serverProcess` 2 | [Debug adapters](debug-adapters.md) | + +Level 1 and 2 manifests have no `requires`. From level 3 on it is required and must list **exactly** the capabilities the manifest uses. + +### Permissions and activation events + +| Permission | Required when | Domain page | +| --- | --- | --- | +| `languageServer.launch` | `languageServers` or `vcsProviders` is not empty, or a build provider starts its own process | [Languages](languages.md), [VCS](vcs.md), [Build providers](build-providers.md) | +| `process.run`, `process.test` | a provider declares `run` or `test` | [Languages](languages.md) | +| `process.build` | `buildSystems` is not empty | [Declarative build systems](build-systems.md) | +| `process.vcs` | `vcsProviders` is not empty | [VCS](vcs.md) | +| `debug.launch`, `debug.attach` | `debugAdapters` is not empty; `debug.attach` only when an adapter lists `attach` in `requests` | [Debug adapters](debug-adapters.md) | +| `project.generate` | `projectGenerators` is not empty | [Project generators](project-generators.md) | +| `ui.contribute`, `ui.web` | `ui` is declared; `ui.web` when a tool window shows web content | [UI](ui.md) | +| `backend.launch` | `backend` is declared | [Plugin backend](backend.md) | +| `keymaps.provide`, `keymaps.readSelectedSources` | `keymapImportProviders` is not empty | [Shortcut import](keymap-import.md) | +| `toolchains.provide` | `toolchainProviders` is declared | [Toolchains](toolchains.md) | +| `credentials.store`, `workspace.*`, `documents.read`, `editor.*`, `preview.open`, `browser.open`, `window.messages`, `terminal.create` | the backend calls the matching host methods | [API catalog](../api-catalog.md#public-host-methods) | + +| Activation event | Meaning | +| --- | --- | +| `onLanguage:` | Start the language server when a document of the language opens in a project | +| `onRun:`, `onTest:` | Activate the run or test provider when the user runs a file, or lists or runs tests | +| `onBuildSystem:` | Activate the build system before its first plan, model, or decode | +| `onVcsRoot:` | Start the VCS provider once a root of its VCS is detected | +| `onDebug:` | Start the debug adapter when a debug session needs it (level 4) | +| `onView:`, `onCommand:` | Activate the UI module when a tool window opens or a command runs; `ui` 4 backend commands also start the backend through `onCommand` | +| `onWorkspaceOpen` | Start a `backend` 2 process once a workspace is open; the only optional event | + +Each provider needs its event and each event needs a provider. There is no event that starts a plugin on every launch (`onStartupFinished`, `*`); declaring one is `plugin.activation.invalid`. Enabling a plugin only permits activation; nothing starts until its event occurs. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Package layout](../../contract/v1/README.md#package-layout) | Package structure, path rules, no symbolic links, installed packages are read-only | +| [Manifest](../../contract/v1/README.md#manifest) | Every top-level field; unknown fields are refused (`plugin.manifest.invalid`) | +| [API levels](../../contract/v1/README.md#api-levels) | What levels 1–4 each add | +| [Capability versions (level 3)](../../contract/v1/README.md#capability-versions-level-3) | `requires`, the `hostCapabilities` table, and publishing rules across levels and versions | +| [Permissions and activation](../../contract/v1/README.md#permissions-and-activation) | Permission and activation event tables | +| [Outcomes and error codes](../../contract/v1/README.md#outcomes-and-error-codes) | Outcomes and stable error codes | +| [Windows mapping](../../contract/v1/README.md#windows-mapping) | How the legacy Windows extension manifest maps onto this contract | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | Steps to move the community Lua template from level 1 to level 2 | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`manifest/hello-lithe.plugin.json`](../../contract/v1/fixtures/manifest/hello-lithe.plugin.json) | The smallest level 2 UI plugin: one tool window and one command. Its ID is in the reserved test namespace and cannot be published as is | +| [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json) | 174 rejection cases across every domain. Each patches a valid fixture and names the stable rejection code and field | + +### Related guides + +- [Building an LSP plugin end to end](../../guides/lsp-plugin-lifecycle.md): manifest, packaging, state directories, installation, publishing +- [Plugin logging](../../guides/plugin-logging.md) diff --git a/reference/domains/marketplace.md b/reference/domains/marketplace.md new file mode 100644 index 0000000..00110a5 --- /dev/null +++ b/reference/domains/marketplace.md @@ -0,0 +1,281 @@ +# 插件市场 / Marketplace + +[中文](#中文) · [English](#english) + +## 中文 + +插件市场是一份 JSON 索引,列出插件和各版本的发布压缩包。宿主负责获取、缓存和下载;索引是否有效、某个版本在当前平台能不能装、安装审查里列出哪些权限和程序,都由 Rust Core 的 `plugins.marketplace.*` 命令决定,所以各个产品看到同样的目录和同样的审查。 + +| 项目 | 内容 | +| --- | --- | +| 索引格式 | [`plugin-index.schema.json`](../../contract/v1/plugin-index.schema.json),`schemaVersion` 固定为 `1`,根对象只有 `schemaVersion` 和 `plugins` | +| 社区索引文件名 | 仓库根目录的 `lithe-plugin-index.json` | +| Core 命令 | `plugins.marketplace.resolveSource`、`plugins.marketplace.validateIndex`、`plugins.marketplace.installReview` | +| 能力版本 | 没有单独的能力版本;版本的可用性按 `pluginAPIVersion` 和 `requires` 判断,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| API 编号 | 不在 API-01–44 内(安装和打包层面的契约),见 [API 总表](../api-catalog.md#计划中的-api)下方的说明 | +| 信任 | 见[包信任](package-trust.md);社区包使用 `vendor.signatureRequirement: "none"` | +| Windows | 没有声明式插件的市场浏览和安装入口,只能从磁盘安装;不调用 `validateIndex` | +| 官方 SDK | 与 SDK 无关 | + +### 版本对照 + +| 版本 | 内容 | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| 索引 `schemaVersion` 1 | 来源、条目字段、下载地址规则 | 校验;其他 `schemaVersion` 整份拒绝(`plugin.index.schemaUnsupported`) | 支持:官方、Lithe Store 和第三方来源 | 不支持 | +| 条目里的 `pluginAPIVersion` 1 | 任何平台都可用 | 支持 | 支持 | 不适用(无市场) | +| 条目里的 `pluginAPIVersion` 2 | 只在 `darwin-*` 平台标为可用 | 支持 | 支持 | 不适用(无市场) | +| 条目里的 `pluginAPIVersion` 3 | 必须写 `requires`,按 `requires` 和宿主能力表判断;契约不认识的能力 ID 允许出现,但该版本不可用 | 支持 | 支持 | 不适用(无市场) | +| 条目里的 `pluginAPIVersion` 4 | 写了 `requires` 时同级别 3;索引 schema 不强制要求,不写时 Core 按级别 2 的规则只在 `darwin-*` 标为可用。发布时应写上包清单的 `requires` | 支持 | 支持 | 不适用(无市场) | +| 条目里的 `pluginAPIVersion` 5 及以上 | Core 不认识,该版本不可用(`plugin.index.apiUnsupported`) | 跳过 | 跳过 | 不适用(无市场) | +| `installReview` | 权限和程序清单、新增程序对照 | 支持 | 市场和磁盘安装都使用 | 磁盘安装使用 | + +### 来源 + +| 来源 | 地址 | 规则 | +| --- | --- | --- | +| 官方 | Lithe 仓库的 `Plugins/marketplace/index.json` | 应用内带一份只读副本供离线使用;刷新时从 `https://raw.githubusercontent.com/Lithe-IDEA/Lithe-IDEA/HEAD/Plugins/marketplace/index.json` 获取 | +| Lithe Store | [Lithe-IDEA/Lithe-store](https://github.com/Lithe-IDEA/Lithe-store) 的 `lithe-plugin-index.json` | 预置的社区来源,各方面都按第三方处理(社区信任、拒绝 `dev.lithe` ID);默认启用,可以删除,删除后宿主记住,不会再加回来 | +| 第三方 | 用户添加 | `resolveSource` 把用户粘贴的内容变成要获取的索引地址,见下表 | + +| 用户输入 | 结果 | +| --- | --- | +| https 索引地址 | 原样使用,去掉 `#` 片段 | +| `https://github.com//` | 默认分支(`HEAD`)根目录的 `lithe-plugin-index.json` | +| `…/blob//` | 对应的 raw 文件 | +| http、带账号密码的地址、其他 GitHub 页面 | `plugin.source.invalid` | + +获取到的索引缓存在平台缓存或 Application Support,不写进安装目录。 + +### 索引条目 + +| 字段 | 规则 | +| --- | --- | +| `id` | 插件 ID,必须等于包里 `plugin.json` 的 `id` | +| `name`、`author`、`description`、`license` | 展示信息,不能为空 | +| `localizedDescriptions` | 可选,`description` 的翻译,键是语言标签(如 `zh-Hans`);宿主按界面语言选,找不到时用 `description` | +| `repository` | `https://github.com//`,拥有该插件的发布和问题跟踪 | +| `homepage` | 可选,https 地址 | +| `tags` | 可选,单个词,Core 转小写并排序 | +| `icon` | 可选,同一仓库在 `raw.githubusercontent.com` 上的文件 | +| `versions[]` | `version`、必填的 `pluginAPIVersion`、`requires`(级别 1、2 不能写;级别 3 必填;级别 4 可写;内容等于包清单的 `requires`)、`releaseDate`(`YYYY-MM-DD`)、`downloads[]` | +| `downloads[]` | `platforms`(如 `darwin-arm64`、`darwin-x64`、`windows-x64`、`windows-arm64`)、`downloadURL`、压缩包的小写十六进制 `sha256`;一个版本里每个平台最多出现在一个下载项 | + +`downloadURL` 必须是条目自己仓库的 GitHub Release `.zip` 资源(`https://github.com///releases/download//.zip`,owner 和仓库名不区分大小写),索引不能把插件指向别的项目的发布。 + +### 校验与可用性 + +`validateIndex` 的输入是 `{index, sourceKind: "official" | "thirdParty", platform, hostCapabilities?}`。索引根对象格式错误或 `schemaVersion` 不支持时整份拒绝;否则返回 `{plugins, rejected}`: + +| 输出 | 内容 | +| --- | --- | +| `rejected` | 按索引顺序列出被拒绝的条目和错误:格式错误 `plugin.index.invalid`;第三方来源列出官方命名空间 ID `plugin.index.officialNamespace`;重复 ID 保留第一个,之后的 `plugin.index.duplicate` | +| `plugins` | 按 ID 排序;每个条目规范化,`versions` 从新到旧,带 `platforms`、`latestVersion`、`latestReleaseDate` 和 `availability` | + +| `availability` | 条件 | +| --- | --- | +| `{status: "available", version, pluginAPIVersion, releaseDate, downloadURL, sha256}` | 当前平台有压缩包、宿主能运行的最新版本:级别 1;`darwin-*` 上的级别 2 和没写 `requires` 的级别 4;写了 `requires` 的版本里每项能力都被宿主支持 | +| `{status: "unsupported", code: "plugin.index.apiUnsupported", platform, message}` | 列出当前平台的版本都需要更新的插件 API 或宿主不支持的能力 | +| `{status: "unsupported", code: "plugin.index.platformUnsupported", platform, message}` | 没有版本列出当前平台;宿主显示“不支持此平台”而不是安装按钮 | + +### 信任与安装审查 + +| 安装来源 | 流程 | +| --- | --- | +| 官方 | 只有来自官方来源、并通过 Lithe 发布者签名的条目才算官方 | +| 社区市场 | 第三方来源的条目:用索引的 SHA-256 校验压缩包(`plugin.archive.checksumMismatch`)→ 校验声明和包 → `installReview` → 显示审查 → 用户确认后安装。索引校验和只证明与那份索引一致,不代表发布者身份 | +| 社区本地磁盘 | 用户选的 ZIP 或解压目录没有索引和仓库:先复制私有快照,再用同一个 Core 命令校验和审查快照;本地校验和不能显示成仓库匹配 | + +`installReview` 的输入是 `{manifest, expectedID?, expectedVersion?, approvedPrograms?, hostCapabilities?}`,返回 `{id, version, permissions, programs, newPrograms}`。ID 或版本与索引条目不符时返回 `plugin.package.mismatch`;级别 3 的包要求宿主没有的能力时返回 `plugin.api.unsupported`。`programs` 列出宿主可能为插件启动的全部程序,按 `kind`、语言和工具排序: + +| `kind` | 含义 | +| --- | --- | +| `packagedLanguageServer`、`systemLanguageServer`、`runtimeLanguageServer` | 包内可执行文件、宿主发现的命令,或由运行时启动的包内入口,带启动参数和校验探针 | +| `buildSystem` | 构建系统能规划的每条命令;`provider` 是把构建请求发给语言服务器的共享形式 | +| `packagedBuildProvider`、`systemBuildProvider`、`runtimeBuildProvider` | 有独立进程的构建提供者,带 `buildSystemID`、`executesProjectCode`、`mayUseNetwork` | +| `packagedVcsProvider`、`systemVcsProvider`、`runtimeVcsProvider` | VCS 提供者进程,带 `vcs`、`vcsTool`、`mayUseNetwork` | +| `runtime` | 宿主发现并交给语言服务器、构建提供者或 VCS 提供者的运行时 | +| `run`、`test` | 运行和测试命令及参数模板 | +| `projectGenerator` | 项目生成器的 `command` 步骤;`files`、`archive` 步骤不启动程序 | + +每项都带决定实际运行内容的全部字段,所以只改参数、参数模板或校验探针的更新也算新程序。宿主显示全部权限和完整程序清单,并把每个批准的程序按规范 JSON 原样保存。 + +| 情形 | 是否需要用户确认 | +| --- | --- | +| 本地磁盘:首次安装、更新、重装、同版本替换 | 每次都要,即使程序和权限都没变;`newPrograms` 为空也不能免确认 | +| 市场:首次安装、没有之前的批准记录、`newPrograms` 非空 | 要 | +| 市场:更新且没有新程序 | 可以沿用记录的批准 | + +同一个 ID 出现在多个来源时,用户在安装时选择来源,之后只从这个来源更新。本地磁盘安装不是市场来源,也不会自动订阅更新。 + +### 宿主现状 + +| 宿主 | 现状 | +| --- | --- | +| macOS | 官方、Lithe Store 和第三方来源;市场安装和磁盘安装(`MacPluginDiskPackageImporter`)。磁盘快照在 `fileStorage.temporaryDirectory()` 下的 `lithe-plugin-disk-/`(ZIP 为 `archive.zip`,文件在 `package/`),目录权限 `0700`,复制深度最多 64 层。安装来源分别记为 `PluginDiskProvenance` 和 `PluginMarketplaceProvenance`,每次扫描、暂存和回退都复查内容摘要 | +| Windows | 没有市场,不调用 `validateIndex`。磁盘安装把快照放在应用本地数据目录的 `plugin-temp/lithe-plugin-disk-`,用 `installReview` 生成审查,确认时只比对 `plugin.json`,不记录已批准的程序和来源 | + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Marketplace](../../contract/v1/README.md#marketplace) | 市场的分工 | +| [Sources](../../contract/v1/README.md#sources) | 官方、Lithe Store、第三方来源和 `resolveSource` | +| [Index entries](../../contract/v1/README.md#index-entries) | 条目字段和下载地址规则 | +| [Validation and availability](../../contract/v1/README.md#validation-and-availability) | `validateIndex` 的输入输出和可用性 | +| [Trust and install review](../../contract/v1/README.md#trust-and-install-review) | 三条安装路径、`installReview`、程序种类和确认规则 | +| [Local disk snapshots and installation provenance (macOS)](../../contract/v1/README.md#local-disk-snapshots-and-installation-provenance-macos) | macOS 磁盘快照、来源记录、清理,以及 Windows 的差距 | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | 给 Lua 模板加界面并发布到 Lithe Store 的步骤 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`marketplace/index.json`](../../contract/v1/fixtures/marketplace/index.json) | 5 个条目:两个版本的 Zig 插件、含未来级别版本的官方命名空间插件、只有未来级别的插件、三个级别 3 版本的 Elixir 插件、重复的 Zig 条目 | +| [`marketplace/index-cases.json`](../../contract/v1/fixtures/marketplace/index-cases.json) | 6 个 `validateIndex` 用例:Apple 芯片上的官方来源、第三方来源不能列官方命名空间、Windows 选自己的压缩包、按宿主能力退回到较旧版本、Core 不认识的能力永远不可用 | +| [`marketplace/invalid-index-entries.json`](../../contract/v1/fixtures/marketplace/invalid-index-entries.json) | 24 个条目错误用例:23 个 `plugin.index.invalid`,1 个 `plugin.index.officialNamespace` | +| [`marketplace/install-reviews.json`](../../contract/v1/fixtures/marketplace/install-reviews.json) | 19 个 `installReview` 用例:首次安装、无变化更新、改了参数或探针需要重新批准、包与条目不符、官方命名空间的未签名包、构建插件的能力要求 | +| [`marketplace/source-urls.json`](../../contract/v1/fixtures/marketplace/source-urls.json) | 11 个 `resolveSource` 用例:5 个解析成功,6 个 `plugin.source.invalid` | + +### 相关指南 + +- [从零构建一个完整生命周期的 LSP 插件](../../guides/lsp-plugin-lifecycle.md):发布、更新与回退演练,社区索引 `lithe-plugin-index.json` + +--- + +## English + +A marketplace is a JSON index that lists plugins and the release archives of each version. Hosts fetch, cache, and download; Rust Core's `plugins.marketplace.*` commands decide whether an index is valid, whether a version can be installed on the current platform, and which permissions and programs the install review lists, so every product shows the same catalog and the same review. + +| Item | Details | +| --- | --- | +| Index format | [`plugin-index.schema.json`](../../contract/v1/plugin-index.schema.json); `schemaVersion` is always `1`, and the root has only `schemaVersion` and `plugins` | +| Community index file name | `lithe-plugin-index.json` at the repository root | +| Core commands | `plugins.marketplace.resolveSource`, `plugins.marketplace.validateIndex`, `plugins.marketplace.installReview` | +| Capability version | None of its own; a version's availability follows its `pluginAPIVersion` and `requires`. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| API number | Outside API-01–44 (an installation and packaging contract); see the note under the [API catalog](../api-catalog.md#planned-apis) | +| Trust | See [Package trust](package-trust.md); community packages use `vendor.signatureRequirement: "none"` | +| Windows | No marketplace browser or install path for declarative plugins; installation is from disk only, and `validateIndex` is never called | +| Official SDKs | Not SDK-related | + +### Versions compared + +| Version | Contents | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| Index `schemaVersion` 1 | Sources, entry fields, download URL rules | Validates; any other `schemaVersion` rejects the whole index (`plugin.index.schemaUnsupported`) | Supported: official, Lithe Store, and third-party sources | Not supported | +| Entry `pluginAPIVersion` 1 | Available on every platform | Supported | Supported | Not applicable (no marketplace) | +| Entry `pluginAPIVersion` 2 | Marked available only on `darwin-*` platforms | Supported | Supported | Not applicable (no marketplace) | +| Entry `pluginAPIVersion` 3 | `requires` is required; decided by `requires` and the host capability table; capability IDs the contract does not define are allowed but make the version unavailable | Supported | Supported | Not applicable (no marketplace) | +| Entry `pluginAPIVersion` 4 | With `requires`, as level 3; the index schema does not require it, and without it Core applies the level 2 rule and marks the version available only on `darwin-*`. Publishers should list the package manifest's `requires` | Supported | Supported | Not applicable (no marketplace) | +| Entry `pluginAPIVersion` 5 and above | Unknown to Core; the version is unavailable (`plugin.index.apiUnsupported`) | Skipped | Skipped | Not applicable (no marketplace) | +| `installReview` | Permission and program lists, new-program comparison | Supported | Used by marketplace and disk installs | Used by disk installs | + +### Sources + +| Source | Location | Rules | +| --- | --- | --- | +| Official | `Plugins/marketplace/index.json` in the Lithe repository | A read-only copy ships with the application for offline use; refreshing fetches `https://raw.githubusercontent.com/Lithe-IDEA/Lithe-IDEA/HEAD/Plugins/marketplace/index.json` | +| Lithe Store | `lithe-plugin-index.json` in [Lithe-IDEA/Lithe-store](https://github.com/Lithe-IDEA/Lithe-store) | A preset community source treated as third-party in every respect (community trust, `dev.lithe` IDs refused); enabled by default and removable, and the host remembers the removal instead of adding it back | +| Third-party | Added by the user | `resolveSource` turns what the user pasted into the index URL to fetch; see the table below | + +| User input | Result | +| --- | --- | +| An https index URL | Used as is, with the `#` fragment removed | +| `https://github.com//` | `lithe-plugin-index.json` at the root of the default branch (`HEAD`) | +| `…/blob//` | The matching raw file | +| Plain http, embedded credentials, other GitHub pages | `plugin.source.invalid` | + +Fetched indexes are cached in platform cache or Application Support storage, never inside the installation. + +### Index entries + +| Field | Rules | +| --- | --- | +| `id` | Plugin ID; must equal `id` in the package's `plugin.json` | +| `name`, `author`, `description`, `license` | Display metadata; must not be empty | +| `localizedDescriptions` | Optional translations of `description`, keyed by language tag such as `zh-Hans`; hosts pick the interface language and fall back to `description` | +| `repository` | `https://github.com//` that owns the plugin's releases and issue tracker | +| `homepage` | Optional https URL | +| `tags` | Optional single words, lowercased and sorted by Core | +| `icon` | Optional `raw.githubusercontent.com` file of the same repository | +| `versions[]` | `version`, required `pluginAPIVersion`, `requires` (not allowed at levels 1 and 2, required at level 3, optional at level 4; it equals the package manifest's `requires`), `releaseDate` (`YYYY-MM-DD`), and `downloads[]` | +| `downloads[]` | `platforms` (for example `darwin-arm64`, `darwin-x64`, `windows-x64`, `windows-arm64`), `downloadURL`, and the archive's lowercase hex `sha256`; a platform may appear in at most one download of a version | + +A `downloadURL` must be a `.zip` GitHub Release asset of the entry's own repository (`https://github.com///releases/download//.zip`, owner and repository compared case-insensitively), so an index cannot point a plugin at another project's release. + +### Validation and availability + +`validateIndex` takes `{index, sourceKind: "official" | "thirdParty", platform, hostCapabilities?}`. A malformed index root or an unsupported `schemaVersion` rejects the whole index; otherwise it returns `{plugins, rejected}`: + +| Output | Contents | +| --- | --- | +| `rejected` | Refused entries in index order with their error: malformed entries `plugin.index.invalid`; an official-namespace ID from a third-party source `plugin.index.officialNamespace`; a repeated ID keeps the first listing and refuses later ones with `plugin.index.duplicate` | +| `plugins` | Sorted by ID; each entry is normalized, lists `versions` newest first, and carries `platforms`, `latestVersion`, `latestReleaseDate`, and `availability` | + +| `availability` | When | +| --- | --- | +| `{status: "available", version, pluginAPIVersion, releaseDate, downloadURL, sha256}` | The newest version with this platform's archive that the host can run: level 1; level 2, and level 4 without `requires`, on `darwin-*`; a version with `requires` when every capability in it is hosted | +| `{status: "unsupported", code: "plugin.index.apiUnsupported", platform, message}` | Every version listing this platform needs a newer plugin API or a capability the host does not run | +| `{status: "unsupported", code: "plugin.index.platformUnsupported", platform, message}` | No version lists this platform; hosts show "not supported on " instead of an install action | + +### Trust and install review + +| Install route | Flow | +| --- | --- | +| Official | An entry is official only when it comes from the official source and its package passes the Lithe publisher signature | +| Community marketplace | Entries from third-party sources: check the archive against the index SHA-256 (`plugin.archive.checksumMismatch`) → validate the declaration and package → `installReview` → show the review → install after the user confirms. The index checksum proves a match to that index's bytes, not an authenticated publisher | +| Community local disk | A user-selected ZIP or unpacked directory has no index or repository: take a private snapshot first, then validate and review the snapshot with the same Core command; a local checksum is never shown as a repository match | + +`installReview` takes `{manifest, expectedID?, expectedVersion?, approvedPrograms?, hostCapabilities?}` and returns `{id, version, permissions, programs, newPrograms}`. An ID or version that differs from the index entry returns `plugin.package.mismatch`; a level 3 package that requires a capability the host lacks returns `plugin.api.unsupported`. `programs` lists everything the host may start for the plugin, sorted by `kind`, language, and tool: + +| `kind` | Meaning | +| --- | --- | +| `packagedLanguageServer`, `systemLanguageServer`, `runtimeLanguageServer` | An executable in the package, a command found by host discovery, or a package entry started by a runtime, with launch arguments and validation probe | +| `buildSystem` | Every command line the build system can plan; `provider` is the shared form that sends build requests to a language server | +| `packagedBuildProvider`, `systemBuildProvider`, `runtimeBuildProvider` | A build provider with a process of its own, with `buildSystemID`, `executesProjectCode`, and `mayUseNetwork` | +| `packagedVcsProvider`, `systemVcsProvider`, `runtimeVcsProvider` | A VCS provider process, with `vcs`, `vcsTool`, and `mayUseNetwork` | +| `runtime` | A runtime the host discovers and passes to a language server, build provider, or VCS provider | +| `run`, `test` | Run and test commands with their argument templates | +| `projectGenerator` | A `command` step of a project generator; `files` and `archive` steps start no program | + +Each entry carries every field that decides what actually runs, so an update that only changes arguments, argument templates, or the validation probe is a new program. Hosts show all permissions and the complete program list, and persist each approved program as canonical JSON without dropping fields. + +| Case | Needs user confirmation | +| --- | --- | +| Local disk: first install, update, reinstall, same-version replacement | Always, even when programs and permissions are unchanged; an empty `newPrograms` never waives it | +| Marketplace: first install, no previous approval, or nonempty `newPrograms` | Yes | +| Marketplace: update with no new programs | May reuse the recorded approval | + +When several sources list the same ID, the user chooses the source at install time and later updates come only from it. A local disk installation is never a marketplace source or an automatic update subscription. + +### Host status + +| Host | Status | +| --- | --- | +| macOS | Official, Lithe Store, and third-party sources; marketplace and disk installation (`MacPluginDiskPackageImporter`). Disk snapshots go to `lithe-plugin-disk-/` under `fileStorage.temporaryDirectory()` (`archive.zip` for a ZIP, files in `package/`), with mode `0700` and a copy depth of at most 64. Provenance is recorded as `PluginDiskProvenance` or `PluginMarketplaceProvenance`, and the content digest is rechecked on every scan, staging, and rollback | +| Windows | No marketplace and no `validateIndex` call. Disk installation puts the snapshot in `plugin-temp/lithe-plugin-disk-` under the application's local data directory, builds the review with `installReview`, checks only `plugin.json` at confirmation, and records no approved programs or provenance | + +### Contract sections + +| Section | Covers | +| --- | --- | +| [Marketplace](../../contract/v1/README.md#marketplace) | Division of work between hosts and Core | +| [Sources](../../contract/v1/README.md#sources) | Official, Lithe Store, and third-party sources, and `resolveSource` | +| [Index entries](../../contract/v1/README.md#index-entries) | Entry fields and download URL rules | +| [Validation and availability](../../contract/v1/README.md#validation-and-availability) | `validateIndex` input, output, and availability | +| [Trust and install review](../../contract/v1/README.md#trust-and-install-review) | The three install routes, `installReview`, program kinds, and confirmation rules | +| [Local disk snapshots and installation provenance (macOS)](../../contract/v1/README.md#local-disk-snapshots-and-installation-provenance-macos) | macOS disk snapshots, provenance records, cleanup, and the Windows gap | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | Steps to add a view to the Lua template and publish it to Lithe Store | + +### Examples and cases + +| File | Contents | +| --- | --- | +| [`marketplace/index.json`](../../contract/v1/fixtures/marketplace/index.json) | 5 entries: a Zig plugin with two versions, an official-namespace plugin with a future-level version, a plugin with only a future level, an Elixir plugin with three level 3 versions, and a duplicate Zig entry | +| [`marketplace/index-cases.json`](../../contract/v1/fixtures/marketplace/index-cases.json) | 6 `validateIndex` cases: the official source on Apple silicon, a third-party source listing the official namespace, Windows picking its own archive, falling back to an older version by host capabilities, and a capability Core cannot validate never being offered | +| [`marketplace/invalid-index-entries.json`](../../contract/v1/fixtures/marketplace/invalid-index-entries.json) | 24 entry error cases: 23 `plugin.index.invalid` and 1 `plugin.index.officialNamespace` | +| [`marketplace/install-reviews.json`](../../contract/v1/fixtures/marketplace/install-reviews.json) | 19 `installReview` cases: first install, an unchanged update, argument or probe changes needing new approval, a package that does not match its entry, an unsigned official-namespace package, and the capability requirements of build plugins | +| [`marketplace/source-urls.json`](../../contract/v1/fixtures/marketplace/source-urls.json) | 11 `resolveSource` cases: 5 resolved and 6 `plugin.source.invalid` | + +### Related guides + +- [Building a complete-lifecycle LSP plugin](../../guides/lsp-plugin-lifecycle.md): release, update, and rollback drills, and the community index `lithe-plugin-index.json` diff --git a/reference/domains/package-trust.md b/reference/domains/package-trust.md new file mode 100644 index 0000000..9e6f8a7 --- /dev/null +++ b/reference/domains/package-trust.md @@ -0,0 +1,213 @@ +# 包信任 / Package trust + +[中文](#中文) · [English](#english) + +## 中文 + +这一页说明插件包的目录规则、清单里的签名要求,以及级别 3 的 `packageTrust` 能力。签名要求决定宿主用什么证据接受一个包:发布者签名、宿主自身安装目录的代码签名,或者社区包的用户确认。签名和信任由宿主验证,Core 校验清单里的声明是否合法。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `vendor.id`、`vendor.displayName`、`vendor.signatureRequirement`;级别 3 可以再加 `vendor.bundledSignatureRequirement` | +| 能力版本 | `packageTrust` 1,只用于 `vendor.bundledSignatureRequirement`,级别 3。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 签名文件 | 包根目录的 `lithe-plugin-signature.json`,Ed25519 签名,覆盖包里其他所有文件 | +| API 编号 | 不在 API-01–44 内(安装和打包层面的契约),见 [API 总表](../api-catalog.md#计划中的-api)下方的说明 | +| 权限、激活事件 | 没有 | +| 宿主信任的密钥 | macOS 只信任 Lithe 官方发布者密钥(`lithe-official-plugins-v2`);社区开发者无法产出宿主信任的发布者签名 | +| Windows | 不验证发布者签名,不声明 `packageTrust`;从磁盘安装的包只靠用户确认安装审查,见下文[宿主现状](#宿主现状) | +| 官方 SDK | 与 SDK 无关;签名属于打包和发布环节 | + +### 版本对照 + +| 版本 | 新增 | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| 基础规则(无能力版本,级别 1 起) | 包目录规则;`signatureRequirement` 的 `publisherPackage`、`sameTeamAsHost`、`none` | 校验清单和命名空间 | 支持:验证签名、安装封存、内容摘要 | 部分:只校验清单,不验证签名,不记录内容摘要 | +| `packageTrust` 1(级别 3) | `vendor.bundledSignatureRequirement: "sameTeamAsHost"`:随宿主发布的官方插件副本可以不带发布者签名 | 支持 | 支持 | 不支持(不声明,Core 拒绝要求它的包) | + +### 包目录规则 + +| 规则 | 内容 | +| --- | --- | +| 根目录 | `plugin.json`、`lithe-plugin-signature.json`(需要时)和清单引用的资源 | +| 路径 | `plugin.json` 里的每个路径都相对包根目录,用 `/` 分隔,不能含 `..`、开头的 `/`、反斜杠或 NUL | +| 符号链接 | 包里不能有符号链接 | +| 只读 | 宿主把已安装的包当作只读;插件的可写状态放在宿主给的状态目录,不能写回包目录 | + +### 三种签名要求 + +| `signatureRequirement` | 适用于 | 宿主怎样接受 | +| --- | --- | --- | +| `publisherPackage` | 官方插件和其他由宿主信任的发布者签名的包 | 签名文件必须有效,并由宿主信任的密钥签名。macOS 只有 DEBUG 构建允许把未签名的包从磁盘装成本地开发包(`localDevelopment`),发布构建拒绝 | +| `sameTeamAsHost` | 随宿主安装包一起发布的包 | 只认宿主自身已签名安装目录里的副本,由宿主的代码签名封存 | +| `none` | 社区包 | 不要求 Lithe 签名。从第三方市场安装时先用索引里的 SHA-256 校验压缩包;从本地 ZIP 或目录安装时先复制私有快照。两条路都要用户确认安装审查。`dev.lithe` 和 `dev.lithe.*` 不能使用(`plugin.manifest.invalid`)。包里带了签名文件仍要验证,无效签名不会被忽略 | + +宿主固定自己官方插件 ID 的签名要求,包不能把自己降级。从磁盘选文件不能绕过这个固定值、命名空间限制、发布者密钥检查,也不能绕过 `sameTeamAsHost` 要求的安装封存。社区 `none` 包在 DEBUG 和发布构建里都走自己的确认流程,不会被当成开发包。不认识 `none` 的旧宿主会把这类清单当作无效清单拒绝,这是有意的。 + +### `packageTrust` 1:随宿主发布的官方副本 + +级别 3 的包可以同时声明: + +- `requires.packageTrust: 1` +- `vendor.signatureRequirement: "publisherPackage"` +- `vendor.bundledSignatureRequirement: "sameTeamAsHost"` + +只有官方命名空间的 ID 可以这样声明,两条要求都必须与宿主固定的官方目录一致。只有实际路径在宿主封存的安装目录内、来源为 `bundled` 的副本可以不带发布者签名;单独安装、导入或回退的副本仍然需要发布者签名。只要带了签名就会验证,封存目录里也一样。这条声明本身不授予任何信任。不支持 `packageTrust` 1 的宿主在下载前就过滤掉这个版本并拒绝它的清单,不需要新增应用版本字段。没有这条声明的旧清单保持原来的规则。 + +### 签名文件 + +`lithe-plugin-signature.json` 的字段: + +| 字段 | 内容 | +| --- | --- | +| `schemaVersion` | `1` | +| `algorithm` | `"ed25519"` | +| `keyID` | 签名密钥的 ID,只能用小写字母、数字、`.`、`-`、`_` | +| `pluginID`、`pluginVersion` | 必须与 `plugin.json` 的 `id`、`version` 一致 | +| `files` | 包里除签名文件以外每个文件的相对路径到 SHA-256(小写十六进制)的映射;文件集合必须与包里的实际文件完全一致 | +| `signature` | 对规范负载的 Ed25519 签名,base64,64 字节 | + +规范负载是 UTF-8 文本,每行以 `\n` 结尾: + +```text +schemaVersion=1 +algorithm=ed25519 +keyID= +pluginID= +pluginVersion= +file== # 每个文件一行,按路径排序 +``` + +`token` 是 UTF-8 字节的 base64url 编码,去掉末尾的 `=`。没有发布者签名的包,宿主在安装时记录内容摘要:把每个文件写成 `token(path)=\n`,按路径排序后拼接,再取 SHA-256。之后每次扫描、暂存和回退都会重新比对。确切字节以 [`signature-vectors.json`](../../contract/v1/fixtures/package-trust/signature-vectors.json) 为准。 + +### 宿主现状 + +| 宿主 | 现状 | +| --- | --- | +| macOS | 完整实现:验证发布者签名(`PluginPackageSignature`),固定官方 ID 的签名要求,`sameTeamAsHost` 只认安装封存内的副本,社区包记录内容摘要并在扫描、暂存和回退时复查,声明 `packageTrust` 1 | +| Windows | 不验证发布者签名,不声明 `packageTrust`。磁盘安装复制私有快照,用 `plugins.marketplace.installReview` 生成审查,用户确认后安装;确认时只比对 `plugin.json` 有没有变,不计算其他文件的摘要,也不记录已批准的程序和来源 | + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Package layout](../../contract/v1/README.md#package-layout) | 包目录、路径和符号链接规则 | +| [Trust](../../contract/v1/README.md#trust) | 三种签名要求、`packageTrust` 1、官方 ID 固定和开发包规则 | +| [Capability versions (level 3)](../../contract/v1/README.md#capability-versions-level-3) | `packageTrust` 在能力表里的位置 | +| [Local disk snapshots and installation provenance (macOS)](../../contract/v1/README.md#local-disk-snapshots-and-installation-provenance-macos) | 内容摘要和安装来源记录,以及 Windows 的差距 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`package-trust/publisher-bundled.plugin.json`](../../contract/v1/fixtures/package-trust/publisher-bundled.plugin.json) | 级别 3 的官方语言服务器插件,`requires` 含 `packageTrust: 1`,同时声明 `publisherPackage` 和 `bundledSignatureRequirement: "sameTeamAsHost"` | +| [`package-trust/signature-vectors.json`](../../contract/v1/fixtures/package-trust/signature-vectors.json) | 跨平台签名向量:RFC 8032 测试密钥(不能签真实的包)、含非 ASCII 文件名的固定包、规范负载、确定性签名和内容摘要。CryptoKit 这类带随机数的签名器要用验证代替字节比对。目前只有 macOS 的测试使用这组向量 | + +### 相关指南 + +- [从零构建一个完整生命周期的 LSP 插件](../../guides/lsp-plugin-lifecycle.md):社区包的信任、签名和发布 + +--- + +## English + +This page covers the package layout, the signature requirement in the manifest, and the level 3 `packageTrust` capability. The signature requirement decides what evidence a host uses to accept a package: a publisher signature, the code signature of the host's own installation, or the user's confirmation for a community package. Hosts verify signatures and trust; Core validates that the manifest declarations are legal. + +| Item | Details | +| --- | --- | +| Manifest fields | `vendor.id`, `vendor.displayName`, `vendor.signatureRequirement`; level 3 may add `vendor.bundledSignatureRequirement` | +| Capability version | `packageTrust` 1, only for `vendor.bundledSignatureRequirement`, level 3. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Signature file | `lithe-plugin-signature.json` at the package root, an Ed25519 signature over every other file in the package | +| API number | Outside API-01–44 (an installation and packaging contract); see the note under the [API catalog](../api-catalog.md#planned-apis) | +| Permissions, activation events | None | +| Keys hosts trust | macOS trusts only the Lithe official publisher key (`lithe-official-plugins-v2`); community developers cannot produce a publisher signature a host trusts | +| Windows | Does not verify publisher signatures and does not advertise `packageTrust`; a package installed from disk relies only on the user confirming the install review. See [Host status](#host-status) | +| Official SDKs | Not SDK-related; signing is part of packaging and release | + +### Versions compared + +| Version | Adds | Core | macOS | Windows | +| --- | --- | --- | --- | --- | +| Baseline rules (no capability version, level 1 and later) | Package layout; `signatureRequirement` values `publisherPackage`, `sameTeamAsHost`, `none` | Validates the manifest and namespace | Supported: signature verification, installation seal, content digest | Partial: validates the manifest only; no signature verification and no content digest | +| `packageTrust` 1 (level 3) | `vendor.bundledSignatureRequirement: "sameTeamAsHost"`: an official plugin copy shipped with the host may omit the publisher signature | Supported | Supported | Not supported (not advertised; Core refuses packages that require it) | + +### Package layout + +| Rule | Details | +| --- | --- | +| Root | `plugin.json`, `lithe-plugin-signature.json` (when required), and the resources the manifest references | +| Paths | Every path in `plugin.json` is relative to the package root, uses `/`, and may not contain `..`, a leading `/`, a backslash, or NUL | +| Symbolic links | Packages may not contain symbolic links | +| Read-only | Hosts treat an installed package as read-only; a plugin's writable state goes in the state directory the host provides, never back into the package | + +### The three signature requirements + +| `signatureRequirement` | For | How a host accepts it | +| --- | --- | --- | +| `publisherPackage` | Official plugins and other packages signed by a publisher the host trusts | The signature file must be valid and signed by a key the host trusts. On macOS only DEBUG builds may install an unsigned package from disk as a local development package (`localDevelopment`); release builds refuse it | +| `sameTeamAsHost` | Packages shipped inside the host's installation | Honored only for a copy inside the host's own signed installation, sealed by the host's code signature | +| `none` | Community packages | No Lithe signature required. From a third-party marketplace the archive is first checked against the index SHA-256; from a local ZIP or directory a private snapshot is taken first. Both paths require the user to confirm the install review. `dev.lithe` and `dev.lithe.*` may not use it (`plugin.manifest.invalid`). A signature file that is present is still verified; an invalid signature is never ignored | + +A host pins the signature requirement of its official plugin IDs so a package cannot downgrade itself. Selecting a file from disk never overrides that pin, the namespace restrictions, the publisher-key checks, or the installation seal `sameTeamAsHost` requires. Community `none` packages use their own approval path in both DEBUG and release builds and are never treated as development packages. Older hosts that do not know `none` refuse such manifests as invalid, which is intended. + +### `packageTrust` 1: official copies shipped with the host + +A level 3 package may declare all of: + +- `requires.packageTrust: 1` +- `vendor.signatureRequirement: "publisherPackage"` +- `vendor.bundledSignatureRequirement: "sameTeamAsHost"` + +Only official-namespace IDs may do so, and both requirements must match the host's pinned official catalog. Only a copy whose actual path is inside the host's sealed installation and whose origin is `bundled` may omit the publisher signature; every independently installed, imported, or rolled-back copy still needs it. A present signature is always verified, inside the seal too. The declaration alone grants no trust. Hosts without `packageTrust` 1 filter the version before download and refuse its manifest; no new application-version field is needed. An older manifest without the declaration keeps its existing rules. + +### Signature file + +Fields of `lithe-plugin-signature.json`: + +| Field | Details | +| --- | --- | +| `schemaVersion` | `1` | +| `algorithm` | `"ed25519"` | +| `keyID` | ID of the signing key, limited to lowercase letters, digits, `.`, `-`, and `_` | +| `pluginID`, `pluginVersion` | Must equal `id` and `version` in `plugin.json` | +| `files` | Map from the relative path of every file except the signature file to its lowercase hex SHA-256; the set must equal the package's actual files exactly | +| `signature` | Base64 Ed25519 signature of the canonical payload, 64 bytes | + +The canonical payload is UTF-8 text with every line ending in `\n`: + +```text +schemaVersion=1 +algorithm=ed25519 +keyID= +pluginID= +pluginVersion= +file== # one line per file, sorted by path +``` + +`token` is the base64url encoding of the UTF-8 bytes with trailing `=` removed. For a package without a publisher signature, the host records a content digest at install time: each file becomes `token(path)=\n`, the lines are sorted by path and concatenated, and the result is hashed with SHA-256. The digest is rechecked on every scan, staging, and rollback. [`signature-vectors.json`](../../contract/v1/fixtures/package-trust/signature-vectors.json) is authoritative for the exact bytes. + +### Host status + +| Host | Status | +| --- | --- | +| macOS | Complete: verifies publisher signatures (`PluginPackageSignature`), pins the signature requirement of official IDs, honors `sameTeamAsHost` only inside the installation seal, records a content digest for community packages and rechecks it on scan, staging, and rollback, and advertises `packageTrust` 1 | +| Windows | Does not verify publisher signatures and does not advertise `packageTrust`. Disk installation takes a private snapshot, builds the review with `plugins.marketplace.installReview`, and installs after the user confirms; confirmation only checks that `plugin.json` did not change, computes no digest of the other files, and records no approved programs or provenance | + +### Contract sections + +| Section | Covers | +| --- | --- | +| [Package layout](../../contract/v1/README.md#package-layout) | Package root, path, and symbolic-link rules | +| [Trust](../../contract/v1/README.md#trust) | The three signature requirements, `packageTrust` 1, official ID pinning, and development packages | +| [Capability versions (level 3)](../../contract/v1/README.md#capability-versions-level-3) | Where `packageTrust` sits in the capability table | +| [Local disk snapshots and installation provenance (macOS)](../../contract/v1/README.md#local-disk-snapshots-and-installation-provenance-macos) | Content digests, installation provenance records, and the Windows gap | + +### Examples and cases + +| File | Contents | +| --- | --- | +| [`package-trust/publisher-bundled.plugin.json`](../../contract/v1/fixtures/package-trust/publisher-bundled.plugin.json) | A level 3 official language-server plugin whose `requires` includes `packageTrust: 1`, declaring both `publisherPackage` and `bundledSignatureRequirement: "sameTeamAsHost"` | +| [`package-trust/signature-vectors.json`](../../contract/v1/fixtures/package-trust/signature-vectors.json) | Cross-platform signature vectors: the RFC 8032 test key (never for real packages), a fixed package with non-ASCII file names, the canonical payload, a deterministic signature, and the content digest. Signers that add randomness, such as CryptoKit, check by verification instead of byte comparison. Only the macOS tests consume these vectors today | + +### Related guides + +- [Building a complete-lifecycle LSP plugin](../../guides/lsp-plugin-lifecycle.md): community package trust, signing, and release diff --git a/reference/domains/project-generators.md b/reference/domains/project-generators.md new file mode 100644 index 0000000..2087724 --- /dev/null +++ b/reference/domains/project-generators.md @@ -0,0 +1,119 @@ +# 项目生成器 / Project generators + +[中文](#中文) · [English](#english) + +## 中文 + +插件可以在宿主的"新建项目"窗口里加入口:某种语言的普通项目,或框架、脚手架生成器。窗口归宿主:入口列表和搜索、项目名称和位置、"创建 Git 仓库"选项、空项目、"创建"按钮。生成器只声明自己的字段、结果需要的语言和构建系统、生成项目的步骤。Core 计算表单和生成计划,宿主执行,宿主代码不按生成器 ID 分支。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `projectGenerators[]`:`id`、`displayName`、`section`、`order`、`icon`、`fields`、`requirements`、`steps`、`openFiles` | +| 能力版本 | `projectGenerators` 1,级别 3。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | 没有进程协议;生成器只是数据,由 Core 命令计算 | +| API 编号 | [API-33](../api-catalog.md#现在能用的-api):项目生成器 | +| 权限 | `project.generate` | +| 激活事件 | 不需要 | +| Windows | 不支持;Windows 宿主不声明 `projectGenerators` | + +### 版本对照 + +| 版本 | 最低级别 | 新增 | Core | macOS | Windows | +| --- | --- | --- | --- | --- | --- | +| `projectGenerators` 1 | 3 | 两个分区(`project`、`generator`);五种字段(`text`、`enum`、`boolean`、`multiSelect`、`runtime`);条件、模板;三种步骤(`files`、`command`、`archive`);五个 Core 命令 | 支持 | 支持 | 不支持 | + +### 字段、步骤与命令 + +| 部分 | 内容 | +| --- | --- | +| `section` | `project` 放在"新建项目"下(某种语言的普通项目),`generator` 放在"生成器"下(框架或脚手架);按分区、`order`(0–1000,默认 100)、名称排序 | +| 字段类型 | `text`(`default` 模板、`pattern`、`maxLength`、`maximumVersion`)、`enum`(1–16 个选项)、`boolean`、`multiSelect`(1–128 个选项,可分组)、`runtime`(宿主发现的安装,目前是 `java`) | +| 条件 | `when`、`visibleWhen`:`equals`、`notEquals`、`oneOf`;隐藏字段不校验,条件里视为不存在 | +| 清单模板 | `${project.name}`、`${field.}`、`${field..version}`、`${field..major}`,修饰符 `path`、`package`、`lower` | +| 模板文件 | 用 `{{ … }}`,因为 POM、Gradle 脚本里本来就有 `${…}`;`dot-` 开头的路径段变成点文件(`dot-gitignore` → `.gitignore`) | +| `files` 步骤 | 把包内模板目录写进项目,保留可执行位;最多 1024 个文件、单个 1 MiB、合计 8 MiB | +| `command` 步骤 | 像构建工具一样解析可执行文件,在暂存目录里运行;`timeoutSeconds` 默认 600、最多 1800;`collect` 把子目录内容上移到项目根 | +| `archive` 步骤 | 下载 zip 并用 `project.extractArchive` 解压;必须是 HTTPS(`localhost`、`127.0.0.1`、`[::1]` 可用 HTTP),不带凭据;最多 20,000 个条目、解压后 512 MiB | +| Core 命令 | `project.generatorCatalog`、`project.generatorForm`、`project.generationPlan`、`project.checkName`、`project.extractArchive` | + +步骤在暂存目录里按顺序执行,结束后宿主把它移到最终位置(目标已存在时拒绝)、可选记录 `java` 运行时为项目 JDK、按需创建 Git 仓库并打开项目。失败或取消会删掉暂存目录,不留下任何东西(`plugin.project.generationFailed`)。`requirements` 只用来提示启用或去市场安装相应插件,不会阻止创建。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Project generators](../../contract/v1/README.md#project-generators) | 宿主和生成器各自负责的部分、清单字段 | +| [Fields](../../contract/v1/README.md#fields) | 字段类型、属性、项目名称规则 | +| [Conditions](../../contract/v1/README.md#conditions) | `when`、`visibleWhen` | +| [Templates](../../contract/v1/README.md#templates-1) | 清单模板、模板文件、点文件 | +| [Steps](../../contract/v1/README.md#steps) | 三种步骤和限制 | +| [Commands](../../contract/v1/README.md#commands-1) | 五个 Core 命令、表单与计划格式、失败清理 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`project-generators/project-generator.plugin.json`](../../contract/v1/fixtures/project-generators/project-generator.plugin.json) | 三个生成器:`app`(`project` 分区,按构建系统条件写模板文件)、`archetype`(运行脚手架命令)、`initializr`(从可配置服务器下载压缩包) | +| [`project-generators/project-plans.json`](../../contract/v1/fixtures/project-generators/project-plans.json) | 10 个 `project.*` 命令的标准答案:表单默认值与派生、计划写模板、运行时过旧、运行命令、下载压缩包、拒绝非 HTTPS 服务器、项目名称检查 | +| [`project-generators/templates/`](../../contract/v1/fixtures/project-generators/templates/) | `app` 的模板目录:`app/README.md`、`app/dot-gitignore`、`main/Main.java`(Java 示例);`.gitattributes` 关闭换行转换,保证模板逐字节输出 | + +共享示例里没有生成器的清单拒绝用例;计划层面的拒绝(运行时过旧、非 HTTPS 服务器、名称带斜杠)在 `project-plans.json` 里。 + +--- + +## English + +A plugin can add entries to the host's New Project window: a plain project of a language, or a framework or scaffolding generator. The window belongs to the host: the entry list and its search, the project name and location, the "create Git repository" option, the empty project, and the Create button. A generator declares only its own fields, the languages and build systems the result needs, and the steps that produce it. Core computes the form and the generation plan, the host executes it, and no host code branches on a generator ID. + +| Item | Value | +| --- | --- | +| Manifest field | `projectGenerators[]`: `id`, `displayName`, `section`, `order`, `icon`, `fields`, `requirements`, `steps`, `openFiles` | +| Capability versions | `projectGenerators` 1, at level 3. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | No process protocol; a generator is data that Core commands evaluate | +| API IDs | [API-33](../api-catalog.md#apis-usable-now): project generators | +| Permissions | `project.generate` | +| Activation events | None needed | +| Windows | Not supported; the Windows host does not advertise `projectGenerators` | + +### Versions compared + +| Version | Lowest level | Adds | Core | macOS | Windows | +| --- | --- | --- | --- | --- | --- | +| `projectGenerators` 1 | 3 | Two sections (`project`, `generator`); five field types (`text`, `enum`, `boolean`, `multiSelect`, `runtime`); conditions and templates; three step kinds (`files`, `command`, `archive`); five Core commands | Yes | Yes | No | + +### Fields, steps, and commands + +| Part | Content | +| --- | --- | +| `section` | `project` lists the entry under "New Project" (a plain project of a language); `generator` lists it under "Generators" (a framework or scaffolding tool); sorted by section, `order` (0–1000, default 100), then name | +| Field types | `text` (`default` template, `pattern`, `maxLength`, `maximumVersion`), `enum` (1–16 options), `boolean`, `multiSelect` (1–128 options, optionally grouped), `runtime` (a host-discovered installation, currently `java`) | +| Conditions | `when` and `visibleWhen`: `equals`, `notEquals`, `oneOf`; a hidden field is not validated and reads as absent in conditions | +| Manifest templates | `${project.name}`, `${field.}`, `${field..version}`, `${field..major}`, with the modifiers `path`, `package`, and `lower` | +| Template files | Use `{{ … }}`, because POMs and Gradle scripts already contain `${…}`; a path segment starting with `dot-` becomes a dotfile (`dot-gitignore` → `.gitignore`) | +| `files` step | Writes a packaged template directory into the project, keeping the executable bit; at most 1024 files, 1 MiB each, 8 MiB in total | +| `command` step | Resolves the executable like a build tool and runs it in the staging directory; `timeoutSeconds` defaults to 600, at most 1800; `collect` moves a subdirectory's contents up into the project root | +| `archive` step | Downloads a zip and extracts it with `project.extractArchive`; HTTPS only (HTTP allowed for `localhost`, `127.0.0.1`, `[::1]`), no credentials; at most 20,000 entries and 512 MiB extracted | +| Core commands | `project.generatorCatalog`, `project.generatorForm`, `project.generationPlan`, `project.checkName`, `project.extractArchive` | + +Steps run in order in a staging directory. Afterwards the host moves it to its final location (refusing an existing one), may record a `java` runtime as the project's JDK, creates the Git repository when asked, and opens the project. A failure or cancellation removes the staging directory and leaves nothing behind (`plugin.project.generationFailed`). `requirements` only prompt the user to enable or install the providing plugins; they never block creation. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Project generators](../../contract/v1/README.md#project-generators) | What the host and the generator each own; manifest fields | +| [Fields](../../contract/v1/README.md#fields) | Field types, properties, project name rules | +| [Conditions](../../contract/v1/README.md#conditions) | `when` and `visibleWhen` | +| [Templates](../../contract/v1/README.md#templates-1) | Manifest templates, template files, dotfiles | +| [Steps](../../contract/v1/README.md#steps) | The three step kinds and their limits | +| [Commands](../../contract/v1/README.md#commands-1) | The five Core commands, form and plan shapes, cleanup on failure | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`project-generators/project-generator.plugin.json`](../../contract/v1/fixtures/project-generators/project-generator.plugin.json) | Three generators: `app` (the `project` section, writing template files conditioned on the build system), `archetype` (running a scaffolding command), `initializr` (downloading an archive from a configurable server) | +| [`project-generators/project-plans.json`](../../contract/v1/fixtures/project-generators/project-plans.json) | Golden answers for 10 `project.*` command cases: form defaults and derived values, a plan writing templates, a runtime that is too old, running a command, downloading an archive, refusing a non-HTTPS server, project name checks | +| [`project-generators/templates/`](../../contract/v1/fixtures/project-generators/templates/) | The `app` template directory: `app/README.md`, `app/dot-gitignore`, `main/Main.java` (a Java example); `.gitattributes` disables line-ending conversion so templates are emitted byte for byte | + +The shared fixtures have no manifest refusal cases for generators; plan-level refusals (a runtime that is too old, a non-HTTPS server, a name with a slash) are in `project-plans.json`. diff --git a/reference/domains/settings.md b/reference/domains/settings.md new file mode 100644 index 0000000..7abaf3e --- /dev/null +++ b/reference/domains/settings.md @@ -0,0 +1,109 @@ +# 插件设置 / Plugin settings + +[中文](#中文) · [English](#english) + +## 中文 + +插件在清单里声明用户可以修改的选项。宿主负责显示控件、保存值、区分全局和项目两层,并把生效值传给 Core;Core 合并后展开到启动计划、`initializationOptions` 或语言服务器的 `workspaceConfiguration`。插件不写设置页代码,宿主也不需要认识插件。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `settings`、`settingActions`(`settings` 2) | +| 能力版本 | `settings` 1–2;相关的 `languageServers` 2、`executableResolution` 1–2,见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | 没有专用协议;`settingActions` 调用的是插件自己的[插件函数](backend.md) | +| API 编号 | [API-19](../api-catalog.md#现在能用的-api):声明式设置。运行期读取、查看来源、修改设置和变化事件仍在计划中 | +| 权限 | 不需要额外权限;设置不会改变运行的程序,所以不进入安装审查的程序列表 | +| 激活事件 | 不需要;值随下一次启动、模型重载、运行或 `immediate` 推送生效 | +| Windows | 宿主声明 `settings` 1、2 | + +### 版本对照 + +| 版本 | 最低级别 | 新增 | +| --- | --- | --- | +| `settings` 1 | 2 | 插件级设置,所有工作区共享;类型 `integer`(带 `minimum`、`maximum`、`unit`)、`enum`(1–16 个选项)、`boolean`;值变化时重启插件的语言服务器 | +| `settings` 2 | 3 | 类型 `multiSelect`、`text`(必须带 `pattern`)、`file`、`directory`;`scope`(`global` 或 `project`,项目值存在 `.lithe/plugin-settings.json`);`effect`(`restart`、`immediate`、`reload`、`nextRun`);`page` 放到某个语言或构建系统的设置页;`section` 分组;最多 16 个 `settingActions` 按钮 | +| `languageServers` 2 | 3 | `workspaceConfiguration`:`immediate` 设置不重启进程,直接推送给运行中的语言服务器 | +| `executableResolution` 1 | 3 | 可执行文件候选可以引用一个全局 `file` 设置(`effect` 为 `reload` 或 `nextRun`),配置了的路径失败时不回退 | +| `executableResolution` 2 | 3 | 工具定义候选和项目/子项目工具链绑定;macOS 只有只读设置页 | + +生效顺序是项目值(只对 `scope: "project"`)、全局值、默认值;存储的值不再被声明接受时跳过,不会让启动失败。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Plugin settings (API 2)](../../contract/v1/README.md#plugin-settings-api-2) | `settings` 1 的字段、类型和模板 `${setting:}` | +| [Settings v2: placement, scope, effect, and page buttons](../../contract/v1/README.md#settings-v2-placement-scope-effect-and-page-buttons) | `settings` 2 的类型、层级、生效时机、放置位置和按钮 | +| [Workspace configuration (`languageServers` 2)](../../contract/v1/README.md#workspace-configuration-languageservers-2) | `workspaceConfiguration` 的展开和推送 | +| [Shared executable candidates (`executableResolution` 1/2)](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | 用设置选择工具路径、候选顺序和严格选择 | +| [Launch templates (API 2)](../../contract/v1/README.md#launch-templates-api-2) | 设置值在模板里的替换规则 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`settings/settings-v2.plugin.json`](../../contract/v1/fixtures/settings/settings-v2.plugin.json) | `settings` 2:放在 Elixir 语言页和 Mix 构建系统页的设置、`immediate` 设置、`settingActions` 按钮 | +| [`languages/node-runtime.plugin.json`](../../contract/v1/fixtures/languages/node-runtime.plugin.json) | `settings` 1:级别 2 的插件设置 | +| [`build-systems/build-tool-path.plugin.json`](../../contract/v1/fixtures/build-systems/build-tool-path.plugin.json) | `settings` 2 加 `executableResolution` 1:用 `file` 设置选择构建工具 | +| [`debug-adapters/debug-adapter.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json) | `settings` 2:调试适配器用 `file` 设置(`nextRun`)选择要调试的程序 | + +设置相关的拒绝用例在 [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json)。 + +### 相关指南 + +- [从零构建一个完整生命周期的 LSP 插件](../../guides/lsp-plugin-lifecycle.md) +- [为语言插件接入构建、运行与测试](../../guides/build-tool-integration.md) + +--- + +## English + +A plugin declares the options a user may change in its manifest. The host draws the controls, stores the values, keeps the global and project layers apart, and passes the effective values to Core; Core merges them and expands them into launch plans, `initializationOptions`, or a language server's `workspaceConfiguration`. The plugin writes no settings-page code, and the host never needs to know the plugin. + +| Item | Value | +| --- | --- | +| Manifest fields | `settings`, `settingActions` (`settings` 2) | +| Capability versions | `settings` 1–2; related `languageServers` 2 and `executableResolution` 1–2; see [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | None of its own; `settingActions` call the plugin's own [plugin functions](backend.md) | +| API IDs | [API-19](../api-catalog.md#apis-usable-now): declarative settings. Runtime reads, source inspection, writes, and change events are still planned | +| Permissions | None; settings never change which program runs, so they are not in the install review's program list | +| Activation | Not needed; values take effect at the next start, model reload, run, or `immediate` push | +| Windows | Advertises `settings` 1 and 2 | + +### Versions compared + +| Version | Lowest level | Adds | +| --- | --- | --- | +| `settings` 1 | 2 | Plugin-wide settings shared by every workspace; types `integer` (with `minimum`, `maximum`, `unit`), `enum` (1–16 options), `boolean`; a change restarts the plugin's language servers | +| `settings` 2 | 3 | Types `multiSelect`, `text` (requires `pattern`), `file`, `directory`; `scope` (`global` or `project`, project values in `.lithe/plugin-settings.json`); `effect` (`restart`, `immediate`, `reload`, `nextRun`); `page` on a language or build-system settings page; `section` grouping; up to 16 `settingActions` buttons | +| `languageServers` 2 | 3 | `workspaceConfiguration`: `immediate` settings reach the running language server without a restart | +| `executableResolution` 1 | 3 | An executable candidate may reference a global `file` setting (`effect` `reload` or `nextRun`); a configured path that fails never falls back | +| `executableResolution` 2 | 3 | Tool-definition candidates and project/subproject toolchain bindings; macOS has read-only settings pages | + +Values resolve as project (only for `scope: "project"`), then global, then default; a stored value the declaration no longer accepts is skipped and never fails a launch. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Plugin settings (API 2)](../../contract/v1/README.md#plugin-settings-api-2) | `settings` 1 fields, types, and `${setting:}` templates | +| [Settings v2: placement, scope, effect, and page buttons](../../contract/v1/README.md#settings-v2-placement-scope-effect-and-page-buttons) | `settings` 2 types, layers, effect timing, placement, and buttons | +| [Workspace configuration (`languageServers` 2)](../../contract/v1/README.md#workspace-configuration-languageservers-2) | Expanding and pushing `workspaceConfiguration` | +| [Shared executable candidates (`executableResolution` 1/2)](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | Selecting a tool path with a setting, candidate order, strict selection | +| [Launch templates (API 2)](../../contract/v1/README.md#launch-templates-api-2) | How setting values are substituted in templates | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`settings/settings-v2.plugin.json`](../../contract/v1/fixtures/settings/settings-v2.plugin.json) | `settings` 2: settings on the Elixir language page and the Mix build-system page, an `immediate` setting, `settingActions` buttons | +| [`languages/node-runtime.plugin.json`](../../contract/v1/fixtures/languages/node-runtime.plugin.json) | `settings` 1: plugin settings at level 2 | +| [`build-systems/build-tool-path.plugin.json`](../../contract/v1/fixtures/build-systems/build-tool-path.plugin.json) | `settings` 2 with `executableResolution` 1: a `file` setting selects the build tool | +| [`debug-adapters/debug-adapter.plugin.json`](../../contract/v1/fixtures/debug-adapters/debug-adapter.plugin.json) | `settings` 2: a debug adapter uses a `file` setting (`nextRun`) to pick the program to debug | + +Settings rejections are in [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json). + +### Related guides + +- [Building an LSP plugin end to end](../../guides/lsp-plugin-lifecycle.md) +- [Build, run, and test for language plugins](../../guides/build-tool-integration.md) diff --git a/reference/domains/toolchains.md b/reference/domains/toolchains.md new file mode 100644 index 0000000..cca6dbe --- /dev/null +++ b/reference/domains/toolchains.md @@ -0,0 +1,279 @@ +# 工具链 / Toolchains + +[中文](#中文) · [English](#english) + +## 中文 + +插件可以告诉 Lithe 一个工具(JDK、Maven、Node、Go 等)长什么样、去哪里找、怎么判断版本,也可以用后端回答"这台机器装了哪些版本、这个项目要哪个版本"。宿主没有内置任何工具定义、别名表或按工具名的分支:工具定义只来自已启用的插件,Core 负责校验和选择,宿主只做文件系统观察、运行版本探针和启动后端这些本机 I/O。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `toolDefinitions[]`(工具定义)、`toolchainProviders[]`(工具链提供者);`executables` 候选集里的 `toolchain` 候选 | +| 能力版本 | 工具定义:`toolDefinitions` 1 + `executableResolution` 2;提供者:`toolchainProviders` 1 + `backend` 2 + `serverProcess` 1;都是级别 3。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheToolchains` 1:Core 只向声明了提供者的插件会话提供 `experimental.litheToolchains: {versions: [1], providers}`,后端回答 `{version: 1, providers}`;请求方法是 `toolchains/query`。见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | [API-23](../api-catalog.md#现在能用的-api):工具链与受管进程(部分完成) | +| 权限 | 工具定义不需要权限;提供者需要 `backend.launch`、`toolchains.provide` | +| 激活事件 | 不需要;提供者被宿主的第一次查询唤起,不需要页面或启动事件 | +| 后端启动 | 提供者的后端只能是包内可执行文件(`packaged`),或包内运行时(`runtime` + `packagedRuntime`),不能依赖需要发现的运行时 | +| Windows | 不支持;Windows 宿主没有工具链适配器,不声明 `toolDefinitions`、`toolchainProviders`、`executableResolution` | +| 官方 SDK | 三个官方 SDK 都还没有实现 `litheToolchains`,提供者后端要自己处理 `toolchains/query` | + +### 版本对照 + +| 版本 | 新增 | Core | macOS | Windows | 官方 SDK | +| --- | --- | --- | --- | --- | --- | +| `executableResolution` 1 | [共享可执行文件候选](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12):设置、环境变量、命令、宿主工具链等候选的排序和严格选择 | 支持 | 支持 | 不支持 | 不涉及 | +| `executableResolution` 2 | 工具定义里的候选,构建候选集里的 `toolchain` 候选可以指向插件定义的工具;不改变已有构建候选集 | 支持 | 支持(只读设置、构建与运行工具) | 不支持 | 不涉及 | +| `toolDefinitions` 1 | 工具身份、可执行文件布局、版本探针、声明式发现、主目录变量、导出的环境变量、`PATH` 前缀、启动模板 | 支持 | 支持(只读设置、构建与运行工具) | 不支持 | 不涉及 | +| `toolchainProviders` 1 | 提供者声明 `{id, displayName, tools, watchFiles?}`;按权限校验应答 | 支持 | 支持(只读设置、构建与运行工具) | 不支持 | 不涉及 | +| `litheToolchains` 1 | `toolchains/query` 请求和应答;后端从声明的提供者中选出非空子集 | 支持 | 支持 | 不支持 | 都未实现 | +| `.lithe/toolchains/local.json` `version` 1 | 机器本地文档:旧的 `toolchains` 条目和按目录的 `pins` | 支持读取和解析 | 只读,不保存 | 不支持 | 不涉及 | + +### 两条安装来源 + +一个工具可以走其中一条、两条都走,或者都不走(只靠固定、项目文件、全局默认和登录 shell 里的命令)。 + +| 来源 | 由谁声明 | 适合 | 谁执行 | +| --- | --- | --- | --- | +| 声明式发现 | 工具定义里的 `discovery`、`homeLayouts`、`homeVariable`、`commands`、`version` | 目录形状固定的安装:用户主目录、环境变量或系统绝对目录下的单段 `*` 通配,可选的主目录后缀,加上对版本探针输出的正则 | 宿主展开 `discoveryPlan` 的扫描,不运行插件代码 | +| 提供者查询 | `toolchainProviders[]` 回答 `toolchains/query` | 声明式原语表达不了的位置:注册表、属性列表、版本管理器数据库、命令输出、版本管理器自己的版本文件 | 提供者的包内后端 | + +声明式原语已经冻结:根目录只有 `home`、`environment`、`absolute` 三种,通配只有单段 `*`。新的安装形状由提供者回答,不再增加根目录类型或宿主解析器。没有 `watchFiles` 的提供者只报告安装,应答里不能有项目版本要求。 + +### 选择顺序 + +| 层 | 规则 | +| --- | --- | +| 固定(pin) | 最近的祖先目录的固定;只按完整路径段匹配,前缀相似的兄弟目录不继承。固定不可用时直接失败,不回退 | +| 项目 | 最近的声明目录;同一深度 wrapper > 环境变量 > 版本声明;同一个文件里原生读取器优先于兼容读取器。离得近的项目要求遮住更上层的要求 | +| 全局 | Lithe 的全局默认 > 没有歧义(或用户已选)的提供者全局默认 > 登录 shell | +| 自动 | 已安装、满足最低版本的最高版本;手动添加的安装只能被固定,不参与自动选择。不同主目录有相同最高版本时算冲突,要求用户选择 | + +没有安装的项目要求会带着原因保留为"未满足",即使更低的层选中了别的安装。冲突必须由用户明确选择,不按插件 ID 或路径先后决定。一次操作还可以带 `fixed: {home}` 或 `fixed: {executable}`,它排在目录固定之前,无效时直接失败。 + +### Core 命令 + +所有命令都只处理 UTF-8 JSON,输入上限 4 MiB,不做任何本机 I/O。成功返回 `{status: "ok", <键>: 值}`,拒绝返回 `{status: "rejected", error: {code, message, field?}}`。 + +| 命令 | 输入 | 成功时的键 | 用途 | +| --- | --- | --- | --- | +| `toolchains.registry` | `{plugins: [{manifest, verifiedOfficial}]}` | `registry` | 重新校验完整清单,汇总定义和提供者;最多 128 个插件、512 个定义、256 个提供者 | +| `toolchains.decodeReply` | 查询请求、提供者应答、宿主观察到的文件系统事实 | `snapshot` | 校验一次 `toolchains/query` 应答,得到 `{installations, statements, watch, diagnostics}` | +| `toolchains.failureReply` | `{params, code}` | `reply` | 查询没有应答时生成对应的 JSON-RPC 错误;`code` 只能是 `plugin.toolchain.timeout` 或 `plugin.toolchain.providerUnavailable` | +| `toolchains.resolvePins` | `{document, directory, tool}` | `resolution` | 在 `local.json` 里找最近的固定,返回 `{pin, sourceDirectory}` | +| `toolchains.resolve` | 某个目录的完整解析输入 | `resolution` | 走四层选择,结果是 `resolved`、`skip` 或 `terminalFailure`,附带 `trace`、`unmet`、`conflicts` | +| `toolchains.checkVersion` | `{registry, tool, output, minimumVersion?}` | `version` | 用定义的版本正则取版本并比较;最多 8 段数字、64 字节,不解释别名 | +| `toolchains.discoveryPlan` | `{registry, platform}` | `plan` | 每个工具的扫描计划和声明它的提供者;最多 256 个根目录、4096 个条目、64 层祖先 | +| `toolchains.candidates` | 设置页的发现输入 | `candidates` | 设置页显示的全局层和自动层:`{tool, shell, installations, skipped, selected, conflicts}`;第一次调用把待探测的可执行文件列为 `probeFailed`,宿主运行探针后再调用一次 | + +Core 另有 `toolchains.resolveConsumerPreferences` 和 `toolchains.editConsumerPreferences`,读取和编辑 `.lithe/run/local.json` 里 `toolchain.preferences` 的按消费者工具选择,只给宿主自己的运行和构建配置使用。这两个命令不属于插件契约。 + +### 提供者应答的限制 + +| 项目 | 规则 | +| --- | --- | +| 应答内容 | `result: {operationID, generation, providerID, installations, statements, watch}`;安装是 `{tool, version, home}`,版本要求是 `{tool, scope, source, request, installation, kind, reader}` | +| 大小 | 最多 1 MiB、512 个安装、256 条要求、256 个监视路径 | +| 路径 | 文件系统事实来自宿主,不来自提供者;主目录不能在任何工作区根目录里,必须符合定义的可执行文件布局 | +| 身份 | JSON-RPC `id`、`operationID`、`generation`、`providerID` 必须和请求一致;数字 `1` 和字符串 `"1"` 是不同的 ID;远程 authority 一律拒绝 | +| 版本要求 | `request` 的写法对 Core 不透明(例如 `lts`),宿主不解释;`installation: null` 表示要求没有被满足 | + +提供者可以在 JSON-RPC 错误里使用下面的错误码,`decodeReply` 按这张表校验(一个数字只对应一个语义): + +| JSON-RPC | 语义码 | 含义 | +| --- | --- | --- | +| `-32601` | `plugin.toolchain.methodUnavailable` | 查询方法或能力没有协商 | +| `-32602` | `plugin.toolchain.invalidParams` | 请求格式错误或声明的输入无效 | +| `-32800` | `plugin.toolchain.cancelled` | 调用方取消了请求 | +| `-32070` | `plugin.toolchain.permissionDenied` | 缺少权限、审查或启用状态 | +| `-32071` | `plugin.toolchain.authorityUnsupported` | 没有这个 authority 的适配器 | +| `-32072` | `plugin.toolchain.timeout` | 超过本地截止时间 | +| `-32073` | `plugin.toolchain.providerUnavailable` | 后端启动失败、断开或上游查询失败 | +| `-32074` | `plugin.toolchain.invalidReply` | 应答格式错误,或安装引用了别的工具 | +| `-32075` | `plugin.toolchain.limitExceeded` | 超过应答、扫描或监视预算 | +| `-32076` | `plugin.toolchain.unsafePath` | 绝对路径、包含关系或可执行文件证据不安全 | +| `-32077` | `plugin.toolchain.operationExpired` | 发出查询时上下文或代次已经失效 | + +### 宿主现状 + +| 用途 | macOS | Windows | +| --- | --- | --- | +| 设置页显示发现结果 | 两条来源都跑,只读;监视的文件变化时重新加载整个快照;不保存任何东西 | 不支持 | +| 构建工具、插件运行方案的可执行文件 | 候选或运行方案指向插件定义的工具(不在宿主旧的工具链表里)时,单独重新解析一次;设置页的发现结果不会被当作启动许可 | 不支持 | +| Java、语言服务器 | 仍然走原来的适配器 | 不支持 | +| 保存固定、`toolchainConsumers`、通用运行时启动、提供者热激活 | 尚未提供 | 尚未提供 | + +`toolchains.provide` 可以影响某个已定义工具启动哪个程序,但不授权替换任意命令、安装软件、访问网络或执行工作区代码。短定义 ID(例如 `java`)要求经过独立验证的官方来源;社区插件的定义 ID 写成 `<自己的插件 ID>:<本地 ID>`,社区提供者可以回答短 ID。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [Plugin-owned toolchains (Core foundation)](../../contract/v1/README.md#plugin-owned-toolchains-core-foundation) | 契约总入口里的工具链说明和当前宿主状态 | +| [Plugin-owned toolchains: Core foundation](../../contract/v1/toolchains/README.md) | 已实现部分的完整说明 | +| [Manifest admission and registry](../../contract/v1/toolchains/README.md#manifest-admission-and-registry) | 能力和权限要求、定义字段、提供者字段、ID 规则 | +| [Two installation routes](../../contract/v1/toolchains/README.md#two-installation-routes) | 声明式发现和提供者查询 | +| [Commands and envelopes](../../contract/v1/toolchains/README.md#commands-and-envelopes) | 八个 Core 命令、返回格式、注册表限制 | +| [Provider reply validation](../../contract/v1/toolchains/README.md#provider-reply-validation) | `decodeReply`、应答格式与限制、`failureReply` | +| [Pins and snapshot resolution](../../contract/v1/toolchains/README.md#pins-and-snapshot-resolution) | `local.json`、`resolve`、四层选择、`checkVersion`、`discoveryPlan` | +| [Settings candidates](../../contract/v1/toolchains/README.md#settings-candidates) | `candidates` 和两次调用的探测流程 | +| [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | `executableResolution` 1、2 的候选集 | +| [提案](../../contract/v1/toolchains/proposal/README.md):[manifest.md](../../contract/v1/toolchains/proposal/manifest.md)、[protocol.md](../../contract/v1/toolchains/proposal/protocol.md) | 更大范围的设计(消费者迁移、原生生命周期、保存、版本管理器插件),尚未实现;[错误码表](../../contract/v1/toolchains/proposal/protocol.md#stable-failure-mapping)已被 Core 使用 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`toolchains/tool-definition.plugin.json`](../../contract/v1/fixtures/toolchains/tool-definition.plugin.json) | 生产形态的工具定义:`java`,8 个候选(项目/全局设置、项目/全局工具链请求、macOS/Linux 与 Windows 的 `JAVA_HOME`、`java` 命令、已安装的工具链),在 macOS 的 `JavaVirtualMachines` 目录里发现 `*.jdk` | +| [`toolchains/toolchain-provider.plugin.json`](../../contract/v1/fixtures/toolchains/toolchain-provider.plugin.json) | 生产形态的提供者:`mise`,覆盖 `java`、`node`、`maven`、`pnpm`、`go`,监视 6 个文件(`.tool-versions` 用兼容读取器),后端是包内可执行文件 | +| [`toolchains/proposal/java-tool-definition.json`](../../contract/v1/fixtures/toolchains/proposal/java-tool-definition.json)、[`mise-provider.json`](../../contract/v1/fixtures/toolchains/proposal/mise-provider.json) | 提案形态的定义(含一个消费者)和提供者 | +| [`toolchains/proposal/local.json`](../../contract/v1/fixtures/toolchains/proposal/local.json) | 带 `pins` 的 `local.json` | +| [`toolchains/proposal/manifest-rejections.json`](../../contract/v1/fixtures/toolchains/proposal/manifest-rejections.json) | 15 个清单拒绝用例:缺权限、导出替换 `PATH`、`PATH` 前缀离开主目录、无界通配、探针带脚本参数、社区插件占用短 ID、后定义覆盖已有所有者等 | +| [`toolchains/proposal/query-cases.json`](../../contract/v1/fixtures/toolchains/proposal/query-cases.json) | 13 个查询用例(未满足的要求、工作区里的主目录、取消后迟到的应答、超时和取消的区别、监视文件变化触发全部重查等)和 11 个错误应答 | +| [`toolchains/proposal/resolution-cases.json`](../../contract/v1/fixtures/toolchains/proposal/resolution-cases.json) | 13 个选择用例(按路径段继承固定、不可用的固定不回退、全局冲突回到登录 shell、按数字比较版本、相同最高版本要求选择等)和 3 个无效 `local.json` | + +Core 的验收测试在主仓库的 [`toolchain_definitions.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/tests/toolchain_definitions.rs) 和同目录的 `toolchain_pins.rs`、`toolchain_queries.rs`、`toolchain_resolution.rs`。 + +### 相关指南 + +- [为语言插件接入构建、运行与测试](../../guides/build-tool-integration.md):构建候选集里的 `toolchain` 可执行文件 +- [原生构建、测试与运行准备](../../guides/native-build-workflows.md):运行准备返回的 `executable.kind: "toolchain"` + +--- + +## English + +A plugin can tell Lithe what a tool (a JDK, Maven, Node, Go and so on) looks like, where to find it and how to read its version, and a backend can answer which versions this machine has and which version a project asks for. The host has no built-in tool definitions, alias table or tool-name branches: definitions come only from enabled plugins, Core validates and selects, and the host performs only native I/O such as filesystem observation, version probes and backend launches. + +| Item | Value | +| --- | --- | +| Manifest fields | `toolDefinitions[]` (tool definitions), `toolchainProviders[]` (toolchain providers); `toolchain` candidates in `executables` candidate sets | +| Capability versions | Definitions: `toolDefinitions` 1 + `executableResolution` 2; providers: `toolchainProviders` 1 + `backend` 2 + `serverProcess` 1; all at level 3. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheToolchains` 1: Core offers `experimental.litheToolchains: {versions: [1], providers}` only to a plugin session that declares providers, and the backend answers `{version: 1, providers}`; the request method is `toolchains/query`. See [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | [API-23](../api-catalog.md#apis-usable-now): toolchains and managed processes (partial) | +| Permissions | Definitions need none; providers need `backend.launch` and `toolchains.provide` | +| Activation events | None needed; the host's first query starts a provider, without a page or startup event | +| Backend launch | A provider backend is a packaged executable (`packaged`) or a packaged runtime (`runtime` + `packagedRuntime`), without discovered runtime dependencies | +| Windows | Not supported; the Windows host has no toolchain adapter and does not advertise `toolDefinitions`, `toolchainProviders` or `executableResolution` | +| Official SDKs | None of the three official SDKs implements `litheToolchains` yet; a provider backend handles `toolchains/query` itself | + +### Versions compared + +| Version | Adds | Core | macOS | Windows | Official SDKs | +| --- | --- | --- | --- | --- | --- | +| `executableResolution` 1 | [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12): ordering and strict selection of setting, environment, command, host toolchain and other candidates | Yes | Yes | No | Not applicable | +| `executableResolution` 2 | Candidates inside tool definitions; a `toolchain` candidate in a build candidate set may name a plugin-defined tool; existing build candidate sets are unchanged | Yes | Yes (read-only settings, Build and run tools) | No | Not applicable | +| `toolDefinitions` 1 | Tool identity, executable layouts, version probe, declarative discovery, home variable, exported environment, `PATH` prefixes, launch template | Yes | Yes (read-only settings, Build and run tools) | No | Not applicable | +| `toolchainProviders` 1 | Provider declarations `{id, displayName, tools, watchFiles?}`; permission-bound reply validation | Yes | Yes (read-only settings, Build and run tools) | No | Not applicable | +| `litheToolchains` 1 | The `toolchains/query` request and reply; the backend selects a nonempty subset of its declared providers | Yes | Yes | No | Not implemented | +| `.lithe/toolchains/local.json` `version` 1 | Machine-local document: legacy `toolchains` entries and per-directory `pins` | Reads and resolves | Read-only, never saved | No | Not applicable | + +### Two installation routes + +A tool may use either route, both, or neither (relying only on pins, project files, global defaults and login-shell commands). + +| Route | Declared by | Fits | Runs | +| --- | --- | --- | --- | +| Declarative discovery | The definition's `discovery`, `homeLayouts`, `homeVariable`, `commands` and `version` | Installations with a fixed directory shape: a single-component `*` glob under the user home, an environment variable or an absolute system directory, an optional home suffix, and a regex over the version probe output | The host expands the `discoveryPlan` scans; no plugin code runs | +| Provider query | `toolchainProviders[]` answering `toolchains/query` | Locations those primitives cannot express: registries, property lists, manager databases, command output, manager-specific version files | The provider's packaged backend | + +The declarative primitives are frozen: roots stay `home`, `environment` and `absolute`, and globs stay single-component `*`. A new installation shape is answered by a provider, never by a new root kind or host parser. A provider without `watchFiles` only reports installations, and its replies may not contain project requirements. + +### Selection order + +| Layer | Rule | +| --- | --- | +| Pin | The nearest ancestor directory's pin, matched by whole path components, so a similarly prefixed sibling never inherits it. An unusable pin fails terminally without fallback | +| Project | The nearest declaring directory; at equal depth wrapper > environment > version statement; for the same file the native reader before the compatible reader. Nearer project requirements mask farther ones | +| Global | Lithe's global default > an unambiguous (or user-chosen) provider global default > login shell | +| Automatic | The highest installed version satisfying the minimum; manually added installations are pin-only and never automatic. Equal highest versions in different homes are a conflict that needs a user choice | + +An uninstalled project requirement stays visible as unmet, with its reason, even when a lower layer selects another installation. Conflicts need an explicit user choice, never plugin-ID or path precedence. One operation may also carry `fixed: {home}` or `fixed: {executable}`, which precedes directory pins and fails terminally when invalid. + +### Core commands + +Every command takes UTF-8 JSON with a 4 MiB input budget and performs no native I/O. Success is `{status: "ok", : value}`; refusal is `{status: "rejected", error: {code, message, field?}}`. + +| Command | Input | Success key | Purpose | +| --- | --- | --- | --- | +| `toolchains.registry` | `{plugins: [{manifest, verifiedOfficial}]}` | `registry` | Revalidates complete manifests and collects definitions and providers; at most 128 plugins, 512 definitions and 256 providers | +| `toolchains.decodeReply` | The query request, the provider reply and host-observed filesystem facts | `snapshot` | Validates one `toolchains/query` reply into `{installations, statements, watch, diagnostics}` | +| `toolchains.failureReply` | `{params, code}` | `reply` | Produces the JSON-RPC error for a query that got no reply; `code` is only `plugin.toolchain.timeout` or `plugin.toolchain.providerUnavailable` | +| `toolchains.resolvePins` | `{document, directory, tool}` | `resolution` | Finds the nearest pin in `local.json` and returns `{pin, sourceDirectory}` | +| `toolchains.resolve` | The complete resolver input for one directory | `resolution` | Runs the four layers; the outcome is `resolved`, `skip` or `terminalFailure`, with `trace`, `unmet` and `conflicts` | +| `toolchains.checkVersion` | `{registry, tool, output, minimumVersion?}` | `version` | Extracts the version with the definition's regex and compares it; at most 8 numeric components and 64 bytes, no aliases interpreted | +| `toolchains.discoveryPlan` | `{registry, platform}` | `plan` | Each tool's scan plan and the providers that declare it; at most 256 roots, 4096 entries and 64 ancestors | +| `toolchains.candidates` | A settings page's discovery input | `candidates` | The global and automatic layers a settings page shows: `{tool, shell, installations, skipped, selected, conflicts}`; the first call lists executables to probe as `probeFailed`, and the host calls again after running the probes | + +Core also has `toolchains.resolveConsumerPreferences` and `toolchains.editConsumerPreferences`, which read and edit the per-consumer tool choices in `toolchain.preferences` of `.lithe/run/local.json` for the host's own Run and Build configuration. They are not part of the plugin contract. + +### Provider reply limits + +| Item | Rule | +| --- | --- | +| Content | `result: {operationID, generation, providerID, installations, statements, watch}`; an installation is `{tool, version, home}` and a requirement is `{tool, scope, source, request, installation, kind, reader}` | +| Size | At most 1 MiB, 512 installations, 256 requirements and 256 watch paths | +| Paths | Filesystem facts come from the host, never the provider; a home may not be inside any workspace root and must match the definition's executable layouts | +| Identity | The JSON-RPC `id`, `operationID`, `generation` and `providerID` must match the request; numeric `1` and string `"1"` are different IDs; remote authorities are always refused | +| Requirements | The `request` syntax (such as `lts`) is opaque to Core and the host never interprets it; `installation: null` records an unmet requirement | + +A provider may use these codes in JSON-RPC errors, and `decodeReply` checks them against this table (one number, one meaning): + +| JSON-RPC | Semantic code | Meaning | +| --- | --- | --- | +| `-32601` | `plugin.toolchain.methodUnavailable` | The query method or capability was not negotiated | +| `-32602` | `plugin.toolchain.invalidParams` | A malformed request or invalid declared input | +| `-32800` | `plugin.toolchain.cancelled` | The caller cancelled the request | +| `-32070` | `plugin.toolchain.permissionDenied` | A permission, review or enabled-state prerequisite is absent | +| `-32071` | `plugin.toolchain.authorityUnsupported` | No adapter for this authority | +| `-32072` | `plugin.toolchain.timeout` | The local deadline passed | +| `-32073` | `plugin.toolchain.providerUnavailable` | The backend failed to start, disconnected or failed its upstream query | +| `-32074` | `plugin.toolchain.invalidReply` | A malformed reply, or an installation that references another tool | +| `-32075` | `plugin.toolchain.limitExceeded` | A reply, scan or watch budget was exceeded | +| `-32076` | `plugin.toolchain.unsafePath` | Unsafe absolute path, containment or executable evidence | +| `-32077` | `plugin.toolchain.operationExpired` | The context or generation was already invalid when the query was sent | + +### Host status + +| Use | macOS | Windows | +| --- | --- | --- | +| Settings pages showing detections | Both routes run read-only; a change to a watched file reloads the whole snapshot; nothing is saved | No | +| Executables for Build tools and plugin run plans | A candidate or run plan that names a plugin-defined tool (not one in the host's legacy toolchain table) is resolved afresh; settings detections are never launch permits | No | +| Java and language servers | Still use their previous adapters | No | +| Saving pins, `toolchainConsumers`, generic runtime launches, live provider activation | Not yet | Not yet | + +`toolchains.provide` may influence which program a defined tool launches, but it does not authorize replacing arbitrary commands, installing software, network access or running workspace code. Short definition IDs (such as `java`) need independently verified official origin; a community plugin writes definition IDs as `:`, and community providers may answer short IDs. + +### Contract sections + +| Section | Content | +| --- | --- | +| [Plugin-owned toolchains (Core foundation)](../../contract/v1/README.md#plugin-owned-toolchains-core-foundation) | The toolchain summary and current host status in the contract entry point | +| [Plugin-owned toolchains: Core foundation](../../contract/v1/toolchains/README.md) | The complete description of the implemented part | +| [Manifest admission and registry](../../contract/v1/toolchains/README.md#manifest-admission-and-registry) | Capability and permission requirements, definition fields, provider fields, ID rules | +| [Two installation routes](../../contract/v1/toolchains/README.md#two-installation-routes) | Declarative discovery and provider queries | +| [Commands and envelopes](../../contract/v1/toolchains/README.md#commands-and-envelopes) | The eight Core commands, result envelopes, registry limits | +| [Provider reply validation](../../contract/v1/toolchains/README.md#provider-reply-validation) | `decodeReply`, reply shape and limits, `failureReply` | +| [Pins and snapshot resolution](../../contract/v1/toolchains/README.md#pins-and-snapshot-resolution) | `local.json`, `resolve`, the four layers, `checkVersion`, `discoveryPlan` | +| [Settings candidates](../../contract/v1/toolchains/README.md#settings-candidates) | `candidates` and the two-call probe flow | +| [Shared executable candidates](../../contract/v1/README.md#shared-executable-candidates-executableresolution-12) | The `executableResolution` 1 and 2 candidate sets | +| [Proposal](../../contract/v1/toolchains/proposal/README.md): [manifest.md](../../contract/v1/toolchains/proposal/manifest.md), [protocol.md](../../contract/v1/toolchains/proposal/protocol.md) | The broader design (consumer migration, native lifecycle, persistence, manager plugins), not implemented; Core already uses its [failure code table](../../contract/v1/toolchains/proposal/protocol.md#stable-failure-mapping) | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`toolchains/tool-definition.plugin.json`](../../contract/v1/fixtures/toolchains/tool-definition.plugin.json) | A production-shape tool definition: `java` with 8 candidates (project and global settings, project and global toolchain requests, `JAVA_HOME` on macOS/Linux and on Windows, the `java` command, installed toolchains), discovering `*.jdk` in the macOS `JavaVirtualMachines` directories | +| [`toolchains/toolchain-provider.plugin.json`](../../contract/v1/fixtures/toolchains/toolchain-provider.plugin.json) | A production-shape provider: `mise` for `java`, `node`, `maven`, `pnpm` and `go`, watching 6 files (`.tool-versions` with the compatible reader), with a packaged executable backend | +| [`toolchains/proposal/java-tool-definition.json`](../../contract/v1/fixtures/toolchains/proposal/java-tool-definition.json), [`mise-provider.json`](../../contract/v1/fixtures/toolchains/proposal/mise-provider.json) | Proposal-shape definition (with one consumer) and provider | +| [`toolchains/proposal/local.json`](../../contract/v1/fixtures/toolchains/proposal/local.json) | A `local.json` with `pins` | +| [`toolchains/proposal/manifest-rejections.json`](../../contract/v1/fixtures/toolchains/proposal/manifest-rejections.json) | 15 manifest refusals: a missing permission, an export replacing `PATH`, a `PATH` prefix leaving the home, an unbounded glob, a probe with a script argument, a community plugin claiming a short ID, a later definition replacing an owner, and more | +| [`toolchains/proposal/query-cases.json`](../../contract/v1/fixtures/toolchains/proposal/query-cases.json) | 13 query cases (an unmet requirement, a home inside the workspace, a late reply after cancellation, timeout versus cancellation, a watched file change requerying every provider, and more) and 11 error replies | +| [`toolchains/proposal/resolution-cases.json`](../../contract/v1/fixtures/toolchains/proposal/resolution-cases.json) | 13 selection cases (component-wise pin inheritance, an unusable pin without fallback, conflicting globals falling back to the login shell, numeric version comparison, equal highest versions needing a choice, and more) and 3 invalid `local.json` documents | + +Core's acceptance tests are [`toolchain_definitions.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/tests/toolchain_definitions.rs) and its siblings `toolchain_pins.rs`, `toolchain_queries.rs` and `toolchain_resolution.rs` in the main repository. + +### Related guides + +- [Build, run, and test for language plugins](../../guides/build-tool-integration.md): `toolchain` executables in build candidate sets +- [Native build, test, and run preparation](../../guides/native-build-workflows.md): `executable.kind: "toolchain"` in run preparation diff --git a/reference/domains/ui.md b/reference/domains/ui.md new file mode 100644 index 0000000..2f450db --- /dev/null +++ b/reference/domains/ui.md @@ -0,0 +1,121 @@ +# 界面:工具窗口、命令、网页与原生内容 / UI: tool windows, commands, web and native content + +[中文](#中文) · [English](#english) + +## 中文 + +插件可以在工作台里放工具窗口和命令。窗口的外壳(停靠、标题、活动栏、顺序、布局、主题、生命周期)始终归宿主;插件只提供窗口内容:纯文本、包内网页,或由宿主绘制的原生树、列表、表单。命令可以打开插件自己的窗口,从 `ui` 4 起也可以只通知后端、不打开窗口。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `ui.toolWindows`、`ui.commands`(可选 `icon`) | +| 能力版本 | `ui` 1–4;原生内容另有 [native schema](../../contract/v1/native-ui.schema.json) 1。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | 网页通过 `functions.*` 调用[插件后端](backend.md);`ui` 4 命令通过 `litheFunctions` 3 的 `commands/didRun` 通知后端 | +| API 编号 | API-02、03、13、14、17,见 [API 总表](../api-catalog.md#现在能用的-api) | +| 权限 | `ui.contribute`;网页加 `ui.web`;`ui` 4 命令加 `backend.launch`;后端弹出消息用 `window.messages` | +| 激活事件 | 每个窗口 `onView:`,每个命令 `onCommand:` | +| Windows | 宿主声明 `ui` 1、2;不声明 `ui` 3(原生内容)和 `ui` 4,这些包在安装前被拒绝 | + +### 版本对照 + +| 版本 | 最低级别 | 新增 | 其他要求 | macOS | Windows | +| --- | --- | --- | --- | --- | --- | +| `ui` 1 | 2 | 1–64 个文本工具窗口(`left`、`bottom`、`right`);最多 128 个命令,放在 `commandPalette`、`toolbar`、`menu`,每个命令打开同一插件的一个窗口 | `ui.contribute` | 支持 | 支持 | +| `ui` 2 | 3 | 窗口内容可以是包内网页 `web.entry`(`.html`) | `ui.web`;声明 `ui` 2 却没有网页窗口会被拒绝 | 支持 | 支持 | +| `ui` 3 | 3 | 由宿主绘制的 `native` 内容:`tree`、`list`、`form`;全部节点最多 2000 个、深度 16;表单最多 32 个控件、16 个按钮 | 只有 `native` 一种窗口内容 | 支持 | 不支持 | +| `ui` 4 | 3 | 命令可以不带 `toolWindowID`,`toolWindows` 可以为空;命令运行时向后端发送 `commands/didRun` | `backend` 2、`backend.launch`;`didRun` 只是事件,不代表命令的业务动作成功 | 支持 | 不支持 | + +所有 ID 以插件 ID 加点开头;标题最多 256 字节,窗口文本最多 65536 字节,按原样显示,不作为 HTML 或脚本执行。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [API level 2: first-stage host-owned workbench UI](../../contract/v1/README.md#api-level-2-first-stage-host-owned-workbench-ui) | `ui` 1:工具窗口、命令、ID、限制、宿主负责的部分 | +| [Web content (`ui` 2)](../../contract/v1/README.md#web-content-ui-2) | 包内网页、桥接、状态和生命周期 | +| [Host-rendered native content (`ui` 3, native schema 1)](../../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1) | 原生树、列表、表单,控件种类与限制 | +| [Backend-only commands (`ui` 4)](../../contract/v1/README.md#backend-only-commands-ui-4) | 不打开窗口的命令与 `commands/didRun` | +| [UI icons (macOS host)](../../contract/v1/README.md#ui-icons-macos-host) | 宿主图标 ID(如 `{"host": "database"}`)与自带 SVG/PNG | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | 后端弹出的消息 `window/showInformation`、`showWarning`、`showError` | +| [Future public extension direction](../../contract/v1/README.md#future-public-extension-direction-not-implemented-in-api-level-2) | 尚未实现的方向,不代表当前能力 | +| [Current Windows availability](../../contract/v1/README.md#current-windows-availability) | Windows 声明的 `ui` 版本:1、2,不声明 3、4 | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | 给社区 Lua 模板加窗口的步骤 | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`manifest/hello-lithe.plugin.json`](../../contract/v1/fixtures/manifest/hello-lithe.plugin.json) | 级别 2、`ui` 1:一个文本窗口和打开它的命令 | +| [`ui/web-panel.plugin.json`](../../contract/v1/fixtures/ui/web-panel.plugin.json) | `ui` 2:两个网页窗口和一个命令 | +| [`ui/native-panel.plugin.json`](../../contract/v1/fixtures/ui/native-panel.plugin.json) | `ui` 3:原生树面板 | +| [`ui/native-form.plugin.json`](../../contract/v1/fixtures/ui/native-form.plugin.json) | `ui` 3:原生表单 | +| [`ui/native-controls.plugin.json`](../../contract/v1/fixtures/ui/native-controls.plugin.json) | `ui` 3:表单的各种控件 | +| [`backend/backend.plugin.json`](../../contract/v1/fixtures/backend/backend.plugin.json) | 网页窗口调用 `backend` 1 | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | `ui` 4:不打开窗口的后端命令 | +| [`native-ui.schema.json`](../../contract/v1/native-ui.schema.json) | 原生内容的 JSON Schema | + +界面相关的拒绝用例在 [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json)。可运行的原生面板示例是 [Node SDK 的 Sample Explorer](../../sdk/node/examples/sample-build/plugin.json)。 + +### 相关指南 + +- [插件后端:工作区上下文与宿主服务](../../guides/plugin-backend-workspace.md) + +--- + +## English + +A plugin can place tool windows and commands in the workbench. The window shell (docking, title, activity bar, order, layout, theme, lifecycle) always belongs to the host; the plugin supplies only the content: plain text, a packaged web page, or a native tree, list, or form drawn by the host. A command opens one of the plugin's windows, and from `ui` 4 on it may instead just notify the backend without opening a window. + +| Item | Value | +| --- | --- | +| Manifest fields | `ui.toolWindows`, `ui.commands` (optional `icon`) | +| Capability versions | `ui` 1–4; native content also has [native schema](../../contract/v1/native-ui.schema.json) 1. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | Pages call the [plugin backend](backend.md) through `functions.*`; `ui` 4 commands notify the backend with `commands/didRun` (`litheFunctions` 3) | +| API IDs | API-02, 03, 13, 14, 17; see the [API catalog](../api-catalog.md#apis-usable-now) | +| Permissions | `ui.contribute`; `ui.web` for web pages; `backend.launch` for `ui` 4 commands; `window.messages` for messages shown by the backend | +| Activation events | `onView:` per window, `onCommand:` per command | +| Windows | Advertises `ui` 1 and 2; not `ui` 3 (native content) or `ui` 4, so those packages are refused before installation | + +### Versions compared + +| Version | Lowest level | Adds | Other requirements | macOS | Windows | +| --- | --- | --- | --- | --- | --- | +| `ui` 1 | 2 | 1–64 text tool windows (`left`, `bottom`, `right`); up to 128 commands in `commandPalette`, `toolbar`, `menu`, each opening one of the same plugin's windows | `ui.contribute` | Yes | Yes | +| `ui` 2 | 3 | Window content may be a packaged web page, `web.entry` (`.html`) | `ui.web`; `ui` 2 without a web window is refused | Yes | Yes | +| `ui` 3 | 3 | Host-drawn `native` content: `tree`, `list`, `form`; at most 2000 nodes and depth 16 in total; a form has at most 32 controls and 16 buttons | `native` is the window's only content | Yes | No | +| `ui` 4 | 3 | Commands without `toolWindowID`, and `toolWindows` may be empty; running a command sends `commands/didRun` to the backend | `backend` 2, `backend.launch`; `didRun` is an event, not proof that the command's action succeeded | Yes | No | + +Every ID starts with the plugin ID and a dot. Titles are at most 256 bytes and window text at most 65536 bytes; text is shown literally and never evaluated as HTML or script. + +### Contract sections + +| Section | Content | +| --- | --- | +| [API level 2: first-stage host-owned workbench UI](../../contract/v1/README.md#api-level-2-first-stage-host-owned-workbench-ui) | `ui` 1: tool windows, commands, IDs, limits, what the host owns | +| [Web content (`ui` 2)](../../contract/v1/README.md#web-content-ui-2) | Packaged pages, bridge, state, and lifecycle | +| [Host-rendered native content (`ui` 3, native schema 1)](../../contract/v1/README.md#host-rendered-native-content-ui-3-native-schema-1) | Native trees, lists, forms; control kinds and limits | +| [Backend-only commands (`ui` 4)](../../contract/v1/README.md#backend-only-commands-ui-4) | Commands without a window, and `commands/didRun` | +| [UI icons (macOS host)](../../contract/v1/README.md#ui-icons-macos-host) | Host icon IDs (such as `{"host": "database"}`) and packaged SVG/PNG | +| [Public document, file and window methods (protocol version 3)](../../contract/v1/README.md#public-document-file-and-window-methods-protocol-version-3) | Backend messages `window/showInformation`, `showWarning`, `showError` | +| [Future public extension direction](../../contract/v1/README.md#future-public-extension-direction-not-implemented-in-api-level-2) | Directions not implemented yet; not current capabilities | +| [Current Windows availability](../../contract/v1/README.md#current-windows-availability) | The `ui` versions Windows advertises: 1 and 2, not 3 or 4 | +| [Existing community store and Lua template](../../contract/v1/README.md#existing-community-store-and-lua-template) | Adding a window to the community Lua template | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`manifest/hello-lithe.plugin.json`](../../contract/v1/fixtures/manifest/hello-lithe.plugin.json) | Level 2, `ui` 1: one text window and the command that opens it | +| [`ui/web-panel.plugin.json`](../../contract/v1/fixtures/ui/web-panel.plugin.json) | `ui` 2: two web windows and a command | +| [`ui/native-panel.plugin.json`](../../contract/v1/fixtures/ui/native-panel.plugin.json) | `ui` 3: a native tree panel | +| [`ui/native-form.plugin.json`](../../contract/v1/fixtures/ui/native-form.plugin.json) | `ui` 3: a native form | +| [`ui/native-controls.plugin.json`](../../contract/v1/fixtures/ui/native-controls.plugin.json) | `ui` 3: every form control kind | +| [`backend/backend.plugin.json`](../../contract/v1/fixtures/backend/backend.plugin.json) | A web window calling `backend` 1 | +| [`backend/command-backend.plugin.json`](../../contract/v1/fixtures/backend/command-backend.plugin.json) | `ui` 4: a backend command without a window | +| [`native-ui.schema.json`](../../contract/v1/native-ui.schema.json) | JSON Schema for native content | + +UI rejections are in [`manifest/invalid-manifests.json`](../../contract/v1/fixtures/manifest/invalid-manifests.json). A runnable native panel is the [Node SDK Sample Explorer](../../sdk/node/examples/sample-build/plugin.json). + +### Related guides + +- [Plugin backend: workspace contexts and host services](../../guides/plugin-backend-workspace.md) diff --git a/reference/domains/vcs.md b/reference/domains/vcs.md new file mode 100644 index 0000000..1d20678 --- /dev/null +++ b/reference/domains/vcs.md @@ -0,0 +1,121 @@ +# 版本控制提供者 / VCS providers + +[中文](#中文) · [English](#english) + +## 中文 + +插件可以为 Git 以外的版本控制系统(第一个是 Subversion)实现一个提供者。提供者是一个 LSP 基础协议进程,和[构建提供者](build-providers.md)同类,回答统一 VCS 模型的请求:根目录、文件状态、基准内容、历史、注解、提交、回滚、更新。版本控制界面、差异视图、提交窗口都归宿主;界面按提供者协商的能力决定显示什么,从不按 VCS 名字分支。Git 由内置支持负责,ID `git` 保留。 + +| 项目 | 内容 | +| --- | --- | +| 清单字段 | `vcsProviders[]`:`id`、`displayName`、`detection.markers`、`metadataPaths`、`tool`(VCS 客户端命令和校验参数)、`serverProcess` 启动字段、`mayUseNetwork` | +| 能力版本 | `vcsProviders` 1 加 `serverProcess` 1,需要级别 3。见[版本对照](../capabilities-and-versions.md#能力版本requires) | +| 协议 | `litheVcs` 1:宿主在 `initialize` 里提供 `capabilities.experimental.litheVcs: {"versions": [1]}`,提供者回答 `{"version": 1, "vcs", "capabilities"}`,见[进程协议版本](../capabilities-and-versions.md#进程协议版本) | +| API 编号 | [API-31](../api-catalog.md#现在能用的-api):VCS 提供者。读取内置 Git 的公共接口是另一项(API-42,计划中) | +| 权限 | `process.vcs`、`languageServer.launch` | +| 激活事件 | `onVcsRoot:`;根目录检测只看标记文件,不启动进程,找到该 VCS 的根目录后才启动提供者 | +| Windows | 不支持;Windows 宿主不声明 `vcsProviders` | +| 官方 SDK | [Node SDK](../../sdk/node/README.md) 实现 `litheVcs` 1;Java、Rust SDK 不实现 | + +### 版本对照 + +| 版本 | 新增 | Core | macOS | Windows | Node SDK | +| --- | --- | --- | --- | --- | --- | +| `vcsProviders` 1 / `litheVcs` 1 | 清单声明、标记检测、元数据监视、`vcs.detectRoots`、`vcs.ownerOf`、`lsp.vcsRequest`、`vcs.decodeReply`;必选能力 `status`、`baseContent`、`checkin`、`rollback`,可选能力 `history`、`fileHistory`、`annotate`、`update`、`push`、`staging`、`fileOperations`、`conflicts`、`cleanup` | 支持 | 支持 | 不支持 | 支持 | + +`push` 在版本 1 里只是声明:协商了 `push` 的提供者告诉宿主"提交是本地的,发布是另一步",版本 1 没有发布操作,以后的版本再建模。 + +### 能力与操作 + +| 能力 | 必选 | 回复 | +| --- | --- | --- | +| `status` | 是 | 根目录的文件状态 | +| `baseContent` | 是 | `{content, binary}`,基准版本的内容,最多 32 MiB;新增或未纳入版本控制的文件为 `null` | +| `checkin` | 是 | `{revision}`;VCS 不报告新版本时为 `null` | +| `rollback` | 是 | `{}` | +| `history`、`fileHistory` | 否 | `{entries, nextCursor}`,分页,保持提供者的顺序(新的在前) | +| `annotate` | 否 | `{revisions, lines}`,最多 500,000 行 | +| `update` | 否 | `{revision, conflicts}` | +| `fileOperations`、`conflicts`、`cleanup` | 否 | `fileOperation`、`resolve`、`cleanup` 回复 `{}` | +| `push`、`staging` | 否 | 只影响界面:没有 `push` 时宿主不提供"提交并推送" | + +路径离开根目录、缺必填字段、超出大小限制的回复都无效。 + +### 契约章节 + +| 章节 | 内容 | +| --- | --- | +| [API level 3: VCS providers](../../contract/v1/README.md#api-level-3-vcs-providers) | 清单字段、保留 ID、检测、安装审查 | +| [VCS provider contract v1](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/vcs-provider-v1.md) | 字段、Core 命令、协商、回复格式、提供者消息、错误码、Subversion 说明(在 Lithe 主仓库) | + +### 示例与用例 + +| 文件 | 内容 | +| --- | --- | +| [`vcs/vcs-provider.plugin.json`](../../contract/v1/fixtures/vcs/vcs-provider.plugin.json) | Subversion 提供者:`.svn` 目录标记、监视 `.svn/wc.db`、`svn` 客户端、Node 运行时启动、`mayUseNetwork` | + +共享示例里没有 VCS 拒绝用例;错误码(包括 `plugin.vcs.reserved`)见 VCS 契约的 [Error codes](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/vcs-provider-v1.md#error-codes)。 + +### 相关指南 + +- [Node SDK:A VCS provider](../../sdk/node/README.md#a-vcs-provider) + +--- + +## English + +A plugin can implement a provider for a version control system other than Git (Subversion is the first). A provider is an LSP base-protocol process, the same kind as a [build provider](build-providers.md), and answers requests of one VCS-neutral model: roots, file status, base content, history, annotation, check-in, rollback, and update. The version control UI, diff views, and commit window belong to the host; the UI decides what to show from the capabilities the provider negotiated, never from the VCS name. Git is served by built-in support, and the ID `git` is reserved. + +| Item | Value | +| --- | --- | +| Manifest field | `vcsProviders[]`: `id`, `displayName`, `detection.markers`, `metadataPaths`, `tool` (the VCS client commands and validation arguments), the `serverProcess` launch fields, `mayUseNetwork` | +| Capability versions | `vcsProviders` 1 with `serverProcess` 1, at level 3. See [versions](../capabilities-and-versions.md#capability-versions-requires) | +| Protocols | `litheVcs` 1: the host offers `capabilities.experimental.litheVcs: {"versions": [1]}` in `initialize`, and the provider answers `{"version": 1, "vcs", "capabilities"}`; see [process protocol versions](../capabilities-and-versions.md#process-protocol-versions) | +| API IDs | [API-31](../api-catalog.md#apis-usable-now): VCS providers. Public reads of the built-in Git are a separate item (API-42, planned) | +| Permissions | `process.vcs`, `languageServer.launch` | +| Activation events | `onVcsRoot:`; root detection reads marker files only and starts no process, and a provider starts only after a root of its VCS is found | +| Windows | Not supported; the Windows host does not advertise `vcsProviders` | +| Official SDKs | The [Node SDK](../../sdk/node/README.md) implements `litheVcs` 1; the Java and Rust SDKs do not | + +### Versions compared + +| Version | Adds | Core | macOS | Windows | Node SDK | +| --- | --- | --- | --- | --- | --- | +| `vcsProviders` 1 / `litheVcs` 1 | Manifest declaration, marker detection, metadata watching, `vcs.detectRoots`, `vcs.ownerOf`, `lsp.vcsRequest`, `vcs.decodeReply`; required capabilities `status`, `baseContent`, `checkin`, `rollback`; optional `history`, `fileHistory`, `annotate`, `update`, `push`, `staging`, `fileOperations`, `conflicts`, `cleanup` | Yes | Yes | No | Yes | + +In version 1, `push` is only a declaration: a provider that negotiates it tells the host that check-in is local and publishing is a separate step. Version 1 has no publishing operation; a later version will model it. + +### Capabilities and operations + +| Capability | Required | Reply | +| --- | --- | --- | +| `status` | Yes | File status of a root | +| `baseContent` | Yes | `{content, binary}`, the content at the base revision, at most 32 MiB; `null` for added or unversioned files | +| `checkin` | Yes | `{revision}`; `null` when the VCS reports none | +| `rollback` | Yes | `{}` | +| `history`, `fileHistory` | No | `{entries, nextCursor}`, paged, in provider order (newest first) | +| `annotate` | No | `{revisions, lines}`, at most 500,000 lines | +| `update` | No | `{revision, conflicts}` | +| `fileOperations`, `conflicts`, `cleanup` | No | `fileOperation`, `resolve`, and `cleanup` reply `{}` | +| `push`, `staging` | No | UI only: without `push` the host offers no "Commit and Push" | + +A reply with a path outside the root, a missing required field, or an exceeded size limit is invalid. + +### Contract sections + +| Section | Content | +| --- | --- | +| [API level 3: VCS providers](../../contract/v1/README.md#api-level-3-vcs-providers) | Manifest fields, the reserved ID, detection, install review | +| [VCS provider contract v1](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/vcs-provider-v1.md) | Fields, Core commands, negotiation, reply shapes, provider messages, error codes, Subversion notes (in the Lithe main repository) | + +### Examples and cases + +| File | Content | +| --- | --- | +| [`vcs/vcs-provider.plugin.json`](../../contract/v1/fixtures/vcs/vcs-provider.plugin.json) | A Subversion provider: a `.svn` directory marker, watching `.svn/wc.db`, the `svn` client, a Node runtime launch, `mayUseNetwork` | + +The shared fixtures have no VCS refusal cases; the error codes (including `plugin.vcs.reserved`) are in [Error codes](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/vcs-provider-v1.md#error-codes) of the VCS contract. + +### Related guides + +- [Node SDK: A VCS provider](../../sdk/node/README.md#a-vcs-provider) diff --git a/reference/protocol-changelog.md b/reference/protocol-changelog.md new file mode 100644 index 0000000..a6ff57e --- /dev/null +++ b/reference/protocol-changelog.md @@ -0,0 +1,238 @@ +# 插件协议更新记录 + +本页记录插件协议和 API 的每次修改:哪天、哪个 PR、谁改的、改了什么。只有中文。 + +- **收录范围**:Lithe 主仓库 `preview` 分支上的 PR,只要它修改了下面三处中的任意一处:插件契约 `shared/contracts/plugins/v1/`、Core 能力表 [`capabilities.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/plugins/capabilities.rs)、插件函数协议 [`plugin_function.rs`](https://github.com/1lck/Lithe-IDEA/blob/preview/rust/lithe-core/src/lsp/interface/plugin_function.rs)。三处都没改、只改宿主内部实现的 PR 不收录。 +- **日期**:PR 合入 `preview` 的日期。 +- **修改人**:原 PR 的作者(GitHub 账号)。登记这些 PR 的同步 PR 会在描述里 @ 他们,请他们核对自己那一行。 +- **记录到**:`preview` 的 [`644afdaac`](https://github.com/1lck/Lithe-IDEA/commit/644afdaac9087b276cb8f7902658b85ae1792d6d)([#1747](https://github.com/1lck/Lithe-IDEA/pull/1747)),即本仓库 `contract/v1/` 当前副本的来源。 +- **每一版的内容和宿主现状**:见[能力与协议版本对照](capabilities-and-versions.md)。字段和行为以 [`contract/v1/README.md`](../contract/v1/README.md) 为准,官方 SDK 的发版见 [SDK 发版历史](capabilities-and-versions.md#sdk-发版历史)。 + +时间线的“协议 / API”一列里,**加粗**的是这个 PR 新增的版本号;没有加粗的,是同一版本内的补充、宿主接入或说明修正。 + +## 按协议查首次出现 + +### 插件 API 级别与文档格式 + +| 名称 | 版本 | 日期 | PR | +| --- | --- | --- | --- | +| 插件 API 级别 `pluginAPIVersion` | 1 | 2026-10-14 | [#1170](https://github.com/1lck/Lithe-IDEA/pull/1170) | +| 插件 API 级别 `pluginAPIVersion` | 2 | 2026-10-14 | [#1214](https://github.com/1lck/Lithe-IDEA/pull/1214);工作台 UI 由 [#1206](https://github.com/1lck/Lithe-IDEA/pull/1206) 加入 | +| 插件 API 级别 `pluginAPIVersion` | 3(`requires`) | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| 插件 API 级别 `pluginAPIVersion` | 4(调试适配器) | 2026-10-26 | [#1510](https://github.com/1lck/Lithe-IDEA/pull/1510) | +| 插件清单 `schemaVersion` | 2 | 2026-10-14 | [#1170](https://github.com/1lck/Lithe-IDEA/pull/1170) | +| 市场索引 `schemaVersion` | 1 | 2026-10-14 | [#1170](https://github.com/1lck/Lithe-IDEA/pull/1170) | +| 原生 UI 文档 `schemaVersion` | 1 | 2026-10-24 | [#1423](https://github.com/1lck/Lithe-IDEA/pull/1423) | +| `litheBuildModel`、`litheDependencies`、`litheTestResults` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `litheTestDiscovery` | 1 | 2026-10-28 | [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | +| `litheBuildConfiguration` | 1 | 2026-11-04 | [#1558](https://github.com/1lck/Lithe-IDEA/pull/1558) | +| 本机工具链文档 `.lithe/toolchains/local.json` | 1 | 2026-11-01 | [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | + +### 能力版本(`requires`) + +`requires` 由 #1318 引入,首批 8 个能力都从版本 1 开始。其中 `languages`、`languageServers`、`serverProcess`、`executionProviders`、`settings`、`ui` 的内容在此之前已经随 API 级别 1、2 提供,#1318 只是给它们定了能力版本号。 + +| 能力 | 版本 | 日期 | PR | +| --- | --- | --- | --- | +| `languages` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `languages` | 2 | 2026-10-31 | [#1617](https://github.com/1lck/Lithe-IDEA/pull/1617) | +| `languageServers` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `languageServers` | 2 | 2026-10-21 | [#1395](https://github.com/1lck/Lithe-IDEA/pull/1395) | +| `serverProcess` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `serverProcess` | 2 | 2026-10-26 | [#1510](https://github.com/1lck/Lithe-IDEA/pull/1510) | +| `executionProviders` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `settings` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `settings` | 2 | 2026-10-21 | [#1395](https://github.com/1lck/Lithe-IDEA/pull/1395) | +| `ui` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `ui` | 2 | 2026-10-18 | [#1334](https://github.com/1lck/Lithe-IDEA/pull/1334) | +| `ui` | 3 | 2026-10-24 | [#1423](https://github.com/1lck/Lithe-IDEA/pull/1423) | +| `ui` | 4 | 2026-10-30 | [#1595](https://github.com/1lck/Lithe-IDEA/pull/1595) | +| `buildSystems` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `buildSystems` | 2 | 2026-10-23 | [#1420](https://github.com/1lck/Lithe-IDEA/pull/1420) | +| `buildSystems` | 3、4 | 2026-11-04 | [#1558](https://github.com/1lck/Lithe-IDEA/pull/1558) | +| `buildProviders` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | +| `buildProviders` | 2–6 | 2026-10-28 | [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | +| `buildProviders` | 7–11 | 2026-11-04 | [#1558](https://github.com/1lck/Lithe-IDEA/pull/1558) | +| `buildModelInputs` | 1 | 2026-10-28 | [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | +| `executableResolution` | 1 | 2026-10-23 | [#1420](https://github.com/1lck/Lithe-IDEA/pull/1420) | +| `executableResolution` | 2 | 2026-11-01 | [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | +| `vcsProviders` | 1 | 2026-10-23 | [#1430](https://github.com/1lck/Lithe-IDEA/pull/1430) | +| `projectGenerators` | 1 | 2026-10-24 | [#1452](https://github.com/1lck/Lithe-IDEA/pull/1452) | +| `backend` | 1 | 2026-10-24 | [#1470](https://github.com/1lck/Lithe-IDEA/pull/1470) | +| `backend` | 2 | 2026-10-30 | [#1595](https://github.com/1lck/Lithe-IDEA/pull/1595) | +| `debugAdapters` | 1 | 2026-10-26 | [#1510](https://github.com/1lck/Lithe-IDEA/pull/1510) | +| `debugAdapters` | 2 | 2026-10-28 | [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | +| `packageTrust` | 1 | 2026-10-28 | [#1483](https://github.com/1lck/Lithe-IDEA/pull/1483) | +| `toolDefinitions` | 1 | 2026-11-01 | [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | +| `toolchainProviders` | 1 | 2026-11-01 | [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | +| `keymapImportProviders` | 1 | 2026-11-03 | [#1695](https://github.com/1lck/Lithe-IDEA/pull/1695) | + +### 进程协议版本 + +| 协议 | 版本 | 日期 | PR | +| --- | --- | --- | --- | +| `litheFunctions` | 1 | 2026-10-20 | [#1358](https://github.com/1lck/Lithe-IDEA/pull/1358) | +| `litheFunctions` | 2(宿主服务) | 2026-10-24 | [#1470](https://github.com/1lck/Lithe-IDEA/pull/1470) | +| `litheFunctions` | 3(工作区上下文) | 2026-10-30 | [#1595](https://github.com/1lck/Lithe-IDEA/pull/1595) | +| `litheBuild` | 1 | 2026-10-18 | [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) 定义;macOS 宿主由 [#1356](https://github.com/1lck/Lithe-IDEA/pull/1356) 接入 | +| `litheBuild` | 2–6 | 2026-10-28 | [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | +| `litheBuild` | 7–11 | 2026-11-04 | [#1558](https://github.com/1lck/Lithe-IDEA/pull/1558) | +| `litheVcs` | 1 | 2026-10-23 | [#1430](https://github.com/1lck/Lithe-IDEA/pull/1430) | +| `litheToolchains` | 1(提案) | 2026-11-01 | [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | +| `litheKeymapImport` | 1 | 2026-11-03 | [#1695](https://github.com/1lck/Lithe-IDEA/pull/1695) | +| `litheLogging` | 1 | 2026-11-04 | [#1699](https://github.com/1lck/Lithe-IDEA/pull/1699) | + +## 时间线(新的在上) + +### 2026-11-05 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1747](https://github.com/1lck/Lithe-IDEA/pull/1747) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 清单 schema:`requires.buildSystems` | schema 的枚举补齐到 1–4(Core 从 #1558 起已经支持 3、4,schema 当时只写到 3)。fixtures 按功能分目录,工具链提案移到 `toolchains/proposal/`,契约 README 的能力版本表随之修正。 | + +### 2026-11-04 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1724](https://github.com/1lck/Lithe-IDEA/pull/1724) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 包签名 | 新增 `fixtures/signature-vectors.json`,是发布者签名的跨平台测试向量:RFC 8032 测试密钥、带非 ASCII 文件名的固定包和它的规范化载荷。同一 PR 的语言服务器启动失败清理、归档解压加固属于宿主行为,没有改字段。 | +| [#1558](https://github.com/1lck/Lithe-IDEA/pull/1558) | [@Mucheen](https://github.com/Mucheen) | **`buildProviders` 7–11**、**`litheBuild` 7–11**、**`buildSystems` 3、4**、**`litheBuildConfiguration` 1** | 7 基于真实来源的原生配置,8 内联测试报告,9 不透明语义测试选择器,10 类型化运行选项与服务/任务运行准备,11 注册表连接调试。`buildSystems` 3 是实例级选项,4 是声明式项目准备角色。 | +| [#1720](https://github.com/1lck/Lithe-IDEA/pull/1720) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `litheLogging` 1:声明式插件 | 声明式贡献没有自己的进程,发不了 `lithe/log`,改由执行方在 `plugins//host` 通道代记 `plugins.operation.finished` 和 `plugins.operation.failed`。每次启动语言服务器或调试适配器、每次运行构建模型或依赖命令,各记为一次操作。失败时带一个错误码,例如 `host.planFailed`、`host.executableUnavailable`、`host.launchFailed`。 | +| [#1699](https://github.com/1lck/Lithe-IDEA/pull/1699) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`litheLogging` 1** | 插件拥有的每个进程都可以协商 `capabilities.experimental.litheLogging`,然后用通知 `lithe/log` 发结构化诊断。参数只接受固定字段,未知字段会被拒绝。新增 `fixtures/plugin-logging.json`。 | + +### 2026-11-03 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1695](https://github.com/1lck/Lithe-IDEA/pull/1695) | [@1lck](https://github.com/1lck) | **`keymapImportProviders` 1**、**`litheKeymapImport` 1**、函数协议 3 的宿主方法 | 快捷键导入提供者,需要 `backend` 2 和对应权限。后端在 `initialize` 中选出要启用的提供者,宿主向它发 `lithe/keymap/import`。单独授权后,后端可以调用 `lithe/host/keymapImport/listInputs` 和 `readChunk`。 | +| [#1676](https://github.com/1lck/Lithe-IDEA/pull/1676) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 启动模板 `${logDir}` | 新增模板变量 `${logDir}`,是当前会话 `plugins//files/` 的绝对路径,由宿主显式传入 `logDirectory`。语言服务器和插件后端可用,`validationArguments` 不可用。`logDirectory` 缺失、为空或不是绝对路径时返回 `plugin.launch.invalidRequest`。 | + +### 2026-11-02 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1661](https://github.com/1lck/Lithe-IDEA/pull/1661) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `toolDefinitions` 1、`toolchainProviders` 1:宿主现状 | macOS 开始声明这两个能力,用于只读的工具链设置页。Windows 不声明。保存固定版本、`toolchainConsumers` 仍未实现。 | +| [#1653](https://github.com/1lck/Lithe-IDEA/pull/1653) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 函数协议 3:插件原生终端 | 新增权限 `terminal.create`(需要 `backend` 2),授予宿主方法 `lithe/host/terminal/create`、`show`、`close`、`reopen`。后端创建和管理自己的原生终端,reopen 时保留有界的显示历史。 | +| [#1650](https://github.com/1lck/Lithe-IDEA/pull/1650) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 函数协议说明 | 契约改为说明函数协议 1–3,并按实际情况描述官方 SDK 实现的协议。不改字段。 | +| [#1646](https://github.com/1lck/Lithe-IDEA/pull/1646) | [@FxRayHughes](https://github.com/FxRayHughes) | 清单 schema:`settings` 2、`languageServers` 2 | schema 补上 Core 从 #1395 起已经支持的这两个版本。 | + +### 2026-11-01 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1643](https://github.com/1lck/Lithe-IDEA/pull/1643) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 契约文档链接 | 工具链提案的链接和 schema `$id` 检查改为符合 lithe-plugin-docs 的同步规则。不改字段。 | +| [#1581](https://github.com/1lck/Lithe-IDEA/pull/1581) | [@FxRayHughes](https://github.com/FxRayHughes) | **`toolDefinitions` 1**、**`toolchainProviders` 1**、**`executableResolution` 2**、**`litheToolchains` 1**(提案) | 插件注入的工具链框架。工具定义描述工具的身份、目录布局、发现方式和候选,需要 `executableResolution` 2。工具链提供者需要 `backend` 2,通过 `litheToolchains` 协议回答 `toolchains/query`。同时新增 `proposals/toolchains/` 提案和本机工具链文档 `.lithe/toolchains/local.json` 版本 1。 | + +### 2026-10-31 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1617](https://github.com/1lck/Lithe-IDEA/pull/1617) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`languages` 2**、函数协议 3:编辑器动作 | `languages[].grammar` 是包内的 Monarch 语法,`languages[].configuration` 是编辑器配置,用到其中任一项都要声明 `languages` 2。后端用 `lithe/host/editor/registerActions` 注册不依赖语言服务器的编辑器动作,用户触发时宿主按普通插件函数调用。 | + +### 2026-10-30 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1595](https://github.com/1lck/Lithe-IDEA/pull/1595) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`backend` 2**、**`ui` 4**、**`litheFunctions` 3** | `backend` 2:后端可以在 `onWorkspaceOpen` 或插件命令时启动,通过函数协议接收工作区上下文,并可读取 `workspace.metadata`。`ui` 4:不打开工具窗口的后端命令。函数协议 3:工作区上下文与能力发现(`lithe/host/getCapabilities`、`lithe/host/workspace/*`)、宿主通知订阅,以及公共的文档、文件、窗口方法。 | + +### 2026-10-28 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1517](https://github.com/1lck/Lithe-IDEA/pull/1517) | [@Mucheen](https://github.com/Mucheen) | **`buildProviders` 2–6**、**`litheBuild` 2–6**、**`buildModelInputs` 1**、**`debugAdapters` 2**、**`litheTestDiscovery` 1** | 2 任务执行(`lithe/build/execute`),3 测试执行和测试发现(`lithe/build/test`、`lithe/build/discoverTests`),4 应用运行准备(`lithe/build/prepareRun`),5 原生调试准备(`lithe/build/prepareDebug`),6 只读模型输入。`buildModelInputs` 1 用于在已启用的工作区插件之间共享公开模型。`debugAdapters` 2 支持绑定调试目标的参数和环境变量。 | +| [#1483](https://github.com/1lck/Lithe-IDEA/pull/1483) | [@Mucheen](https://github.com/Mucheen) | **`packageTrust` 1** | 新增 `vendor.bundledSignatureRequirement`:随宿主发布、由发布者签名的插件副本按来源信任。Java Support 从此单独分发。 | + +### 2026-10-27 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1525](https://github.com/1lck/Lithe-IDEA/pull/1525) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `debugAdapters` 1:远程 attach | `attach` 配置的 `targetBindings` 可以带 `connection`(远端主机和端口),`debug.attach` 权限也覆盖远程 attach。适配器仍在本机运行,只有被调试进程在远端。 | +| [#1520](https://github.com/1lck/Lithe-IDEA/pull/1520) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `debugAdapters` 1:`tcpLanguageServer` 传输 | 新增传输方式 `tcpLanguageServer`:调试适配器由插件自己的语言服务器承载,例如 JDT LS 里的 Java Debug Server。`endpoint` 用 `languageID` 指定语言服务器,用 `command` 指定启动命令。 | +| [#1512](https://github.com/1lck/Lithe-IDEA/pull/1512) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `debugAdapters` 1:macOS 宿主 | macOS 开始运行调试适配器,插件可热激活。新增 `configurations.launch.executable`:调试目标是适配器不能直接调试的源文件(C、C++、Swift)时,从插件的文件设置或最多 8 个候选路径找可执行文件。新增 `console.commandPrefix` 和 `console.commands[]`,供调试控制台使用。 | + +### 2026-10-26 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1510](https://github.com/1lck/Lithe-IDEA/pull/1510) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **插件 API 级别 4**、**`debugAdapters` 1**、**`serverProcess` 2** | 新增 `debugAdapters[]`,用来声明 DAP 适配器的启动、传输、配置和权限,需要 API 级别 4。`serverProcess` 2 新增 `runSelectedRuntime`,只用于调试适配器。 | + +### 2026-10-24 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1470](https://github.com/1lck/Lithe-IDEA/pull/1470) | [@1lck](https://github.com/1lck) | **`backend` 1**、**`litheFunctions` 2** | 插件后端:插件自己的长驻进程,为自己的网页提供服务。函数协议 2 加入宿主服务,即凭据 `lithe/host/credentials/get`、`set`、`delete`;宿主把插件权限允许的服务列表传给后端。 | +| [#1424](https://github.com/1lck/Lithe-IDEA/pull/1424) | [@Mucheen](https://github.com/Mucheen) | `ui` 2:Windows 宿主 | Windows 声明式插件宿主上线,用 WebView2 实现包内网页并声明 `ui` 1、2,不声明 `ui` 3。 | +| [#1423](https://github.com/1lck/Lithe-IDEA/pull/1423) | [@arkleselect](https://github.com/arkleselect) | **`ui` 3**、**原生 UI 文档 schema 1** | 宿主渲染的原生控件,新增 `native-ui.schema.json`。 | +| [#1452](https://github.com/1lck/Lithe-IDEA/pull/1452) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`projectGenerators` 1** | 新建项目窗口里的项目生成器,可以用模板文件、脚手架命令或归档下载生成项目,需要 `project.generate` 权限。 | + +### 2026-10-23 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1430](https://github.com/1lck/Lithe-IDEA/pull/1430) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`vcsProviders` 1**、**`litheVcs` 1** | VCS 提供者。协议在 `initialize` 的 `experimental.litheVcs` 中协商,定义见主仓库的 `shared/contracts/vcs-provider-v1.md`。 | +| [#1442](https://github.com/1lck/Lithe-IDEA/pull/1442) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 契约发布 | 契约开始发布到 lithe-plugin-docs,fixtures 按能力改名,SDK 移出主仓库。不改字段。 | +| [#1420](https://github.com/1lck/Lithe-IDEA/pull/1420) | [@Mucheen](https://github.com/Mucheen) | **`buildSystems` 2**、**`executableResolution` 1** | 构建系统可以把可执行文件路径交给插件设置来配置。共享的可执行文件候选:统一的候选排序和严格选择。 | + +### 2026-10-21 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1395](https://github.com/1lck/Lithe-IDEA/pull/1395) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`languageServers` 2**、**`settings` 2** | `workspaceConfiguration` 用于向语言服务器推送工作区配置。设置 2 新增放置位置(`page`、`section`)、作用域、生效方式,`multiSelect`、`text`、`file`、`directory` 类型和页面按钮;插件可以注册设置页。 | +| [#1382](https://github.com/1lck/Lithe-IDEA/pull/1382) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `litheBuild` 1 | 构建提供者的请求可以带 `refresh`(用户要求重新加载)和 `tool.executable`(宿主解析出的构建工具绝对路径)。两者都是可选字段,协议仍是版本 1。 | + +### 2026-10-20 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1379](https://github.com/1lck/Lithe-IDEA/pull/1379) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 官方 Java SDK | Java 后端 SDK 首发,当时从主仓库发布。契约补充:探测 `java` 运行时版本用 `-version`,Java 8 也能识别。 | +| [#1366](https://github.com/1lck/Lithe-IDEA/pull/1366) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 网页调用插件函数 | macOS 把每个构建提供者进程作为所属插件的会话启动。插件网页的桥接新增 `functions.list`、`functions.invoke`、`functions.cancel`、`functions.progress`。 | +| [#1365](https://github.com/1lck/Lithe-IDEA/pull/1365) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 官方 Node SDK | Node 后端 SDK 首发,实现函数协议和 `litheBuild` 1。 | +| [#1358](https://github.com/1lck/Lithe-IDEA/pull/1358) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`litheFunctions` 1** | 插件函数协议:注册、撤回、调用、结果、进度、取消,在 `initialize` 的 `capabilities.experimental.litheFunctions` 中协商,由 Core 负责路由。 | +| [#1356](https://github.com/1lck/Lithe-IDEA/pull/1356) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `litheBuild` 1:macOS 宿主 | macOS 开始启动构建提供者并发送 `lithe/build/*` 请求。每个提供者是独立进程,工作区关闭、插件停用或插件包被替换前会停止。 | + +### 2026-10-19 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1345](https://github.com/1lck/Lithe-IDEA/pull/1345) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 社区插件安装 | 社区插件除了从第三方市场安装,还可以从本地 ZIP 或解包目录安装:宿主先取私有快照,再请用户确认安装审查。包里有签名文档时仍然必须通过验证。 | +| [#1339](https://github.com/1lck/Lithe-IDEA/pull/1339) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | `ui` 2:宿主现状 | macOS 用 WKWebView 实现包内网页并声明 `ui` 1、2。Windows 当时只声明 `ui` 1,`ui` 2 插件返回 `plugin.api.unsupported`,直到 #1424。 | + +### 2026-10-18 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1334](https://github.com/1lck/Lithe-IDEA/pull/1334) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **`ui` 2** | 工具窗口中的包内网页。 | +| [#1318](https://github.com/1lck/Lithe-IDEA/pull/1318) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **插件 API 级别 3**(`requires`)、**`buildSystems` 1**、**`buildProviders` 1**、**`litheBuild` 1**、**`litheBuildModel` / `litheDependencies` / `litheTestResults` 1** | 从级别 3 起,插件要按能力声明版本,首批 8 个能力都是版本 1(见上方能力表);市场索引里级别 3 的版本必须带 `requires`。新增声明式构建工具插件,以及构建提供者协议 `litheBuild` 1(`lithe/build/model`、`lithe/build/dependencies`)。 | + +### 2026-10-15 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1206](https://github.com/1lck/Lithe-IDEA/pull/1206) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | 插件 API 级别 2:`ui` 与 `ui.contribute` | 级别 2 加入第一阶段工作台 UI(宿主渲染的工具窗口和命令)和 `ui.contribute` 权限。`languages` 不再必填,插件可以只提供 UI。 | + +### 2026-10-14 + +| PR | 修改人 | 协议 / API | 改了什么 | +| --- | --- | --- | --- | +| [#1214](https://github.com/1lck/Lithe-IDEA/pull/1214) | [@xiaoyumuxi](https://github.com/xiaoyumuxi) | **插件 API 级别 2** | 新增启动模板(`arguments` 可用变量)、`runtimeDependencies` 的 `java`、`initializationOptions`、`hostIntegration`(仅官方插件)、包内或已发现运行时启动(`runtime`、`packagedRuntime`)和插件设置。Java Support 改为声明式官方插件。 | +| [#1170](https://github.com/1lck/Lithe-IDEA/pull/1170) | [@1lck](https://github.com/1lck) | **契约 v1**:**插件 API 级别 1**、**清单 `schemaVersion` 2**、**市场索引 `schemaVersion` 1** | 契约 v1 进入 `preview`:语言、语言服务器(`packaged` 和 `systemCommand` 启动)、`node` 和 `go` 运行时依赖、运行和测试提供者,以及市场索引和标准用例。 | + +## 维护 + +每次在本仓库同步新的契约版本(`node scripts/contract.mjs sync --revision `)时,都要在同一个 PR 里更新本页: + +1. 在 Lithe 主仓库列出两次同步之间改动契约的 PR: + + ```sh + git log origin/preview --first-parent --format='%h %cd %s' --date=short <上次记录到的 commit>..<本次 commit> -- shared/contracts/plugins/v1 rust/lithe-core/src/plugins/capabilities.rs rust/lithe-core/src/lsp/interface/plugin_function.rs + ``` + +2. 按日期把这些 PR 补到时间线最上面。新增的版本号要加粗,并写进“按协议查首次出现”的表。 +3. “修改人”填原 PR 作者的 GitHub 账号,写成 `[@账号](https://github.com/账号)`。账号可以这样查: + + ```sh + gh pr view --repo 1lck/Lithe-IDEA --json author --jq .author.login + ``` + +4. 把开头的“记录到”改成本次同步的 commit。 +5. 同时更新[能力与协议版本对照](capabilities-and-versions.md)和受影响的功能页。 +6. 在同步 PR 的描述里列出本次登记的 PR,并直接写 `@账号` 提到每位修改人,请他们核对自己那一行。本页里的 `[@账号](…)` 只是链接,不会通知任何人。 diff --git a/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/BackendTest.java b/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/BackendTest.java index df05e50..adb72f4 100644 --- a/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/BackendTest.java +++ b/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/BackendTest.java @@ -96,7 +96,7 @@ void functionIdsFollowTheSharedContractIncludingTheFixtureCases() throws IOExcep // Every ID the fixture accepts is accepted locally, and the fixture's // malformed ID refused, so the SDK never declares what Core rejects. Map fixture = object(Json.parse(new String(Files.readAllBytes( - Paths.get("../../../contract/v1/fixtures/function-protocol.json")), StandardCharsets.UTF_8))); + Paths.get("../../../contract/v1/fixtures/backend/function-protocol.json")), StandardCharsets.UTF_8))); for (Object entry : (List) fixture.get("negotiation")) { Map testCase = object(entry); List ids = new ArrayList<>(); diff --git a/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/HostTest.java b/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/HostTest.java index 278089a..57b2024 100644 --- a/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/HostTest.java +++ b/sdk/java/plugin-sdk/src/test/java/io/github/litheidea/plugin/sdk/HostTest.java @@ -40,7 +40,7 @@ class HostTest { private static Map loadFixture() { try { return object(Json.parse(new String(Files.readAllBytes( - Paths.get("../../../contract/v1/fixtures/function-protocol.json")), StandardCharsets.UTF_8))); + Paths.get("../../../contract/v1/fixtures/backend/function-protocol.json")), StandardCharsets.UTF_8))); } catch (IOException error) { throw new UncheckedIOException(error); } diff --git a/sdk/node/test/backend.test.mjs b/sdk/node/test/backend.test.mjs index 2d3dda8..15c037d 100644 --- a/sdk/node/test/backend.test.mjs +++ b/sdk/node/test/backend.test.mjs @@ -5,7 +5,7 @@ import { createBackend, isFunctionId, PluginError, PluginSdkError } from '../src import { gate, TestHost, withTimeout } from './harness.mjs'; const fixture = JSON.parse( - readFileSync(new URL('../../../contract/v1/fixtures/function-protocol.json', import.meta.url), 'utf8'), + readFileSync(new URL('../../../contract/v1/fixtures/backend/function-protocol.json', import.meta.url), 'utf8'), ); function functionBackend() { diff --git a/sdk/node/test/host.test.mjs b/sdk/node/test/host.test.mjs index ba19d81..e8e047d 100644 --- a/sdk/node/test/host.test.mjs +++ b/sdk/node/test/host.test.mjs @@ -5,7 +5,7 @@ import { createBackend, HOST_ERRORS, HostError, LIMITS, PluginSdkError } from '. import { gate, TestHost, withTimeout } from './harness.mjs'; const fixture = JSON.parse( - readFileSync(new URL('../../../contract/v1/fixtures/function-protocol.json', import.meta.url), 'utf8'), + readFileSync(new URL('../../../contract/v1/fixtures/backend/function-protocol.json', import.meta.url), 'utf8'), ); const CONTEXT = Object.freeze({ contextID: 'w1', generation: 2 }); diff --git a/sdk/rust/plugin-sdk/tests/host_services.rs b/sdk/rust/plugin-sdk/tests/host_services.rs index b857c8a..3ce8c78 100644 --- a/sdk/rust/plugin-sdk/tests/host_services.rs +++ b/sdk/rust/plugin-sdk/tests/host_services.rs @@ -22,7 +22,7 @@ const QUIET: Duration = Duration::from_millis(200); fn fixture() -> Value { // Read at run time: the packaged crate has no contract directory. let path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../../contract/v1/fixtures/function-protocol.json"); + .join("../../../contract/v1/fixtures/backend/function-protocol.json"); let text = fs::read_to_string(&path).unwrap_or_else(|error| panic!("{}: {error}", path.display())); serde_json::from_str(&text).expect("the fixture is JSON")