Repository navigation
feat(plugin): 插件自有语言服务器会话与后端决定启动方式(API-25,含 API-12/20/22) - #1745
Merged
Merged
Conversation
…thods (API-25) Add languageServers 3 session fields, serverProcess 3 backend launch with plugins.languageServers.decodeLaunch, and the languageServer host methods, notifications, permissions and error codes -32022/-32023/-32024/-32026.
- add the session policy engine for plugin-owned language servers: declared method and command whitelist, serialized commands that join the running one, gates that hold requests until the server reports readiness, and a bound on waiting requests - send backend requests and notifications to the plugin's own server through lsp.pluginServerRequest and lsp.pluginServerNotify; the server's result reaches the backend untouched and server errors keep their code and message as details - end every operation in one place (the request-completed hook), so answer, cancel, timeout and session failure leave the policy consistent - forward the notifications the plugin declared as pluginServerNotification - refuse a session of another contribution, a stopped server and a remote workspace the plugin marked unsupported - register plugins.languageServers.decodeLaunch and stateRetention
Document the backend-resolved language server launch (serverProcess 3) and the session fields of languageServers 3 in the plugin contract: README, manifest schema, a backend-launch fixture, golden launch-resolution cases, and invalid-manifest cases. Add the languageServer host methods, content provider, and host notifications to the README, and the four new Core commands to rust-core-api.md. Core tests run the golden cases, check the normalized session declaration, the host capability refusal, and the install review program. The contract verifier checks the new golden file.
宿主侧读取 launch.kind=backend 的声明、两步启动计划(先向后端询问命令行, 再让 Core 解码答案)、state.retention 和 temporaryDirectory,并让安装审查 列出由后端决定命令行的语言服务器。不改变既有三种启动方式。
宿主会话把后端对自己语言服务器的请求和通知转给 Core(lsp.pluginServerRequest / lsp.pluginServerNotify), 把 sessionPolicy 随 lsp.startServer 交给 Core,并把 Core 转发的服务器通知和错误 details 原样带回后端。 策略校验、白名单、串行和超时全部在 Core,Swift 只做 I/O。
Route lithe/host/languageServer/request, notify and setStatus from a plugin backend to the window's language server session, and deliver the server's state changes and forwarded notifications back to the owning backend with a per-process generation, so a replaced process never reaches the backend.
Add plugins.contentProviders.decode: a content provider function answers a string or {content}, and Core bounds it to 1 MiB so the host opens the text without interpreting it.
Register lithe/host/documents/registerContentProvider per workspace window, route navigation to a plugin scheme through the provider function, and open the answer as a read-only virtual document.
…end (API-25) A language server declared with a backend launch (serverProcess 3) now starts by asking the plugin's backend what to run. Rust Core builds the question and bounds the answer; the host moves them between the backend, the file system and the tool resolver. - MacPluginLanguageServerLaunchResolver: backendLaunchQuestion and backendLaunchProcess wrap the two Core steps, create the host directories, and keep a per-start scratch directory in the temporary area. - MacPluginBackendLaunchPreparer: calls the backend function in the window's workspace context, resolves a tool answer as the plugin's toolchain and refuses a program inside the project, drops a start whose context changed, and removes the scratch directory when the process ends or the start fails. - The language server runtime enters the existing prepareProcess flow for a backend-resolved server; registration accepts a server without executable names; remote roots are refused with a message. - PluginWorkspaceContexts.localContext(forFolder:) finds the context of the window that shows a folder. - Two interface texts in shared/i18n.
…estion (API-25) The README no longer promises a tools parameter the macOS host does not send yet, and lists the host limits of backend launches today: remote workspaces, project-local tool selection, and the per-start scratch directory.
…issions (API-22, API-25) A plugin-owned language server can report preparing, ready, degraded or failed through lithe/host/languageServer/setStatus. The workbench status bar now shows the plugin's own text (or a neutral sentence when it sent none) for the first server that is not ready, with the percentage when given. The host adds no wording about any language or tool. The plugin permission list explains the two new grants in plain words: languageServer.request and documents.contentProvider.
…-25) The host-service tests check what the host adds to request, notify and setStatus: parameter ranges, option refusal, failure code mapping, no sessions and a missing grant. The relay tests check generations, state order, dropped notifications of a replaced process, status ownership, the 1024-byte message cut and status cleanup.
…API-20) The per-workspace state a plugin-owned server writes under PluginState/<plugin>/<language>/workspaces/ had no bound. Rust Core already decides which of those directories expire from the plugin's declared state.retention; the host now lists them, hands Core the facts and removes the names Core returns. - MacPluginLanguageServerWorkspaceState: markUsed refreshes the directory time on each backend-launched start, prune deletes what Core names (never an open workspace, never a file or link, never a name it did not list), and reset discards one workspace's state. - The preparer runs the sweep in the background after the launch question is built; a failure is not reported to the start. - The workspace state resetter closure handles plugin servers that have no host integration, so rebuildWorkspaceState reaches them. - DeclarativeLanguageServerLaunchSource carries stateRetention. - Mark two best-effort scratch cleanups with log-exempt reasons.
…nguage server (API-20) The retention entries are the host's per-workspace directories, not the sub-directories a plugin makes inside its own workspaceStateDirectory. The host refreshes their time on each start and sweeps in the background. Discard of one workspace's state exists in the engine only; the claim of a generic user action is removed.
…PI-12, API-20, API-22) macOS implements the host methods, backend launch, content providers, state pruning and status display; Windows has no plugin backend host and keeps the legacy path, so it stays missing.
Why the host methods, session policy, generations, setStatus, backend launch and state retention live where they do, what the JDT paths map to, and what was rejected.
…erver sessions API-12 gains the read-only content provider, API-20 the language server's per-workspace state retention, API-22 the server preparation status and API-25 the languageServers 3 / serverProcess 3 session and backend launch. All are macOS-only; the rows keep what is still missing.
…ckend Backend 2 validation, added on preview, rejects a backend without a page, command, workspace or provider trigger. A language server with launch.kind backend asks the backend for its command line, so it is such a provider trigger.
…n capabilities The new host services use L10n from LitheModuleAPI, and the state retention call names RustCoreBridge.CoreCallError. macOS now announces languageServers 3 and serverProcess 3 so plugins with backend-resolved launches and plugin-owned sessions pass Core's exact version check.
This was referenced Nov 5, 2026
xiaoyumuxi
marked this pull request as ready for review
November 5, 2026 03:13
Lithe Review⏳ 正在以标准模式审查代码,完成或失败后会更新本评论。 |
Lithe 运行验收版本: 对照:共同祖先
未覆盖:
工作副本与运行状态已清理;可复用编译缓存按 12 GiB / 7 天预算保留。 |
- Plan doc: keep this PR's delivered API-12/20/22/25 rows and the other PRs' plan notes; take preview's rows elsewhere. - The new startPluginLanguageServer overload carries the session policy and no build-tool or host-integration parameters. A server with a session policy and no hostIntegration starts through it; any other server keeps preview's start unchanged. - The manifest schema forbids hostIntegration on a backend-resolved launch with not/required instead of a false property schema; the meaning is the same.
Lithe 运行验收版本: 对照:共同祖先
未覆盖:
工作副本与运行状态已清理;可复用编译缓存按 12 GiB / 7 天预算保留。 |
…anguage-server-api # Conflicts: # rust/lithe-core/src/tests/declarative_plugins.rs # shared/contracts/plugins/v1/README.md # shared/contracts/plugins/v1/plugin-manifest.schema.json
…anguage-server-api # Conflicts: # macos/Sources/LitheModuleAPI/Plugins/PluginTypes.swift # rust/lithe-core/src/plugins/capabilities.rs # rust/lithe-core/src/tests/declarative_plugins.rs # scripts/architecture/verify-shared-contracts.sh # shared/contracts/plugins/v1/fixtures/manifest/invalid-manifests.json # shared/contracts/plugins/v1/plugin-manifest.schema.json
# Conflicts: # docs/development/macos-plugin-api-plan.md # macos/Sources/Lithe/Application/Composition/AppServices.swift # macos/Sources/Lithe/Core/Rust/RustDeclarativePluginOperations.swift # macos/Sources/Lithe/Platform/MacOS/MacServiceContainer.swift # macos/Sources/Lithe/Platform/MacOS/Plugins/MacPluginBackendSessions.swift # rust/lithe-core/src/lsp/interface/engine.rs # rust/lithe-core/src/tests/declarative_plugins.rs # shared/contracts/plugins/v1/fixtures/manifest/invalid-manifests.json
合入 #1742(插件函数中转)。冲突全部是两边在同一位置各自新增: HostParams 与校验函数、错误码表(-32022..-32026 与 -32027..-32071 不重叠)、 SessionState 字段、request 事件分发、契约 README 与 fixture。 provider_query_trigger 合成为 toolchain、keymap、后端启动语言服务器、 导出函数四个条件之一即可。
xiaoyumuxi
added a commit
that referenced
this pull request
Nov 5, 2026
preview 合入了 #1745(插件自有语言服务器会话与后端决定启动方式)。冲突处理: - serverProcess 版本改为累积语义:选中运行时的调试适配器需要 2,后端 决定启动方式的语言服务器需要 3,requiresTools 需要 4;4 覆盖 2 和 3, 清单仍须声明覆盖所用特性的最低版本。capabilities 列表为 [1, 2, 3, 4], schema、契约 README 同步说明。macOS 宣告的版本保持 preview 的 [1, 3]。 - 主机通知沿用本分支的 grant 字段与 admitted(),admitted() 改用 #1745 的 holds_service,按方法授予的服务也能收到对应通知;run/didExit 与两个 languageServer 通知并存。 - 权限、错误码(-32022..-32026 按序)、通知参数校验、README 章节、契约 fixture、MacServiceContainer 的宿主服务注册、计划文档 API-22 行均取并集。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #1495
这个 PR 做了什么
让语言插件的后端拥有并驱动自己的语言服务器,并补齐通用通道,用来替代 Java 专属的启动、状态、虚拟文档和状态回收逻辑。这个 PR 只建基础设施:JDT 旧路径一条都不删,Windows 仍走旧路径。宿主和 Core 的新代码里没有 Java 词汇,也没有按插件名分支。
API-25:后端使用自己的语言服务器
新增宿主方法
lithe/host/languageServer/request、notify、setStatus。清单权限languageServer.request授权这三个方法。会话策略由 Rust Core 执行,Swift 只做 I/O。具体规则如下:
busy。remote:"unsupported"时拒绝远程工作区。宿主向后端推送两个通知:
didChangeState(starting/running/stopped/crashed)和didReceiveNotification。两者都带generation,上一代进程晚到的消息会被丢弃。新错误码,一码一义:
plugin.host.languageServerUnavailablelanguageServerErrormethodNotDeclaredforeignContribution超时、取消、busy、超限和前置条件失败沿用已有错误码。
清单版本:
languageServers3 表示会话字段,serverProcess3 表示launch.kind:"backend"。pluginAPIVersion仍为 4。macOS 的
PluginManifest.hostCapabilities宣告languageServers[1, 2, 3]和serverProcess[1, 3]。Core 按精确版本比对,serverProcess3 只允许由后端启动,和版本 2 的runSelectedRuntime没有重叠,所以 macOS 不需要一并宣告 2。由后端决定服务器的启动方式
plugins.declarative.languageServerLaunchPlan告诉宿主要调用哪个后端函数、超时多久、需要先创建哪些目录。plugins.languageServers.decodeLaunch在 Core 里校验,宿主再启动服务器。writablePaths。plugin.launch.invalidResult、outsidePackage、undeclaredTool、unwritablePath、invalidRequest、unavailable和notBackend。provider_query_trigger,契约 README 的 “Backend 2: workspace contexts” 已同步。API-12:内容提供者
lithe/host/documents/registerContentProvider,需要权限documents.contentProvider。{context, uri}调用它。返回值是字符串或{content},上限 1 MiB,由 Core 解码。API-20:工作区状态与回收
Application Support/Lithe/PluginState/<插件>/<语言>/workspaces/<哈希>/。state.retention决定。Swift 只列出目录,并删除 Core 点名、且自己列出过的目录。rebuildWorkspaceState。API-22:准备状态
setStatus上报 preparing/ready/degraded/failed 状态、一段文字(不超过 1024 字节,按字符边界截断)和百分比。进程结束时状态随之清除。lithe/function/progress,因为它只属于一次正在运行的调用,而准备工作(导入构建、建索引)通常比任何一次调用都长。languageServer.request和documents.contentProvider的权限说明。契约与文档
shared/contracts/plugins/v1/:fixtures/languages/目录:language-server-backend-launch.plugin.json和language-server-launch-resolution.json。README 顶部的 fixture 表已登记这两个文件。invalid-manifests.json追加 19 个用例。rust-core-api.md追加 3 条命令。.agents/notes/implemented/architecture/2026-11-04-plugin-owned-language-server-sessions.md。plugin-language-server-sessions:macOS 已实现、待验证,Windows 未实现。docs/development/macos-plugin-api-plan.md的 API-12、20、22、25 四行已更新。没有新增 API 编号。其他小改动
try?清理补了log-exempt理由。startPluginLanguageServer重载只带会话策略,不带构建工具参数和hostIntegrationLaunch。只有声明了会话字段、且没有hostIntegration的服务器走这个重载;其余服务器(包括带hostIntegration的官方集成)仍按 preview 原来的方式启动,不暴露给插件后端。无法执行会话策略的运行时,默认实现会把它当作普通插件后端启动,不带策略,因此插件后端无法访问该服务器。JDT 旧路径与新通道的对应
旧路径在本 PR 中全部保留,macOS 的旧路径由后续 PR 删除,Windows 在 Windows 迁移 PR 中处理。
is_java_build_commandserializedCommandsjava_configuration_runninggatesproject_preparation状态条setStatus(API-22)java做的远程检查remoteMacJdtWorkspaceState/jdtCacheRetentionstate.retention+plugins.languageServers.stateRetentionhostIntegration: jdtlaunch.kind:"backend"已知缺口
setStatus没有actions,也没有{done, total}。running。crashed上报。tools参数。用户选了工程内的工具时,工具类启动会直接失败,不会回退到其他候选。合并顺序与后续事项
validate_server_process_version:本 PR 的后端启动要求serverProcess恰好为 3,运行与调试 PR 会加 4。后合并的那个需要合入最新 preview,并按本 PR 的版本语义处理。plugin-manifest.schema.json里“后端启动不得带hostIntegration”这条约束改用not: {required: ["hostIntegration"]}表达,意思不变:闸门会把出现在properties里的hostIntegration计为宿主集成名,原写法因此会让 schemaEnums 从 13 升到 14。feat(plugin-api): 插件后端发起原生运行与调试,声明调试适配器行为与输出链接(API-22/23/30) #1746 锁定了scripts/architecture/language-specifics-baseline.json的两处下降,后合并的那个需要合入 preview 后重新运行--update-baseline。node scripts/contract.mjs sync --revision <合并后的 preview commit>同步契约。reference/protocol-changelog.md登记合入日期、本 PR、PR 作者,以及改动的协议:languageServers3、serverProcess3、内容提供者和准备状态等宿主函数。新版本号同时写进该页“按协议查首次出现”的表。需要确认的问题
tools参数?现在不发。setStatus的actions和{done, total}是否要在 Java 插件接入之前补上?entitlements 和 Info.plist 都没有改。
验证
合入 preview c5c6812 后的复验
上一轮 CI(e07197aaa)有两项失败:
cargo fmt --check:大部分是 preview docs(contracts): 插件契约 fixtures 按功能分目录并修正能力版本表 #1747 把 fixture 移进子目录后留下的超长行,preview 已在 feat: 项目生成器增加通用工具字段(projectGenerators 2,API-33) #1740 修正;另一处是本 PR 上次合并时移动的language_server_sessions.rs的LAUNCH_RESOLUTION常量,由 1dcf06b 修正。HTMLPreviewCapture/aGrantedCameraShowsInTheIndicatorAndCanBeMutedAndStopped()记为 incomplete:这个测试只输出了开始,没有输出结果,测试进程以 0 退出。该测试来自 feat(macos): HTML 预览新增页内查找、打印与导出 PDF、右键菜单调整;摄像头和麦克风统一默认拒绝、逐次确认 #1421,本 PR 没有改 HTML 预览;如果这一轮还出现,会另外提出。之后以 merge 方式先合入 4bb1978,再合入 c5c6812,带进了 #1729(Windows 终端停靠宽度)、#1740(项目生成器工具字段,含 rustfmt 修正)、#1751(宿主方法按 API 编号登记)、#1752(删除工具链契约草案)和 #1730(编辑器 Provider 与编辑器动作第 2 版)。
冲突及处理:
capabilities.rs和 SwifthostCapabilities里两边取并集:projectGenerators用 preview 的[1, 2],languageServers和serverProcess保留本分支的版本 3。verify-shared-contracts.sh:本分支的启动解析用例校验和 preview 的项目计划用例校验都保留,路径统一用fixtures变量。invalid-manifests.json、declarative_plugins.rs的 fixture 常量和校验列表:两边的用例都保留。AppServices、MacServiceContainer、MacPluginBackendSessions、RustDeclarativePluginOperations:preview 的编辑器 Provider 服务和本分支的语言服务器宿主服务、内容提供者、后端启动准备都保留,各自注册到宿主分发器。engine.rs的validate_start_request:同时校验本分支的session_policy和 preview 的host_options。docs/development/macos-plugin-api-plan.md的 API-25 行:本分支已交付的语言服务器部分和 feat(plugins): 编辑器 Provider 与多车道编辑器动作(API-25、API-27) #1730 已交付的 Provider 部分合在一起;单元格里代码片段的|转义为\|,避免表格断列。AppModel+CodeNavigation.swift:本分支原有的内容提供者钩子(3 行)随 preview 的改动挪了位置。已知的 preview 问题:
scripts/architecture/verify-core.sh的服务边界检查要求每个 AppModel 扩展不超过 600 行,而 preview 的AppModel+CodeNavigation.swift已有 627 行,合入后为 630 行,所以本地这一项不通过。CI 工作流不运行verify-core.sh。拆分这个文件不在本 PR 范围内。复验结果(macOS、Apple Silicon 本地):
scripts/rust-core/verify-rust-core-static.sh(与 CI 相同,cargo fmt --check只查lithe-core):通过。cargo test -p lithe-core:共 1936 个测试,完整运行时 1926 个通过。另外 10 个失败:9 个是remote::sessions::integration_tests,原因是两个工作树同时跑完整检查,本地 socket 报 EAGAIN(os error 35);1 个是search_everywhere_pages_all_sources_past_200_without_duplicates_or_missing_matches。单独重跑这两组,19 个全部通过。scripts/macos/test/test-macos.sh --filter,覆盖 PluginLanguageServerRelay、MacPluginLanguageServerWorkspaceState、PluginBackendLaunch、PluginContentProvider、PluginLanguageServerHostService、PluginLiveActivation、ProjectGeneration、ToolchainPluginAdmission、PluginManagementPresentation、PluginEditorProvider、PluginEditorAction:11 个套件 153 个测试全部通过。verify-shared-contracts.sh、verify-module-boundaries.sh。git diff --check。.agents/skills/write-stable-tests/scripts/verify-test-stability.sh --platform all --base c5c68126b --head HEAD:通过。合入 preview ef6f853 后的复验
以 merge 方式合入 ef6f853,带进了 #1744(Core 生成文件 URI)、#1747(插件契约 fixtures 按功能分目录)、#1748(Git watcher)和 #1750(协议登记规则)。
冲突及处理:
fixtures/languages/一行补上本 PR 的两个 fixture。languageServers保留本分支的版本 3;serverProcess行保留本分支的版本 3。declarative_plugins.rs的 fixture 常量采用 preview 的子目录路径,再补上本 PR 的LANGUAGE_SERVER_BACKEND_FIXTURE。git mv移到fixtures/languages/。Rust 测试、两个 Swift 测试、verify-shared-contracts.sh、rust-core-api.md和功能矩阵里的引用都改到新路径;README 里的fixtures/function-protocol.json改为fixtures/backend/function-protocol.json。LspDocumentURI,两者没有语义冲突。复验结果(macOS、Apple Silicon 本地):
cargo test -p lithe-core:1943 个通过,0 失败(单元测试 1909 个,集成测试 34 个)。LITHE_RUN_JAVA_DEBUG_INTEGRATION=1时运行。scripts/architecture/verify-shared-contracts.sh、verify-core.sh、verify-module-boundaries.sh。git diff --check。更早的验证
分支先 rebase 到 preview 的 c63bcd2,之后以 merge 方式合入 dbe8441(#1727)。合入时
docs/development/macos-plugin-api-plan.md有冲突:本 PR 已交付的 API-12、20、22、25 行用本分支内容,其他 PR 的计划备注保留。合入 #1727 后的复验(#1727 不改 Rust,合入时本 PR 的改动只涉及三个 Swift 文件和 manifest schema):
node scripts/architecture/verify-no-host-language-specifics.mjs和它的单元测试:通过,每项计数与基线一致。scripts/architecture/verify-core.sh、scripts/architecture/verify-module-boundaries.sh、node scripts/i18n/verify-app-language-localization.mjs、node scripts/i18n/generate-i18n.mjs --check、日志用法与日志事件、node --test scripts/plugins/plugin-settings-schema.test.mjs、git diff --check:全部通过。合入 #1727 之前的验证(均在 macOS、Apple Silicon 本地完成):
cargo test -p lithe-core:1935 个通过,0 失败(单元测试 1902 个,集成测试 33 个)。scripts/macos/test/test-macos.sh --filter ...:覆盖语言服务器宿主服务、后端启动与启动解析、内容提供者、工作区状态回收、宿主服务分发、后端会话、构建提供者会话、语言服务器状态、语言智能模块、声明式语言服务器等套件,共 276 个测试(18 个套件)全部通过。scripts/architecture/verify-shared-contracts.sh、Rust Core 注释规范、Agent Note、功能矩阵及其变更检查。node scripts/i18n/verify-app-language-localization.mjs、node scripts/i18n/generate-i18n.mjs --check。scripts/macos/verify/verify-runtime-bundle-immutability.sh)、模块边界。git diff --check,以及改动的.rs文件的 rustfmt。node scripts/sdk-e2e.mjs --source <本工作树> --ref feat/1495-plugin-language-server-api:构建、Java、Node、Rust 四组共 9 个测试全部通过。这一项是在 rebase 到最新 preview 之前的提交 3889fe692 上跑的;rebase 没有冲突,rebase 后重跑了 Core 测试、Swift 测试和静态检查。cargo check --bin lithe-windows:只有 preview 原有的两个 Windows 专属错误(project_window_registry.rs的os::windows和as_raw_handle),本 PR 没有新增。Windows 实际构建以 CI 为准。