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

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

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

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

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

从编辑命令到查找替换

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

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

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

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

想象这个过程:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

用反例证明保护确实生效

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

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

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

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


相关推荐
Patrick_Wilson5 小时前
读懂一个 Go JSON 处理函数:从输入数据追踪结构体、校验与错误返回
go·json·编程语言
Patrick_Wilson5 小时前
第一次读 Go 项目:从两个源码文件弄清 package、import 和 go.mod
后端·go·编程语言
Patrick_Wilson5 小时前
执行 go build . 后,Go 怎样找到代码并生成程序
后端·go·编程语言
Patrick_Wilson5 小时前
Go 变量不赋值时,为什么也有值
后端·go·编程语言
Patrick_Wilson5 小时前
读 Go 项目里的命令:run、build、test 分别做了什么
后端·go·编程语言
vipxieliang10 小时前
Go GC 性能优化实践:从 GOGC、GOMEMLIMIT 到分配热点定位
go
孔令飞10 小时前
很多人想学AI Infra,今天推荐一个 AI Agent / AI Infra 学习路线
云原生·go·ai编程
我不会起名字32221 小时前
Redis 从 0 到 1(十七):订单 30 分钟不支付自动关闭——ZSet 延迟队列 Go 实现
redis·go·延迟队列·zset·订单
FungLeo1 天前
成为全栈·Go 后端篇·从 Node SQLite 到 PostgreSQL:把迁移做成独立交付物
postgresql·golang·sqlite·go·数据库迁移·成为全栈
皓月盈江1 天前
GO使用Gin框架实现模板定义、解析与渲染
linux·golang·go·gin·定义模板·解析模板·渲染模板