Merge pull request #186 from mitchellh/clear-screen

clear_screen keybinding works even when not at shell prompt
This commit is contained in:
Mitchell Hashimoto
2023-07-05 14:11:12 -07:00
committed by GitHub
3 changed files with 120 additions and 19 deletions

View File

@ -1181,8 +1181,21 @@ fn maxCapacity(self: Screen) usize {
return (self.rows + self.max_scrollback) * (self.cols + 1); return (self.rows + self.max_scrollback) * (self.cols + 1);
} }
/// Clear all the history. This moves the viewport back to the "top", too. pub const ClearMode = enum {
pub fn clearHistory(self: *Screen) void { /// Delete all history. This will also move the viewport area to the top
/// so that the viewport area never contains history. This does NOT
/// change the active area.
history,
/// Clear all the lines above the cursor in the active area. This does
/// not touch history.
above_cursor,
};
/// Clear the screen contents according to the given mode.
pub fn clear(self: *Screen, mode: ClearMode) !void {
switch (mode) {
.history => {
// If there is no history, do nothing. // If there is no history, do nothing.
if (self.history == 0) return; if (self.history == 0) return;
@ -1192,6 +1205,35 @@ pub fn clearHistory(self: *Screen) void {
// Back to the top // Back to the top
self.viewport = 0; self.viewport = 0;
},
.above_cursor => {
// First we copy all the rows from our cursor down to the top
// of the active area.
var y: usize = self.cursor.y;
const y_max = @min(self.rows, self.rowsWritten()) - 1;
const copy_n = (y_max - y) + 1;
while (y <= y_max) : (y += 1) {
const dst_y = y - self.cursor.y;
const dst = self.getRow(.{ .active = dst_y });
const src = self.getRow(.{ .active = y });
try dst.copyRow(src);
}
// Next we want to clear all the rows below the copied amount.
y = copy_n;
while (y <= y_max) : (y += 1) {
const dst = self.getRow(.{ .active = y });
dst.clear(.{});
}
// Move our cursor to the top
self.cursor.y = 0;
// Scroll to the top of the viewport
self.viewport = self.history;
},
}
} }
/// Select the line under the given point. This will select across soft-wrapped /// Select the line under the given point. This will select across soft-wrapped
@ -3631,7 +3673,7 @@ test "Screen: clear history with no history" {
defer s.deinit(); defer s.deinit();
try s.testWriteString("4ABCD\n5EFGH\n6IJKL"); try s.testWriteString("4ABCD\n5EFGH\n6IJKL");
try testing.expect(s.viewportIsBottom()); try testing.expect(s.viewportIsBottom());
s.clearHistory(); try s.clear(.history);
try testing.expect(s.viewportIsBottom()); try testing.expect(s.viewportIsBottom());
{ {
// Test our contents rotated // Test our contents rotated
@ -3665,7 +3707,7 @@ test "Screen: clear history" {
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents); try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents);
} }
s.clearHistory(); try s.clear(.history);
try testing.expect(s.viewportIsBottom()); try testing.expect(s.viewportIsBottom());
{ {
// Test our contents rotated // Test our contents rotated
@ -3681,6 +3723,57 @@ test "Screen: clear history" {
} }
} }
test "Screen: clear above cursor" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 10, 10, 3);
defer s.deinit();
try s.testWriteString("4ABCD\n5EFGH\n6IJKL");
try testing.expect(s.viewportIsBottom());
try s.clear(.above_cursor);
try testing.expect(s.viewportIsBottom());
{
var contents = try s.testString(alloc, .viewport);
defer alloc.free(contents);
try testing.expectEqualStrings("6IJKL", contents);
}
{
var contents = try s.testString(alloc, .screen);
defer alloc.free(contents);
try testing.expectEqualStrings("6IJKL", contents);
}
try testing.expectEqual(@as(usize, 5), s.cursor.x);
try testing.expectEqual(@as(usize, 0), s.cursor.y);
}
test "Screen: clear above cursor with history" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 3, 10, 3);
defer s.deinit();
try s.testWriteString("1ABCD\n2EFGH\n3IJKL\n");
try s.testWriteString("4ABCD\n5EFGH\n6IJKL");
try testing.expect(s.viewportIsBottom());
try s.clear(.above_cursor);
try testing.expect(s.viewportIsBottom());
{
var contents = try s.testString(alloc, .viewport);
defer alloc.free(contents);
try testing.expectEqualStrings("6IJKL", contents);
}
{
var contents = try s.testString(alloc, .screen);
defer alloc.free(contents);
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL\n6IJKL", contents);
}
try testing.expectEqual(@as(usize, 5), s.cursor.x);
try testing.expectEqual(@as(usize, 0), s.cursor.y);
}
test "Screen: selectionString basic" { test "Screen: selectionString basic" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -1063,7 +1063,10 @@ pub fn eraseDisplay(
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
}, },
.scrollback => self.screen.clearHistory(), .scrollback => self.screen.clear(.history) catch |err| {
// This isn't a huge issue, so just log it.
log.err("failed to clear scrollback: {}", .{err});
},
} }
} }

View File

@ -258,15 +258,20 @@ pub fn resize(
/// Clear the screen. /// Clear the screen.
pub fn clearScreen(self: *Exec, history: bool) !void { pub fn clearScreen(self: *Exec, history: bool) !void {
// Queue form-feed ASCII code to clear the visible page.
try self.queueWrite(&[_]u8{0x0C});
// Clear our scrollback
if (history) {
self.renderer_state.mutex.lock(); self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock(); defer self.renderer_state.mutex.unlock();
self.terminal.screen.clearHistory();
} // If we're on the alternate screen, we do not clear. Since this is an
// emulator-level screen clear, this messes up the running programs
// knowledge of where the cursor is and causes rendering issues. So,
// for alt screen, we do nothing.
if (self.terminal.active_screen == .alternate) return;
// Clear our scrollback
if (history) try self.terminal.screen.clear(.history);
// Clear above the cursor
try self.terminal.screen.clear(.above_cursor);
} }
pub inline fn queueWrite(self: *Exec, data: []const u8) !void { pub inline fn queueWrite(self: *Exec, data: []const u8) !void {