cspell-lib 9.4.0 → 9.6.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/README.md CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  <!--- @@inject: ../../static/sponsor.md --->
8
8
 
9
- - [![GitHub Sponsors](https://img.shields.io/badge/-black?style=social&logo=githubsponsors&label=GitHub%20Sponsor%3A%20Street%20Side%20Software)](https://github.com/sponsors/streetsidesoftware)
10
- - [![PayPal](https://img.shields.io/badge/-black?style=social&logo=paypal&label=PayPal%20Donate%3A%20Street%20Side%20Software)](https://www.paypal.com/donate/?hosted_button_id=26LNBP2Q6MKCY)
11
- - [![Open Collective](https://img.shields.io/badge/-black?style=social&logo=opencollective&label=Open%20Collective%3A%20CSpell)](https://opencollective.com/cspell)
9
+ [![GitHub Sponsors](https://img.shields.io/badge/-Street_Side_Software-black?style=for-the-badge&logo=githubsponsors&label=GitHub%20Sponsor%3A)](https://github.com/sponsors/streetsidesoftware)
12
10
 
13
- <!---
14
- - [![Patreon](https://img.shields.io/badge/-black?style=social&logo=patreon&label=Patreon%3A%20Street%20Side%20Software)](https://patreon.com/streetsidesoftware)
15
- --->
11
+ [![Open Collective](https://img.shields.io/badge/-CSpell-black?style=for-the-badge&logo=opencollective&label=Open%20Collective%3A)](https://opencollective.com/cspell)
12
+
13
+ [![Street Side Software](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fstreetsidesoftware%2Fcspell%2Frefs%2Fheads%2Fmain%2Fstatic%2Fcspell-badge.json)](https://streetsidesoftware.com/sponsor/)
14
+
15
+ [![PayPal](https://img.shields.io/badge/-Street_Side_Software-black?style=for-the-badge&logo=paypal&label=PayPal%20Donate%3A)](https://www.paypal.com/donate/?hosted_button_id=26LNBP2Q6MKCY)
16
16
 
17
17
  <!--- @@inject-end: ../../static/sponsor.md --->
18
18
 
@@ -2,6 +2,9 @@ import { isError } from '../../util/errors.js';
2
2
  export class ImportError extends Error {
3
3
  cause;
4
4
  constructor(msg, cause) {
5
+ if (isError(cause)) {
6
+ msg += `\n ${cause.message}`;
7
+ }
5
8
  super(msg);
6
9
  this.cause = isError(cause) ? cause : undefined;
7
10
  }
@@ -1,11 +1,13 @@
1
1
  import type { CSpellSettings, CSpellUserSettings, ImportFileRef } from '@cspell/cspell-types';
2
- import { CSpellConfigFile, CSpellConfigFileReaderWriter, ICSpellConfigFile } from 'cspell-config-lib';
2
+ import type { CSpellConfigFileReaderWriter, ICSpellConfigFile } from 'cspell-config-lib';
3
+ import { CSpellConfigFile, CSpellConfigFileWithErrors } from 'cspell-config-lib';
3
4
  import type { VFileSystem } from '../../../fileSystem.js';
4
- import { AutoResolveCache, CacheStats } from '../../../util/AutoResolve.js';
5
+ import type { CacheStats } from '../../../util/AutoResolve.js';
6
+ import { AutoResolveCache } from '../../../util/AutoResolve.js';
5
7
  import { FileResolver } from '../../../util/resolveFile.js';
6
8
  import type { LoaderResult } from '../pnpLoader.js';
7
9
  import { ConfigSearch } from './configSearch.js';
8
- import { StopSearchAt } from './defaultConfigLoader.js';
10
+ import type { StopSearchAt } from './defaultConfigLoader.js';
9
11
  import { normalizeCacheSettings } from './normalizeRawSettings.js';
10
12
  import type { PnPSettingsOptional } from './PnPSettings.js';
11
13
  import type { CSpellSettingsI, CSpellSettingsWST } from './types.js';
@@ -123,7 +125,7 @@ export declare class ConfigLoader implements IConfigLoader {
123
125
  readSettingsAsync(filename: string | URL, relativeTo?: string | URL, pnpSettings?: PnPSettingsOptional): Promise<CSpellSettingsI>;
124
126
  readConfigFile(filenameOrURL: string | URL, relativeTo?: string | URL): Promise<CSpellConfigFile | Error>;
125
127
  searchForConfigFileLocation(searchFrom: URL | string | undefined, stopSearchAt?: URL[] | undefined): Promise<URL | undefined>;
126
- searchForConfigFile(searchFrom: URL | string | undefined, stopSearchAt?: URL[]): Promise<CSpellConfigFile | undefined>;
128
+ searchForConfigFile(searchFrom: URL | string | undefined, stopSearchAt?: URL[]): Promise<CSpellConfigFile | CSpellConfigFileWithErrors | undefined>;
127
129
  /**
128
130
  *
129
131
  * @param searchFrom the directory / file URL to start searching from.
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { CSpellConfigFile } from 'cspell-config-lib';
4
+ import { CSpellConfigFile, CSpellConfigFileWithErrors } from 'cspell-config-lib';
5
5
  import { createReaderWriter } from 'cspell-config-lib';
6
6
  import { isUrlLike, toFileURL } from 'cspell-io';
7
7
  import { URI, Utils as UriUtils } from 'vscode-uri';
@@ -15,13 +15,13 @@ import { FileResolver } from '../../../util/resolveFile.js';
15
15
  import { envToTemplateVars } from '../../../util/templates.js';
16
16
  import { addTrailingSlash, cwdURL, resolveFileWithURL, toFileDirURL, toFilePathOrHref, toFileUrl, windowsDriveLetterToUpper, } from '../../../util/url.js';
17
17
  import { configSettingsFileVersion0_1, configSettingsFileVersion0_2, currentSettingsFileVersion, defaultConfigFileModuleRef, ENV_CSPELL_GLOB_ROOT, } from '../../constants.js';
18
- import { getMergeStats, mergeSettings } from '../../CSpellSettingsServer.js';
18
+ import { getMergeStats, mergeSettings, toInternalSettings } from '../../CSpellSettingsServer.js';
19
19
  import { getGlobalConfig } from '../../GlobalSettings.js';
20
20
  import { ImportError } from '../ImportError.js';
21
21
  import { pnpLoader } from '../pnpLoader.js';
22
22
  import { searchPlaces } from './configLocations.js';
23
23
  import { ConfigSearch } from './configSearch.js';
24
- import { configToRawSettings } from './configToRawSettings.js';
24
+ import { configErrorToRawSettings, configToRawSettings } from './configToRawSettings.js';
25
25
  import { defaultSettings } from './defaultSettings.js';
26
26
  import { normalizeCacheSettings, normalizeDictionaryDefs, normalizeGitignoreRoot, normalizeImport, normalizeLanguageSettings, normalizeOverrides, normalizeReporters, normalizeSettingsGlobs, } from './normalizeRawSettings.js';
27
27
  import { defaultPnPSettings, normalizePnPSettings } from './PnPSettings.js';
@@ -31,7 +31,7 @@ export const sectionCSpell = 'cSpell';
31
31
  export const defaultFileName = 'cspell.json';
32
32
  let defaultConfigLoader = undefined;
33
33
  const defaultExtensions = ['.json', '.yaml', '.yml', '.jsonc', '.toml'];
34
- const defaultJsExtensions = ['.js', '.cjs', '.mjs', '.ts', '.mts'];
34
+ const defaultJsExtensions = ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts'];
35
35
  export const defaultExtensionsAll = [...defaultExtensions, ...defaultJsExtensions];
36
36
  const trustedSearch = new Map([
37
37
  ['*', defaultExtensions],
@@ -110,7 +110,10 @@ export class ConfigLoader {
110
110
  if (!location)
111
111
  return undefined;
112
112
  const file = await this.readConfigFile(location);
113
- return file instanceof Error ? undefined : file;
113
+ if (file instanceof Error) {
114
+ return new CSpellConfigFileWithErrors(location, configErrorToRawSettings(file, location), file);
115
+ }
116
+ return file;
114
117
  }
115
118
  /**
116
119
  *
@@ -123,6 +126,9 @@ export class ConfigLoader {
123
126
  const configFile = await this.searchForConfigFile(searchFrom, stopAt);
124
127
  if (!configFile)
125
128
  return undefined;
129
+ if (configFile instanceof CSpellConfigFileWithErrors) {
130
+ return toInternalSettings(configFile.settings);
131
+ }
126
132
  return this.mergeConfigFileWithImports(configFile, options);
127
133
  }
128
134
  getGlobalSettings() {
@@ -9,6 +9,7 @@ export const supportedExtensions = [
9
9
  '.toml',
10
10
  '.mts',
11
11
  '.ts',
12
+ '.cts',
12
13
  ];
13
14
  /**
14
15
  * Logic of the locations:
@@ -27,10 +27,12 @@ function fixPath(def) {
27
27
  return def;
28
28
  }
29
29
  const newPath = fixDicPath(def.path, def.file);
30
+ const newBTriePath = def.btrie ? fixDicPath(def.btrie, def.file) : undefined;
30
31
  return {
31
32
  ...def,
32
33
  file: undefined,
33
34
  path: newPath,
35
+ btrie: newBTriePath,
34
36
  };
35
37
  }
36
38
  function fixDicPath(defPath, defFile) {
@@ -103,15 +105,18 @@ class _DictionaryDefinitionInternalWithSource {
103
105
  this.__source = sourceURL.href;
104
106
  // this bit of assignment is to have the compiler help use if any new fields are added.
105
107
  const defAll = def;
106
- const { path: relPath = '', file = '', addWords, description, dictionaryInformation, type, repMap, noSuggest, ignoreForbiddenWords, scope, supportNonStrictSearches, useCompounds, } = defAll;
108
+ const { path: relPath = '', file = '', btrie, addWords, description, dictionaryInformation, type, repMap, noSuggest, ignoreForbiddenWords, scope, supportNonStrictSearches, useCompounds, } = defAll;
107
109
  const defaultPath = sourceURL;
108
110
  const filePath = fixDicPath(relPath, file);
109
111
  const name = determineName(filePath, def);
110
112
  const resolvedPath = toFilePathOrHref(resolveRelativeTo(filePath, defaultPath));
113
+ let bTriePath = btrie ? fixDicPath(btrie, file) : undefined;
114
+ bTriePath = bTriePath ? toFilePathOrHref(resolveRelativeTo(bTriePath, defaultPath)) : undefined;
111
115
  const ddi = {
112
116
  name,
113
117
  file: undefined,
114
118
  path: resolvedPath,
119
+ btrie: bTriePath,
115
120
  addWords,
116
121
  description,
117
122
  dictionaryInformation,
@@ -5,14 +5,14 @@ export const regExHRef = /\bhref\s*=\s*".*?"/gi;
5
5
  export const regExMatchCommonHexFormats = /(?:#[0-9a-f]{3,8})|(?:0x[0-9a-f]+)|(?:\\u[0-9a-f]{4})|(?:\\x\{[0-9a-f]{4}\})/gi;
6
6
  export const regExCommitHash = /\b(?![a-f]+\b)(?:0x)?[0-9a-f]{7,}\b/gi; // Match Commit Hashes that contain at least one digit.
7
7
  export const regExCommitHashLink = /\[[0-9a-f]{7,}\]/gi; // Match Commit Hash Markdown link: [abcdef0].
8
- export const regExCStyleHexValue = /\b0x[0-9a-f_]+\b/gi;
8
+ export const regExCStyleHexValue = /\b0x[0-9a-f_]+n?\b/gi;
9
9
  export const regExCSSHexValue = /#[0-9a-f]{3,8}\b/gi;
10
10
  export const regExUUID = /\b[0-9a-fx]{8}-[0-9a-fx]{4}-[0-9a-fx]{4}-[0-9a-fx]{4}-[0-9a-fx]{12}\b/gi; // x - represents placeholder values
11
11
  export const regExUnicodeRef = /\bU\+[0-9a-f]{4,5}(?:-[0-9a-f]{4,5})?/gi;
12
12
  export const regExSpellingGuardBlock = /(\bc?spell(?:-?checker)?::?)\s*disable(?!-line|-next)\b(?!-)[\s\S]*?((?:\1\s*enable\b)|$)/gi;
13
13
  export const regExSpellingGuardNext = /\bc?spell(?:-?checker)?::?\s*disable-next\b.*\s\s?.*/gi;
14
14
  export const regExSpellingGuardLine = /^.*\bc?spell(?:-?checker)?::?\s*disable-line\b.*/gim;
15
- export const regExIgnoreSpellingDirectives = /\bc?spell(?:-?checker)?::?\s*ignoreRegExp.*/gim;
15
+ export const regExIgnoreSpellingDirectives = /\bc?spell(?:-?checker)?::?\s*(ignoreRegExp|word|ignore).*/gim;
16
16
  export const regExPublicKey = /-{5}BEGIN\s+((?:RSA\s+)?PUBLIC\s+KEY)[\w=+\-/=\\\s]+?END\s+\1-{5}/g;
17
17
  export const regExCert = /-{5}BEGIN\s+(CERTIFICATE|(?:RSA\s+)?(?:PRIVATE|PUBLIC)\s+KEY)[\w=+\-/=\\\s]+?END\s+\1-{5}/g;
18
18
  export const regExSshRSA = /ssh-rsa\s+[a-z0-9/+]{28,}={0,3}(?![a-z0-9/+=])/gi;
@@ -1,4 +1,4 @@
1
- import { CSpellSettings } from '@cspell/cspell-types';
1
+ import type { CSpellSettings } from '@cspell/cspell-types';
2
2
  export declare const legacyLocationDir: string | undefined;
3
3
  export declare const cspellGlobalLocationDir: string;
4
4
  export declare const defaultConfigFileName = "cspell.json";
@@ -78,7 +78,7 @@ export async function removePathsFromGlobalImports(paths) {
78
78
  return c === Path.sep || c === Path.posix.sep;
79
79
  }
80
80
  function matchFilename(pathToRemove) {
81
- return Path.dirname(pathToRemove) != '.'
81
+ return Path.dirname(pathToRemove) !== '.'
82
82
  ? ({ filename }) => compareFilenames(filename, pathToRemove)
83
83
  : () => false;
84
84
  }
@@ -1,4 +1,4 @@
1
- import { CacheStats } from '../util/AutoResolve.js';
1
+ import type { CacheStats } from '../util/AutoResolve.js';
2
2
  interface IDisposable {
3
3
  dispose(): void;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { CacheStats } from '../util/AutoResolve.js';
1
+ import type { CacheStats } from '../util/AutoResolve.js';
2
2
  /**
3
3
  * Merges two lists and removes duplicates. Order is NOT preserved.
4
4
  */
@@ -3,6 +3,7 @@ import { StrongWeakMap } from '@cspell/strong-weak-map';
3
3
  import { createFailedToLoadDictionary, createInlineSpellingDictionary, createSpellingDictionary, createSpellingDictionaryFromTrieFile, } from 'cspell-dictionary';
4
4
  import { compareStats, toFileURL, urlBasename } from 'cspell-io';
5
5
  import { isDictionaryDefinitionInlineInternal, isDictionaryFileDefinitionInternal, } from '../../Models/CSpellSettingsInternalDef.js';
6
+ import { measurePerfFn } from '../../perf/index.js';
6
7
  import { AutoResolveWeakCache, AutoResolveWeakWeakCache } from '../../util/AutoResolve.js';
7
8
  import { toError } from '../../util/errors.js';
8
9
  import { SimpleCache } from '../../util/simpleCache.js';
@@ -42,7 +43,7 @@ export class DictionaryLoader {
42
43
  if (entry) {
43
44
  return entry.pending.then(([dictionary]) => dictionary);
44
45
  }
45
- const loadedEntry = this.loadEntry(def.path, def);
46
+ const loadedEntry = this.loadEntry(def.btrie || def.path, def);
46
47
  this.setCacheEntry(key, loadedEntry, def);
47
48
  this.keepAliveCache.set(def, loadedEntry);
48
49
  return loadedEntry.pending.then(([dictionary]) => dictionary);
@@ -98,8 +99,8 @@ export class DictionaryLoader {
98
99
  loadEntry(fileOrUri, options, now = Date.now()) {
99
100
  const url = toFileURL(fileOrUri);
100
101
  options = this.normalizeOptions(url, options);
101
- const pDictionary = load(this.reader, toFileURL(fileOrUri), options).catch((e) => createFailedToLoadDictionary(options.name, fileOrUri, new SpellingDictionaryLoadError(url.href, options, e, 'failed to load'), options));
102
- const pStat = this.getStat(fileOrUri);
102
+ const pDictionary = load(this.reader, url, options).catch((e) => createFailedToLoadDictionary(options.name, fileOrUri, new SpellingDictionaryLoadError(url.href, options, e, 'failed to load'), options));
103
+ const pStat = this.getStat(url);
103
104
  const pending = Promise.all([pDictionary, pStat]);
104
105
  const sig = now + Math.random();
105
106
  const entry = {
@@ -153,12 +154,19 @@ export class DictionaryLoader {
153
154
  }
154
155
  }
155
156
  function toReader(fs) {
156
- async function readFile(url) {
157
- return (await fs.readFile(url)).getText();
157
+ function readResource(url) {
158
+ return fs.readFile(url);
159
+ }
160
+ async function readText(url) {
161
+ return (await readResource(url)).getText();
162
+ }
163
+ async function read(url) {
164
+ return (await readResource(url)).getBytes();
158
165
  }
159
166
  return {
160
- read: readFile,
161
- readLines: async (filename) => toLines(await readFile(filename)),
167
+ read,
168
+ readText,
169
+ readLines: async (filename) => toLines(await readText(filename)),
162
170
  };
163
171
  }
164
172
  const importantOptionKeys = ['name', 'noSuggest', 'useCompounds', 'type'];
@@ -170,17 +178,17 @@ function determineType(uri, opts) {
170
178
  const t = (opts.type && opts.type in loaders && opts.type) || 'S';
171
179
  const defLoaderType = t;
172
180
  const defType = uri.pathname.endsWith('.trie.gz') ? 'T' : defLoaderType;
173
- const regTrieTest = /\.trie\b/i;
181
+ const regTrieTest = /\.b?trie\b/i;
174
182
  return regTrieTest.test(uri.pathname) ? 'T' : defType;
175
183
  }
176
- function load(reader, uri, options) {
184
+ async function load(reader, uri, options) {
177
185
  const type = determineType(uri, options);
178
186
  const loader = loaders[type] || loaders.default;
179
187
  return loader(reader, uri, options);
180
188
  }
181
189
  async function legacyWordList(reader, filename, options) {
182
190
  const lines = await reader.readLines(filename);
183
- return _legacyWordListSync(lines, filename, options);
191
+ return measurePerfFn('legacyWords', () => _legacyWordListSync(lines, filename, options));
184
192
  }
185
193
  function _legacyWordListSync(lines, filename, options) {
186
194
  const words = pipe(lines,
@@ -188,11 +196,11 @@ function _legacyWordListSync(lines, filename, options) {
188
196
  opMap((line) => line.replaceAll(/#.*/g, '')),
189
197
  // Split on everything else
190
198
  opConcatMap((line) => line.split(/[^\w\p{L}\p{M}'’]+/gu)), opFilter((word) => !!word));
191
- return createSpellingDictionary(words, options.name, filename.toString(), options);
199
+ return createSpellingDictionary(words, options.name, filename.toString(), options, true);
192
200
  }
193
201
  async function wordsPerLineWordList(reader, filename, options) {
194
202
  const lines = await reader.readLines(filename);
195
- return _wordsPerLineWordList(lines, filename.toString(), options);
203
+ return measurePerfFn('wordsPerLineWordList', () => _wordsPerLineWordList(lines, filename.toString(), options));
196
204
  }
197
205
  function _wordsPerLineWordList(lines, filename, options) {
198
206
  const words = pipe(lines,
@@ -200,15 +208,15 @@ function _wordsPerLineWordList(lines, filename, options) {
200
208
  opMap((line) => line.replaceAll(/#.*/g, '')),
201
209
  // Split on everything else
202
210
  opConcatMap((line) => line.split(/\s+/gu)), opFilter((word) => !!word));
203
- return createSpellingDictionary(words, options.name, filename, options);
211
+ return createSpellingDictionary(words, options.name, filename, options, true);
204
212
  }
205
213
  async function loadSimpleWordList(reader, filename, options) {
206
214
  const lines = await reader.readLines(filename);
207
- return createSpellingDictionary(lines, options.name, filename.href, options);
215
+ return measurePerfFn('loadSimpleWordList', () => createSpellingDictionary(lines, options.name, filename.href, options));
208
216
  }
209
217
  async function loadTrie(reader, filename, options) {
210
218
  const content = await reader.read(filename);
211
- return createSpellingDictionaryFromTrieFile(content, options.name, filename.href, options);
219
+ return measurePerfFn('loadTrie', () => createSpellingDictionaryFromTrieFile(content, options.name, filename.href, options));
212
220
  }
213
221
  function toLines(content) {
214
222
  return content.split(/\n|\r\n|\r/);
@@ -1,3 +1,4 @@
1
+ export { measurePerf, measurePerfEnd, measurePerfFn, measurePerfStart } from './performance.js';
1
2
  export type { PerfTimer } from './timer.js';
2
3
  export { createPerfTimer } from './timer.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,3 @@
1
+ export { measurePerf, measurePerfEnd, measurePerfFn, measurePerfStart } from './performance.js';
1
2
  export { createPerfTimer } from './timer.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,16 @@
1
+ export declare function measurePerfStart(name: string): void;
2
+ export declare function measurePerfEnd(name: string): void;
3
+ /**
4
+ * Creates performance marks and measures the time taken between them.
5
+ * @param name - name of the performance entry
6
+ * @returns a function to stop the timer.
7
+ */
8
+ export declare function measurePerf(name: string): () => void;
9
+ /**
10
+ * Measure the performance of a function.
11
+ * @param name - name of the performance entry
12
+ * @param fn - function to measure
13
+ * @returns the result of the function
14
+ */
15
+ export declare function measurePerfFn<R>(name: string, fn: () => R): R;
16
+ //# sourceMappingURL=performance.d.ts.map
@@ -0,0 +1,34 @@
1
+ export function measurePerfStart(name) {
2
+ performance.mark(name + '-start');
3
+ }
4
+ export function measurePerfEnd(name) {
5
+ performance.mark(name + '-end');
6
+ performance.measure(name, name + '-start', name + '-end');
7
+ }
8
+ /**
9
+ * Creates performance marks and measures the time taken between them.
10
+ * @param name - name of the performance entry
11
+ * @returns a function to stop the timer.
12
+ */
13
+ export function measurePerf(name) {
14
+ measurePerfStart(name);
15
+ return () => {
16
+ measurePerfEnd(name);
17
+ };
18
+ }
19
+ /**
20
+ * Measure the performance of a function.
21
+ * @param name - name of the performance entry
22
+ * @param fn - function to measure
23
+ * @returns the result of the function
24
+ */
25
+ export function measurePerfFn(name, fn) {
26
+ const perfEnd = measurePerf(name);
27
+ try {
28
+ return fn();
29
+ }
30
+ finally {
31
+ perfEnd();
32
+ }
33
+ }
34
+ //# sourceMappingURL=performance.js.map
@@ -1,5 +1,5 @@
1
1
  import type { CSpellSettingsWithSourceTrace, CSpellUserSettings } from '@cspell/cspell-types';
2
- import { ICSpellConfigFile } from 'cspell-config-lib';
2
+ import type { ICSpellConfigFile } from 'cspell-config-lib';
3
3
  import type { Document, DocumentWithText } from './Document/index.js';
4
4
  import type { Uri } from './util/IUri.js';
5
5
  import type { ValidateTextOptions, ValidationIssue } from './validator.js';
@@ -1,5 +1,5 @@
1
1
  import type { CSpellSettings, LocaleId } from '@cspell/cspell-types';
2
- import { ICSpellConfigFile } from 'cspell-config-lib';
2
+ import type { ICSpellConfigFile } from 'cspell-config-lib';
3
3
  import type { LanguageId } from './fileTypes.js';
4
4
  import type { SpellingDictionaryCollection, SuggestionResult, SuggestOptions } from './SpellingDictionary/index.js';
5
5
  export interface WordSuggestion extends SuggestionResult {
@@ -1,5 +1,5 @@
1
1
  import type { CSpellUserSettings, MappedText, ParsedText } from '@cspell/cspell-types';
2
- import { ICSpellConfigFile } from 'cspell-config-lib';
2
+ import type { ICSpellConfigFile } from 'cspell-config-lib';
3
3
  import type { CSpellSettingsInternal, CSpellSettingsInternalFinalized } from '../Models/CSpellSettingsInternalDef.js';
4
4
  import type { ExtendedSuggestion } from '../Models/Suggestion.js';
5
5
  import type { TextDocument, TextDocumentRef } from '../Models/TextDocument.js';
@@ -9,7 +9,7 @@ import type { WordSuggestion } from '../suggestions.js';
9
9
  import type { MatchRange } from '../util/TextRange.js';
10
10
  import type { TextValidator } from './lineValidatorFactory.js';
11
11
  import type { SimpleRange } from './parsedText.js';
12
- import { TraceResult } from './traceWord.js';
12
+ import type { TraceResult } from './traceWord.js';
13
13
  import type { ValidateTextOptions } from './ValidateTextOptions.js';
14
14
  import type { ValidationOptions } from './ValidationTypes.js';
15
15
  export interface DocumentValidatorOptions extends ValidateTextOptions {
@@ -5,8 +5,8 @@ import { toFilePathOrHref, toFileURL } from '@cspell/url';
5
5
  import { satisfiesCSpellConfigFile } from 'cspell-config-lib';
6
6
  import { getGlobMatcherForExcluding } from '../globs/getGlobMatcher.js';
7
7
  import { documentUriToURL, updateTextDocument } from '../Models/TextDocument.js';
8
- import { createPerfTimer } from '../perf/index.js';
9
- import { finalizeSettings, loadConfig, mergeSettings, resolveConfigFileImports, resolveSettingsImports, searchForConfig, } from '../Settings/index.js';
8
+ import { createPerfTimer, measurePerf } from '../perf/index.js';
9
+ import { extractImportErrors, finalizeSettings, loadConfig, mergeSettings, resolveConfigFileImports, resolveSettingsImports, searchForConfig, } from '../Settings/index.js';
10
10
  import { validateInDocumentSettings } from '../Settings/InDocSettings.js';
11
11
  import { getDictionaryInternal } from '../SpellingDictionary/index.js';
12
12
  import { calcSuggestionAdjustedToToMatchCase } from '../suggestions.js';
@@ -82,13 +82,14 @@ export class DocumentValidator {
82
82
  ? timePromise(this.perfTiming, '__searchForDocumentConfig', searchForDocumentConfig(this._document, settings, settings))
83
83
  : undefined;
84
84
  if (pLocalConfig) {
85
- timePromise(this.perfTiming, '_loadConfig', pLocalConfig);
85
+ timePromise(this.perfTiming, '_loadConfig', pLocalConfig).catch(() => undefined);
86
86
  }
87
87
  const localConfig = (await catchPromiseError(pLocalConfig, (e) => this.addPossibleError(e))) || {};
88
- this.addPossibleError(localConfig?.__importRef?.error);
88
+ extractImportErrors(localConfig).forEach((e) => this.addPossibleError(e.error));
89
89
  const config = mergeSettings(settings, localConfig);
90
90
  const docSettings = await timePromise(this.perfTiming, '_determineTextDocumentSettings', determineTextDocumentSettings(this._document, config));
91
91
  const dict = await timePromise(this.perfTiming, '_getDictionaryInternal', getDictionaryInternal(docSettings));
92
+ const stopMeasure = measurePerf('DocumentValidator._prepareAsync');
92
93
  const recGlobMatcherTime = recordPerfTime(this.perfTiming, '_GlobMatcher');
93
94
  const matcher = getGlobMatcherForExcluding(localConfig?.ignorePaths);
94
95
  const uri = this._document.uri;
@@ -120,6 +121,7 @@ export class DocumentValidator {
120
121
  this._ready = true;
121
122
  this._preparationTime = timer.elapsed;
122
123
  this.perfTiming.prepTime = this._preparationTime;
124
+ stopMeasure();
123
125
  }
124
126
  async _updatePrep() {
125
127
  assert(this._preparations, ERROR_NOT_PREPARED);
@@ -127,6 +129,7 @@ export class DocumentValidator {
127
129
  const prep = this._preparations;
128
130
  const docSettings = await determineTextDocumentSettings(this._document, prep.config);
129
131
  const dict = await getDictionaryInternal(docSettings);
132
+ const stopMeasure = measurePerf('DocumentValidator._updatePrep');
130
133
  const shouldCheck = docSettings.enabled ?? true;
131
134
  const finalSettings = finalizeSettings(docSettings);
132
135
  const validateOptions = settingsToValidateOptions(finalSettings);
@@ -144,6 +147,7 @@ export class DocumentValidator {
144
147
  textValidator,
145
148
  };
146
149
  this._preparationTime = timer.elapsed;
150
+ stopMeasure();
147
151
  }
148
152
  /**
149
153
  * The amount of time in ms to prepare for validation.
@@ -297,6 +301,7 @@ export class DocumentValidator {
297
301
  const { maxNumberOfProblems = defaultMaxNumberOfProblems, maxDuplicateProblems = defaultMaxDuplicateProblems } = this._preparations.validateOptions;
298
302
  let numProblems = 0;
299
303
  const mapOfProblems = new Map();
304
+ const stopMeasure = measurePerf('DocumentValidator._checkParsedText');
300
305
  for (const pText of parsedTexts) {
301
306
  for (const issue of this.check(pText)) {
302
307
  const { text } = issue;
@@ -309,6 +314,7 @@ export class DocumentValidator {
309
314
  return;
310
315
  }
311
316
  }
317
+ stopMeasure();
312
318
  }
313
319
  addPossibleError(error) {
314
320
  if (!error)
@@ -422,7 +428,7 @@ export async function shouldCheckDocument(doc, options, settings) {
422
428
  ? searchForDocumentConfig(doc, settings, settings)
423
429
  : undefined;
424
430
  const localConfig = (await catchPromiseError(pLocalConfig, addPossibleError)) || {};
425
- addPossibleError(localConfig?.__importRef?.error);
431
+ extractImportErrors(localConfig).forEach((e) => addPossibleError(e.error));
426
432
  const config = mergeSettings(settings, localConfig);
427
433
  const matcher = getGlobMatcherForExcluding(localConfig?.ignorePaths);
428
434
  // eslint-disable-next-line unicorn/prefer-regexp-test
@@ -1,4 +1,4 @@
1
- import { TextOffset } from '@cspell/cspell-types';
1
+ import type { TextOffset } from '@cspell/cspell-types';
2
2
  /**
3
3
  * Try to detect if a string is a random string of characters or is it camel case / snake case words.
4
4
  * @param s - string to check
@@ -13,7 +13,7 @@ import { isWordValidWithEscapeRetry } from './isWordValid.js';
13
13
  import { mapRangeBackToOriginalPos } from './parsedText.js';
14
14
  const MIN_HEX_SEQUENCE_LENGTH = 8;
15
15
  export function lineValidatorFactory(sDict, options) {
16
- const { minWordLength = defaultMinWordLength, flagWords = [], allowCompoundWords = false, ignoreCase = true, ignoreRandomStrings = defaultCSpellSettings.ignoreRandomStrings, minRandomLength = defaultCSpellSettings.minRandomLength, unknownWords = unknownWordsChoices.ReportAll, } = options;
16
+ const { minWordLength = defaultMinWordLength, flagWords = [], allowCompoundWords = false, ignoreCase = true, ignoreRandomStrings = defaultCSpellSettings.ignoreRandomStrings, minRandomLength = defaultCSpellSettings.minRandomLength, unknownWords = unknownWordsChoices.ReportAll, numSuggestions, } = options;
17
17
  const hasWordOptions = {
18
18
  ignoreCase,
19
19
  useCompounds: allowCompoundWords || undefined, // let the dictionaries decide on useCompounds if allow is false
@@ -67,17 +67,21 @@ export function lineValidatorFactory(sDict, options) {
67
67
  return autoResolve(cacheGetPreferredSuggestions, word, () => dictCol.getPreferredSuggestions(word));
68
68
  }
69
69
  const cacheHasSimpleSuggestions = new Map();
70
- function hasSimpleSuggestions(word) {
70
+ function getSimpleSuggestions(word) {
71
+ const numSug = numSuggestions ?? 5;
71
72
  return autoResolve(cacheHasSimpleSuggestions, word, () => {
72
73
  const sugs = dictCol.suggest(word, {
73
74
  numSuggestions: 1,
74
75
  compoundMethod: 0,
75
- includeTies: false,
76
+ includeTies: true, // We want the top suggestions even if there are ties
76
77
  ignoreCase,
77
78
  timeout: 100,
78
79
  numChanges: 1.8, // Only consider very simple changes (1 edit distance plus case changes)
79
80
  });
80
- return !!sugs.length;
81
+ if (sugs.length > numSug) {
82
+ sugs.length = numSug;
83
+ }
84
+ return sugs;
81
85
  });
82
86
  }
83
87
  function isWordFlagged(wo) {
@@ -92,7 +96,11 @@ export function lineValidatorFactory(sDict, options) {
92
96
  if (!sugs?.length) {
93
97
  issue.hasPreferredSuggestions = sugs !== undefined ? false : undefined;
94
98
  if (unknownWords === unknownWordsChoices.ReportSimple) {
95
- issue.hasSimpleSuggestions = hasSimpleSuggestions(issue.text);
99
+ const sug = getSimpleSuggestions(issue.text);
100
+ issue.hasSimpleSuggestions = !!sug.length;
101
+ if (sug.length) {
102
+ issue.suggestionsEx = sug.map((s) => ({ ...s, isPreferred: !!s.isPreferred }));
103
+ }
96
104
  }
97
105
  return issue;
98
106
  }
@@ -31,6 +31,7 @@ export interface TraceResult extends Array<DictionaryTraceResult> {
31
31
  }
32
32
  export interface TraceOptions extends Pick<CSpellSettingsWithSourceTrace, 'source' | 'allowCompoundWords'> {
33
33
  ignoreCase?: boolean;
34
+ compoundSeparator?: string | undefined;
34
35
  }
35
36
  export declare function traceWord(word: string, dictCollection: SpellingDictionaryCollection, config: TraceOptions): TraceResult;
36
37
  export {};
@@ -7,6 +7,7 @@ export function traceWord(word, dictCollection, config) {
7
7
  const opts = {
8
8
  ignoreCase: config.ignoreCase ?? true,
9
9
  useCompounds: config.allowCompoundWords || false,
10
+ compoundSeparator: '•',
10
11
  };
11
12
  const splits = split({ text: word, offset: 0 }, 0, checkWord);
12
13
  const wfSplits = splits.words.map((s) => ({ word: s.text, found: s.isFound }));
@@ -1,5 +1,5 @@
1
1
  import type { CSpellSettings, LocaleId } from '@cspell/cspell-types';
2
- import { ICSpellConfigFile } from 'cspell-config-lib';
2
+ import type { ICSpellConfigFile } from 'cspell-config-lib';
3
3
  import type { LanguageId } from './fileTypes.js';
4
4
  import type { DictionaryTraceResult, WordSplits } from './textValidation/traceWord.js';
5
5
  export interface TraceResult extends DictionaryTraceResult {
@@ -11,6 +11,7 @@ export interface TraceOptions {
11
11
  locale?: LocaleId;
12
12
  ignoreCase?: boolean;
13
13
  allowCompoundWords?: boolean;
14
+ compoundSeparator?: string | undefined;
14
15
  }
15
16
  export interface TraceWordResult extends Array<TraceResult> {
16
17
  splits: readonly WordSplits[];
package/dist/lib/trace.js CHANGED
@@ -15,7 +15,7 @@ export async function traceWords(words, settings, options) {
15
15
  return s;
16
16
  }
17
17
  export async function* traceWordsAsync(words, settingsOrConfig, options) {
18
- const { languageId, locale: language, ignoreCase = true, allowCompoundWords } = options || {};
18
+ const { languageId, locale: language, ignoreCase = true, allowCompoundWords, compoundSeparator } = options || {};
19
19
  const settings = satisfiesCSpellConfigFile(settingsOrConfig)
20
20
  ? await resolveConfigFileImports(settingsOrConfig)
21
21
  : settingsOrConfig;
@@ -44,7 +44,7 @@ export async function* traceWordsAsync(words, settingsOrConfig, options) {
44
44
  const { config, dicts, activeDictionaries } = await finalize(settings);
45
45
  const setOfActiveDicts = new Set(activeDictionaries);
46
46
  function processWord(word) {
47
- const results = traceWord(word, dicts, { ...config, ignoreCase });
47
+ const results = traceWord(word, dicts, { ...config, ignoreCase, compoundSeparator });
48
48
  const r = results.map((r) => ({
49
49
  ...r,
50
50
  dictActive: setOfActiveDicts.has(r.dictName),
@@ -26,7 +26,7 @@ export function isError(e) {
26
26
  if (!e || typeof e !== 'object')
27
27
  return false;
28
28
  const ex = e;
29
- return typeof ex.name == 'string' && typeof ex.message == 'string' && (typeof ex.stack) in allowStringOrUndefined;
29
+ return typeof ex.name === 'string' && typeof ex.message === 'string' && (typeof ex.stack) in allowStringOrUndefined;
30
30
  }
31
31
  export function toError(e, errorFactory = UnknownError) {
32
32
  if (isError(e))
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "provenance": true
6
6
  },
7
- "version": "9.4.0",
7
+ "version": "9.6.0",
8
8
  "description": "A library of useful functions used across various cspell tools.",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -64,21 +64,21 @@
64
64
  },
65
65
  "homepage": "https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-lib#readme",
66
66
  "dependencies": {
67
- "@cspell/cspell-bundled-dicts": "9.4.0",
68
- "@cspell/cspell-pipe": "9.4.0",
69
- "@cspell/cspell-resolver": "9.4.0",
70
- "@cspell/cspell-types": "9.4.0",
71
- "@cspell/dynamic-import": "9.4.0",
72
- "@cspell/filetypes": "9.4.0",
73
- "@cspell/strong-weak-map": "9.4.0",
74
- "@cspell/url": "9.4.0",
67
+ "@cspell/cspell-bundled-dicts": "9.6.0",
68
+ "@cspell/cspell-pipe": "9.6.0",
69
+ "@cspell/cspell-resolver": "9.6.0",
70
+ "@cspell/cspell-types": "9.6.0",
71
+ "@cspell/dynamic-import": "9.6.0",
72
+ "@cspell/filetypes": "9.6.0",
73
+ "@cspell/strong-weak-map": "9.6.0",
74
+ "@cspell/url": "9.6.0",
75
75
  "clear-module": "^4.1.2",
76
- "cspell-config-lib": "9.4.0",
77
- "cspell-dictionary": "9.4.0",
78
- "cspell-glob": "9.4.0",
79
- "cspell-grammar": "9.4.0",
80
- "cspell-io": "9.4.0",
81
- "cspell-trie-lib": "9.4.0",
76
+ "cspell-config-lib": "9.6.0",
77
+ "cspell-dictionary": "9.6.0",
78
+ "cspell-glob": "9.6.0",
79
+ "cspell-grammar": "9.6.0",
80
+ "cspell-io": "9.6.0",
81
+ "cspell-trie-lib": "9.6.0",
82
82
  "env-paths": "^3.0.0",
83
83
  "gensequence": "^8.0.8",
84
84
  "import-fresh": "^3.3.1",
@@ -91,21 +91,23 @@
91
91
  "node": ">=20"
92
92
  },
93
93
  "devDependencies": {
94
- "@cspell/dict-cpp": "^6.0.15",
95
- "@cspell/dict-csharp": "^4.0.7",
96
- "@cspell/dict-css": "^4.0.18",
94
+ "@cspell/cspell-tools": "9.6.0",
95
+ "@cspell/dict-cpp": "^7.0.2",
96
+ "@cspell/dict-csharp": "^4.0.8",
97
+ "@cspell/dict-css": "^4.0.19",
98
+ "@cspell/dict-en_us": "^4.4.27",
97
99
  "@cspell/dict-fa-ir": "^4.0.5",
98
100
  "@cspell/dict-fr-fr": "^2.3.2",
99
- "@cspell/dict-html": "^4.0.13",
101
+ "@cspell/dict-html": "^4.0.14",
100
102
  "@cspell/dict-nl-nl": "^2.4.2",
101
- "@cspell/dict-python": "^4.2.23",
103
+ "@cspell/dict-python": "^4.2.25",
102
104
  "@types/configstore": "^6.0.2",
103
- "comment-json": "^4.4.1",
105
+ "comment-json": "^4.5.1",
104
106
  "configstore": "^7.1.0",
105
107
  "cspell-dict-nl-nl": "^1.1.2",
106
108
  "leaked-handles": "^5.2.0",
107
109
  "lorem-ipsum": "^2.0.8",
108
110
  "perf-insight": "^2.0.1"
109
111
  },
110
- "gitHead": "12dba3d8b880384d1401c765cb2186647f5a266f"
112
+ "gitHead": "163793ddf2a0ad90bc7c90351698a106003297af"
111
113
  }
@@ -1,20 +0,0 @@
1
- interface PerfEntry {
2
- readonly entryType: string;
3
- readonly name: string;
4
- readonly startTime: number;
5
- readonly duration?: number | undefined;
6
- }
7
- interface PerfMark extends PerfEntry {
8
- readonly entryType: 'mark';
9
- }
10
- export declare class PerfMonitor {
11
- private _performance;
12
- private _enabled;
13
- constructor(_performance?: Performance);
14
- mark(name: string): PerfMark;
15
- get enabled(): boolean;
16
- set enabled(value: boolean);
17
- }
18
- export declare const perf: PerfMonitor;
19
- export {};
20
- //# sourceMappingURL=perf.d.ts.map
@@ -1,32 +0,0 @@
1
- export class PerfMonitor {
2
- _performance;
3
- _enabled = false;
4
- constructor(_performance = performance) {
5
- this._performance = _performance;
6
- }
7
- mark(name) {
8
- const mark = ((this._enabled && this._performance.mark(name)) || {
9
- entryType: 'mark',
10
- name,
11
- startTime: performance.now(),
12
- });
13
- return mark;
14
- }
15
- // measure(name: string, startMark: string, endMark: string) {
16
- // return this._enabled && this._performance.measure(name, startMark, endMark);
17
- // }
18
- // clearMarks(name?: string) {
19
- // this._enabled && this._performance.clearMarks(name);
20
- // }
21
- // clearMeasures(name?: string) {
22
- // this._enabled && this._performance.clearMeasures(name);
23
- // }
24
- get enabled() {
25
- return this._enabled;
26
- }
27
- set enabled(value) {
28
- this._enabled = value;
29
- }
30
- }
31
- export const perf = new PerfMonitor();
32
- //# sourceMappingURL=perf.js.map