@@ -45,6 +45,9 @@ const rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
|
||||
const WIDTH_CACHE_SIZE = 512;
|
||||
const widthCache = new Map<string, number>();
|
||||
|
||||
const cjkBreakRegex =
|
||||
/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}\p{Script_Extensions=Bopomofo}]/u;
|
||||
|
||||
function isPrintableAscii(str: string): boolean {
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const code = str.charCodeAt(i);
|
||||
@@ -605,9 +608,18 @@ function splitIntoTokensWithAnsi(text: string): string[] {
|
||||
const tokens: string[] = [];
|
||||
let current = "";
|
||||
let pendingAnsi = ""; // ANSI codes waiting to be attached to next visible content
|
||||
let inWhitespace = false;
|
||||
let currentKind: "space" | "word" | null = null;
|
||||
let i = 0;
|
||||
|
||||
const flushCurrent = (): void => {
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
tokens.push(current);
|
||||
current = "";
|
||||
currentKind = null;
|
||||
};
|
||||
|
||||
while (i < text.length) {
|
||||
const ansiResult = extractAnsiCode(text, i);
|
||||
if (ansiResult) {
|
||||
@@ -617,29 +629,48 @@ function splitIntoTokensWithAnsi(text: string): string[] {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char = text[i];
|
||||
const charIsSpace = char === " ";
|
||||
|
||||
if (charIsSpace !== inWhitespace && current) {
|
||||
// Switching between whitespace and non-whitespace, push current token
|
||||
tokens.push(current);
|
||||
current = "";
|
||||
let end = i;
|
||||
while (end < text.length && !extractAnsiCode(text, end)) {
|
||||
end++;
|
||||
}
|
||||
|
||||
// Attach any pending ANSI codes to this visible character
|
||||
if (pendingAnsi) {
|
||||
current += pendingAnsi;
|
||||
pendingAnsi = "";
|
||||
for (const { segment } of graphemeSegmenter.segment(text.slice(i, end))) {
|
||||
const segmentIsSpace = segment === " ";
|
||||
if (!segmentIsSpace && cjkBreakRegex.test(segment)) {
|
||||
flushCurrent();
|
||||
const token = pendingAnsi + segment;
|
||||
pendingAnsi = "";
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
const segmentKind = segmentIsSpace ? "space" : "word";
|
||||
if (current && currentKind !== segmentKind) {
|
||||
flushCurrent();
|
||||
}
|
||||
|
||||
// Attach any pending ANSI codes to this visible character
|
||||
if (pendingAnsi) {
|
||||
current += pendingAnsi;
|
||||
pendingAnsi = "";
|
||||
}
|
||||
|
||||
currentKind = segmentKind;
|
||||
current += segment;
|
||||
}
|
||||
|
||||
inWhitespace = charIsSpace;
|
||||
current += char;
|
||||
i++;
|
||||
i = end;
|
||||
}
|
||||
|
||||
// Handle any remaining pending ANSI codes (attach to last token)
|
||||
if (pendingAnsi) {
|
||||
current += pendingAnsi;
|
||||
if (current) {
|
||||
current += pendingAnsi;
|
||||
} else if (tokens.length > 0) {
|
||||
tokens[tokens.length - 1] += pendingAnsi;
|
||||
} else {
|
||||
current = pendingAnsi;
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
|
||||
Reference in New Issue
Block a user