Terminal: wrap table cells by grapheme width

This commit is contained in:
Vincent Koc
2026-03-10 10:50:11 -04:00
parent 4efe7a4dcd
commit 1ec49e33f3

View File

@@ -1,5 +1,5 @@
import { displayString } from "../utils.js";
import { visibleWidth } from "./ansi.js";
import { splitGraphemes, visibleWidth } from "./ansi.js";
type Align = "left" | "right" | "center";
@@ -94,13 +94,15 @@ function wrapLine(text: string, width: number): string[] {
}
}
const cp = text.codePointAt(i);
if (!cp) {
break;
let nextEsc = text.indexOf(ESC, i);
if (nextEsc < 0) {
nextEsc = text.length;
}
const ch = String.fromCodePoint(cp);
tokens.push({ kind: "char", value: ch });
i += ch.length;
const plainChunk = text.slice(i, nextEsc);
for (const grapheme of splitGraphemes(plainChunk)) {
tokens.push({ kind: "char", value: grapheme });
}
i = nextEsc;
}
const firstCharIndex = tokens.findIndex((t) => t.kind === "char");
@@ -139,7 +141,7 @@ function wrapLine(text: string, width: number): string[] {
const bufToString = (slice?: Token[]) => (slice ?? buf).map((t) => t.value).join("");
const bufVisibleWidth = (slice: Token[]) =>
slice.reduce((acc, t) => acc + (t.kind === "char" ? 1 : 0), 0);
slice.reduce((acc, t) => acc + (t.kind === "char" ? visibleWidth(t.value) : 0), 0);
const pushLine = (value: string) => {
const cleaned = value.replace(/\s+$/, "");
@@ -195,12 +197,13 @@ function wrapLine(text: string, width: number): string[] {
}
continue;
}
if (bufVisible + 1 > width && bufVisible > 0) {
const charWidth = visibleWidth(ch);
if (bufVisible + charWidth > width && bufVisible > 0) {
flushAt(lastBreakIndex);
}
buf.push(token);
bufVisible += 1;
bufVisible += charWidth;
if (isBreakChar(ch)) {
lastBreakIndex = buf.length;
}