Skip to content

fix(desktop): LSP 日志级别前缀着色、自动换行与横向滚动,状态栏打开语言服务菜单 - #1876

Merged
xiaoyumuxi merged 1 commit into
previewfrom
fix/1875-desktop-lsp-log-view
Nov 13, 2026
Merged

xiaoyumuxi merged 1 commit into
previewfrom
fix/1875-desktop-lsp-log-view

Conversation

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

关联 Issue / Linked issue

PR 类型 / Type

  • feat:新功能或用户可见的改进
  • fix:缺陷修复
  • docs:文档、Agent Note、Skill 等规则文件
  • release:稳定版发布准备

改动说明 / What changes

背景 / Why

#1873 合入后,Viva 桌面端(desktop/)的 LSP 日志已经是普通页签,但用户实际使用时还有三个问题:

  1. 日志行开头的 [INFO]、[ERROR] 等级别前缀和正文颜色相同,看不出级别。
  2. 超出页签宽度的长行在右边缘被截断,既不能横向滚动,也没有自动换行开关。多行日志事件挤在一行里。
  3. 点击状态栏的“● LSP 就绪”直接打开日志,没有旧 macOS 客户端那样的“语言服务”菜单,不能在状态栏查看各服务器状态、停止或重启。圆点颜色也不随状态变化。

改动 / Changes

级别前缀单独着色(lsp_status.go)

  • 日志行 lspLogLine 新增 tag 字段,记录 [LEVEL] 前缀的字节长度,parts() 拆成前缀和正文两段,页签里画成两个标签。
  • 新增 lspLevelColor:ERROR 用主题危险色,WARN/WARNING 用警告色,DEBUG/TRACE 用弱化色,其余级别(INFO、NOTICE 等)用强调色。
  • 正文颜色沿用 lspLogColor:错误、警告、调试行的正文继续按级别着色,信息行正文是普通文字色。

长行:横向滚动与自动换行(lsp_status.go)

  • 日志行第一次绘制时按日志字体量出宽度,缓存在 lspLogLine.width 里(measureLogLines),之后不再重复测量。
  • 不换行(默认):列表的 RowWidth 取最宽的行加左右内边距,长行保持单行,整个列表可以横向滚动(触控板或 Shift+滚轮)。行用 Text 加 Shrink(0),不会被压缩截断。
  • 页签工具栏新增“自动换行”开关(复用已有词条 native.softWraps,按钮名 lspWrap)。开启后 RowWidth 为 0,行用 ui.Wrap 按页签宽度换行;切换时横向滚动回到最左边(ScrollToX(0))。
  • 已知限制:Viva 的 List 目前不绘制横向滚动条,只能用触控板或 Shift+滚轮横向滚动。这是 Viva 的能力缺口,按仓库规则不在应用层绕过。

日志事件的行处理(recordServerEvent、appendLog、cutLogLine)

  • 多行事件(\n、\r\n)拆成多行,只有第一行带 [LEVEL] 前缀,后续行保留相同级别,正文颜色一致。
  • 制表符展开为 4 个空格(lspLogTab),避免宽度测量和绘制不一致。
  • 单行上限(lspLogLineBytes)从 600 字节放宽到 4096 字节,原来 600 字节会切掉常见的堆栈行和 JSON 消息。超出时仍按字符边界截断,不会切坏多字节字符;现在会加上“…”标记(lspLogCut),原来是无标记地静默截断。
  • 环形缓冲上限仍是每个服务器 500 行,按拆分后的行计数;logsAdded 同样按行计数,所以自动跟随到底部的判断不变。

