@leanandmean/coding-agent 0.74.1-scramjet.10 → 0.74.1-scramjet.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spellcheck.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/spellcheck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE5E,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAID,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,CA0BlD;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvD;AAED,qBAAa,cAAe,YAAW,kBAAkB;IACxD,QAAQ,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAQ;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B,OAAO,CAAC,OAAO,CAAmD;IAClE,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,YAAY,CAAgB;
|
|
1
|
+
{"version":3,"file":"spellcheck.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/spellcheck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE5E,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAID,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,CA0BlD;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvD;AAED,qBAAa,cAAe,YAAW,kBAAkB;IACxD,QAAQ,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAQ;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B,OAAO,CAAC,OAAO,CAAmD;IAClE,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,YAAY,CAAgB;IAEpC,IAAI,SAAS,IAAI,MAAM,CAEtB;IACD,OAAO,CAAC,aAAa,CAA8C;IAEnE,cAEC;YAEa,cAAc;IAa5B,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CAQxD;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CASjC;IAED,OAAO,CAAC,OAAO;IAmBf,OAAO,CAAC,aAAa;CAwBrB","sourcesContent":["import type { SpellcheckProvider, SpellcheckRange } from \"@leanandmean/tui\";\n\ninterface Token {\n\tword: string;\n\tstart: number;\n\tend: number;\n}\n\nconst CODE_CHARS = new Set([\"_\", \"/\", \"\\\\\", \".\", \"@\", \"#\", \":\"]);\n\nexport function tokenizeLine(line: string): Token[] {\n\tconst tokens: Token[] = [];\n\tconst len = line.length;\n\tlet i = 0;\n\n\twhile (i < len) {\n\t\t// Skip non-word characters\n\t\twhile (i < len && !isWordChar(line.charCodeAt(i))) i++;\n\t\tif (i >= len) break;\n\n\t\t// Collect word run: [a-zA-Z'] sequences\n\t\tconst start = i;\n\t\twhile (i < len && isWordChar(line.charCodeAt(i))) i++;\n\n\t\t// Trim leading/trailing apostrophes\n\t\tlet wordStart = start;\n\t\tlet wordEnd = i;\n\t\twhile (wordStart < wordEnd && line[wordStart] === \"'\") wordStart++;\n\t\twhile (wordEnd > wordStart && line[wordEnd - 1] === \"'\") wordEnd--;\n\n\t\tif (wordEnd - wordStart > 1) {\n\t\t\ttokens.push({ word: line.slice(wordStart, wordEnd), start: wordStart, end: wordEnd });\n\t\t}\n\t}\n\n\treturn tokens;\n}\n\nfunction isWordChar(code: number): boolean {\n\treturn (\n\t\t(code >= 65 && code <= 90) || // A-Z\n\t\t(code >= 97 && code <= 122) || // a-z\n\t\tcode === 39 // '\n\t);\n}\n\nexport function isCodeLikeToken(token: string): boolean {\n\tfor (let i = 0; i < token.length; i++) {\n\t\tif (CODE_CHARS.has(token[i])) return true;\n\t}\n\treturn false;\n}\n\nexport function hasCodeLikeCasing(word: string): boolean {\n\tif (word === word.toUpperCase()) return false;\n\tif (word === word.toLowerCase()) return false;\n\t// Title case: only first letter uppercase\n\tif (word[0] === word[0].toUpperCase() && word.slice(1) === word.slice(1).toLowerCase()) return false;\n\t// Interior uppercase = code-like\n\treturn true;\n}\n\nexport class NspellProvider implements SpellcheckProvider {\n\tonUpdate: (() => void) | null = null;\n\treadonly ready: Promise<void>;\n\n\tprivate checker: { correct(word: string): boolean } | null = null;\n\tprivate cache = new Map<string, SpellcheckRange[]>();\n\tprivate currentLines: string[] = [];\n\n\tget cacheSize(): number {\n\t\treturn this.cache.size;\n\t}\n\tprivate debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor() {\n\t\tthis.ready = this.initDictionary();\n\t}\n\n\tprivate async initDictionary(): Promise<void> {\n\t\ttry {\n\t\t\tconst [nspellMod, { default: dictionary }] = await Promise.all([import(\"nspell\"), import(\"dictionary-en\")]);\n\t\t\tthis.checker = nspellMod.default(dictionary.aff as Buffer, dictionary.dic as Buffer);\n\t\t\t// Re-check with dictionary now available\n\t\t\tif (this.currentLines.length > 0) {\n\t\t\t\tthis.recheck();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.warn(\"Spellcheck dictionary failed to load:\", err instanceof Error ? err.message : err);\n\t\t}\n\t}\n\n\tgetMisspelledRanges(lineIndex: number): SpellcheckRange[] {\n\t\tif (!this.checker || lineIndex >= this.currentLines.length) return [];\n\t\tconst line = this.currentLines[lineIndex];\n\t\tconst cached = this.cache.get(line);\n\t\tif (cached !== undefined) return cached;\n\t\tconst ranges = this.computeRanges(line);\n\t\tthis.cache.set(line, ranges);\n\t\treturn ranges;\n\t}\n\n\ttextChanged(lines: string[]): void {\n\t\tthis.currentLines = lines;\n\t\tif (this.debounceTimer !== null) {\n\t\t\tclearTimeout(this.debounceTimer);\n\t\t}\n\t\tthis.debounceTimer = setTimeout(() => {\n\t\t\tthis.debounceTimer = null;\n\t\t\tthis.recheck();\n\t\t}, 150);\n\t}\n\n\tprivate recheck(): void {\n\t\tif (!this.checker) return;\n\t\tlet changed = false;\n\t\tconst pruned = new Map<string, SpellcheckRange[]>();\n\t\tfor (const line of this.currentLines) {\n\t\t\tconst cached = this.cache.get(line);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tpruned.set(line, cached);\n\t\t\t} else {\n\t\t\t\tpruned.set(line, this.computeRanges(line));\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tthis.cache = pruned;\n\t\tif (changed && this.onUpdate) {\n\t\t\tthis.onUpdate();\n\t\t}\n\t}\n\n\tprivate computeRanges(line: string): SpellcheckRange[] {\n\t\tif (!this.checker) return [];\n\t\tconst ranges: SpellcheckRange[] = [];\n\t\tconst words = line.split(/\\s+/);\n\t\tlet offset = 0;\n\n\t\tfor (const wholeToken of words) {\n\t\t\tconst tokenStart = line.indexOf(wholeToken, offset);\n\t\t\toffset = tokenStart + wholeToken.length;\n\n\t\t\tconst stripped = wholeToken.replace(/[.,!?;:)\\]]+$/, \"\");\n\t\t\tif (stripped.length > 0 && isCodeLikeToken(stripped)) continue;\n\n\t\t\tconst tokens = tokenizeLine(wholeToken);\n\t\t\tfor (const token of tokens) {\n\t\t\t\tif (hasCodeLikeCasing(token.word)) continue;\n\t\t\t\tif (!this.checker.correct(token.word)) {\n\t\t\t\t\tranges.push({ start: tokenStart + token.start, end: tokenStart + token.end });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ranges;\n\t}\n}\n"]}
|
|
@@ -56,6 +56,9 @@ export class NspellProvider {
|
|
|
56
56
|
checker = null;
|
|
57
57
|
cache = new Map();
|
|
58
58
|
currentLines = [];
|
|
59
|
+
get cacheSize() {
|
|
60
|
+
return this.cache.size;
|
|
61
|
+
}
|
|
59
62
|
debounceTimer = null;
|
|
60
63
|
constructor() {
|
|
61
64
|
this.ready = this.initDictionary();
|
|
@@ -69,8 +72,8 @@ export class NspellProvider {
|
|
|
69
72
|
this.recheck();
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
|
-
catch {
|
|
73
|
-
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.warn("Spellcheck dictionary failed to load:", err instanceof Error ? err.message : err);
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
79
|
getMisspelledRanges(lineIndex) {
|
|
@@ -98,12 +101,18 @@ export class NspellProvider {
|
|
|
98
101
|
if (!this.checker)
|
|
99
102
|
return;
|
|
100
103
|
let changed = false;
|
|
104
|
+
const pruned = new Map();
|
|
101
105
|
for (const line of this.currentLines) {
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
const cached = this.cache.get(line);
|
|
107
|
+
if (cached !== undefined) {
|
|
108
|
+
pruned.set(line, cached);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
pruned.set(line, this.computeRanges(line));
|
|
104
112
|
changed = true;
|
|
105
113
|
}
|
|
106
114
|
}
|
|
115
|
+
this.cache = pruned;
|
|
107
116
|
if (changed && this.onUpdate) {
|
|
108
117
|
this.onUpdate();
|
|
109
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spellcheck.js","sourceRoot":"","sources":["../../../src/modes/interactive/spellcheck.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpellcheckProvider, SpellcheckRange } from \"@leanandmean/tui\";\n\ninterface Token {\n\tword: string;\n\tstart: number;\n\tend: number;\n}\n\nconst CODE_CHARS = new Set([\"_\", \"/\", \"\\\\\", \".\", \"@\", \"#\", \":\"]);\n\nexport function tokenizeLine(line: string): Token[] {\n\tconst tokens: Token[] = [];\n\tconst len = line.length;\n\tlet i = 0;\n\n\twhile (i < len) {\n\t\t// Skip non-word characters\n\t\twhile (i < len && !isWordChar(line.charCodeAt(i))) i++;\n\t\tif (i >= len) break;\n\n\t\t// Collect word run: [a-zA-Z'] sequences\n\t\tconst start = i;\n\t\twhile (i < len && isWordChar(line.charCodeAt(i))) i++;\n\n\t\t// Trim leading/trailing apostrophes\n\t\tlet wordStart = start;\n\t\tlet wordEnd = i;\n\t\twhile (wordStart < wordEnd && line[wordStart] === \"'\") wordStart++;\n\t\twhile (wordEnd > wordStart && line[wordEnd - 1] === \"'\") wordEnd--;\n\n\t\tif (wordEnd - wordStart > 1) {\n\t\t\ttokens.push({ word: line.slice(wordStart, wordEnd), start: wordStart, end: wordEnd });\n\t\t}\n\t}\n\n\treturn tokens;\n}\n\nfunction isWordChar(code: number): boolean {\n\treturn (\n\t\t(code >= 65 && code <= 90) || // A-Z\n\t\t(code >= 97 && code <= 122) || // a-z\n\t\tcode === 39 // '\n\t);\n}\n\nexport function isCodeLikeToken(token: string): boolean {\n\tfor (let i = 0; i < token.length; i++) {\n\t\tif (CODE_CHARS.has(token[i])) return true;\n\t}\n\treturn false;\n}\n\nexport function hasCodeLikeCasing(word: string): boolean {\n\tif (word === word.toUpperCase()) return false;\n\tif (word === word.toLowerCase()) return false;\n\t// Title case: only first letter uppercase\n\tif (word[0] === word[0].toUpperCase() && word.slice(1) === word.slice(1).toLowerCase()) return false;\n\t// Interior uppercase = code-like\n\treturn true;\n}\n\nexport class NspellProvider implements SpellcheckProvider {\n\tonUpdate: (() => void) | null = null;\n\treadonly ready: Promise<void>;\n\n\tprivate checker: { correct(word: string): boolean } | null = null;\n\tprivate cache = new Map<string, SpellcheckRange[]>();\n\tprivate currentLines: string[] = [];\n\tprivate debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor() {\n\t\tthis.ready = this.initDictionary();\n\t}\n\n\tprivate async initDictionary(): Promise<void> {\n\t\ttry {\n\t\t\tconst [nspellMod, { default: dictionary }] = await Promise.all([import(\"nspell\"), import(\"dictionary-en\")]);\n\t\t\tthis.checker = nspellMod.default(dictionary.aff as Buffer, dictionary.dic as Buffer);\n\t\t\t// Re-check with dictionary now available\n\t\t\tif (this.currentLines.length > 0) {\n\t\t\t\tthis.recheck();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Dictionary load failure is non-fatal — spellcheck simply won't activate\n\t\t}\n\t}\n\n\tgetMisspelledRanges(lineIndex: number): SpellcheckRange[] {\n\t\tif (!this.checker || lineIndex >= this.currentLines.length) return [];\n\t\tconst line = this.currentLines[lineIndex];\n\t\tconst cached = this.cache.get(line);\n\t\tif (cached !== undefined) return cached;\n\t\tconst ranges = this.computeRanges(line);\n\t\tthis.cache.set(line, ranges);\n\t\treturn ranges;\n\t}\n\n\ttextChanged(lines: string[]): void {\n\t\tthis.currentLines = lines;\n\t\tif (this.debounceTimer !== null) {\n\t\t\tclearTimeout(this.debounceTimer);\n\t\t}\n\t\tthis.debounceTimer = setTimeout(() => {\n\t\t\tthis.debounceTimer = null;\n\t\t\tthis.recheck();\n\t\t}, 150);\n\t}\n\n\tprivate recheck(): void {\n\t\tif (!this.checker) return;\n\t\tlet changed = false;\n\t\tfor (const line of this.currentLines) {\n\t\t\tif (!this.cache.has(line)) {\n\t\t\t\tthis.cache.set(line, this.computeRanges(line));\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed && this.onUpdate) {\n\t\t\tthis.onUpdate();\n\t\t}\n\t}\n\n\tprivate computeRanges(line: string): SpellcheckRange[] {\n\t\tif (!this.checker) return [];\n\t\tconst ranges: SpellcheckRange[] = [];\n\t\tconst words = line.split(/\\s+/);\n\t\tlet offset = 0;\n\n\t\tfor (const wholeToken of words) {\n\t\t\tconst tokenStart = line.indexOf(wholeToken, offset);\n\t\t\toffset = tokenStart + wholeToken.length;\n\n\t\t\tconst stripped = wholeToken.replace(/[.,!?;:)\\]]+$/, \"\");\n\t\t\tif (stripped.length > 0 && isCodeLikeToken(stripped)) continue;\n\n\t\t\tconst tokens = tokenizeLine(wholeToken);\n\t\t\tfor (const token of tokens) {\n\t\t\t\tif (hasCodeLikeCasing(token.word)) continue;\n\t\t\t\tif (!this.checker.correct(token.word)) {\n\t\t\t\t\tranges.push({ start: tokenStart + token.start, end: tokenStart + token.end });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ranges;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"spellcheck.js","sourceRoot":"","sources":["../../../src/modes/interactive/spellcheck.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpellcheckProvider, SpellcheckRange } from \"@leanandmean/tui\";\n\ninterface Token {\n\tword: string;\n\tstart: number;\n\tend: number;\n}\n\nconst CODE_CHARS = new Set([\"_\", \"/\", \"\\\\\", \".\", \"@\", \"#\", \":\"]);\n\nexport function tokenizeLine(line: string): Token[] {\n\tconst tokens: Token[] = [];\n\tconst len = line.length;\n\tlet i = 0;\n\n\twhile (i < len) {\n\t\t// Skip non-word characters\n\t\twhile (i < len && !isWordChar(line.charCodeAt(i))) i++;\n\t\tif (i >= len) break;\n\n\t\t// Collect word run: [a-zA-Z'] sequences\n\t\tconst start = i;\n\t\twhile (i < len && isWordChar(line.charCodeAt(i))) i++;\n\n\t\t// Trim leading/trailing apostrophes\n\t\tlet wordStart = start;\n\t\tlet wordEnd = i;\n\t\twhile (wordStart < wordEnd && line[wordStart] === \"'\") wordStart++;\n\t\twhile (wordEnd > wordStart && line[wordEnd - 1] === \"'\") wordEnd--;\n\n\t\tif (wordEnd - wordStart > 1) {\n\t\t\ttokens.push({ word: line.slice(wordStart, wordEnd), start: wordStart, end: wordEnd });\n\t\t}\n\t}\n\n\treturn tokens;\n}\n\nfunction isWordChar(code: number): boolean {\n\treturn (\n\t\t(code >= 65 && code <= 90) || // A-Z\n\t\t(code >= 97 && code <= 122) || // a-z\n\t\tcode === 39 // '\n\t);\n}\n\nexport function isCodeLikeToken(token: string): boolean {\n\tfor (let i = 0; i < token.length; i++) {\n\t\tif (CODE_CHARS.has(token[i])) return true;\n\t}\n\treturn false;\n}\n\nexport function hasCodeLikeCasing(word: string): boolean {\n\tif (word === word.toUpperCase()) return false;\n\tif (word === word.toLowerCase()) return false;\n\t// Title case: only first letter uppercase\n\tif (word[0] === word[0].toUpperCase() && word.slice(1) === word.slice(1).toLowerCase()) return false;\n\t// Interior uppercase = code-like\n\treturn true;\n}\n\nexport class NspellProvider implements SpellcheckProvider {\n\tonUpdate: (() => void) | null = null;\n\treadonly ready: Promise<void>;\n\n\tprivate checker: { correct(word: string): boolean } | null = null;\n\tprivate cache = new Map<string, SpellcheckRange[]>();\n\tprivate currentLines: string[] = [];\n\n\tget cacheSize(): number {\n\t\treturn this.cache.size;\n\t}\n\tprivate debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n\tconstructor() {\n\t\tthis.ready = this.initDictionary();\n\t}\n\n\tprivate async initDictionary(): Promise<void> {\n\t\ttry {\n\t\t\tconst [nspellMod, { default: dictionary }] = await Promise.all([import(\"nspell\"), import(\"dictionary-en\")]);\n\t\t\tthis.checker = nspellMod.default(dictionary.aff as Buffer, dictionary.dic as Buffer);\n\t\t\t// Re-check with dictionary now available\n\t\t\tif (this.currentLines.length > 0) {\n\t\t\t\tthis.recheck();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.warn(\"Spellcheck dictionary failed to load:\", err instanceof Error ? err.message : err);\n\t\t}\n\t}\n\n\tgetMisspelledRanges(lineIndex: number): SpellcheckRange[] {\n\t\tif (!this.checker || lineIndex >= this.currentLines.length) return [];\n\t\tconst line = this.currentLines[lineIndex];\n\t\tconst cached = this.cache.get(line);\n\t\tif (cached !== undefined) return cached;\n\t\tconst ranges = this.computeRanges(line);\n\t\tthis.cache.set(line, ranges);\n\t\treturn ranges;\n\t}\n\n\ttextChanged(lines: string[]): void {\n\t\tthis.currentLines = lines;\n\t\tif (this.debounceTimer !== null) {\n\t\t\tclearTimeout(this.debounceTimer);\n\t\t}\n\t\tthis.debounceTimer = setTimeout(() => {\n\t\t\tthis.debounceTimer = null;\n\t\t\tthis.recheck();\n\t\t}, 150);\n\t}\n\n\tprivate recheck(): void {\n\t\tif (!this.checker) return;\n\t\tlet changed = false;\n\t\tconst pruned = new Map<string, SpellcheckRange[]>();\n\t\tfor (const line of this.currentLines) {\n\t\t\tconst cached = this.cache.get(line);\n\t\t\tif (cached !== undefined) {\n\t\t\t\tpruned.set(line, cached);\n\t\t\t} else {\n\t\t\t\tpruned.set(line, this.computeRanges(line));\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tthis.cache = pruned;\n\t\tif (changed && this.onUpdate) {\n\t\t\tthis.onUpdate();\n\t\t}\n\t}\n\n\tprivate computeRanges(line: string): SpellcheckRange[] {\n\t\tif (!this.checker) return [];\n\t\tconst ranges: SpellcheckRange[] = [];\n\t\tconst words = line.split(/\\s+/);\n\t\tlet offset = 0;\n\n\t\tfor (const wholeToken of words) {\n\t\t\tconst tokenStart = line.indexOf(wholeToken, offset);\n\t\t\toffset = tokenStart + wholeToken.length;\n\n\t\t\tconst stripped = wholeToken.replace(/[.,!?;:)\\]]+$/, \"\");\n\t\t\tif (stripped.length > 0 && isCodeLikeToken(stripped)) continue;\n\n\t\t\tconst tokens = tokenizeLine(wholeToken);\n\t\t\tfor (const token of tokens) {\n\t\t\t\tif (hasCodeLikeCasing(token.word)) continue;\n\t\t\t\tif (!this.checker.correct(token.word)) {\n\t\t\t\t\tranges.push({ start: tokenStart + token.start, end: tokenStart + token.end });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ranges;\n\t}\n}\n"]}
|