cspell 9.4.0 → 9.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,13 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { isAsyncIterable, opFilter, opMap, opTap, operators, pipeAsync, pipeAsync as asyncPipe, toAsyncIterable, toAsyncIterable as mergeAsyncIterables } from "@cspell/cspell-pipe";
3
3
  import * as cspell from "cspell-lib";
4
- import { ENV_CSPELL_GLOB_ROOT, IncludeExcludeFlag, SuggestionError, Text, checkTextDocument, combineTextAndLanguageSettings, createPerfTimer, extractDependencies, extractImportErrors, fileToDocument, getDefaultSettings, getDictionary, getGlobalSettingsAsync, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, mergeSettings, setLogger, shouldCheckDocument, spellCheckDocument, suggestionsForWords, traceWordsAsync } from "cspell-lib";
4
+ import { ENV_CSPELL_GLOB_ROOT, IncludeExcludeFlag, MessageTypes, SuggestionError, Text, checkTextDocument, combineTextAndLanguageSettings, createPerfTimer, extractDependencies, extractImportErrors, fileToDocument, getDefaultSettings, getDictionary, getGlobalSettingsAsync, getSystemFeatureFlags, isBinaryFile, isSpellingDictionaryLoadError, mergeSettings, setLogger, shouldCheckDocument, spellCheckDocument, suggestionsForWords, traceWordsAsync } from "cspell-lib";
5
5
  import assert from "node:assert";
6
6
  import { format, formatWithOptions, stripVTControlCharacters } from "node:util";
7
7
  import { isUrlLike, toFileDirURL, toFilePathOrHref, toFileURL, urlRelative } from "@cspell/url";
8
8
  import chalk, { Chalk } from "chalk";
9
9
  import { makeTemplate } from "chalk-template";
10
+ import ansiRegex from "ansi-regex";
10
11
  import fs, { stat } from "node:fs/promises";
11
12
  import { MutableCSpellConfigFile, createReaderWriter, cspellConfigFileSchema, isCfgArrayNode } from "cspell-config-lib";
12
13
  import { promises } from "node:fs";
@@ -14,8 +15,8 @@ import { fileURLToPath } from "node:url";
14
15
  import * as path$1 from "node:path";
15
16
  import path, { isAbsolute, posix, relative, resolve, sep } from "node:path";
16
17
  import { opMap as opMap$1, pipe } from "@cspell/cspell-pipe/sync";
17
- import { IssueType, MessageTypes, unknownWordsChoices } from "@cspell/cspell-types";
18
- import { _debug } from "cspell-dictionary";
18
+ import { IssueType, MessageTypes as MessageTypes$1, unknownWordsChoices } from "@cspell/cspell-types";
19
+ import { dictionaryCacheEnableLogging, dictionaryCacheGetLog } from "cspell-dictionary";
19
20
  import { GitIgnore, findRepoRoot } from "cspell-gitignore";
20
21
  import { GlobMatcher, fileOrGlobToGlob, workaroundPicomatchBug } from "cspell-glob";
21
22
  import crypto from "node:crypto";
@@ -25,7 +26,6 @@ import { glob } from "tinyglobby";
25
26
  import * as readline from "node:readline";
26
27
  import { parse, stringify } from "flatted";
27
28
  import { dynamicImport } from "@cspell/dynamic-import";
28
- import ansiRegex from "ansi-regex";
29
29
 
30
30
  //#region src/console.ts
31
31
  var ImplChannel = class {
@@ -89,7 +89,7 @@ var IOError = class extends ApplicationError {
89
89
  return this.cause.code === "ENOENT";
90
90
  }
91
91
  };
