Skip to content

feat(plugins): 编辑器 Provider 与多车道编辑器动作(API-25、API-27) - #1730

Merged
xiaoyumuxi merged 13 commits into
previewfrom
feat/1495-plugin-editor-providers-api
Nov 5, 2026
Merged

xiaoyumuxi merged 13 commits into
previewfrom
feat/1495-plugin-editor-providers-api

Conversation

@xiaoyumuxi

@xiaoyumuxi xiaoyumuxi commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #1495

做了什么

对应计划 API-25 的"后续:新 Provider"和 API-27。API-26(诊断)不在本 PR。

  • 新服务 lithe/host/editor/registerProviders,权限 editor.providers。
    插件可以为 definition、hover、completion、codeLens、foldingRange、semanticTokens、documentLink 注册同插件函数。
    每个插件最多 32 个注册。不支持的类型返回 unsupportedOption(-32018),其余校验失败返回 invalidParams。
  • 选择器由语言 ID、扩展名、文件名组成,不区分大小写。Core 解码时统一转成小写、去掉扩展名开头的点、排序去重,宿主只做小写比较。
  • 合并规则:语言服务器先回答,插件只做补充(例如 Spring/MyBatis 的跨文件能力)。
    • 定义跳转追加插件给出的位置;LS 指回自身时让给插件;重复位置去重。
    • 悬浮和补全交给 Monaco 合并;CodeLens、折叠、链接直接追加。
    • 语义着色只在 LS 没有结果的位置生效。
    • 过期或陈旧的结果静默丢弃,只记 PluginsEditorProviderFailed 日志。
    • 只读预览不询问 Provider。
  • editor/registerActions 升级到 version 2:
    • 新图标:run、run-all、debug、test、test-passed、test-failed、test-skipped、navigate-super、navigate-implementations。
    • alternatives(最多 4 个),点击后 reveal(1 到 100 个位置),运行与导航分两条车道。
  • 安全:
    • 插件 URL 不进入 Monaco,编辑器里只画不透明的 lithe-plugin-link URL。
    • 宿主只打开 http/https 和工作区内的 file:。
    • 补全丢弃 data/command;CodeLens 命令只能指向同插件函数。
  • 协议 3 纯加法,支持热激活,插件停用或过期时撤回全部注册。没有新增 API 编号或错误码。
  • 宿主通用代码不含 Java 词汇;测试用第二种工具做桩(i18n key 跳转、Go、Rust、.http)。
  • 契约、fixtures、Rust Core API 文档、日志事件、i18n 词条、计划表、Agent Note、功能矩阵(新增 plugin-editor-providers,macOS partial,Windows missing)都已同步。
  • 拆分归属:Java 前端和宿主里的旧分支由后续 PR 删除,本 PR 不删。

宿主方法

原计划编号 方法 用途
API-25 lithe/host/editor/registerProviders 把 definition、hover、completion、codeLens、foldingRange、semanticTokens、documentLink 绑定到同插件函数,语言服务器优先、插件补充
API-27 lithe/host/editor/registerActions(version 2) 已有方法升级:行首多图标、alternatives、点击后 reveal

评审修复(lithe-code-review)

  • 按住修饰键悬停时只保留最新一次插件定义查询:新的悬停会取消旧查询,并让旧票据失效,旧结果不会再画出来。
  • 只读预览不向插件询问定义。跳转前复查文档版本,结果在呈现处再确认一次仍然有效。
  • 用户可见的错误改走 L10n.string。
  • 修正 EditorHostOptions 的 rustdoc 链接。
  • 插件的编辑器 Provider 在宿主 Provider 之前注册,悬停探针拿到的结果顺序稳定。
  • 新增 PluginEditorProviderTests 用例覆盖以上行为。

