Skip to content

第 12 章:Task、Terminal 与 Debugger——进程能力如何被产品化

Task 把“上下文相关的命令模板”解析成 SpawnInTerminal,Terminal 把 PTY/子进程投影成可交互 Emulator,Debugger 再把 build、launch/attach、DAP session 和编辑器位置串起来。

1. Task 不是 shell command 字符串

Task 分三层:

text
TaskTemplate
  + TaskContext / TaskVariables
  → ResolvedTask
  → SpawnInTerminal

ResolvedTask 保存原模板、稳定 TaskId、已替换变量、最终 label 和 spawn 描述。 源码:crates/task/src/task.rs:112-147

2. TaskContext 从哪里来

Editor/Project 收集:

  • 当前文件、相对路径、目录、文件名;
  • cursor row/column、selection;
  • Language;
  • Tree-sitter runnable symbol;
  • worktree root;
  • Git repo/ref/SHA;
  • extension 自定义变量;
  • project environment。

变量枚举见 crates/task/src/task.rs:149-201

因此同一个 cargo test $ZED_SYMBOL 模板在不同 cursor 下生成不同 ResolvedTask/TaskId。

3. Task source 是可组合的

TaskStore 聚合:

  • 用户 tasks.json
  • project .zed/tasks.json
  • language runnable query;
  • extension task provider;
  • VS Code task/debug config;
  • Debugger build task。

Source 提供模板和优先级;解析阶段统一替换变量和应用 settings。

4. SpawnInTerminal 是执行边界

它包含 command、args、cwd、env、shell、是否复用 tab、是否允许并发、show/hide/reveal、保存策略。 源码:crates/task/src/task.rs:40-80

Task 逻辑到此停止,不直接依赖 Alacritty 或 Pane。Workspace 的 TerminalProvider 决定在本地、远端、 center item 还是 bottom panel 创建终端。

5. Terminal 的三层结构

text
PTY / child process
  ↕ bytes
alacritty_terminal state machine
  ↕ cells / modes / cursor / selection
terminal::Terminal Entity
  ↕ Event / RenderableCells
terminal_view::TerminalView / Panel / Element

底层 terminal crate 不负责产品布局;terminal_view 才接 Workspace、keymap、tabs 和 GPUI 绘制。

6. PTY 为什么重要

交互程序需要:

  • controlling terminal;
  • window size change;
  • foreground process group;
  • ANSI mode;
  • Ctrl-C/信号;
  • shell prompt 和 job control。

直接 Command::output 无法提供这些语义。Zed 用 PTY 运行普通终端,并把字节送入 Alacritty emulator。

7. Headless fallback

Eval CLI 等无 controlling TTY 环境无法分配 PTY。HeadlessTerminal Global 开启后,Terminal 用 管道启动普通 subprocess,并把 stdout/stderr 泵进 emulator。

源码:crates/terminal/src/terminal.rs:105-117:3057-3075

管道不会自动把 LF 变 CRLF,Zed 还显式插入 \r,否则 emulator cursor 下移后不回到列 0。

8. Terminal 关闭是进程树问题

只关闭 PTY master 通常发 SIGHUP,但 foreground job 可能忽略。Zed 先对 shell/foreground process group 发温和信号,等待 grace period,再 SIGKILL 存活进程;时间必须短于 GPUI 全局 shutdown timeout。

源码说明:crates/terminal/src/terminal.rs:78-91

这说明 UI tab 生命周期必须显式映射到 OS process 生命周期。

9. DAP 分层

对象
配置DebugScenario、launch/attach、build task
Adapterbinary、command、locator、capability
协议dap::Client、transport、request/event
ProjectDapStore、breakpoint store、session
UIDebugPanel、Session view、stack/variables/console

dap_adapters 提供内置 CodeLLDB/GDB/Go/JS/Python 适配;扩展也可通过 Extension API 提供 adapter 和 locator。

10. 启动调试的编排

text
用户选择 DebugScenario
  → 解析变量 / 选择 process(attach)
  → 可选 build Task
  → locator 根据 build output 找 artifact
  → adapter 获取/下载 debug adapter binary
  → spawn DAP client
  → initialize
  → launch 或 attach
  → setBreakpoints / configurationDone
  → Session 事件驱动 UI 与 Editor

调试不是“启动一个进程”而是 Task 和协议状态机的组合。

11. Breakpoint 为什么放 Project

Breakpoint 关联 ProjectPath/Anchor,需要:

  • 文件 rename 后仍可追踪;
  • Session 启动时发给 adapter;
  • 多个 Debug Session 共享用户定义;
  • remote Project 发送到 host;
  • Editor gutter 观察更新。

因此 BreakpointStore 属于 Project,而不是某个 Editor 或 DebugPanel。

12. StackFrame 如何定位回 Editor

DAP Source/line/column 使用 adapter 路径与 1-based position。Project/DAP layer:

  1. 根据 remote/local path mapping 找 ProjectPath;
  2. 打开 Buffer;
  3. 把 line/column 转 Point/Anchor;
  4. Workspace 打开或激活 Editor;
  5. DisplayMap 投影 stack frame line highlight;
  6. inline values 在当前 frame/context 请求后成为 inlay/block。

13. Local/Remote 再次同构

Remote Project 上 Task/Terminal/DAP 必须在远端执行,但本地仍显示 TerminalView 和 DebugPanel。 执行描述、事件和状态经过 proto;Workspace 的 Item/Panel 不需要另一套远程 UI。

14. 失败路径

  • task variable 缺失:模板不可 resolve,向 picker 解释原因;
  • command 启动失败:Terminal 保留错误输出与 TaskStatus;
  • build task 失败:不进入 launch;
  • adapter 不支持请求:capability gate;
  • remote disconnect:Session 转 stopped/disconnected;
  • source path 无法映射:仍可在 loaded source/反汇编视图显示;
  • tab 关闭但进程存活:执行信号升级和清理。

15. 架构经验

  1. 模板、解析结果、执行描述分层;
  2. 进程后端和终端 UI 分 crate;
  3. Debugger 复用 Task 作为 build 阶段,不再造命令系统;
  4. Breakpoint/Session 属于 Project,Panel 只是投影;
  5. headless/remote 通过替换执行后端复用 emulator 和上层状态。

独立源码学习笔记 · 文档采用 CC BY-SA 4.0