Skip to content

Commit b2e014f

Browse files
authored
fix(cspell-tools): support adding directives (#5860)
1 parent a5dde6a commit b2e014f

File tree

7 files changed

+112
-16
lines changed

7 files changed

+112
-16
lines changed

packages/cspell-tools/cspell-tools.config.schema.json

+18-4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@
144144
"description": "gzip the file?",
145145
"type": "boolean"
146146
},
147+
"dictionaryDirectives": {
148+
"description": "Injects `cspell-dictionary` directives into the dictionary header.\n\nExample:\n\n```ini # cspell-dictionary: no-generate-alternatives ```\n\nKnown Directives: ```yaml\n- split # Tell the dictionary loader to split words\n- no-split # Tell the dictionary loader to not split words (default)\n- generate-alternatives # Tell the dictionary loader to generate alternate spellings (default)\n- no-generate-alternatives # Tell the dictionary loader to not generate alternate spellings ```",
149+
"items": {
150+
"type": "string"
151+
},
152+
"type": "array"
153+
},
147154
"excludeWordsFrom": {
148155
"description": "Words from the sources that are found in `excludeWordsFrom` files will not be added to the dictionary.",
149156
"items": {
@@ -156,7 +163,7 @@
156163
"description": "Format of the dictionary."
157164
},
158165
"generateNonStrict": {
159-
"default": true,
166+
"default": false,
160167
"description": "Generate lower case / accent free versions of words.",
161168
"type": "boolean"
162169
},
@@ -165,7 +172,7 @@
165172
"type": "string"
166173
},
167174
"sort": {
168-
"default": ": true",
175+
"default": true,
169176
"description": "Sort the words in the resulting dictionary. Does not apply to `trie` based formats.",
170177
"type": "boolean"
171178
},
@@ -221,8 +228,15 @@
221228
"boolean"
222229
]
223230
},
231+
"dictionaryDirectives": {
232+
"description": "Injects `cspell-dictionary` directives into the dictionary header.\n\nExample:\n\n```ini # cspell-dictionary: no-generate-alternatives ```\n\nKnown Directives: ```yaml\n- split # Tell the dictionary loader to split words\n- no-split # Tell the dictionary loader to not split words (default)\n- generate-alternatives # Tell the dictionary loader to generate alternate spellings (default)\n- no-generate-alternatives # Tell the dictionary loader to not generate alternate spellings ```",
233+
"items": {
234+
"type": "string"
235+
},
236+
"type": "array"
237+
},
224238
"generateNonStrict": {
225-
"default": true,
239+
"default": false,
226240
"description": "Generate lower case / accent free versions of words.",
227241
"type": "boolean"
228242
},
@@ -240,7 +254,7 @@
240254
"type": "string"
241255
},
242256
"sort": {
243-
"default": ": true",
257+
"default": true,
244258
"description": "Sort the words in the resulting dictionary. Does not apply to `trie` based formats.",
245259
"type": "boolean"
246260
},

packages/cspell-tools/src/compiler/CompileOptions.ts

+12
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,16 @@ export interface CompileOptions {
1616
* @returns `true` to keep the word, `false` to exclude it.
1717
*/
1818
filter?: (word: string) => boolean;
19+
20+
/**
21+
* Injects `cspell-dictionary` directives into the dictionary header.
22+
*
23+
* Example:
24+
*
25+
* ```ini
26+
* # cspell-dictionary: no-generate-alternatives
27+
* ```
28+
*
29+
*/
30+
dictionaryDirectives?: string[] | undefined;
1931
}

