mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
Merge pull request #627 from mitchellh/xt-tab
xterm audit: horizontal tab forward and back (CHT, CBT)
This commit is contained in:
@ -1497,7 +1497,7 @@ pub fn horizontalTab(self: *Terminal) !void {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
while (self.screen.cursor.x < self.cols - 1) {
|
while (self.screen.cursor.x < self.scrolling_region.right) {
|
||||||
// Move the cursor right
|
// Move the cursor right
|
||||||
self.screen.cursor.x += 1;
|
self.screen.cursor.x += 1;
|
||||||
|
|
||||||
@ -1513,9 +1513,12 @@ pub fn horizontalTabBack(self: *Terminal) !void {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
|
// With origin mode enabled, our leftmost limit is the left margin.
|
||||||
|
const left_limit = if (self.modes.get(.origin)) self.scrolling_region.left else 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// If we're already at the edge of the screen, then we're done.
|
// If we're already at the edge of the screen, then we're done.
|
||||||
if (self.screen.cursor.x == 0) return;
|
if (self.screen.cursor.x == left_limit) return;
|
||||||
|
|
||||||
// Move the cursor left
|
// Move the cursor left
|
||||||
self.screen.cursor.x -= 1;
|
self.screen.cursor.x -= 1;
|
||||||
@ -2291,6 +2294,43 @@ test "Terminal: horizontal tabs" {
|
|||||||
try testing.expectEqual(@as(usize, 19), t.screen.cursor.x);
|
try testing.expectEqual(@as(usize, 19), t.screen.cursor.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: horizontal tabs starting on tabstop" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 20, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.screen.cursor.x = 8;
|
||||||
|
try t.print('X');
|
||||||
|
t.screen.cursor.x = 8;
|
||||||
|
try t.horizontalTab();
|
||||||
|
try t.print('A');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" X A", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: horizontal tabs with right margin" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 20, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.scrolling_region.left = 2;
|
||||||
|
t.scrolling_region.right = 5;
|
||||||
|
t.screen.cursor.x = 0;
|
||||||
|
try t.print('X');
|
||||||
|
try t.horizontalTab();
|
||||||
|
try t.print('A');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("X A", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: horizontal tabs back" {
|
test "Terminal: horizontal tabs back" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 20, 5);
|
var t = try init(alloc, 20, 5);
|
||||||
@ -2314,6 +2354,44 @@ test "Terminal: horizontal tabs back" {
|
|||||||
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
try testing.expectEqual(@as(usize, 0), t.screen.cursor.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: horizontal tabs back starting on tabstop" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 20, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.screen.cursor.x = 8;
|
||||||
|
try t.print('X');
|
||||||
|
t.screen.cursor.x = 8;
|
||||||
|
try t.horizontalTabBack();
|
||||||
|
try t.print('A');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("A X", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: horizontal tabs with left margin in origin mode" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 20, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.modes.set(.origin, true);
|
||||||
|
t.scrolling_region.left = 2;
|
||||||
|
t.scrolling_region.right = 5;
|
||||||
|
t.screen.cursor.x = 3;
|
||||||
|
try t.print('X');
|
||||||
|
try t.horizontalTabBack();
|
||||||
|
try t.print('A');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" AX", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: setCursorPosition" {
|
test "Terminal: setCursorPosition" {
|
||||||
var t = try init(testing.allocator, 80, 80);
|
var t = try init(testing.allocator, 80, 80);
|
||||||
defer t.deinit(testing.allocator);
|
defer t.deinit(testing.allocator);
|
||||||
|
83
website/app/vt/cbt/page.mdx
Normal file
83
website/app/vt/cbt/page.mdx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Cursor Backward Tabulation (CBT)
|
||||||
|
|
||||||
|
<VTSequence sequence={["CSI", "Pn", "Z"]} />
|
||||||
|
|
||||||
|
Move the cursor `n` tabs left.
|
||||||
|
|
||||||
|
The leftmost valid column for this operation is the first column. If
|
||||||
|
[origin mode](#TODO) is enabled, then the leftmost valid column for this
|
||||||
|
operatin is the [left margin](#TODO).
|
||||||
|
|
||||||
|
Move the cursor left until the cursor position is on a tabstop. If the
|
||||||
|
cursor would move past the leftmost valid column, the cursor remains at
|
||||||
|
the leftmost valid column and the operation completes. Repeat this process
|
||||||
|
`n` times.
|
||||||
|
|
||||||
|
Tabstops are dynamic and can be set with escape sequences such as
|
||||||
|
[horizontal tab set (HTS)](/vt/hts), [tab clear (TBC)](/vt/tbc), etc.
|
||||||
|
A terminal emulator may default tabstops at any interval, though an interval
|
||||||
|
of 8 spaces is most common.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### CBT V-1: Left Beyond First Column
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[10Z"
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|Ac________|
|
||||||
|
```
|
||||||
|
|
||||||
|
### CBT V-2: Left Starting After Tab Stop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[1;10H"
|
||||||
|
printf "X"
|
||||||
|
printf "\033[Z"
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|________AX|
|
||||||
|
```
|
||||||
|
|
||||||
|
### CBT V-3: Left Starting on Tabstop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[1;9H"
|
||||||
|
printf "X"
|
||||||
|
printf "\033[1;9H"
|
||||||
|
printf "\033[Z"
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|A_______X_|
|
||||||
|
```
|
||||||
|
|
||||||
|
### CBT V-4: Left Margin with Origin Mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[?6h" # enable origin mode
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[3;6s" # scroll region left/right
|
||||||
|
printf "\033[1;2H" # move cursor in region
|
||||||
|
printf "X"
|
||||||
|
printf "\033[Z"
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|__AX______|
|
||||||
|
```
|
70
website/app/vt/cht/page.mdx
Normal file
70
website/app/vt/cht/page.mdx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Cursor Horizontal Tabulation (CHT)
|
||||||
|
|
||||||
|
<VTSequence sequence={["CSI", "Pn", "I"]} />
|
||||||
|
|
||||||
|
Move the cursor `n` tabs right.
|
||||||
|
|
||||||
|
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 rightmost valid column for this operation is the rightmost column in
|
||||||
|
the terminal screen or the [right margin](#TODO), whichever is smaller.
|
||||||
|
This sequence does not change behavior with [origin mode](#TODO) set.
|
||||||
|
|
||||||
|
Move the cursor right until the cursor position is on a tabstop. If the
|
||||||
|
cursor would move past the rightmost valid column, the cursor remains at
|
||||||
|
the rightmost valid column and the operation completes. Repeat this process
|
||||||
|
`n` times.
|
||||||
|
|
||||||
|
Tabstops are dynamic and can be set with escape sequences such as
|
||||||
|
[horizontal tab set (HTS)](/vt/hts), [tab clear (TBC)](/vt/tbc), etc.
|
||||||
|
A terminal emulator may default tabstops at any interval, though an interval
|
||||||
|
of 8 spaces is most common.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
### CHT V-1: Right Beyond Last Column
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[100I" # assuming the test terminal has less than 800 columns
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|_________A|
|
||||||
|
```
|
||||||
|
|
||||||
|
### CHT V-2: Right From Before a Tabstop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[1;2H"
|
||||||
|
printf "A"
|
||||||
|
printf "\033[I"
|
||||||
|
printf "X"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|_A_____X__|
|
||||||
|
```
|
||||||
|
|
||||||
|
### CHT V-3: Right Margin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf "\033[1;1H" # move to top-left
|
||||||
|
printf "\033[0J" # clear screen
|
||||||
|
printf "\033[?W" # reset tab stops
|
||||||
|
printf "\033[?69h" # enable left/right margins
|
||||||
|
printf "\033[3;6s" # scroll region left/right
|
||||||
|
printf "\033[1;1H" # move cursor in region
|
||||||
|
printf "X"
|
||||||
|
printf "\033[I"
|
||||||
|
printf "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|__AX______|
|
||||||
|
```
|
8
website/app/vt/tab/page.mdx
Normal file
8
website/app/vt/tab/page.mdx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import VTSequence from "@/components/VTSequence";
|
||||||
|
|
||||||
|
# Tab (TAB)
|
||||||
|
|
||||||
|
<VTSequence sequence="TAB" />
|
||||||
|
|
||||||
|
This is an alias for [cursor horizontal tabulation (CHT)](/vt/cht) with
|
||||||
|
`n = 1`.
|
@ -45,6 +45,7 @@ function VTElem({ elem }: { elem: string }) {
|
|||||||
const special: { [key: string]: number } = {
|
const special: { [key: string]: number } = {
|
||||||
BEL: 0x07,
|
BEL: 0x07,
|
||||||
BS: 0x08,
|
BS: 0x08,
|
||||||
|
TAB: 0x09,
|
||||||
LF: 0x0a,
|
LF: 0x0a,
|
||||||
CR: 0x0d,
|
CR: 0x0d,
|
||||||
ESC: 0x1b,
|
ESC: 0x1b,
|
||||||
|
Reference in New Issue
Block a user