红金纱丽肖像
2026-08-03 3438118
2026-08-03 0

上一期,我没有继续加功能,而是先给这个文件对比工具补了一份开发计划。
计划里的 P0 只有一件事:改掉内容对比编辑器里最难用的操作。
旧版虽然能标出左右文件的不同内容,也能向左、向右复制,但每次都要先手动选中文字。选少了,内容不完整;选多了,又可能把相邻代码一起覆盖。文件里只有一两处差异时还能忍,差异多了以后,操作比直接打开两个编辑器修改还麻烦。
所以第 6 期正式开始做 P0:让程序识别每一处连续差异,并在旁边直接放 ←、→,点击一次只处理当前这一块。
旧版编辑器中间有固定的左右复制按钮。它做的事情很简单:读取当前选中的文字,再替换另一侧光标位置的内容。
这种实现写起来不难,但把最麻烦的判断留给了使用者:
更麻烦的是,旧版主要按相同行号比较。假设右侧文件中间多了一行,后面的内容即使完全相同,也可能因为行号错位而连续标红。
因此这次不能只把两个按钮挪到差异旁边。程序得先有真正的“差异块”,知道左右各自对应哪些行。
正式改代码前,我先确认了三种差异的方向含义。
| 差异情况 | 点击 → | 点击 ← |
|---|---|---|
| 左右都有内容,但内容不同 | 用左侧替换右侧 | 用右侧替换左侧 |
| 只有左侧有内容 | 把左侧内容插入右侧 | 删除左侧内容 |
| 只有右侧有内容 | 删除右侧内容 | 把右侧内容插入左侧 |
这里最容易误操作的是删除。
比如某几行只存在于左侧,点击 ← 的结果不是“从右侧复制”,而是让左侧与右侧保持一致,也就是删除左侧这几行。如果所有按钮都是同样的蓝色,使用时很容易顺手点错。
最终方案把会产生删除的箭头改成红色。普通替换和插入继续使用蓝色,方向不变,但风险一眼就能看出来。
编辑器改成左、中、右三栏:
←、→。当时先做出的效果图如下:

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

这样第一次操作有保护,确认自己理解方向后,也不必反复点击同样的弹窗。
差异块是这次功能的基础。它至少要正确处理:
这些情况看起来只是比较字符串,但真正麻烦的是行如何对齐。自己写一个“从上往下找不同”的算法,很容易在重复行和中间插入时选错位置。
这次没有继续坚持纯 JDK,而是使用了 java-diff-utils 4.12。它提供了成熟的 Myers 行差异算法,支持 Java 8,许可证是 Apache License 2.0,也没有额外运行时依赖。项目里直接带上 JAR,启动脚本把 lib 加入类路径即可。
这类基础算法没有必要为了“自己写”再造一遍。工具真正需要自己控制的是差异块怎样展示、点击箭头后怎样修改文件,以及怎样防止误操作。
原来的内容编辑器代码都写在主窗口类里。如果继续往里面增加差异算法、对齐行、箭头、撤销和删除确认,文件只会越来越难改。
这次先把核心逻辑拆成几块:
DiffEngine:计算左右文件的差异。DiffHunk:记录一个连续差异块的类型、位置和内容。LineDocument:保留文本行、LF/CRLF 和末尾换行状态。HunkApplyService:把指定差异块应用到左边或右边。DiffAlignmentService:生成左右等高的显示行和占位行。DiffEditorFrame:负责三栏界面、按钮、编辑、保存和撤销。先有模型的好处很直接:差异算法和文件修改可以脱离界面测试。即使 Swing 窗口还没写完,也能先确认点击某个方向后,最终文本到底对不对。
方案阶段考虑过使用两个 JTextPane,在文本中插入不可保存的占位行,再把中间按钮定位到对应段落。
实际往下推时,这种方式有两个麻烦:
最终编辑区改成左右两个对齐表格。每一行背后都保存真实文件行号;没有内容的一侧使用只读占位单元格。中间轨道和左右表格使用同样的固定行高,因此插入、删除后仍然能保持对齐。
真实行可以双击编辑,也可以通过右键菜单插入或删除一行。占位行只负责显示,不会写入文件。
这不是为了把界面做成表格,而是为了明确区分“真实文件内容”和“为了对齐而显示的内容”。
以 → 为例,实际流程是:
一次差异块操作只产生一条撤销记录,按 Ctrl+Z 可以整体撤回。只有点击保存后,修改才真正写入文件。
手动编辑后不会立即在界面线程里反复计算,而是等待约 300 毫秒,再在后台重新计算差异。这样连续输入时不会每敲一个字都重排整个编辑器。
功能编译通过、测试通过后,我用三类差异生成了一张真实 Swing 窗口截图。
第一张截图里,代码正常显示,但“此处无对应内容”变成了几个方框。原因是占位行错误地沿用了 Consolas,当前系统没有正确回退到中文字库。
最后把代码行继续保留为等宽字体,占位提示单独改成微软雅黑,重新截图后才正常。
代码复查时还发现另一个边界:左右都有内容但行数不同的 CHANGE 差异,也可能产生删除。最初的红色判断只覆盖“仅左侧”和“仅右侧”,会漏掉这种不等长替换。后来把判断改成比较左右差异块行数,并补了一条回归测试。
这两个问题都不是编译器能发现的。一个只能从真实界面看出来,另一个需要把交互语义重新代入边界场景。
最终编辑器如下:

现在三种差异都能直接处理:
两侧纵向滚动仍然联动,长代码可以各自横向滚动。整文件复制、分别保存、全部保存和重新加载也都保留了。
相比旧版,最大的变化不是少选了几次文字,而是不用再自己判断目标位置。程序已经把属于同一处的差异放在了一行操作轨道上。
←、→。Ctrl+Z 撤销。java-diff-utils 4.12。run.bat 的编译和运行类路径。如果要在一个现有 Java Swing 文件对比工具中实现同类功能,可以使用下面这份提示词:
复制代码请继续开发一个现有的 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 和换行方式,保存时尽量保持不变。同时还要处理识别不确定时的提示,不能只凭一次猜测直接覆盖。
之后再做同步预览和备份。目录同步涉及覆盖文件,真正用于项目之前,最好先明确展示将新增和覆盖哪些内容,并保留失败恢复能力。
这一期终于把开发计划里的第一个核心问题解决了。
以前看到一处差异,要先判断范围、选中文字、找到目标位置,再点复制;现在程序先把差异对齐,操作的人只需要决定方向。
这也是工具从“功能能跑”到“操作顺手”的区别。很多时候并不是少一个按钮,而是程序有没有替使用者完成本来就能自动完成的判断。
不过现在的使用体验仍然谈不上成熟。编辑器目前是行级对比,还没有字符级高亮;常见中文编码没有识别;大文件差异计算、快捷键和差异导航也还有优化空间。
后面会继续按计划往下做,不再临时想到什么就加什么。下一期先处理编码问题,继续记录方案、提示词、实现过程和实际效果。