Skip to content

Silently consume unrecognized or malformed escape sequences#5738

Merged
stefanhaller merged 3 commits into
masterfrom
swallow-unknown-escape-sequences
Jun 30, 2026
Merged

Silently consume unrecognized or malformed escape sequences#5738
stefanhaller merged 3 commits into
masterfrom
swallow-unknown-escape-sequences

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

Lazygit only recognizes a handful of escape sequences (mainly for colors, erasing to the end of the line, and OSC-8 hyperlinks). It would render all other ones as literal text in the UI, which doesn't make sense. This wasn't a problem so far because other sequences tend not to occur in pager output, but we are going to add ConPTY support for Windows in a later PR, and ConPTY does emit a lot of those escape sequences, which would then show up as junk in the UI.

While we're at it, also swallow malformed escape sequences instead of printing them verbatim.

stefanhaller and others added 3 commits June 30, 2026 09:18
The escape interpreter errors on anything outside the handful of
sequences it understands (SGR, EL, OSC 8 hyperlinks), and view.go then
renders the unparsed bytes as text cells.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A text-mode escape interpreter can't do anything meaningful with cursor
positioning, DEC private modes, or terminal resets — but it must still
consume them, not print them as literal text. Before this change, any
sequence outside SGR / EL / OSC-8 errored out of parseOne, and view.go
rendered the unparsed bytes as visible cells. On Windows this would show
up as junk at the start of main-panel output once we add PTY support
using ConPTY, because ConPTY's session-init stream is full of such
sequences.

Three additions to the state machine:

- stateEscape: a single byte in 0x30–0x7E after ESC (e.g. ESC c = RIS)
  is a complete Fs/Fp sequence per ECMA-48; consume and reset.
- stateCSI: accept the DEC private-mode prefix bytes (<, =, >, ?), and
  accept a CSI final byte (0x40–0x7E) immediately after [ as the end of
  a zero-param sequence.
- stateParams: accept any CSI final byte we don't implement as the end
  of the sequence rather than a parse error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the previous commit, the escape interpreter still had five paths
that returned an error from parseOne, which view.go handles by rendering
whatever bytes it had accumulated as literal cells. Each of these is a
case where silently consuming the sequence is strictly better than
leaking garbage.

- ';' as the first CSI byte: '\x1b[;5H' is a valid sequence (row
  defaults to 1) but we errored on the leading ';'.
- Intermediate bytes in CSI ('\x1b[0 q' = DECSCUSR): the sequence ends
  in a final byte we don't implement, so consume and drop.
- Malformed SGR params (empty slot like '\x1b[1;;m'): if outputCSI
  fails mid-parse, reset state instead of re-emitting the sequence.
- OSC 8 that isn't actually OSC 8 ('\x1b]8x...'): treat as an OSC we
  don't understand and skip to its terminator rather than error-
  resetting mid-sequence, which used to leave the rest of the OSC body
  to be printed as text.
- The sanity-check overflow paths (too many params, param too long)
  now switch to a 'discard until final byte' state rather than
  returning the accumulated bytes.

A new stateCSIDiscard centralizes the 'consume bytes until the CSI
final' behavior used by both the intermediate-byte and overflow paths.
errCSITooLong and errOSCParseError are gone with their only callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@stefanhaller stefanhaller added the bug Something isn't working label Jun 30, 2026
@stefanhaller stefanhaller enabled auto-merge June 30, 2026 07:24
@stefanhaller stefanhaller merged commit 62e1622 into master Jun 30, 2026
22 of 25 checks passed
@stefanhaller stefanhaller deleted the swallow-unknown-escape-sequences branch June 30, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant