# Selective Erase (DECSCA, DECSED, DECSEL)

> Protect characters from erasure, then erase everything else on the line or the screen.

- **Sequence:** `CSI Ps " q`
- **Defaults:** Ps = 0
- **DEC STD 070:** [§5.11.1 Select Character Attribute, p. 5-166](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n420/mode/1up)
- **VT510:** [DECSCA—Select Character Protection Attribute](https://vt100.net/docs/vt510-rm/DECSCA.html)

DEC's way of filling in a form: draw the form's labels protected, let the
user type into the gaps, then clear just the typing and leave the form
standing. It takes three functions.

**DECSCA** (`CSI Ps " q`) chooses whether the characters written from now on
can be erased selectively. Like [SGR](https://control-codes.page/csi/sgr/index.html.md), it applies only to what is
written afterwards, and changes nothing already on the screen.

| `Ps` | Characters written afterwards |
| --- | --- |
| `0`, or left out | can be erased selectively |
| `1` | are protected: the selective erases leave them alone |
| `2` | can be erased selectively, as with `0` |

DEC STD 070 calls the attribute *Selectively Erasable*, so `1` turns it off
and `2` turns it on; the VT510 manual describes it, as this page does, by
what DECSED and DECSEL may erase.

**DECSEL** (`CSI ? Ps K`) and **DECSED** (`CSI ? Ps J`) are
[EL](https://control-codes.page/csi/el/index.html.md) and [ED](https://control-codes.page/csi/ed/index.html.md) with a `?`, and take the same `Ps`: `0`
from the cursor to the end of the line or the screen, `1` from the start to
the cursor, and `2` all of it. They erase only the characters that are not
protected. Neither is limited by the margins, nor DECSED by the scrolling
region or origin mode. DEC STD 070 defines DECSEL in
[§5.11, p. 5-159](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n413/mode/1up)
and DECSED on
[p. 5-162](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n416/mode/1up),
and the VT510 manual as [DECSEL](https://vt100.net/docs/vt510-rm/DECSEL.html)
and [DECSED](https://vt100.net/docs/vt510-rm/DECSED.html).

Plain EL and ED erase everything, protected or not. DEC STD 070 describes
the two forms of each as one that erases all characters "regardless of their
logical attributes", and one that erases only the selectively erasable ones
([§5.11.1.2, p. 5-165](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n419/mode/1up)).
xterm switches DEC protection off for the length of any erase that is not a
selective one
([`do_erase_line`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/util.c#L2406-L2429),
[`do_erase_display`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/util.c#L2438-L2500)).

DEC STD 070 says a selective erase turns a character into a space and
leaves "the character rendition and attributes associated with each
position" unchanged. xterm does not keep them: it clears the cells it erases
selectively with the same
[`ClearCells`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/screen.c#L839-L885)
that EL and ED use, which gives them the current colors and no other
rendition. libghostty-vt does the same as xterm.

The protection attribute is part of what [DECSC](https://control-codes.page/esc/decsc/index.html.md) saves and
DECRC restores.

## With guarded areas

[SPA and EPA](https://control-codes.page/esc/spa/index.html.md) protect characters too, ECMA-48's way, and the two
kinds meet in xterm's `protected_mode`, which records whichever was used
last: SPA sets it to ISO protection
([`CASE_SPA`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5367-L5372))
and DECSCA to DEC protection
([`CASE_DECSCA`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5135-L5146)).
While it is ISO, every erase respects protection, the selective ones
included. esctest2 holds that DECSED and DECSEL should not respect ISO
protection (`test_DECSED_doesNotRespectISOProtect`), but marks the test as a
known bug in xterm, which keeps it "for backward compatibility".
libghostty-vt does what xterm does.

## DECSERA

DECSERA (`CSI Pt ; Pl ; Pb ; Pr $ {`) erases the unprotected characters of
a rectangle
([DEC STD 070 §5.12.1, p. 5-172](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n426/mode/1up),
[VT510 DECSERA](https://vt100.net/docs/vt510-rm/DECSERA.html)). The
rectangle works as it does for [DECERA](https://control-codes.page/csi/decera/index.html.md). xterm turns every
character in it that DECSCA did not protect into a space and leaves the
attributes alone
([`ScrnWipeRectangle`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/screen.c#L3092-L3156)).

DEC STD 070 makes DECSERA a level 4 function. libghostty-vt identifies
itself as a VT220, level 2 (see [DA](https://control-codes.page/csi/da/index.html.md)), and has no handler for
`CSI … $ {` in `stream.zig`, so it ignores it. xterm is a VT420 by default
and implements it
([`CASE_DECSERA`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L5686-L5693)),
so DECSCA-10 expects xterm's result and is a known difference (see the
[sources](https://control-codes.page/sources/index.html.md) page).

## Validation

### DECSCA-1: DECSEL leaves protected characters

Input, one step per line:

```text
AB
\e[1"q    # protect what follows
CD
\e[0"q    # and stop
EF
\e[1;1H
\e[?K     # selective erase to the end of the line
```

Expected screen:

```text
|__CD__|
cursor 1,1
```

### DECSCA-2: From the start of the line

Input, one step per line:

```text
AB\e[1"qCD\e[0"qEF
\e[1;5H
\e[?1K
```

Expected screen:

```text
|__CD_F|
cursor 1,5
```

### DECSCA-3: DECSED below and above

Input, one step per line:

```text
AB\e[1"qCD\e[0"qEF\r\n
GHIJKL
\e[2;3H
\e[?1J    # selective erase from the start of the screen
```

Expected screen:

```text
|__CD__|
|___JKL|
```

### DECSCA-4: EL erases protected characters too

Input, one step per line:

```text
AB\e[1"qCD\e[0"qEF\r\n
GHIJKL
\e[1;1H
\e[K      # EL, not selective
\e[2;1H
\e[?K     # DECSEL on a line with nothing protected
```

Expected screen:

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

### DECSCA-5: Parameter 2 is the same as 0

Input, one step per line:

```text
AB\e[1"qCD
\e[2"q    # selectively erasable again
EF
\e[1;1H
\e[?2K
```

Expected screen:

```text
|__CD__|
```

### DECSCA-6: DECSC saves the attribute

Input, one step per line:

```text
\e[1"q    # protect
\e7       # save the cursor
\e[0"q    # stop protecting
\e8       # restore: protecting again
CD
\e[0"q
EF
\e[1;1H
\e[?2K
```

Expected screen:

```text
|CD____|
```

### DECSCA-7: Erased cells lose their rendition

Input, one step per line:

```text
\e[1;31m  # bold red
AB
\e[0m
\e[1;1H
\e[?2K
```

Expected screen:

```text
|______|
attr 1,1 plain
attr 1,2 plain
```

### DECSCA-8: Guarded text survives DECSED too

Input, one step per line:

```text
AB
\eV       # SPA
CD
\eW       # EPA
EF
\e[?2J
```

Expected screen:

```text
|__CD__|
|______|
```

### DECSCA-9: After DECSCA, ED erases guarded text

Input, one step per line:

```text
AB\eVCD\eW # CD guarded, ISO protection
\e[1"q    # DEC protection is now in force
EF
\e[0"q
\e[2J     # ED ignores DEC protection
```

Expected screen:

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

### DECSCA-10: DECSERA keeps protected characters

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

Input, one step per line:

```text
AB\e[1"qCD\e[0"qEF
\e[1;1;1;6\x24{ # DECSERA over the whole line
```

Expected screen:

```text
|__CD__|
```

---

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