^[control-codes live in libghostty-vt

Parsing

C0 in sequences Controls Inside Sequences

What a control character does when it arrives in the middle of a control sequence or a string.

CSI P… BS P… F
DEC STD 070
§3.5.1.1 Precedence of Control Functions, p. 3-18
xterm
Definitions

A control sequence is not always unbroken. A program can be interrupted, or a sequence can be written in two pieces with something else in between, and the terminal then meets a control character part-way through one.

In a control sequence a C0 control is carried out at once, and the sequence then carries on as if it had not been there: CSI 2 BS C moves back one column and then forward two. DEC STD 070 asks for this: control characters take precedence over the sequences around them (§3.5.1.1, p. 3-18). xterm’s parse tables do it, giving each C0 control its usual action in every state of a control sequence (csi_table). DEL, 7F, is ignored. The exceptions are CAN and SUB, which abandon the sequence, and ESC, which abandons it and starts another.

In a string, the text of an OSC, DCS, SOS, PM or APC, xterm ignores every C0 control except the ones that end or abandon it: they are left out of the string, so OSC 2 ; a LF b ST sets the title ab, and so is DEL (sos_table). BEL ends an OSC, as xterm has always allowed, and rings the bell inside a DCS. An ESC ends the string only as the first half of ST, ESC \. Followed by anything else, it abandons the string and starts an escape sequence, and the string is thrown away: xterm keeps collecting it only while it waits for the byte after the ESC, and carries out an OSC only on its terminator (doparsing).

libghostty-vt does the same, with two exceptions. It keeps a DEL in a string, so it ends up in the title. And an ESC that is not part of ST ends an OSC and carries it out before starting the escape sequence, so a title cut short that way is still set. Those cases are known differences.

Validation

CTL-1: Backspace inside a control sequence

ABCD
\e[2\bC   # BS, then CUF 2
X
|ABCD_X__|
cursor 1,7

CTL-2: Carriage return

ABCD
\e[\r2C   # CR, then CUF 2
X
|ABXD____|
cursor 1,4

CTL-3: Line feed, between the parameters

AB
\e[\n2C
X
|AB______|
|____X___|
cursor 2,6

CTL-4: DEL is ignored

AB
\e[1\x7f;5H
X
|AB__X___|
cursor 1,6

CTL-5: Controls are left out of a string

\e]2;a\bb\nc\x00d\x1fe\a
|________|
title "abcde"

CTL-6: And so is DEL

\e]2;a\x7fb\a
|________|
title "ab"

CTL-7: An ESC abandons a string

\e]2;cut\e7   # ESC 7, DECSC, not ST
X
|X_______|
title ""