packages/cspell-tools/src/compiler/__snapshots__/wordListCompiler.test.ts.snap

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`Validate the wordListCompiler > Specify directives 1`] = `
4+
{
5+
"error": "",
6+
"log": "",
7+
}
8+
`;
9+
310
exports[`Validate the wordListCompiler > a simple hunspell dictionary depth 0 1`] = `
411
{
512
"error": "",

packages/cspell-tools/src/compiler/compile.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,21 @@ export async function compile(request: CompileRequest, options?: CompileOptions)
5353
};
5454
const conditional = options?.conditionalBuild || false;
5555
const checksumFile = resolveChecksumFile(request.checksumFile || conditional, rootDir);
56+
const dictionaryDirectives = request.dictionaryDirectives;
5657

5758
const dependencies = new Set<string>();
5859

5960
for (const target of targets) {
6061
const keep = options?.filter?.(target) ?? true;
6162
if (!keep) continue;
6263
const adjustedTarget: Target = { ...targetOptions, ...target };
63-
const deps = await compileTarget(adjustedTarget, request, { rootDir, cwd, conditional, checksumFile });
64+
const deps = await compileTarget(adjustedTarget, request, {
65+
rootDir,
66+
cwd,
67+
conditional,
68+
checksumFile,
69+
dictionaryDirectives,
70+
});
6471
deps.forEach((dep) => dependencies.add(dep));
6572
}
6673

@@ -87,6 +94,7 @@ interface CompileTargetOptions {
8794
cwd: string | undefined;
8895
conditional: boolean;
8996
checksumFile: string | undefined;
97+
dictionaryDirectives: string[] | undefined;
9098
}
9199

92100
export async function compileTarget(
@@ -98,6 +106,7 @@ export async function compileTarget(
98106
const { rootDir, cwd, checksumFile, conditional } = compileOptions;
99107
const { format, sources, trieBase, sort = true, generateNonStrict = false, excludeWordsFrom } = target;
100108
const targetDirectory = path.resolve(rootDir, target.targetDirectory ?? cwd ?? process.cwd());
109+
const dictionaryDirectives = compileOptions.dictionaryDirectives;
101110

102111
const excludeFilter = await createExcludeFilter(excludeWordsFrom);
103112

@@ -114,7 +123,12 @@ export async function compileTarget(
114123
opAwaitAsync(),
115124
);
116125
const filesToProcess: FileToProcess[] = await toArray(filesToProcessAsync);
117-
const normalizer = normalizeTargetWords({ sort: useTrie || sort, generateNonStrict, filter: excludeFilter });
126+
const normalizer = normalizeTargetWords({
127+
sort: useTrie || sort,
128+
generateNonStrict,
129+
filter: excludeFilter,
130+
dictionaryDirectives,
131+
});
118132
const checksumRoot = (checksumFile && path.dirname(checksumFile)) || rootDir;
119133

120134
const deps = [...calculateDependencies(filename, filesToProcess, excludeWordsFrom, checksumRoot)];
@@ -135,10 +149,11 @@ export async function compileTarget(
135149
trie3: format === 'trie3',
136150
trie4: format === 'trie4',
137151
generateNonStrict: generateNonStrictTrie,
152+
dictionaryDirectives: undefined,
138153
});
139154
}
140155
: async (words: Iterable<string>, dst: string) => {
141-
return compileWordList(pipe(words, normalizer), dst, { sort, generateNonStrict });
156+
return compileWordList(pipe(words, normalizer), dst, { sort, generateNonStrict, dictionaryDirectives });
142157
};
143158

144159
await processFiles(action, filesToProcess, filename);

packages/cspell-tools/src/compiler/wordListCompiler.test.ts

+31-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('Validate the wordListCompiler', () => {
7070
const output = await fsp.readFile(destName, 'utf8');
7171
expect(output).toBe(
7272
wordListHeader +
73-
'\n' +
73+
'\n\n' +
7474
citiesSorted +
7575
citiesSorted
7676
.toLowerCase()
@@ -115,7 +115,7 @@ describe('Validate the wordListCompiler', () => {
115115
const destName = path.join(temp, 'example0.txt');
116116
await compileWordList(source, destName, compileOpt(false));
117117
const output = await fsp.readFile(destName, 'utf8');
118-
expect(output).toBe(__testing__.wordListHeader + '\n' + 'hello\ntry\nwork\n');
118+
expect(output).toBe(__testing__.wordListHeader + '\n\n' + 'hello\ntry\nwork\n');
119119
expect(consoleOutput()).toMatchSnapshot();
120120
});
121121

@@ -144,6 +144,33 @@ describe('Validate the wordListCompiler', () => {
144144
);
145145
expect(consoleOutput()).toMatchSnapshot();
146146
});
147+
148+
test('Specify directives', async () => {
149+
const source = await streamSourceWordsFromFile(path.join(samples, 'hunspell/example.dic'), {
150+
...readOptions,
151+
maxDepth: 1,
152+
});
153+
const destName = path.join(temp, 'example2.txt');
154+
await compileWordList(source, destName, compileOpt(false, false, ['no-generate-alternatives']));
155+
const output = await fsp.readFile(destName, 'utf8');
156+
expect(output.split('\n')).toEqual(
157+
`\
158+
159+
# cspell-tools: keep-case no-split
160+
# cspell-dictionary: no-generate-alternatives
161+
162+
hello
163+
rework
164+
tried
165+
try
166+
work
167+
worked
168+
`
169+
.split('\n')
170+
.map((line) => line.trim()),
171+
);
172+
expect(consoleOutput()).toMatchSnapshot();
173+
});
147174
});
148175

149176
describe('Validate Larger Dictionary', () => {
@@ -202,8 +229,8 @@ function legacyNormalizeWords(lines: Iterable<string>): Iterable<string> {
202229
);
203230
}
204231

205-
function compileOpt(sort: boolean, generateNonStrict = true): CompileOptions {
206-
return { sort, generateNonStrict };
232+
function compileOpt(sort: boolean, generateNonStrict = true, dictionaryDirectives?: string[]): CompileOptions {
233+
return { sort, generateNonStrict, dictionaryDirectives };
207234
}
208235

209236
// const cities = `\