同步 preview(含 #1727、#1740、#1744、#1747、#1748、#1750、#1751、#1752)

验证

已通过:

  • cargo test --lib:plugins::editor_providers、plugins::editor_actions、declarative_plugins、plugin_function、toolchain
  • scripts/macos/test/test-macos.sh --filter 'PluginEditorProviderTests|PluginEditorActionTests|LanguageNavigationCoordinatorTests|MonacoNavigationCoordinatorTests|ExternalNavigationTests|NavigationHistoryFeatureModelTests':8 个套件 62 个测试通过
  • 更早一轮:PluginEditorProviderTests|PluginEditorActionTests|MonacoWorkbenchSessionLifetimeTests 27 个测试、Bun 两个规则测试
  • verify-no-host-language-specifics.mjs --base origin/preview(Ratchet verified)、verify-shared-contracts.sh、verify-log-usage.mjs、generate-i18n.mjs --check、verify-app-language-localization.mjs、rustfmt
  • 更早一轮:verify-editor-boundaries、verify-agent-notes、平台功能矩阵两个闸门、日志事件 generate --check、verify-service-boundaries、verify-module-boundaries、verify-runtime-bundle-immutability、verify-java-semantic-ownership、git diff --check
  • cargo check --bin lithe-windows:本 PR 没有改 windows/,在 macOS 上只剩 Windows 专用文件(project_window_registry.rs)本来就有的编译错误

尚未运行:

  • lithe-plugin-docs 的 sdk-e2e(合并后同步契约时运行)
  • Monaco 真机悬停探针、真机端到端;Windows 实机

暂按默认值处理、等评审确认的问题

  1. 测试结果只在 API-29 的运行输出里显示,暂不直接进入原生测试面板。
  2. 语义着色只在 LS 没有结果的位置生效,不和 LS 叠加。
  3. 链接只开放 http/https 和工作区内 file:,其余 scheme 丢弃。
  4. API-26 诊断延后到单独的 PR。

已知未完成

  • symbols、formatting、rename、codeAction、signatureHelp、inlayHints 暂不支持。
  • Windows 未接线(矩阵标为 missing)。
  • 真机端到端未验证(矩阵 verificationStatus: pending)。
  • 合并后在 lithe-plugin-docs 运行 node scripts/contract.mjs sync --revision <合并后的 preview commit> 同步契约。在同一个 PR 里把本 PR(editorProviders 新服务、editorActions: 2)登记进 reference/protocol-changelog.md 和 API 总表,新版本号写进"按协议查首次出现"表,并 @ 本 PR 作者核对。

…5, API-27)

Core 新增 plugins.editorProviders.decode(注册、结果规范化、definition 合并)并扩展
plugins.editorActions.decode 到 version 2:额外图标、alternatives、点击结果 reveal。
宿主通过 lsp.startServer hostOptions.editor 声明图标、快照上限和路由的 Provider 类型,
getCapabilities 回显。未知 Provider 类型返回 unsupportedOption(-32018)。

Refs #1495
…-27)

Refs #1495

- Declare the macOS editor host options (nine gutter icons, 1 MiB action
  source, seven provider types) and send them in the language-server
  start options so Core reports them through getCapabilities.
- Add the editor.providers permission label and the editor service
  `lithe/host/editor/registerProviders`: registrations are leased per
  plugin context, withdrawn on function withdrawal, session end and
  context close, and limited to 32 per plugin.
- Query matching providers concurrently with Core's per-type budgets and
  drop late, stale, failed or malformed answers; completion triggers only
  reach registrations that asked for the typed character.
- Supplement go-to-definition with plugin locations through Core's merge
  rule; the language server's answer is never replaced.
- Route editor action alternatives and the reveal result of a clicked
  action to the native navigation list.
- Bridge code lenses, document links, hover, completion, folding ranges
  and semantic tokens to the web editor behind opaque per-batch keys, so
  plugin targets and lens parameters never come from the page.
…s (API-25, API-27)

Refs #1495

- Plugin editor actions get a left lane for navigation icons and a right
  lane for run icons, one marker per line and lane. A click opens a menu
  with each action followed by its alternatives (for example Debug after
  Run) and posts the opaque action and alternative keys.
- Add the nine icon names the macOS host offers plugins with light and
  dark SVGs, and check that every offered name has an SVG.
- Register hover, completion, code lens, folding range, semantic token
  and document link providers for all languages. They only supplement
  the language server: semantic tokens apply where no language-specific
  provider exists.
- Draw document links with opaque URLs of a private scheme and open them
  through a registered opener that asks the host, so a plugin-supplied
  URL (including command:) never reaches Monaco.
- Keep the page rules (range conversion, hover escaping, folding limits,
  semantic legend merge, action markers and menus) in pure modules with
  unit tests.
… extension (API-25)

AppModel+CodeNavigation grew to 619 lines and tripped the 600-line AppModel
extension limit in verify-service-boundaries.sh. The plugin-only helpers
(presenting plugin definitions, wrapping link availability) move next to
pluginDefinitionQuery. The log-exempt marker for the handled decode failure
now sits on the catch line, where verify-log-usage.mjs looks for it.
…(API-25, API-27)

