Improve logic

This commit is contained in:
Leonabcd123 2025-12-22 18:19:54 +02:00
parent 3353374bb9
commit 9c2d7200ca
3 changed files with 20 additions and 13 deletions

View file

@ -217,7 +217,7 @@ async function joinOverlappingHints(
activeWordLetters: NodeListOf<Element>,
hintElements: HTMLCollection,
): Promise<void> {
const isWordRightToLeft = Strings.isWordRightToLeft(
const [isWordRightToLeft, _isFullMatch] = Strings.isWordRightToLeft(
TestWords.words.getCurrent(),
TestState.isLanguageRightToLeft,
TestState.isDirectionReversed,

View file

@ -405,7 +405,7 @@ export class Caret {
isLanguageRightToLeft: boolean;
isDirectionReversed: boolean;
}): { left: number; top: number; width: number } {
const isWordRTL = isWordRightToLeft(
const [isWordRTL, isFullMatch] = isWordRightToLeft(
options.wordText,
options.isLanguageRightToLeft,
options.isDirectionReversed,
@ -455,7 +455,7 @@ export class Caret {
// yes, this is all super verbose, but its easier to maintain and understand
if (isWordRTL) {
if (options.wordText) options.word.addClass("wordRtl");
if (isFullMatch) options.word.addClass("wordRtl");
let afterLetterCorrection = 0;
if (options.side === "afterLetter") {
if (this.isFullWidth()) {

View file

@ -214,23 +214,24 @@ export function replaceControlCharacters(textToClear: string): string {
* @param word the word to check for RTL characters
* @returns true if the word contains RTL characters, false otherwise
*/
function hasRTLCharacters(word: string): boolean {
function hasRTLCharacters(word: string): [boolean, number] {
if (!word || word.length === 0) {
return false;
return [false, 0];
}
// This covers Arabic, Farsi, Urdu, and other RTL scripts
const rtlPattern =
/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/;
/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]+/;
return rtlPattern.test(word);
const result = rtlPattern.exec(word);
return [result !== null, result?.[0].length ?? 0];
}
/**
* Cache for word direction to avoid repeated calculations per word
* Keyed by the stripped core of the word; can be manually cleared when needed
*/
let wordDirectionCache: Map<string, boolean> = new Map();
let wordDirectionCache: Map<string, [boolean, number]> = new Map();
export function clearWordDirectionCache(): void {
wordDirectionCache.clear();
@ -240,25 +241,31 @@ export function isWordRightToLeft(
word: string | undefined,
languageRTL: boolean,
reverseDirection?: boolean,
): boolean {
): [boolean, boolean] {
if (word === undefined || word.length === 0) {
return reverseDirection ? !languageRTL : languageRTL;
return reverseDirection ? [!languageRTL, false] : [languageRTL, false];
}
// Strip leading/trailing punctuation and whitespace so attached opposite-direction
// punctuation like "word؟" or "،word" doesn't flip the direction detection
// and if only punctuation/symbols/whitespace, use main language direction
const core = word.replace(/^[\p{P}\p{S}\s]+|[\p{P}\p{S}\s]+$/gu, "");
if (core.length === 0) return reverseDirection ? !languageRTL : languageRTL;
if (core.length === 0)
return reverseDirection ? [!languageRTL, false] : [languageRTL, false];
// cache by core to handle variants like "word" vs "word؟"
const cached = wordDirectionCache.get(core);
if (cached !== undefined) return reverseDirection ? !cached : cached;
if (cached !== undefined)
return reverseDirection
? [!cached[0], false]
: [cached[0], cached[1] === word.length];
const result = hasRTLCharacters(core);
wordDirectionCache.set(core, result);
return reverseDirection ? !result : result;
return reverseDirection
? [!result[0], false]
: [result[0], result[1] === word.length];
}
export const CHAR_EQUIVALENCE_SETS = [