Skip to content

Commit 7ec3650

Browse files
committed
improve dictionary error
1 parent f78b686 commit 7ec3650

File tree

4 files changed

+113
-60
lines changed

4 files changed

+113
-60
lines changed

dist/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ <h1>
149149
id="custom-dictionary"
150150
placeholder="Type here custom dictionary. Press Help above to get started."
151151
></textarea>
152+
<details>
153+
<summary id="custom-dictionary-error-summary">Errors (0):</summary>
154+
<ul id="custom-dictionary-error-list"></ul>
155+
</details>
152156
<div>
153157
<div></div>
154158
<button id="discard-button">Discard Changes and Close</button>

dist/style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dialog.flex:modal {
5454
#custom-dictionary-box {
5555
box-sizing: border-box;
5656
height: 100%;
57+
width: 100%;
5758
}
5859
dialog.flex details {
5960
margin: 0;
@@ -90,6 +91,12 @@ dialog.flex > div > div,
9091
dialog.flex > div > input[type="text"] {
9192
flex-grow: 1;
9293
}
94+
#custom-dictionary-error-list li {
95+
cursor: pointer;
96+
}
97+
#custom-dictionary-error-list li:hover {
98+
text-decoration: underline;
99+
}
93100
@media (min-width: 800px) {
94101
body {
95102
margin: 50px;

src/dictionary.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { dictionary as globalDictionary } from "../dictionary/dictionary.ts";
2-
import { dictionaryParser } from "../dictionary/parser.ts";
32
import { Definition, Dictionary } from "../dictionary/type.ts";
43
import { load } from "../telo_misikeke/telo_misikeke.js";
5-
import { ArrayResultError } from "./array_result.ts";
64

75
// All of these global constants are mutable
86

@@ -18,20 +16,12 @@ export const tokiPonaWordSet: Set<string> = new Set();
1816

1917
update();
2018

21-
export function loadCustomDictionary(
22-
dictionaryText: string,
23-
): null | ReadonlyArray<ArrayResultError> {
24-
const result = dictionaryParser.parse(dictionaryText);
25-
if (result.isError()) {
26-
return result.errors;
27-
}
28-
const dictionary = result.array[0];
19+
export function loadCustomDictionary(dictionary: Dictionary): void {
2920
customDictionary.clear();
3021
for (const [key, value] of dictionary) {
3122
customDictionary.set(key, value);
3223
}
3324
update();
34-
return null;
3525
}
3626
function update(): void {
3727
dictionary.clear();

src/main.ts

Lines changed: 101 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ if (LIVE_RELOAD) {
99
.addEventListener("change", () => location.reload());
1010
}
1111
import { dictionary } from "../dictionary/dictionary.ts";
12+
import { dictionaryParser } from "../dictionary/parser.ts";
1213
import PROJECT_DATA from "../project_data.json" with { type: "json" };
1314
import { loadCustomDictionary } from "./dictionary.ts";
1415
import { checkLocalStorage, setIgnoreError } from "./local_storage.ts";
1516
import { translate } from "./mod.ts";
17+
import { PositionedError } from "./parser/parser_lib.ts";
1618
import { settings } from "./settings.ts";
1719
import {
1820
loadFromElements,
@@ -21,6 +23,11 @@ import {
2123
resetElementsToDefault,
2224
} from "./settings_frontend.ts";
2325

26+
const DICTIONARY_AUTO_PARSE_THRESHOLD = 9000;
27+
28+
// never change this
29+
const DICTIONARY_KEY = "dictionary";
30+
2431
const TRANSLATE_LABEL = "Translate";
2532
const TRANSLATE_LABEL_MULTILINE = "Translate (Ctrl + Enter)";
2633

@@ -31,31 +38,27 @@ const SINGULAR_ERROR_MESSAGE = "An error has been found:";
3138
const MULTIPLE_ERROR_MESSAGE = "Multiple errors has been found:";
3239

3340
const DEFAULT_CUSTOM_DICTIONARY_MESSAGE = `\
34-
====================================
35-
Welcome to Custom Dictionary Editor!
36-
====================================
37-
38-
Here you can customize the dictionary
39-
used in ilo Token. You may change the
40-
definitions of existing words and
41-
even extend ilo Token with more
42-
non-pu words. Just know that the
43-
custom dictionary comes with
44-
limitations. Press Help above to get
45-
started.`;
41+
# ====================================
42+
# Welcome to Custom Dictionary Editor!
43+
# ====================================
44+
#
45+
# Here you can customize the dictionary
46+
# used in ilo Token. You may change the
47+
# definitions of existing words and
48+
# even extend ilo Token with more
49+
# non-pu words. Just know that the
50+
# custom dictionary comes with
51+
# limitations. Press Help above to get
52+
# started.
53+
`;
4654

4755
const DICTIONARY_LOADING_FAILED_MESSAGE =
4856
"Failed to load custom dictionary. This is mostly likely because the " +
4957
"syntax has been updated and your custom dictionary still uses the old " +
5058
"syntax. Please fix it. Apologies for the inconvenience.";
5159
const NO_WORD_MESSAGE = "Please provide a word";
5260
const WORD_NOT_FOUND_MESSAGE = "Word not found";
53-
54-
const DICTIONARY_ERROR_MESSAGE =
55-
"Please fix these errors before saving.\n(You may remove these when fixed)";
56-
57-
// never change this
58-
const DICTIONARY_KEY = "dictionary";
61+
const DICTIONARY_ERROR_MESSAGE = "Please fix the errors before saving";
5962

6063
function main(): void {
6164
// load elements
@@ -106,6 +109,12 @@ function main(): void {
106109
const customDictionaryTextBox = document.getElementById(
107110
"custom-dictionary",
108111
) as HTMLTextAreaElement;
112+
const customDictionaryErrorSummary = document.getElementById(
113+
"custom-dictionary-error-summary",
114+
) as HTMLElement;
115+
const customDictionaryErrorList = document.getElementById(
116+
"custom-dictionary-error-list",
117+
) as HTMLUListElement;
109118
const discardButton = document.getElementById(
110119
"discard-button",
111120
) as HTMLButtonElement;
@@ -151,14 +160,21 @@ function main(): void {
151160
// load settings
152161
loadFromLocalStorage();
153162

154-
// load custom dictionary
155-
const customDictionary = checkLocalStorage()
163+
// states for storing previous dictionary states for discarding dictionary edits
164+
let lastSavedText = checkLocalStorage()
156165
? localStorage.getItem(DICTIONARY_KEY) ?? ""
157166
: customDictionaryTextBox.value;
158-
if (customDictionary.trim() !== "") {
159-
if (loadCustomDictionary(customDictionary) != null) {
160-
showMessage(DICTIONARY_LOADING_FAILED_MESSAGE);
161-
}
167+
let lastSavedDictionary = dictionaryParser.parse(lastSavedText);
168+
169+
// this variable also holds error messages
170+
let currentDictionary = lastSavedDictionary;
171+
172+
// load custom dictionary
173+
if (!currentDictionary.isError()) {
174+
loadCustomDictionary(currentDictionary.array[0]);
175+
} else {
176+
showDictionaryError();
177+
showMessage(DICTIONARY_LOADING_FAILED_MESSAGE);
162178
}
163179

164180
// initial text area size
@@ -176,6 +192,29 @@ function main(): void {
176192
: TRANSLATE_LABEL;
177193
}
178194

195+
// show custom dictionary errors
196+
function showDictionaryError(): void {
197+
customDictionaryErrorSummary.innerText =
198+
`Errors (${currentDictionary.errors.length}):`;
199+
customDictionaryErrorList.innerHTML = "";
200+
for (const error of currentDictionary.errors) {
201+
const element = document.createElement("li");
202+
element.innerText = error.message;
203+
if (error instanceof PositionedError && error.position != null) {
204+
const { position, length } = error.position;
205+
element.addEventListener("click", () => {
206+
customDictionaryTextBox.focus();
207+
customDictionaryTextBox.setSelectionRange(
208+
position,
209+
position + length,
210+
);
211+
});
212+
} else {
213+
throw new Error("error without position");
214+
}
215+
customDictionaryErrorList.appendChild(element);
216+
}
217+
}
179218
// add all event listener
180219
translateButton.addEventListener("click", updateOutput);
181220
inputTextBox.addEventListener("input", resizeTextarea);
@@ -239,7 +278,7 @@ function main(): void {
239278
customDictionaryDialogBox.showModal();
240279
if (checkLocalStorage()) {
241280
customDictionaryTextBox.value = localStorage.getItem(DICTIONARY_KEY) ??
242-
`${asComment(DEFAULT_CUSTOM_DICTIONARY_MESSAGE)}\n`;
281+
DEFAULT_CUSTOM_DICTIONARY_MESSAGE;
243282
}
244283
});
245284
importWordButton.addEventListener("click", importWord);
@@ -249,43 +288,61 @@ function main(): void {
249288
importWord();
250289
}
251290
});
252-
function displayToCustomDictionary(message: string): void {
253-
const original = customDictionaryTextBox.value.trimEnd();
254-
const append = original === "" ? "" : "\n\n";
255-
customDictionaryTextBox.value =
256-
`${original}${append}${message.trimEnd()}\n`;
257-
customDictionaryTextBox.scrollTo(0, customDictionaryTextBox.scrollHeight);
258-
}
259291
function importWord(): void {
260292
const word = importWordTextBox.value.trim();
261293
if (word === "") {
262294
showMessage(NO_WORD_MESSAGE);
263295
} else {
264296
const definitions = dictionary.get(word)?.source;
265297
if (definitions != null) {
266-
displayToCustomDictionary(`${word}:${definitions}`);
298+
const original = customDictionaryTextBox.value.trimEnd();
299+
const append = original === "" ? "" : "\n\n";
300+
customDictionaryTextBox.value =
301+
`${original}${append}${word}:${definitions.trimEnd()}\n`;
302+
customDictionaryTextBox.scrollTo(
303+
0,
304+
customDictionaryTextBox.scrollHeight,
305+
);
267306
} else {
268307
showMessage(WORD_NOT_FOUND_MESSAGE);
269308
}
270309
}
271310
}
311+
customDictionaryTextBox.addEventListener("input", () => {
312+
if (
313+
customDictionaryTextBox.value.length <= DICTIONARY_AUTO_PARSE_THRESHOLD
314+
) {
315+
updateDictionary();
316+
}
317+
});
272318
discardButton.addEventListener("click", () => {
273-
customDictionaryDialogBox.close();
319+
customDictionaryTextBox.value = lastSavedText;
320+
currentDictionary = lastSavedDictionary;
321+
tryCloseDictionary();
274322
});
275323
saveButton.addEventListener("click", () => {
276-
const { value } = customDictionaryTextBox;
277-
const errors = loadCustomDictionary(value);
278-
if (errors == null) {
279-
setIgnoreError(DICTIONARY_KEY, value);
324+
if (
325+
customDictionaryTextBox.value.length > DICTIONARY_AUTO_PARSE_THRESHOLD
326+
) {
327+
updateDictionary();
328+
}
329+
tryCloseDictionary();
330+
});
331+
function updateDictionary(): void {
332+
currentDictionary = dictionaryParser.parse(customDictionaryTextBox.value);
333+
showDictionaryError();
334+
}
335+
function tryCloseDictionary(): void {
336+
if (!currentDictionary.isError()) {
337+
lastSavedText = customDictionaryTextBox.value;
338+
lastSavedDictionary = currentDictionary;
339+
loadCustomDictionary(currentDictionary.array[0]);
340+
setIgnoreError(DICTIONARY_KEY, customDictionaryTextBox.value);
280341
customDictionaryDialogBox.close();
281342
} else {
282-
const errorListMessage = errors
283-
.map((error) => `\n- ${error.message.replaceAll(/\r?\n/g, "$& ")}`);
284-
displayToCustomDictionary(
285-
asComment(`${DICTIONARY_ERROR_MESSAGE}${errorListMessage}`),
286-
);
343+
showMessage(DICTIONARY_ERROR_MESSAGE);
287344
}
288-
});
345+
}
289346
closeButton.addEventListener("click", () => {
290347
alertBox.close();
291348
});
@@ -311,8 +368,3 @@ const unused = [...new Array(localStorage.length).keys()]
311368
for (const key of unused) {
312369
localStorage.removeItem(key);
313370
}
314-
export function asComment(text: string): string {
315-
return text
316-
.replaceAll(/^/mg, "# ")
317-
.replaceAll(/^#\s+$/mg, "#");
318-
}

0 commit comments

Comments
 (0)