From 5b403d91b350d337f743d3a99ac0637fd3c12953 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Tue, 17 Mar 2026 23:48:29 -0700 Subject: [PATCH] fix: bug in `trimForTerminal` with ansi escapes --- lib/log.ts | 2 +- lib/readme.changes.md | 5 ++++- lib/string/ansi.test.ts | 6 +++--- lib/string/ansi.ts | 5 ++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/log.ts b/lib/log.ts index 3062eea89ee677c7cce14eabeeb4c4e5c15675ca..371f23c635e10244c1f032856a9e00ad6fcb832c 100644 --- a/lib/log.ts +++ b/lib/log.ts @@ -404,7 +404,7 @@ export function createWidgetHost(env: WidgetHostOptions): WidgetHost { if (rowsLeft === 1) break; const lines = outText.split("\n").slice(0, rowsLeft); newWidgetLines.push( - ...lines.map((line) => ansi.trimToWidth(line, columns - 1)), + ...lines.map((line) => ansi.trimForTerminal(line, columns - 1)), ); next = Math.min(next, UNWRAP(internals[w]).frameTime); diff --git a/lib/readme.changes.md b/lib/readme.changes.md index 15b6d89825186302fb21bba6db0250fef73162c0..1f7958cea3e5893670833ee92c066e6bd060180d 100644 --- a/lib/readme.changes.md +++ b/lib/readme.changes.md @@ -22,7 +22,9 @@ you to give some information about where the cursor was moved to. - messages now do not imply a newline - `render` is now deprecated with no replacement. in the downstream `sitegen` - project, the entire codebase is moving to Marko after depending on both renderers. + project, the codebase is moving to Marko after depending on both renderers. +- `string/ansi` + - rename `trimToWidth` to `trimForTerminal` ### features @@ -36,6 +38,7 @@ a newline. - `async.delay` handles timers longer than 23 days. - `Lru.revive` recieves bug fixes. this function previously didn't really work. +- `ansi` gets more cursor control constants ## v3 diff --git a/lib/string/ansi.test.ts b/lib/string/ansi.test.ts index 17bbe7fd5b288c5908e6da195423d3f228659c19..a0cda58633e80f10cb27b56b5d89a2ced8db1596 100644 --- a/lib/string/ansi.test.ts +++ b/lib/string/ansi.test.ts @@ -8,13 +8,13 @@ test("widthInTerminal", () => { 9, ); }); -test("trimToWidth", () => { +test("trimForTerminal", () => { assert.equal( - ansi.trimToWidth("snowmobile", 6), + ansi.trimForTerminal("snowmobile", 6), "snowmo", ); assert.equal( - ansi.trimToWidth("🏳️‍⚧️ " + ansi.style(ansi.bold, "rights"), 5), + ansi.trimForTerminal("🏳️‍⚧️ " + ansi.style(ansi.bold, "rights"), 5), "🏳️‍⚧️ " + ansi.bold + "ri" + ansi.reset, ); }); diff --git a/lib/string/ansi.ts b/lib/string/ansi.ts index 589c934873c4a0ea7ceec2b0428c9d48ac05cb4f..9ee0c24289321dcccbb5f28c50c35602a781cba2 100644 --- a/lib/string/ansi.ts +++ b/lib/string/ansi.ts @@ -277,8 +277,7 @@ export function widthInTerminal(str: string): number { * Like `str.slice(0, columns)`, but respecting width in a terminal. * preserves ansi escape sequences. */ -// TODO: rename to trimForTerminal -export function trimToWidth(str: string, columns: number): string { +export function trimForTerminal(str: string, columns: number): string { segmenter ??= new Intl.Segmenter(); let width = 0; let ansi = false; @@ -286,7 +285,7 @@ export function trimToWidth(str: string, columns: number): string { const code = str.charCodeAt(i); if (code < 32) { if (code === 0x1b) { - const esc = str.slice().match(ansiEscapes); + const esc = str.slice(i).match(ansiEscapes); if (esc) i += esc[0].length - 1, ansi = true; } continue; -- 2.54.0