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/src/termio/Exec.zig b/src/termio/Exec.zig
index df8998aa6..0938bae5a 100644
--- a/src/termio/Exec.zig
+++ b/src/termio/Exec.zig
@@ -1389,8 +1389,8 @@ const StreamHandler = struct {
}
pub fn nextLine(self: *StreamHandler) !void {
- self.terminal.carriageReturn();
try self.terminal.index();
+ self.terminal.carriageReturn();
}
pub fn setTopAndBottomMargin(self: *StreamHandler, top: u16, bot: u16) !void {
diff --git a/website/app/vt/cnl/page.mdx b/website/app/vt/cnl/page.mdx
new file mode 100644
index 000000000..06f5ca9be
--- /dev/null
+++ b/website/app/vt/cnl/page.mdx
@@ -0,0 +1,13 @@
+import VTSequence from "@/components/VTSequence";
+
+# Cursor Next Line (CNL)
+
+
+
+Move the cursor `n` cells down and to the beginning of the line.
+
+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.
+
+The logic of this sequence is identical to [Cursor Down (CUD)](/vt/cud)
+followed by [Carriage Return (CR)](/vt/cr).
diff --git a/website/app/vt/cpl/page.mdx b/website/app/vt/cpl/page.mdx
new file mode 100644
index 000000000..b055f7e87
--- /dev/null
+++ b/website/app/vt/cpl/page.mdx
@@ -0,0 +1,13 @@
+import VTSequence from "@/components/VTSequence";
+
+# Cursor Previous Line (CPL)
+
+
+
+Move the cursor `n` cells up and to the beginning of the line.
+
+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.
+
+The logic of this sequence is identical to [Cursor Up (CUU)](/vt/cuu)
+followed by [Carriage Return (CR)](/vt/cr).
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`.