From 55db659d2b7c462777b8085d769cd8a64f5bf979 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 24 Jun 2022 17:47:43 -0700 Subject: [PATCH] basic "index" implementation --- src/terminal/Terminal.zig | 74 ++++++++++++++++++++++++++++++++------- src/terminal/stream.zig | 24 +++++++------ 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 3a6422b50..1b6e3320b 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -195,6 +195,33 @@ pub fn decaln(self: *Terminal) void { } } +/// Move the cursor to the next line in the scrolling region, possibly scrolling. +/// +/// If the cursor is outside of the scrolling region: move the cursor one line +/// down if it is not on the bottom-most line of the screen. +/// +/// If the cursor is inside the scrolling region: +/// If the cursor is on the bottom-most line of the scrolling region: +/// invoke scroll up with amount=1 +/// If the cursor is not on the bottom-most line of the scrolling region: +/// move the cursor one line down +/// +/// This unsets the pending wrap state without wrapping. +pub fn index(self: *Terminal) void { + // TODO: outside of scrolling region + + // If we're at the end of the screen, scroll up. This is surprisingly + // common because most terminals live with a full screen so we do this + // check first. + if (self.cursor.y == self.rows - 1) { + self.scrollUp(); + return; + } + + // Increase cursor by 1 + self.cursor.y += 1; +} + /// Move the cursor to the previous line in the scrolling region, possibly /// scrolling. /// @@ -468,19 +495,7 @@ pub fn carriageReturn(self: *Terminal) void { /// Linefeed moves the cursor to the next line. pub fn linefeed(self: *Terminal) void { - const tracy = trace(@src()); - defer tracy.end(); - - // If we're at the end of the screen, scroll up. This is surprisingly - // common because most terminals live with a full screen so we do this - // check first. - if (self.cursor.y == self.rows - 1) { - self.scrollUp(); - return; - } - - // Increase cursor by 1 - self.cursor.y += 1; + self.index(); } /// Insert amount lines at the current cursor row. The contents of the line @@ -998,6 +1013,39 @@ test "Terminal: reverseIndex from the top" { } } +test "Terminal: index" { + const alloc = testing.allocator; + var t = try init(alloc, 2, 5); + defer t.deinit(alloc); + + t.index(); + try t.print('A'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("\nA", str); + } +} + +test "Terminal: index from the bottom" { + const alloc = testing.allocator; + var t = try init(alloc, 2, 5); + defer t.deinit(alloc); + + t.setCursorPos(5, 1); + try t.print('A'); + t.index(); + + try t.print('B'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("\n\n\nA\nB", str); + } +} + test "Terminal: DECALN" { const alloc = testing.allocator; var t = try init(alloc, 2, 2); diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 2db3b8d94..71dd08480 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -97,7 +97,19 @@ pub fn Stream(comptime Handler: type) type { } } - fn csiDispatch(self: *Self, action: Parser.Action.CSI) !void { + fn csiDispatch(self: *Self, input: Parser.Action.CSI) !void { + // Handles aliases first + const action = switch (input.final) { + // Alias for set cursor position + 'f' => blk: { + var copy = input; + copy.final = 'H'; + break :blk copy; + }, + + else => input, + }; + switch (action.final) { // CUU - Cursor Up 'A' => if (@hasDecl(T, "setCursorUp")) try self.handler.setCursorUp( @@ -281,16 +293,6 @@ pub fn Stream(comptime Handler: type) type { }, ) else log.warn("unimplemented CSI callback: {}", .{action}), - // Alias for set cursor position (H) - 'f' => { - var alias = action; - alias.final = 'H'; - - // Try would be better here but recursive try on - // inferred error sets are not allowed. - return self.csiDispatch(alias); - }, - // SM - Set Mode 'h' => if (@hasDecl(T, "setMode")) { for (action.params) |mode|