From cbded6a95af970ce5159ea4850ce6388328c51c2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 25 Jun 2023 09:49:18 -0700 Subject: [PATCH 1/2] terminal: horizontalTabBack function --- src/terminal/Terminal.zig | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 0f0fef511..bcc909714 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1218,6 +1218,21 @@ pub fn horizontalTab(self: *Terminal) !void { } } +// Same as horizontalTab but moves to the previous tabstop instead of the next. +pub fn horizontalTabBack(self: *Terminal) !void { + const tracy = trace(@src()); + defer tracy.end(); + + while (true) { + // If we're already at the edge of the screen, then we're done. + if (self.screen.cursor.x == 0) return; + + // Move the cursor left + self.screen.cursor.x -= 1; + if (self.tabstops.get(self.screen.cursor.x)) return; + } +} + /// Clear tab stops. /// TODO: test pub fn tabClear(self: *Terminal, cmd: csi.TabClear) void { @@ -1798,6 +1813,29 @@ test "Terminal: horizontal tabs" { try testing.expectEqual(@as(usize, 19), t.screen.cursor.x); } +test "Terminal: horizontal tabs back" { + const alloc = testing.allocator; + var t = try init(alloc, 20, 5); + defer t.deinit(alloc); + + // Edge of screen + t.screen.cursor.x = 19; + + // HT + try t.horizontalTabBack(); + try testing.expectEqual(@as(usize, 15), t.screen.cursor.x); + + // HT + try t.horizontalTabBack(); + try testing.expectEqual(@as(usize, 7), t.screen.cursor.x); + + // HT + try t.horizontalTabBack(); + try testing.expectEqual(@as(usize, 0), t.screen.cursor.x); + try t.horizontalTabBack(); + try testing.expectEqual(@as(usize, 0), t.screen.cursor.x); +} + test "Terminal: setCursorPosition" { var t = try init(testing.allocator, 80, 80); defer t.deinit(testing.allocator); From 03fd649b5e21d0299146db47470972a5130e7da6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 25 Jun 2023 09:53:58 -0700 Subject: [PATCH 2/2] terminal: CHT/CBT --- src/terminal/stream.zig | 26 +++++++++++++++++++++++++- src/termio/Exec.zig | 16 ++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index e78b3f457..3b6b9b06b 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -92,7 +92,7 @@ pub fn Stream(comptime Handler: type) type { log.warn("unimplemented execute: {x}", .{c}), .HT => if (@hasDecl(T, "horizontalTab")) - try self.handler.horizontalTab() + try self.handler.horizontalTab(1) else log.warn("unimplemented execute: {x}", .{c}), @@ -205,6 +205,18 @@ pub fn Stream(comptime Handler: type) type { else => log.warn("invalid CUP command: {}", .{action}), } else log.warn("unimplemented CSI callback: {}", .{action}), + // CHT - Cursor Horizontal Tabulation + 'I' => if (@hasDecl(T, "horizontalTab")) try self.handler.horizontalTab( + switch (action.params.len) { + 0 => 1, + 1 => action.params[0], + else => { + log.warn("invalid horizontal tab command: {}", .{action}); + return; + }, + }, + ) else log.warn("unimplemented CSI callback: {}", .{action}), + // Erase Display // TODO: test 'J' => if (@hasDecl(T, "eraseDisplay")) try self.handler.eraseDisplay( @@ -316,6 +328,18 @@ pub fn Stream(comptime Handler: type) type { }, ) else log.warn("unimplemented CSI callback: {}", .{action}), + // CHT - Cursor Horizontal Tabulation Back + 'Z' => if (@hasDecl(T, "horizontalTabBack")) try self.handler.horizontalTabBack( + switch (action.params.len) { + 0 => 1, + 1 => action.params[0], + else => { + log.warn("invalid horizontal tab back command: {}", .{action}); + return; + }, + }, + ) else log.warn("unimplemented CSI callback: {}", .{action}), + // Repeat Previous Char (REP) 'b' => if (@hasDecl(T, "printRepeat")) try self.handler.printRepeat( switch (action.params.len) { diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 1345d8f0d..75491dc6f 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -959,8 +959,20 @@ const StreamHandler = struct { self.terminal.backspace(); } - pub fn horizontalTab(self: *StreamHandler) !void { - try self.terminal.horizontalTab(); + pub fn horizontalTab(self: *StreamHandler, count: u16) !void { + for (0..count) |_| { + const x = self.terminal.screen.cursor.x; + try self.terminal.horizontalTab(); + if (x == self.terminal.screen.cursor.x) break; + } + } + + pub fn horizontalTabBack(self: *StreamHandler, count: u16) !void { + for (0..count) |_| { + const x = self.terminal.screen.cursor.x; + try self.terminal.horizontalTabBack(); + if (x == self.terminal.screen.cursor.x) break; + } } pub fn linefeed(self: *StreamHandler) !void {