End DCS/OSC strings at any ESC, default the external terminal to Terminal.app

A string with no ST swallowed every later sequence, RIS included, and
Clear Screen could not recover the tab. Any ESC now ends it, as in xterm,
and the sequence it starts is dispatched. The shipped setting needed Kitty.

Also: REP, SCOSC/SCORC, DECOM-relative CPR, `:` subparams and more DECRQM
modes answered; new rows inserted in one edit; controls stripped from
pastes; wide-char mouse columns.
This commit is contained in:
Vaclav Tvrdik
2026-11-01 20:22:56 +01:00
parent 93253aa564
commit 2abbc503cf
9 changed files with 264 additions and 55 deletions

View File

@@ -357,7 +357,13 @@ command = make_command(terminal)
stream.feed("".join("\x1b[31mr{}\x1b[0m\r\n".format(i) for i in range(300)))
command.update_lines(None, terminal)
terminal.screen.dirty.clear()
assert set(command.state.archived_chunks) == {0}, command.state.archived_chunks
# rows new to the view went in as one insert, coloured straight onto their
# chunk keys; only row 0 and the two coloured screen rows have a key of their own
assert set(command.state.archived_chunks) == {0, 1}, command.state.archived_chunks
assert len(terminal.view.regions["vterm@1@vterm.red.default"][1]) == terminal.offset - CHUNK
row_keys = [k for k in terminal.view.regions if k.startswith("vterm#")]
assert len(row_keys) == 3, row_keys
assert terminal.view.text.split("\n")[:3] == ["r0", "r1", "r2"], terminal.view.text[:20]
command.trim_history(None, terminal)
assert "vterm@0@vterm.red.default" not in terminal.view.regions, "trimmed chunk key left behind"
assert 0 not in command.state.archived_chunks
@@ -665,7 +671,15 @@ terminal.view.window_to_text = lambda xy: terminal.view.text_point(2, 4)
mouse_release.run(None, event)
assert terminal.process.written == "\x1b[<0;1;1M\x1b[<32;5;3M\x1b[<0;5;3m", repr(terminal.process.written)
print("click tests passed (first click focuses only; press, drag and release report)")
# the column is counted in cells: a wide char before the point takes two
terminal.process.writes.clear()
terminal.view.text = "\u65e5\u672cx \nr1 \nr2 \nr3 \nr4 \n"
terminal.view.window_to_text = lambda xy: terminal.view.text_point(0, 2)
mouse_press.run(None, event)
mouse_release.run(None, event)
assert terminal.process.written == "\x1b[<0;5;1M\x1b[<0;5;1m", repr(terminal.process.written)
print("click tests passed (first click focuses only; press, drag and release report; wide chars)")
# entering the alt screen folds the scrollback away (kitty hides it); leaving
@@ -751,4 +765,12 @@ line[0] = Char("r", "red", "default", False, False, False, False, True)
runs = [r for r in segment_buffer_line(line) if r[1] != r[2]]
assert runs == [("r", 0, 1, "reverse_default", "red", "")], runs
print("segmenter tests passed (leading gap, wide char, orphan filler, reverse)")
# a cell can hold two code points (a mark NFC could not compose): the spans
# that follow are offsets into the row text, so they move by two
line = StaticDefaultDict(Char(" "))
line[0] = Char("q\u0301", "default", "default")
line[1] = Char("x", "blue", "default")
runs = [r for r in segment_buffer_line(line) if r[1] != r[2]]
assert runs == [("q\u0301", 0, 2, "default", "default", ""), ("x", 2, 3, "blue", "default", "")], runs
print("segmenter tests passed (leading gap, wide char, orphan filler, reverse, two-code-point cell)")