perf(runtime): reduce terminal ANSI scanning work (#27344)

Adapted from outof-place normalization work in #27170. Preserve tail behavior while avoiding repeated CSI scans and newline-density regressions.

Co-authored-by: outof-place <contact@outofplace.space>
This commit is contained in:
Neil
2026-10-10 21:00:37 -07:00
committed by GitHub
co-authored by outof-place
parent a2c2f8b859
commit b411bf33fd
2 changed files with 112 additions and 57 deletions
+87 -57
View File
@@ -1,5 +1,8 @@
import { MAX_TAIL_PENDING_ANSI_CHARS } from './terminal-tail-limits'
import { classifyTerminalEscapeIntroducer } from '../../shared/terminal-escape-introducer'
import {
classifyTerminalEscapeIntroducer,
type TerminalEscapeIntroducer
} from '../../shared/terminal-escape-introducer'
import { ownRetainedString } from '../../shared/own-retained-string'
export function parseAnsiControlSequence(
@@ -14,44 +17,56 @@ export function parseAnsiControlSequence(
| null {
// charCodeAt, not value[i]: indexing mints a one-char string on every escape.
const introducer = classifyTerminalEscapeIntroducer(value.charCodeAt(escapeIndex + 1))
const endIndex = findTerminalEscapeSequenceEnd(value, escapeIndex, introducer)
if (endIndex === -1) {
return null
}
if (introducer !== 'csi') {
return { kind: 'other', endIndex }
}
return parseTerminalCsiAtEnd(value, escapeIndex, endIndex)
}
function parseTerminalCsiAtEnd(value: string, escapeIndex: number, endIndex: number) {
const params = value.slice(escapeIndex + 2, endIndex)
const firstParamMatch = /^(\d+)/.exec(params)
return {
kind: 'csi' as const,
final: value[endIndex] ?? '',
params,
firstParam: firstParamMatch ? Number(firstParamMatch[1]) : null,
endIndex
}
}
/** The index of the sequence's last code unit, or -1 when the input ends first. */
function findTerminalEscapeSequenceEnd(
value: string,
escapeIndex: number,
introducer: TerminalEscapeIntroducer
): number {
if (introducer === 'csi') {
for (let index = escapeIndex + 2; index < value.length; index += 1) {
const code = value.charCodeAt(index)
if (code < 0x40 || code > 0x7e) {
continue
}
const params = value.slice(escapeIndex + 2, index)
const firstParamMatch = /^(\d+)/.exec(params)
return {
kind: 'csi',
final: value[index] ?? '',
params,
firstParam: firstParamMatch ? Number(firstParamMatch[1]) : null,
endIndex: index
if (code >= 0x40 && code <= 0x7e) {
return index
}
}
return null
return -1
}
if (introducer === 'osc') {
if (introducer === 'osc' || introducer === 'string') {
for (let index = escapeIndex + 2; index < value.length; index += 1) {
if (value[index] === '\u0007') {
return { kind: 'other', endIndex: index }
const code = value.charCodeAt(index)
if (code === 0x07 && introducer === 'osc') {
return index
}
if (value[index] === '\u001b' && value[index + 1] === '\\') {
return { kind: 'other', endIndex: index + 1 }
if (code === 0x1b && value.charCodeAt(index + 1) === 0x5c) {
return index + 1
}
}
return null
return -1
}
if (introducer === 'string') {
for (let index = escapeIndex + 2; index < value.length; index += 1) {
if (value[index] === '\u001b' && value[index + 1] === '\\') {
return { kind: 'other', endIndex: index + 1 }
}
}
return null
}
return { kind: 'other', endIndex: escapeIndex + 1 }
return escapeIndex + 1
}
export function hasCanonicalNumericCsiParams(params: string): boolean {
@@ -61,18 +76,19 @@ export function hasCanonicalNumericCsiParams(params: string): boolean {
export function containsTerminalVerticalLineControl(value: string): boolean {
// Only ESC can introduce a vertical control; ordinary output needs no code-unit walk.
for (let index = value.indexOf('\x1b'); index !== -1; index = value.indexOf('\x1b', index + 1)) {
const parsed = parseAnsiControlSequence(value, index)
if (!parsed) {
const introducer = classifyTerminalEscapeIntroducer(value.charCodeAt(index + 1))
const endIndex = findTerminalEscapeSequenceEnd(value, index, introducer)
if (endIndex === -1) {
return false
}
index = parsed.endIndex
if (
parsed.kind === 'csi' &&
parsed.final === 'A' &&
hasCanonicalNumericCsiParams(parsed.params)
introducer === 'csi' &&
value.charCodeAt(endIndex) === 0x41 &&
hasCanonicalNumericCsiParams(value.slice(index + 2, endIndex))
) {
return true
}
index = endIndex
}
return false
}
@@ -88,42 +104,50 @@ export function normalizeTerminalChunk(
const combined = `${pendingAnsi}${chunk}`
const parts: string[] = []
let textStart = 0
// Copy spans and skip parameter parsing for controls the preview discards.
for (let index = 0; index < combined.length; index += 1) {
const char = combined[index]
if (char === '\x1b') {
const code = combined.charCodeAt(index)
if (code >= 0x20 && code < 0x7f) {
continue
}
if (code === 0x1b) {
appendTerminalNormalizedSpan(parts, combined, textStart, index)
if (index + 1 >= combined.length) {
return { text: parts.join(''), pendingAnsi: combined.slice(index) }
}
const parsed = parseAnsiControlSequence(combined, index)
if (!parsed) {
const introducer = classifyTerminalEscapeIntroducer(combined.charCodeAt(index + 1))
const endIndex = findTerminalEscapeSequenceEnd(combined, index, introducer)
if (endIndex === -1) {
return {
text: parts.join(''),
// Own the tail so it stops pinning the consumed chunk it was sliced from.
pendingAnsi: ownRetainedString(trimPendingAnsiControl(combined.slice(index)))
}
}
if (parsed.kind === 'csi' && isTerminalPreviewLineControl(parsed)) {
// Why: Codex redraws status text with ANSI controls but no CR; keep them so the tail overwrites the prior frame.
parts.push(combined.slice(index, parsed.endIndex + 1))
if (
introducer === 'csi' &&
isTerminalPreviewLineControlFinal(combined.charCodeAt(endIndex))
) {
const parsed = parseTerminalCsiAtEnd(combined, index, endIndex)
if (isTerminalPreviewLineControl(parsed)) {
// Why: Codex redraws status text with ANSI controls but no CR; keep them so the tail overwrites the prior frame.
parts.push(combined.slice(index, endIndex + 1))
}
}
index = parsed.endIndex
index = endIndex
textStart = index + 1
continue
}
if (char === '\r' && combined[index + 1] === '\n') {
if (code === 0x0d && combined.charCodeAt(index + 1) === 0x0a) {
// CRLF keeps only its LF, which starts the next span.
appendTerminalNormalizedSpan(parts, combined, textStart, index)
parts.push('\n')
index += 1
textStart = index + 1
continue
}
const code = combined.charCodeAt(index)
if (code === 0x08 || code === 0x09 || code === 0x0a || code === 0x0d) {
appendTerminalNormalizedSpan(parts, combined, textStart, index)
parts.push(char)
textStart = index + 1
} else if (!isTerminalPreviewPrintableCodeUnit(code)) {
continue
}
if (!isTerminalPreviewPrintableCodeUnit(code)) {
appendTerminalNormalizedSpan(parts, combined, textStart, index)
textStart = index + 1
}
@@ -173,6 +197,18 @@ function trimPendingAnsiControl(value: string): string {
return `${introducer}${value.slice(-suffixBudget)}`
}
/** CSI controls that affect retained preview rows. */
function isTerminalPreviewLineControlFinal(code: number): boolean {
return (
code === 0x4b ||
code === 0x41 ||
code === 0x47 ||
code === 0x60 ||
code === 0x44 ||
code === 0x43
)
}
function isTerminalPreviewLineControl(parsed: {
final: string
params: string
@@ -185,11 +221,5 @@ function isTerminalPreviewLineControl(parsed: {
const mode = parsed.firstParam ?? 0
return mode === 0 || mode === 1 || mode === 2
}
return (
parsed.final === 'A' ||
parsed.final === 'G' ||
parsed.final === '`' ||
parsed.final === 'D' ||
parsed.final === 'C'
)
return isTerminalPreviewLineControlFinal(parsed.final.charCodeAt(0))
}
@@ -6,6 +6,31 @@ import {
import { appendNormalizedToTailBuffer } from './terminal-tail-buffer'
describe('terminal vertical-control scanning', () => {
it('scans retained CSI parameters once while normalizing', () => {
const input = `\x1b[${'1;'.repeat(2048)}A`
const charCodeAt = vi.spyOn(String.prototype, 'charCodeAt')
let normalized: ReturnType<typeof normalizeTerminalChunk>
let inspections: number
try {
normalized = normalizeTerminalChunk(input)
inspections = charCodeAt.mock.calls.length
} finally {
charCodeAt.mockRestore()
}
expect(normalized).toEqual({ text: input, pendingAnsi: '' })
expect(inspections).toBeLessThanOrEqual(input.length + 8)
})
it('preserves printable spans and carried controls across chunk boundaries', () => {
const first = normalizeTerminalChunk('漢\ud800\x1b[31m字\t\r\n\x00a\x1b[2')
expect(first).toEqual({ text: '漢\ud800字\t\na', pendingAnsi: '\x1b[2' })
expect(normalizeTerminalChunk('K\udfff😀\b\r\x7f\x9fend', first.pendingAnsi)).toEqual({
text: '\x1b[2K\udfff😀\b\rend',
pendingAnsi: ''
})
})
it.each([
['plain', 'log output '.repeat(8192), false],
['nonvertical CSI', `\x1b[31m${'log output '.repeat(8192)}\x1b[0m`, false],