Skip to content

Commit ab7ea19

Browse files
committed
fix(input): support oe and ae ligature input
1 parent 0ba7f62 commit ab7ea19

3 files changed

Lines changed: 87 additions & 0 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
getMatchingLigatureOverride,
4+
shouldIgnoreLigatureCompletion,
5+
} from "../../../src/ts/input/helpers/ligatures";
6+
7+
describe("insert-text ligature input overrides", () => {
8+
it.each([
9+
["o", "œ", "œ"],
10+
["O", "Œ", "Œ"],
11+
["a", "æ", "æ"],
12+
["A", "Æ", "Æ"],
13+
])(
14+
"normalizes '%s' to '%s' when target is '%s'",
15+
(data, target, expected) => {
16+
expect(getMatchingLigatureOverride(data, target)).toBe(expected);
17+
},
18+
);
19+
20+
it.each([
21+
["e", "œ", "œuvre"],
22+
["E", "Œ", "ŒUVRE"],
23+
["e", "æ", "æther"],
24+
["E", "Æ", "ÆTHER"],
25+
])("ignores completion '%s' after '%s'", (data, input, word) => {
26+
expect(shouldIgnoreLigatureCompletion(data, input, word)).toBe(true);
27+
});
28+
29+
it("does not normalize unrelated input", () => {
30+
expect(getMatchingLigatureOverride("e", "œ")).toBeNull();
31+
expect(shouldIgnoreLigatureCompletion("u", "œ", "œuvre")).toBe(false);
32+
});
33+
});

frontend/src/ts/input/handlers/insert-text.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ import {
3737
isCharCorrect,
3838
shouldInsertSpaceCharacter,
3939
} from "../helpers/validation";
40+
import {
41+
getMatchingLigatureOverride,
42+
shouldIgnoreLigatureCompletion,
43+
} from "../helpers/ligatures";
4044

4145
const charOverrides = new Map<string, string>([
4246
["…", "..."],
@@ -82,6 +86,18 @@ export async function onInsertText(options: OnInsertTextParams): Promise<void> {
8286
return;
8387
}
8488

89+
if (
90+
shouldIgnoreLigatureCompletion(
91+
options.data,
92+
TestInput.input.current,
93+
TestWords.words.getCurrentText(),
94+
)
95+
) {
96+
setInputElementValue(inputValue.slice(0, -options.data.length));
97+
TestInput.input.syncWithInputElement();
98+
return;
99+
}
100+
85101
const charOverride = charOverrides.get(options.data);
86102
if (
87103
charOverride !== undefined &&
@@ -301,6 +317,12 @@ function normalizeDataAndUpdateInputIfNeeded(
301317
) {
302318
replaceInputElementLastValueChar(targetChar);
303319
normalizedData = targetChar;
320+
} else {
321+
const ligatureOverride = getMatchingLigatureOverride(data, targetChar);
322+
if (ligatureOverride !== null) {
323+
replaceInputElementLastValueChar(ligatureOverride);
324+
normalizedData = ligatureOverride;
325+
}
304326
}
305327
return normalizedData;
306328
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const ligatureInputOverrides = new Map<string, string>([
2+
["œ", "oe"],
3+
["Œ", "OE"],
4+
["æ", "ae"],
5+
["Æ", "AE"],
6+
]);
7+
8+
export function getMatchingLigatureOverride(
9+
data: string,
10+
targetChar: string | undefined,
11+
): string | null {
12+
if (targetChar === undefined) return null;
13+
14+
const override = ligatureInputOverrides.get(targetChar);
15+
if (override?.[0] !== data) return null;
16+
17+
return targetChar;
18+
}
19+
20+
export function shouldIgnoreLigatureCompletion(
21+
data: string,
22+
testInput: string,
23+
currentWord: string,
24+
): boolean {
25+
const previousTargetChar = currentWord[testInput.length - 1];
26+
if (previousTargetChar === undefined) return false;
27+
28+
const override = ligatureInputOverrides.get(previousTargetChar);
29+
if (override === undefined) return false;
30+
31+
return testInput.endsWith(previousTargetChar) && data === override.slice(1);
32+
}

0 commit comments

Comments
 (0)