packages/cspell-tools/src/compiler/wordListCompiler.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ const mkdirp = async (p: string) => {
1515

1616
// Indicate that a word list has already been processed.
1717
const wordListHeader = `
18-
# cspell-tools: keep-case no-split
19-
`;
18+
# cspell-tools: keep-case no-split`;
2019
const wordListHeaderLines = wordListHeader.split('\n').map((a) => a.trim());
2120

2221
export async function compileWordList(
@@ -26,7 +25,10 @@ export async function compileWordList(
2625
): Promise<void> {
2726
const finalLines = normalize(lines, options);
2827

29-
const finalSeq = pipe(wordListHeaderLines, opAppend(finalLines));
28+
const directives = options.dictionaryDirectives ?? [];
29+
const directivesLines = directives.map((a) => `# cspell-dictionary: ${a}`);
30+
31+
const finalSeq = pipe([...wordListHeaderLines, ...directivesLines, ''], opAppend(finalLines));
3032

3133
return createWordListTarget(destFilename)(finalSeq);
3234
}

packages/cspell-tools/src/config/config.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ export interface Experimental {
4545
export interface CompileTargetOptions {
4646
/**
4747
* Generate lower case / accent free versions of words.
48-
* @default true
48+
* @default false
4949
*/
5050
generateNonStrict?: boolean | undefined;
5151

5252
/**
5353
* Sort the words in the resulting dictionary.
5454
* Does not apply to `trie` based formats.
55-
* @default: true
55+
* @default true
5656
*/
5757
sort?: boolean | undefined;
5858

@@ -66,6 +66,25 @@ export interface CompileTargetOptions {
6666
* dictionary.
6767
*/
6868
allowedSplitWords?: FilePath | FilePath[] | undefined;
69+
70+
/**
71+
* Injects `cspell-dictionary` directives into the dictionary header.
72+
*
73+
* Example:
74+
*
75+
* ```ini
76+
* # cspell-dictionary: no-generate-alternatives
77+
* ```
78+
*
79+
* Known Directives:
80+
* ```yaml
81+
* - split # Tell the dictionary loader to split words
82+
* - no-split # Tell the dictionary loader to not split words (default)
83+
* - generate-alternatives # Tell the dictionary loader to generate alternate spellings (default)
84+
* - no-generate-alternatives # Tell the dictionary loader to not generate alternate spellings
85+
* ```
86+
*/
87+
dictionaryDirectives?: string[] | undefined;
6988
}
7089

7190
export interface Target extends CompileTargetOptions {

0 commit comments

Comments
 (0)