Skip to content

Commit 01e6707

Browse files
authored
docs: add LSP plugin lifecycle and build integration guides (#9)
1 parent d11a97e commit 01e6707

5 files changed

Lines changed: 583 additions & 0 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@
1515
- 不修改已发布版本的 `downloadURL` 与 `sha256`;不删除历史版本(撤回问题版本除外)。
1616
- 插件的 bug 与功能请求请提到插件自己的仓库,本仓库只处理收录相关事务。
1717

18+
第一次开发语言插件,先读[LSP 插件完整生命周期](docs/lsp-plugin-lifecycle.md),
19+
再按[构建、运行与测试接入指南](docs/build-tool-integration.md)扩展能力,
20+
最后按[发布文档](docs/publishing.md)提交收录。
21+
1822
审查标准见 [docs/maintainers.md](docs/maintainers.md#审查清单)。

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Lithe 会把仓库地址解析为
3939

4040
| 文档 | 内容 |
4141
| --- | --- |
42+
| [LSP 插件完整生命周期](docs/lsp-plugin-lifecycle.md) | 从 Lua 模板开始:清单、服务器打包、激活、状态目录、停止、升级与卸载 |
43+
| [构建、运行与测试接入](docs/build-tool-integration.md) | 可用的执行提供者示例、工程后端边界,以及开发中的 API 3 状态 |
4244
| [docs/publishing.md](docs/publishing.md) | 插件作者:如何打包、发布 Release、提交收录和发布新版本 |
4345
| [docs/versioning-and-updates.md](docs/versioning-and-updates.md) | 版本号如何追踪、Lithe 如何选择可安装版本、用户如何更新/回退 |
4446
| [docs/maintainers.md](docs/maintainers.md) | 维护者:审查清单、下架与撤回、同步 Lithe 契约 |

‎docs/build-tool-integration.md‎

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# 为语言插件接入构建、运行与测试
2+
3+
LSP 服务器负责语言分析,构建工具负责项目导入、编译、依赖解析与产物。
4+
一个实用的语言插件应把这两条链路分别验证,再验证它们在保存、切换项目和停止时能配合工作。
5+
先完成[LSP 插件生命周期教程](lsp-plugin-lifecycle.md),再按本篇增加执行能力。
6+
本篇的可操作示例以 macOS 声明式插件宿主为范围;Windows 仍有独立扩展实现,
7+
不能凭 Core 校验通过推断已有相同的运行/测试宿主能力。
8+
9+
## 1. 先选择当前能发布的接入方式
10+
11+
本文于 2026-10-17 对照 Lithe 主仓库及 Lua 模板核对。API 3 的构建系统接口仍在开发;
12+
下面区分已支持的声明与 Core 中尚未形成产品闭环的接口。发布前还要核实目标宿主版本。
13+
14+
| 需求 | 当前接入方式 | 能做什么、边界在哪里 |
15+
| --- | --- | --- |
16+
| 补全、跳转、诊断 | API 1/2 `languageServers` | 按 LSP 提供能力,不自动声明编译和测试 |
17+
| 运行文件或固定项目命令 | API 1/2 `executionProviders[].run` | 命令与 argv 模板,工作目录为工作区根;不提供通用目标选择或构建模型 |
18+
| 运行整个工程、文件所在范围或指定测试 | API 1/2 `executionProviders[].test` | 测试项目标记、文件发现和命令规划;不是完整语义测试发现与结构化结果协议 |
19+
| 官方 Java 项目的运行/测试/调试 | 官方 Java 插件的 API 2 `hostIntegration: jdt` | 宿主已有 JDT 集成,不能复制为社区通用扩展点 |
20+
| 通用 Build 工具窗口、目标与依赖模型、增量同步 | API 3 `buildSystems`,开发中 | Core 验证/规划接口预览,尚不能作为市场可安装的社区构建插件发布 |
21+
22+
当需求只是 `lua <当前文件>`、`go test ./...` 这类固定命令,可以使用现有执行提供者。
23+
当需求需要 Maven/Gradle/Cargo 的模块、profile、target、classpath、依赖树或任务动态发现,
24+
应使用成熟构建后端和完整模型;不要把一个 `run` 命令包装成已经完成工程集成。
25+
26+
## 2. 给 Lua 模板加上运行能力
27+
28+
在生命周期教程的最小清单中,替换下面三个顶层字段,其他字段保留:
29+
30+
```json
31+
{
32+
"permissions": ["languageServer.launch", "process.run"],
33+
"activationEvents": ["onLanguage:lua", "onRun:lua"],
34+
"executionProviders": [
35+
{
36+
"languageID": "lua",
37+
"run": {
38+
"executable": { "command": "lua" },
39+
"arguments": ["${file}", "${userArguments}"]
40+
}
41+
}
42+
]
43+
}
44+
```
45+
46+
这也是[Lua 模板](https://github.com/Lithe-IDEA/lithe-plugin-lua)实际采用的方式。
47+
用户需要另行安装 Lua 解释器;随包提供的 LuaLS 不等于解释器。
48+
`process.run` 声明运行权限,`onRun:lua` 声明按运行操作激活,二者都不能省略。
49+
测试声明同理需要 `process.test` 和 `onTest:<languageID>`。
50+
51+
`executable` 只能选一个:裸 `command` 交给宿主工具路径发现,或使用宿主已经支持的
52+
项目 `toolchain` ID,例如 Go 的 `project-go`。插件不能发明一个 toolchain ID 就指望宿主实现。
53+
54+
所有参数都是独立 argv 元素,不经过 shell:
55+
56+
| 模板 | 含义与范围 |
57+
| --- | --- |
58+
| `${file}` | 工作区相对路径;以 `-` 开头时宿主加 `./`,适用于运行、文件测试和用例测试 |
59+
| `${fileDirectory}` | 文件所在目录,根目录为 `.`,其他为 `./…` |
60+
| `${userArguments}` | 仅用于运行,必须独占一个元素,展开为用户参数数组 |
61+
| `${testName}` | 指定测试用例名,去除首尾空白 |
62+
| `${testName:regex}` | 将用例名的正则特殊字符转义,适合测试筛选参数 |
63+
64+
不要在数组里写 `"lua ${file} && echo done"`、额外 shell 引号或依赖变量替换;
65+
那只是一个普通参数。路径中的空格也不需要作者再加引号。API 1/2 执行提供者没有可自行
66+
设置的 `cwd`、`${projectDir}` 或通用构建设置字段,工作目录固定是工作区根。
67+
68+
## 3. 用 Go 说明三个测试范围
69+
70+
下面是一个 **Go 语言插件**的顶层字段片段。它来自现有 Go 提供者的命令结构;
71+
需要与声明 `go` 语言及其服务器的清单合并,不能直接粘贴到 Lua 插件中使用。
72+
73+
```json
74+
{
75+
"permissions": ["languageServer.launch", "process.run", "process.test"],
76+
"activationEvents": ["onLanguage:go", "onRun:go", "onTest:go"],
77+
"executionProviders": [
78+
{
79+
"languageID": "go",
80+
"run": {
81+
"executable": { "toolchain": "project-go" },
82+
"arguments": ["run", "${file}", "${userArguments}"]
83+
},
84+
"test": {
85+
"frameworkID": "go",
86+
"workspaceLabel": "All Go Tests",
87+
"projectMarkers": ["go.mod", "go.work"],
88+
"testFilePatterns": ["*_test.go"],
89+
"executable": { "toolchain": "project-go" },
90+
"workspaceArguments": ["test", "./..."],
91+
"fileArguments": ["test", "${fileDirectory}"],
92+
"testCaseArguments": [
93+
"test",
94+
{ "value": "${fileDirectory}", "when": "fileSelected" },
95+
{ "value": "./...", "when": "noFileSelected" },
96+
"-run",
97+
"^${testName:regex}$"
98+
]
99+
}
100+
}
101+
]
102+
}
103+
```
104+
105+
- 工作区有任一 `projectMarkers` 才被识别为测试项目。`testFilePatterns` 是忽略大小写、
106+
只支持 `*` 的文件名模式,不是正则或任意 glob。
107+
- 工作区范围运行 `go test ./...`;“文件”范围运行文件所在的包,符合 Go 的测试单位,
108+
不承诺只执行该文件。
109+
- 用例范围有选中文件时限定包,没有文件时使用 `./...`;引用 `${file}` 或
110+
`${fileDirectory}` 的用例参数必须用 `when: "fileSelected"` 保护。
111+
- 声明式发现返回工作区和匹配文件,不会仅凭上述字段解析任意框架的测试函数、
112+
测试结果树或调试入口。要得到这些能力,需要对应后端及宿主消费支持。
113+
114+
这里的 `go run <文件>` 也只运行选中文件,不能替代需要多个源码文件的包运行。
115+
需求超出模板能力时,应如实说明边界,或等待完整构建模型接入。
116+
117+
## 4. 将项目事实留给成熟后端
118+
119+
先检查你选用的语言服务器或项目后端是否已经支持工程导入、构建命令和运行目标。
120+
例如 Java 的模块、classpath、测试/调试目标应使用相应 Java 后端给出的事实,
121+
不要由插件另读构建文件再拼一套容易漂移的 classpath。其他语言也应复用其成熟工具链。
122+
123+
一个完整的工程工作流需要确认以下顺序:
124+
125+
```text
126+
保存文档 → 等待后端接收并同步工程变更 → 构建目标 → 检查构建结果
127+
→ 取得本次构建后的运行路径/产物 → 启动所选入口
128+
```
129+
130+
构建失败时不能继续运行旧产物;项目切换后不能用旧模型启动新项目;取消后不能把迟到结果
131+
覆盖到新一次操作。上述职责需要真实后端与宿主配合,声明一条命令并不会自动完成。
132+
133+
构建工具正常解析项目依赖时,可能需要访问网络并写用户的工具缓存;这与启动插件时临时下载
134+
语言服务器不同。语言服务器和随包运行时应在打包阶段准备好。项目依赖、构建产物、
135+
Gradle daemon 状态、Maven 仓库或 Cargo target 等应由对应工具放到其用户缓存或工作区,
136+
不能写入插件包或应用安装目录。每个后台进程都要有明确的停止与清理策略。
137+
138+
## 5. API 3 构建系统接口:目前能研究什么
139+
140+
**当前状态:开发中的 Core 接口,不是可发布的完整宿主功能。**
141+
截至本次核对,已提交基线支持 API 1/2;开发工作区新增 API 3 的清单验证与纯规划模块。
142+
macOS/Windows 尚无通用 Build 工具窗口对它的完整消费链路,市场可安装版本选择也暂时跳过
143+
API 3。因此不要把自己的正式插件升到 API 3,再让用户通过安装市场获得尚未实现的能力。
144+
145+
如果你参与 Core/宿主开发,可以阅读开发版本中的 `buildSystems` 契约及 Cargo、Go、
146+
Gradle、Maven fixtures,并用下列问题检查设计。它们是接口评估项,不能替代产品验收:
147+
148+
| 模型部分 | 需要回答的问题 |
149+
| --- | --- |
150+
| 检测 | 如何发现根工程、嵌套模块、wrapper,如何排除生成目录? |
151+
| 工具与运行时 | wrapper/系统命令如何选择,所需 JDK/其他运行时如何发现和验证? |
152+
| 选项 | profile、target、模式、平台等如何进入明确的参数与环境,而不是拼接 shell? |
153+
| 工程模型 | 模块、目标、依赖和运行入口来自哪个成熟后端,如何解码与校验? |
154+
| 任务 | 支持哪些范围、是否需要用户参数、是否执行项目代码或访问网络? |
155+
| 同步 | 哪些配置变更触发重导入,如何取消旧请求并拒绝过期结果? |
156+
| 测试与诊断 | 发现、执行、结果映射由谁提供,路径如何对应用户源码? |
157+
| 生命周期 | 谁启动进程、监督超时、终止任务并清理状态,宿主是否已经消费这些计划? |
158+
159+
Core 的计划接口本身不启动进程、不监视文件、不持久化选择,也不会显示界面。
160+
即使 fixture 能生成正确命令,宿主仍须实现执行、权限/确认、取消、状态、结果和工具窗口。
161+
不要把内部 dispatcher 命令写成社区插件可以调用的宿主脚本 API。
162+
163+
`languageServers[].hostIntegration` 的 JDT 集成与开发中的构建 `hostIntegration` 是不同接口,
164+
都不是社区作者任意命名即可扩展的机制。现有 JDT 路径限官方插件;
165+
不要复制官方 ID 或修改 `vendor` 来绕过限制。未来正式开放的能力以目标版本契约和宿主为准。
166+
167+
## 6. 验证完整工作流
168+
169+
打包与清单校验完成后,还应在目标平台、实际宿主中完成:
170+
171+
1. 分别准备单文件、多文件、嵌套工程和带空格路径的项目,明确插件支持的范围。
172+
2. 保存修改后运行/测试,确认使用当前文件和目标;将源码改错,确认错误能被看到,
173+
完整构建集成应阻止继续启动旧产物。
174+
3. 覆盖工作区、文件和指定用例范围,核对实际命令、工作目录、输出与退出码。
175+
4. 缺少解释器、工具链版本不符、wrapper 无执行权限或依赖下载失败时,错误能定位到原因。
176+
5. 取消耗时任务,随后再运行;切换项目与关闭工作区,确认旧任务、锁与后端进程没有残留。
177+
若宿主或工具尚不能做到,应报告限制,不能只在文档里宣称已经支持。
178+
6. 禁用/卸载语言插件后检查该语言的运行、测试与 LSP 都处于正确状态。
179+
插件声明的语言由插件负责,不能假定缺失能力会回退到宿主内置实现。
180+
7. 清理验收启动的应用和辅助进程,再按[LSP 生命周期检查表](lsp-plugin-lifecycle.md#7-把停止失败和卸载也当作功能验收)
181+
验证重启、升级和卸载。
182+
183+
命令规划测试证明参数正确,进程冒烟验证证明工具能够启动,真实项目验收才能证明用户工作流
184+
成立。发布说明应明确已经验证的平台、宿主、语言服务器及构建工具版本。
185+
186+
相关入口:[生命周期教程](lsp-plugin-lifecycle.md) · [发布社区插件](publishing.md) ·
187+
[版本追踪](versioning-and-updates.md) ·
188+
[Lithe 插件契约](https://github.com/Lithe-IDEA/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/README.md)。

0 commit comments

Comments
 (0)