mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 19:26:09 +03:00
terminal: semantic prompt aware resize
This commit is contained in:
@ -2027,6 +2027,7 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
||||
try self.scroll(.{ .delta = 1 });
|
||||
}
|
||||
new_row = self.getRow(.{ .active = y });
|
||||
new_row.setSemanticPrompt(old_row.getSemanticPrompt());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2112,6 +2113,8 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
||||
}
|
||||
|
||||
const row = self.getRow(.{ .active = y });
|
||||
row.setSemanticPrompt(old_row.getSemanticPrompt());
|
||||
|
||||
fastmem.copy(
|
||||
StorageCell,
|
||||
row.storage[1..],
|
||||
@ -2125,6 +2128,7 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
||||
// Slow path: the row is wrapped or doesn't fit so we have to
|
||||
// wrap ourselves. In this case, we basically just "print and wrap"
|
||||
var row = self.getRow(.{ .active = y });
|
||||
row.setSemanticPrompt(old_row.getSemanticPrompt());
|
||||
var x: usize = 0;
|
||||
var cur_old_row = old_row;
|
||||
var cur_old_row_wrapped = old_row_wrapped;
|
||||
@ -2145,6 +2149,7 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
||||
}
|
||||
|
||||
row = self.getRow(.{ .active = y });
|
||||
row.setSemanticPrompt(cur_old_row.getSemanticPrompt());
|
||||
}
|
||||
|
||||
// If our cursor is on this char, then set the new cursor.
|
||||
|
@ -94,6 +94,12 @@ modes: packed struct {
|
||||
|
||||
bracketed_paste: bool = false, // 2004
|
||||
|
||||
// This isn't a mode, this is set by OSC 133 using the "A" event.
|
||||
// If this is true, it tells us that the shell supports redrawing
|
||||
// the prompt and that when we resize, if the cursor is at a prompt,
|
||||
// then we should clear the screen below and allow the shell to redraw.
|
||||
shell_redraws_prompt: bool = false,
|
||||
|
||||
test {
|
||||
// We have this here so that we explicitly fail when we change the
|
||||
// size of modes. The size of modes is NOT particularly important,
|
||||
@ -312,6 +318,7 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
|
||||
|
||||
// If we're making the screen smaller, dealloc the unused items.
|
||||
if (self.active_screen == .primary) {
|
||||
self.clearPromptForResize();
|
||||
try self.screen.resize(rows, cols);
|
||||
try self.secondary_screen.resizeWithoutReflow(rows, cols);
|
||||
} else {
|
||||
@ -330,6 +337,58 @@ pub fn resize(self: *Terminal, alloc: Allocator, cols_req: usize, rows: usize) !
|
||||
};
|
||||
}
|
||||
|
||||
/// If modes.shell_redraws_prompt is true and we're on the primary screen,
|
||||
/// then this will clear the screen from the cursor down if the cursor is
|
||||
/// on a prompt in order to allow the shell to redraw the prompt.
|
||||
fn clearPromptForResize(self: *Terminal) void {
|
||||
assert(self.active_screen == .primary);
|
||||
|
||||
if (!self.modes.shell_redraws_prompt) return;
|
||||
|
||||
// We need to find the first y that is a prompt. If we find any line
|
||||
// that is NOT a prompt (or input -- which is part of a prompt) then
|
||||
// we are not at a prompt and we can exit this function.
|
||||
const prompt_y: usize = prompt_y: {
|
||||
// Keep track of the found value, because we want to find the START
|
||||
var found: ?usize = null;
|
||||
|
||||
// Search from the cursor up
|
||||
var y: usize = 0;
|
||||
while (y <= self.screen.cursor.y) : (y += 1) {
|
||||
const real_y = self.screen.cursor.y - y;
|
||||
const row = self.screen.getRow(.{ .active = real_y });
|
||||
switch (row.getSemanticPrompt()) {
|
||||
// If we're at a prompt or input area, then we are at a prompt.
|
||||
// We mark our found value and continue because the prompt
|
||||
// may be multi-line.
|
||||
.prompt,
|
||||
.input,
|
||||
=> found = real_y,
|
||||
|
||||
// If we have command output, then we're most certainly not
|
||||
// at a prompt. Break out of the loop.
|
||||
.command => break,
|
||||
|
||||
// If we don't know, we keep searching.
|
||||
.unknown => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (found) |found_y| break :prompt_y found_y;
|
||||
return;
|
||||
};
|
||||
assert(prompt_y < self.rows);
|
||||
|
||||
// We want to clear all the lines from prompt_y downwards because
|
||||
// the shell will redraw the prompt.
|
||||
for (prompt_y..self.rows) |y| {
|
||||
const row = self.screen.getRow(.{ .active = y });
|
||||
row.setWrapped(false);
|
||||
row.setDirty(true);
|
||||
row.clear(.{});
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current string value of the terminal. Newlines are
|
||||
/// encoded as "\n". This omits any formatting such as fg/bg.
|
||||
///
|
||||
|
@ -471,9 +471,9 @@ pub fn Stream(comptime Handler: type) type {
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
.prompt_start => {
|
||||
.prompt_start => |v| {
|
||||
if (@hasDecl(T, "promptStart")) {
|
||||
try self.handler.promptStart();
|
||||
try self.handler.promptStart(v.aid, v.redraw);
|
||||
} else log.warn("unimplemented OSC callback: {}", .{cmd});
|
||||
},
|
||||
|
||||
|
@ -1236,8 +1236,10 @@ const StreamHandler = struct {
|
||||
}, .{ .forever = {} });
|
||||
}
|
||||
|
||||
pub fn promptStart(self: *StreamHandler) !void {
|
||||
pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void {
|
||||
_ = aid;
|
||||
self.terminal.markSemanticPrompt(.prompt);
|
||||
self.terminal.modes.shell_redraws_prompt = redraw;
|
||||
}
|
||||
|
||||
pub fn promptEnd(self: *StreamHandler) !void {
|
||||
|
Reference in New Issue
Block a user