大模型手搓文件对比工具(6):差异不用再手选

上一期,我没有继续加功能,而是先给这个文件对比工具补了一份开发计划。

计划里的 P0 只有一件事:改掉内容对比编辑器里最难用的操作。

旧版虽然能标出左右文件的不同内容,也能向左、向右复制,但每次都要先手动选中文字。选少了,内容不完整;选多了,又可能把相邻代码一起覆盖。文件里只有一两处差异时还能忍,差异多了以后,操作比直接打开两个编辑器修改还麻烦。

所以第 6 期正式开始做 P0:让程序识别每一处连续差异,并在旁边直接放 ,点击一次只处理当前这一块。

旧版的问题不在按钮,而在差异判断

旧版编辑器中间有固定的左右复制按钮。它做的事情很简单:读取当前选中的文字,再替换另一侧光标位置的内容。

这种实现写起来不难,但把最麻烦的判断留给了使用者:

  1. 哪几行属于同一处差异?
  2. 应该从哪一行选到哪一行?
  3. 目标一侧是替换、插入还是删除?
  4. 复制完成后,其他行有没有跟着错位?

更麻烦的是,旧版主要按相同行号比较。假设右侧文件中间多了一行,后面的内容即使完全相同,也可能因为行号错位而连续标红。

因此这次不能只把两个按钮挪到差异旁边。程序得先有真正的"差异块",知道左右各自对应哪些行。

先把三种操作说清楚

正式改代码前,我先确认了三种差异的方向含义。

差异情况 点击 点击
左右都有内容,但内容不同 用左侧替换右侧 用右侧替换左侧
只有左侧有内容 把左侧内容插入右侧 删除左侧内容
只有右侧有内容 删除右侧内容 把右侧内容插入左侧

这里最容易误操作的是删除。

比如某几行只存在于左侧,点击 的结果不是"从右侧复制",而是让左侧与右侧保持一致,也就是删除左侧这几行。如果所有按钮都是同样的蓝色,使用时很容易顺手点错。

最终方案把会产生删除的箭头改成红色。普通替换和插入继续使用蓝色,方向不变,但风险一眼就能看出来。

第一版 UI 方案

编辑器改成左、中、右三栏:

  • 左边显示左侧文件。
  • 右边显示右侧文件。
  • 中间是固定的差异操作轨道。
  • 每个连续差异块只显示一组
  • 一侧缺少对应内容时,显示"此处无对应内容"的占位行。
  • 整个文件覆盖操作移到底部,不再和单个差异块混在一起。

当时先做出的效果图如下:

这张图确认了主要布局,但删除操作还有一个实际问题:如果每次点红色箭头都弹窗确认,连续处理十几个差异块会很烦;如果完全不提示,又容易误删。

最后采用了一个折中方案:

  • 删除差异块时默认弹出确认。
  • 确认框里可以勾选"下次不再提示删除确认"。
  • 这个选择只在本次程序运行期间有效。
  • 随时可以从"编辑"菜单重新勾选"删除差异块前确认"。
  • 程序重启后自动恢复默认确认。

这样第一次操作有保护,确认自己理解方向后,也不必反复点击同样的弹窗。

差异算法没有自己硬写

差异块是这次功能的基础。它至少要正确处理:

  • 普通行修改。
  • 左侧新增。
  • 右侧新增。
  • 文件开头和结尾的差异。
  • 空文件。
  • 连续多行修改。
  • 重复行附近的插入和删除。

这些情况看起来只是比较字符串,但真正麻烦的是行如何对齐。自己写一个"从上往下找不同"的算法,很容易在重复行和中间插入时选错位置。

这次没有继续坚持纯 JDK,而是使用了 java-diff-utils 4.12。它提供了成熟的 Myers 行差异算法,支持 Java 8,许可证是 Apache License 2.0,也没有额外运行时依赖。项目里直接带上 JAR,启动脚本把 lib 加入类路径即可。

这类基础算法没有必要为了"自己写"再造一遍。工具真正需要自己控制的是差异块怎样展示、点击箭头后怎样修改文件,以及怎样防止误操作。

代码先拆模型,再接 Swing

原来的内容编辑器代码都写在主窗口类里。如果继续往里面增加差异算法、对齐行、箭头、撤销和删除确认,文件只会越来越难改。

这次先把核心逻辑拆成几块:

  • DiffEngine:计算左右文件的差异。
  • DiffHunk:记录一个连续差异块的类型、位置和内容。
  • LineDocument:保留文本行、LF/CRLF 和末尾换行状态。
  • HunkApplyService:把指定差异块应用到左边或右边。
  • DiffAlignmentService:生成左右等高的显示行和占位行。
  • DiffEditorFrame:负责三栏界面、按钮、编辑、保存和撤销。

先有模型的好处很直接:差异算法和文件修改可以脱离界面测试。即使 Swing 窗口还没写完,也能先确认点击某个方向后,最终文本到底对不对。

为什么最终用了对齐表格

