cspell-lib 10.2.0-alpha.7 → 10.2.1

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.
Files changed (30) hide show
  1. package/dist/lib/Settings/LanguageSettings.js +17 -1
  2. package/dist/lib/Settings/sanitizeSettings.js +3 -0
  3. package/dist/lib/Transform/IntlSegmentTextTransformer.d.ts +11 -0
  4. package/dist/lib/Transform/IntlSegmentTextTransformer.js +41 -0
  5. package/dist/lib/Transform/SubstitutionTransformer.d.ts +3 -1
  6. package/dist/lib/Transform/SubstitutionTransformer.js +10 -28
  7. package/dist/lib/Transform/TextMapEdit.d.ts +5 -0
  8. package/dist/lib/Transform/TextMapEdit.js +42 -0
  9. package/dist/lib/Transform/Transformer.d.ts +25 -0
  10. package/dist/lib/Transform/Transformer.js +27 -0
  11. package/dist/lib/Transform/index.d.ts +3 -0
  12. package/dist/lib/Transform/index.js +2 -0
  13. package/dist/lib/textValidation/docValidator.d.ts +3 -2
  14. package/dist/lib/textValidation/docValidator.js +22 -20
  15. package/dist/lib/textValidation/isRandomString.js +4 -3
  16. package/dist/lib/textValidation/lineValidatorFactory.d.ts +2 -2
  17. package/dist/lib/textValidation/lineValidatorFactory.js +7 -2
  18. package/dist/lib/textValidation/textValidator.d.ts +2 -2
  19. package/dist/lib/textValidation/traceWord.js +1 -1
  20. package/dist/lib/util/textRegex.js +1 -1
  21. package/dist/lib/util/wordSplitter/generateWordBreaks.d.ts +32 -0
  22. package/dist/lib/util/wordSplitter/generateWordBreaks.js +120 -0
  23. package/dist/lib/util/wordSplitter/index.d.ts +4 -0
  24. package/dist/lib/util/wordSplitter/index.js +3 -0
  25. package/dist/lib/util/{wordSplitter.d.ts → wordSplitter/wordSplitter.d.ts} +13 -22
  26. package/dist/lib/util/wordSplitter/wordSplitter.js +338 -0
  27. package/dist/lib/util/{wordSplitter.perf.js → wordSplitter/wordSplitter.perf.js} +2 -2
  28. package/package.json +20 -19
  29. package/dist/lib/util/wordSplitter.js +0 -331
  30. /package/dist/lib/util/{wordSplitter.perf.d.ts → wordSplitter/wordSplitter.perf.d.ts} +0 -0
@@ -38,9 +38,25 @@ export function normalizeLocale(locale) {
38
38
  return _normalizeLocale(locale);
39
39
  }
40
40
  export function normalizeLocaleIntl(locale) {
41
- const values = [...normalizeLocale(locale)].map((locale) => locale.replace(/^([a-z]{2})-?([a-z]{2})$/, (_, lang, locale) => locale ? `${lang}-${locale.toUpperCase()}` : lang));
41
+ const values = [...normalizeLocale(locale)].map((locale) => locale.replace(/^([a-z]{2})(?:-?([a-z]{4}))?-?([a-z]{2})$/, (_, lang, script, region) => toLocaleIntl(lang, script, region)));
42
42
  return new Set(values);
43
43
  }
