fix(tui): wordWrapLine overflow with wide chars at wrap boundary (#2082)
This commit is contained in:
@@ -58,13 +58,18 @@ export function wordWrapLine(line: string, maxWidth: number): TextChunk[] {
|
||||
|
||||
// Overflow check before advancing.
|
||||
if (currentWidth + gWidth > maxWidth) {
|
||||
if (wrapOppIndex >= 0) {
|
||||
// Backtrack to last wrap opportunity.
|
||||
if (wrapOppIndex >= 0 && currentWidth - wrapOppWidth + gWidth <= maxWidth) {
|
||||
// Backtrack to last wrap opportunity (the remaining content
|
||||
// plus the current grapheme still fits within maxWidth).
|
||||
chunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });
|
||||
chunkStart = wrapOppIndex;
|
||||
currentWidth -= wrapOppWidth;
|
||||
} else if (chunkStart < charIndex) {
|
||||
// No wrap opportunity: force-break at current position.
|
||||
// No viable wrap opportunity: force-break at current position.
|
||||
// This also handles the case where backtracking to a word
|
||||
// boundary wouldn't help because the remaining content plus
|
||||
// the current grapheme (e.g. a wide character) still exceeds
|
||||
// maxWidth.
|
||||
chunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });
|
||||
chunkStart = charIndex;
|
||||
currentWidth = 0;
|
||||
|
||||
@@ -874,6 +874,24 @@ describe("Editor component", () => {
|
||||
assert.strictEqual(chunks[1]!.text, "amet, ");
|
||||
assert.strictEqual(chunks[2]!.text, " consectetur");
|
||||
});
|
||||
|
||||
it("force-breaks when wide char after word boundary wrap still overflows", () => {
|
||||
// " " (1) + "a"*186 (186) + "你" (2) = 189 visible width
|
||||
// maxWidth = 187: backtracking to the space would leave 186 + 2 = 188 > 187,
|
||||
// so the algorithm must force-break before the wide char instead.
|
||||
const line = ` ${"a".repeat(186)}你`;
|
||||
const chunks = wordWrapLine(line, 187);
|
||||
|
||||
for (const chunk of chunks) {
|
||||
assert.ok(
|
||||
visibleWidth(chunk.text) <= 187,
|
||||
`chunk "${chunk.text.slice(0, 20)}..." has visible width ${visibleWidth(chunk.text)}, expected <= 187`,
|
||||
);
|
||||
}
|
||||
// Verify no content is lost
|
||||
const reconstructed = chunks.map((c) => line.slice(c.startIndex, c.endIndex)).join("");
|
||||
assert.strictEqual(reconstructed, line);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Kill ring", () => {
|
||||
|
||||
Reference in New Issue
Block a user