From 3e9a4ea234b450f2b7e55362e121ef1e0abad940 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 29 Dec 2023 12:45:57 -0800 Subject: [PATCH] terminal: cub with reverse wrap consumes pending wrap state as one Related to #1184 See the updated website page and associated test. --- src/terminal/Terminal.zig | 53 ++++++++++++++++++++++++++++++++++--- website/app/vt/cub/page.mdx | 22 +++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index f21970424..32f6ced86 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1564,18 +1564,23 @@ pub fn cursorLeft(self: *Terminal, count_req: usize) void { break :wrap_mode .none; }; - // Unset the pending wrap state no matter what - self.screen.cursor.pending_wrap = false; - var count: usize = @max(count_req, 1); // If we are in no wrap mode, then we move the cursor left and exit // since this is the fastest and most typical path. if (wrap_mode == .none) { self.screen.cursor.x -|= count; + self.screen.cursor.pending_wrap = false; return; } + // If we have a pending wrap state and we are in either reverse wrap + // modes then we decrement the amount we move by one to match xterm. + if (self.screen.cursor.pending_wrap) { + count -= 1; + self.screen.cursor.pending_wrap = false; + } + // The margins we can move to. const top = self.scrolling_region.top; const bottom = self.scrolling_region.bottom; @@ -6265,6 +6270,48 @@ test "Terminal: cursorLeft unsets pending wrap state with longer jump" { } } +test "Terminal: cursorLeft reverse wrap with pending wrap state" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.modes.set(.wraparound, true); + t.modes.set(.reverse_wrap, true); + + for ("ABCDE") |c| try t.print(c); + try testing.expect(t.screen.cursor.pending_wrap); + t.cursorLeft(1); + try testing.expect(!t.screen.cursor.pending_wrap); + try t.print('X'); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("ABCDX", str); + } +} + +test "Terminal: cursorLeft reverse wrap extended with pending wrap state" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.modes.set(.wraparound, true); + t.modes.set(.reverse_wrap_extended, true); + + for ("ABCDE") |c| try t.print(c); + try testing.expect(t.screen.cursor.pending_wrap); + t.cursorLeft(1); + try testing.expect(!t.screen.cursor.pending_wrap); + try t.print('X'); + + { + const str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("ABCDX", str); + } +} + test "Terminal: cursorLeft reverse wrap" { const alloc = testing.allocator; var t = try init(alloc, 5, 5); diff --git a/website/app/vt/cub/page.mdx b/website/app/vt/cub/page.mdx index f8c6ff068..4a29d992b 100644 --- a/website/app/vt/cub/page.mdx +++ b/website/app/vt/cub/page.mdx @@ -51,6 +51,11 @@ is a remaining value of `n`. If the cursor is already on the [top margin](#TODO), do not move the cursor up. This wrapping mode does not wrap the cursor row back to the bottom margin. +For **extended reverse wrap** or **reverse wrap** modes, if the pending +wrap state is set, decrease `n` by 1. In these modes, the initial cursor +backward count is consumed by the pending wrap state, as if you pressed +"backspace" on an empty newline and the cursor moved back to the previous line. + ## Validation ### CUB V-1: Pending Wrap is Unset @@ -159,3 +164,20 @@ printf "X" |__________| |Xc________| ``` + +### CUB V-7: Reverse Wrap with Pending Wrap State + +```bash + +cols=$(tput cols) +printf "\033[?45h" +printf "\033[${cols}G" +printf "\033[4D" +printf "ABCDE" +printf "\033[D" +printf "X" +``` + +``` +|_____ABCDX| +```