44
+ /**
45
+ * See: https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag
46
+ * @returns locale normalized to BCP_47
47
+ */
48
+ function toLocaleIntl(lang, script, region) {
49
+ const parts = [lang];
50
+ if (script)
51
+ parts.push(firstUpper(script.toLowerCase()));
52
+ if (region)
53
+ parts.push(region.toUpperCase());
54
+ return parts.join('-');
55
+ }
56
+ function firstUpper(str) {
57
+ const [first, ...rest] = [...str];
58
+ return [first.toUpperCase(), ...rest].join('');
59
+ }
44
60
  export function isLocaleInSet(locale, setOfLocals) {
45
61
  const locales = normalizeLocale(locale);
46
62
  return doSetsIntersect(locales, setOfLocals);
@@ -68,6 +68,7 @@ const handlers = {
68
68
  suggestWords: copy1,
69
69
  unknownWords: copy1,
70
70
  useGitignore: copy1,
71
+ useIntlWordSegmentation: copy1,
71
72
  usePnP: skip,
72
73
  userWords: copy1,
73
74
  validateDirectives: copy1,
@@ -183,6 +184,7 @@ const LanguageSettingsHandlers = {
183
184
  substitutions: copy1,
184
185
  suggestWords: copy1,
185
186
  unknownWords: copy1,
187
+ useIntlWordSegmentation: copy1,
186
188
  words: copy1,
187
189
  // Experimental
188
190
  parser: skip,
@@ -245,6 +247,7 @@ const OverridesHandlers = {
245
247
  suggestionsTimeout: copy1,
246
248
  suggestWords: copy1,
247
249
  unknownWords: copy1,
250
+ useIntlWordSegmentation: copy1,
248
251
  usePnP: skip,
249
252
  words: copy1,
250
253
  parser: skip,
@@ -0,0 +1,11 @@
1
+ import type { MappedText } from '@cspell/cspell-types';
2
+ import type { TextTransformer } from './Transformer.js';
3
+ export declare const softHyphen = "\u00AD";
4
+ export declare class IntlSegmentTextTransformer implements TextTransformer {
5
+ #private;
6
+ constructor(locale: string | string[]);
7
+ transform(text: string | MappedText): MappedText;
8
+ transformAll(src: Iterable<string | MappedText>): Iterable<MappedText>;
9
+ }
10
+ export declare function createIntlSegmentTextTransformer(locale: string | string[]): IntlSegmentTextTransformer;
11
+ //# sourceMappingURL=IntlSegmentTextTransformer.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { normalizeLocaleIntl } from '../Settings/LanguageSettings.js';
2
+ import { applyEditsToMappedText } from './TextMapEdit.js';
3
+ import { toMappedText } from './Transformer.js';
4
+ export const softHyphen = '\u00AD';
5
+ const regExSplitWordCharacters = /(?<=[\p{L}\p{M}])[\p{L}\p{M}]/uy;
6
+ export class IntlSegmentTextTransformer {
7
+ #locale;
8
+ constructor(locale) {
9
+ this.#locale = [...normalizeLocaleIntl(locale)];
10
+ }
11
+ transform(text) {
12
+ // Implement the transformation logic here
13
+ text = toMappedText(text);
14
+ return segmentText(text, new Intl.Segmenter(this.#locale, { granularity: 'word' }));
15
+ }
16
+ *transformAll(src) {
17
+ for (const item of src) {
18
+ yield this.transform(item);
19
+ }
20
+ }
21
+ }
22
+ function segmentText(mText, segmenter) {
23
+ const edits = [];
24
+ let lastIndex = -1;
25
+ const reg = new RegExp(regExSplitWordCharacters);
26
+ const text = mText.text;
27
+ for (const { segment, index, isWordLike } of segmenter.segment(mText.text)) {
28
+ const endIndex = index + segment.length;
29
+ reg.lastIndex = index;
30
+ if (isWordLike && index === lastIndex && reg.test(text)) {
31
+ const edit = { text: softHyphen + segment, range: [index, endIndex] };
32
+ edits.push(edit);
33
+ }
34
+ lastIndex = endIndex;
35
+ }
36
+ return applyEditsToMappedText(mText, edits);
37
+ }
38
+ export function createIntlSegmentTextTransformer(locale) {
39
+ return new IntlSegmentTextTransformer(locale);
40
+ }
41
+ //# sourceMappingURL=IntlSegmentTextTransformer.js.map
@@ -1,4 +1,5 @@
1
1
  import type { MappedText, SubstitutionDefinitions, Substitutions } from '@cspell/cspell-types';
2
+ import type { TextTransformer } from './Transformer.js';
2
3
  type DeepReadonly<T> = T extends object ? {
3
4
  readonly [K in keyof T]: DeepReadonly<T[K]>;
4
5
  } : T;
@@ -7,10 +8,11 @@ export interface SubstitutionInfo {
7
8
  substitutionDefinitions?: SubstitutionDefinitions | undefined;
8
9
  }
9
10
  export type ReadonlySubstitutionInfo = DeepReadonly<SubstitutionInfo>;
10
- export declare class SubstitutionTransformer {
11
+ export declare class SubstitutionTransformer implements TextTransformer {
11
12
  #private;
12
13
  constructor(subMap: Map<string, string> | undefined);
13
14
  transform(text: string | MappedText): MappedText;
15
+ transformAll(src: Iterable<string | MappedText>): Iterable<MappedText>;
14
16
  transformString(text: string): MappedText;
15
17
  }
16
18
  export interface CreateSubstitutionTransformerResult {
@@ -1,5 +1,7 @@
1
1
  import { GTrie } from 'cspell-trie-lib';
2
- import { mapOffsetPairsToSourceMap, mergeSourceMaps } from './SourceMap.js';
2
+ import { mergeSourceMaps } from './SourceMap.js';
3
+ import { applyEditsToText } from './TextMapEdit.js';
4
+ import { toMappedText } from './Transformer.js';
3
5
  export class SubstitutionTransformer {
4
6
  #trie;
5
7
  constructor(subMap) {
@@ -21,36 +23,16 @@ export class SubstitutionTransformer {
21
23
  };
22
24
  return result;
23
25
  }
26
+ *transformAll(src) {
27
+ for (const item of src) {
28
+ yield this.transform(item);
29
+ }
30
+ }
24
31
  transformString(text) {
25
32
  if (!this.#trie) {
26
- return { text, range: [0, text.length], rawText: text };
27
- }
28
- const map = [0, 0];
29
- let repText = '';
30
- let lastEnd = 0;
31
- for (const edit of calcEdits(text, this.#trie)) {
32
- if (edit.range[0] > lastEnd) {
33
- repText += text.slice(lastEnd, edit.range[0]);
34
- map.push(edit.range[0], repText.length);
35
- }
36
- repText += edit.text;
37
- map.push(edit.range[1], repText.length);
38
- lastEnd = edit.range[1];
39
- }
40
- if (lastEnd === 0) {
41
- return { text, range: [0, text.length], rawText: text };
33
+ return toMappedText(text);
42
34
  }
43
- if (lastEnd < text.length) {
44
- repText += text.slice(lastEnd);
45
- map.push(text.length, repText.length);
46
- }
47
- const result = {
48
- text: repText,
49
- range: [0, text.length],
50
- map: mapOffsetPairsToSourceMap(map),
51
- rawText: text,
52
- };
53
- return result;
35
+ return applyEditsToText(text, calcEdits(text, this.#trie));
54
36
  }
55
37
  }
56
38
  function* calcEdits(text, subTrie) {
@@ -0,0 +1,5 @@
1
+ import type { MappedText } from '@cspell/cspell-types';
2
+ import type { Edit } from './Transformer.js';
3
+ export declare function applyEditsToMappedText(text: MappedText, edits: Iterable<Edit>): MappedText;
4
+ export declare function applyEditsToText(text: string, edits: Iterable<Edit>): MappedText;
5
+ //# sourceMappingURL=TextMapEdit.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { mapOffsetPairsToSourceMap, mergeSourceMaps } from './SourceMap.js';
2
+ import { toMappedText } from './Transformer.js';
3
+ export function applyEditsToMappedText(text, edits) {
4
+ const transformed = applyEditsToText(text.text, edits);
5
+ const rawText = text.rawText ?? text.text;
6
+ const result = {
7
+ ...text,
8
+ text: transformed.text,
9
+ map: mergeSourceMaps(text.map, transformed.map),
10
+ rawText,
11
+ };
12
+ return result;
13
+ }
14
+ export function applyEditsToText(text, edits) {
15
+ const map = [0, 0];
16
+ let repText = '';
17
+ let lastEnd = 0;
18
+ for (const edit of edits) {
19
+ if (edit.range[0] > lastEnd) {
20
+ repText += text.slice(lastEnd, edit.range[0]);
21
+ map.push(edit.range[0], repText.length);
22
+ }
23
+ repText += edit.text;
24
+ map.push(edit.range[1], repText.length);
25
+ lastEnd = edit.range[1];
26
+ }
27
+ if (lastEnd === 0) {
28
+ return toMappedText(text);
29
+ }
30
+ if (lastEnd < text.length) {
31
+ repText += text.slice(lastEnd);
32
+ map.push(text.length, repText.length);
33
+ }
34
+ const result = {
35
+ text: repText,
36
+ range: [0, text.length],
37
+ map: mapOffsetPairsToSourceMap(map),
38
+ rawText: text,
39
+ };
40
+ return result;
41
+ }
42
+ //# sourceMappingURL=TextMapEdit.js.map
@@ -0,0 +1,25 @@
1
+ import type { MappedText } from '@cspell/cspell-types';
2
+ import type { Range } from '@cspell/cspell-types/Parser';
3
+ export interface TextTransformer {
4
+ transform(text: string | MappedText): MappedText;
5
+ transformAll(src: Iterable<string | MappedText>): Iterable<MappedText>;
6
+ }
7
+ export interface Edit {
8
+ /**
9
+ * The replacement text for the edit. This is the text that will replace the original text in the string.
10
+ */
11
+ text: string;
12
+ /**
13
+ * The range of the text to be replaced. This is a tuple of the form `[start, end]`,
14
+ * where start is the index of the first character to be replaced,
15
+ * and end is the index of the first character after the last character to be replaced.
16
+ */
17
+ range: Range;
18
+ }
19
+ export declare function toMappedText(text: string | MappedText): MappedText;
20
+ export declare function chainTransformers(...transformers: TextTransformer[]): TextTransformer;
21
+ export declare class IdentityTextTransformer implements TextTransformer {
22
+ transform(text: string | MappedText): MappedText;
23
+ transformAll(src: Iterable<string | MappedText>): Iterable<MappedText>;
24
+ }
25
+ //# sourceMappingURL=Transformer.d.ts.map
@@ -0,0 +1,27 @@
1
+ export function toMappedText(text) {
2
+ if (typeof text === 'string') {
3
+ return { text, range: [0, text.length], rawText: text };
4
+ }
5
+ return text;
6
+ }
7
+ export function chainTransformers(...transformers) {
8
+ return {
9
+ transform(text) {
10
+ return transformers.reduce((acc, transformer) => transformer.transform(acc), toMappedText(text));
11
+ },
12
+ transformAll(src) {
13
+ return Array.from(src, (text) => transformers.reduce((acc, transformer) => transformer.transform(acc), toMappedText(text)));
14
+ },
15
+ };
16
+ }
17
+ export class IdentityTextTransformer {
18
+ transform(text) {
19
+ return toMappedText(text);
20
+ }
21
+ *transformAll(src) {
22
+ for (const item of src) {
23
+ yield this.transform(item);
24
+ }
25
+ }
26
+ }
27
+ //# sourceMappingURL=Transformer.js.map
@@ -1,3 +1,4 @@
1
+ export { createIntlSegmentTextTransformer } from './IntlSegmentTextTransformer.js';
1
2
  export { createMappedTextSegmenter, mapRangeBackToOriginalPos } from './parsedText.js';
2
3
  export type { SourceMapCursor } from './SourceMap.js';
3
4
  export { createSourceMapCursor } from './SourceMap.js';
@@ -5,5 +6,7 @@ export type { SubstitutionInfo } from './SubstitutionTransformer.js';
5
6
  export { createSubstitutionTransformer, SubstitutionTransformer } from './SubstitutionTransformer.js';
6
7
  export type { MatchRange } from './TextRange.js';
7
8
  export { excludeRanges, extractRangeText, findMatchingRanges, findMatchingRangesForPatterns } from './TextRange.js';
9
+ export type { TextTransformer } from './Transformer.js';
10
+ export { chainTransformers } from './Transformer.js';
8
11
  export type { Offset, SimpleRange } from './types.js';
9
12
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,7 @@
1
+ export { createIntlSegmentTextTransformer } from './IntlSegmentTextTransformer.js';
1
2
  export { createMappedTextSegmenter, mapRangeBackToOriginalPos } from './parsedText.js';
2
3
  export { createSourceMapCursor } from './SourceMap.js';
3
4
  export { createSubstitutionTransformer, SubstitutionTransformer } from './SubstitutionTransformer.js';
4
5
  export { excludeRanges, extractRangeText, findMatchingRanges, findMatchingRangesForPatterns } from './TextRange.js';
6
+ export { chainTransformers } from './Transformer.js';
5
7
  //# sourceMappingURL=index.js.map
@@ -6,7 +6,7 @@ import type { ValidationIssue } from '../Models/ValidationIssue.js';
6
6
  import type { CSpellSettingsInternal, CSpellSettingsInternalFinalized, ImportFileRefWithError } from '../Settings/index.js';
7
7
  import type { SpellingDictionaryCollection } from '../SpellingDictionary/index.js';
8
8
  import type { WordSuggestion } from '../suggestions.js';
9
- import type { MatchRange, SimpleRange, SubstitutionTransformer } from '../Transform/index.js';
9
+ import type { MatchRange, SimpleRange, TextTransformer } from '../Transform/index.js';
10
10
  import type { TextValidationFactoryOptions, TextValidator } from './lineValidatorFactory.js';
11
11
  import type { TraceResult } from './traceWord.js';
12
12
  import type { ValidateTextOptions } from './ValidateTextOptions.js';
@@ -37,6 +37,7 @@ export interface DocumentValidatorOptions extends ValidateTextOptions {
37
37
  }
38
38
  type PerfTimings = Record<string, number>;
39
39
  export declare class DocumentValidator {
40
+ #private;
40
41
  readonly settings: CSpellUserSettings;
41
42
  private _document;
42
43
  private _ready;
@@ -133,7 +134,7 @@ interface Preparations {
133
134
  validateOptions: TextValidationFactoryOptions;
134
135
  localConfig: CSpellUserSettings | undefined;
135
136
  localConfigFilepath: string | undefined;
136
- subTransformer: SubstitutionTransformer;
137
+ transformer: TextTransformer;
137
138
  }
138
139
  interface ShouldCheckDocumentResult {
139
140
  /** possible errors found while loading configuration. */
@@ -10,7 +10,7 @@ import { extractImportErrors, finalizeSettings, loadConfig, mergeSettings, resol
10
10
  import { validateInDocumentSettings } from '../Settings/InDocSettings.js';
11
11
  import { getDictionaryInternal } from '../SpellingDictionary/index.js';
12
12
  import { calcSuggestionAdjustedToToMatchCase } from '../suggestions.js';
13
- import { createMappedTextSegmenter, createSubstitutionTransformer } from '../Transform/index.js';
13
+ import { chainTransformers, createIntlSegmentTextTransformer, createMappedTextSegmenter, createSubstitutionTransformer, } from '../Transform/index.js';
14
14
  import { catchPromiseError, toError } from '../util/errors.js';
15
15
  import { AutoCache } from '../util/simpleCache.js';
16
16
  import { uriToFilePath } from '../util/Uri.js';
@@ -101,15 +101,7 @@ export class DocumentValidator {
101
101
  recShouldCheckTime();
102
102
  const recFinalizeTime = recordPerfTime(this.perfTiming, '_finalizeSettings');
103
103
  const finalSettings = finalizeSettings(docSettings);
104
- const sub = createSubstitutionTransformer(finalSettings);
105
- if (sub.missing) {
106
- this.addPossibleError(`Missing substitutions: ${sub.missing.join(', ')}`);
107
- }
108
- const validateOptions = { ...settingsToValidateOptions(finalSettings), transformer: sub.transformer };
109
- const rangeTransformer = finalSettings.substitutions?.length ? sub.transformer : undefined;
110
- const includeRanges = calcTextInclusionRanges(this._document.text, validateOptions, rangeTransformer);
111
- const segmenter = createMappedTextSegmenter(includeRanges);
112
- const textValidator = textValidatorFactory(dict, validateOptions);
104
+ const { transformer, validateOptions, includeRanges, segmenter, textValidator } = this.#calcSharedPrep(finalSettings, dict);
113
105
  recFinalizeTime();
114
106
  this._preparations = {
115
107
  config,
@@ -123,7 +115,7 @@ export class DocumentValidator {
123
115
  textValidator,
124
116
  localConfig,
125
117
  localConfigFilepath: localConfig?.__importRef?.filename,
126
- subTransformer: sub.transformer,
118
+ transformer,
127
119
  };
128
120
  this._ready = true;
129
121
  this._preparationTime = timer.elapsed;
@@ -139,28 +131,38 @@ export class DocumentValidator {
139
131
  const stopMeasure = measurePerf('DocumentValidator._updatePrep');
140
132
  const shouldCheck = docSettings.enabled ?? true;
141
133
  const finalSettings = finalizeSettings(docSettings);
142
- const sub = createSubstitutionTransformer(finalSettings);
143
- if (sub.missing) {
144
- this.addPossibleError(`Missing substitutions: ${sub.missing.join(', ')}`);
145
- }
146
- const validateOptions = { ...settingsToValidateOptions(finalSettings), transformer: sub.transformer };
147
- const rangeTransformer = finalSettings.substitutions?.length ? sub.transformer : undefined;
148
- const includeRanges = calcTextInclusionRanges(this._document.text, validateOptions, rangeTransformer);
149
- const segmenter = createMappedTextSegmenter(includeRanges);
150
- const textValidator = textValidatorFactory(dict, validateOptions);
134
+ const { transformer, validateOptions, includeRanges, segmenter, textValidator } = this.#calcSharedPrep(finalSettings, dict);
151
135
  this._preparations = {
152
136
  ...prep,
153
137
  dictionary: dict,
154
138
  docSettings,
139
+ finalSettings,
155
140
  shouldCheck,
156
141
  validateOptions,
157
142
  includeRanges,
158
143
  segmenter,
159
144
  textValidator,
145
+ transformer,
160
146
  };
161
147
  this._preparationTime = timer.elapsed;
162
148
  stopMeasure();
163
149
  }
150
+ #calcSharedPrep(finalSettings, dict) {
151
+ const sub = createSubstitutionTransformer(finalSettings);
152
+ if (sub.missing) {
153
+ this.addPossibleError(`Missing substitutions: ${sub.missing.join(', ')}`);
154
+ }
155
+ const transformer = finalSettings.useIntlWordSegmentation
156
+ ? chainTransformers(sub.transformer, createIntlSegmentTextTransformer(finalSettings.language || ''))
157
+ : sub.transformer;
158
+ const validateOptions = { ...settingsToValidateOptions(finalSettings), transformer };
159
+ // Avoid excluding text ranges that have a full substitution applied.
160
+ const rangeTransformer = finalSettings.substitutions?.length ? sub.transformer : undefined;
161
+ const includeRanges = calcTextInclusionRanges(this._document.text, validateOptions, rangeTransformer);
162
+ const segmenter = createMappedTextSegmenter(includeRanges);
163
+ const textValidator = textValidatorFactory(dict, validateOptions);
164
+ return { segmenter, textValidator, includeRanges, validateOptions, transformer };
165
+ }
164
166
  /**
165
167
  * The amount of time in ms to prepare for validation.
166
168
  */
@@ -1,3 +1,4 @@
1
+ import { softHyphen } from '../util/wordSplitter/index.js';
1
2
  const maxRadio = 0.5;
2
3
  /**
3
4
  * Try to detect if a string is a random string of characters or is it camel case / snake case words.
@@ -5,6 +6,9 @@ const maxRadio = 0.5;
5
6
  * @returns true if the string is considered random;
6
7
  */
7
8
  export function isRandomString(s, maxNoiseToLengthRatio = maxRadio) {
9
+ // Soft hyphen indicates a word break, so it's likely not a random string.
10
+ if (s.includes(softHyphen))
11
+ return false;
8
12
  return scoreRandomString(s) >= maxNoiseToLengthRatio;
9
13
  }
10
14
  /**
@@ -28,9 +32,6 @@ function sumWordLengths(s) {
28
32
  }
29
33
  return total;
30
34
  }
31
- function _nonLetterRatio(s) {
32
- return s.replaceAll(/\p{L}/gu, '').length / s.length;
33
- }
34
35
  export function categorizeString(s) {
35
36
  const n = s
36
37
  .replaceAll(/\d+/g, '0')
@@ -1,5 +1,5 @@
1
1
  import type { CachingDictionary, SpellingDictionary } from 'cspell-dictionary';
2
- import type { SubstitutionTransformer } from '../Transform/index.js';
2
+ import type { TextTransformer } from '../Transform/index.js';
3
3
  import type { LineValidatorFn, TextValidatorFn, ValidationOptions } from './ValidationTypes.js';
4
4
  interface LineValidator {
5
5
  fn: LineValidatorFn;
@@ -11,7 +11,7 @@ export interface TextValidator {
11
11
  lineValidator: LineValidator;
12
12
  }
13
13
  export interface TextValidationFactoryOptions extends ValidationOptions {
14
- transformer: SubstitutionTransformer | undefined;
14
+ transformer: TextTransformer | undefined;
15
15
  }
16
16
  export declare function textValidatorFactory(dict: SpellingDictionary, options: TextValidationFactoryOptions): TextValidator;
17
17
  export {};
@@ -7,7 +7,7 @@ import { mapRangeBackToOriginalPos } from '../Transform/index.js';
7
7
  import { autoResolve } from '../util/AutoResolve.js';
8
8
  import { extractPossibleWordsFromTextOffset, extractText, extractWordsFromTextOffset, splitWordWithOffset, } from '../util/text.js';
9
9
  import { regExpCamelCaseWordBreaksWithEnglishSuffix } from '../util/textRegex.js';
10
- import { split } from '../util/wordSplitter.js';
10
+ import { split } from '../util/wordSplitter/index.js';
11
11
  import { defaultMinWordLength } from './defaultConstants.js';
12
12
  import { extractHexSequences, isRandomString } from './isRandomString.js';
13
13
  import { isWordValidWithEscapeRetry } from './isWordValid.js';
@@ -327,7 +327,12 @@ export function lineValidatorFactory(sDict, options) {
327
327
  const v = checkWord({ ...w, text: m[1], line: lineSegment.line });
328
328
  return v.isFlagged || !v.isFound;
329
329
  });
330
- const filtered = filterExcludedTextOffsets(nonMatching.map((w) => ({ ...w, line: lineSegment.line })).map(annotateIsFlagged), hexSequences);
330
+ // Ignore 1 or 2 letter segments.
331
+ const minWordLen = Math.min(3, minWordLength);
332
+ const filtered = filterExcludedTextOffsets(nonMatching
333
+ .map((w) => ({ ...w, line: lineSegment.line }))
334
+ .map(annotateIsFlagged)
335
+ .filter((w) => w.isFlagged || w.text.length >= minWordLen), hexSequences);
331
336
  if (filtered.length < mismatches.length) {
332
337
  return filtered;
333
338
  }
@@ -1,6 +1,6 @@
1
1
  import type { SpellingDictionary } from 'cspell-dictionary';
2
2
  import type { ValidationResult } from '../Models/ValidationResult.js';
3
- import type { SubstitutionTransformer } from '../Transform/index.js';
3
+ import type { TextTransformer } from '../Transform/index.js';
4
4
  import * as TextRange from '../Transform/index.js';
5
5
  import type { IncludeExcludeOptions, LineSegment, ValidationOptions } from './ValidationTypes.js';
6
6
  /**
@@ -8,7 +8,7 @@ import type { IncludeExcludeOptions, LineSegment, ValidationOptions } from './Va
8
8
  * @deprecation Use spellCheckDocument
9
9
  */
10
10
  export declare function validateText(text: string, dict: SpellingDictionary, options: ValidationOptions): Iterable<ValidationResult>;
11
- export declare function calcTextInclusionRanges(text: string, options: IncludeExcludeOptions, transformer?: SubstitutionTransformer): TextRange.MatchRange[];
11
+ export declare function calcTextInclusionRanges(text: string, options: IncludeExcludeOptions, transformer?: TextTransformer): TextRange.MatchRange[];
12
12
  /**
13
13
  * Returns a mapper function that will segment a TextOffset based upon the includeRanges.
14
14
  * This function is optimized for forward scanning. It will perform poorly for randomly ordered offsets.
@@ -2,7 +2,7 @@ import { getSources } from '../Settings/index.js';
2
2
  import { createCollection, getInlineConfigDictionaries, mapSpecialDictionaryNamesToSettings, } from '../SpellingDictionary/index.js';
3
3
  import { toFileUrl } from '../util/url.js';
4
4
  import { uniqueFn } from '../util/util.js';
5
- import { split } from '../util/wordSplitter.js';
5
+ import { split } from '../util/wordSplitter/index.js';
6
6
  export function traceWord(word, dictCollection, config) {
7
7
  const opts = {
8
8
  ignoreCase: config.ignoreCase ?? true,
@@ -7,7 +7,7 @@ export const regExpCamelCaseWordBreaks_NOT_USED = /(?<=\p{Ll}\p{M}?)(?=\p{Lu})|(
7
7
  export const regExpAllPossibleWordBreaks_NOT_USED = /(?<=\p{Ll}\p{M}?)(?=\p{Lu})|(?<=\p{Lu}\p{M}?)(?=\p{Lu}\p{M}?\p{Ll})|(?<=\p{Lu}\p{M}?\p{Lu}\p{M}?)(?=\p{Ll})|(?<=\p{L}\p{M}?)(?=\P{L})|(?<=\P{L})(?=\p{L})/gu;
8
8
  export const regExWords = /\p{L}\p{M}?(?:(?:\\?['’])?\p{L}\p{M}?)*/gu;
9
9
  // Words can be made of letters, numbers, period, underscore, dash, plus, and single quote
10
- export const regExWordsAndDigits = /[\p{L}\w'’`.+-](?:(?:\\(?=[']))?[\p{L}\p{M}\w'’`.+-])*/gu;
10
+ export const regExWordsAndDigits = /[\p{L}\w'’`.+\u00AD-](?:(?:\\(?=[']))?[\p{L}\p{M}\w'’`.+\u00AD-])*/gu;
11
11
  export const regExIgnoreCharacters = /[\p{sc=Hiragana}\p{sc=Han}\p{sc=Katakana}\u30A0-\u30FF\p{sc=Hangul}]/gu;
12
12
  export const regExFirstUpper = /^\p{Lu}\p{M}?\p{Ll}+$/u;
13
13
  export const regExAllUpper = /^(?:\p{Lu}\p{M}?)+$/u;
@@ -0,0 +1,32 @@
1
+ import type { TextOffset } from '@cspell/cspell-types';
2
+ export declare const softHyphen = "\u00AD";
3
+ export interface LineSegment {
4
+ line: TextOffset;
5
+ /**
6
+ * The starting offset within line.text.
7
+ * The absolute offset can be obtained by adding line.offset.
8
+ */
9
+ relStart: number;
10
+ /**
11
+ * The ending offset within line.text.
12
+ * The absolute offset can be obtained by adding line.offset.
13
+ */
14
+ relEnd: number;
15
+ }
16
+ export type BreakPairs = readonly [number, number];
17
+ export interface PossibleWordBreak {
18
+ /** offset from the start of the string */
19
+ offset: number;
20
+ /**
21
+ * break pairs (start, end)
22
+ * (the characters between the start and end are removed)
23
+ * With a pure break, start === end.
24
+ */
25
+ breaks: BreakPairs[];
26
+ }
27
+ export type SortedBreaks = PossibleWordBreak[];
28
+ export interface WordBreakOptions {
29
+ optionalWordBreakCharacters?: string;
30
+ }
31
+ export declare function generateWordBreaks(line: LineSegment, options: WordBreakOptions): SortedBreaks;
32
+ //# sourceMappingURL=generateWordBreaks.d.ts.map