summaryrefslogtreecommitdiff
path: root/src/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-27 19:37:05 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit7f31e62b63a64cef3916aac21417b7b1414000aa (patch)
tree75ae00e196e7eff22fc4659b512b12ce81f10cf6 /src/panes.zig
parent594970f18dee92308fd1554663cc408cda4d102c (diff)
downloadpardes-7f31e62b63a64cef3916aac21417b7b1414000aa.tar.gz
pardes-7f31e62b63a64cef3916aac21417b7b1414000aa.zip
pty/run answers with the command's output; pin cloud9 with 9ns concurrency and E.BUSY
The answer is now the header line (exit N, or exit N cut M when only the last 64 KiB were kept, or exit N cut when its start scrolled out of the history), then what the command printed: the screen text between its C and D marks, which the marks handler pins so scrolling keeps them. Also from review: a plain /log open honours its offset until follow is written, so tail -n and less work; whitespace-only run lines are refused. cloud9 is pinned at 82d8152c: 9ns keeps up to 32 requests in flight per mount so a waiting read no longer freezes the rest of it, and E.BUSY replaces the errno pardes spelled locally. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/panes.zig')
-rw-r--r--src/panes.zig37
1 files changed, 37 insertions, 0 deletions
diff --git a/src/panes.zig b/src/panes.zig
index 410492b8..68e03a04 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -6526,11 +6526,26 @@ pub const Terminal = struct {
/// The newest prompt continues an unfinished line (bash's PS2, marked
/// `k=c`): the shell still holds the start of the command.
continuation: bool = false,
+ /// Where the newest command's output began (its C) and ended (its
+ /// D) on the primary screen, pinned so scrolling keeps them.
+ out_start: ?*ghostty_vt.Pin = null,
+ out_end: ?*ghostty_vt.Pin = null,
+
/// The newest command to end: which one, and its status if given.
finished: ?struct { seq: u32, status: ?i32 } = null,
const Action = ghostty_vt.TerminalStream.Action;
+ /// Move one of the output pins to the cursor. Only the primary screen
+ /// is pinned: a command's own full-screen display is not its output.
+ fn pinCursor(self: *@This(), slot: *?*ghostty_vt.Pin, now: bool) void {
+ const screen = self.inner.terminal.screens.get(.primary) orelse return;
+ if (slot.*) |old| screen.pages.untrackPin(old);
+ slot.* = null;
+ if (!now or self.inner.terminal.screens.active_key != .primary) return;
+ slot.* = screen.pages.trackPin(screen.cursor.page_pin.*) catch null;
+ }
+
pub fn vt(self: *@This(), comptime action: Action.Tag, value: Action.Value(action)) void {
if (comptime action == .semantic_prompt) mark: {
const aid = value.readOption(.aid) orelse break :mark;
@@ -6549,10 +6564,13 @@ pub const Terminal = struct {
.end_input_start_output => if (self.phase == .input) {
self.phase = .running;
self.started +%= 1;
+ self.pinCursor(&self.out_start, true);
+ self.pinCursor(&self.out_end, false);
},
.end_command => if (self.phase == .running) {
self.phase = .none;
self.finished = .{ .seq = self.started, .status = value.readOption(.exit_code) };
+ self.pinCursor(&self.out_end, true);
} else {
self.declined +%= 1;
},
@@ -7357,6 +7375,25 @@ pub const Terminal = struct {
state.vt.screens.active.cursor.semantic_content == .input;
}
+ /// What the newest finished command printed: the screen text between its
+ /// C and D marks, owned by the caller. When its start has scrolled out of
+ /// the history, ghostty moved that pin to the oldest row it kept (and
+ /// flagged it `garbage`), so this is the part that survived.
+ pub fn commandOutput(pane: *Pane, gpa: std.mem.Allocator) ![:0]const u8 {
+ if (comptime enabled) printed: {
+ const state = pane.terminal orelse break :printed;
+ const start = state.stream.handler.out_start orelse break :printed;
+ const end = state.stream.handler.out_end orelse break :printed;
+ if (end.garbage) break :printed;
+ const screen = state.vt.screens.get(.primary) orelse break :printed;
+ // D is where the next prompt begins: the output ends a cell before.
+ const last = end.leftWrap(1) orelse break :printed;
+ if (start.eql(end.*) or last.before(start.*)) break :printed;
+ return try screen.selectionString(gpa, .{ .sel = ghostty_vt.Selection.init(start.*, last, false) });
+ }
+ return try gpa.dupeZ(u8, ""); // it printed nothing
+ }
+
/// At a prompt with nothing typed on its line: no input left of the
/// cursor and none under it (text typed and then Ctrl-A'd starts at the
/// cursor). Nothing further right counts: that is where fish draws a