summaryrefslogtreecommitdiff
path: root/src/panes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/panes.zig')
-rw-r--r--src/panes.zig62
1 files changed, 47 insertions, 15 deletions
diff --git a/src/panes.zig b/src/panes.zig
index 08310830..0fb49636 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -6535,6 +6535,11 @@ pub const Terminal = struct {
/// D) on the primary screen, pinned so scrolling keeps them.
out_start: ?*ghostty_vt.Pin = null,
out_end: ?*ghostty_vt.Pin = null,
+ /// The running command's output was erased under it -- a clear, ED 3,
+ /// a reset -- so the pins no longer span what it printed: ED 3 moves
+ /// a pin to the next page without flagging it `garbage`
+ /// (PageList.erasePage).
+ lost: bool = false,
/// The newest command to end: which one, and its status if given.
finished: ?struct { seq: u32, status: ?i32 } = null,
@@ -6569,6 +6574,7 @@ pub const Terminal = struct {
.end_input_start_output => if (self.phase == .input) {
self.phase = .running;
self.started +%= 1;
+ self.lost = false;
self.pinCursor(&self.out_start, true);
self.pinCursor(&self.out_end, false);
},
@@ -6582,6 +6588,9 @@ pub const Terminal = struct {
else => {},
}
}
+ if (comptime action == .erase_display_complete or action == .erase_display_scrollback or action == .full_reset) {
+ if (self.phase == .running) self.lost = true;
+ }
self.inner.vt(action, value);
}
@@ -7380,23 +7389,46 @@ pub const Terminal = struct {
state.vt.screens.active.cursor.semantic_content == .input;
}
+ pub const CommandOutput = struct {
+ /// Owned by the caller.
+ text: [:0]const u8,
+ /// Whether `text` starts where the command did. It does not when that
+ /// start is not there to read: cut off by `max_rows`, scrolled out of
+ /// the history (ghostty moved the pin to the oldest row it kept and
+ /// flagged it `garbage`), erased under it (`MarkedHandler.lost`), or
+ /// never pinned (a C or D that came on the alternate screen).
+ whole: bool,
+ };
+
/// 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;
+ /// C and D marks, read from at most `max_rows` rows above its end, so a
+ /// command that printed the whole history costs no more than its tail.
+ pub fn commandOutput(pane: *Pane, gpa: std.mem.Allocator, max_rows: usize) !CommandOutput {
+ // Unless there is text to read, whether the nothing it printed is the
+ // whole of it.
+ const whole = if (comptime enabled) printed: {
+ const state = pane.terminal orelse break :printed true;
+ const marks = &state.stream.handler;
+ const start = marks.out_start orelse break :printed false;
+ const end = marks.out_end orelse break :printed false;
+ const screen = state.vt.screens.get(.primary) orelse break :printed false;
+ if (end.garbage) break :printed false;
+ var whole = !marks.lost and !start.garbage;
// 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
+ const last = end.leftWrap(1) orelse break :printed whole;
+ if (start.eql(end.*) or last.before(start.*)) break :printed whole;
+ var from = start.*;
+ if (last.up(max_rows)) |floor| if (from.before(floor)) {
+ from = floor;
+ from.x = 0;
+ whole = false;
+ };
+ return .{
+ .text = try screen.selectionString(gpa, .{ .sel = ghostty_vt.Selection.init(from, last, false) }),
+ .whole = whole,
+ };
+ } else true;
+ return .{ .text = try gpa.dupeZ(u8, ""), .whole = whole };
}
/// At a prompt with nothing typed on its line: no input left of the