Skip to content

Commit 994616b

Browse files
Leonabcd123Miodec
andauthored
refactor: word navigation correctness approach (@Leonabcd123, @Miodec) (#8141)
To reproduce (`nospace`): 1. Set funbox to `nospace` 2. Type the first letter of the first word incorrectly 3. Complete the word 4. Notice how there isn't a red underline under it To reproduce (newline): 1. Switch to custom mode 2. Change text to: ``` asdf asdf ``` 3. Type `aaaa` 4. Press `enter` 5. Notice how there isn't a red underline under it --------- Co-authored-by: Miodec <jack@monkeytype.com>
1 parent 0bd1b21 commit 994616b

3 files changed

Lines changed: 60 additions & 58 deletions

File tree

frontend/__tests__/input/helpers/validation.spec.ts

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, it, expect, vi, beforeEach, afterAll } from "vitest";
22
import {
33
isCharCorrect,
4+
isWordCorrect,
45
shouldInsertSpaceCharacter,
56
} from "../../../src/ts/input/helpers/validation";
67
import { __testing } from "../../../src/ts/config/testing";
@@ -76,14 +77,6 @@ describe("isCharCorrect", () => {
7677

7778
describe("Space Handling", () => {
7879
it.each([
79-
["returns true at the end of a correct word", " ", "word", "word", true],
80-
[
81-
"returns false at the end of an incorrect word",
82-
" ",
83-
"worx",
84-
"word",
85-
false,
86-
],
8780
["returns false in the middle of a word", " ", "wor", "word", false],
8881
["returns false at the start of a word", " ", "", "word", false],
8982
[
@@ -105,34 +98,33 @@ describe("isCharCorrect", () => {
10598
});
10699
});
107100

108-
describe("newline handling", () => {
101+
describe("Space Handling at the end of a word", () => {
109102
it.each([
103+
["returns true at the end of a correct word", " ", "word", "word", true],
110104
[
111-
"returns true at the end of a correct word",
112-
"\n",
105+
"returns false at the end of an incorrect word",
106+
" ",
107+
"worx",
113108
"word",
114-
"word\n",
115-
true,
109+
false,
116110
],
117111
[
118-
"returns false at the end of an incorrect word",
112+
"returns true when committing a word with a newline",
119113
"\n",
120-
"worx",
114+
"word",
121115
"word\n",
122-
false,
116+
true,
123117
],
124-
["returns false in the middle of a word", "\n", "wor", "word\n", false],
125-
["returns false at the start of a word", "\n", "", "word\n", false],
126118
[
127-
"returns false when longer than a word",
119+
"returns false when committing an incorrect word with a newline",
128120
"\n",
129-
"wordwordword",
121+
"xord",
130122
"word\n",
131123
false,
132124
],
133125
])("%s", (_desc, char, input, word, expected) => {
134126
expect(
135-
isCharCorrect({
127+
isWordCorrect({
136128
data: char,
137129
inputValue: input,
138130
targetWord: word,
@@ -161,17 +153,6 @@ describe("isCharCorrect", () => {
161153
},
162154
);
163155
});
164-
165-
it("throws error if data is undefined", () => {
166-
expect(() =>
167-
isCharCorrect({
168-
data: undefined as any,
169-
inputValue: "val",
170-
targetWord: "word",
171-
correctShiftUsed: true,
172-
}),
173-
).toThrow();
174-
});
175156
});
176157

177158
describe("shouldInsertSpaceCharacter", () => {

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { goToNextWord } from "../helpers/word-navigation";
3333
import { onBeforeInsertText } from "./before-insert-text";
3434
import {
3535
isCharCorrect,
36+
isWordCorrect,
3637
shouldInsertSpaceCharacter,
3738
} from "../helpers/validation";
3839
import { getCurrentInput, logTestEvent } from "../../test/events/data";
@@ -134,7 +135,7 @@ export async function onInsertText(options: OnInsertTextParams): Promise<void> {
134135
data,
135136
currentWord[(testInput + data).length - 1] ?? "",
136137
);
137-
const correct =
138+
const charCorrect =
138139
funboxCorrect ??
139140
isCharCorrect({
140141
data,
@@ -143,6 +144,26 @@ export async function onInsertText(options: OnInsertTextParams): Promise<void> {
143144
correctShiftUsed,
144145
});
145146

147+
// word navigation check
148+
const noSpaceForce =
149+
isFunboxActiveWithProperty("nospace") &&
150+
(testInput + data).length === TestWords.words.getCurrentText().length;
151+
// does this input try to move to the next word (before removeLastChar can block it)
152+
const goingToNextWord =
153+
((charIsSpace || charIsNewline) && !shouldInsertSpace) || noSpaceForce;
154+
155+
// when moving to the next word, correctness is word-level (a correct word-completing
156+
// space has charCorrect === false, so charCorrect can't be used below)
157+
const correct = goingToNextWord
158+
? (funboxCorrect ??
159+
isWordCorrect({
160+
data,
161+
inputValue: testInput,
162+
targetWord: currentWord,
163+
correctShiftUsed,
164+
}))
165+
: charCorrect;
166+
146167
// handing cases where last char needs to be removed
147168
// this is here and not in beforeInsertText because we want to penalize for incorrect spaces
148169
// like accuracy, keypress errors, and missed words
@@ -155,7 +176,7 @@ export async function onInsertText(options: OnInsertTextParams): Promise<void> {
155176
removeLastChar = true;
156177
}
157178

158-
if (!isSpace(data) && correctShiftUsed === false) {
179+
if (!charIsSpace && correctShiftUsed === false) {
159180
removeLastChar = true;
160181
visualInputOverride = undefined;
161182
incrementIncorrectShiftsInARow();
@@ -169,13 +190,8 @@ export async function onInsertText(options: OnInsertTextParams): Promise<void> {
169190
resetIncorrectShiftsInARow();
170191
}
171192

172-
// word navigation check
173-
const noSpaceForce =
174-
isFunboxActiveWithProperty("nospace") &&
175-
(testInput + data).length === TestWords.words.getCurrentText().length;
176-
const shouldGoToNextWord =
177-
!removeLastChar &&
178-
(((charIsSpace || charIsNewline) && !shouldInsertSpace) || noSpaceForce);
193+
// stop-on-error and opposite shift mode can block navigation, so this is derived after removeLastChar
194+
const shouldGoToNextWord = goingToNextWord && !removeLastChar;
179195

180196
if (Config.keymapMode === "react") {
181197
flash(data, correct);

frontend/src/ts/input/helpers/validation.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,37 @@ export function isCharCorrect(options: {
1818
const { data, inputValue, targetWord, correctShiftUsed } = options;
1919

2020
if (Config.mode === "zen") return true;
21-
2221
if (correctShiftUsed === false) return false;
2322

24-
if (data === undefined) {
25-
throw new Error("Failed to check if char is correct - data is undefined");
26-
}
27-
28-
if (isSpace(data)) {
29-
return inputValue === targetWord;
30-
}
31-
32-
if (data === "\n") {
33-
return inputValue + data === targetWord;
34-
}
35-
3623
const targetChar = targetWord[inputValue.length];
3724

3825
if (targetChar === undefined) {
3926
return false;
4027
}
4128

42-
if (data === targetChar) {
43-
return true;
44-
}
29+
return data === targetChar;
30+
}
31+
32+
/**
33+
* Check if the input data is correct
34+
* @param options - Options object
35+
* @param options.inputValue - Current input value (use getCurrentInput(), not input element value)
36+
* @param options.targetWord - Target word
37+
* @param options.correctShiftUsed - Whether the correct shift state was used. Null means disabled
38+
*/
39+
export function isWordCorrect(options: {
40+
data: string;
41+
inputValue: string;
42+
targetWord: string;
43+
correctShiftUsed: boolean | null; //null means disabled
44+
}): boolean {
45+
const { data, inputValue, targetWord, correctShiftUsed } = options;
46+
47+
if (Config.mode === "zen") return true;
48+
if (correctShiftUsed === false) return false;
4549

46-
return false;
50+
const finalInputValue = inputValue + (isSpace(data) ? "" : data);
51+
return finalInputValue === targetWord;
4752
}
4853

4954
/**

0 commit comments

Comments
 (0)