为了让文档在 git checkout 后也不会变空,我们不看时间戳,而看字节
切换分支后,是否遇到过已经打开的文档变成空白画面的情况?我们在为博客截取截图时,在自己的应用中看到了这一幕,并借着修复它的机会,一并解决了另一个更隐蔽的 bug。

mote 完全信任文件。屏幕上的内容与磁盘中的字节相同,保存时也会原样写入这些字节。
但是,如果文件在应用外部发生了变化呢?如果 mote 没能察觉,下一次保存时就可能覆盖其他人写入的内容。
这个问题是在截取截图时发现的。我们在来回切换分支、准备“变化的字节”画面时,文档偶尔会停在空白画面。
空白画面背后隐藏的两个问题
git checkout并不是一次性更改文件。它会删除现有文件,重新创建文件,然后写入内容。
文件监视器会通过多个事件报告这个过程。但 mote 一收到第一个事件就开始读取文件。当时内容还没有写完,所以有时会读到空文件。
重新读取文件时,我们也重新创建了监视器。在此期间,旧监视器中残留的事件消失了,画面便停留在空白状态。
还有一个更危险的问题。
mote 会把保存后 1.5 秒内收到的事件视为“由自己造成的更改”并予以忽略。但在这段时间里,格式化程序或保存钩子也可能再次修改文件。
实际上,如果在保存 0.3 秒后修改文件,mote 不会显示任何变化。如果用户再次保存,外部修改的内容就会悄无声息地消失。
一个问题看得见,另一个问题看不见。原因却相同。
我们当时是根据时间而不是内容来判断文件变化的。
我们不再看时间,而是比较内容
现在收到文件事件后,不会立即读取。我们会等待 200ms,每当有新事件进入时,便重新开始计时。
也就是说,会等到删除、创建和写入文件的过程结束后,只读取一次最终状态。旧计时器通过代次编号失效。我们也没有创建在无事发生时仍持续运行的计时器。
读取到的内容会与缓冲区最后看到的磁盘字节进行比较。
如果相同,说明是应用进行了保存,或者相同内容被再次写入。如果不同,才是真正的外部更改。保存后经过了 0.1 秒还是 1 秒并不重要。
重新读取同一个文件时,监视器也会保持不变。这样可以避免在更换监视器期间丢失事件。
只在编辑时询问
发现外部更改后,会查看用户当前的状态。
如果存在尚未保存的编辑,就会显示横幅。用户可以选择重新加载已更改的文件,或者继续编辑当前内容。无论选择哪一边,都可能丢失内容,因此应用不会替用户作出决定。
如果当前不在编辑,就会立即加载新内容。不会显示横幅,并且会原样保留光标和滚动位置。因为在切换分支阅读文档时,比起显示通知,保留原先阅读的位置更加自然。

文件消失时,也不会立即作出判断。由于其他编辑器可能正在重命名文件,我们会在 300ms 后再次确认。
如果那时文件仍然不存在,就会通知用户。正在编写的内容仍保留在缓冲区中,再次保存时会重新创建文件。
在真实环境中检查六种情况
这个问题很难仅通过单元测试进行验证。因为它是在文件监视器、真实磁盘和窗口管理器共同运行时发生的。
因此,我们制作了在真实环境中运行的检查。
我们会确认,即使切换十次分支,缓冲区是否仍会跟随文件变化;即使在保存 300ms 后修改文件,变化是否仍会得到反映。还会检查应用亲自保存时是否不会显示通知,以及编辑期间发生外部更改时,是否会在保留已编写内容的同时显示横幅。
总共有六种。每次发布时都会运行。

tools/e2e/external.sh的输出。六行全部为ok,最后一行为PASS=6 FAIL=0。把文件视为真相,并不止于原样写入保存的内容。
当文件在我们不知情的情况下发生变化时,也必须确保不会错过这些变化。