summaryrefslogtreecommitdiff
path: root/src/builtins.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 12:24:04 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit21d2240b9b86ae5ba0036988b0ed84fbcf18d3af (patch)
treed155d9c7e8f6eb51734cb3dc6544fbf6ceb2e1c5 /src/builtins.zig
parentcfcff94ab2b1ff5a0fb61dead60ead09bf892d30 (diff)
downloadpardes-21d2240b9b86ae5ba0036988b0ed84fbcf18d3af.tar.gz
pardes-21d2240b9b86ae5ba0036988b0ed84fbcf18d3af.zip
Restore asks about unsaved text first, as Exit does
Restore replaces every pane with a dump's, so a pane edited since its last save lost that text without a word, where Exit asks. acme's Load only adds a dump's windows and so never asks; Restore now asks what Exit asks (acme's rowclean and winclean, wind.c:511-529): each modified pane says so once, and Restore again with nothing edited since goes ahead. Exit and Restore share the check and its warned-at revision, as acme's winclean clears one dirty flag for any asker. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/builtins.zig')
-rw-r--r--src/builtins.zig56
1 files changed, 39 insertions, 17 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 93eae8ec..88c65704 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -226,6 +226,18 @@ test "Exit asks once about unsaved text, and quits when asked again" {
try std.testing.expect(p.quit);
}
+test "Restore asks about unsaved text as Exit does, and restores when asked again" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
+ defer p.deinit();
+ const pane = try p.setTestFile("saved\n");
+ 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(p.executeBuiltinLine(p.active, "Restore /tmp/some.dump.zon"));
+ try std.testing.expectEqualStrings("/tmp/some.dump.zon", p.restore_req.?);
+}
+
test "Kill stops the commands pardes typed, bare all and by name those that match, and never quits" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24 });
defer p.deinit();
@@ -340,26 +352,33 @@ pub const Exec = struct {
// ---- session ----
-/// acme's Exit (editors/acme/exec.c, `exit` and rowclean): quit, unless a
-/// pane holds unsaved text nobody has been told about. Each such pane says
-/// so, `<name>: Modified`, and the Exit goes no further; an Exit after that,
-/// with nothing edited since, quits. A scratch of under 100 bytes is no
-/// loss worth the question, as acme's winclean does not ask for one.
+/// 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
+/// 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 {
+ var modified = false;
+ for (c.p.panes, 0..) |slot, id| {
+ const pane = slot orelse continue;
+ const f = pane.file orelse continue;
+ if (f.revision == f.saved_revision or !panes.Output.fileTraits(f.output).saves) continue;
+ 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);
+ modified = true;
+ }
+ return modified;
+}
+
+/// acme's Exit (editors/acme/exec.c:892): quit, once warnModified has
+/// nothing to say.
pub const Exit = struct {
pub const scope: Scope = .session;
pub fn run(c: Ctx) void {
- var modified = false;
- for (c.p.panes, 0..) |slot, id| {
- const pane = slot orelse continue;
- const f = pane.file orelse continue;
- if (f.revision == f.saved_revision or !panes.Output.fileTraits(f.output).saves) continue;
- if (f.output != null and f.content.len < 100) continue;
- if (pane.exit_warned == f.revision) continue;
- pane.exit_warned = f.revision;
- c.p.reportError(id, f.path, error.Modified);
- modified = true;
- }
- if (modified) return;
+ if (warnModified(c)) return;
c.p.quit = true;
c.p.emit(.quit);
}
@@ -412,6 +431,9 @@ pub const Restore = struct {
pub fn run(c: Ctx) void {
const path = c.arg orelse (c.p.last_dump orelse return);
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;
@memcpy(c.p.restore_buf[0..path.len], path);
c.p.restore_req = c.p.restore_buf[0..path.len];
}