状态栏“语言服务”菜单(lsp_status.go、app.go、chrome_view.go)

  • 点击状态栏的 LSP 状态项不再直接打开日志,而是切换 a.editor.lsp.menu,在状态项上方弹出菜单(lspStatusMenu,ui.Menu 加 Above())。菜单内容与旧 macOS 客户端的“语言服务”菜单一致:
    • 标题:“语言服务”(native.languageServices)。
    • 每个运行中的服务器一行,以插件 ID 为名:图标形状表示状态(lspPhaseIcon),附“状态 · 版本”。子菜单包括:
      • “查看日志”:打开该服务器的日志页签。
      • “重启”“停止”:复用页签工具栏的同一套流程。
    • 分隔线后是:
      • “查看日志”
      • “重启所有服务器”(native.restartAllServers)
      • “停止所有服务器”(native.stopAllServers)
    • 最后是“管理语言服务器插件…”(新词条 desktop.lsp.managePlugins),打开设置窗口的插件页。
    • 没有服务器时,显示一个不可点的“未注册语言服务器”(native.noRegisteredLanguageServers)。
  • 状态栏圆点按服务器状态着色(lspStatusColor):就绪绿、出错红、已停止灰、启动中黄。原来固定是弱化色。
  • 新增 lspPhase 枚举和 phase(),状态文字、图标和颜色都由它决定。原来写在 lspStatusText 里的状态文字抽成 lspPhaseText(server),菜单中的每个服务器也用它,词条键不变。
  • 抽出 showLSPLog(打开或聚焦页签并切到指定服务器)和 restartLSPServer,供菜单和页签工具栏共用。

i18n(shared/i18n/、desktop/internal/i18n/shared.generated.go)

  • 新增 desktop.lsp.managePlugins,中文“管理语言服务器插件…”,英文“Manage Language Server Plugins…”。
  • 以下已有词条的平台范围加上 desktop,文案不变,并重新生成:
    • native.inline.viewLogs
    • native.noRegisteredLanguageServers
    • native.restartAllServers
    • native.stopAllServers
    • native.softWraps

功能矩阵(shared/platform-feature-matrix/features/lsp-status-logs.json)

  • notes 改写,写明菜单、圆点颜色、多行拆分、制表符展开、4096 字节截断、前缀着色、自动换行开关,以及 Viva 不画横向滚动条的限制。macOS、Windows 的证据都加上 chrome_view.go。状态字段不变。

测试(lsp_status_test.go、close_tab_shortcut_test.go)

  • 新增:
    • TestLSPLogSplitsLinesAndCutsHugeOnes:多行拆分、前缀只在第一行、制表符展开、中文长行按字符边界截断。
    • TestLSPStatusMenuListsTheServersAndStopsThem:
      • 三种状态下圆点的颜色。
      • 点击状态项只开菜单,不开页签;菜单位于状态项上方,四个固定项都在。
      • 服务器子菜单能打开该服务器的日志。
      • 停止所有服务器后,菜单显示“未注册语言服务器”。
    • TestLSPLogScrollsSidewaysOrWrapsLongLines:
      • 不换行时行宽覆盖最长行,用真实滚动事件确认能横向滚动。
      • 开启自动换行后行宽归零、横向滚动复位,长行占多行,短行仍是一行。
  • 改写:
    • TestLSPLogLineColorFollowsTheEventLevel:增加 lspLevelColor 的检查,并检查页签实际绘制的颜色:[INFO] 是强调色,正文是普通文字色,[ERROR] 和它的正文都是危险色。
    • TestLSPLogIsBounded:增加对截断标记的检查。
    • 其余用例的日志行字面量改为带字段名的写法。
  • 新增辅助函数 openLSPLogFromStatus:按用户的操作路径,先点状态项打开菜单,再点“查看日志”。TestCloseTabShortcutClosesTheSelectedEditorTab 也改用它打开日志页签。

不在本 PR 范围内 / Out of scope


类型专属:fix

复现与根因

  • 复现步骤、实际结果、期望结果:
    1. 在 Viva 桌面端打开项目,打开一个由插件语言服务器负责的文件,等状态栏显示“● LSP 就绪”。
    2. 点击状态项。实际结果:直接打开日志页签,没有菜单,圆点始终是灰色。期望结果:弹出“语言服务”菜单,圆点按状态着色。
    3. 在日志页签中查看。实际结果:[INFO] 和正文颜色相同;长行被截断,不能横向滚动,没有自动换行开关;多行事件挤在一行。期望结果:前缀单独着色;长行可以横向滚动,或者切换成自动换行;多行事件分行显示。
  • 根因:
  • 影响平台:Viva 桌面端 macOS 和 Windows 共用这段代码,两端都有这三个问题,本 PR 同时修复。

