功能越多,越要小心那一次保存和替换

功能越多,越要小心那一次保存和替换

接入窗口之后,砚陆续有了行编辑、查找替换、跨文件搜索和自动保存。看上去,编辑器已经越来越完整。

但第一次预览发布前,最重要的一轮工作却是把几个数据安全问题找出来:自动保存可能盖掉外部修改,旧搜索结果可能跨工作区返回,单条替换可能操作过期的位置。

这些问题很适合写下来。它们很少出现在演示的最顺路径上,后果却比颜色画错严重得多。

从编辑命令到查找替换

基础编辑逐步加入了缩进、行注释、移动与复制行、智能回车、括号引号自动闭合、排序行、驼峰转换等。它们走共享内核,多光标也要一起工作。

文件内查找支持大小写、全词、正则和替换分组。跨文件搜索支持包含和排除规则,已经打开且未保存的文件也要以缓冲区内容为准,否则用户刚输入的内容会像"搜不到"一样消失。

这些功能共同遇到一个要求:展示给用户的结果,与真正修改时的内容,必须仍然对应。

自动保存先问一句:磁盘还是刚才的版本吗

想象这个过程:

text 复制代码
砚打开文件 → 我在砚里修改
         → 外部程序也修改同一个文件
         → 砚失去焦点,触发自动保存

如果最后一步只管把缓冲区写出去,外部修改就没了。即使底层用了临时文件和 rename,这次覆盖也会执行得很完整。

现在 Buffer 记录读取或保存时的磁盘标记,用修改时间和大小比对。发现变化时,普通保存与自动保存拒绝覆盖,并让用户选择"覆盖磁盘上的版本"或"还原为磁盘上的版本"。还原也可以撤销。

这不是文件锁或完整的事务系统;当前标记也不是内容哈希。因此文章里更准确的说法是:已经增加外部变化检查和明确冲突处理,不能承诺任何并发写入条件下都绝无覆盖风险。

保存触发点也不能漏。空闲定时器、失焦、切标签、关闭、切文件夹、保存全部、Git 操作前保存,都要沿着同一套保护走。

取消了任务,不等于它一定不会回来

另一个场景是先在项目 A 搜索,马上切到项目 B。A 的后台任务可能已经把结果排进队列,取消信号也撤不回已经发生的事。

砚给工作区加了递增的 epoch。回调带着发起时的身份,执行前确认自己仍属于当前工作区。搜索还要检查查询代数,切目录时旧定时器与任务也被停止。

text 复制代码
任务返回
  → 工作区仍一致?
  → 查询仍是这一轮?
  → 才更新结果

Git、blame、HEAD 内容读取等后台结果,也需要类似边界。单一所有者保证不会多线程乱写,epoch 则解决"串行地写入了过时内容"。这是两个不同问题。

单条替换尤其容易放松警惕

搜索时某处是 cat,用户后来把它改成同样长度的 dog。旧结果中的偏移仍然合法,如果只检查范围没越界,点击替换就可能把 dog 改掉。

所以替换前还要核对查询与选项、来源版本、原始匹配文字,以及路径是否仍在工作区范围内。内容变了就刷新搜索,不沿着旧坐标继续写。

这里"同样长度"很适合作为负例:它能绕过很多只验证边界的测试。

用反例证明保护确实生效

发布报告记录了针对这些问题的回归测试。例如先扣住 A 的实际搜索结果,切到 B 并开始 B 的搜索,再投递 A 的结果,检查 A 没被改、B 的替换仍然正确。

一些测试还通过临时移除保护验证会失败。这样能确认测试确实命中了那条危险路径,而不是碰巧跑过一个不相关的成功场景。

这轮之后,我对"功能完成"的理解具体了一些。查找替换不只是替换成功,还包括结果过期时拒绝修改;自动保存不只是按时写入,还包括冲突时把失败告诉用户。

这类工作不会让截图多几个按钮,却决定了工具能不能被用来打开真正重要的文件。


相关推荐
我的div丢了肿么办6 小时前
go语言通过反射来操作结构体中的数据
后端·go
人人都是软件工程师6 小时前
16-并发编程(下):channel、select 与同步原语
go
猪猪拆迁队1 天前
跨电脑复制共享-局域网直连踩坑
前端·后端·go
tachibana21 天前
Go 语言的 Map 是否是并发安全的?
开发语言·数据结构·后端·golang·go·语言
tachibana21 天前
Go 语言 Map 的扩容过程是怎样的?
开发语言·后端·golang·go·语言
共饮一杯无1 天前
LiveKit本地录制落盘路径排查与修复实践
java·go·livekit
探索云原生1 天前
KServe 工作流程:从 InferenceService 到 vLLM 服务
docker·ai·云原生·kubernetes·go
人人都是软件工程师1 天前
13-错误处理:把失败变成可读、可判断的结果
go
林壮Allen2 天前
用 Go 从零实现多 Agent 协作框架:两个让我踩坑的设计问题
go·ai编程