terminal: dch xterm audit

This commit is contained in:
Mitchell Hashimoto
2023-10-08 22:16:35 -07:00
parent 33ce2c29ea
commit 60f0e9289a
2 changed files with 233 additions and 10 deletions

View File

@ -1197,16 +1197,35 @@ pub fn deleteChars(self: *Terminal, count: usize) !void {
if (count == 0) return; if (count == 0) return;
// If our cursor is outside the margins then do nothing. We DO reset
// wrap state still so this must remain below the above logic.
if (self.screen.cursor.x < self.scrolling_region.left or
self.screen.cursor.x > self.scrolling_region.right) return;
// This resets the pending wrap state // This resets the pending wrap state
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
const pen: Screen.Cell = .{
.bg = self.screen.cursor.pen.bg,
.attrs = .{ .has_bg = self.screen.cursor.pen.attrs.has_bg },
};
// If our X is a wide spacer tail then we need to erase the
// previous cell too so we don't split a multi-cell character.
const line = self.screen.getRow(.{ .active = self.screen.cursor.y });
if (self.screen.cursor.x > 0) {
const cell = line.getCellPtr(self.screen.cursor.x);
if (cell.attrs.wide_spacer_tail) {
line.getCellPtr(self.screen.cursor.x - 1).* = pen;
}
}
// We go from our cursor right to the end and either copy the cell // We go from our cursor right to the end and either copy the cell
// "count" away or clear it. // "count" away or clear it.
const line = self.screen.getRow(.{ .active = self.screen.cursor.y }); for (self.screen.cursor.x..self.scrolling_region.right + 1) |x| {
for (self.screen.cursor.x..self.cols) |x| {
const copy_x = x + count; const copy_x = x + count;
if (copy_x >= self.cols) { if (copy_x >= self.scrolling_region.right + 1) {
line.getCellPtr(x).* = .{}; line.getCellPtr(x).* = pen;
continue; continue;
} }
@ -1484,7 +1503,9 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
// Unset pending wrap state without wrapping // Unset pending wrap state without wrapping. Note: this purposely
// happens BEFORE the scroll region check below, because that's what
// xterm does.
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
// If our cursor is outside the margins then do nothing. We DO reset // If our cursor is outside the margins then do nothing. We DO reset
@ -3466,21 +3487,22 @@ test "Terminal: insertBlanks inside left/right scroll region" {
test "Terminal: insertBlanks outside left/right scroll region" { test "Terminal: insertBlanks outside left/right scroll region" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 10, 10); var t = try init(alloc, 6, 10);
defer t.deinit(alloc); defer t.deinit(alloc);
t.setCursorPos(1, 4);
for ("ABC") |c| try t.print(c);
t.scrolling_region.left = 2; t.scrolling_region.left = 2;
t.scrolling_region.right = 4; t.scrolling_region.right = 4;
t.setCursorPos(1, 3); try testing.expect(t.screen.cursor.pending_wrap);
for ("ABC") |c| try t.print(c);
t.setCursorPos(1, 1);
t.insertBlanks(2); t.insertBlanks(2);
try testing.expect(!t.screen.cursor.pending_wrap);
try t.print('X'); try t.print('X');
{ {
var str = try t.plainString(testing.allocator); var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str); defer testing.allocator.free(str);
try testing.expectEqualStrings("X ABC", str); try testing.expectEqualStrings(" ABX", str);
} }
} }
@ -3725,6 +3747,101 @@ test "Terminal: deleteChars resets wrap" {
} }
} }
test "Terminal: deleteChars simple operation" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
defer t.deinit(alloc);
try t.printString("ABC123");
t.setCursorPos(1, 3);
try t.deleteChars(2);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("AB23", str);
}
}
test "Terminal: deleteChars background sgr" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
defer t.deinit(alloc);
const pen: Screen.Cell = .{
.bg = .{ .r = 0xFF, .g = 0x00, .b = 0x00 },
.attrs = .{ .has_bg = true },
};
try t.printString("ABC123");
t.setCursorPos(1, 3);
t.screen.cursor.pen = pen;
try t.deleteChars(2);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("AB23", str);
for (t.cols - 2..t.cols) |x| {
const cell = t.screen.getCell(.active, 0, x);
try testing.expectEqual(pen, cell);
}
}
}
test "Terminal: deleteChars outside scroll region" {
const alloc = testing.allocator;
var t = try init(alloc, 6, 10);
defer t.deinit(alloc);
try t.printString("ABC123");
t.scrolling_region.left = 2;
t.scrolling_region.right = 4;
try testing.expect(t.screen.cursor.pending_wrap);
try t.deleteChars(2);
try testing.expect(t.screen.cursor.pending_wrap);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("ABC123", str);
}
}
test "Terminal: deleteChars inside scroll region" {
const alloc = testing.allocator;
var t = try init(alloc, 6, 10);
defer t.deinit(alloc);
try t.printString("ABC123");
t.scrolling_region.left = 2;
t.scrolling_region.right = 4;
t.setCursorPos(1, 4);
try t.deleteChars(1);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("ABC2 3", str);
}
}
test "Terminal: deleteChars split wide character" {
const alloc = testing.allocator;
var t = try init(alloc, 6, 10);
defer t.deinit(alloc);
try t.printString("A橋123");
t.setCursorPos(1, 3);
try t.deleteChars(1);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("A 123", str);
}
}
test "Terminal: eraseChars resets wrap" { test "Terminal: eraseChars resets wrap" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 5, 5); var t = try init(alloc, 5, 5);

