Skip to content

Commit

Permalink
Merge pull request #655 from mitchellh/xterm-stuff
Browse files Browse the repository at this point in the history
xterm audit: CNL, CPL, NEL, RI, HTS
  • Loading branch information
mitchellh authored Oct 10, 2023
2 parents 2f7dc9d + fbc305c commit 71d8875
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 8 deletions.
142 changes: 135 additions & 7 deletions src/terminal/Terminal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/termio/Exec.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions website/app/vt/cnl/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import VTSequence from "@/components/VTSequence";

# Cursor Next Line (CNL)

<VTSequence sequence={["CSI", "Pn", "E"]} />

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).
13 changes: 13 additions & 0 deletions website/app/vt/cpl/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import VTSequence from "@/components/VTSequence";

# Cursor Previous Line (CPL)

<VTSequence sequence={["CSI", "Pn", "F"]} />

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).
138 changes: 138 additions & 0 deletions website/app/vt/ri/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import VTSequence from "@/components/VTSequence";

# Reverse Index (RI)

<VTSequence sequence={["ESC", "M"]} />

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`.

0 comments on commit 71d8875

Please sign in to comment.