From d5f94e6e7a6758dac16371ad9285b8163ee7950e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 10 Oct 2026 04:19:53 -0400 Subject: [PATCH] ElyMark: a merge taken from disk leaves the tab saved (M-03) Every path that takes the disk text as saved goes through adopt_disk, which sets the tab's mark and the view's baseline together; the merge branch no longer repeats it by hand. Co-Authored-By: Claude Opus 5.5 --- apps/ely-mark/src/compare.rs | 16 +++------------- apps/ely-mark/src/outside.rs | 14 ++++++-------- tasks/BUGS.md | 2 +- tasks/reviews/M-03-r1.md | 3 +++ tasks/reviews/M-03-r2.md | 3 +++ tasks/reviews/M-03-r3.md | 1 + 6 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 tasks/reviews/M-03-r1.md create mode 100644 tasks/reviews/M-03-r2.md create mode 100644 tasks/reviews/M-03-r3.md diff --git a/apps/ely-mark/src/compare.rs b/apps/ely-mark/src/compare.rs index 6a305b1..0ea0346 100644 --- a/apps/ely-mark/src/compare.rs +++ b/apps/ely-mark/src/compare.rs @@ -112,20 +112,10 @@ impl Desk { if open > 0 { return log::error!("mark: a merge with {open} open conflicts"); } - let Some(tab) = self.tab_mut(id) else { - return; - }; - if let Err(error) = tab.session.reload_at(fingerprint) { - let why = error.to_string(); - return self.set_outside(id, Outside::Unread(why), cx); + // Disk text is saved; the merge edits it. + if let Some(view) = self.adopt_disk(id, &disk, fingerprint, cx) { + view.update(cx, |view, cx| view.adopt(merged, cx)); } - tab.extra.base = disk.text.clone(); - tab.mark = crate::engine::mark(&disk.text); - tab.extra.restart_autosave(); - tab.doc.lock().expect("the text lock").source = disk; - let view = tab.view.clone(); - view.update(cx, |view, cx| view.adopt(merged, cx)); - self.set_outside(id, Outside::Clear, cx); } } } diff --git a/apps/ely-mark/src/outside.rs b/apps/ely-mark/src/outside.rs index 48dbd3d..2b67c94 100644 --- a/apps/ely-mark/src/outside.rs +++ b/apps/ely-mark/src/outside.rs @@ -236,11 +236,7 @@ impl Desk { match (disk.text == current, dirty) { // The same text: only the file underneath is new. (true, _) => { - if let Some(view) = self.adopt_disk(id, &disk, fingerprint, cx) { - // What shows is now saved: the tab is clean. - let saved = crate::engine::mark(&disk.text); - view.update(cx, |view, cx| view.saved(&saved, cx)); - } + self.adopt_disk(id, &disk, fingerprint, cx); } // The text as last saved: the edits stay unsaved. (false, true) if kept => self.keep_edits(id, disk, fingerprint, cx), @@ -294,8 +290,8 @@ impl Desk { self.save_after_pause(id, cx); } - /// The tab takes the file as saved; returns its view. - fn adopt_disk( + /// The tab and its view take the file as saved. + pub(crate) fn adopt_disk( &mut self, id: u64, disk: &Source, @@ -309,7 +305,8 @@ impl Desk { } tab.extra.base = disk.text.clone(); // The file holds the disk's text: it is saved. - tab.mark = crate::engine::mark(&disk.text); + let saved = crate::engine::mark(&disk.text); + tab.mark = saved.clone(); tab.extra.restart_autosave(); // Its bytes (BOM, line ends) are what saves keep now. tab.doc.lock().expect("the text lock").source = disk.clone(); @@ -322,6 +319,7 @@ impl Desk { log::warn!("mark: the replaced file did not register: {error}"); } self.set_outside(id, Outside::Clear, cx); + view.update(cx, |view, cx| view.saved(&saved, cx)); Some(view) } diff --git a/tasks/BUGS.md b/tasks/BUGS.md index 8cb5dae..77e765a 100644 --- a/tasks/BUGS.md +++ b/tasks/BUGS.md @@ -63,7 +63,7 @@ - [x] D-08 Linux、Windows 上“打印…”可用,导出 PDF 后才报“此平台未构建” — 未构建平台禁用打印、不绑快捷键、错误不带路径;命令面板先查禁用 - [x] M-01 键入期间外部以相同内容重写文件,丢掉上次停顿之后键入的全部文字 — 先等页面送出未送的键;同文只接过文件与字节、标签为已保存;磁盘回到上次保存的文字时保留编辑并恢复自动保存;R1–R4 字节、自动保存、在途写入、已保存基线;R5 PASS - [x] M-02 `refresh` 归零版本计数却不换代,在途的一键让标签一直“未落定” — 刷新即换代、归零并结束欠下的渲染(tally.rs);R1 渲染回调悬空、无在途消息的测试;R2 测试未证旧代归零;R3 PASS -- [ ] M-03 干净标签接过磁盘文字后关闭仍问“是否保存” +- [x] M-03 干净标签接过磁盘文字后关闭仍问“是否保存” — 接过磁盘文字的各路(同文、干净重载、合并)都经 adopt_disk,标签与视图的保存基线同设;R1 合并采用未同步视图基线;R2 注释超长;R3 PASS - [ ] M-04 第二次打印或导出 PDF 悄悄取消第一次,没有文件也没有错误 - [ ] M-05 点页面里的链接什么也不发生 - [ ] M-06 `on_app_quit` 无条件删恢复点与崩溃锁,AppKit 直接终止时丢未保存内容 diff --git a/tasks/reviews/M-03-r1.md b/tasks/reviews/M-03-r1.md new file mode 100644 index 0000000..3ea03e0 --- /dev/null +++ b/tasks/reviews/M-03-r1.md @@ -0,0 +1,3 @@ +FAIL + +apps/ely-mark/src/compare.rs:123 — 合并采用只把 `tab.mark` 改成磁盘文字的散列,没有同步 `MarkView.saved`;`view.adopt` 也不更新这个基线,`MarkView::modified` 仍与最初载入的文字比较。具体场景:打开 `base\n`,本地改成 `ours\n`,外部改成 `theirs\n`;比较时冲突选磁盘侧,再按“使用合并结果”。文字回报落定后,内存与磁盘都是 `theirs\n`,历史也等于新的 `tab.mark`,视图却仍报告 modified,`request_close` 在自动保存前仍弹保存询问,M-03 未完整修复(PRD FILE-02、§14.4)。Windows 与 WSL 的提取状态探针使用实际 `mark`、`modified` 和本行赋值,均得到“text equals disk: true; history equals tab mark: true; view modified: true”,同步视图基线后断言通过;这是状态探针,未运行 macOS 窗口测试。证据保留在 `C:/Users/zhang/Documents/M03-windows-probe.log` 与 WSL `~/M03-linux-probe.log`,探针目录已删除。相关 Cargo 测试在 WSL 通过;Windows 除已归属 W-02 的尾随空格路径用例外通过。修复方向:合并采用时同时把视图的保存基线设为磁盘文字,再把合并结果作为可撤销编辑采用;补测合并恰等于磁盘时无需保存、合并不同于磁盘时仍需保存及关闭询问。 diff --git a/tasks/reviews/M-03-r2.md b/tasks/reviews/M-03-r2.md new file mode 100644 index 0000000..28522c3 --- /dev/null +++ b/tasks/reviews/M-03-r2.md @@ -0,0 +1,3 @@ +FAIL + +apps/ely-mark/src/compare.rs:115 — 本轮新增注释 `The disk is the saved text; the merge an edit on it.` 共 12 个词,违反 agents.md 第 5 条的十词上限。具体复现:运行 `tests/scripts/comments.py`,Windows 与 WSL 均在此行报 `comments break the one-line ten-word rule`;Windows 仅扫描本轮两个改动文件也失败。修复方向:将此注释缩至十词以内,或删除。 diff --git a/tasks/reviews/M-03-r3.md b/tasks/reviews/M-03-r3.md new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/tasks/reviews/M-03-r3.md @@ -0,0 +1 @@ +PASS