cspell-dictionary 8.11.0 → 8.13.0
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.
- package/dist/SpellingDictionary/CachingDictionary.d.ts +13 -0
- package/dist/SpellingDictionary/CachingDictionary.js +19 -2
- package/dist/SpellingDictionary/SpellingDictionaryCollection.js +3 -4
- package/dist/SpellingDictionary/SpellingDictionaryFromTrie.d.ts +0 -2
- package/dist/SpellingDictionary/SpellingDictionaryFromTrie.js +13 -27
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/perf/has.perf.js +122 -12
- package/dist/perf/misc.perf.d.ts +2 -0
- package/dist/perf/misc.perf.js +48 -0
- package/dist/util/AutoCache.d.ts +3 -6
- package/dist/util/AutoCache.js +20 -12
- package/package.json +7 -7
|
@@ -21,6 +21,17 @@ export interface CachingDictionary {
|
|
|
21
21
|
stats(): CallStats;
|
|
22
22
|
getPreferredSuggestions(word: string): PreferredSuggestion[] | undefined;
|
|
23
23
|
}
|
|
24
|
+
interface LogEntryBase extends SearchOptions {
|
|
25
|
+
time: number;
|
|
26
|
+
method: 'has';
|
|
27
|
+
word: string;
|
|
28
|
+
value?: unknown;
|
|
29
|
+
}
|
|
30
|
+
interface LogEntryHas extends LogEntryBase {
|
|
31
|
+
method: 'has';
|
|
32
|
+
value: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type LogEntry = LogEntryHas;
|
|
24
35
|
/**
|
|
25
36
|
* create a caching dictionary
|
|
26
37
|
* @param dict - Dictionary to cache the search results.
|
|
@@ -28,5 +39,7 @@ export interface CachingDictionary {
|
|
|
28
39
|
* @returns CachingDictionary
|
|
29
40
|
*/
|
|
30
41
|
export declare function createCachingDictionary(dict: SpellingDictionary | SpellingDictionaryCollection, options: SearchOptions): CachingDictionary;
|
|
42
|
+
export declare function enableLogging(enabled?: boolean): void;
|
|
43
|
+
export declare function getLog(): LogEntryBase[];
|
|
31
44
|
export {};
|
|
32
45
|
//# sourceMappingURL=CachingDictionary.d.ts.map
|
|
@@ -2,6 +2,9 @@ import { autoCache, extractStats } from '../util/AutoCache.js';
|
|
|
2
2
|
import { canonicalSearchOptions } from './SpellingDictionaryMethods.js';
|
|
3
3
|
let dictionaryCounter = 0;
|
|
4
4
|
const DefaultAutoCacheSize = 1000;
|
|
5
|
+
let logRequests = false;
|
|
6
|
+
const log = [];
|
|
7
|
+
const startTime = performance.now();
|
|
5
8
|
class CachedDict {
|
|
6
9
|
dict;
|
|
7
10
|
options;
|
|
@@ -13,7 +16,15 @@ class CachedDict {
|
|
|
13
16
|
this.name = dict.name;
|
|
14
17
|
// console.log(`CachedDict for ${this.name}`);
|
|
15
18
|
}
|
|
16
|
-
has = autoCache((word) => this.dict.has(word, this.options), DefaultAutoCacheSize);
|
|
19
|
+
#has = autoCache((word) => this.dict.has(word, this.options), DefaultAutoCacheSize);
|
|
20
|
+
has = logRequests
|
|
21
|
+
? (word) => {
|
|
22
|
+
const time = performance.now() - startTime;
|
|
23
|
+
const value = this.#has(word);
|
|
24
|
+
log.push({ time, method: 'has', word, value });
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
: this.#has;
|
|
17
28
|
isNoSuggestWord = autoCache((word) => this.dict.isNoSuggestWord(word, this.options), DefaultAutoCacheSize);
|
|
18
29
|
isForbidden = autoCache((word) => this.dict.isForbidden(word), DefaultAutoCacheSize);
|
|
19
30
|
getPreferredSuggestions = autoCache((word) => this.dict.getPreferredSuggestions?.(word), DefaultAutoCacheSize);
|
|
@@ -21,7 +32,7 @@ class CachedDict {
|
|
|
21
32
|
return {
|
|
22
33
|
name: this.name,
|
|
23
34
|
id: this.id,
|
|
24
|
-
has: extractStats(this
|
|
35
|
+
has: extractStats(this.#has),
|
|
25
36
|
isNoSuggestWord: extractStats(this.isNoSuggestWord),
|
|
26
37
|
isForbidden: extractStats(this.isForbidden),
|
|
27
38
|
getPreferredSuggestions: extractStats(this.getPreferredSuggestions),
|
|
@@ -49,4 +60,10 @@ export function createCachingDictionary(dict, options) {
|
|
|
49
60
|
knownOptions.set(dict, cached);
|
|
50
61
|
return cached;
|
|
51
62
|
}
|
|
63
|
+
export function enableLogging(enabled = !logRequests) {
|
|
64
|
+
logRequests = enabled;
|
|
65
|
+
}
|
|
66
|
+
export function getLog() {
|
|
67
|
+
return log;
|
|
68
|
+
}
|
|
52
69
|
//# sourceMappingURL=CachingDictionary.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CASE_INSENSITIVE_PREFIX, CompoundWordsMethod } from 'cspell-trie-lib';
|
|
2
|
-
import { genSequence } from 'gensequence';
|
|
3
2
|
import { isDefined } from '../util/util.js';
|
|
4
3
|
import * as Defaults from './defaults.js';
|
|
5
4
|
import { defaultNumSuggestions, hasOptionToSearchOption, suggestionCollector } from './SpellingDictionaryMethods.js';
|
|
@@ -98,7 +97,7 @@ export function createCollection(dictionaries, name, source) {
|
|
|
98
97
|
return new SpellingDictionaryCollectionImpl(dictionaries, name, source);
|
|
99
98
|
}
|
|
100
99
|
function isWordInAnyDictionary(dicts, word, options) {
|
|
101
|
-
return
|
|
100
|
+
return dicts.find((dict) => dict.has(word, options));
|
|
102
101
|
}
|
|
103
102
|
function findInAnyDictionary(dicts, word, options) {
|
|
104
103
|
const found = dicts.map((dict) => dict.find(word, options)).filter(isDefined);
|
|
@@ -111,10 +110,10 @@ function findInAnyDictionary(dicts, word, options) {
|
|
|
111
110
|
}));
|
|
112
111
|
}
|
|
113
112
|
function isNoSuggestWordInAnyDictionary(dicts, word, options) {
|
|
114
|
-
return
|
|
113
|
+
return dicts.find((dict) => dict.isNoSuggestWord(word, options));
|
|
115
114
|
}
|
|
116
115
|
function isWordForbiddenInAnyDictionary(dicts, word, ignoreCase) {
|
|
117
|
-
return
|
|
116
|
+
return dicts.find((dict) => dict.isForbidden(word, ignoreCase));
|
|
118
117
|
}
|
|
119
118
|
export function isSpellingDictionaryCollection(dict) {
|
|
120
119
|
return dict instanceof SpellingDictionaryCollectionImpl;
|
|
@@ -6,7 +6,6 @@ export declare class SpellingDictionaryFromTrie implements SpellingDictionary {
|
|
|
6
6
|
readonly name: string;
|
|
7
7
|
readonly options: SpellingDictionaryOptions;
|
|
8
8
|
readonly source: string;
|
|
9
|
-
static readonly cachedWordsLimit = 50000;
|
|
10
9
|
private _size;
|
|
11
10
|
readonly knownWords: Set<string>;
|
|
12
11
|
readonly unknownWords: Set<string>;
|
|
@@ -26,7 +25,6 @@ export declare class SpellingDictionaryFromTrie implements SpellingDictionary {
|
|
|
26
25
|
private _findAnyForm;
|
|
27
26
|
isNoSuggestWord(word: string, options?: HasOptions): boolean;
|
|
28
27
|
isForbidden(word: string, _ignoreCaseAndAccents?: boolean): boolean;
|
|
29
|
-
private _isForbidden;
|
|
30
28
|
suggest(word: string, suggestOptions?: SuggestOptions): SuggestionResult[];
|
|
31
29
|
private _suggest;
|
|
32
30
|
genSuggestions(collector: SuggestionCollector, suggestOptions: SuggestOptions): void;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CompoundWordsMethod, decodeTrie, suggestionCollector } from 'cspell-trie-lib';
|
|
2
|
-
import { autoCache, createCache01 } from '../util/AutoCache.js';
|
|
3
2
|
import { clean } from '../util/clean.js';
|
|
4
3
|
import { createMapper, createRepMapper } from '../util/repMap.js';
|
|
5
4
|
import * as Defaults from './defaults.js';
|
|
@@ -11,7 +10,6 @@ export class SpellingDictionaryFromTrie {
|
|
|
11
10
|
name;
|
|
12
11
|
options;
|
|
13
12
|
source;
|
|
14
|
-
static cachedWordsLimit = 50_000;
|
|
15
13
|
_size = 0;
|
|
16
14
|
knownWords = new Set();
|
|
17
15
|
unknownWords = new Set();
|
|
@@ -68,7 +66,7 @@ export class SpellingDictionaryFromTrie {
|
|
|
68
66
|
const { useCompounds = this.options.useCompounds, ignoreCase = Defaults.ignoreCase } = hasOptionToSearchOption(hasOptions);
|
|
69
67
|
return { useCompounds, ignoreCase };
|
|
70
68
|
}
|
|
71
|
-
_find =
|
|
69
|
+
_find = (word, useCompounds, ignoreCase) => this.findAnyForm(word, useCompounds, ignoreCase);
|
|
72
70
|
findAnyForm(word, useCompounds, ignoreCase) {
|
|
73
71
|
const outerForms = outerWordForms(word, this.remapWord || ((word) => [this.mapWord(word)]));
|
|
74
72
|
for (const form of outerForms) {
|
|
@@ -106,11 +104,8 @@ export class SpellingDictionaryFromTrie {
|
|
|
106
104
|
return this.containsNoSuggestWords ? this.has(word, options) : false;
|
|
107
105
|
}
|
|
108
106
|
isForbidden(word, _ignoreCaseAndAccents) {
|
|
109
|
-
return this._isForbidden(word);
|
|
110
|
-
}
|
|
111
|
-
_isForbidden = autoCache((word) => {
|
|
112
107
|
return this.trie.isForbiddenWord(word);
|
|
113
|
-
}
|
|
108
|
+
}
|
|
114
109
|
suggest(word, suggestOptions = {}) {
|
|
115
110
|
return this._suggest(word, suggestOptions);
|
|
116
111
|
}
|
|
@@ -154,38 +149,29 @@ export function createSpellingDictionaryFromTrieFile(data, name, source, options
|
|
|
154
149
|
const trie = decodeTrie(data);
|
|
155
150
|
return new SpellingDictionaryFromTrie(trie, name, options, source);
|
|
156
151
|
}
|
|
157
|
-
function findCache(fn, size = 2000) {
|
|
158
|
-
const cache = createCache01(size);
|
|
159
|
-
function find(word, useCompounds, ignoreCase) {
|
|
160
|
-
const r = cache.get(word);
|
|
161
|
-
if (r !== undefined && r.useCompounds === useCompounds && r.ignoreCase === ignoreCase) {
|
|
162
|
-
return r.findResult;
|
|
163
|
-
}
|
|
164
|
-
const findResult = fn(word, useCompounds, ignoreCase);
|
|
165
|
-
cache.set(word, { useCompounds, ignoreCase, findResult });
|
|
166
|
-
return findResult;
|
|
167
|
-
}
|
|
168
|
-
return find;
|
|
169
|
-
}
|
|
170
152
|
function* outerWordForms(word, mapWord) {
|
|
171
153
|
// Only generate the needed forms.
|
|
172
154
|
const sent = new Set();
|
|
173
155
|
let w = word;
|
|
156
|
+
const ww = w;
|
|
174
157
|
yield w;
|
|
175
158
|
sent.add(w);
|
|
176
159
|
w = word.normalize('NFC');
|
|
177
|
-
if (
|
|
160
|
+
if (w !== ww) {
|
|
178
161
|
yield w;
|
|
179
|
-
|
|
162
|
+
sent.add(w);
|
|
163
|
+
}
|
|
180
164
|
w = word.normalize('NFD');
|
|
181
|
-
if (!sent.has(w))
|
|
165
|
+
if (w !== ww && !sent.has(w)) {
|
|
182
166
|
yield w;
|
|
183
|
-
|
|
184
|
-
|
|
167
|
+
sent.add(w);
|
|
168
|
+
}
|
|
169
|
+
for (const f of sent) {
|
|
185
170
|
for (const m of mapWord(f)) {
|
|
186
|
-
if (!sent.has(m))
|
|
171
|
+
if (m !== ww && !sent.has(m)) {
|
|
187
172
|
yield m;
|
|
188
|
-
|
|
173
|
+
sent.add(m);
|
|
174
|
+
}
|
|
189
175
|
}
|
|
190
176
|
}
|
|
191
177
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { enableLogging as cacheDictionaryEnableLogging, getLog as cacheDictionaryGetLog } from './SpellingDictionary/CachingDictionary.js';
|
|
1
2
|
export type { CachingDictionary, FindOptions, FindResult, HasOptions, SearchOptions, SpellingDictionary, SpellingDictionaryCollection, SpellingDictionaryOptions, SuggestionCollector, SuggestionResult, SuggestOptions, } from './SpellingDictionary/index.js';
|
|
2
3
|
export { createCachingDictionary, createCollection, createFailedToLoadDictionary, createFlagWordsDictionary, createForbiddenWordsDictionary, createIgnoreWordsDictionary, createInlineSpellingDictionary, createSpellingDictionary, createSpellingDictionaryFromTrieFile, createSuggestDictionary, createSuggestOptions, } from './SpellingDictionary/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Debugging utilities.
|
|
6
|
+
*/
|
|
7
|
+
export declare const _debug: {
|
|
8
|
+
cacheDictionaryEnableLogging: typeof cacheDictionaryEnableLogging;
|
|
9
|
+
cacheDictionaryGetLog: typeof cacheDictionaryGetLog;
|
|
10
|
+
};
|
|
3
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
import { enableLogging as cacheDictionaryEnableLogging, getLog as cacheDictionaryGetLog, } from './SpellingDictionary/CachingDictionary.js';
|
|
1
2
|
export { createCachingDictionary, createCollection, createFailedToLoadDictionary, createFlagWordsDictionary, createForbiddenWordsDictionary, createIgnoreWordsDictionary, createInlineSpellingDictionary, createSpellingDictionary, createSpellingDictionaryFromTrieFile, createSuggestDictionary, createSuggestOptions, } from './SpellingDictionary/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Debugging utilities.
|
|
5
|
+
*/
|
|
6
|
+
export const _debug = {
|
|
7
|
+
cacheDictionaryEnableLogging,
|
|
8
|
+
cacheDictionaryGetLog,
|
|
9
|
+
};
|
|
2
10
|
//# sourceMappingURL=index.js.map
|
package/dist/perf/has.perf.js
CHANGED
|
@@ -2,26 +2,41 @@ import assert from 'node:assert';
|
|
|
2
2
|
import { buildITrieFromWords } from 'cspell-trie-lib';
|
|
3
3
|
import { loremIpsum } from 'lorem-ipsum';
|
|
4
4
|
import { suite } from 'perf-insight';
|
|
5
|
+
import { createCachingDictionary } from '../SpellingDictionary/CachingDictionary.js';
|
|
5
6
|
import { createSpellingDictionary } from '../SpellingDictionary/createSpellingDictionary.js';
|
|
6
7
|
import { createCollection } from '../SpellingDictionary/SpellingDictionaryCollection.js';
|
|
7
8
|
suite('dictionary has', async (test) => {
|
|
8
|
-
const
|
|
9
|
+
const words1 = genWords(10_000);
|
|
9
10
|
const words2 = genWords(1000);
|
|
10
11
|
const words3 = genWords(1000);
|
|
11
|
-
const
|
|
12
|
-
const
|
|
12
|
+
const words = words1;
|
|
13
|
+
const iTrie = buildITrieFromWords(words1);
|
|
14
|
+
const dict = createSpellingDictionary(words1, 'test', import.meta.url);
|
|
13
15
|
const dict2 = createSpellingDictionary(words2, 'test2', import.meta.url);
|
|
14
16
|
const dict3 = createSpellingDictionary(words3, 'test3', import.meta.url);
|
|
15
17
|
const dictCol = createCollection([dict, dict2, dict3], 'test-collection');
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const dictColRev = createCollection([dict3, dict2, dict], 'test-collection-reverse');
|
|
19
|
+
const cacheDictSingle = createCachingDictionary(dict, {});
|
|
20
|
+
const cacheDictCol = createCachingDictionary(dictCol, {});
|
|
21
|
+
const dictSet = new Set(words);
|
|
22
|
+
test('Set has 100k words', () => {
|
|
23
|
+
checkWords(dictSet, words);
|
|
18
24
|
});
|
|
19
|
-
test('dictionary has 100k words
|
|
25
|
+
test('dictionary has 100k words', () => {
|
|
20
26
|
checkWords(dict, words);
|
|
21
27
|
});
|
|
22
28
|
test('collection has 100k words', () => {
|
|
23
29
|
checkWords(dictCol, words);
|
|
24
30
|
});
|
|
31
|
+
test('collection reverse has 100k words', () => {
|
|
32
|
+
checkWords(dictColRev, words);
|
|
33
|
+
});
|
|
34
|
+
test('cache dictionary has 100k words', () => {
|
|
35
|
+
checkWords(cacheDictSingle, words);
|
|
36
|
+
});
|
|
37
|
+
test('cache collection has 100k words', () => {
|
|
38
|
+
checkWords(cacheDictCol, words);
|
|
39
|
+
});
|
|
25
40
|
test('iTrie has 100k words', () => {
|
|
26
41
|
checkWords(iTrie, words);
|
|
27
42
|
});
|
|
@@ -43,24 +58,98 @@ suite('dictionary has Not', async (test) => {
|
|
|
43
58
|
const dict2 = createSpellingDictionary(words2, 'test2', import.meta.url);
|
|
44
59
|
const dict3 = createSpellingDictionary(words3, 'test3', import.meta.url);
|
|
45
60
|
const dictCol = createCollection([dict, dict2, dict3], 'test-collection');
|
|
46
|
-
|
|
61
|
+
const dictSet = new Set(words);
|
|
62
|
+
test('Set has not 100k words', () => {
|
|
63
|
+
checkWords(dictSet, missingWords, false);
|
|
64
|
+
});
|
|
65
|
+
test('dictionary has not 100k words', () => {
|
|
47
66
|
checkWords(dict, missingWords, false);
|
|
48
67
|
});
|
|
49
|
-
test('
|
|
68
|
+
test('collection has not 100k words', () => {
|
|
69
|
+
checkWords(dictCol, missingWords, false);
|
|
70
|
+
});
|
|
71
|
+
test('iTrie has not 100k words', () => {
|
|
72
|
+
checkWords(iTrie, missingWords, false);
|
|
73
|
+
});
|
|
74
|
+
test('iTrie.hasWord has not 100k words', () => {
|
|
75
|
+
const dict = { has: (word) => iTrie.hasWord(word, true) };
|
|
50
76
|
checkWords(dict, missingWords, false);
|
|
51
77
|
});
|
|
78
|
+
test('iTrie.data has not 100k words', () => {
|
|
79
|
+
checkWords(iTrie.data, missingWords, false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
suite('dictionary has sampling', async (test) => {
|
|
83
|
+
const words1 = genWords(10_000);
|
|
84
|
+
const words2 = genWords(1000);
|
|
85
|
+
const words3 = genWords(1000);
|
|
86
|
+
const sampleIdx = genSamples(100_000, words1.length);
|
|
87
|
+
const wordsSample = sampleIdx.map((i) => words1[i]);
|
|
88
|
+
const iTrie = buildITrieFromWords(words1);
|
|
89
|
+
const dict = createSpellingDictionary(words1, 'test', import.meta.url);
|
|
90
|
+
const dict2 = createSpellingDictionary(words2, 'test2', import.meta.url);
|
|
91
|
+
const dict3 = createSpellingDictionary(words3, 'test3', import.meta.url);
|
|
92
|
+
const dictCol = createCollection([dict, dict2, dict3], 'test-collection');
|
|
93
|
+
const dictColRev = createCollection([dict3, dict2, dict], 'test-collection-reverse');
|
|
94
|
+
const cacheDictSingle = createCachingDictionary(dict, {});
|
|
95
|
+
const cacheDictCol = createCachingDictionary(dictCol, {});
|
|
96
|
+
const dictSet = new Set(words1);
|
|
97
|
+
test('Set has 100k words', () => {
|
|
98
|
+
checkWords(dictSet, wordsSample);
|
|
99
|
+
});
|
|
100
|
+
test('dictionary has 100k words', () => {
|
|
101
|
+
checkWords(dict, wordsSample);
|
|
102
|
+
});
|
|
52
103
|
test('collection has 100k words', () => {
|
|
53
|
-
checkWords(dictCol,
|
|
104
|
+
checkWords(dictCol, wordsSample);
|
|
105
|
+
});
|
|
106
|
+
test('collection reverse has 100k words', () => {
|
|
107
|
+
checkWords(dictColRev, wordsSample);
|
|
108
|
+
});
|
|
109
|
+
test('cache dictionary has 100k words', () => {
|
|
110
|
+
checkWords(cacheDictSingle, wordsSample);
|
|
111
|
+
});
|
|
112
|
+
test('cache collection has 100k words', () => {
|
|
113
|
+
checkWords(cacheDictCol, wordsSample);
|
|
54
114
|
});
|
|
55
115
|
test('iTrie has 100k words', () => {
|
|
56
|
-
checkWords(iTrie,
|
|
116
|
+
checkWords(iTrie, wordsSample);
|
|
57
117
|
});
|
|
58
118
|
test('iTrie.hasWord has 100k words', () => {
|
|
59
119
|
const dict = { has: (word) => iTrie.hasWord(word, true) };
|
|
60
|
-
checkWords(dict,
|
|
120
|
+
checkWords(dict, wordsSample);
|
|
61
121
|
});
|
|
62
122
|
test('iTrie.data has 100k words', () => {
|
|
63
|
-
checkWords(iTrie.data,
|
|
123
|
+
checkWords(iTrie.data, wordsSample);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
suite('dictionary isForbidden sampling', async (test) => {
|
|
127
|
+
const words1 = genWords(10_000);
|
|
128
|
+
const words2 = genWords(1000);
|
|
129
|
+
const words3 = genWords(1000);
|
|
130
|
+
const sampleIdx = genSamples(100_000, words1.length);
|
|
131
|
+
const wordsSample = sampleIdx.map((i) => words1[i]);
|
|
132
|
+
const dict = createSpellingDictionary(words1, 'test', import.meta.url);
|
|
133
|
+
const dict2 = createSpellingDictionary(words2, 'test2', import.meta.url);
|
|
134
|
+
const dict3 = createSpellingDictionary(words3, 'test3', import.meta.url);
|
|
135
|
+
const dictCol = createCollection([dict, dict2, dict3], 'test-collection');
|
|
136
|
+
const dictColRev = createCollection([dict3, dict2, dict], 'test-collection-reverse');
|
|
137
|
+
const cacheDictSingle = createCachingDictionary(dict, {});
|
|
138
|
+
const cacheDictCol = createCachingDictionary(dictCol, {});
|
|
139
|
+
test('dictionary isForbidden 100k words', () => {
|
|
140
|
+
checkForForbiddenWords(dict, wordsSample);
|
|
141
|
+
});
|
|
142
|
+
test('collection isForbidden 100k words', () => {
|
|
143
|
+
checkForForbiddenWords(dictCol, wordsSample);
|
|
144
|
+
});
|
|
145
|
+
test('collection reverse isForbidden 100k words', () => {
|
|
146
|
+
checkForForbiddenWords(dictColRev, wordsSample);
|
|
147
|
+
});
|
|
148
|
+
test('cache dictionary isForbidden 100k words', () => {
|
|
149
|
+
checkForForbiddenWords(cacheDictSingle, wordsSample);
|
|
150
|
+
});
|
|
151
|
+
test('cache collection isForbidden 100k words', () => {
|
|
152
|
+
checkForForbiddenWords(cacheDictCol, wordsSample);
|
|
64
153
|
});
|
|
65
154
|
});
|
|
66
155
|
function checkWords(dict, words, expected = true, totalChecks = 100_000) {
|
|
@@ -76,6 +165,16 @@ function checkWords(dict, words, expected = true, totalChecks = 100_000) {
|
|
|
76
165
|
}
|
|
77
166
|
assert(has, 'All words should be found in the dictionary');
|
|
78
167
|
}
|
|
168
|
+
function checkForForbiddenWords(dict, words, totalChecks = 100_000) {
|
|
169
|
+
let result = true;
|
|
170
|
+
const len = words.length;
|
|
171
|
+
for (let i = 0; i < totalChecks; ++i) {
|
|
172
|
+
const word = words[i % len];
|
|
173
|
+
const r = !dict.isForbidden(word);
|
|
174
|
+
result = r && result;
|
|
175
|
+
}
|
|
176
|
+
assert(result, 'All words should not be forbidden');
|
|
177
|
+
}
|
|
79
178
|
function genWords(count, includeForbidden = true) {
|
|
80
179
|
const setOfWords = new Set(loremIpsum({ count }).split(' '));
|
|
81
180
|
if (includeForbidden) {
|
|
@@ -101,4 +200,15 @@ function genWords(count, includeForbidden = true) {
|
|
|
101
200
|
}
|
|
102
201
|
return [...setOfWords];
|
|
103
202
|
}
|
|
203
|
+
function genSamples(count, max, depth = 3) {
|
|
204
|
+
const r = Array(count);
|
|
205
|
+
for (let j = 0; j < count; ++j) {
|
|
206
|
+
let n = Math.random() * max;
|
|
207
|
+
for (let i = 1; i < depth; ++i) {
|
|
208
|
+
n = Math.random() * n;
|
|
209
|
+
}
|
|
210
|
+
r[j] = Math.floor(n);
|
|
211
|
+
}
|
|
212
|
+
return r;
|
|
213
|
+
}
|
|
104
214
|
//# sourceMappingURL=has.perf.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { genSequence } from 'gensequence';
|
|
2
|
+
import { loremIpsum } from 'lorem-ipsum';
|
|
3
|
+
import { suite } from 'perf-insight';
|
|
4
|
+
suite('Array Primitives', async (test) => {
|
|
5
|
+
const words = genWords(20);
|
|
6
|
+
const toFind = [...words, 'not-a-word'];
|
|
7
|
+
const iterations = 1000;
|
|
8
|
+
test('Array.find', () => {
|
|
9
|
+
for (let i = 0; i < iterations; ++i) {
|
|
10
|
+
for (const word of toFind) {
|
|
11
|
+
words.find((w) => w === word);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
test('genSequence.first', () => {
|
|
16
|
+
for (let i = 0; i < iterations; ++i) {
|
|
17
|
+
for (const word of toFind) {
|
|
18
|
+
genSequence(words).first((w) => w === word);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
function genWords(count, includeForbidden = true) {
|
|
24
|
+
const setOfWords = new Set(loremIpsum({ count }).split(' '));
|
|
25
|
+
if (includeForbidden) {
|
|
26
|
+
setOfWords.add('!forbidden');
|
|
27
|
+
setOfWords.add('!bad-word');
|
|
28
|
+
setOfWords.add('!rejection');
|
|
29
|
+
}
|
|
30
|
+
while (setOfWords.size < count) {
|
|
31
|
+
const words = [...setOfWords];
|
|
32
|
+
for (const a of words) {
|
|
33
|
+
for (const b of words) {
|
|
34
|
+
if (a !== b) {
|
|
35
|
+
setOfWords.add(a + b);
|
|
36
|
+
}
|
|
37
|
+
if (setOfWords.size >= count) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (setOfWords.size >= count) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...setOfWords];
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=misc.perf.js.map
|
package/dist/util/AutoCache.d.ts
CHANGED
|
@@ -6,17 +6,14 @@ export interface CacheStats {
|
|
|
6
6
|
misses: number;
|
|
7
7
|
swaps: number;
|
|
8
8
|
}
|
|
9
|
-
declare class Cache01<R> implements CacheStats {
|
|
9
|
+
declare abstract class Cache01<R> implements CacheStats {
|
|
10
10
|
readonly maxSize: number;
|
|
11
|
-
private count;
|
|
12
|
-
private cache0;
|
|
13
|
-
private cache1;
|
|
14
11
|
hits: number;
|
|
15
12
|
misses: number;
|
|
16
13
|
swaps: number;
|
|
17
14
|
constructor(maxSize: number);
|
|
18
|
-
get(key: string): R | undefined;
|
|
19
|
-
set(key: string, value: R): this;
|
|
15
|
+
abstract get(key: string): R | undefined;
|
|
16
|
+
abstract set(key: string, value: R): this;
|
|
20
17
|
}
|
|
21
18
|
export declare function createCache01<R>(size: number): Cache01<R>;
|
|
22
19
|
export declare function autoCache<R>(fn: (p: string) => R, size?: number): AutoCache<R>;
|
package/dist/util/AutoCache.js
CHANGED
|
@@ -1,46 +1,54 @@
|
|
|
1
1
|
const CACHE_SIZE = 100;
|
|
2
2
|
class Cache01 {
|
|
3
3
|
maxSize;
|
|
4
|
-
count = 0;
|
|
5
|
-
cache0 = Object.create(null);
|
|
6
|
-
cache1 = Object.create(null);
|
|
7
4
|
hits = 0;
|
|
8
5
|
misses = 0;
|
|
9
6
|
swaps = 0;
|
|
10
7
|
constructor(maxSize) {
|
|
11
8
|
this.maxSize = maxSize;
|
|
12
9
|
}
|
|
10
|
+
}
|
|
11
|
+
class Cache01Map extends Cache01 {
|
|
12
|
+
count = 0;
|
|
13
|
+
cache0 = new Map();
|
|
14
|
+
cache1 = new Map();
|
|
15
|
+
constructor(maxSize) {
|
|
16
|
+
super(maxSize);
|
|
17
|
+
}
|
|
13
18
|
get(key) {
|
|
14
19
|
const cache0 = this.cache0;
|
|
15
20
|
const cache1 = this.cache1;
|
|
16
|
-
|
|
21
|
+
let found = cache0.get(key);
|
|
22
|
+
if (found !== undefined) {
|
|
17
23
|
++this.hits;
|
|
18
|
-
return
|
|
24
|
+
return found;
|
|
19
25
|
}
|
|
20
|
-
|
|
26
|
+
found = cache1.get(key);
|
|
27
|
+
if (found !== undefined) {
|
|
21
28
|
++this.hits;
|
|
22
29
|
++this.count;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return r;
|
|
30
|
+
cache0.set(key, found);
|
|
31
|
+
return found;
|
|
26
32
|
}
|
|
27
33
|
++this.misses;
|
|
28
34
|
return undefined;
|
|
29
35
|
}
|
|
30
36
|
set(key, value) {
|
|
31
37
|
if (this.count >= this.maxSize) {
|
|
38
|
+
const c = this.cache1;
|
|
32
39
|
this.cache1 = this.cache0;
|
|
33
|
-
this.cache0 =
|
|
40
|
+
this.cache0 = c;
|
|
41
|
+
c.clear();
|
|
34
42
|
this.swaps++;
|
|
35
43
|
this.count = 0;
|
|
36
44
|
}
|
|
37
45
|
++this.count;
|
|
38
|
-
this.cache0
|
|
46
|
+
this.cache0.set(key, value);
|
|
39
47
|
return this;
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
export function createCache01(size) {
|
|
43
|
-
return new
|
|
51
|
+
return new Cache01Map(size);
|
|
44
52
|
}
|
|
45
53
|
export function autoCache(fn, size = CACHE_SIZE) {
|
|
46
54
|
const cache = createCache01(size);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cspell-dictionary",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.13.0",
|
|
4
4
|
"description": "A spelling dictionary library useful for checking words and getting suggestions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
"node": ">=18"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@cspell/cspell-pipe": "8.
|
|
54
|
-
"@cspell/cspell-types": "8.
|
|
55
|
-
"cspell-trie-lib": "8.
|
|
56
|
-
"fast-equals": "^5.0.1"
|
|
57
|
-
"gensequence": "^7.0.0"
|
|
53
|
+
"@cspell/cspell-pipe": "8.13.0",
|
|
54
|
+
"@cspell/cspell-types": "8.13.0",
|
|
55
|
+
"cspell-trie-lib": "8.13.0",
|
|
56
|
+
"fast-equals": "^5.0.1"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
59
|
+
"gensequence": "^7.0.0",
|
|
60
60
|
"lorem-ipsum": "^2.0.8"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "2fd3fb430cc96a8a50543f57d96b288219a11923"
|
|
63
63
|
}
|