fix(copy): measure the characters at a wrap by code point, not UTF-16 unit
isWide looked at a single UTF-16 code unit, so an astral character at the break (CJK Ext-B, emoji) was a lone surrogate of width 1 and the rejoin inserted a space between two wide characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
da17bc2d0d
commit
f93fad61ab
2 files changed
+13
-3
No files matched your search
@@ -208,6 +208,14 @@ describe("rejoinHardWraps", () => {
|
||||
expect(rejoinHardWraps(`${a}\n の続き`, { cols: 69 })).toBe(`${a}の続き`);
|
||||
});
|
||||
|
||||
it("joins astral wide characters (CJK Ext-B) without inserting a space", () => {
|
||||
const a = " " + "\u{20000}".repeat(33); // 2 + 66 = 68 cells
|
||||
expect(cellWidth(a)).toBe(68);
|
||||
expect(rejoinHardWraps(`${a}\n \u{20001}\u{20002}`, { cols: 69 })).toBe(`${a}\u{20001}\u{20002}`);
|
||||
const e = " " + "\u{1F600}".repeat(33); // emoji, also two cells each
|
||||
expect(rejoinHardWraps(`${e}\n \u{1F601}`, { cols: 69 })).toBe(`${e}\u{1F601}`);
|
||||
});
|
||||
|
||||
it("uses the selection's start column for a first line that began mid-row", () => {
|
||||
// Selection started at column 18, so the first line is only 51
|
||||
// characters but ended at column 69 of its row.
|
||||
|
||||
@@ -172,7 +172,10 @@ function toRow(text: string, offset: number): Row {
|
||||
|
||||
const lastToken = (s: string) => s.slice(s.lastIndexOf(" ") + 1);
|
||||
const firstToken = (s: string) => s.split(" ", 1)[0];
|
||||
const isWide = (ch: string | undefined) => !!ch && charWidth(ch) === 2;
|
||||
/** First / last *code point* — an astral character (CJK Ext-B, most emoji) is two UTF-16 units. */
|
||||
const firstChar = (s: string) => (s ? String.fromCodePoint(s.codePointAt(0)!) : "");
|
||||
const lastChar = (s: string) => Array.from(s.slice(-2)).pop() ?? "";
|
||||
const isWide = (ch: string) => !!ch && charWidth(ch) === 2;
|
||||
|
||||
/**
|
||||
* Re-joins rows that Claude Code broke to fit the terminal width.
|
||||
@@ -235,8 +238,7 @@ function joinerFor(prev: Row, cur: Row, head: Row, headPartial: boolean, cols: n
|
||||
const next = firstToken(cur.body);
|
||||
if (prev.end + 1 + cellWidth(next) <= width) return null;
|
||||
|
||||
const prevLast = prev.body[prev.body.length - 1];
|
||||
if (isWide(prevLast) && isWide(cur.body[0])) return "";
|
||||
if (isWide(lastChar(prev.body)) && isWide(firstChar(cur.body))) return "";
|
||||
if (prev.end === width && cellWidth(lastToken(prev.body)) + cellWidth(next) > width - cur.lead) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user