From 39c2549b1af4e4f2bbba24f809e14e1bcd4295f7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 19 Nov 2023 20:45:57 -0800 Subject: [PATCH] terminal: add ESC [ 22 J (scroll and clear) --- src/terminal/Screen.zig | 28 ++++++++++++++++++++++++++++ src/terminal/Terminal.zig | 31 +++++++++++++++++++++++++++++++ src/terminal/csi.zig | 4 ++++ 3 files changed, 63 insertions(+) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 55a4f16f0..8ebc5da34 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -6421,6 +6421,34 @@ test "Screen: resize less cols with reflow previously wrapped and scrollback" { try testing.expectEqual(@as(usize, 2), s.cursor.y); } +// test "Screen: resize less cols with scrollback keeps cursor row" { +// const testing = std.testing; +// const alloc = testing.allocator; +// +// var s = try init(alloc, 3, 5, 5); +// defer s.deinit(); +// const str = "1A\n2B\n3C\n4D\n5E"; +// try s.testWriteString(str); +// +// // Put our cursor on the end +// s.cursor.x = 1; +// s.cursor.y = s.rows - 1; +// try testing.expectEqual(@as(u32, 'E'), s.getCell(.active, s.cursor.y, s.cursor.x).char); +// +// try s.resize(3, 3); +// +// { +// const contents = try s.testString(alloc, .viewport); +// defer alloc.free(contents); +// const expected = "3C\n4D\n5E"; +// try testing.expectEqualStrings(expected, contents); +// } +// +// // Cursor should be on the last line +// try testing.expectEqual(@as(usize, 1), s.cursor.x); +// try testing.expectEqual(@as(usize, 2), s.cursor.y); +// } +// test "Screen: resize more rows, less cols with reflow with scrollback" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index b6544d36d..6544a6d67 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1144,6 +1144,20 @@ pub fn eraseDisplay( const protected = self.screen.protected_mode == .iso or protected_req; switch (mode) { + .scroll_complete => { + self.screen.scroll(.{ .clear = {} }) catch |err| { + log.warn("scroll clear failed, doing a normal clear err={}", .{err}); + self.eraseDisplay(alloc, .complete, protected_req); + return; + }; + + // Unsets pending wrap state + self.screen.cursor.pending_wrap = false; + + // Clear all Kitty graphics state for this screen + self.screen.kitty_images.delete(alloc, self, .{ .all = true }); + }, + .complete => { var it = self.screen.rowIterator(.active); while (it.next()) |row| { @@ -6017,6 +6031,23 @@ test "Terminal: eraseDisplay protected above" { var t = try init(alloc, 10, 5); defer t.deinit(alloc); + try t.print('A'); + t.carriageReturn(); + try t.linefeed(); + t.eraseDisplay(alloc, .scroll_complete, false); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("", str); + } +} + +test "Terminal: eraseDisplay scroll complete" { + const alloc = testing.allocator; + var t = try init(alloc, 10, 3); + defer t.deinit(alloc); + try t.print('A'); t.carriageReturn(); try t.linefeed(); diff --git a/src/terminal/csi.zig b/src/terminal/csi.zig index 757d932f7..877f5986e 100644 --- a/src/terminal/csi.zig +++ b/src/terminal/csi.zig @@ -4,6 +4,10 @@ pub const EraseDisplay = enum(u8) { above = 1, complete = 2, scrollback = 3, + + /// This is an extension added by Kitty to move the viewport into the + /// scrollback and then erase the display. + scroll_complete = 22, }; // Modes for the EL CSI command.