92
- function toError(e) {
92
+ function toError$1(e) {
93
93
  if (isError(e)) return e;
94
94
  if (isErrorLike(e)) {
95
95
  const ex = new Error(e.message, { cause: e });
@@ -109,10 +109,349 @@ function isErrorLike(e) {
109
109
  }
110
110
  function toApplicationError(e, message) {
111
111
  if (e instanceof ApplicationError && !message) return e;
112
- const err = toError(e);
112
+ const err = toError$1(e);
113
113
  return new ApplicationError(message ?? err.message, void 0, err);
114
114
  }
115
115
 
116
+ //#endregion
117
+ //#region src/util/perfMeasurements.ts
118
+ function getPerfMeasurements() {
119
+ const measurements = performance.getEntriesByType("measure");
120
+ const root = {
121
+ depth: -1,
122
+ totalTimeMs: 0,
123
+ nestedTimeMs: 0,
124
+ children: /* @__PURE__ */ new Map()
125
+ };
126
+ if (!measurements.length) return [];
127
+ const stack = [];
128
+ let depth = 0;
129
+ for (let i = 0; i < measurements.length; i++) {
130
+ const m = measurements[i];
131
+ rollUpStack(m.startTime);
132
+ const s = {
133
+ m,
134
+ p: addToParent(depth === 0 ? root : stack[depth - 1].p, m)
135
+ };
136
+ stack[depth++] = s;
137
+ }
138
+ sortChildren(root);
139
+ return [...root.children.values()].flatMap((r) => [...flattenChildren(r)]);
140
+ function contains(m, t) {
141
+ const stop = m.startTime + m.duration;
142
+ return t >= m.startTime && t < stop;
143
+ }
144
+ function rollUpStack(t) {
145
+ for (; depth > 0 && !contains(stack[depth - 1].m, t); --depth);
146
+ }
147
+ function addToParent(p, m) {
148
+ p.children ??= /* @__PURE__ */ new Map();
149
+ p.nestedTimeMs += m.duration;
150
+ return updateChild(p.children, m, p.depth + 1);
151
+ }
152
+ function updateChild(children, m, depth$1) {
153
+ const p = children.get(m.name);
154
+ if (p) {
155
+ p.totalTimeMs += m.duration;
156
+ p.count += 1;
157
+ p.minTimeMs = Math.min(p.minTimeMs, m.duration);
158
+ p.maxTimeMs = Math.max(p.maxTimeMs, m.duration);
159
+ return p;
160
+ }
161
+ const n = {
162
+ name: m.name,
163
+ depth: depth$1,
164
+ totalTimeMs: m.duration,
165
+ nestedTimeMs: 0,
166
+ count: 1,
167
+ minTimeMs: m.duration,
168
+ maxTimeMs: m.duration
169
+ };
170
+ children.set(m.name, n);
171
+ return n;
172
+ }
173
+ function* flattenChildren(m) {
174
+ yield m;
175
+ if (!m.children) return;
176
+ for (const child of m.children.values()) yield* flattenChildren(child);
177
+ }
178
+ function sortChildren(m) {
179
+ if (!m.children) return;
180
+ m.children = new Map([...m.children.entries()].sort((a, b) => b[1].totalTimeMs - a[1].totalTimeMs));
181
+ m.children.forEach(sortChildren);
182
+ }
183
+ }
184
+
185
+ //#endregion
186
+ //#region src/util/ansi.ts
187
+ function isAnsiString(s) {
188
+ return s.includes("\x1B") || s.includes("›");
189
+ }
190
+ /**
191
+ *
192
+ * @param s - the string to measure - should NOT contains ANSI codes
193
+ * @param tabWidth -
194
+ * @returns
195
+ */
196
+ function width(s, tabWidth = 1) {
197
+ return s.replaceAll("…", ".").replaceAll(" ", " ".repeat(tabWidth)).replaceAll(/\p{M}/gu, "").replaceAll(/\p{L}/gu, ".").replaceAll(/[\u0000-\u001F\u0300-\u036F]/g, "").replaceAll(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, ".").length;
198
+ }
199
+ /**
200
+ * Measure the width of a string containing ANSI control characters.
201
+ * @param s - string to measure with width in characters.
202
+ * @returns the approximate number of screen characters.
203
+ */
204
+ function ansiWidth(s) {
205
+ return width(stripVTControlCharacters(s));
206
+ }
207
+ function fragmentString(str, splitOnRegex, sType) {
208
+ const fragments = [];
209
+ let lastIndex = 0;
210
+ for (const match of str.matchAll(new RegExp(splitOnRegex))) {
211
+ if (match.index > lastIndex) fragments.push({
212
+ type: "text",
213
+ text: str.slice(lastIndex, match.index)
214
+ });
215
+ fragments.push({
216
+ type: sType,
217
+ text: match[0]
218
+ });
219
+ lastIndex = match.index + match[0].length;
220
+ }
221
+ if (lastIndex < str.length) fragments.push({
222
+ type: "text",
223
+ text: str.slice(lastIndex)
224
+ });
225
+ return fragments;
226
+ }
227
+ const ansi = ansiRegex();
228
+ function parseAnsiStr(str) {
229
+ return fragmentString(str, ansi, "ansi");
230
+ }
231
+ /**
232
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
233
+ * @param str - the text to prune - ANSI is supported
234
+ * @param maxWidth - the maximum width of the text
235
+ * @param pad - the string to use for padding, default is '…'
236
+ * @returns the pruned text
237
+ */
238
+ function pruneAnsiTextEnd(str, maxWidth, pad$1 = "…") {
239
+ if (!maxWidth || maxWidth <= 0) return str;
240
+ if (str.length <= maxWidth) return str;
241
+ if (ansiWidth(str) <= maxWidth) return str;
242
+ const padWidth$1 = ansiWidth(pad$1);
243
+ const fragments = parseAnsiStr(str);
244
+ let remaining = maxWidth - padWidth$1;
245
+ for (const frag of fragments) {
246
+ if (frag.type !== "text") continue;
247
+ if (remaining <= 0) {
248
+ frag.text = "";
249
+ continue;
250
+ }
251
+ const pruned = pruneTextEnd(frag.text, remaining, pad$1);
252
+ if (pruned !== frag.text) {
253
+ frag.text = pruned;
254
+ remaining = 0;
255
+ continue;
256
+ }
257
+ remaining -= width(frag.text);
258
+ }
259
+ return fragments.map((frag) => frag.text).join("");
260
+ }
261
+ /**
262
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
263
+ * @param str - the text to prune - ANSI is supported
264
+ * @param maxWidth - the maximum width of the text
265
+ * @param pad - the string to use for padding, default is '…'
266
+ * @returns the pruned text
267
+ */
268
+ function pruneAnsiTextStart(str, maxWidth, pad$1 = "…") {
269
+ if (!maxWidth || maxWidth <= 0) return str;
270
+ if (str.length <= maxWidth) return str;
271
+ if (ansiWidth(str) <= maxWidth) return str;
272
+ const padWidth$1 = ansiWidth(pad$1);
273
+ const fragments = parseAnsiStr(str);
274
+ let remaining = maxWidth - padWidth$1;
275
+ for (const frag of fragments.reverse()) {
276
+ if (frag.type !== "text") continue;
277
+ if (remaining <= 0) {
278
+ frag.text = "";
279
+ continue;
280
+ }
281
+ const pruned = pruneTextStart(frag.text, remaining, pad$1);
282
+ if (pruned !== frag.text) {
283
+ frag.text = pruned;
284
+ remaining = 0;
285
+ continue;
286
+ }
287
+ remaining -= width(frag.text);
288
+ }
289
+ return fragments.reverse().map((frag) => frag.text).join("");
290
+ }
291
+ /**
292
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
293
+ * @param str - the text to prune - ANSI is not supported
294
+ * @param maxWidth - the maximum width of the text
295
+ * @param pad - the string to use for padding, default is '…'
296
+ * @returns the pruned text
297
+ */
298
+ function pruneTextEnd(str, maxWidth, pad$1 = "…") {
299
+ if (!maxWidth || maxWidth <= 0) return str;
300
+ if (str.length <= maxWidth) return str;
301
+ if (isAnsiString(str)) return pruneAnsiTextEnd(str, maxWidth, pad$1);
302
+ const maxWidthWithPad = maxWidth - width(pad$1);
303
+ const letters = [...str];
304
+ let len = 0;
305
+ for (let i = 0; i < letters.length; i++) {
306
+ const c = letters[i];
307
+ len += width(c);
308
+ if (len > maxWidthWithPad) {
309
+ let j = i + 1;
310
+ while (j < letters.length && width(letters[j]) === 0) ++j;
311
+ return j === letters.length ? str : letters.slice(0, i).join("") + pad$1;
312
+ }
313
+ }
314
+ return str;
315
+ }
316
+ /**
317
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
318
+ * @param str - the text to prune - ANSI is not supported
319
+ * @param maxWidth - the maximum width of the text
320
+ * @param pad - the string to use for padding, default is '…'
321
+ * @returns the pruned text
322
+ */
323
+ function pruneTextStart(str, maxWidth, pad$1 = "…") {
324
+ if (!maxWidth || maxWidth <= 0) return str;
325
+ if (str.length <= maxWidth) return str;
326
+ const maxWidthWithPad = maxWidth - width(pad$1);
327
+ const letters = [...str];
328
+ let len = 0;
329
+ for (let i = letters.length - 1; i >= 1; i--) {
330
+ const c = letters[i];
331
+ len += width(c);
332
+ if (len > maxWidthWithPad) {
333
+ i += 1;
334
+ while (i < letters.length && width(letters[i]) === 0) ++i;
335
+ return pad$1 + letters.slice(i).join("");
336
+ }
337
+ }
338
+ return str;
339
+ }
340
+
341
+ //#endregion
342
+ //#region src/util/pad.ts
343
+ function pad(s, w) {
344
+ const p = padWidth(s, w);
345
+ if (!p) return s;
346
+ return s.padEnd(p + s.length);
347
+ }
348
+ function padWidth(s, target) {
349
+ const sWidth = ansiWidth(s);
350
+ return Math.max(target - sWidth, 0);
351
+ }
352
+ function padLeft(s, w) {
353
+ const p = padWidth(s, w);
354
+ if (!p) return s;
355
+ return s.padStart(p + s.length);
356
+ }
357
+
358
+ //#endregion
359
+ //#region src/util/table.ts
360
+ function tableToLines(table, deliminator) {
361
+ const del = deliminator || table.deliminator || " | ";
362
+ const columnWidths = [];
363
+ const columnAlignments = table.columnAlignments || [];
364
+ const maxColumnWidthsMap = table.maxColumnWidths || {};
365
+ const tableIndent = table.indent ? typeof table.indent === "number" ? " ".repeat(table.indent) : table.indent : "";
366
+ const { header, rows } = table;
367
+ const simpleHeader = header.map((col) => Array.isArray(col) ? col[1] : col);
368
+ const columnFieldNames = header.map((col) => Array.isArray(col) ? col[0] : col);
369
+ const maxColumnWidths = columnFieldNames.map((field, idx) => maxColumnWidthsMap[field] ?? maxColumnWidthsMap[idx]);
370
+ function getCell(row, col) {
371
+ return getCellFromRow(rows[row], col);
372
+ }
373
+ function getCellFromRow(row, col) {
374
+ if (!row) return void 0;
375
+ if (Array.isArray(row)) return row[col];
376
+ return row[columnFieldNames[col]];
377
+ }
378
+ function rowToCells(row) {
379
+ if (Array.isArray(row)) return row;
380
+ return columnFieldNames.map((fieldName) => row[fieldName]);
381
+ }
382
+ function getText(col, maxWidth) {
383
+ return !col ? "" : typeof col === "string" ? pruneTextEnd(col, maxWidth) : col(maxWidth);
384
+ }
385
+ function getRCText(row, col, maxWidth) {
386
+ return getText(getCell(row, col), maxWidth);
387
+ }
388
+ function recordHeaderWidths(header$1) {
389
+ header$1.forEach((col, idx) => {
390
+ columnWidths[idx] = Math.max(ansiWidth(col), columnWidths[idx] || 0);
391
+ });
392
+ }
393
+ function recordColWidths() {
394
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) for (let colIndex = 0; colIndex < columnFieldNames.length; colIndex++) columnWidths[colIndex] = Math.max(ansiWidth(getRCText(rowIndex, colIndex, void 0)), columnWidths[colIndex] || 0);
395
+ }
396
+ function justifyRow(c, i) {
397
+ return columnAlignments[i] === "R" ? padLeft(c, columnWidths[i]) : pad(c, columnWidths[i]);
398
+ }
399
+ function toHeaderLine(header$1) {
400
+ return tableIndent + decorateRowWith(header$1.map((c, i) => getText(c, columnWidths[i])), justifyRow, headerDecorator).join(del);
401
+ }
402
+ function toLine(row) {
403
+ return tableIndent + decorateRowWith(rowToCells(row).map((c, i) => getText(c, columnWidths[i])), justifyRow).join(del);
404
+ }
405
+ function* process$1() {
406
+ if (table.title) yield table.title;
407
+ yield toHeaderLine(simpleHeader);
408
+ yield* rows.map(toLine);
409
+ }
410
+ function sumColumnWidths() {
411
+ return columnWidths.reduce((sum, width$1) => sum + width$1, 0);
412
+ }
413
+ function adjustColWidths() {
414
+ for (let i = 0; i < columnWidths.length; i++) {
415
+ const mw = maxColumnWidths[i];
416
+ if (!mw) continue;
417
+ columnWidths[i] = Math.min(columnWidths[i], mw);
418
+ }
419
+ if (!table.terminalWidth) return;
420
+ const dWidth = (columnWidths.length - 1) * ansiWidth(del);
421
+ const lineWidth = table.terminalWidth - dWidth;
422
+ if (lineWidth <= columnWidths.length * 2) {
423
+ const fixedWidth = Math.max(Math.min(...columnWidths), 5);
424
+ for (let i = 0; i < columnWidths.length; i++) columnWidths[i] = fixedWidth;
425
+ return;
426
+ }
427
+ if (columnWidths.length === 1) {
428
+ columnWidths[0] = lineWidth;
429
+ return;
430
+ }
431
+ function trimWidestColumn(neededToTrim) {
432
+ let first = 0;
433
+ let second = 0;
434
+ for (let i = 0; i < columnWidths.length; i++) if (columnWidths[i] > columnWidths[first]) {
435
+ second = first;
436
+ first = i;
437
+ } else if (columnWidths[i] > columnWidths[second]) second = i;
438
+ const diff = Math.max(columnWidths[first] - columnWidths[second], 1);
439
+ columnWidths[first] -= Math.min(diff, neededToTrim);
440
+ }
441
+ for (let sum = sumColumnWidths(); sum > lineWidth; sum = sumColumnWidths()) trimWidestColumn(sum - lineWidth);
442
+ }
443
+ recordHeaderWidths(simpleHeader);
444
+ recordColWidths();
445
+ adjustColWidths();
446
+ return [...process$1()];
447
+ }
448
+ function headerDecorator(t) {
449
+ return chalk.bold(chalk.underline(t));
450
+ }
451
+ function decorateRowWith(row, ...decorators) {
452
+ return decorators.reduce((row$1, decorator) => row$1.map(decorator), row);
453
+ }
454
+
116
455
  //#endregion
117
456
  //#region src/util/util.ts
118
457
  const uniqueFn = uniqueFilterFnGenerator;
@@ -279,6 +618,7 @@ function getReporter(options, config) {
279
618
  if (!fileGlobs.length && !result.files) return;
280
619
  const { files, issues: issues$1, cachedFiles, filesWithIssues, errors, skippedFiles } = result;
281
620
  const numFilesWithIssues = filesWithIssues.size;
621
+ const chalk$1 = stderr.chalk;
282
622
  if (stderr.getColorLevel() > 0) {
283
623
  stderr.write("\r");
284
624
  stderr.clearLine(0);
@@ -305,14 +645,80 @@ function getReporter(options, config) {
305
645
  consoleError(` Files Skipped : ${perfStats.filesSkipped.toString().padStart(6)}`);
306
646
  consoleError(` Files Cached : ${perfStats.filesCached.toString().padStart(6)}`);
307
647
  consoleError(` Processing Time: ${perfStats.elapsedTimeMs.toFixed(2).padStart(9)}ms`);
308
- consoleError("Stats:");
309
- const stats = Object.entries(perfStats.perf).filter((p) => !!p[1]).map(([key, value]) => [key, value.toFixed(2)]);
310
- const padName = Math.max(...stats.map((s) => s[0].length));
311
- const padValue = Math.max(...stats.map((s) => s[1].length));
312
- stats.sort((a, b) => a[0].localeCompare(b[0]));
313
- for (const [key, value] of stats) value && consoleError(` ${key.padEnd(padName)}: ${value.padStart(padValue)}ms`);
648
+ const tableStats = {
649
+ title: chalk$1.bold("Perf Stats:"),
650
+ header: ["Name", "Time (ms)"],
651
+ columnAlignments: ["L", "R"],
652
+ indent: 2,
653
+ rows: Object.entries(perfStats.perf).filter((p) => !!p[1]).map(([key, value]) => [key, value.toFixed(2)])
654
+ };
655
+ consoleError("");
656
+ for (const line of tableToLines(tableStats)) consoleError(line);
657
+ if (options.verboseLevel) verbosePerfReport();
314
658
  }
315
659
  };
660
+ function verbosePerfReport() {
661
+ const perfMeasurements = getPerfMeasurements();
662
+ if (!perfMeasurements.length) return;
663
+ const notable = extractNotableBySelfTimeInGroup(perfMeasurements);
664
+ const chalk$1 = stderr.chalk;
665
+ const maxDepth = Math.max(...perfMeasurements.map((m) => m.depth));
666
+ const depthIndicator = (d) => "⋅".repeat(d) + " ".repeat(maxDepth - d);
667
+ const rows = perfMeasurements.map((m) => {
668
+ const cbd = (text) => colorByDepth(chalk$1, m.depth, text);
669
+ const cNotable = (text) => notable.has(m) ? chalk$1.yellow(text) : text;
670
+ return [
671
+ chalk$1.dim("⋅".repeat(m.depth)) + colorByDepthGrayscale(stderr.chalk, m.depth, m.name),
672
+ cbd(m.totalTimeMs.toFixed(2) + chalk$1.dim(depthIndicator(m.depth))),
673
+ cbd(cNotable((m.totalTimeMs - m.nestedTimeMs).toFixed(2))),
674
+ cbd(m.count.toString()),
675
+ cbd(m.minTimeMs.toFixed(2)),
676
+ cbd(m.maxTimeMs.toFixed(2)),
677
+ cbd((m.totalTimeMs / m.count).toFixed(2))
678
+ ];
679
+ });
680
+ const table = tableToLines({
681
+ title: chalk$1.bold("Detailed Measurements:"),
682
+ header: [
683
+ "Name",
684
+ "Total Time (ms)",
685
+ "Self (ms)",
686
+ "Count",
687
+ "Min (ms)",
688
+ "Max (ms)",
689
+ "Avg (ms)"
690
+ ],
691
+ rows,
692
+ columnAlignments: [
693
+ "L",
694
+ "R",
695
+ "R",
696
+ "R",
697
+ "R",
698
+ "R",
699
+ "R"
700
+ ],
701
+ indent: 2
702
+ });
703
+ consoleError("\n-------------------------------------------\n");
704
+ for (const line of table) consoleError(line);
705
+ }
706
+ function colorByDepth(chalk$1, depth, text) {
707
+ const colors = [
708
+ chalk$1.green,
709
+ chalk$1.cyan,
710
+ chalk$1.blue,
711
+ chalk$1.magenta,
712
+ chalk$1.red
713
+ ];
714
+ const color = colors[depth % colors.length];
715
+ if (depth / colors.length >= 1) return chalk$1.dim(color(text));
716
+ return color(text);
717
+ }
718
+ function colorByDepthGrayscale(chalk$1, depth, text) {
719
+ const grayLevel = Math.max(32, 255 - depth * 20);
720
+ return chalk$1.rgb(grayLevel, grayLevel, grayLevel)(text);
721
+ }
316
722
  function collectPerfStats(p) {
317
723
  if (p.cached) {
318
724
  perfStats.filesCached++;
@@ -338,6 +744,27 @@ function getReporter(options, config) {
338
744
  features: void 0
339
745
  };
340
746
  }
747
+ function extractNotableBySelfTimeInGroup(measurements) {
748
+ const notable = /* @__PURE__ */ new Set();
749
+ if (!measurements.length) return notable;
750
+ let highest;
751
+ let highestSelfTime = 0;
752
+ for (const m of measurements) {
753
+ if (m.depth === 0 || !highest) {
754
+ if (highest) notable.add(highest);
755
+ highest = m;
756
+ highestSelfTime = m.totalTimeMs - m.nestedTimeMs;
757
+ continue;
758
+ }
759
+ const selfTime = m.totalTimeMs - m.nestedTimeMs;
760
+ if (selfTime > highestSelfTime) {
761
+ highest = m;
762
+ highestSelfTime = selfTime;
763
+ }
764
+ }
765
+ if (highest) notable.add(highest);
766
+ return notable;
767
+ }
341
768
  function formatIssue(io, templateStr, issue, maxIssueTextWidth) {
342
769
  function clean$1(t$1) {
343
770
  return t$1.replace(/\s+/, " ");
@@ -447,7 +874,7 @@ async function fileExists(url) {
447
874
  try {
448
875
  return (await promises.stat(url)).isFile();
449
876
  } catch (e) {
450
- if (toError(e).code === "ENOENT") return false;
877
+ if (toError$1(e).code === "ENOENT") return false;
451
878
  throw e;
452
879
  }
453
880
  }
@@ -770,7 +1197,7 @@ const pkgDir = _dirname;
770
1197
  //#endregion
771
1198
  //#region src/pkgInfo.ts
772
1199
  const name = "cspell";
773
- const version$1 = "9.4.0";
1200
+ const version$1 = "9.6.0";
774
1201
  const engines = { node: ">=20" };
775
1202
  const npmPackage = {
776
1203
  name,
@@ -991,7 +1418,7 @@ function readFileInfo(filename, encoding = UTF8, handleNotFound = false) {
991
1418
  text,
992
1419
  filename
993
1420
  }), (e) => {
994
- const error = toError(e);
1421
+ const error = toError$1(e);
995
1422
  return handleNotFound && error.code === "EISDIR" ? Promise.resolve({
996
1423
  text: "",
997
1424
  filename,
@@ -1181,7 +1608,7 @@ var ImplFileEntryCache = class {
1181
1608
  return {
1182
1609
  key: file,
1183
1610
  notFound: true,
1184
- err: toError$1(error)
1611
+ err: toError(error)
1185
1612
  };
1186
1613
  }
1187
1614
  if (this.useChecksum) return this.#getFileDescriptorUsingChecksum(file);
@@ -1296,7 +1723,7 @@ var ImplFileEntryCache = class {
1296
1723
  function isNodeError(error) {
1297
1724
  return typeof error === "object" && error !== null && "code" in error;
1298
1725
  }
1299
- function toError$1(error) {
1726
+ function toError(error) {
1300
1727
  if (error instanceof Error) return error;
1301
1728
  if (typeof error === "string") return new Error(error);
1302
1729
  return new Error("Unknown error", { cause: error });
@@ -1647,61 +2074,6 @@ async function readConfigHandleError(filename) {
1647
2074
  }
1648
2075
  }
1649
2076
 
1650
- //#endregion
1651
- //#region src/util/extractContext.ts
1652
- function prefCharIndex(text, offset, count = 1) {
1653
- if (offset - count < 0) return 0;
1654
- for (; count > 0 && offset > 0; count--) {
1655
- let code = text.charCodeAt(--offset) || 0;
1656
- if (code === 65039) code = text.charCodeAt(--offset) || 0;
1657
- offset -= (code & 64512) === 56320 ? 1 : 0;
1658
- }
1659
- return offset < 0 ? 0 : offset;
1660
- }
1661
- function nextCharIndex(text, offset, count = 1) {
1662
- if (offset + count >= text.length) return text.length;
1663
- for (; count > 0 && offset < text.length; count--) {
1664
- const code = text.charCodeAt(offset++) || 0;
1665
- offset += (code & 64512) === 55296 ? 1 : 0;
1666
- if (text.charCodeAt(offset) === 65039) offset++;
1667
- }
1668
- return offset > text.length ? text.length : offset;
1669
- }
1670
- function lineContext(lineText, start, end, contextRange) {
1671
- let left = prefCharIndex(lineText, start, contextRange);
1672
- let right = nextCharIndex(lineText, end, contextRange);
1673
- const isLetter = /^\p{L}$/u;
1674
- const isMark = /^\p{M}$/u;
1675
- for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
1676
- const c = lineText[left - 1];
1677
- if (isMark.test(c)) {
1678
- if (!isLetter.test(lineText[left - 2])) break;
1679
- left--;
1680
- continue;
1681
- }
1682
- if (!isLetter.test(lineText[left - 1])) break;
1683
- }
1684
- for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
1685
- if (!isLetter.test(lineText[right])) break;
1686
- if (isMark.test(lineText[right + 1])) right++;
1687
- }
1688
- left = left < 0 ? 0 : left;
1689
- const t0 = lineText.slice(left, right);
1690
- const tLeft = t0.trimStart();
1691
- left = Math.min(left + t0.length - tLeft.length, start);
1692
- return {
1693
- text: tLeft.trimEnd(),
1694
- offset: left
1695
- };
1696
- }
1697
- function extractContext(tdo, contextRange) {
1698
- const { line, offset, text } = tdo;
1699
- const start = offset - line.offset;
1700
- const context = lineContext(line.text, start, start + text.length, contextRange);
1701
- context.offset += line.offset;
1702
- return context;
1703
- }
1704
-
1705
2077
  //#endregion
1706
2078
  //#region src/util/prefetch.ts
1707
2079
  function* prefetchIterable(iterable, size) {
@@ -1750,7 +2122,7 @@ async function loadReporters(reporters, defaultReporter, config) {
1750
2122
  const { getReporter: getReporter$1 } = await dynamicImport(moduleName, [process.cwd(), pkgDir]);
1751
2123
  return getReporter$1(settings, config);
1752
2124
  } catch (e) {
1753
- throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError(e).message}`);
2125
+ throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError$1(e).message}`);
1754
2126
  }
1755
2127
  }
1756
2128
  reporters = !reporters || !reporters.length ? ["default"] : [...reporters];
@@ -1892,11 +2264,11 @@ function getTimeMeasurer() {
1892
2264
  * @returns
1893
2265
  */
1894
2266
  function indent(str, padding, firstLinePadding = "") {
1895
- let pad = firstLinePadding;
2267
+ let pad$1 = firstLinePadding;
1896
2268
  const lines = [];
1897
2269
  for (const line of str.split("\n")) {
1898
- lines.push(pad + line);
1899
- pad = padding;
2270
+ lines.push(pad$1 + line);
2271
+ pad$1 = padding;
1900
2272
  }
1901
2273
  return lines.join("\n");
1902
2274
  }
@@ -1983,162 +2355,6 @@ function sizeToNumber(size) {
1983
2355
  return Number.parseFloat(p.digits) * (unitSizes[p.units] || 1);
1984
2356
  }
1985
2357
 
1986
- //#endregion
1987
- //#region src/util/ansi.ts
1988
- function isAnsiString(s) {
1989
- return s.includes("\x1B") || s.includes("›");
1990
- }
1991
- /**
1992
- *
1993
- * @param s - the string to measure - should NOT contains ANSI codes
1994
- * @param tabWidth -
1995
- * @returns
1996
- */
1997
- function width(s, tabWidth = 1) {
1998
- return s.replaceAll("…", ".").replaceAll(" ", " ".repeat(tabWidth)).replaceAll(/\p{M}/gu, "").replaceAll(/\p{L}/gu, ".").replaceAll(/[\u0000-\u001F\u0300-\u036F]/g, "").replaceAll(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, ".").length;
1999
- }
2000
- /**
2001
- * Measure the width of a string containing ANSI control characters.
2002
- * @param s - string to measure with width in characters.
2003
- * @returns the approximate number of screen characters.
2004
- */
2005
- function ansiWidth(s) {
2006
- return width(stripVTControlCharacters(s));
2007
- }
2008
- function fragmentString(str, splitOnRegex, sType) {
2009
- const fragments = [];
2010
- let lastIndex = 0;
2011
- for (const match of str.matchAll(new RegExp(splitOnRegex))) {
2012
- if (match.index > lastIndex) fragments.push({
2013
- type: "text",
2014
- text: str.slice(lastIndex, match.index)
2015
- });
2016
- fragments.push({
2017
- type: sType,
2018
- text: match[0]
2019
- });
2020
- lastIndex = match.index + match[0].length;
2021
- }
2022
- if (lastIndex < str.length) fragments.push({
2023
- type: "text",
2024
- text: str.slice(lastIndex)
2025
- });
2026
- return fragments;
2027
- }
2028
- const ansi = ansiRegex();
2029
- function parseAnsiStr(str) {
2030
- return fragmentString(str, ansi, "ansi");
2031
- }
2032
- /**
2033
- * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
2034
- * @param str - the text to prune - ANSI is supported
2035
- * @param maxWidth - the maximum width of the text
2036
- * @param pad - the string to use for padding, default is '…'
2037
- * @returns the pruned text
2038
- */
2039
- function pruneAnsiTextEnd(str, maxWidth, pad = "…") {
2040
- if (!maxWidth || maxWidth <= 0) return str;
2041
- if (str.length <= maxWidth) return str;
2042
- if (ansiWidth(str) <= maxWidth) return str;
2043
- const padWidth = ansiWidth(pad);
2044
- const fragments = parseAnsiStr(str);
2045
- let remaining = maxWidth - padWidth;
2046
- for (const frag of fragments) {
2047
- if (frag.type !== "text") continue;
2048
- if (remaining <= 0) {
2049
- frag.text = "";
2050
- continue;
2051
- }
2052
- const pruned = pruneTextEnd(frag.text, remaining, pad);
2053
- if (pruned !== frag.text) {
2054
- frag.text = pruned;
2055
- remaining = 0;
2056
- continue;
2057
- }
2058
- remaining -= width(frag.text);
2059
- }
2060
- return fragments.map((frag) => frag.text).join("");
2061
- }
2062
- /**
2063
- * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
2064
- * @param str - the text to prune - ANSI is supported
2065
- * @param maxWidth - the maximum width of the text
2066
- * @param pad - the string to use for padding, default is '…'
2067
- * @returns the pruned text
2068
- */
2069
- function pruneAnsiTextStart(str, maxWidth, pad = "…") {
2070
- if (!maxWidth || maxWidth <= 0) return str;
2071
- if (str.length <= maxWidth) return str;
2072
- if (ansiWidth(str) <= maxWidth) return str;
2073
- const padWidth = ansiWidth(pad);
2074
- const fragments = parseAnsiStr(str);
2075
- let remaining = maxWidth - padWidth;
2076
- for (const frag of fragments.reverse()) {
2077
- if (frag.type !== "text") continue;
2078
- if (remaining <= 0) {
2079
- frag.text = "";
2080
- continue;
2081
- }
2082
- const pruned = pruneTextStart(frag.text, remaining, pad);
2083
- if (pruned !== frag.text) {
2084
- frag.text = pruned;
2085
- remaining = 0;
2086
- continue;
2087
- }
2088
- remaining -= width(frag.text);
2089
- }
2090
- return fragments.reverse().map((frag) => frag.text).join("");
2091
- }
2092
- /**
2093
- * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
2094
- * @param str - the text to prune - ANSI is not supported
2095
- * @param maxWidth - the maximum width of the text
2096
- * @param pad - the string to use for padding, default is '…'
2097
- * @returns the pruned text
2098
- */
2099
- function pruneTextEnd(str, maxWidth, pad = "…") {
2100
- if (!maxWidth || maxWidth <= 0) return str;
2101
- if (str.length <= maxWidth) return str;
2102
- if (isAnsiString(str)) return pruneAnsiTextEnd(str, maxWidth, pad);
2103
- const maxWidthWithPad = maxWidth - width(pad);
2104
- const letters = [...str];
2105
- let len = 0;
2106
- for (let i = 0; i < letters.length; i++) {
2107
- const c = letters[i];
2108
- len += width(c);
2109
- if (len > maxWidthWithPad) {
2110
- let j = i + 1;
2111
- while (j < letters.length && width(letters[j]) === 0) ++j;
2112
- return j === letters.length ? str : letters.slice(0, i).join("") + pad;
2113
- }
2114
- }
2115
- return str;
2116
- }
2117
- /**
2118
- * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
2119
- * @param str - the text to prune - ANSI is not supported
2120
- * @param maxWidth - the maximum width of the text
2121
- * @param pad - the string to use for padding, default is '…'
2122
- * @returns the pruned text
2123
- */
2124
- function pruneTextStart(str, maxWidth, pad = "…") {
2125
- if (!maxWidth || maxWidth <= 0) return str;
2126
- if (str.length <= maxWidth) return str;
2127
- const maxWidthWithPad = maxWidth - width(pad);
2128
- const letters = [...str];
2129
- let len = 0;
2130
- for (let i = letters.length - 1; i >= 1; i--) {
2131
- const c = letters[i];
2132
- len += width(c);
2133
- if (len > maxWidthWithPad) {
2134
- i += 1;
2135
- while (i < letters.length && width(letters[i]) === 0) ++i;
2136
- return pad + letters.slice(i).join("");
2137
- }
2138
- }
2139
- return str;
2140
- }
2141
-
2142
2358
  //#endregion
2143
2359
  //#region src/util/wrap.ts
2144
2360
  const wrapSep = /\s+|(?<=,)|\.(?=\w)/g;
@@ -2199,6 +2415,206 @@ function writeStream(stream, data) {
2199
2415
  });
2200
2416
  }
2201
2417
 
2418
+ //#endregion
2419
+ //#region src/util/extractContext.ts
2420
+ function prefCharIndex(text, offset, count = 1) {
2421
+ if (offset - count < 0) return 0;
2422
+ for (; count > 0 && offset > 0; count--) {
2423
+ let code = text.charCodeAt(--offset) || 0;
2424
+ if (code === 65039) code = text.charCodeAt(--offset) || 0;
2425
+ offset -= (code & 64512) === 56320 ? 1 : 0;
2426
+ }
2427
+ return offset < 0 ? 0 : offset;
2428
+ }
2429
+ function nextCharIndex(text, offset, count = 1) {
2430
+ if (offset + count >= text.length) return text.length;
2431
+ for (; count > 0 && offset < text.length; count--) {
2432
+ const code = text.charCodeAt(offset++) || 0;
2433
+ offset += (code & 64512) === 55296 ? 1 : 0;
2434
+ if (text.charCodeAt(offset) === 65039) offset++;
2435
+ }
2436
+ return offset > text.length ? text.length : offset;
2437
+ }
2438
+ function lineContext(lineText, start, end, contextRange) {
2439
+ let left = prefCharIndex(lineText, start, contextRange);
2440
+ let right = nextCharIndex(lineText, end, contextRange);
2441
+ const isLetter = /^\p{L}$/u;
2442
+ const isMark = /^\p{M}$/u;
2443
+ for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
2444
+ const c = lineText[left - 1];
2445
+ if (isMark.test(c)) {
2446
+ if (!isLetter.test(lineText[left - 2])) break;
2447
+ left--;
2448
+ continue;
2449
+ }
2450
+ if (!isLetter.test(lineText[left - 1])) break;
2451
+ }
2452
+ for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
2453
+ if (!isLetter.test(lineText[right])) break;
2454
+ if (isMark.test(lineText[right + 1])) right++;
2455
+ }
2456
+ left = left < 0 ? 0 : left;
2457
+ const t0 = lineText.slice(left, right);
2458
+ const tLeft = t0.trimStart();
2459
+ left = Math.min(left + t0.length - tLeft.length, start);
2460
+ return {
2461
+ text: tLeft.trimEnd(),
2462
+ offset: left
2463
+ };
2464
+ }
2465
+ function extractContext(tdo, contextRange) {
2466
+ const { line, offset, text } = tdo;
2467
+ const start = offset - line.offset;
2468
+ const context = lineContext(line.text, start, start + text.length, contextRange);
2469
+ context.offset += line.offset;
2470
+ return context;
2471
+ }
2472
+
2473
+ //#endregion
2474
+ //#region src/lint/LinterError.ts
2475
+ var LinterError = class extends Error {
2476
+ constructor(message) {
2477
+ super(message);
2478
+ }
2479
+ toString() {
2480
+ return this.message;
2481
+ }
2482
+ };
2483
+
2484
+ //#endregion
2485
+ //#region src/lint/processFile.ts
2486
+ async function processFile(filename, cache, prefetch, processFileOptions) {
2487
+ if (prefetch?.fileResult) return prefetch.fileResult;
2488
+ const { reporter, cfg, configInfo } = processFileOptions;
2489
+ const getElapsedTimeMs = getTimeMeasurer();
2490
+ const reportIssueOptions = prefetch?.reportIssueOptions;
2491
+ const cachedResult = await cache.getCachedLintResults(filename);
2492
+ if (cachedResult) {
2493
+ reporter.debug(`Filename: ${filename}, using cache`);
2494
+ return {
2495
+ ...cachedResult,
2496
+ elapsedTimeMs: getElapsedTimeMs(),
2497
+ reportIssueOptions: {
2498
+ ...cachedResult.reportIssueOptions,
2499
+ ...reportIssueOptions
2500
+ }
2501
+ };
2502
+ }
2503
+ const result = {
2504
+ fileInfo: { filename },
2505
+ issues: [],
2506
+ processed: false,
2507
+ errors: 0,
2508
+ configErrors: 0,
2509
+ elapsedTimeMs: 0,
2510
+ reportIssueOptions
2511
+ };
2512
+ const fileInfo = prefetch?.fileInfo || await readFileInfo(filename, void 0, true);
2513
+ if (fileInfo.errorCode) {
2514
+ if (fileInfo.errorCode !== "EISDIR" && cfg.options.mustFindFiles) {
2515
+ const err = new LinterError(`File not found: "${filename}"`);
2516
+ reporter.error("Linter:", err);
2517
+ result.errors += 1;
2518
+ }
2519
+ return result;
2520
+ }
2521
+ const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
2522
+ const { text } = fileInfo;
2523
+ result.fileInfo = fileInfo;
2524
+ let spellResult = {};
2525
+ try {
2526
+ const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
2527
+ const numSuggestions = configInfo.config.numSuggestions ?? 5;
2528
+ const r = await spellCheckDocument(doc, clean({
2529
+ generateSuggestions,
2530
+ numSuggestions,
2531
+ validateDirectives,
2532
+ skipValidation
2533
+ }), configInfo.config);
2534
+ spellResult = r;
2535
+ result.processed = r.checked;
2536
+ result.perf = r.perf ? { ...r.perf } : void 0;
2537
+ result.issues = Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
2538
+ } catch (e) {
2539
+ reporter.error(`Failed to process "${filename}"`, toError$1(e));
2540
+ result.errors += 1;
2541
+ }
2542
+ result.elapsedTimeMs = getElapsedTimeMs();
2543
+ const config = spellResult.settingsUsed ?? {};
2544
+ result.reportIssueOptions = mergeReportIssueOptions(spellResult.settingsUsed || configInfo.config, reportIssueOptions);
2545
+ result.configErrors += await reportConfigurationErrors(config, processFileOptions);
2546
+ reportCheckResult(result, doc, spellResult, config, processFileOptions);
2547
+ const dep = calcDependencies(config);
2548
+ await cache.setCachedLintResults(result, dep.files);
2549
+ return result;
2550
+ function mapIssue({ doc: _, ...tdo }) {
2551
+ const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : void 0;
2552
+ return clean({
2553
+ ...tdo,
2554
+ context
2555
+ });
2556
+ }
2557
+ }
2558
+ function reportCheckResult(result, _doc, spellResult, config, processFileOptions) {
2559
+ const { configInfo, reporter, verboseLevel, useColor, cfg, chalk: chalk$1 } = processFileOptions;
2560
+ const elapsed = result.elapsedTimeMs || 0;
2561
+ const dictionaries = config.dictionaries || [];
2562
+ if (verboseLevel > 1) {
2563
+ const dictsUsed = [...dictionaries].sort().map((name$1) => chalk$1.green(name$1)).join(", ");
2564
+ const msg = unindent`
2565
+ File type: ${config.languageId}, Language: ${config.language}, Issues: ${result.issues.length} ${elapsed.toFixed(2)}ms
2566
+ Config file Used: ${relativeToCwd(spellResult.localConfigFilepath || configInfo.source, cfg.root)}
2567
+ Dictionaries Used:
2568
+ ${wordWrapAnsiText(dictsUsed, 70)}`;
2569
+ reporter.info(indent(msg, " "), MessageTypes.Info);
2570
+ }
2571
+ if (cfg.options.debug) {
2572
+ const { enabled, language, languageId, dictionaries: dictionaries$1 } = config;
2573
+ const msg = unindent`\
2574
+ Debug Config: ${formatWithOptions({
2575
+ depth: 2,
2576
+ colors: useColor
2577
+ }, {
2578
+ languageId,
2579
+ enabled,
2580
+ language,
2581
+ dictionaries: dictionaries$1
2582
+ })}`;
2583
+ reporter.debug(msg);
2584
+ }
2585
+ }
2586
+ function calcDependencies(config) {
2587
+ const { configFiles, dictionaryFiles } = extractDependencies(config);
2588
+ return { files: [...configFiles, ...dictionaryFiles] };
2589
+ }
2590
+ async function reportConfigurationErrors(config, processFileOptions) {
2591
+ const { reporter, configErrors } = processFileOptions;
2592
+ const errors = extractImportErrors(config);
2593
+ let count = 0;
2594
+ errors.forEach((ref) => {
2595
+ const key = ref.error.toString();
2596
+ if (configErrors.has(key)) return;
2597
+ configErrors.add(key);
2598
+ count += 1;
2599
+ reporter.error("Configuration", ref.error);
2600
+ });
2601
+ (await getDictionary(config)).dictionaries.forEach((dict) => {
2602
+ const dictErrors = dict.getErrors?.() || [];
2603
+ const msg = `Dictionary Error with (${dict.name})`;
2604
+ dictErrors.forEach((error) => {
2605
+ const key = msg + error.toString();
2606
+ if (configErrors.has(key)) return;
2607
+ configErrors.add(key);
2608
+ count += 1;
2609
+ reporter.error(msg, error);
2610
+ });
2611
+ });
2612
+ return count;
2613
+ }
2614
+ function countConfigErrors(configInfo, processFileOptions) {
2615
+ return reportConfigurationErrors(configInfo.config, processFileOptions);
2616
+ }
2617
+
2202
2618
  //#endregion
2203
2619
  //#region src/lint/lint.ts
2204
2620
  const version = npmPackage.version;
@@ -2211,7 +2627,7 @@ async function runLint(cfg) {
2211
2627
  const useColor = cfg.options.color ?? true;
2212
2628
  const timer = getTimeMeasurer();
2213
2629
  const logDictRequests = truthy(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOGGING"));
2214
- if (logDictRequests) _debug.cacheDictionaryEnableLogging(true);
2630
+ if (logDictRequests) dictionaryCacheEnableLogging(true);
2215
2631
  const lintResult = await run();
2216
2632
  if (logDictRequests) await writeDictionaryLog();
2217
2633
  await reporter.result(lintResult);
@@ -2260,104 +2676,6 @@ async function runLint(cfg) {
2260
2676
  result: fetch$1().catch((e) => toApplicationError(e))
2261
2677
  };
2262
2678
  }
2263
- async function processFile(filename, configInfo, cache, prefetch$1) {
2264
- if (prefetch$1?.fileResult) return prefetch$1.fileResult;
2265
- const getElapsedTimeMs = getTimeMeasurer();
2266
- const reportIssueOptions = prefetch$1?.reportIssueOptions;
2267
- const cachedResult = await cache.getCachedLintResults(filename);
2268
- if (cachedResult) {
2269
- reporter.debug(`Filename: ${filename}, using cache`);
2270
- return {
2271
- ...cachedResult,
2272
- elapsedTimeMs: getElapsedTimeMs(),
2273
- reportIssueOptions: {
2274
- ...cachedResult.reportIssueOptions,
2275
- ...reportIssueOptions
2276
- }
2277
- };
2278
- }
2279
- const result = {
2280
- fileInfo: { filename },
2281
- issues: [],
2282
- processed: false,
2283
- errors: 0,
2284
- configErrors: 0,
2285
- elapsedTimeMs: 0,
2286
- reportIssueOptions
2287
- };
2288
- const fileInfo = prefetch$1?.fileInfo || await readFileInfo(filename, void 0, true);
2289
- if (fileInfo.errorCode) {
2290
- if (fileInfo.errorCode !== "EISDIR" && cfg.options.mustFindFiles) {
2291
- const err = new LinterError(`File not found: "${filename}"`);
2292
- reporter.error("Linter:", err);
2293
- result.errors += 1;
2294
- }
2295
- return result;
2296
- }
2297
- const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
2298
- const { text } = fileInfo;
2299
- result.fileInfo = fileInfo;
2300
- let spellResult = {};
2301
- try {
2302
- const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
2303
- const numSuggestions = configInfo.config.numSuggestions ?? 5;
2304
- const r = await spellCheckDocument(doc, clean({
2305
- generateSuggestions,
2306
- numSuggestions,
2307
- validateDirectives,
2308
- skipValidation
2309
- }), configInfo.config);
2310
- spellResult = r;
2311
- result.processed = r.checked;
2312
- result.perf = r.perf ? { ...r.perf } : void 0;
2313
- result.issues = Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
2314
- } catch (e) {
2315
- reporter.error(`Failed to process "${filename}"`, toError(e));
2316
- result.errors += 1;
2317
- }
2318
- result.elapsedTimeMs = getElapsedTimeMs();
2319
- const config = spellResult.settingsUsed ?? {};
2320
- result.reportIssueOptions = mergeReportIssueOptions(spellResult.settingsUsed || configInfo.config, reportIssueOptions);
2321
- result.configErrors += await reportConfigurationErrors(config);
2322
- reportCheckResult(result, doc, spellResult, configInfo, config);
2323
- const dep = calcDependencies(config);
2324
- await cache.setCachedLintResults(result, dep.files);
2325
- return result;
2326
- }
2327
- function reportCheckResult(result, _doc, spellResult, configInfo, config) {
2328
- const elapsed$1 = result.elapsedTimeMs || 0;
2329
- const dictionaries = config.dictionaries || [];
2330
- if (verboseLevel > 1) {
2331
- const dictsUsed = [...dictionaries].sort().map((name$1) => chalk.green(name$1)).join(", ");
2332
- const msg = unindent`
2333
- File type: ${config.languageId}, Language: ${config.language}, Issues: ${result.issues.length} ${elapsed$1.toFixed(2)}ms
2334
- Config file Used: ${relativeToCwd(spellResult.localConfigFilepath || configInfo.source, cfg.root)}
2335
- Dictionaries Used:
2336
- ${wordWrapAnsiText(dictsUsed, 70)}`;
2337
- reporter.info(indent(msg, " "), MessageTypes.Info);
2338
- }
2339
- if (cfg.options.debug) {
2340
- const { enabled, language, languageId, dictionaries: dictionaries$1 } = config;
2341
- const msg = unindent`\
2342
- Debug Config: ${formatWithOptions({
2343
- depth: 2,
2344
- colors: useColor
2345
- }, {
2346
- languageId,
2347
- enabled,
2348
- language,
2349
- dictionaries: dictionaries$1
2350
- })}`;
2351
- reporter.debug(msg);
2352
- }
2353
- }
2354
- function mapIssue({ doc: _, ...tdo }) {
2355
- const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : void 0;
2356
- return clean({
2357
- ...tdo,
2358
- context
2359
- });
2360
- }
2361
2679
  async function processFiles(files, configInfo, cacheSettings) {
2362
2680
  const fileCount = Array.isArray(files) ? files.length : void 0;
2363
2681
  const status = runResult();
@@ -2398,7 +2716,7 @@ async function runLint(cfg) {
2398
2716
  return {
2399
2717
  filename,
2400
2718
  fileNum: index,
2401
- result: await processFile(filename, configInfo, cache, fetchResult)
2719
+ result: await processFile(filename, cache, fetchResult, getProcessFileOptions(configInfo))
2402
2720
  };
2403
2721
  }
2404
2722
  async function* loadAndProcessFiles() {
@@ -2426,39 +2744,10 @@ async function runLint(cfg) {
2426
2744
  await cache.reconcile();
2427
2745
  return status;
2428
2746
  }
2429
- function calcDependencies(config) {
2430
- const { configFiles, dictionaryFiles } = extractDependencies(config);
2431
- return { files: [...configFiles, ...dictionaryFiles] };
2432
- }
2433
- async function reportConfigurationErrors(config) {
2434
- const errors = extractImportErrors(config);
2435
- let count = 0;
2436
- errors.forEach((ref) => {
2437
- const key = ref.error.toString();
2438
- if (configErrors.has(key)) return;
2439
- configErrors.add(key);
2440
- count += 1;
2441
- reporter.error("Configuration", ref.error);
2442
- });
2443
- (await getDictionary(config)).dictionaries.forEach((dict) => {
2444
- const dictErrors = dict.getErrors?.() || [];
2445
- const msg = `Dictionary Error with (${dict.name})`;
2446
- dictErrors.forEach((error) => {
2447
- const key = msg + error.toString();
2448
- if (configErrors.has(key)) return;
2449
- configErrors.add(key);
2450
- count += 1;
2451
- reporter.error(msg, error);
2452
- });
2453
- });
2454
- return count;
2455
- }
2456
- function countConfigErrors(configInfo) {
2457
- return reportConfigurationErrors(configInfo.config);
2458
- }
2459
2747
  async function run() {
2460
2748
  if (cfg.options.root) setEnvironmentVariable(ENV_CSPELL_GLOB_ROOT, cfg.root);
2461
2749
  const configInfo = await readConfig(cfg.configFile, cfg.root, cfg.options.stopConfigSearchAt);
2750
+ const processFileOptions = getProcessFileOptions(configInfo);
2462
2751
  if (cfg.options.defaultConfiguration !== void 0) configInfo.config.loadDefaultConfiguration = cfg.options.defaultConfiguration;
2463
2752
  configInfo.config = mergeSettings(configInfo.config, cfg.cspellSettingsFromCliOptions);
2464
2753
  const reporterConfig = clean({
@@ -2478,8 +2767,8 @@ async function runLint(cfg) {
2478
2767
  if (!fileGlobs.length && !hasFileLists && !cfg.files?.length) return runResult();
2479
2768
  header(fileGlobs, excludeGlobs);
2480
2769
  checkGlobs(fileGlobs, reporter);
2481
- if (verboseLevel > 1) reporter.info(`Config Files Found:\n ${relativeToCwd(configInfo.source)}\n`, MessageTypes.Info);
2482
- const configErrors$1 = await countConfigErrors(configInfo);
2770
+ if (verboseLevel > 1) reporter.info(`Config Files Found:\n ${relativeToCwd(configInfo.source)}\n`, MessageTypes$1.Info);
2771
+ const configErrors$1 = await countConfigErrors(configInfo, processFileOptions);
2483
2772
  if (configErrors$1 && cfg.options.exitCode !== false && !cfg.options.continueOnError) return runResult({ errors: configErrors$1 });
2484
2773
  const { root } = cfg;
2485
2774
  try {
@@ -2509,7 +2798,18 @@ async function runLint(cfg) {
2509
2798
  files: ${formattedFiles}
2510
2799
  wordsOnly: ${yesNo(!!cfg.options.wordsOnly)}
2511
2800
  unique: ${yesNo(!!cfg.options.unique)}
2512
- `, MessageTypes.Info);
2801
+ `, MessageTypes$1.Info);
2802
+ }
2803
+ function getProcessFileOptions(configInfo) {
2804
+ return {
2805
+ reporter,
2806
+ chalk,
2807
+ configInfo,
2808
+ cfg,
2809
+ verboseLevel,
2810
+ useColor,
2811
+ configErrors
2812
+ };
2513
2813
  }
2514
2814
  }
2515
2815
  function checkGlobs(globs, reporter) {
@@ -2541,7 +2841,7 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2541
2841
  const globsToExclude = globsToExcludeRaw.filter((g) => !globPattern(g).startsWith("!"));
2542
2842
  if (globsToExclude.length !== globsToExcludeRaw.length) {
2543
2843
  const msg = `Negative glob exclusions are not supported: ${globsToExcludeRaw.map((g) => globPattern(g)).filter((g) => g.startsWith("!")).join(", ")}`;
2544
- reporter.info(msg, MessageTypes.Warning);
2844
+ reporter.info(msg, MessageTypes$1.Warning);
2545
2845
  }
2546
2846
  const globMatcher = buildGlobMatcher(globsToExclude, root, true);
2547
2847
  const globOptions = {
@@ -2567,13 +2867,13 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2567
2867
  const r = globMatcherExclude.matchEx(absFilename);
2568
2868
  if (r.matched) {
2569
2869
  const { glob: glob$2, source } = extractGlobSource(r.pattern);
2570
- if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Excluded File: ${path$1.relative(root, absFilename)}; Excluded by ${glob$2} from ${source}`, MessageTypes.Info);
2870
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Excluded File: ${path$1.relative(root, absFilename)}; Excluded by ${glob$2} from ${source}`, MessageTypes$1.Info);
2571
2871
  }
2572
2872
  return r.matched;
2573
2873
  }
2574
2874
  function filterOutExcludedFilesFn(globMatcherExclude) {
2575
2875
  const excludeInfo = globMatcherExclude.patterns.map(extractGlobSource).map(({ glob: glob$2, source }) => `Glob: ${glob$2} from ${source}`).filter(uniqueFn());
2576
- if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Exclusion Globs: \n ${excludeInfo.join("\n ")}\n`, MessageTypes.Info);
2876
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Exclusion Globs: \n ${excludeInfo.join("\n ")}\n`, MessageTypes$1.Info);
2577
2877
  return (filename) => !isExcluded(filename, globMatcherExclude);
2578
2878
  }
2579
2879
  return _determineFilesToCheck();
@@ -2653,20 +2953,12 @@ async function* concatAsyncIterables(...iterables) {
2653
2953
  }
2654
2954
  async function writeDictionaryLog() {
2655
2955
  const fields = (getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOG_FIELDS") || "time, word, value").split(",").map((f) => f.trim());
2656
- const data = fields.join(", ") + "\n" + _debug.cacheDictionaryGetLog().filter((d) => d.method === "has").map((d) => fields.map((f) => f in d ? `${d[f]}` : "").join(", ")).join("\n") + "\n";
2956
+ const data = fields.join(", ") + "\n" + dictionaryCacheGetLog().filter((d) => d.method === "has").map((d) => fields.map((f) => f in d ? `${d[f]}` : "").join(", ")).join("\n") + "\n";
2657
2957
  await writeFileOrStream(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOG_FILE") || "cspell-dictionary-log.csv", data);
2658
2958
  }
2659
2959
  function globPattern(g) {
2660
2960
  return typeof g === "string" ? g : g.glob;
2661
2961
  }
2662
- var LinterError = class extends Error {
2663
- constructor(message) {
2664
- super(message);
2665
- }
2666
- toString() {
2667
- return this.message;
2668
- }
2669
- };
2670
2962
  function calcVerboseLevel(options) {
2671
2963
  return options.verboseLevel ?? (options.verbose ? 1 : 0);
2672
2964
  }
@@ -3010,5 +3302,5 @@ function parseApplicationFeatureFlags(flags) {
3010
3302
  }
3011
3303
 
3012
3304
  //#endregion
3013
- export { console as C, CheckFailed as S, unindent as _, parseApplicationFeatureFlags as a, getReporter as b, listDictionaries as c, ansiWidth as d, pruneAnsiTextEnd as f, validateUnitSize as g, width as h, lint as i, ReportChoicesAll as l, pruneTextEnd as m, checkText as n, suggestions as o, pruneAnsiTextStart as p, createInit as r, trace as s, IncludeExcludeFlag as t, cvtLinterCliCommandOptionsToLinterCliOptions as u, DEFAULT_CACHE_LOCATION as v, ApplicationError as x, npmPackage as y };
3014
- //# sourceMappingURL=application-DCyfKhGm.js.map
3305
+ export { CheckFailed as C, ApplicationError as S, padLeft as _, parseApplicationFeatureFlags as a, pruneAnsiTextStart as b, listDictionaries as c, validateUnitSize as d, unindent as f, tableToLines as g, getReporter as h, lint as i, ReportChoicesAll as l, npmPackage as m, checkText as n, suggestions as o, DEFAULT_CACHE_LOCATION as p, createInit as r, trace as s, IncludeExcludeFlag as t, cvtLinterCliCommandOptionsToLinterCliOptions as u, padWidth as v, console as w, width as x, pruneAnsiTextEnd as y };
3306
+ //# sourceMappingURL=application-BJHh60xM.js.map