cspell-lib 10.2.1 → 10.3.0-alpha.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.
@@ -107,7 +107,7 @@ class _DictionaryDefinitionInternalWithSource {
107
107
  this.__source = sourceURL.href;
108
108
  // this bit of assignment is to have the compiler help use if any new fields are added.
109
109
  const defAll = def;
110
- const { path: relPath = '', file = '', btrie, addWords, description, dictionaryInformation, type, repMap, noSuggest, ignoreForbiddenWords, scope, supportNonStrictSearches, useCompounds, } = defAll;
110
+ const { path: relPath = '', file = '', btrie, addWords, description, dictionaryInformation, type, repMap, noSuggest, kind, ignoreForbiddenWords, scope, supportNonStrictSearches, useCompounds, } = defAll;
111
111
  const defaultPath = sourceURL;
112
112
  const filePath = fixDicPath(relPath, file);
113
113
  const name = determineName(filePath, def);
@@ -125,6 +125,7 @@ class _DictionaryDefinitionInternalWithSource {
125
125
  type,
126
126
  repMap,
127
127
  noSuggest,
128
+ kind,
128
129
  ignoreForbiddenWords,
129
130
  supportNonStrictSearches,
130
131
  scope,
@@ -52,7 +52,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import { opConcatMap, opFilter, opMap, pipe } from '@cspell/cspell-pipe/sync';
54
54
  import { StrongWeakMap } from '@cspell/strong-weak-map';
55
- import { createFailedToLoadDictionary, createInlineSpellingDictionary, createSpellingDictionary, createSpellingDictionaryFromTrieFile, } from 'cspell-dictionary';
55
+ import { createFailedToLoadDictionary, createFlagWordsDictionary, createFlagWordsDictionaryFromTrieFile, createInlineSpellingDictionary, createSpellingDictionary, createSpellingDictionaryFromTrieFile, } from 'cspell-dictionary';
56
56
  import { compareStats, toFileURL, urlBasename } from 'cspell-io';
57
57
  import { measurePerf } from '../../perf/index.js';
58
58
  import { isDictionaryDefinitionInlineInternal, isDictionaryFileDefinitionInternal } from '../../Settings/index.js';
@@ -221,7 +221,13 @@ function toReader(fs) {
221
221
  readLines: async (filename) => toLines(await readText(filename)),
222
222
  };
223
223
  }
224
- const importantOptionKeys = ['name', 'noSuggest', 'useCompounds', 'type'];
224
+ const importantOptionKeys = [
225
+ 'name',
226
+ 'noSuggest',
227
+ 'kind',
228
+ 'useCompounds',
229
+ 'type',
230
+ ];
225
231
  function isError(e) {
226
232
  const err = e;
227
233
  return !!err.message;
@@ -248,7 +254,10 @@ async function legacyWordList(reader, filename, options) {
248
254
  opMap((line) => line.replaceAll(/#.*/g, '')),
249
255
  // Split on everything else
250
256
  opConcatMap((line) => line.split(/[^\w\p{L}\p{M}'’]+/gu)), opFilter((word) => !!word));
251
- return createSpellingDictionary(words, options.name, filename.toString(), options, true);
257
+ if (options.kind === 'flag-words') {
258
+ return createFlagWordsDictionary([...words], options.name, filename.toString());
259
+ }
260
+ return createSpellingDictionary(words, options.name, filename.toString(), applyKind(options), true);
252
261
  }
253
262
  catch (e_1) {
254
263
  env_1.error = e_1;
@@ -265,14 +274,20 @@ async function wordsPerLineWordList(reader, filename, options) {
265
274
  opMap((line) => line.replaceAll(/#.*/g, '')),
266
275
  // Split on everything else
267
276
  opConcatMap((line) => line.split(/\s+/gu)), opFilter((word) => !!word));
268
- return createSpellingDictionary(words, options.name, filename.href, options, true);
277
+ if (options.kind === 'flag-words') {
278
+ return createFlagWordsDictionary([...words], options.name, filename.href);
279
+ }
280
+ return createSpellingDictionary(words, options.name, filename.href, applyKind(options), true);
269
281
  }
270
282
  async function loadSimpleWordList(reader, filename, options) {
271
283
  const env_2 = { stack: [], error: void 0, hasError: false };
272
284
  try {
273
285
  const lines = await reader.readLines(filename);
274
286
  const _ = __addDisposableResource(env_2, measurePerf('loadSimpleWordList'), false);
275
- return createSpellingDictionary(lines, options.name, filename.href, options);
287
+ if (options.kind === 'flag-words') {
288
+ return createFlagWordsDictionary(lines, options.name, filename.href);
289
+ }
290
+ return createSpellingDictionary(lines, options.name, filename.href, applyKind(options));
276
291
  }
277
292
  catch (e_2) {
278
293
  env_2.error = e_2;
@@ -287,7 +302,10 @@ async function loadTrie(reader, filename, options) {
287
302
  try {
288
303
  const content = await reader.read(filename);
289
304
  const _ = __addDisposableResource(env_3, measurePerf('loadTrie'), false);
290
- return createSpellingDictionaryFromTrieFile(content, options.name, filename.href, options);
305
+ if (options.kind === 'flag-words') {
306
+ return createFlagWordsDictionaryFromTrieFile(content, options.name, filename.href);
307
+ }
308
+ return createSpellingDictionaryFromTrieFile(content, options.name, filename.href, applyKind(options));
291
309
  }
292
310
  catch (e_3) {
293
311
  env_3.error = e_3;
@@ -297,6 +315,13 @@ async function loadTrie(reader, filename, options) {
297
315
  __disposeResources(env_3);
298
316
  }
299
317
  }
318
+ /**
319
+ * Apply the effect of `kind` on the options passed to a normal `words` dictionary loader.
320
+ * `flag-words` is handled by the callers directly since it uses a different dictionary implementation.
321
+ */
322
+ function applyKind(options) {
323
+ return options.kind === 'ignore-words' && !options.noSuggest ? { ...options, noSuggest: true } : options;
324
+ }
300
325
  function toLines(content) {
301
326
  return content.split(/\n|\r\n|\r/);
302
327
  }
@@ -329,10 +329,16 @@ export function lineValidatorFactory(sDict, options) {
329
329
  });
330
330
  // Ignore 1 or 2 letter segments.
331
331
  const minWordLen = Math.min(3, minWordLength);
332
+ const keepIssue = (w) => w.isFlagged ||
333
+ w.text.length >= minWordLen ||
334
+ // eslint-disable-next-line unicorn/prefer-code-point
335
+ (w.text.charCodeAt(0) & 0xff80) !== 0 || // Keep if Non-ASCII
336
+ // eslint-disable-next-line unicorn/prefer-code-point
337
+ (w.text.charCodeAt(1) & 0xff80) !== 0; // Keep if Non-ASCII
332
338
  const filtered = filterExcludedTextOffsets(nonMatching
333
339
  .map((w) => ({ ...w, line: lineSegment.line }))
334
340
  .map(annotateIsFlagged)
335
- .filter((w) => w.isFlagged || w.text.length >= minWordLen), hexSequences);
341
+ .filter(keepIssue), hexSequences);
336
342
  if (filtered.length < mismatches.length) {
337
343
  return filtered;
338
344
  }
@@ -1,8 +1,62 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { counter, measurePerf } from '@cspell/cspell-performance-monitor';
1
54
  import { PairingHeap } from '../PairingHeap.js';
2
55
  import { regExFirstUpper, regExNumericLiteral, regExWordsAndDigits } from '../textRegex.js';
3
56
  import { generateWordBreaks, softHyphen } from './generateWordBreaks.js';
4
57
  const ignoreBreak = Object.freeze([]);
5
- const maxSkippedBreaks = 8; // Maximum number of breaks that can be skipped during word splitting.
58
+ const maxSkippedBreaks = 16; // Maximum number of breaks that can be skipped during word splitting.
59
+ const maxAttempts = 64_000; // Maximum number of attempts to split words.
6
60
  // Quote/backtick characters that `regExWordsAndDigits` treats as word characters, so a quoted
7
61
  // or templated string literal in source code gets scanned with its delimiters attached.
8
62
  const regExLeadingTrimCharacters = /^['’`\\-]+/;
@@ -16,98 +70,110 @@ const regExTrailingTrimCharacters = /['’`\\-]+$/;
16
70
  * @returns SplitResult
17
71
  */
18
72
  export function split(line, offset, isValidWord, options = {}) {
19
- const relWordToSplit = findNextWordText({ text: line.text, offset: offset - line.offset });
20
- const hasSoftHyphen = relWordToSplit.text.includes(softHyphen);
21
- const lineOffset = line.offset;
22
- const requested = new Map();
23
- const regExpIgnoreSegment = /^[-.+\d_eE'`\\\s\u00AD]+$/;
24
- if (!relWordToSplit.text) {
25
- const text = rebaseTextOffsetToAbsolute(relWordToSplit);
26
- return {
27
- line,
28
- offset,
29
- text: text,
30
- words: [],
31
- endOffset: text.offset + (text.length ?? text.text.length),
32
- };
33
- }
34
- const lineSegment = {
35
- line,
36
- relStart: relWordToSplit.offset,
37
- relEnd: relWordToSplit.offset + relWordToSplit.text.length,
38
- };
39
- const possibleBreaks = generateWordBreaks(lineSegment, options);
40
- if (!possibleBreaks.length) {
41
- const text = rebaseTextOffsetToAbsolute(relWordToSplit);
42
- return {
73
+ const env_1 = { stack: [], error: void 0, hasError: false };
74
+ try {
75
+ const _x = __addDisposableResource(env_1, measurePerf('wordSplitter.split'), false);
76
+ counter('wordSplitter.split').inc();
77
+ const relWordToSplit = findNextWordText({ text: line.text, offset: offset - line.offset });
78
+ const hasSoftHyphen = relWordToSplit.text.includes(softHyphen);
79
+ const lineOffset = line.offset;
80
+ const requested = new Map();
81
+ const regExpIgnoreSegment = /^[-.+\d_eE'`\\\s\u00AD]+$/;
82
+ if (!relWordToSplit.text) {
83
+ const text = rebaseTextOffsetToAbsolute(relWordToSplit);
84
+ return {
85
+ line,
86
+ offset,
87
+ text: text,
88
+ words: [],
89
+ endOffset: text.offset + (text.length ?? text.text.length),
90
+ };
91
+ }
92
+ const lineSegment = {
43
93
  line,
44
- offset,
45
- text: text,
46
- words: [{ ...text, length: text.length || text.text.length, isFound: isValidWord(text) }],
47
- endOffset: text.offset + (text.length ?? text.text.length),
48
- };
49
- }
50
- function rebaseTextOffsetToAbsolute(relText) {
51
- relText.offset += lineOffset;
52
- return relText;
53
- }
54
- function rebaseTextOffsetToRelative(relText) {
55
- relText.offset -= lineOffset;
56
- return relText;
57
- }
58
- function createTextOffsetWithValid(start, end, isFound) {
59
- const word = {
60
- offset: start,
61
- length: end - start,
62
- text: line.text.slice(start, end),
63
- isFound,
94
+ relStart: relWordToSplit.offset,
95
+ relEnd: relWordToSplit.offset + relWordToSplit.text.length,
64
96
  };
65
- return word;
66
- }
67
- function calcKey(start, end) {
68
- const i = start;
69
- const j = end - start;
70
- const v = i + (j << 20);
71
- return i < 1 << 20 && j < 1 << 11 ? v : -1;
72
- }
73
- function checkTextRange(start, end) {
74
- const v = calcKey(start, end);
75
- const f = v >= 0 ? requested.get(v) : undefined;
76
- if (f)
77
- return f;
78
- const validated = createTextOffsetWithValid(start, end, false);
79
- if (regExpIgnoreSegment.test(validated.text)) {
80
- validated.isFound = true;
97
+ const possibleBreaks = generateWordBreaks(lineSegment, options);
98
+ if (!possibleBreaks.length) {
99
+ const text = rebaseTextOffsetToAbsolute(relWordToSplit);
100
+ return {
101
+ line,
102
+ offset,
103
+ text: text,
104
+ words: [{ ...text, length: text.length || text.text.length, isFound: isValidWord(text) }],
105
+ endOffset: text.offset + (text.length ?? text.text.length),
106
+ };
107
+ }
108
+ function rebaseTextOffsetToAbsolute(relText) {
109
+ relText.offset += lineOffset;
110
+ return relText;
111
+ }
112
+ function rebaseTextOffsetToRelative(relText) {
113
+ relText.offset -= lineOffset;
114
+ return relText;
115
+ }
116
+ function createTextOffsetWithValid(start, end, isFound) {
117
+ const word = {
118
+ offset: start,
119
+ length: end - start,
120
+ text: line.text.slice(start, end),
121
+ isFound,
122
+ };
123
+ return word;
124
+ }
125
+ function calcKey(start, end) {
126
+ const i = start;
127
+ const j = end - start;
128
+ const v = i + (j << 20);
129
+ return i < 1 << 20 && j < 1 << 11 ? v : -1;
130
+ }
131
+ function checkTextRange(start, end) {
132
+ const v = calcKey(start, end);
133
+ const f = v >= 0 ? requested.get(v) : undefined;
134
+ if (f)
135
+ return f;
136
+ const validated = createTextOffsetWithValid(start, end, false);
137
+ if (regExpIgnoreSegment.test(validated.text)) {
138
+ validated.isFound = true;
139
+ if (v >= 0) {
140
+ requested.set(v, validated);
141
+ }
142
+ return validated;
143
+ }
144
+ rebaseTextOffsetToAbsolute(validated);
145
+ validated.isFound = isValidWord(validated);
146
+ if (hasSoftHyphen && !validated.isFound && validated.text.includes(softHyphen)) {
147
+ removeSoftHyphen(validated);
148
+ validated.isFound = isValidWord(validated);
149
+ }
150
+ rebaseTextOffsetToRelative(validated);
81
151
  if (v >= 0) {
82
152
  requested.set(v, validated);
83
153
  }
84
154
  return validated;
85
155
  }
86
- rebaseTextOffsetToAbsolute(validated);
87
- validated.isFound = isValidWord(validated);
88
- if (hasSoftHyphen && !validated.isFound && validated.text.includes(softHyphen)) {
89
- removeSoftHyphen(validated);
90
- validated.isFound = isValidWord(validated);
91
- }
92
- rebaseTextOffsetToRelative(validated);
93
- if (v >= 0) {
94
- requested.set(v, validated);
95
- }
96
- return validated;
156
+ // Add a dummy break at the end to avoid needing to check for last break.
157
+ possibleBreaks.push({
158
+ offset: lineSegment.relEnd,
159
+ breaks: [ignoreBreak],
160
+ });
161
+ const result = {
162
+ line,
163
+ offset,
164
+ text: rebaseTextOffsetToAbsolute(relWordToSplit),
165
+ words: splitIntoWords(lineSegment, possibleBreaks, checkTextRange).map(rebaseTextOffsetToAbsolute),
166
+ endOffset: lineOffset + lineSegment.relEnd,
167
+ };
168
+ return result;
169
+ }
170
+ catch (e_1) {
171
+ env_1.error = e_1;
172
+ env_1.hasError = true;
173
+ }
174
+ finally {
175
+ __disposeResources(env_1);
97
176
  }
98
- // Add a dummy break at the end to avoid needing to check for last break.
99
- possibleBreaks.push({
100
- offset: lineSegment.relEnd,
101
- breaks: [ignoreBreak],
102
- });
103
- const result = {
104
- line,
105
- offset,
106
- text: rebaseTextOffsetToAbsolute(relWordToSplit),
107
- words: splitIntoWords(lineSegment, possibleBreaks, checkTextRange).map(rebaseTextOffsetToAbsolute),
108
- endOffset: lineOffset + lineSegment.relEnd,
109
- };
110
- return result;
111
177
  }
112
178
  function removeSoftHyphen(word) {
113
179
  word.text = word.text.replaceAll(softHyphen, '');
@@ -132,7 +198,6 @@ function findNextWordText({ text, offset }) {
132
198
  }
133
199
  function splitIntoWords(lineSeg, breaks, checkTextRange) {
134
200
  const maxIndex = lineSeg.relEnd;
135
- const maxAttempts = 1000;
136
201
  /**
137
202
  * Map of known paths by their starting index in the text.
138
203
  * This helps in quickly finding previously computed paths to avoid redundant calculations.
@@ -182,6 +247,7 @@ function splitIntoWords(lineSeg, breaks, checkTextRange) {
182
247
  * @param currentCost - current cost accrued
183
248
  */
184
249
  function makeCandidates(p, i, bi, bs, currentCost) {
250
+ counter('wordSplitter.makeCandidates').inc();
185
251
  const len = maxIndex;
186
252
  const nBi = findNearestBreakIndex(i, bi);
187
253
  const nBs = findNearestBreakIndex(i, bs);
@@ -195,6 +261,7 @@ function splitIntoWords(lineSeg, breaks, checkTextRange) {
195
261
  }
196
262
  const br = breaks[bi];
197
263
  function calcBreakCost(bp) {
264
+ counter('wordSplitter.calcBreakCost').inc();
198
265
  if (bp.length < 2) {
199
266
  // We are skipping this break pair.
200
267
  return { p, i, j: len, bi, bs, bp, c: currentCost, ec: currentCost + len - i, text: undefined };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "provenance": true
6
6
  },
7
- "version": "10.2.1",
7
+ "version": "10.3.0-alpha.0",
8
8
  "description": "A library of useful functions used across various cspell tools.",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -75,22 +75,22 @@
75
75
  },
76
76
  "homepage": "https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-lib#readme",
77
77
  "dependencies": {
78
- "@cspell/cspell-bundled-dicts": "10.2.1",
79
- "@cspell/cspell-performance-monitor": "10.2.1",
80
- "@cspell/cspell-pipe": "10.2.1",
81
- "@cspell/cspell-resolver": "10.2.1",
82
- "@cspell/cspell-types": "10.2.1",
83
- "@cspell/dynamic-import": "10.2.1",
84
- "@cspell/filetypes": "10.2.1",
85
- "@cspell/rpc": "10.2.1",
86
- "@cspell/strong-weak-map": "10.2.1",
87
- "@cspell/url": "10.2.1",
88
- "cspell-config-lib": "10.2.1",
89
- "cspell-dictionary": "10.2.1",
90
- "cspell-glob": "10.2.1",
91
- "cspell-grammar": "10.2.1",
92
- "cspell-io": "10.2.1",
93
- "cspell-trie-lib": "10.2.1",
78
+ "@cspell/cspell-bundled-dicts": "10.3.0-alpha.0",
79
+ "@cspell/cspell-performance-monitor": "10.3.0-alpha.0",
80
+ "@cspell/cspell-pipe": "10.3.0-alpha.0",
81
+ "@cspell/cspell-resolver": "10.3.0-alpha.0",
82
+ "@cspell/cspell-types": "10.3.0-alpha.0",
83
+ "@cspell/dynamic-import": "10.3.0-alpha.0",
84
+ "@cspell/filetypes": "10.3.0-alpha.0",
85
+ "@cspell/rpc": "10.3.0-alpha.0",
86
+ "@cspell/strong-weak-map": "10.3.0-alpha.0",
87
+ "@cspell/url": "10.3.0-alpha.0",
88
+ "cspell-config-lib": "10.3.0-alpha.0",
89
+ "cspell-dictionary": "10.3.0-alpha.0",
90
+ "cspell-glob": "10.3.0-alpha.0",
91
+ "cspell-grammar": "10.3.0-alpha.0",
92
+ "cspell-io": "10.3.0-alpha.0",
93
+ "cspell-trie-lib": "10.3.0-alpha.0",
94
94
  "env-paths": "^4.0.0",
95
95
  "gensequence": "^8.0.8",
96
96
  "import-fresh": "^4.0.0",
@@ -103,7 +103,7 @@
103
103
  "node": ">=22.18.0"
104
104
  },
105
105
  "devDependencies": {
106
- "@cspell/cspell-tools": "10.2.1",
106
+ "@cspell/cspell-tools": "10.3.0-alpha.0",
107
107
  "@cspell/dict-cpp": "^7.1.0",
108
108
  "@cspell/dict-csharp": "^4.0.8",
109
109
  "@cspell/dict-css": "^4.1.2",
@@ -123,5 +123,5 @@
123
123
  "lorem-ipsum": "^3.0.0",
124
124
  "perf-insight": "^2.0.1"
125
125
  },
126
- "gitHead": "67a64ad0f3f4dcb681f9b9deac60c9758619ffbc"
126
+ "gitHead": "39297075c7a4946feb1a4f0a509e329cfb0b4f11"
127
127
  }