From fbc305c9012e91d30cf86c7f4085762c28b6e9b2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 10 Oct 2023 15:15:53 -0700 Subject: [PATCH] terminal: reverse index xterm audit --- src/terminal/Terminal.zig | 142 +++++++++++++++++++++++++++++++++++-- website/app/vt/ri/page.mdx | 138 +++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 7 deletions(-) create mode 100644 website/app/vt/ri/page.mdx diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 5d66d72d5..b4a2c1a8b 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -973,15 +973,15 @@ pub fn reverseIndex(self: *Terminal) !void { const tracy = trace(@src()); defer tracy.end(); - // If the cursor is on the top-most line of the scroll region or - // its on the top of the screen we scroll down. - if (self.screen.cursor.y == self.scrolling_region.top or - self.screen.cursor.y == 0) + if (self.screen.cursor.y != self.scrolling_region.top or + self.screen.cursor.x < self.scrolling_region.left or + self.screen.cursor.x > self.scrolling_region.right) { - try self.scrollDown(1); - } else { - self.screen.cursor.y -|= 1; + self.cursorUp(1); + return; } + + try self.scrollDown(1); } // Set Cursor Position. Move cursor to the position indicated @@ -3346,6 +3346,134 @@ test "Terminal: reverseIndex top of scrolling region" { } } +test "Terminal: reverseIndex top of screen" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.print('A'); + t.setCursorPos(2, 1); + try t.print('B'); + t.setCursorPos(3, 1); + try t.print('C'); + t.setCursorPos(1, 1); + try t.reverseIndex(); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("X\nA\nB\nC", str); + } +} + +test "Terminal: reverseIndex not top of screen" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.print('A'); + t.setCursorPos(2, 1); + try t.print('B'); + t.setCursorPos(3, 1); + try t.print('C'); + t.setCursorPos(2, 1); + try t.reverseIndex(); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("X\nB\nC", str); + } +} + +test "Terminal: reverseIndex top/bottom margins" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.print('A'); + t.setCursorPos(2, 1); + try t.print('B'); + t.setCursorPos(3, 1); + try t.print('C'); + t.setTopAndBottomMargin(2, 3); + t.setCursorPos(2, 1); + try t.reverseIndex(); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\n\nB", str); + } +} + +test "Terminal: reverseIndex outside top/bottom margins" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.print('A'); + t.setCursorPos(2, 1); + try t.print('B'); + t.setCursorPos(3, 1); + try t.print('C'); + t.setTopAndBottomMargin(2, 3); + t.setCursorPos(1, 1); + try t.reverseIndex(); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\nB\nC", str); + } +} + +test "Terminal: reverseIndex left/right margins" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.printString("ABC"); + t.setCursorPos(2, 1); + try t.printString("DEF"); + t.setCursorPos(3, 1); + try t.printString("GHI"); + t.modes.set(.enable_left_and_right_margin, true); + t.setLeftAndRightMargin(2, 3); + t.setCursorPos(1, 2); + try t.reverseIndex(); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\nDBC\nGEF\n HI", str); + } +} + +test "Terminal: reverseIndex outside left/right margins" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.printString("ABC"); + t.setCursorPos(2, 1); + try t.printString("DEF"); + t.setCursorPos(3, 1); + try t.printString("GHI"); + t.modes.set(.enable_left_and_right_margin, true); + t.setLeftAndRightMargin(2, 3); + t.setCursorPos(1, 1); + try t.reverseIndex(); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("ABC\nDEF\nGHI", str); + } +} + test "Terminal: index" { const alloc = testing.allocator; var t = try init(alloc, 2, 5); diff --git a/website/app/vt/ri/page.mdx b/website/app/vt/ri/page.mdx new file mode 100644 index 000000000..bd9bcaf45 --- /dev/null +++ b/website/app/vt/ri/page.mdx @@ -0,0 +1,138 @@ +import VTSequence from "@/components/VTSequence"; + +# Reverse Index (RI) + + + +Move the cursor up one cell, scrolling if necessary. + +This sequence does not unset the pending wrap state. + +If the cursor is exactly on the [top margin](/vt/decstbm) and is within +[left and right margins](/vt/decslrm), invoke [scroll down (SD)](/vt/sd) +with `n = 1`. The operation is complete. + +Otherwise, scrolling isn't necessary. Perform a +[cursor up](/vt/cuu) operation with `n = 1`. + +## Validation + +### RI V-1: No Scroll Region, Top of Screen + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "A\n" +printf "B\n" +printf "C\n" +printf "\033[1;1H" # move to top-left +printf "\033M" # reverse index +printf "X" +``` + +``` +|Xc________| +|A_________| +|B_________| +|C_________| +``` + +### RI V-2: No Scroll Region, Not Top of Screen + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "A\n" +printf "B\n" +printf "C\n" +printf "\033[2;1H" +printf "\033M" # reverse index +printf "X" +``` + +``` +|Xc________| +|B_________| +|C_________| +``` + +### RI V-3: Top/Bottom Scroll Region + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "A\n" +printf "B\n" +printf "C\n" +printf "\033[2;3r" # scroll region +printf "\033[2;1H" +printf "\033M" # reverse index +``` + +``` +|A_________| +|c_________| +|B_________| +``` + +### RI V-4: Outside of Top/Bottom Scroll Region + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "A\n" +printf "B\n" +printf "C\n" +printf "\033[2;3r" # scroll region +printf "\033[1;1H" +printf "\033M" # reverse index +``` + +``` +|A_________| +|B_________| +|C_________| +``` + +### RI V-5: Left/Right Scroll Region + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "ABC\n" +printf "DEF\n" +printf "GHI\n" +printf "\033[?69h" # enable left/right margins +printf "\033[2;3s" # scroll region left/right +printf "\033[1;2H" +printf "\033M" +``` + +``` +|A_________| +|DBC_______| +|GEF_______| +|_HI_______| +``` + +### RI V-6: Outside Left/Right Scroll Region + +```bash +printf "\033[1;1H" # move to top-left +printf "\033[0J" # clear screen +printf "ABC\n" +printf "DEF\n" +printf "GHI\n" +printf "\033[?69h" # enable left/right margins +printf "\033[2;3s" # scroll region left/right +printf "\033[2;1H" +printf "\033M" +``` + +``` +|ABC_______| +|DEF_______| +|GHI_______| +``` + +Cursor on the `A`.