# Scroll Left and Right (SL, SR)

> Move the contents of the scrolling region left or right by n columns.

- **Sequence:** `CSI Pn SP @`
- **Defaults:** Pn = 1
- **ECMA-48:** [§8.3.121 SL – Scroll Left, p. 63](https://archive.org/details/ecma-48-5th-edition-june-1991/page/n76/mode/1up)
- **xterm:** [Functions using CSI](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

`CSI Pn SP @` (SL) moves what is on the screen `Pn` columns to the left, and
`CSI Pn SP A` (SR) moves it `Pn` columns to the right. They are
[SU](https://control-codes.page/csi/su/index.html.md) and [SD](https://control-codes.page/csi/sd/index.html.md) turned on their side. ECMA-48 defines SR
at
[§8.3.135, p. 68](https://archive.org/details/ecma-48-5th-edition-june-1991/page/n81/mode/1up).
Neither is in DEC STD 070 or the VT510 manual: DEC's terminals scroll
sideways with [DECIC and DECDC](https://control-codes.page/csi/decic/index.html.md) and
[DECBI and DECFI](https://control-codes.page/esc/decbi/index.html.md) instead.

xterm implements both
([`CASE_SL`, `CASE_SR`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5386-L5396)),
and calls them "from ISO 6429, not found in any of DEC's terminals"
([`xtermScrollLR`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/util.c#L1129-L1139)).
It does the same as DECDC or DECIC with the cursor at the left margin
([`xtermColScroll`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/util.c#L1176-L1245)):

- every line of the scrolling region moves, and only the part of it between
  the left and right margins;
- SL loses the columns that pass the left margin and brings in blank ones at
  the right margin, SR the other way round;
- a `0` is taken as `1`, and a count larger than the region empties it;
- the cursor does not move;
- if the cursor is outside the margins, top, bottom, left or right, nothing
  happens.

libghostty-vt ignores SL and SR. Its CSI dispatch in `stream.zig` has no case
for `@` or `A` with a space intermediate, so the cases below that change the
screen are marked as known differences.

The space matters: `CSI Pn @` is [ICH](https://control-codes.page/csi/ich/index.html.md) and `CSI Pn A` is
[CUU](https://control-codes.page/csi/cuu/index.html.md).

## Validation

### SL-1: Scroll left

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

Input, one step per line:

```text
ABCDE\r\n
FGHIJ
\e[1;3H
\e[\s@    # SL by one column
```

Expected screen:

```text
|BCDE____|
|GHIJ____|
cursor 1,3
```

### SL-2: Scroll right

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

Input, one step per line:

```text
ABCDE\r\n
FGHIJ
\e[1;3H
\e[2\sA   # SR by two columns
```

Expected screen:

```text
|__ABCDE_|
|__FGHIJ_|
cursor 1,3
```

### SL-3: Zero is one

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

Input, one step per line:

```text
ABCDE
\e[1;1H
\e[0\s@
```

Expected screen:

```text
|BCDE_|
cursor 1,1
```

### SL-4: More than the width empties the region

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

Input, one step per line:

```text
ABCDE\r\n
FGHIJ
\e[2;2H
\e[9\sA
```

Expected screen:

```text
|_____|
|_____|
cursor 2,2
```

### SL-5: Only the scrolling region moves

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

Input, one step per line:

```text
ABCDE\r\n
FGHIJ\r\n
KLMNO\r\n
PQRST
\e[2;3r   # scrolling region: lines 2 and 3
\e[2;1H
\e[\s@
```

Expected screen:

```text
|ABCDE|
|GHIJ_|
|LMNO_|
|PQRST|
cursor 2,1
```

### SL-6: Between the left and right margins

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

Input, one step per line:

```text
ABCDEF\r\n
GHIJKL
\e[?69h   # allow left and right margins
\e[2;5s   # margins at columns 2 and 5
\e[1;3H
\e[\s@    # SL: in from the right margin
```

Expected screen:

```text
|ACDE_F|
|GIJK_L|
cursor 1,3
```

### SL-7: SR between the margins

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

Input, one step per line:

```text
ABCDEF\r\n
GHIJKL
\e[?69h
\e[2;5s
\e[1;3H
\e[2\sA   # SR by two: lost past the right margin
```

Expected screen:

```text
|A__BCF|
|G__HIL|
cursor 1,3
```

### SL-8: Nothing happens with the cursor outside the margins

Input, one step per line:

```text
ABCDEF\r\n
GHIJKL
\e[?69h
\e[3;5s
\e[1;1H   # left of the left margin
\e[\s@
\e[2\sA
```

Expected screen:

```text
|ABCDEF|
|GHIJKL|
cursor 1,1
```

### SL-9: Nor above the scrolling region

Input, one step per line:

```text
ABCDE\r\n
FGHIJ\r\n
KLMNO
\e[2;3r
\e[1;3H   # above the top margin
\e[\s@
```

Expected screen:

```text
|ABCDE|
|FGHIJ|
|KLMNO|
cursor 1,3
```

---

This is the Markdown version of <https://control-codes.page/csi/sl/>. 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>.
