From 79b7505cba89a97153e2cfa2a3fef69bb4792faf Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 13:47:11 -0300 Subject: A Restore's warning about unsaved text no longer lets the next Exit discard it Exit and Restore shared one warned-at revision, so a Restore a script was refused over ctl spent the warning, and the person's next Exit quit without asking, throwing the text away. The warning now records which word gave it, and only that word goes ahead on it. Co-Authored-By: Claude Opus 5.5 --- src/builtins.zig | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'src/builtins.zig') diff --git a/src/builtins.zig b/src/builtins.zig index d6b89cf2..d06897ff 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -238,6 +238,21 @@ test "Restore asks about unsaved text as Exit does, and restores when asked agai try std.testing.expectEqualStrings("/tmp/some.dump.zon", p.restore_req.?); } +test "a Restore's warning is not an Exit's: each word is warned on its own" { + 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); + // The Restore warned; an Exit after it has not been, and asks. + 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 (Exit again to discard)")); + try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); + try std.testing.expect(p.quit); +} + 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(); @@ -356,21 +371,22 @@ pub const Exec = struct { /// what would throw unsaved text away: each pane holding some nobody has /// been told about says so, `: 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, asking: []const u8) bool { +/// then clean for the asking, as winclean clears `dirty`; so the same word +/// again, with nothing edited since, goes ahead. Only the same word: a +/// Restore a script was refused does not spend the warning an Exit owes +/// the person at the keyboard. A scratch of under 100 bytes is no loss +/// worth the question, as winclean does not ask for one. +fn warnModified(c: Ctx, asking: Pane.Discarding) 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; + if (pane.discard_warned) |w| if (w.revision == f.revision and w.by == asking) continue; + pane.discard_warned = .{ .revision = f.revision, .by = asking }; 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); + c.p.reportFailure(id, std.fmt.bufPrint(&buf, "{s}: Modified ({s} again to discard)", .{ f.path, @tagName(asking) }) catch f.path); modified = true; } return modified; @@ -381,7 +397,7 @@ fn warnModified(c: Ctx, asking: []const u8) bool { pub const Exit = struct { pub const scope: Scope = .session; pub fn run(c: Ctx) void { - if (warnModified(c, "Exit")) return; + if (warnModified(c, .Exit)) return; c.p.quit = true; c.p.emit(.quit); } @@ -436,7 +452,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, "Restore")) 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]; } -- cgit v1.3