方案阶段考虑过使用两个 JTextPane,在文本中插入不可保存的占位行,再把中间按钮定位到对应段落。

实际往下推时,这种方式有两个麻烦:

  1. 占位内容混在编辑文档里,保存前必须保证它一定被剔除。
  2. 用户手动增加或删除一行后,按钮位置和占位位置都要重新映射。

最终编辑区改成左右两个对齐表格。每一行背后都保存真实文件行号;没有内容的一侧使用只读占位单元格。中间轨道和左右表格使用同样的固定行高,因此插入、删除后仍然能保持对齐。

真实行可以双击编辑,也可以通过右键菜单插入或删除一行。占位行只负责显示,不会写入文件。

这不是为了把界面做成表格,而是为了明确区分"真实文件内容"和"为了对齐而显示的内容"。

点击一个箭头后发生了什么

为例,实际流程是:

  1. 读取当前差异块在左右文件中的行范围。
  2. 判断操作会产生替换、插入还是删除。
  3. 如果会删除右侧内容,根据当前设置决定是否确认。
  4. 把右侧修改前的文本放入撤销记录。
  5. 只替换当前差异块对应的右侧行。
  6. 重新计算全部差异块和对齐行。
  7. 保持滚动位置,刷新中间箭头。
  8. 标记右侧文件"已修改",但暂时不写磁盘。

一次差异块操作只产生一条撤销记录,按 Ctrl+Z 可以整体撤回。只有点击保存后,修改才真正写入文件。

手动编辑后不会立即在界面线程里反复计算,而是等待约 300 毫秒,再在后台重新计算差异。这样连续输入时不会每敲一个字都重排整个编辑器。

真实截图还是发现了问题

功能编译通过、测试通过后,我用三类差异生成了一张真实 Swing 窗口截图。

第一张截图里,代码正常显示,但"此处无对应内容"变成了几个方框。原因是占位行错误地沿用了 Consolas,当前系统没有正确回退到中文字库。

最后把代码行继续保留为等宽字体,占位提示单独改成微软雅黑,重新截图后才正常。

代码复查时还发现另一个边界:左右都有内容但行数不同的 CHANGE 差异,也可能产生删除。最初的红色判断只覆盖"仅左侧"和"仅右侧",会漏掉这种不等长替换。后来把判断改成比较左右差异块行数,并补了一条回归测试。

这两个问题都不是编译器能发现的。一个只能从真实界面看出来,另一个需要把交互语义重新代入边界场景。

最终运行效果

最终编辑器如下:

现在三种差异都能直接处理:

  • 内容不同:点击方向按钮替换当前块。
  • 左侧独有:向右插入,或者从左侧删除。
  • 右侧独有:向左插入,或者从右侧删除。

两侧纵向滚动仍然联动,长代码可以各自横向滚动。整文件复制、分别保存、全部保存和重新加载也都保留了。

相比旧版,最大的变化不是少选了几次文字,而是不用再自己判断目标位置。程序已经把属于同一处的差异放在了一行操作轨道上。

这次实际修改了哪些内容

1. 差异模型

  • 引入 Myers 行差异算法。
  • 支持修改、左侧独有、右侧独有三类差异。
  • 记录左右起止行和差异内容。
  • 保留 LF、CRLF 和文件末尾换行状态。

2. 编辑器界面

  • 改成左右文件加中间操作轨道的三栏结构。
  • 差异行使用红色背景,相同行使用浅绿色背景。
  • 缺失位置显示不可编辑的占位行。
  • 每个连续差异块显示一组
  • 双击真实行可以编辑,右键可以插入或删除行。

3. 操作安全

  • 删除型箭头标红。
  • 删除前默认确认。
  • 支持本次运行期间不再提示。
  • 支持从"编辑"菜单恢复删除确认。
  • 每次差异块操作可以通过 Ctrl+Z 撤销。
  • 未保存关闭和重新加载仍然需要确认。

4. 构建与依赖

  • 增加 java-diff-utils 4.12
  • 更新 run.bat 的编译和运行类路径。
  • 增加第三方依赖说明。
  • 保持 Java 8 兼容。

5. 测试

  • 修改、插入、删除差异识别。
  • 重复行和空文件。
  • 差异块左右应用结果。
  • 占位行对齐。
  • LF、CRLF 和末尾换行。
  • 不等长修改块的删除提示。
  • 原有过滤规则回归。
  • 真实 Swing 窗口截图检查。

最终版提示词

如果要在一个现有 Java Swing 文件对比工具中实现同类功能,可以使用下面这份提示词:

text 复制代码
请继续开发一个现有的 Java 8 Swing 文件对比工具。工具已经支持文件和目录
SHA-256 对比、可折叠目录树、过滤规则、左右同步、F5 刷新、联动滚动和
双击打开内容编辑器。

本次实现 P0"差异块级双向操作",替换当前依赖手动选中文字再复制的方式。

功能要求:

