mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
Merge pull request #186 from mitchellh/clear-screen
clear_screen keybinding works even when not at shell prompt
This commit is contained in:
@ -1181,17 +1181,59 @@ fn maxCapacity(self: Screen) usize {
|
||||
return (self.rows + self.max_scrollback) * (self.cols + 1);
|
||||
}
|
||||
|
||||
/// Clear all the history. This moves the viewport back to the "top", too.
|
||||
pub fn clearHistory(self: *Screen) void {
|
||||
// If there is no history, do nothing.
|
||||
if (self.history == 0) return;
|
||||
pub const ClearMode = enum {
|
||||
/// 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,
|
||||
|
||||
// Delete all our history
|
||||
self.storage.deleteOldest(self.history * (self.cols + 1));
|
||||
self.history = 0;
|
||||
/// Clear all the lines above the cursor in the active area. This does
|
||||
/// not touch history.
|
||||
above_cursor,
|
||||
};
|
||||
|
||||
// Back to the top
|
||||
self.viewport = 0;
|
||||
/// 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 (self.history == 0) return;
|
||||
|
||||
// Delete all our history
|
||||
self.storage.deleteOldest(self.history * (self.cols + 1));
|
||||
self.history = 0;
|
||||
|
||||
// Back to the top
|
||||
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
|
||||
@ -3631,7 +3673,7 @@ test "Screen: clear history with no history" {
|
||||
defer s.deinit();
|
||||
try s.testWriteString("4ABCD\n5EFGH\n6IJKL");
|
||||
try testing.expect(s.viewportIsBottom());
|
||||
s.clearHistory();
|
||||
try s.clear(.history);
|
||||
try testing.expect(s.viewportIsBottom());
|
||||
{
|
||||
// Test our contents rotated
|
||||
@ -3665,7 +3707,7 @@ test "Screen: clear history" {
|
||||
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents);
|
||||
}
|
||||
|
||||
s.clearHistory();
|
||||
try s.clear(.history);
|
||||
try testing.expect(s.viewportIsBottom());
|
||||
{
|
||||
// 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" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
@ -1063,7 +1063,10 @@ pub fn eraseDisplay(
|
||||
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});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,15 +258,20 @@ pub fn resize(
|
||||
|
||||
/// Clear the screen.
|
||||
pub fn clearScreen(self: *Exec, history: bool) !void {
|
||||
// Queue form-feed ASCII code to clear the visible page.
|
||||
try self.queueWrite(&[_]u8{0x0C});
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
|
||||
// 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) {
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
self.terminal.screen.clearHistory();
|
||||
}
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user