From 9b7c778e55203156529688484e52cd0a1d8322a7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 6 Oct 2023 22:41:40 -0700 Subject: [PATCH] terminal: cursor up respects scroll margins --- src/terminal/Terminal.zig | 86 +++++++++++++++++++++++++++++++++++-- website/app/vt/cuu/page.mdx | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 website/app/vt/cuu/page.mdx diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 54ee0598c..3b7f1713c 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1474,13 +1474,21 @@ pub fn cursorDown(self: *Terminal, count_req: usize) void { /// Move the cursor up amount lines. If amount is greater than the maximum /// move distance then it is internally adjusted to the maximum. If amount is /// 0, adjust it to 1. -// TODO: test -pub fn cursorUp(self: *Terminal, count: usize) void { +pub fn cursorUp(self: *Terminal, count_req: usize) void { const tracy = trace(@src()); defer tracy.end(); - self.screen.cursor.y -|= if (count == 0) 1 else count; + // Always resets pending wrap self.screen.cursor.pending_wrap = false; + + // The min the cursor can move to depends where the cursor currently is + const min = if (self.screen.cursor.y >= self.scrolling_region.top) + self.scrolling_region.top + else + 0; + + const count = @max(count_req, 1); + self.screen.cursor.y = @max(min, self.screen.cursor.y -| count); } /// Backspace moves the cursor back a column (but not less than 0). @@ -3964,3 +3972,75 @@ test "Terminal: cursorDown resets wrap" { try testing.expectEqualStrings("ABCDE\n X", str); } } + +test "Terminal: cursorUp basic" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setCursorPos(3, 1); + try t.print('A'); + t.cursorUp(10); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings(" X\n\nA", str); + } +} + +test "Terminal: cursorUp below top scroll margin" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setScrollingRegion(2, 4); + t.setCursorPos(3, 1); + try t.print('A'); + t.cursorUp(5); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("\n X\nA", str); + } +} + +test "Terminal: cursorUp above top scroll margin" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setScrollingRegion(3, 5); + t.setCursorPos(3, 1); + try t.print('A'); + t.setCursorPos(2, 1); + t.cursorUp(10); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("X\n\nA", str); + } +} + +test "Terminal: cursorUp resets wrap" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + for ("ABCDE") |c| try t.print(c); + try testing.expect(t.screen.cursor.pending_wrap); + t.cursorUp(1); + try testing.expect(!t.screen.cursor.pending_wrap); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("ABCDX", str); + } +} diff --git a/website/app/vt/cuu/page.mdx b/website/app/vt/cuu/page.mdx new file mode 100644 index 000000000..4a46efb06 --- /dev/null +++ b/website/app/vt/cuu/page.mdx @@ -0,0 +1,78 @@ +import VTSequence from "@/components/VTSequence"; + +# Cursor Up (CUU) + + + +Move the cursor `n` cells up. + +The parameter `n` must be an integer greater than or equal to 1. If `n` is less than +or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. + +This sequence always unsets the pending wrap state. + +If the current cursor position is at or below the [top margin](#TODO), +the highest point the cursor can move is the top margin. If the current +cursor position is above the top margin, the highest point the cursor +can move is the first row. + +## Validation + +### CUU V-1: Cursor Up + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\033[3;1H" +printf "A" +printf "\033[2A" # cursor up +printf "X" +``` + +``` +|_Xc_______| +|__________| +|A_________| +``` + +### CUU V-2: Cursor Up Below Top Margin + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\n\n\n\n" # screen is 4 high +printf "\033[2;4r" # set scrolling region +printf "\033[3;1H" +printf "A" +printf "\033[5A" # cursor up +printf "X" +``` + +``` +|__________| +|_Xc_______| +|A_________| +|__________| +``` + +### CUU V-3: Cursor Up Above Top Margin + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "\n\n\n\n\n" # screen is 5 high +printf "\033[3;5r" # set scrolling region +printf "\033[3;1H" +printf "A" +printf "\033[2;1H" # move above region +printf "\033[5A" # cursor up +printf "X" +``` + +``` +|Xc________| +|__________| +|A_________| +|__________| +|__________| +```