1. 使用可靠的行差异算法识别连续差异块,覆盖 CHANGE、仅左侧、仅右侧。
2. 每个差异块记录左右起止行和内容,不能继续按相同行号简单比较。
3. 编辑器改成左侧文件、中间差异操作轨道、右侧文件三栏结构。
4. 左右显示行必须对齐;一侧缺失内容时显示只读占位行,占位文字绝不能保存。
5. 每个连续差异块在中间显示一组 ←、→,不需要先选择文字。
6. → 表示让右侧当前差异块与左侧一致;← 表示让左侧与右侧一致。
7. 修改、插入、删除三种语义必须正确。任何会减少目标侧行数的操作都属于删除。
8. 删除型箭头使用红色,普通替换和插入使用蓝色,并提供清晰的悬浮说明。
9. 删除前默认确认,确认框增加"下次不再提示删除确认"。
10. 关闭提示只在当前程序会话生效;编辑菜单提供可勾选的
    "删除差异块前确认",程序重启后恢复默认确认。
11. 每次差异块操作只修改当前块,不能影响其他差异;操作后重新计算差异。
12. 每次差异块操作作为一个撤销单元,支持 Ctrl+Z。
13. 保留双击编辑、手动插入删除行、未保存状态、分别保存、全部保存、
    重新加载和整文件双向复制。
14. 左右纵向滚动保持联动,横向滚动互不影响。
15. P0 继续使用 UTF-8,但必须保留 LF/CRLF 和文件末尾换行状态。
16. 差异计算放到后台执行,手动编辑使用短延迟合并刷新,避免阻塞 Swing EDT。
17. 优先把差异算法、差异块应用和行对齐拆成独立模型,不要继续堆在主窗口类中。
18. 引入第三方差异库前检查 Java 8 兼容性、许可证和运行时依赖,并更新启动脚本。
19. 增加回归测试,至少覆盖修改、插入、删除、重复行、空文件、文件首尾、
    不等长差异、左右应用、占位对齐、LF/CRLF 和末尾换行。
20. 完成后编译全部源码和测试,运行真实 Swing 界面并生成截图检查字体、对齐、
    箭头颜色、最小窗口和按钮文字。

修改前先阅读现有代码和开发计划,给出执行计划、交互方案和 UI 效果图。
我确认后再修改正式代码。实现完成后更新 README、开发计划状态和第三方依赖说明。

下一步

P0 已经完成,开发计划中的下一项是 P1:编码与换行保真。

当前编辑器仍然按 UTF-8 读取和保存文件。遇到 GBK、GB2312 或 UTF-16 文件时,打开后可能乱码,直接保存还可能破坏原文件。

下一步需要先做编码识别,再记录每一侧文件的原始编码、BOM 和换行方式,保存时尽量保持不变。同时还要处理识别不确定时的提示,不能只凭一次猜测直接覆盖。

之后再做同步预览和备份。目录同步涉及覆盖文件,真正用于项目之前,最好先明确展示将新增和覆盖哪些内容,并保留失败恢复能力。

最后

这一期终于把开发计划里的第一个核心问题解决了。

以前看到一处差异,要先判断范围、选中文字、找到目标位置,再点复制;现在程序先把差异对齐,操作的人只需要决定方向。

这也是工具从"功能能跑"到"操作顺手"的区别。很多时候并不是少一个按钮,而是程序有没有替使用者完成本来就能自动完成的判断。

不过现在的使用体验仍然谈不上成熟。编辑器目前是行级对比,还没有字符级高亮;常见中文编码没有识别;大文件差异计算、快捷键和差异导航也还有优化空间。

后面会继续按计划往下做,不再临时想到什么就加什么。下一期先处理编码问题,继续记录方案、提示词、实现过程和实际效果。

相关推荐
极连AI3 小时前
极连AI平台解读、Codex5.6仅需0.01倍率,无需Token焦虑,极速响应
人工智能·gpt·chatgpt·aigc·ai编程·ai写作·gpu算力
leeyi3 小时前
Embedder 接口 + 缓存层源码:一个把 key 撑大 3 倍的实现(第71篇-E57)
aigc·agent·ai编程
小虎AI生活4 小时前
WorkBuddy 加 Remotion,批量视频成本趋近于零
ai编程
极客密码5 小时前
DeepSeek V4-Flash 正式版发布:Agent 基准、价格与 Codex 接入保姆级教程
agent·ai编程·deepseek
音符犹如代码5 小时前
DeepSeek V4 Flash正式版发布
ai·ai编程·deep learning
TrisighT7 小时前
一张路由表少写一行,装好的 tri-loop 就成了死 skill:我把 tri-intent 的 27 个落点扒了一遍
aigc·agent·ai编程
不吃辣4907 小时前
vibe coding | 如何做一个 AI 音乐生成工具?
java·人工智能·后端·ai·ai编程
AI大模型-小华8 小时前
ChatGPT 服务充值与账户管理实操指南
人工智能·chatgpt·ai编程·codex·chatgpt plus·chatgpt pro
小沈同学呀9 小时前
【Agent开发第三期】短期记忆history,让模型“记住“上一句
人工智能·ai编程·agent开发·agent记忆