Update the plugin API plan rows for API-25/26/27, rewrite the editor
extension Note around the shipped provider and action design, and register
the editor providers capability in the platform feature matrix (macOS
partial, Windows missing).
契约要求选择器条目不区分大小写匹配,宿主按小写比较语言 ID,
Core 解码注册时把 languages 转成小写再排序去重。
同时把两处测试里的 compactMap 移出 #require,避免宏展开后报
call can throw 的编译错误。
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 4, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 4, 2026 23:12
@lithe-code-review

lithe-code-review Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← feat/1495-plugin-editor-providers-api · head: 26667473

未发现需要阻塞合并的问题。

Findings

  1. [P2] 原生 Go to Definition 入口没有查询插件 definition Provider
    macos/Sources/Lithe/Models/AppModel/AppModel+CodeNavigation.swift:108

    • 实际影响:原生菜单、命令面板和快捷键调用的 goToDefinition() 仍只走语言服务器;插件专属或插件补充的定义结果不会出现。Monaco 的点击/F12 路径走 navigateToSymbol(),因此入口行为不一致。
    • 触发条件:插件注册匹配当前文件的 definition Provider,然后执行原生 Go to Definition 命令。
    • 复现步骤:注册一个只提供 definition 的插件,在无语言服务器定义结果的文件中执行原生 Go to Definition;goToDefinition() 在 AppModel+CodeNavigation.swift:15-37 直接调用 performGenericNavigation,没有传入 pluginDefinitionQuery。
    • 来源:本次引入。新增查询只接入了 navigateToSymbol(),遗漏了现有原生入口。
  2. [P2] 只读预览仍会触发插件 Provider
    macos/Sources/Lithe/Models/AppModel/AppModel+CodeNavigation.swift:72

    • 实际影响:只读预览中的定义悬停或跳转会启动插件后端查询,可能读取预览文档快照,违反 PR 声明的“只读预览不询问 Provider”边界。
    • 触发条件:在 previewID != nil 的预览窗口中,对匹配 Provider 的符号执行修饰键悬停或跳转。
    • 复现步骤:打开只读预览并移动到符号;MonacoWorkbenchEditor 的 definitionHover/definition 路由没有 isReadOnlyPreview 守卫,而新增的 pluginSupplementedAvailability 和 pluginDefinitionQuery 会继续调用插件。
    • 来源:本次引入。页面 Provider 路由已有只读检查,但原生定义路径绕过了该检查。
  3. [P2] 定义结果在文档版本变化后仍可能被呈现
    macos/Sources/Lithe/Models/AppModel/AppModel+PluginDefinitions.swift:49

    • 实际影响:插件返回结果后,如果文档在 query 的最后一次版本检查与外层 await 恢复之间被编辑,旧位置仍可能被打开或放入导航列表。
    • 触发条件:插件查询完成后、presentPluginDefinitions() 或 performGenericNavigation() 继续执行前编辑同一文档,且光标位置未变化。
    • 复现步骤:让 Provider 延迟返回;在 PluginEditorProviderHostService.query 的版本检查通过后编辑文档。presentPluginDefinitions() 只检查光标,合并分支 AppModel+CodeNavigation.swift:455-460 只检查 operation ownership,没有再次比较 document.contentVersion。
    • 来源:本次引入。版本检查存在于查询内部,但缺少最终呈现边界的检查。
  4. [P2] definition 可用性查询没有按光标取消或全局并发上限
    macos/Sources/Lithe/Models/AppModel/AppModel+PluginDefinitions.swift:63

    • 实际影响:语言服务器无结果时,快速移动修饰键悬停会为每个符号启动一组插件调用;旧查询继续运行至 1500 ms 预算,多个匹配注册会进一步放大后端并发。
    • 触发条件:Provider 较慢、语言服务器返回空结果,并在多个符号之间快速移动鼠标。
    • 复现步骤:连续触发多个 definitionHover;前端旧请求只丢弃结果,Swift 侧没有保存或取消 pluginDefinitionQuery 任务,也没有复用页面 Provider 的 maximumRequests 限制。
    • 来源:本次引入。新增悬停补充路径没有加入高频交互的取消/限流。
  5. [P2] Provider/Action 失败通知绕过统一 i18n
    macos/Sources/Lithe/Models/AppModel/AppModel+PluginDefinitions.swift:91、macos/Sources/Lithe/Platform/MacOS/MonacoPluginEditorProviders.swift:262、macos/Sources/Lithe/Platform/MacOS/MonacoWorkbenchEditor.swift:1434

    • 实际影响:新增用户可见错误直接传入 showNotification,绕过 L10n.string;应用语言与系统语言不一致时无法保证通知使用应用语言。
    • 触发条件:定义合并失败、CodeLens/Provider 执行失败或编辑器 Action 执行失败。
    • 复现步骤:让对应插件函数返回错误,观察通知直接使用英文字符串。
    • 来源:本次引入。共享 i18n 已有 plugin.host.requestFailed 词条,但这些调用没有使用统一接口。

