diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 17:44:42 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 9f6de14934df03c2e3d521af78ce2e5d7362cc88 (patch) | |
| tree | 6c05f115b5e6a21c1ad81d9d0236c5b969332ae8 | |
| parent | 5d29c3151e40c05e764a9112d407082df89a19e1 (diff) | |
| download | pardes-9f6de14934df03c2e3d521af78ce2e5d7362cc88.tar.gz pardes-9f6de14934df03c2e3d521af78ce2e5d7362cc88.zip | |
An Exit or Restore refused over a ctl write names every unsaved pane, not just the first
Each pane got its own message, but a ctl write fails with its first error only, so a script was told about one pane, and the next Exit discarded both. The refusal now lists them all, as acme's rowclean does, and says the next one discards them all.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | .agents/skills/pardes-9p/SKILL.md | 4 | ||||
| -rw-r--r-- | src/builtins.zig | 33 |
2 files changed, 35 insertions, 2 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md index 58009df5..87cbbe96 100644 --- a/.agents/skills/pardes-9p/SKILL.md +++ b/.agents/skills/pardes-9p/SKILL.md @@ -68,8 +68,8 @@ $m/focus the serial of the pane with the keyboard (empty while a column/wor $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 started (command panes, lines it typed into shells), a word - matching a command line's first word; Exit and Restore refuse once per - unsaved pane, `<name>: Modified (Exit again to discard)`, and the same + matching a command line's first word; Exit and Restore refuse once, + naming every unsaved pane, `<a>, <b>: Modified (Exit again to discard them all)`, and the same word again DISCARDS that text -- not a retry, unlike lock's `file in use`; Kill signals only the foreground job, so of `sleep 30; echo done` the echo still runs; Joincol needs a column to the right of the keyboard's); diff --git a/src/builtins.zig b/src/builtins.zig index 97f7817f..ef425c91 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -240,6 +240,28 @@ test "Exit asks once about unsaved text, and quits when asked again" { try std.testing.expect(p.quit); } +test "Exit over a ctl write names every pane it asks about, and the next Exit discards them all" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + const first = try p.setTestFile("saved\n"); + first.file.?.saved_revision = first.file.?.revision -% 1; + p.newScratchBelow(p.active); + const second = p.panes[p.active].?; + panes.File.setContent(p, &second.file.?, try std.testing.allocator.dupe(u8, "x" ** 120)); + try std.testing.expect(second.file.?.revision != second.file.?.saved_revision); + 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, 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)")); + p.fs.failure_len = 0; + try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); + try std.testing.expect(p.quit); +} + /// A dump file that exists, for a Restore to get past reading it. fn testDump(buf: []u8) ![]const u8 { var tmp = std.testing.tmpDir(.{}); @@ -419,6 +441,10 @@ pub const Exec = struct { /// worth the question, as winclean does not ask for one. fn warnModified(c: Ctx, asking: Pane.Discarding) bool { var modified = false; + // Every pane asked about is named in the one refusal a ctl write gets, + // as acme's rowclean names them all (wind.c:511). + var all_buf: [256]u8 = undefined; + var all = std.Io.Writer.fixed(&all_buf); for (c.p.panes, 0..) |slot, id| { const pane = slot orelse continue; const f = pane.file orelse continue; @@ -428,8 +454,15 @@ fn warnModified(c: Ctx, asking: Pane.Discarding) bool { 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, @tagName(asking) }) catch f.path); + all.print("{s}{s}", .{ if (modified) ", " else "", f.path }) catch {}; modified = true; } + if (modified and c.p.fs.no_prompt) { + all.print(": Modified ({s} again to discard them all)", .{@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); + } return modified; } |
