diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 12:29:21 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | c5c1c200ad66a16b73428de2c369fc15aacac8b4 (patch) | |
| tree | eb569b17cce2e5faddcc740b994a2ad938d3b7cd | |
| parent | 21d2240b9b86ae5ba0036988b0ed84fbcf18d3af (diff) | |
| download | pardes-c5c1c200ad66a16b73428de2c369fc15aacac8b4.tar.gz pardes-c5c1c200ad66a16b73428de2c369fc15aacac8b4.zip | |
An Exit or Restore refusal says the same word again discards the text
'<name>: Modified' read like a failure to retry, and a script that retried
Exit or Restore threw the text away without knowing it. The refusal now names
the word asking and what repeating it does, '<name>: Modified (Exit again to
discard)', and the skill sets it apart from lock's 'file in use', which is
the one to retry.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | .agents/skills/pardes-9p/SKILL.md | 4 | ||||
| -rw-r--r-- | docs/fs.md | 4 | ||||
| -rw-r--r-- | src/builtins.zig | 17 |
3 files changed, 15 insertions, 10 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md index e031f436..77acade3 100644 --- a/.agents/skills/pardes-9p/SKILL.md +++ b/.agents/skills/pardes-9p/SKILL.md @@ -59,7 +59,9 @@ $m/focus the serial of the pane with the keyboard (empty while a column/wor echo a serial into it to move the keyboard (a folded pane stays folded) $m/ctl the settings, one a line as a write takes them; write a setting or a session builtin (Newcol, Dump, Theme x; Exit QUITS the editor, Kill [word...] stops the - commands pardes typed into terminals); a pane's builtins (Del, Save f, + commands pardes typed into terminals; Exit and Restore refuse once per + unsaved pane, `<name>: Modified (Exit again to discard)`, and the same + word again DISCARDS that text -- not a retry, unlike lock's `file in use`); a pane's builtins (Del, Save f, and the column words Delcol, Collapse, which act on that pane's column) go to $m/pane/<n>/ctl $m/commands every builtin: `Word`, `Word arg`, then `root` or `pane` (which ctl takes it) @@ -105,8 +105,8 @@ every setting is one, the rest act on a pane). `/ctl` takes the session's builtins, one a line, at whichever pane has the keyboard as each runs -- `Newcol`, `Dump`, `Mount name dial`, `Theme ink`, `Verbose off`; `Exit`, which quits the editor as acme's does (it refuses once, naming each pane with -unsaved text, `<name>: Modified`, and a second `Exit` with nothing edited -since quits; a scratch under 100 bytes is not asked about; `Restore`, which +unsaved text, `<name>: Modified (Exit again to discard)`, and a second +`Exit` with nothing edited since quits, throwing that text away; a scratch under 100 bytes is not asked about; `Restore`, which replaces every pane, asks the same first); and `Kill`, which does not quit but stops commands, as acme's does: bare, every command pardes started, and `Kill make ls`, those whose line begins with one of the words. A diff --git a/src/builtins.zig b/src/builtins.zig index 88c65704..6bfc9b26 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -220,7 +220,7 @@ test "Exit asks once about unsaved text, and quits when asked again" { pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); try std.testing.expect(!p.quit); - try std.testing.expect(std.mem.endsWith(u8, pane.msg[0..pane.msg_len], ": Modified")); + try std.testing.expect(std.mem.endsWith(u8, pane.msg[0..pane.msg_len], ": Modified (Exit again to discard)")); // Told once, and nothing edited since: the second Exit quits. try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); try std.testing.expect(p.quit); @@ -233,7 +233,7 @@ test "Restore asks about unsaved text as Exit does, and restores when asked agai pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified try std.testing.expect(p.executeBuiltinLine(p.active, "Restore /tmp/some.dump.zon")); try std.testing.expect(p.restore_req == null); - try std.testing.expect(std.mem.endsWith(u8, pane.msg[0..pane.msg_len], ": Modified")); + try std.testing.expect(std.mem.endsWith(u8, pane.msg[0..pane.msg_len], ": Modified (Restore again to discard)")); try std.testing.expect(p.executeBuiltinLine(p.active, "Restore /tmp/some.dump.zon")); try std.testing.expectEqualStrings("/tmp/some.dump.zon", p.restore_req.?); } @@ -354,11 +354,13 @@ pub const Exec = struct { /// acme's rowclean (editors/acme/wind.c:511-529, winclean), asked before /// what would throw unsaved text away: each pane holding some nobody has -/// been told about says so, `<name>: Modified`, and is then clean for the +/// been told about says so, `<name>: Modified (Exit again to discard)`, +/// naming the word asking, since repeating it throws the text away, and is +/// then clean for the /// asking, as winclean clears `dirty`; so the same word again, with /// nothing edited since, goes ahead. A scratch of under 100 bytes is no /// loss worth the question, as winclean does not ask for one. -fn warnModified(c: Ctx) bool { +fn warnModified(c: Ctx, asking: []const u8) bool { var modified = false; for (c.p.panes, 0..) |slot, id| { const pane = slot orelse continue; @@ -367,7 +369,8 @@ fn warnModified(c: Ctx) bool { if (f.output != null and f.content.len < 100) continue; if (pane.discard_warned == f.revision) continue; pane.discard_warned = f.revision; - c.p.reportError(id, f.path, error.Modified); + var buf: [limits.host_path_cap + 48]u8 = undefined; + c.p.reportFailure(id, std.fmt.bufPrint(&buf, "{s}: Modified ({s} again to discard)", .{ f.path, asking }) catch f.path); modified = true; } return modified; @@ -378,7 +381,7 @@ fn warnModified(c: Ctx) bool { pub const Exit = struct { pub const scope: Scope = .session; pub fn run(c: Ctx) void { - if (warnModified(c)) return; + if (warnModified(c, "Exit")) return; c.p.quit = true; c.p.emit(.quit); } @@ -433,7 +436,7 @@ pub const Restore = struct { if (path.len > c.p.restore_buf.len) return; // acme's Load adds a dump's windows to the ones there; a Restore // replaces them all, so it asks what Exit asks first. - if (warnModified(c)) return; + if (warnModified(c, "Restore")) return; @memcpy(c.p.restore_buf[0..path.len], path); c.p.restore_req = c.p.restore_buf[0..path.len]; } |