106
website/app/vt/dch/page.mdx Normal file
View File

@ -0,0 +1,106 @@
import VTSequence from "@/components/VTSequence";
# Delete Character (DCH)
<VTSequence sequence={["CSI", "Pn", "P"]} />
Deletes `n` characters at the current cursor position and shifts existing
cell contents left.
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.
If the current cursor position is outside of the current scroll region,
this sequence does nothing. The cursor is outside of the current scroll
region if it is left of the [left margin](#TODO), or right of the
[right margin](#TODO).
This sequence unsets the pending wrap state. This sequence does _not_ unset
the pending wrap state if the cursor position is outside of the current
scroll region. This has to be called out explicitly because this behavior
differs from [Insert Character (ICH)](/vt/ich).
Only cells within the scroll region are deleted or shifted. Cells to the
right of the right margin are unmodified.
The blank cells inserted from the right margin are blank with the backgroud
color colored according to the current SGR state.
If a multi-cell character (such as "橋") is shifted so that the cell is split
in half, the multi-cell character can either be clipped or erased. Typical
behavior is to clip at the right edge of the screen and erase at a right
margin, but either behavior is acceptable.
## Validation
### DCH V-1: Simple Delete Character
```bash
printf "ABC123"
printf "\033[3G"
printf "\033[2P"
```
```
|AB23____|
```
### DCH V-2: SGR State
```bash
printf "ABC123"
printf "\033[3G"
printf "\033[41m"
printf "\033[2P"
```
```
|AB23____|
```
The two rightmost cells should have a red background color.
### DCH V-3: Outside Left/Right Scroll Region
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "ABC123"
printf "\033[?69h" # enable left/right margins
printf "\033[3;5s" # scroll region left/right
printf "\033[2G"
printf "\033[P"
```
```
|ABC123__|
```
### DCH V-4: Inside Left/Right Scroll Region
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "ABC123"
printf "\033[?69h" # enable left/right margins
printf "\033[3;5s" # scroll region left/right
printf "\033[4G"
printf "\033[P"
```
```
|ABC2_3__|
```
### DCH V-5: Split Wide Character
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "A橋123"
printf "\033[3G"
printf "\033[P"
```
```
|A_123_____|
```