terminal: semantic prompt aware resize

This commit is contained in:
Mitchell Hashimoto
2023-05-31 16:16:00 -07:00
parent 283c94f874
commit 414f2e52a5
4 changed files with 69 additions and 3 deletions

View File

@ -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.

View File

@ -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.
///

View File

@ -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});
},

View File

@ -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 {