Skip to content

fix(terminal): Tab 交还 shell——cd 不再被隐藏参数抢占,多候选不默认填第一项 - #249

Merged
jinpy666 merged 1 commit into
jinpy666:mainfrom
Evvvvvvvan:codex/ssh/terminal-input-completion-fix
Oct 27, 2026
Merged

jinpy666 merged 1 commit into
jinpy666:mainfrom
Evvvvvvvan:codex/ssh/terminal-input-completion-fix

Conversation

@Evvvvvvvan

Copy link
Copy Markdown
Collaborator

Agent handoff

  • Task ID: 《服务器终端输入与补全优化计划》T1 / T2 / T3 / T4 / T6(T5 在 HOST 仓库单独提 PR)
  • Stage: implementation
  • Base SHA: b19eefb(main)
  • Allowed paths respected: yes
  • Depends on PR: —

跟进

计划里六项输入/补全缺陷中的五项。P1/P2/P3/P6 是本地可复现的确定性缺陷,P4 是前后端源码确认的语义缺口。

改动

  • P1 隐藏特殊参数抢占 Tab:figCompletionSource.buildItems() 跳过参数 suggestions 里的 hidden: true。cd 语料里就这两个 hidden 特殊参数(-/~),占满菜单后 Tab 会把 \u0015cd - 写进输入行,原生路径补全永远到不了 shell。现在 cd 解析为空 → 菜单不开 → Tab 原样到达 shell;目录生成器仍是 Fig custom 回调形态,collectGenerators 照旧不产槽位,不擅自执行回调。
  • P1 Tab 与候选选择:useCommandSuggestions.ts 新增显式选择锁(按候选 id)、菜单归属快照(行/会话)与接受门(输入模型可信、无命令运行或传输占用)。Tab 顺序固定为:明确选择 → 唯一候选 → 公共前缀 → 消费但不改输入(不再默认填第一项)。↑↓ 与模板 activate 统一走 activateCompletion(),默认高亮第 0 项不再等于选中;App.vue 只改 @activate 接线与解构。
  • P2 缓存命中后的编辑范围:generatorScheduler.run() 的缓存条目是「一次执行的全量产出」,edit 范围停在首次调用的 tokenEnd。命中后改为按本次 slot 的 tokenStart/tokenEnd 生成独立候选与 edit 副本,缓存对象不被改写。git checkout m→ma 接受 main 不再得到 maina;cursorOffset 语义未动。
  • P3 异步 source 不调度动态候选:CompletionController.dispatch() 的 thenable 分支此前直接 deliver 就返回。现在与同步路径共用新的 deliverOutcome();resolve 前先校验 requestId/revision/sessionId/enabled——过期请求既不进 UI 也不启动远端查询;reject 仍降级 pass-through 且不产生动态执行副作用。
  • P4 SSH 补全按当前目录执行:completion/ssh.rs + security.rs。非空 cwd 必须是绝对 POSIX 路径,在该次独立 exec 内 cd '<cwd>' 2>/dev/null && <原命令>(&& 短路、只抑制 cd 自身 stderr),不向交互式 PTY 注入 cd;~/相对路径/盘符报 completion: cwd must be an absolute POSIX path。docs/PROTOCOL.zh-CN.md 对应段落同步。
  • P6 删词语义与不可信保护:applyLineEditControlChar() 的 Ctrl+W 改为「删行尾空白 + 最后一个词、保留词前分隔空白」;wordEditingLineUntrusted() 扩到 Tab、Ctrl+W/A/E/R、CSI/SS3 方向键/Home/End/历史序列以及被拆包的 ESC 前缀。App.vue 复用既有接线,无 diff。
  • CHANGELOG:Unreleased 双语条目。

测试:新增 useCommandSuggestions.spec.ts——驱动真实 fig 语料与真实键盘处理器,断言的是最终写入终端的字节与行缓冲,而不是候选数组;另外给 fig 语料、scheduler、controller、行模型补齐回归用例。

验证

  • pnpm --dir frontend exec vitest run src/lib/completion src/components/CompletionMenu.spec.ts src/lib/terminalInputQueue.spec.ts src/lib/terminalLineModel.spec.ts src/lib/terminalWordEditing.spec.ts src/lib/ghostAcceptKey.spec.ts src/lib/suggestionGuard.spec.ts src/composables/useCommandSuggestions.spec.ts:301 passed
  • pnpm --dir frontend test:202 文件 / 2181 用例全过
  • pnpm --dir frontend typecheck:通过
  • python3 scripts/validate_repo.py、python3 scripts/check_vendor_lockstep.py:PASS
  • python3 scripts/smoke_terminal_word_editing.py(本机 Bash PTY 对照):10 项 PASS,含 5 个新增 Ctrl+W 场景(abc def→abc 、abc def →abc 、abc →空、纯空白→空、'a b'→'a )
  • 回归有效性:逐文件 stash 后重跑,T1 8 项 / T2 2 项 / T3 4 项失败;修复后全绿

风险与未验证

  • Rust 侧未经编译验证:本机没有 Rust 工具链,rustup 官方源与 rsproxy 镜像下载都失败,cargo fmt --check / clippy / test 均未执行,T4 的两个纯函数测试已写好但未编译。其 shell 语义另用本机 sh 手工对照过(cd '<path>' 2>/dev/null && cmd 成功时执行、目录不存在时短路且不吐错误文本)。合入前请务必让 CI 或本地补跑 backend 检查。
  • 真实 SSH 会话联调(协议层)与桌面实机组合验收未做——本机没有可用测试 SSH 环境。
  • 未跑 pnpm --dir frontend build:会生成 ui/,按仓库约定属集成者产物。
  • 多候选 + 异步 source 时,接受后到下一帧响应之间再按 Tab 会被归属快照挡成透传,属有意的保守行为。
  • 命令执行语义(cwd 拼装)按模板要求需人工 review;只影响 completion 通道,不改交互式会话与认证路径。

P1 cd 的 hidden 特殊参数不再进入候选;多候选 Tab 只推进公共前缀,
显式选择(方向键/指针激活)才接受,接受前核对行/会话/信任门。
P2 generator 缓存命中后按本次 tokenEnd 重建候选与 edit(m→ma 不再 maina)。
P3 thenable source 与同步路径共用后续流程,异步请求也调度动态槽位。
P4 ssh completion 在独立 exec 通道内按绝对 POSIX 路径切目录,失败短路。
P6 Ctrl+W 保留词前分隔空白(对齐 bash),Tab/编辑键让行模型进入不可信态。
@jinpy666
jinpy666 merged commit 6f3f777 into jinpy666:main Oct 27, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants