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.
- package/dist/lib/Settings/LanguageSettings.js +17 -1
- package/dist/lib/Settings/sanitizeSettings.js +3 -0
- package/dist/lib/Transform/IntlSegmentTextTransformer.d.ts +11 -0
- package/dist/lib/Transform/IntlSegmentTextTransformer.js +41 -0
- package/dist/lib/Transform/SubstitutionTransformer.d.ts +3 -1
- package/dist/lib/Transform/SubstitutionTransformer.js +10 -28
- package/dist/lib/Transform/TextMapEdit.d.ts +5 -0
- package/dist/lib/Transform/TextMapEdit.js +42 -0
- package/dist/lib/Transform/Transformer.d.ts +25 -0
- package/dist/lib/Transform/Transformer.js +27 -0
- package/dist/lib/Transform/index.d.ts +3 -0
- package/dist/lib/Transform/index.js +2 -0
- package/dist/lib/textValidation/docValidator.d.ts +3 -2
- package/dist/lib/textValidation/docValidator.js +22 -20
- package/dist/lib/textValidation/isRandomString.js +4 -3
- package/dist/lib/textValidation/lineValidatorFactory.d.ts +2 -2
- package/dist/lib/textValidation/lineValidatorFactory.js +7 -2
- package/dist/lib/textValidation/textValidator.d.ts +2 -2
- package/dist/lib/textValidation/traceWord.js +1 -1
- package/dist/lib/util/textRegex.js +1 -1
- package/dist/lib/util/wordSplitter/generateWordBreaks.d.ts +32 -0
- package/dist/lib/util/wordSplitter/generateWordBreaks.js +120 -0
- package/dist/lib/util/wordSplitter/index.d.ts +4 -0
- package/dist/lib/util/wordSplitter/index.js +3 -0
- package/dist/lib/util/{wordSplitter.d.ts → wordSplitter/wordSplitter.d.ts} +13 -22
- package/dist/lib/util/wordSplitter/wordSplitter.js +338 -0
- package/dist/lib/util/{wordSplitter.perf.js → wordSplitter/wordSplitter.perf.js} +2 -2
- package/package.json +20 -19
- package/dist/lib/util/wordSplitter.js +0 -331
- /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})
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
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?:
|
|
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'
|
|
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
|