summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 23:18:16 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit72129eba38522de8f3d0ec6d98b612676d226612 (patch)
tree3ab8cc2bd10212cd1fc810791710262943203bc5
parent90530f893236c665deb9f5bddc925ee1fe51cca5 (diff)
downloadpardes-72129eba38522de8f3d0ec6d98b612676d226612.tar.gz
pardes-72129eba38522de8f3d0ec6d98b612676d226612.zip
Exit and Restore name a modified +New scratch too, in one wording
Their refusal named only file panes, so text typed into a +New scratch went unasked about and was lost. A scratch with text counts as unsaved as a file does (acme's winclean asks about an unnamed window too). The refusal a ctl write gets now reads as the message row does, `<names>: Modified (Exit again to discard)`, instead of a second "discard them all" wording. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/fs.md7
-rw-r--r--src/builtins.zig30
2 files changed, 30 insertions, 7 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 1e09c412..5093e754 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -119,9 +119,10 @@ 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 (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,
+unsaved text, a `+New` scratch with text in it too, in one line,
+`<name>, <name>: Modified (Exit again to discard)`, and a second `Exit`
+with nothing edited since quits, throwing that text away; a command's
+output under 100 bytes is not asked about; `Restore`, which replaces every pane,
asks the same first -- `Dump` writes `pardes-<date>-<time>.zon` (UTC) in
`DumpDir` (default `$XDG_DATA_HOME/pardes`, else `~/.local/share/pardes`)
and logs `dump <path>`, and `Restore` with no path takes the last one; a
diff --git a/src/builtins.zig b/src/builtins.zig
index be674692..384e007f 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -258,7 +258,7 @@ test "Exit over a ctl write names every pane it asks about, and the next Exit di
const said = p.fs.failure[0..p.fs.failure_len];
try std.testing.expect(std.mem.indexOf(u8, said, first.file.?.path) != null);
try std.testing.expect(std.mem.indexOf(u8, said, second.file.?.path) != null);
- try std.testing.expect(std.mem.endsWith(u8, said, ": Modified (Exit again to discard them all)"));
+ try std.testing.expect(std.mem.endsWith(u8, said, ": Modified (Exit again to discard)"));
p.fs.failure_len = 0;
try std.testing.expect(p.executeBuiltinLine(p.active, "Exit"));
try std.testing.expect(p.quit);
@@ -454,8 +454,11 @@ fn warnModified(c: Ctx, asking: Pane.Discarding) bool {
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;
+ // A +New scratch saves nowhere, but what is typed in it is as lost.
+ const scratch = if (f.output) |o| o.from == .cmd and o.from.cmd == .New else false;
+ if (f.revision == f.saved_revision or !(panes.Output.fileTraits(f.output).saves or scratch)) continue;
+ if (f.output != null and !scratch and f.content.len < 100) continue;
+ if (scratch and f.content.len == 0) continue;
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;
@@ -464,7 +467,7 @@ fn warnModified(c: Ctx, asking: Pane.Discarding) bool {
modified = true;
}
if (modified and c.p.fs.no_prompt) {
- all.print(": Modified ({s} again to discard them all)", .{@tagName(asking)}) catch {};
+ all.print(": Modified ({s} again to discard)", .{@tagName(asking)}) catch {};
const said = @import("Messages.zig").clip(all.buffered(), c.p.fs.failure.len);
@memcpy(c.p.fs.failure[0..said.len], said);
c.p.fs.failure_len = @intCast(said.len);
@@ -1733,3 +1736,22 @@ const Board = struct {
try std.testing.expect(20 != 0x20);
}
};
+
+test "Exit and Restore name a modified +New scratch as well as files" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
+ defer p.deinit();
+ const file = try p.setTestFile("saved\n");
+ file.file.?.saved_revision = file.file.?.revision -% 1; // modified
+ p.newScratchBelow(p.active);
+ const scratch = &p.panes[p.active].?.file.?;
+ scratch.content = try p.gpa.realloc(scratch.content, 6);
+ @memcpy(scratch.content, "notes\n");
+ scratch.revision +%= 1; // typed into, never saved
+ p.fs.no_prompt = true;
+ defer p.fs.no_prompt = false;
+ try std.testing.expect(p.executeBuiltinLine(p.active, "Exit"));
+ try std.testing.expect(!p.quit);
+ const said = p.fs.failure[0..p.fs.failure_len];
+ try std.testing.expect(std.mem.indexOf(u8, said, "test.txt") != null);
+ try std.testing.expect(std.mem.indexOf(u8, said, config.scratch_buffer) != null);
+}