summaryrefslogtreecommitdiff
path: root/src/Output.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 01:14:37 -0300
committerGabriel Schneider <[email protected]>2026-10-01 01:35:12 -0300
commit77bda7e14e0ea81db5da54c233fc30d218773f47 (patch)
tree403fd24114e80d98318cf8b7cb827eda1b89c53c /src/Output.zig
parente6ff64843b4a8986e9740c576f346690217cf5a7 (diff)
downloadpardes-77bda7e14e0ea81db5da54c233fc30d218773f47.tar.gz
pardes-77bda7e14e0ea81db5da54c233fc30d218773f47.zip
pardes - keeps one +Pager per directory, refilled, named so from its first log record; streams the text in bounded writes; never loses it; handles \r, NUL and BEL as a terminal does
Each paged command made another +Pager until placement failed, and then git's text was lost with exit 0; a 130 MB page timed out half written and dirty; a directory whose name held a newline left a stray +New; and the log said new +New then rename. The root ctl's `pager <dir>` now makes the directory's +Pager (named before it is announced, placed without the keyboard) or empties the one there; the text goes into it a line-ended 8 MiB chunk a write, one undo step, up to the file limit and its note, and is left clean. A directory a ctl line cannot name pages into the session's. Any failure prints the text to stderr with why and exits 1. A carriage return keeps a progress line's last state and CRLF is a newline; NUL, BEL, SO and SI go; empty stdin makes no pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/Output.zig')
-rw-r--r--src/Output.zig43
1 files changed, 43 insertions, 0 deletions
diff --git a/src/Output.zig b/src/Output.zig
index 1160c455..e6448e36 100644
--- a/src/Output.zig
+++ b/src/Output.zig
@@ -666,6 +666,49 @@ pub fn openUnsaved(p: *Pardes, id: usize, content: []u8) void {
p.active = id;
}
+/// `pardes -`'s pane: the one `<dir>/+Pager`, emptied for the next paged
+/// text when it is there (a directory's paged commands refill it, as its
+/// command pane is reused), else made, named so before it is announced,
+/// and placed wherever there is room without taking the keyboard. `dir`
+/// empty (or not absolute): the session's directory. Its id; NoPaneRoom
+/// or NoPaneSlots when it has no room at all.
+pub fn openPager(p: *Pardes, from: usize, dir_arg: []const u8) !usize {
+ const dir = if (dir_arg.len > 0 and dir_arg[0] == '/') dir_arg else p.sessionDir();
+ const path = try std.fs.path.join(p.gpa, &.{ dir, "+Pager" });
+ for (p.panes, 0..) |slot, i| {
+ const pane = slot orelse continue;
+ const f = if (pane.file) |*file| file else continue;
+ if (!std.mem.eql(u8, f.path, path)) continue;
+ p.gpa.free(path);
+ File.setContent(p, f, try p.gpa.alloc(u8, 0));
+ resetBody(p, pane);
+ f.saved_revision = f.revision;
+ return i;
+ }
+ const free = p.freeSlot() orelse {
+ p.gpa.free(path);
+ return error.NoPaneSlots;
+ };
+ const empty = p.gpa.alloc(u8, 0) catch |err| {
+ p.gpa.free(path);
+ return err;
+ };
+ const pane = open(p, free, dir, .{ .cmd = .New }, "", empty) catch |err| {
+ p.gpa.free(path);
+ return err;
+ };
+ // The pane owns the name from here.
+ p.gpa.free(pane.file.?.path);
+ pane.file.?.path = path;
+ const keyboard = p.active;
+ exec.placeNew(p, from, null, free, .fs);
+ if (p.unplaced.isSet(free)) return error.NoPaneRoom;
+ // Shown, not asked for: the keyboard stays where it was, as a command
+ // pane's does.
+ if (p.panes[keyboard] != null) p.active = keyboard;
+ return free;
+}
+
/// Pane `gone` closed: its row goes from +Unsaved, which lists only panes
/// still there to lose.
pub fn dropUnsaved(p: *Pardes, gone: *const Pane) void {