Scope Check

  • 关联 Issue:[Feature] macOS 存量重构计划:清理废弃实现,将 Maven/Java 领域逻辑完整迁入插件后端 #1495 是宏观迁移计划;本 PR 可验证地覆盖 API-25/API-27 的编辑器 Provider/Action 子范围,不能视为完成整个 Maven/Java 迁移计划。
  • 已完成范围:Rust Core 校验与合并规则、共享契约、macOS 宿主接线、Monaco Provider/Action 展示及对应单元测试。
  • 未完成范围:Windows 接线、真机端到端和 lithe-plugin-docs SDK E2E 仍在 PR 上下文中标为未完成或待运行。
  • 上游复用:实现复用了现有 LSP、Monaco 和插件函数调用基础设施;上述入口一致性、预览隔离和生命周期边界仍需补齐。
  • 每条 Finding 均由本次新增 Provider 路径或其新增调用点引入,属于本次变更职责范围。

Suggestions

  1. 把原生 goToDefinition()、Monaco 跳转和 Provider-only 文件统一到同一条合并路径,并补充原生入口集成测试。
  2. 在 definition 与 definitionHover 的消息入口阻止只读预览 Provider 查询。
  3. 将文档版本带到最终呈现点,再次校验后才合并或展示位置。
  4. 为定义可用性查询增加按文档/光标的取消和有界并发;补充快速悬停回归测试。
  5. 将三个通知改为 L10n.string(...),并运行两项 i18n 校验。PR 涉及编辑器 UI,但上下文未提供截图;请补充受影响界面的截图或前后对比。

Verification

  • 实际静态检查:base/head 指定 diff;AppModel+CodeNavigation.swift、AppModel+PluginDefinitions.swift、MonacoWorkbenchEditor.swift、MonacoPluginEditorProviders.swift、PluginEditorProviderHostService.swift、Rust Core Provider/Action 校验、前端 Provider/Action 规则、契约 fixtures、相关调用方和 Agent Note。
  • PR 正文提供了 Rust、Swift、Bun、契约、i18n、边界脚本等通过结果;head 上多项远程 Check Run 在提供的上下文中仍为 in_progress。
  • 本次未运行测试、应用、i18n 脚本或 CI,也未访问网络。
  • 非阻塞缺口:Windows 与真机验证、SDK E2E、截图和上述原生调用链集成测试仍需补充。

@lithe-code-review

Copy link
Copy Markdown

Lithe 运行验收

版本:26667473ff5881bcdffef12df04511d2587a936a;状态:partial。
本报告独立于代码审查;只报告实际执行的场景,不代表所有问题均已修复。

对照:共同祖先 c664ef6dfca6eed5f6f7b7d25b00000691377689 → PR head;当前目标分支合并适用性:未运行。

  • 条件不足,未验证:API-25 shared validation, normalization, result sanitization and definition merge rules, implemented in rust/lithe-core/src/plugins/editor_providers.rs and exposed through plugins.editorProviders.decode.
    • Cargo and rustc are available, but the inventory does not establish the MSVC compiler/linker and Windows SDK required to build Core and its native dependencies. No exact-revision Core binary is supplied. These prerequisites must be established before freezing a runnable native probe.
  • 条件不足,未验证:API-27 Action v2 icons, alternatives and reveal validation while preserving version 1 compatibility in rust/lithe-core/src/plugins/editor_actions.rs.
    • Requires the same confirmed Windows native build prerequisites or an exact-revision Core artifact as core-provider-contract. Frontend rule imports cannot establish native Action execution or reveal behavior.
  • 条件不足,未验证:Native Provider/Action integration, consistent definition entry points, read-only isolation, final-presentation freshness, lifecycle cleanup and actual editor interactions, including the review findings.
    • Requires macOS with an interactive desktop and an exact-revision Lithe product. This environment is headless Windows, and this PR explicitly leaves Windows host wiring missing. Language-server integration additionally requires an identified real server fixture. The document-version race requires controlled synchronization; hover performance requires a prespecified statistical protocol.