检查项

  • 已先写回归测试,确认它在修复前失败、修复后通过。测试路径:desktop/internal/workbench/lsp_status_test.go。新测试用到 preview 上没有的字段和函数,没法直接在 preview 上编译,所以把实现文件换回 preview(6bc91bc57)的版本,用只依赖 preview 已有接口的等价临时测试运行。失败输出如下:
    zz_prefix_check_test.go:18: the status item opened 1 log tabs and no Language Services menu
    zz_prefix_check_test.go:33: "[INFO]" is painted {0 0 0 0} (shown false), not the accent color
    zz_prefix_check_test.go:44: rows are 0 wide and the tab has no Soft Wraps toggle
    zz_prefix_check_test.go:52: a two-line event kept 1 rows
    
    在本分支上,同一组临时测试和正式回归测试都通过。临时测试已删除,没有提交。
  • [N/A] 无法自动化时,已写明手动复现与验证步骤:上述行为都有自动化测试覆盖,包括真实绘制颜色、真实滚动事件和菜单点击。
  • 已搜索被修改函数、类型、契约字段的全部调用方:
    • lspLogLine 只在 lsp_status.go 和它的测试中使用。
    • filterLogLines、lspLogColor、openLSPLog 的调用方都在 lsp_status.go、app.go 和测试中。
    • 点击 itemLSP 只在 app.go 的 activate 中处理。
    • chromeStatusItem 的弹出层只在 chrome_view.go 中。
    • 不涉及契约字段、插件、SDK 或脚本。
  • 已检查另一平台是否有同类缺陷:两个平台共用 Viva 桌面端这份代码,本 PR 一起修复。旧 macOS 客户端已在 Epic(0.7.0): 基于 Viva 框架的跨平台方案的迁移(#1794) #1799 删除。
  • 只改了修复所需的代码。showLSPLog 和 restartLSPServer 是为了让菜单和工具栏复用同一流程而抽出的;lspPhase 是为了让状态文字、图标、颜色共用一个判断。发现的既有问题(Viva 不画横向滚动条)属于 Viva,见“不在本 PR 范围内”。
  • [N/A] 修复改变了行为约束或容易复发,已在 .agents/notes/implemented/bug-fix/ 记录:没有新增需要长期记住的取舍。行为已由回归测试和功能矩阵的 notes 固定。

范围检查 / Scope checks

功能矩阵 / Feature matrix

  • 已更新 shared/platform-feature-matrix/features/lsp-status-logs.json 的 notes 和两端的证据路径。实现状态和验证状态未改:Windows 没有实机验证,保持原状态。
  • 已运行 ./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh(validated 223 platform capabilities)和 ./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD(matrix change gate passed)。
  • 没有提交 .artifacts/ 下生成的矩阵视图。
  • [N/A] 没有更新矩阵的理由:已更新。

统一日志系统 / Structured logging

  • 已接入统一日志系统:新的用户操作都使用已声明的 app.action.performed 事件,没有新增事件 ID,也没有直接输出:
    • 菜单中的操作记 lsp.log.open、lsp.server.restart、lsp.server.stop、lsp.servers.restartAll、lsp.servers.stopAll,surface 为 menu。
    • 自动换行开关记 lsp.log.wrap,surface 为 button。
    • “管理语言服务器插件…”通过 openPluginSettings 记 settings.open,surface 为 menu。
    • 只开关状态栏菜单本身不记日志。
  • 一个操作只有一条终态。“重启/停止所有服务器”整体记一条,不按服务器逐条记录;没有新增失败路径,也没有高频的 INFO 输出。
  • 没有记录日志内容、源码或路径,只记录操作 ID 和来源。
  • 已运行 node scripts/logging/generate-log-events.mjs --check 和 node scripts/logging/verify-log-usage.mjs:未完成。scripts/logging/ 下的门禁脚本已被移除,无法运行;恢复工作由 [Feature] 恢复 Viva 日志事件类型生成和目录使用门禁 #1802 跟踪。

界面文案 / i18n

  • 文案都走共享 i18n:新增 desktop.lsp.managePlugins,中英文都已补齐;复用的五个已有词条只在平台范围加上 desktop。已用 node scripts/i18n/generate-i18n.mjs 重新生成,没有手改生成文件。
  • 已运行 node scripts/i18n/verify-app-language-localization.mjs 和 node scripts/i18n/generate-i18n.mjs --check,都通过;没有放宽脚本或扩大豁免名单。

逻辑归属 / Logic placement

性能与内存 / Performance and memory

  • 没有新增后台任务或轮询,菜单和页签打开时才渲染。日志缓冲仍是每个服务器 500 行,每行仍有上限。单行上限从 600 字节放宽到 4096 字节,所以每个服务器最坏情况下的日志内存从约 300 KB 增加到约 2 MB,仍然有界。每行只在第一次绘制时测量一次宽度,结果缓存在行上。
  • 主线程上没有新增文件读写、目录遍历或进程启动。
  • [N/A] 性能基线:没有改动 Viva 编辑器模块(desktop/internal/editor/**、editorview/**),也没有新增依赖。

设置 / Settings

N/A:没有新增或修改设置项。“管理语言服务器插件…”只是打开已有的插件设置页。

插件契约 / Plugin contract

N/A:不涉及插件契约、能力版本、插件函数协议或宿主方法。

发行包与资源 / Bundle and resources

  • 运行时不向已安装的 app bundle 或 Windows 安装目录写入,也没有新增资源路径;已运行 ./scripts/desktop/verify/verify-runtime-bundle-immutability.sh,通过。
  • [N/A] 下载、解压、生成或缓存资源:本 PR 没有新增或修改。

无用代码与重构 / Dead code and refactoring


验证 / Verification

命令或操作 平台 结果
gofmt -l(改动的 Go 文件) macOS arm64 无输出
scripts/desktop/desktop-go.sh vet ./... macOS arm64 通过
scripts/desktop/desktop-go.sh test ./internal/workbench/ ./internal/settingswindow/(基于最新 preview 6bc91bc) macOS arm64 全部通过(workbench 139.4s)
回归测试在 preview 上先失败(输出见上方“检查项”),在本分支上通过 macOS arm64 修复前 4 项失败,修复后全部通过
scripts/desktop/desktop-go.sh test ./internal/workbench/ -run 'TestLSP|TestCloseTab' -count=1 -v macOS arm64 12 个测试通过
.agents/skills/write-stable-tests/scripts/test-stability-macos.sh --package ./internal/workbench --filter '<本 PR 新增和改写的测试>'(静态检查加计时运行) macOS arm64 通过,共 12 个测试
./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh macOS arm64 通过(223 项)
./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD macOS arm64 通过
node scripts/i18n/verify-app-language-localization.mjs macOS arm64 通过
node scripts/i18n/generate-i18n.mjs --check macOS arm64 通过
./scripts/desktop/verify/verify-runtime-bundle-immutability.sh macOS arm64 通过

未能执行的检查和原因:

  • Windows 构建与实机验证:当前只有 macOS 机器。代码两端共用,但没有在 Windows 上运行过。
  • 统一日志门禁(scripts/logging/*):脚本已被移除,见 [Feature] 恢复 Viva 日志事件类型生成和目录使用门禁 #1802。
  • 整机 GUI 验收和截图(明暗主题、中英文):没有启动打包后的应用。只运行了工作台的组件和交互测试,包括测试驱动的点击、滚动事件和绘制颜色快照。

提交前确认 / Final checks

  • 分支从最新的 preview(6bc91bc57)创建;目标分支为 preview。
  • 以 Draft 提交,本地验证通过后才标记 ready。
  • 没有无关改动,没有提交 .build/、target/、dist/、.artifacts/ 等产物,没有凭据、个人路径和未脱敏的日志或截图。
  • 本次没有启动 Lithe 应用;测试进程都已结束,没有进程残留。

- 日志行的 [INFO]/[ERROR] 等级别前缀单独着色:信息用强调色,错误红、警告黄、调试与跟踪灰;
  错误、警告、调试行的正文继续按级别着色。
- 日志页签工具栏新增“自动换行”开关。关闭时长行保持单行,整个列表按最宽的行横向滚动
  (触控板或 Shift+滚轮);开启时按页签宽度换行。
- 多行日志事件拆成多行,制表符展开为 4 个空格;单行超过 4096 字节按字符边界截断并加“…”。
- 点击状态栏的 LSP 状态不再直接打开日志,而是在上方弹出“语言服务”菜单:每个服务器一行,
  图标形状表示状态,附状态与版本,子菜单可查看日志、重启、停止;其后是查看日志、重启所有服务器、
  停止所有服务器和“管理语言服务器插件…”;没有服务器时显示“未注册语言服务器”。
- 状态栏圆点按服务器状态着色。

Closes #1875
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 13, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 13, 2026 18:40
@xiaoyumuxi
xiaoyumuxi merged commit 1cad09c into preview Nov 13, 2026
5 of 7 checks passed
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