scrollablediff通过三步实现精准差异显示:先set_content加载原始行,再set_width触发换行包装并生成wrapped_src_idx映射,最后按scroll位置截取视口行并回查原始行号生成@@标记;需注意width一致、启用renderinlinediffs及及时调用state.clamp()避免错位。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

想快速看清Codex新旧版本代码改动在哪、改了多少、为什么这样改,必须理解它底层的Diff差异显示逻辑——不是简单高亮两行文字,而是基于ScrollableDiff组件对原始行、包装行、滚动状态三重映射后的可视化呈现。
ScrollableDiff如何决定哪一行该显示
第一步:调用set_content传入原始行数组,此时raw被替换,wrapped和wrapped_src_idx被清空,wrap_cols置为None;
第二步:调用set_width指定换行列宽,触发重新包装——每行按width切分,长行被拆成多行,短行保持原样,同时生成wrapped_src_idx映射表,记录每条包装行对应哪条原始行;
第三步:渲染时,ScrollableDiff根据state.scroll位置,从wrapped中截取视口高度内的行序列,再通过wrapped_src_idx回查原始行号,用于生成@@行号标记和-/+符号定位;
【若跳过set_width直接渲染,wrapped为空,将无法显示任何内容】
差异块中的行号标记怎么算出来的
方法一:看@@ -497,8 +497,8 @@这类标记
“-497,8”表示从旧文件第497行起取8行作为上下文,“+497,8”表示从新文件第497行起取8行;但这个497不是原始行号,而是wrapped_src_idx映射后、经clamp约束过的有效起始位置;
方法二:当某原始行被拆成3条包装行,它在diff中会占据3行视觉空间,但@@标记仍只计为1行上下文——因为wrapped_src_idx把这3条都指向同一个原始索引,Diff引擎据此合并行号逻辑;
通过本地 Codex 或 OpenClaw OAuth 凭证直接调用 ChatGPT/Codex Responses 的 image_generation 工具来生成或编辑光栅图像,然后保存
如果content_h远大于viewport_h,而scroll又未被clamp校正,会出现@@标记行号错乱甚至负值,此时必须手动调用state.clamp()。
为什么有些修改没标黄,却实际变了
第一步:检查diffEditor.ignoreTrimWhitespace是否为true(默认开启)
第二步:确认set_width传入的width是否一致——若新旧版本使用不同换行宽度,相同原始行可能被拆成不同数量的包装行,导致wrapped_src_idx错位,Diff引擎误判为“整块删除+新增”,而非“局部修改”;
第三步:打开diffEditor.renderInlineDiffs,启用内联字符级比对;否则仅靠行级包装映射,无法识别同一包装行内字符串的子串变更。
滚动时差异高亮突然消失或错位
ScrollableDiff内部依赖ScrollViewState维持三个关键值:scroll(当前滚动行偏移)、viewport_h(可视区行数)、content_h(总包装行数);
每次set_width后,content_h会重算,但scroll不会自动重置;若新content_h小于旧值,原有scroll可能超出范围,造成部分差异块无法进入视口;
【必须在set_width之后立即调用state.clamp(),否则滚动位置悬空】
这一步操作起来很简单,直接在set_width调用后追加self.state.clamp()即可。










