# Cursor Character Absolute (CHA)

> Move the cursor to column n of the line it is on.

- **Sequence:** `CSI Pn G`
- **Defaults:** Pn = 1
- **ECMA-48:** [§8.3.9 CHA – Cursor Character Absolute, p. 34](https://archive.org/details/ecma-48-5th-edition-june-1991/page/n47/mode/1up)
- **VT510:** [CHA—Cursor Horizontal Absolute](https://vt100.net/docs/vt510-rm/CHA.html)

Move the cursor to column `Pn` of the line it is on, counting from 1. The
line does not change. A `0` is taken as `1`, and a column past the right
edge of the screen is clamped to the last column.

Left and right margins do not limit it: with them set, CHA can still move
the cursor to any column on the line. Origin mode changes where it counts
from, though. With origin mode on, column 1 is the left margin, as it is for
[CUP](https://control-codes.page/csi/cup/index.html.md), and the column is clamped to the right margin.

[HPA](https://control-codes.page/csi/hpa/index.html.md) does the same thing with a different final byte. ECMA-48
defines CHA in terms of the *presentation* component, the image on the
screen, and HPA in terms of the *data* component, the stored text the image
is made from. It also says that for a device with no separate data
component, references to the data component "are to be read as referring
to" the presentation component (§6, p. 14). A terminal is such a device, and
libghostty-vt handles both final bytes with the same code.

## Support

| Terminal | Version | Support | Notes |
| --- | --- | --- | --- |
| VT220 | [manual](https://vt100.net/docs/vt220-rm/), 2nd ed., 1984 | No | not in the manual |
| VT510 | [manual](https://vt100.net/docs/vt510-rm/), 1st ed., 1993 | Yes | [CHA](https://vt100.net/docs/vt510-rm/CHA.html) |
| xterm | patch 412, [xterm-411a](https://github.com/ThomasDickey/xterm-snapshots/tree/xterm-411a) | Yes |  |
| libghostty-vt | [0f171f6](https://github.com/ghostty-org/ghostty/tree/0f171f650a1cbbd6df4a2573b6b0c7de57cc6d90) | Yes | CHA-5: in origin mode the cursor lands a line lower |
| kitty | 0.49.1, [run in a VM](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/kitty.json) | Partly | fails CHA-5 of the 4 cases that show it working |
| foot | 1.28.0, [run in a VM](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/foot.json) | Partly | fails CHA-5 of the 4 cases that show it working |
| pyte | 0.8.2, [run as a library](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/pyte.json) | Partly | fails CHA-5 of the 5 cases that show it working |
| xterm.js headless | 6.0.0, [run as a library](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/xtermjs.json) | Partly | fails CHA-5 of the 5 cases that show it working |
| alacritty_terminal | 0.26.0, [run as a library](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/alacritty.json) | Partly | fails CHA-5 of the 5 cases that show it working |
| ConEmu | build 230724, its [documentation](https://conemu.github.io/en/AnsiEscapeCodes.html) | Yes | lists `ESC [ col G`: [ANSI escape codes](https://conemu.github.io/en/AnsiEscapeCodes.html) |
| rxvt-unicode | 9.31, [run in a VM](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/urxvt.json) | Partly | fails CHA-5 of the 4 cases that show it working |
| VTE | 0.84.1, [run as a library](https://git.jcollie.dev/jeff/control-codes/src/branch/main/emulators/results/vte.json) | Yes | passes the 5 cases that show it working |

## Validation

### CHA-1: To the first column by default

Input, one step per line:

```text
\e[1;4H
\e[G
X
```

Expected screen:

```text
|X____|
|_____|
cursor 1,2
```

### CHA-2: To a column

Input, one step per line:

```text
\e[2;1H
\e[4G
X
```

Expected screen:

```text
|_____|
|___X_|
```

### CHA-3: Clamped to the last column

Input, one step per line:

```text
\e[99G
X
```

Expected screen:

```text
|____X|
pending-wrap yes
```

### CHA-4: Not limited by the left margin

Input, one step per line:

```text
\e[?69h   # allow left and right margins
\e[3;5s   # margins at columns 3 and 5
\e[1;4H
\e[1G
X
```

Expected screen:

```text
|X____|
```

### CHA-5: Counted from the left margin in origin mode

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

```text
\e[2;3r   # region is lines 2 and 3
\e[?69h   # allow left and right margins
\e[3;5s   # margins at columns 3 and 5
\e[?6h    # origin mode
\e[1;2H   # line 2, column 4 of the screen
\e[G
X
\e[?6l
```

Expected screen:

```text
|______|
|__X___|
|______|
```

The `X` should land in the left margin's column on the line the cursor was
on, which is what esctest2's `test_CHA_RespectsOriginMode` checks. In
libghostty-vt it lands a line lower. CHA there is
`setCursorPos(current line + 1, Pn)`, and in origin mode `setCursorPos` adds
the top margin to the line it is given, so the line the cursor is already on
is offset a second time.

---

This is the Markdown version of <https://control-codes.page/csi/cha/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.

The validation cases are written in the notation described in <https://control-codes.page/notation/index.html.md>.
