Merge pull request #5585 from haoqixu/fix-editor-cjk-wrap
fix(tui): wrap CJK text at character boundaries in editor
This commit is contained in:
@@ -4,7 +4,14 @@ import { decodePrintableKey, matchesKey } from "../keys.ts";
|
|||||||
import { KillRing } from "../kill-ring.ts";
|
import { KillRing } from "../kill-ring.ts";
|
||||||
import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.ts";
|
import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.ts";
|
||||||
import { UndoStack } from "../undo-stack.ts";
|
import { UndoStack } from "../undo-stack.ts";
|
||||||
import { getGraphemeSegmenter, getWordSegmenter, isWhitespaceChar, truncateToWidth, visibleWidth } from "../utils.ts";
|
import {
|
||||||
|
cjkBreakRegex,
|
||||||
|
getGraphemeSegmenter,
|
||||||
|
getWordSegmenter,
|
||||||
|
isWhitespaceChar,
|
||||||
|
truncateToWidth,
|
||||||
|
visibleWidth,
|
||||||
|
} from "../utils.ts";
|
||||||
import { findWordBackward, findWordForward } from "../word-navigation.ts";
|
import { findWordBackward, findWordForward } from "../word-navigation.ts";
|
||||||
import { SelectList, type SelectListLayoutOptions, type SelectListTheme } from "./select-list.ts";
|
import { SelectList, type SelectListLayoutOptions, type SelectListTheme } from "./select-list.ts";
|
||||||
|
|
||||||
@@ -174,13 +181,21 @@ export function wordWrapLine(line: string, maxWidth: number, preSegmented?: Intl
|
|||||||
// Advance.
|
// Advance.
|
||||||
currentWidth += gWidth;
|
currentWidth += gWidth;
|
||||||
|
|
||||||
// Record wrap opportunity: whitespace followed by non-whitespace.
|
// Record wrap opportunity: whitespace followed by non-whitespace
|
||||||
// Multiple spaces join (no break between them); the break point is
|
// (multiple spaces join; the break point is after the last space),
|
||||||
// after the last space before the next word.
|
// or at a boundary where either side is CJK (CJK allows breaking
|
||||||
|
// between any adjacent characters).
|
||||||
const next = segments[i + 1];
|
const next = segments[i + 1];
|
||||||
if (isWs && next && (isPasteMarker(next.segment) || !isWhitespaceChar(next.segment))) {
|
if (isWs && next && (isPasteMarker(next.segment) || !isWhitespaceChar(next.segment))) {
|
||||||
wrapOppIndex = next.index;
|
wrapOppIndex = next.index;
|
||||||
wrapOppWidth = currentWidth;
|
wrapOppWidth = currentWidth;
|
||||||
|
} else if (!isWs && next && !isWhitespaceChar(next.segment)) {
|
||||||
|
const isCjk = !isPasteMarker(grapheme) && cjkBreakRegex.test(grapheme);
|
||||||
|
const nextIsCjk = !isPasteMarker(next.segment) && cjkBreakRegex.test(next.segment);
|
||||||
|
if (isCjk || nextIsCjk) {
|
||||||
|
wrapOppIndex = next.index;
|
||||||
|
wrapOppWidth = currentWidth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
|
|||||||
const WIDTH_CACHE_SIZE = 512;
|
const WIDTH_CACHE_SIZE = 512;
|
||||||
const widthCache = new Map<string, number>();
|
const widthCache = new Map<string, number>();
|
||||||
|
|
||||||
const cjkBreakRegex =
|
export const cjkBreakRegex =
|
||||||
/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}\p{Script_Extensions=Bopomofo}]/u;
|
/[\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 {
|
function isPrintableAscii(str: string): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user