# Agent Note: TUI diff 上下文行保持中性

Status: implemented
Archived: 2026-08-04

[English](2026-07-31-tui-diff-context-line-accounting.md) | 中文

## 问题

文件系统 diff 返回结果时，每个 `FileDiff.oldText` 和 `FileDiff.newText` 都会包含已应用的变更及其前后各 3 行上下文。TUI 将旧侧的每一行都渲染为删除行，将新侧的每一行都渲染为新增行，其中包括两侧相同的上下文。因此，一行编辑会显示为删除 7 行并新增 7 行，页脚还会重复这些虚高的合计值。

## 决策

TUI 会比较每个变更前后文本均可用的 `FileDiff`。新增行和删除行仍分别使用绿色 `+` 和红色 `-` 标记；相同的上下文行则使用弱化的正文色调，并带有由两个空格构成的中性前缀。页脚只汇总归类为新增或删除的行。`maxDiffEditLength` 以新增行与删除行的合计数为精确比较设置上限，默认值为 1000。超过上限时，TUI 会把完整旧侧渲染为删除内容、把完整新侧渲染为新增内容，将页脚标记为近似结果，并缓存该结果，避免后续重绘重复比较。工具结果会在派生已结算视图前清除待处理视图缓存，即使 presenter 修改并复用同一个视图对象也一样。

当 `oldText` 为 `null` 时，渲染器无法区分文件创建、待处理覆写，以及旧文本不可用的参数回退。因此，它会把新侧的每个非空行显示并计作新增行，但不会声称这些行原先不存在于已有文件中。新内容为空时，不会渲染虚构的新增行。

该行为仍然只是消费方对现有 `FileDiff` 契约的解释。文件系统工具仍会持久化带上下文的变更前后片段，因此其他消费方仍能获得定位上下文，已有会话日志在回放时也会采用修正后的 TUI 呈现。TUI 与 `dsh-tool-fs` 共用同一个受维护的 `diff` 包（package），无需引入第二套逐行 diff 实现。

## 考虑过的替代方案

**从文件系统结果元数据中移除上下文。** 不予采纳：带上下文的已应用 hunk 是有意保留的生产方输出，供具备相应能力的编辑器使用；更改这些内容会让所有消费方丢失信息，同时旧会话日志在 TUI 中仍会产生误导。

**为 `FileDiff` 扩展持久化的逐行标签。** 不予采纳：这些标签可以根据现有的变更前后文本对确定性派生；仅为一个渲染器持久化标签，会扩大跨包契约和会话日志契约。

**不使用 diff 算法，按位置匹配相同行。** 不予采纳：插入和删除会使后续上下文发生位移，因此按位置配对会把有效 hunk 错误分类。

**让所有比较都运行至完成。** 不予采纳：待处理工具视图可能包含由模型生成且长度不受限制的新旧字符串，无界的 Myers 比较可能阻塞同步终端渲染器。

## 后果

TUI diff 卡片会区分用于佐证的上下文与变更本身，精确的 `+A -R` 页脚会报告实际的行变更量。回放已有的上下文 diff 无需迁移即可获得修正后的渲染。结果时刻的文件系统 hunk 受上下文范围限制；不受限制的待处理视图要么在配置的编辑长度预算内完成比较，要么降级为明确标注为近似结果的线性渲染。

聚焦的 TUI 测试覆盖中性上下文、精确合计值、空文件创建、有界回退、结果到达时的缓存失效和重绘缓存复用。组装后的 `advanced-cards` 终端快照在卡片折叠和展开状态下固定了中性上下文样式、变更行的语义色彩、精确结果页脚和近似回退。