未覆盖:

  • All selected scenarios remain unexecuted and blocked; none of their requirements currently has acceptance evidence.
  • Actual protocol-3 capability discovery, editor.providers permission admission, same-plugin/session function ownership, 32 registrations per plugin, duplicate registration rejection and host error mapping to invalidParams or unsupportedOption (-32018).
  • Actual case-insensitive host selector matching across language IDs, extensions and filenames; completion trigger-character routing.
  • Native hover/completion merging, appended CodeLens/folding/link results, and semantic coloring restricted to positions without language-server results.
  • Provider deadlines, silent late/stale result disposal and PluginsEditorProviderFailed logging; document/context/session ownership at final presentation and click boundaries.
  • Rapid definition-hover cancellation, concurrency limits and performance. No statistical acceptance protocol has been supplied.
  • Document-link security: opaque lithe-plugin-link URLs reaching Monaco, only http/https or workspace-contained file targets opening, and forbidden schemes or outside-workspace targets being rejected.
  • CodeLens invocation restricted to live same-plugin functions; forged or stale opaque action/lens/link keys rejected without executing side effects.
  • Hot activation, subscription disposal, disable/uninstall, backend restart, workspace generation changes and editor/project closure withdrawing registrations, canceling owned work and releasing resources.
  • Action collection from dirty live buffers, source/result limits, save/edit responsiveness, correct alternative payloads, reveal navigation, and Run/Debug output routing without unintended side effects or retries.
  • Application-language failure notifications, all icon resources, visual lane layout, menus, keyboard focus and genuine native interaction.
  • Runtime installation/bundle immutability and process/resource cleanup after representative plugin workflows.
  • Contract/fixture/documentation synchronization, logging and i18n generation consistency, generic-host language independence, feature-matrix accuracy and lithe-plugin-docs SDK E2E.
  • Windows host functionality remains explicitly missing. API-26 diagnostics and symbols/formatting/rename/codeAction/signatureHelp/inlayHints remain deferred; this plan does not certify those capabilities or completion of the broader [Feature] macOS 存量重构计划:清理废弃实现,将 Maven/Java 领域逻辑完整迁入插件后端 #1495 migration.

工作副本与运行状态已清理;可复用编译缓存按 12 GiB / 7 天预算保留。
详细步骤和原始证据保存在服务器受限目录(14 天、总计不超过 1 GiB);当前没有公开下载链接。

- 修饰键悬停只保留最新一次插件定义查询,新悬停取消旧查询并让旧票据失效
- 只读预览不向插件询问定义,跳转前复查文档版本,结果在呈现处再确认仍然有效
- 用户可见错误改走 L10n.string
- 修正 EditorHostOptions 的 rustdoc 链接
- 插件编辑器 Provider 在宿主 Provider 之前注册,悬停探针的顺序稳定
# Conflicts:
#	docs/development/macos-plugin-api-plan.md
#	rust/lithe-core/src/tests/declarative_plugins.rs
#	shared/platform-feature-matrix/features/plugin-editor-actions.json
- editor-providers、editor-actions-v2 fixtures 移入 fixtures/editor/,同步测试、契约脚本、README 和功能矩阵路径
- 转到定义的插件分支复用 languageServerIsPreparing,不在宿主导航代码里新增语言名
- plugin-editor-actions.ts 语言词计数降为 0,更新基线
# Conflicts:
#	rust/lithe-core/src/tests/declarative_plugins.rs
#	shared/contracts/plugins/v1/fixtures/manifest/invalid-manifests.json
- #1752 新增的 toolchain_vectors.rs 写死了 9 处工具名,Verify Host Language Specifics 在 preview 上失败
- 工具 ID 改从 fixtures/toolchains/tool-definition.plugin.json 读取,路径里的工具名换成中性的 tool
- toolchain_resolution.rs 复用 MANUAL_HOME,计数 90 -> 89,基线只调低这一行
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant