cspell-lib 8.16.0 → 8.17.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.
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert';
2
2
  import path from 'node:path';
3
- import { fileURLToPath, pathToFileURL } from 'node:url';
3
+ import { fileURLToPath } from 'node:url';
4
4
  import { CSpellConfigFile } from 'cspell-config-lib';
5
5
  import { createReaderWriter } from 'cspell-config-lib';
6
6
  import { isUrlLike, toFileURL } from 'cspell-io';
@@ -13,7 +13,7 @@ import { autoResolve, AutoResolveCache, autoResolveWeak } from '../../../util/Au
13
13
  import { logError, logWarning } from '../../../util/logger.js';
14
14
  import { FileResolver } from '../../../util/resolveFile.js';
15
15
  import { envToTemplateVars } from '../../../util/templates.js';
16
- import { addTrailingSlash, cwdURL, resolveFileWithURL, toFilePathOrHref, toFileUrl, windowsDriveLetterToUpper, } from '../../../util/url.js';
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
18
  import { getMergeStats, mergeSettings } from '../../CSpellSettingsServer.js';
19
19
  import { getGlobalConfig } from '../../GlobalSettings.js';
@@ -70,12 +70,12 @@ export class ConfigLoader {
70
70
  toDispose = [];
71
71
  async readSettingsAsync(filename, relativeTo, pnpSettings) {
72
72
  await this.onReady;
73
- const ref = await this.resolveFilename(filename, relativeTo || pathToFileURL('./'));
73
+ const ref = await this.resolveFilename(filename, relativeTo || toFileDirURL('./'));
74
74
  const entry = this.importSettings(ref, pnpSettings || defaultPnPSettings, []);
75
75
  return entry.onReady;
76
76
  }
77
77
  async readConfigFile(filenameOrURL, relativeTo) {
78
- const ref = await this.resolveFilename(filenameOrURL.toString(), relativeTo || pathToFileURL('./'));
78
+ const ref = await this.resolveFilename(filenameOrURL.toString(), relativeTo || toFileDirURL('./'));
79
79
  const url = toFileURL(ref.filename);
80
80
  const href = url.href;
81
81
  if (ref.error)
@@ -1,5 +1,5 @@
1
- import { pathToFileURL } from 'node:url';
2
1
  import { CSpellConfigFileInMemory, CSpellConfigFileJson } from 'cspell-config-lib';
2
+ import { toFileURL } from 'cspell-io';
3
3
  import { getSourceDirectoryUrl, toFilePathOrHref } from '../util/url.js';
4
4
  import { GlobalConfigStore } from './cfgStore.js';
5
5
  import { configToRawSettings } from './Controller/configLoader/configToRawSettings.js';
@@ -10,7 +10,7 @@ export async function getRawGlobalSettings() {
10
10
  export async function getGlobalConfig() {
11
11
  const name = 'CSpell Configstore';
12
12
  const configPath = getGlobalConfigPath();
13
- let urlGlobal = configPath ? pathToFileURL(configPath) : new URL('global-config.json', getSourceDirectoryUrl());
13
+ let urlGlobal = configPath ? toFileURL(configPath) : new URL('global-config.json', getSourceDirectoryUrl());
14
14
  const source = {
15
15
  name,
16
16
  filename: toFilePathOrHref(urlGlobal),
@@ -20,7 +20,7 @@ export async function getGlobalConfig() {
20
20
  const found = await globalConfig.readConfigFile();
21
21
  if (found && found.config && found.filename) {
22
22
  const cfg = found.config;
23
- urlGlobal = pathToFileURL(found.filename);
23
+ urlGlobal = toFileURL(found.filename);
24
24
  // Only populate globalConf is there are values.
25
25
  if (cfg && Object.keys(cfg).length) {
26
26
  Object.assign(globalConf, cfg);
@@ -1,4 +1,4 @@
1
- import { pathToFileURL } from 'node:url';
1
+ import { toFileDirURL, toFileURL } from '@cspell/url';
2
2
  export class CwdUrlResolver {
3
3
  #lastPath;
4
4
  #lastUrl;
@@ -6,7 +6,7 @@ export class CwdUrlResolver {
6
6
  #cwdUrl;
7
7
  constructor() {
8
8
  this.#cwd = process.cwd();
9
- this.#cwdUrl = pathToFileURL(this.#cwd);
9
+ this.#cwdUrl = toFileDirURL(this.#cwd);
10
10
  this.#lastPath = this.#cwd;
11
11
  this.#lastUrl = this.#cwdUrl;
12
12
  }
@@ -17,12 +17,12 @@ export class CwdUrlResolver {
17
17
  if (path === this.#cwd)
18
18
  return this.#cwdUrl;
19
19
  this.#lastPath = path;
20
- this.#lastUrl = pathToFileURL(path);
20
+ this.#lastUrl = toFileURL(path);
21
21
  return this.#lastUrl;
22
22
  }
23
23
  reset(cwd = process.cwd()) {
24
24
  this.#cwd = cwd;
25
- this.#cwdUrl = pathToFileURL(this.#cwd);
25
+ this.#cwdUrl = toFileDirURL(this.#cwd);
26
26
  }
27
27
  }
28
28
  //# sourceMappingURL=resolveCwd.js.map
@@ -1,4 +1,5 @@
1
1
  import type { CSpellSettingsWithSourceTrace, CSpellUserSettings } from '@cspell/cspell-types';
2
+ import { ICSpellConfigFile } from 'cspell-config-lib';
2
3
  import type { Document, DocumentWithText } from './Document/index.js';
3
4
  import type { Uri } from './util/IUri.js';
4
5
  import type { ValidateTextOptions, ValidationIssue } from './validator.js';
@@ -45,14 +46,14 @@ export interface SpellCheckFileResult {
45
46
  * @param options - options to control checking
46
47
  * @param settings - default settings to use.
47
48
  */
48
- export declare function spellCheckFile(file: string | Uri | URL, options: SpellCheckFileOptions, settings: CSpellUserSettings): Promise<SpellCheckFileResult>;
49
+ export declare function spellCheckFile(file: string | Uri | URL, options: SpellCheckFileOptions, settingsOrConfigFile: CSpellUserSettings | ICSpellConfigFile): Promise<SpellCheckFileResult>;
49
50
  /**
50
51
  * Spell Check a Document.
51
52
  * @param document - document to be checked. If `document.text` is `undefined` the file will be loaded
52
53
  * @param options - options to control checking
53
54
  * @param settings - default settings to use.
54
55
  */
55
- export declare function spellCheckDocument(document: Document | DocumentWithText, options: SpellCheckFileOptions, settings: CSpellUserSettings): Promise<SpellCheckFileResult>;
56
+ export declare function spellCheckDocument(document: Document | DocumentWithText, options: SpellCheckFileOptions, settingsOrConfigFile: CSpellUserSettings | ICSpellConfigFile): Promise<SpellCheckFileResult>;
56
57
  export interface DetermineFinalDocumentSettingsResult {
57
58
  document: DocumentWithText;
58
59
  settings: CSpellSettingsWithSourceTrace;
@@ -1,3 +1,4 @@
1
+ import { satisfiesCSpellConfigFile } from 'cspell-config-lib';
1
2
  import { isBinaryDoc } from './Document/isBinaryDoc.js';
2
3
  import { documentToTextDocument, resolveDocument } from './Document/resolveDocument.js';
3
4
  import { createTextDocument } from './Models/TextDocument.js';
@@ -12,11 +13,11 @@ import { toUri } from './util/Uri.js';
12
13
  * @param options - options to control checking
13
14
  * @param settings - default settings to use.
14
15
  */
15
- export function spellCheckFile(file, options, settings) {
16
+ export function spellCheckFile(file, options, settingsOrConfigFile) {
16
17
  const doc = {
17
18
  uri: toUri(file).toString(),
18
19
  };
19
- return spellCheckDocument(doc, options, settings);
20
+ return spellCheckDocument(doc, options, settingsOrConfigFile);
20
21
  }
21
22
  /**
22
23
  * Spell Check a Document.
@@ -24,12 +25,15 @@ export function spellCheckFile(file, options, settings) {
24
25
  * @param options - options to control checking
25
26
  * @param settings - default settings to use.
26
27
  */
27
- export async function spellCheckDocument(document, options, settings) {
28
+ export async function spellCheckDocument(document, options, settingsOrConfigFile) {
29
+ const settingsUsed = satisfiesCSpellConfigFile(settingsOrConfigFile)
30
+ ? settingsOrConfigFile.settings
31
+ : settingsOrConfigFile;
28
32
  if (isBinaryDoc(document)) {
29
33
  return {
30
34
  document,
31
35
  options,
32
- settingsUsed: settings,
36
+ settingsUsed,
33
37
  localConfigFilepath: undefined,
34
38
  issues: [],
35
39
  checked: false,
@@ -43,14 +47,14 @@ export async function spellCheckDocument(document, options, settings) {
43
47
  return {
44
48
  document,
45
49
  options,
46
- settingsUsed: settings,
50
+ settingsUsed,
47
51
  localConfigFilepath: undefined,
48
52
  issues: [],
49
53
  checked: false,
50
54
  errors: undefined,
51
55
  };
52
56
  }
53
- const result = await spellCheckFullDocument(doc, options, settings);
57
+ const result = await spellCheckFullDocument(doc, options, settingsOrConfigFile);
54
58
  const perf = result.perf || {};
55
59
  perf.loadTimeMs = timer.elapsed;
56
60
  result.perf = perf;
@@ -61,7 +65,7 @@ export async function spellCheckDocument(document, options, settings) {
61
65
  return {
62
66
  document,
63
67
  options,
64
- settingsUsed: settings,
68
+ settingsUsed,
65
69
  localConfigFilepath: undefined,
66
70
  issues: [],
67
71
  checked: false,
@@ -69,7 +73,7 @@ export async function spellCheckDocument(document, options, settings) {
69
73
  };
70
74
  }
71
75
  }
72
- async function spellCheckFullDocument(document, options, settings) {
76
+ async function spellCheckFullDocument(document, options, settingsOrConfigFile) {
73
77
  // if (options.skipValidation) {
74
78
  // return {
75
79
  // document,
@@ -87,15 +91,17 @@ async function spellCheckFullDocument(document, options, settings) {
87
91
  const timerPrepare = createPerfTimer('prepare', (elapsed) => (perf.prepareTimeMs = elapsed));
88
92
  const doc = documentToTextDocument(document);
89
93
  const docValOptions = options;
90
- const docValidator = new DocumentValidator(doc, docValOptions, settings);
91
- await docValidator.prepare().finally(() => timerPrepare.end());
94
+ const docValidator = await DocumentValidator.create(doc, docValOptions, settingsOrConfigFile).finally(() => timerPrepare.end());
92
95
  Object.assign(perf, Object.fromEntries(Object.entries(docValidator.perfTiming).map(([k, v]) => ['_' + k, v])));
93
96
  const prep = docValidator._getPreparations();
94
97
  if (docValidator.errors.length) {
95
98
  return {
96
99
  document,
97
100
  options,
98
- settingsUsed: prep?.localConfig || settings,
101
+ settingsUsed: prep?.localConfig ||
102
+ (satisfiesCSpellConfigFile(settingsOrConfigFile)
103
+ ? settingsOrConfigFile.settings
104
+ : settingsOrConfigFile),
99
105
  localConfigFilepath: prep?.localConfigFilepath,
100
106
  issues: [],
101
107
  checked: false,
@@ -1,4 +1,5 @@
1
1
  import type { CSpellSettings, LocaleId } from '@cspell/cspell-types';
2
+ import { ICSpellConfigFile } from 'cspell-config-lib';
2
3
  import type { LanguageId } from './fileTypes.js';
3
4
  import type { SpellingDictionaryCollection, SuggestionResult, SuggestOptions } from './SpellingDictionary/index.js';
4
5
  export interface WordSuggestion extends SuggestionResult {
@@ -64,7 +65,7 @@ export interface SuggestionOptions extends FromSuggestOptions {
64
65
  includeDefaultConfig?: boolean;
65
66
  }
66
67
  export declare function suggestionsForWords(words: Iterable<string> | AsyncIterable<string>, options?: SuggestionOptions, settings?: CSpellSettings): AsyncIterable<SuggestionsForWordResult>;
67
- export declare function suggestionsForWord(word: string, options?: SuggestionOptions, settings?: CSpellSettings): Promise<SuggestionsForWordResult>;
68
+ export declare function suggestionsForWord(word: string, options?: SuggestionOptions, settings?: CSpellSettings | ICSpellConfigFile): Promise<SuggestionsForWordResult>;
68
69
  export declare function calcSuggestionAdjustedToToMatchCase<T extends SuggestionResult>(originalWord: string, sugs: T[], locale: string | string[] | undefined, ignoreCase: boolean, dict: SpellingDictionaryCollection): (T & WordSuggestion)[];
69
70
  export declare class SuggestionError extends Error {
70
71
  readonly code: string;
@@ -1,5 +1,6 @@
1
1
  import assert from 'node:assert';
2
- import { finalizeSettings, getDefaultSettings, getGlobalSettingsAsync, mergeSettings } from './Settings/index.js';
2
+ import { satisfiesCSpellConfigFile } from 'cspell-config-lib';
3
+ import { finalizeSettings, getDefaultSettings, getGlobalSettingsAsync, mergeSettings, resolveConfigFileImports, } from './Settings/index.js';
3
4
  import { calcSettingsForLanguageId, isValidLocaleIntlFormat, normalizeLocaleIntl, } from './Settings/LanguageSettings.js';
4
5
  import { getDictionaryInternal, refreshDictionaryCache } from './SpellingDictionary/index.js';
5
6
  import { createAutoResolveCache } from './util/AutoResolve.js';
@@ -8,8 +9,9 @@ import * as util from './util/util.js';
8
9
  const emptySuggestionOptions = Object.freeze({});
9
10
  const emptyCSpellSettings = Object.freeze({});
10
11
  export async function* suggestionsForWords(words, options, settings) {
12
+ const cspellSettings = satisfiesCSpellConfigFile(settings) ? await resolveConfigFileImports(settings) : settings;
11
13
  for await (const word of words) {
12
- yield await suggestionsForWord(word, options, settings);
14
+ yield await suggestionsForWord(word, options, cspellSettings);
13
15
  }
14
16
  }
15
17
  const memorizeSuggestions = memorizeLastCall(cacheSuggestionsForWord);
@@ -17,8 +19,9 @@ function cacheSuggestionsForWord(options, settings) {
17
19
  const cache = createAutoResolveCache();
18
20
  return (word) => cache.get(word, (word) => _suggestionsForWord(word, options, settings));
19
21
  }
20
- export function suggestionsForWord(word, options = emptySuggestionOptions, settings = emptyCSpellSettings) {
21
- return memorizeSuggestions(options, settings)(word);
22
+ export async function suggestionsForWord(word, options = emptySuggestionOptions, settings = emptyCSpellSettings) {
23
+ const cspellSettings = satisfiesCSpellConfigFile(settings) ? await resolveConfigFileImports(settings) : settings;
24
+ return memorizeSuggestions(options, cspellSettings)(word);
22
25
  }
23
26
  async function _suggestionsForWord(word, options, settings) {
24
27
  const { languageId, locale: language, includeDefaultConfig = true, dictionaries } = options;
@@ -11,6 +11,8 @@ export interface ValidationOptions extends IncludeExcludeOptions {
11
11
  allowCompoundWords?: boolean;
12
12
  /** ignore case when checking words against dictionary or ignore words list */
13
13
  ignoreCase: boolean;
14
+ ignoreRandomStrings?: boolean | undefined;
15
+ minRandomLength?: number | undefined;
14
16
  }
15
17
  export interface CheckOptions extends ValidationOptions {
16
18
  allowCompoundWords: boolean;
@@ -1,4 +1,5 @@
1
1
  import type { CSpellUserSettings, MappedText, ParsedText } from '@cspell/cspell-types';
2
+ import { ICSpellConfigFile } from 'cspell-config-lib';
2
3
  import type { CSpellSettingsInternal, CSpellSettingsInternalFinalized } from '../Models/CSpellSettingsInternalDef.js';
3
4
  import type { ExtendedSuggestion } from '../Models/Suggestion.js';
4
5
  import type { TextDocument, TextDocumentRef } from '../Models/TextDocument.js';
@@ -44,6 +45,7 @@ export declare class DocumentValidator {
44
45
  readonly options: DocumentValidatorOptions;
45
46
  readonly perfTiming: PerfTimings;
46
47
  skipValidation: boolean;
48
+ static create(doc: TextDocument, options: DocumentValidatorOptions, settingsOrConfigFile: CSpellUserSettings | ICSpellConfigFile): Promise<DocumentValidator>;
47
49
  /**
48
50
  * @param doc - Document to validate
49
51
  * @param config - configuration to use (not finalized).
@@ -1,12 +1,12 @@
1
1
  import assert from 'node:assert';
2
- import { pathToFileURL } from 'node:url';
3
2
  import { opConcatMap, opMap, pipeSync } from '@cspell/cspell-pipe/sync';
4
3
  import { IssueType } from '@cspell/cspell-types';
5
4
  import { toFilePathOrHref, toFileURL } from '@cspell/url';
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
8
  import { createPerfTimer } from '../perf/index.js';
9
- import { finalizeSettings, loadConfig, mergeSettings, resolveSettingsImports, searchForConfig, } from '../Settings/index.js';
9
+ import { 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';
@@ -33,6 +33,14 @@ export class DocumentValidator {
33
33
  options;
34
34
  perfTiming = {};
35
35
  skipValidation;
36
+ static async create(doc, options, settingsOrConfigFile) {
37
+ const settings = satisfiesCSpellConfigFile(settingsOrConfigFile)
38
+ ? await resolveConfigFileImports(settingsOrConfigFile)
39
+ : settingsOrConfigFile;
40
+ const validator = new DocumentValidator(doc, options, settings);
41
+ await validator.prepare();
42
+ return validator;
43
+ }
36
44
  /**
37
45
  * @param doc - Document to validate
38
46
  * @param config - configuration to use (not finalized).
@@ -63,7 +71,7 @@ export class DocumentValidator {
63
71
  assert(!this._ready);
64
72
  const timer = createPerfTimer('_prepareAsync');
65
73
  const { options, settings: rawSettings } = this;
66
- const resolveImportsRelativeTo = toFileURL(options.resolveImportsRelativeTo || pathToFileURL('./virtual.settings.json'));
74
+ const resolveImportsRelativeTo = toFileURL(options.resolveImportsRelativeTo || toFileURL('./virtual.settings.json'));
67
75
  const settings = rawSettings.import?.length
68
76
  ? await resolveSettingsImports(rawSettings, resolveImportsRelativeTo)
69
77
  : rawSettings;
@@ -0,0 +1,18 @@
1
+ import { TextOffset } from '@cspell/cspell-types';
2
+ /**
3
+ * Try to detect if a string is a random string of characters or is it camel case / snake case words.
4
+ * @param s - string to check
5
+ * @returns true if the string is considered random;
6
+ */
7
+ export declare function isRandomString(s: string, maxNoiseToLengthRatio?: number): boolean;
8
+ /**
9
+ * Calculate the ratio of noise to the length of the string.
10
+ * @param s - string to check
11
+ * @returns true if the string is considered random;
12
+ */
13
+ export declare function scoreRandomString(s: string): number;
14
+ export declare function scoreLongWordRatio(s: string): number;
15
+ export declare function categorizeString(s: string): string;
16
+ export declare function isHexNumber(s: string): boolean;
17
+ export declare function extractHexSequences(s: string, minLength?: number): TextOffset[];
18
+ //# sourceMappingURL=isRandomString.d.ts.map
@@ -0,0 +1,65 @@
1
+ const maxRadio = 0.5;
2
+ /**
3
+ * Try to detect if a string is a random string of characters or is it camel case / snake case words.
4
+ * @param s - string to check
5
+ * @returns true if the string is considered random;
6
+ */
7
+ export function isRandomString(s, maxNoiseToLengthRatio = maxRadio) {
8
+ return scoreRandomString(s) >= maxNoiseToLengthRatio;
9
+ }
10
+ /**
11
+ * Calculate the ratio of noise to the length of the string.
12
+ * @param s - string to check
13
+ * @returns true if the string is considered random;
14
+ */
15
+ export function scoreRandomString(s) {
16
+ if (!s.length)
17
+ return 0;
18
+ const n = categorizeString(s);
19
+ return n.length / s.length; // * 0.75 + (1 - scoreLongWordRatio(s)) * 0.25 + nonLetterRatio(s) * 0.25;
20
+ }
21
+ export function scoreLongWordRatio(s) {
22
+ return sumWordLengths(s) / s.length;
23
+ }
24
+ function sumWordLengths(s) {
25
+ let total = 0;
26
+ for (const w of s.matchAll(/\p{Lu}\p{Ll}{3,}|\p{Ll}{4,}|\p{Lu}{4,}/gu)) {
27
+ total += w[0].length;
28
+ }
29
+ return total;
30
+ }
31
+ function _nonLetterRatio(s) {
32
+ return s.replaceAll(/\p{L}/gu, '').length / s.length;
33
+ }
34
+ export function categorizeString(s) {
35
+ const n = s
36
+ .replaceAll(/\d+/g, '0')
37
+ .replaceAll(/\p{Ll}\p{M}+/gu, 'a')
38
+ .replaceAll(/\p{Lu}\p{M}+/gu, 'A')
39
+ .replaceAll(/\p{Lu}?\p{Ll}+/gu, '1')
40
+ .replaceAll(/\p{Lu}+/gu, '2')
41
+ .replaceAll(/\p{M}/gu, '4')
42
+ .replaceAll('_', '')
43
+ .replaceAll(/[-_.']+/g, '3');
44
+ return n;
45
+ }
46
+ const hexLowerRegex = /^[0-9a-f]+$/;
47
+ const hexUpperRegex = /^[0-9A-F]+$/;
48
+ export function isHexNumber(s) {
49
+ return hexLowerRegex.test(s) || hexUpperRegex.test(s);
50
+ }
51
+ const hexSequence = /(?:\b|(?<=[\W_]))[0-9a-fA-F][-0-9a-fA-F]*[0-9a-fA-F](?:\b|(?=[\W_]))/g;
52
+ const isLetter = /\p{L}/uy;
53
+ function isLetterAt(s, idx) {
54
+ isLetter.lastIndex = idx;
55
+ return isLetter.test(s);
56
+ }
57
+ const MIN_HEX_SEQUENCE_LENGTH = 4;
58
+ export function extractHexSequences(s, minLength = MIN_HEX_SEQUENCE_LENGTH) {
59
+ return [...s.matchAll(hexSequence)]
60
+ .filter((m) => m[0].length >= minLength &&
61
+ (m.index === 0 || !isLetterAt(s, m.index - 1)) &&
62
+ !isLetterAt(s, m.index + m[0].length))
63
+ .map((m) => ({ text: m[0], offset: m.index }));
64
+ }
65
+ //# sourceMappingURL=isRandomString.js.map
@@ -1,15 +1,18 @@
1
1
  import assert from 'node:assert';
2
2
  import { opConcatMap, opFilter, pipe } from '@cspell/cspell-pipe/sync';
3
+ import { defaultCSpellSettings } from '@cspell/cspell-types';
3
4
  import { createCachingDictionary } from 'cspell-dictionary';
4
5
  import * as RxPat from '../Settings/RegExpPatterns.js';
5
6
  import { extractPossibleWordsFromTextOffset, extractText, extractWordsFromTextOffset, splitWordWithOffset, } from '../util/text.js';
6
7
  import { regExpCamelCaseWordBreaksWithEnglishSuffix } from '../util/textRegex.js';
7
8
  import { split } from '../util/wordSplitter.js';
8
9
  import { defaultMinWordLength } from './defaultConstants.js';
10
+ import { extractHexSequences, isRandomString } from './isRandomString.js';
9
11
  import { isWordValidWithEscapeRetry } from './isWordValid.js';
10
12
  import { mapRangeBackToOriginalPos } from './parsedText.js';
13
+ const MIN_HEX_SEQUENCE_LENGTH = 8;
11
14
  export function lineValidatorFactory(sDict, options) {
12
- const { minWordLength = defaultMinWordLength, flagWords = [], allowCompoundWords = false, ignoreCase = true, } = options;
15
+ const { minWordLength = defaultMinWordLength, flagWords = [], allowCompoundWords = false, ignoreCase = true, ignoreRandomStrings = defaultCSpellSettings.ignoreRandomStrings, minRandomLength = defaultCSpellSettings.minRandomLength, } = options;
13
16
  const hasWordOptions = {
14
17
  ignoreCase,
15
18
  useCompounds: allowCompoundWords || undefined, // let the dictionaries decide on useCompounds if allow is false
@@ -101,7 +104,7 @@ export function lineValidatorFactory(sDict, options) {
101
104
  const fn = (lineSegment) => {
102
105
  const line = lineSegment.line;
103
106
  function isWordTooShort(word, ignoreSuffix = false) {
104
- if (word.text.length >= minWordLength)
107
+ if (word.text.length >= minWordLength * 2 || [...word.text].length >= minWordLength)
105
108
  return false;
106
109
  const offset = word.offset - line.offset;
107
110
  assert.equal(line.text.slice(offset, offset + word.text.length), word.text);
@@ -217,6 +220,28 @@ export function lineValidatorFactory(sDict, options) {
217
220
  return issue;
218
221
  });
219
222
  }
223
+ function checkForFlaggedWord(possibleWord) {
224
+ if (isWordFlagged(possibleWord)) {
225
+ const vr = {
226
+ ...possibleWord,
227
+ line: lineSegment.line,
228
+ isFlagged: true,
229
+ };
230
+ return vr;
231
+ }
232
+ if (possibleWord.text.endsWith('.')) {
233
+ const pw = { ...possibleWord, text: possibleWord.text.slice(0, -1) };
234
+ if (isWordFlagged(pw)) {
235
+ const vr = {
236
+ ...pw,
237
+ line: lineSegment.line,
238
+ isFlagged: true,
239
+ };
240
+ return vr;
241
+ }
242
+ }
243
+ return undefined;
244
+ }
220
245
  function checkPossibleWords(possibleWord) {
221
246
  const known = setOfKnownIssues.get(possibleWord.text);
222
247
  if (known) {
@@ -230,15 +255,10 @@ export function lineValidatorFactory(sDict, options) {
230
255
  return issues;
231
256
  }
232
257
  function _checkPossibleWords(possibleWord) {
233
- if (isWordFlagged(possibleWord)) {
234
- const vr = {
235
- ...possibleWord,
236
- line: lineSegment.line,
237
- isFlagged: true,
238
- };
239
- return [vr];
240
- }
241
- const mismatches = [];
258
+ const flagged = checkForFlaggedWord(possibleWord);
259
+ if (flagged)
260
+ return [flagged];
261
+ let mismatches = [];
242
262
  for (const wo of extractWordsFromTextOffset(possibleWord)) {
243
263
  if (setOfKnownSuccessfulWords.has(wo.text))
244
264
  continue;
@@ -251,6 +271,19 @@ export function lineValidatorFactory(sDict, options) {
251
271
  mismatches.push(w);
252
272
  }
253
273
  }
274
+ if (!mismatches.length)
275
+ return mismatches;
276
+ const hexSequences = !ignoreRandomStrings
277
+ ? []
278
+ : extractHexSequences(possibleWord.text, MIN_HEX_SEQUENCE_LENGTH)
279
+ .filter(
280
+ // Only consider hex sequences that are all upper case or all lower case and contain a `-` or a digit.
281
+ (w) => (w.text === w.text.toLowerCase() || w.text === w.text.toUpperCase()) &&
282
+ /[\d-]/.test(w.text))
283
+ .map((w) => ((w.offset += possibleWord.offset), w));
284
+ if (hexSequences.length) {
285
+ mismatches = filterExcludedTextOffsets(mismatches, hexSequences);
286
+ }
254
287
  if (mismatches.length) {
255
288
  // Try the more expensive word splitter
256
289
  const splitResult = split(lineSegment.segment, possibleWord.offset, splitterIsValid);
@@ -263,13 +296,19 @@ export function lineValidatorFactory(sDict, options) {
263
296
  const v = checkWord({ ...w, text: m[1], line: lineSegment.line });
264
297
  return v.isFlagged || !v.isFound;
265
298
  });
266
- if (nonMatching.length < mismatches.length) {
267
- return nonMatching.map((w) => ({ ...w, line: lineSegment.line })).map(annotateIsFlagged);
299
+ const filtered = filterExcludedTextOffsets(nonMatching.map((w) => ({ ...w, line: lineSegment.line })).map(annotateIsFlagged), hexSequences);
300
+ if (filtered.length < mismatches.length) {
301
+ return filtered;
268
302
  }
269
303
  }
270
304
  return mismatches;
271
305
  }
272
- const checkedPossibleWords = pipe(extractPossibleWordsFromTextOffset(lineSegment.segment), opFilter(filterAlreadyChecked), opConcatMap(checkPossibleWords));
306
+ function isNotRandom(textOff) {
307
+ if (textOff.text.length < minRandomLength || !ignoreRandomStrings)
308
+ return true;
309
+ return !isRandomString(textOff.text);
310
+ }
311
+ const checkedPossibleWords = pipe(extractPossibleWordsFromTextOffset(lineSegment.segment), opFilter(isNotRandom), opFilter(filterAlreadyChecked), opConcatMap(checkPossibleWords));
273
312
  return checkedPossibleWords;
274
313
  };
275
314
  function getWordInfo(word) {
@@ -303,4 +342,27 @@ export function textValidatorFactory(dict, options) {
303
342
  lineValidator,
304
343
  };
305
344
  }
345
+ function filterExcludedTextOffsets(issues, excluded) {
346
+ if (!excluded.length)
347
+ return issues;
348
+ const keep = [];
349
+ let i = 0;
350
+ let j = 0;
351
+ for (i = 0; i < issues.length && j < excluded.length; i++) {
352
+ const issue = issues[i];
353
+ while (j < excluded.length && excluded[j].offset + excluded[j].text.length <= issue.offset) {
354
+ j++;
355
+ }
356
+ if (j >= excluded.length) {
357
+ break;
358
+ }
359
+ if (issue.isFlagged || issue.offset < excluded[j].offset) {
360
+ keep.push(issue);
361
+ }
362
+ }
363
+ if (i < issues.length) {
364
+ keep.push(...issues.slice(i));
365
+ }
366
+ return keep;
367
+ }
306
368
  //# sourceMappingURL=lineValidatorFactory.js.map
@@ -2,6 +2,8 @@ export function settingsToValidateOptions(settings) {
2
2
  const opt = {
3
3
  ...settings,
4
4
  ignoreCase: !(settings.caseSensitive ?? false),
5
+ ignoreRandomStrings: settings.ignoreRandomStrings,
6
+ minRandomLength: settings.minRandomLength,
5
7
  };
6
8
  return opt;
7
9
  }
@@ -1,4 +1,5 @@
1
1
  import type { CSpellSettings, LocaleId } from '@cspell/cspell-types';
2
+ import { ICSpellConfigFile } from 'cspell-config-lib';
2
3
  import type { LanguageId } from './fileTypes.js';
3
4
  import type { DictionaryTraceResult, WordSplits } from './textValidation/traceWord.js';
4
5
  export interface TraceResult extends DictionaryTraceResult {
@@ -14,6 +15,6 @@ export interface TraceOptions {
14
15
  export interface TraceWordResult extends Array<TraceResult> {
15
16
  splits: readonly WordSplits[];
16
17
  }
17
- export declare function traceWords(words: string[], settings: CSpellSettings, options: TraceOptions | undefined): Promise<TraceResult[]>;
18
- export declare function traceWordsAsync(words: Iterable<string> | AsyncIterable<string>, settings: CSpellSettings, options: TraceOptions | undefined): AsyncIterableIterator<TraceWordResult>;
18
+ export declare function traceWords(words: string[], settings: CSpellSettings | ICSpellConfigFile, options: TraceOptions | undefined): Promise<TraceResult[]>;
19
+ export declare function traceWordsAsync(words: Iterable<string> | AsyncIterable<string>, settingsOrConfig: CSpellSettings | ICSpellConfigFile, options: TraceOptions | undefined): AsyncIterableIterator<TraceWordResult>;
19
20
  //# sourceMappingURL=trace.d.ts.map
package/dist/lib/trace.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { satisfiesCSpellConfigFile } from 'cspell-config-lib';
1
2
  import { genSequence } from 'gensequence';
2
3
  import { toInternalSettings } from './Settings/CSpellSettingsServer.js';
3
- import { finalizeSettings, mergeSettings } from './Settings/index.js';
4
+ import { finalizeSettings, mergeSettings, resolveConfigFileImports } from './Settings/index.js';
4
5
  import { calcSettingsForLanguageId } from './Settings/LanguageSettings.js';
5
6
  import { getDictionaryInternal, refreshDictionaryCache } from './SpellingDictionary/index.js';
6
7
  import { traceWord } from './textValidation/traceWord.js';
@@ -13,8 +14,11 @@ export async function traceWords(words, settings, options) {
13
14
  .toArray();
14
15
  return s;
15
16
  }
16
- export async function* traceWordsAsync(words, settings, options) {
17
+ export async function* traceWordsAsync(words, settingsOrConfig, options) {
17
18
  const { languageId, locale: language, ignoreCase = true, allowCompoundWords } = options || {};
19
+ const settings = satisfiesCSpellConfigFile(settingsOrConfig)
20
+ ? await resolveConfigFileImports(settingsOrConfig)
21
+ : settingsOrConfig;
18
22
  async function finalize(config) {
19
23
  const withLocale = mergeSettings(config, util.clean({
20
24
  language: language || config.language,
@@ -1,6 +1,5 @@
1
1
  import assert from 'node:assert';
2
- import { pathToFileURL } from 'node:url';
3
- import { toFilePathOrHref, toFileURL, toURL } from '@cspell/url';
2
+ import { toFileDirURL, toFilePathOrHref, toFileURL, toURL } from '@cspell/url';
4
3
  import { isUrlLike } from 'cspell-io';
5
4
  import { URI, Utils } from 'vscode-uri';
6
5
  const STDIN_PROTOCOL = 'stdin:';
@@ -21,7 +20,7 @@ export function toUri(uriOrFile) {
21
20
  }
22
21
  const isWindows = process.platform === 'win32';
23
22
  const hasDriveLetter = /^[a-zA-Z]:[\\/]/;
24
- const rootUrl = pathToFileURL('/');
23
+ const rootUrl = toFileDirURL('/');
25
24
  export function uriToFilePath(uri) {
26
25
  let url = documentUriToURL(uri);
27
26
  url = url.protocol === 'stdin:' ? new URL(url.pathname, rootUrl) : url;
@@ -1,7 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
- import { pathToFileURL } from 'node:url';
5
4
  import { fileURLToPath } from 'node:url';
6
5
  import { resolveGlobal } from '@cspell/cspell-resolver';
7
6
  import { importResolveModuleName } from '@cspell/dynamic-import';
@@ -9,8 +8,9 @@ import resolveFrom from 'resolve-from';
9
8
  import { getFileSystem } from '../fileSystem.js';
10
9
  import { srcDirectory } from '../pkg-info.mjs';
11
10
  import { envToTemplateVars, replaceTemplate } from './templates.js';
12
- import { fileURLOrPathToPath, isDataURL, isFileURL, isURLLike, resolveFileWithURL, toFilePathOrHref, toFileUrl, toURL, } from './url.js';
11
+ import { fileURLOrPathToPath, isDataURL, isFileURL, isURLLike, resolveFileWithURL, toFileDirURL, toFilePathOrHref, toFileUrl, toURL, } from './url.js';
13
12
  const regExpStartsWidthNodeModules = /^node_modules[/\\]/;
13
+ const debugMode = false;
14
14
  export class FileResolver {
15
15
  fs;
16
16
  templateReplacements;
@@ -129,13 +129,16 @@ export class FileResolver {
129
129
  tryCreateRequire = (filename, relativeTo) => {
130
130
  if (filename instanceof URL)
131
131
  return undefined;
132
- const rel = !isURLLike(relativeTo) || isFileURL(relativeTo) ? relativeTo : pathToFileURL('./');
133
- const require = createRequire(rel);
132
+ const rel = !isURLLike(relativeTo) || isFileURL(relativeTo) ? relativeTo : toFileDirURL('./');
134
133
  try {
134
+ const require = createRequire(rel);
135
135
  const r = require.resolve(filename);
136
136
  return { filename: r, relativeTo: rel.toString(), found: true, method: 'tryCreateRequire' };
137
137
  }
138
- catch {
138
+ catch (error) {
139
+ if (debugMode) {
140
+ console.error('Error in tryCreateRequire: %o', { filename, rel, relativeTo, error: `${error}` });
141
+ }
139
142
  return undefined;
140
143
  }
141
144
  };
@@ -1,6 +1,4 @@
1
- import path from 'node:path';
2
- import { pathToFileURL } from 'node:url';
3
- import { toFilePathOrHref, toFileURL } from '@cspell/url';
1
+ import { toFileDirURL, toFilePathOrHref, toFileURL } from '@cspell/url';
4
2
  import { srcDirectory } from '../pkg-info.mjs';
5
3
  export { addTrailingSlash, isDataURL, isFileURL, isUrlLike as isURLLike, toFileURL as resolveFileWithURL, toFileDirURL, toFilePathOrHref, toURL, } from '@cspell/url';
6
4
  /**
@@ -8,7 +6,7 @@ export { addTrailingSlash, isDataURL, isFileURL, isUrlLike as isURLLike, toFileU
8
6
  * @returns URL for the source directory
9
7
  */
10
8
  export function getSourceDirectoryUrl() {
11
- const srcDirectoryURL = pathToFileURL(path.join(srcDirectory, '/'));
9
+ const srcDirectoryURL = toFileDirURL(srcDirectory);
12
10
  return srcDirectoryURL;
13
11
  }
14
12
  /**
@@ -20,7 +18,7 @@ export function relativeTo(path, relativeTo) {
20
18
  return toFileURL(path, relativeTo ?? cwdURL());
21
19
  }
22
20
  export function cwdURL() {
23
- return pathToFileURL('./');
21
+ return toFileDirURL('./');
24
22
  }
25
23
  export function toFileUrl(file) {
26
24
  return toFileURL(file, cwdURL());
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "provenance": true
6
6
  },
7
- "version": "8.16.0",
7
+ "version": "8.17.0",
8
8
  "description": "A library of useful functions used across various cspell tools.",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -64,22 +64,22 @@
64
64
  },
65
65
  "homepage": "https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-lib#readme",
66
66
  "dependencies": {
67
- "@cspell/cspell-bundled-dicts": "8.16.0",
68
- "@cspell/cspell-pipe": "8.16.0",
69
- "@cspell/cspell-resolver": "8.16.0",
70
- "@cspell/cspell-types": "8.16.0",
71
- "@cspell/dynamic-import": "8.16.0",
72
- "@cspell/filetypes": "8.16.0",
73
- "@cspell/strong-weak-map": "8.16.0",
74
- "@cspell/url": "8.16.0",
67
+ "@cspell/cspell-bundled-dicts": "8.17.0",
68
+ "@cspell/cspell-pipe": "8.17.0",
69
+ "@cspell/cspell-resolver": "8.17.0",
70
+ "@cspell/cspell-types": "8.17.0",
71
+ "@cspell/dynamic-import": "8.17.0",
72
+ "@cspell/filetypes": "8.17.0",
73
+ "@cspell/strong-weak-map": "8.17.0",
74
+ "@cspell/url": "8.17.0",
75
75
  "clear-module": "^4.1.2",
76
76
  "comment-json": "^4.2.5",
77
- "cspell-config-lib": "8.16.0",
78
- "cspell-dictionary": "8.16.0",
79
- "cspell-glob": "8.16.0",
80
- "cspell-grammar": "8.16.0",
81
- "cspell-io": "8.16.0",
82
- "cspell-trie-lib": "8.16.0",
77
+ "cspell-config-lib": "8.17.0",
78
+ "cspell-dictionary": "8.17.0",
79
+ "cspell-glob": "8.17.0",
80
+ "cspell-grammar": "8.17.0",
81
+ "cspell-io": "8.17.0",
82
+ "cspell-trie-lib": "8.17.0",
83
83
  "env-paths": "^3.0.0",
84
84
  "fast-equals": "^5.0.1",
85
85
  "gensequence": "^7.0.0",
@@ -93,14 +93,14 @@
93
93
  "node": ">=18"
94
94
  },
95
95
  "devDependencies": {
96
- "@cspell/dict-cpp": "^6.0.1",
96
+ "@cspell/dict-cpp": "^6.0.2",
97
97
  "@cspell/dict-csharp": "^4.0.5",
98
98
  "@cspell/dict-css": "^4.0.16",
99
99
  "@cspell/dict-fa-ir": "^4.0.3",
100
100
  "@cspell/dict-fr-fr": "^2.2.5",
101
101
  "@cspell/dict-html": "^4.0.10",
102
102
  "@cspell/dict-nl-nl": "^2.3.3",
103
- "@cspell/dict-python": "^4.2.12",
103
+ "@cspell/dict-python": "^4.2.13",
104
104
  "@types/configstore": "^6.0.2",
105
105
  "configstore": "^7.0.0",
106
106
  "cspell-dict-nl-nl": "^1.1.2",
@@ -108,5 +108,5 @@
108
108
  "lorem-ipsum": "^2.0.8",
109
109
  "perf-insight": "^1.2.0"
110
110
  },
111
- "gitHead": "41cd50f9ba34033b6da32408855d7fc3b888c5e0"
111
+ "gitHead": "cac17fe7b993db5965aa441648b1c22cb5486929"
112
112
  }