cspell 9.4.0 → 9.6.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.
@@ -1,21 +1,23 @@
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, 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";
13
14
  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";
17
+ import { enablePerformanceMeasurements, measurePerf } from "@cspell/cspell-performance-monitor";
16
18
  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";
19
+ import { IssueType, MessageTypes as MessageTypes$1, unknownWordsChoices } from "@cspell/cspell-types";
20
+ import { dictionaryCacheEnableLogging, dictionaryCacheGetLog } from "cspell-dictionary";
19
21
  import { GitIgnore, findRepoRoot } from "cspell-gitignore";
20
22
  import { GlobMatcher, fileOrGlobToGlob, workaroundPicomatchBug } from "cspell-glob";
21
23
  import crypto from "node:crypto";
@@ -25,7 +27,6 @@ import { glob } from "tinyglobby";
25
27
  import * as readline from "node:readline";
26
28
  import { parse, stringify } from "flatted";
27
29
  import { dynamicImport } from "@cspell/dynamic-import";
28
- import ansiRegex from "ansi-regex";
29
30
 
30
31
  //#region src/console.ts
31
32
  var ImplChannel = class {
@@ -89,7 +90,7 @@ var IOError = class extends ApplicationError {
89
90
  return this.cause.code === "ENOENT";
90
91
  }
91
92
  };
92
- function toError(e) {
93
+ function toError$1(e) {
93
94
  if (isError(e)) return e;
94
95
  if (isErrorLike(e)) {
95
96
  const ex = new Error(e.message, { cause: e });
@@ -109,10 +110,349 @@ function isErrorLike(e) {
109
110
  }
110
111
  function toApplicationError(e, message) {
111
112
  if (e instanceof ApplicationError && !message) return e;
112
- const err = toError(e);
113
+ const err = toError$1(e);
113
114
  return new ApplicationError(message ?? err.message, void 0, err);
114
115
  }
115
116
 
117
+ //#endregion
118
+ //#region src/util/perfMeasurements.ts
119
+ function getPerfMeasurements() {
120
+ const measurements = performance.getEntriesByType("measure");
121
+ const root = {
122
+ depth: -1,
123
+ totalTimeMs: 0,
124
+ nestedTimeMs: 0,
125
+ children: /* @__PURE__ */ new Map()
126
+ };
127
+ if (!measurements.length) return [];
128
+ const stack = [];
129
+ let depth = 0;
130
+ for (let i = 0; i < measurements.length; i++) {
131
+ const m = measurements[i];
132
+ rollUpStack(m.startTime);
133
+ const s = {
134
+ m,
135
+ p: addToParent(depth === 0 ? root : stack[depth - 1].p, m)
136
+ };
137
+ stack[depth++] = s;
138
+ }
139
+ sortChildren(root);
140
+ return [...root.children.values()].flatMap((r) => [...flattenChildren(r)]);
141
+ function contains(m, t) {
142
+ const stop = m.startTime + m.duration;
143
+ return t >= m.startTime && t < stop;
144
+ }
145
+ function rollUpStack(t) {
146
+ for (; depth > 0 && !contains(stack[depth - 1].m, t); --depth);
147
+ }
148
+ function addToParent(p, m) {
149
+ p.children ??= /* @__PURE__ */ new Map();
150
+ p.nestedTimeMs += m.duration;
151
+ return updateChild(p.children, m, p.depth + 1);
152
+ }
153
+ function updateChild(children, m, depth$1) {
154
+ const p = children.get(m.name);
155
+ if (p) {
156
+ p.totalTimeMs += m.duration;
157
+ p.count += 1;
158
+ p.minTimeMs = Math.min(p.minTimeMs, m.duration);
159
+ p.maxTimeMs = Math.max(p.maxTimeMs, m.duration);
160
+ return p;
161
+ }
162
+ const n = {
163
+ name: m.name,
164
+ depth: depth$1,
165
+ totalTimeMs: m.duration,
166
+ nestedTimeMs: 0,
167
+ count: 1,
168
+ minTimeMs: m.duration,
169
+ maxTimeMs: m.duration
170
+ };
171
+ children.set(m.name, n);
172
+ return n;
173
+ }
174
+ function* flattenChildren(m) {
175
+ yield m;
176
+ if (!m.children) return;
177
+ for (const child of m.children.values()) yield* flattenChildren(child);
178
+ }
179
+ function sortChildren(m) {
180
+ if (!m.children) return;
181
+ m.children = new Map([...m.children.entries()].sort((a, b) => b[1].totalTimeMs - a[1].totalTimeMs));
182
+ m.children.forEach(sortChildren);
183
+ }
184
+ }
185
+
186
+ //#endregion
187
+ //#region src/util/ansi.ts
188
+ function isAnsiString(s) {
189
+ return s.includes("\x1B") || s.includes("›");
190
+ }
191
+ /**
192
+ *
193
+ * @param s - the string to measure - should NOT contains ANSI codes
194
+ * @param tabWidth -
195
+ * @returns
196
+ */
197
+ function width(s, tabWidth = 1) {
198
+ 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;
199
+ }
200
+ /**
201
+ * Measure the width of a string containing ANSI control characters.
202
+ * @param s - string to measure with width in characters.
203
+ * @returns the approximate number of screen characters.
204
+ */
205
+ function ansiWidth(s) {
206
+ return width(stripVTControlCharacters(s));
207
+ }
208
+ function fragmentString(str, splitOnRegex, sType) {
209
+ const fragments = [];
210
+ let lastIndex = 0;
211
+ for (const match of str.matchAll(new RegExp(splitOnRegex))) {
212
+ if (match.index > lastIndex) fragments.push({
213
+ type: "text",
214
+ text: str.slice(lastIndex, match.index)
215
+ });
216
+ fragments.push({
217
+ type: sType,
218
+ text: match[0]
219
+ });
220
+ lastIndex = match.index + match[0].length;
221
+ }
222
+ if (lastIndex < str.length) fragments.push({
223
+ type: "text",
224
+ text: str.slice(lastIndex)
225
+ });
226
+ return fragments;
227
+ }
228
+ const ansi = ansiRegex();
229
+ function parseAnsiStr(str) {
230
+ return fragmentString(str, ansi, "ansi");
231
+ }
232
+ /**
233
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
234
+ * @param str - the text to prune - ANSI is supported
235
+ * @param maxWidth - the maximum width of the text
236
+ * @param pad - the string to use for padding, default is '…'
237
+ * @returns the pruned text
238
+ */
239
+ function pruneAnsiTextEnd(str, maxWidth, pad$1 = "…") {
240
+ if (!maxWidth || maxWidth <= 0) return str;
241
+ if (str.length <= maxWidth) return str;
242
+ if (ansiWidth(str) <= maxWidth) return str;
243
+ const padWidth$1 = ansiWidth(pad$1);
244
+ const fragments = parseAnsiStr(str);
245
+ let remaining = maxWidth - padWidth$1;
246
+ for (const frag of fragments) {
247
+ if (frag.type !== "text") continue;
248
+ if (remaining <= 0) {
249
+ frag.text = "";
250
+ continue;
251
+ }
252
+ const pruned = pruneTextEnd(frag.text, remaining, pad$1);
253
+ if (pruned !== frag.text) {
254
+ frag.text = pruned;
255
+ remaining = 0;
256
+ continue;
257
+ }
258
+ remaining -= width(frag.text);
259
+ }
260
+ return fragments.map((frag) => frag.text).join("");
261
+ }
262
+ /**
263
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
264
+ * @param str - the text to prune - ANSI is supported
265
+ * @param maxWidth - the maximum width of the text
266
+ * @param pad - the string to use for padding, default is '…'
267
+ * @returns the pruned text
268
+ */
269
+ function pruneAnsiTextStart(str, maxWidth, pad$1 = "…") {
270
+ if (!maxWidth || maxWidth <= 0) return str;
271
+ if (str.length <= maxWidth) return str;
272
+ if (ansiWidth(str) <= maxWidth) return str;
273
+ const padWidth$1 = ansiWidth(pad$1);
274
+ const fragments = parseAnsiStr(str);
275
+ let remaining = maxWidth - padWidth$1;
276
+ for (const frag of fragments.reverse()) {
277
+ if (frag.type !== "text") continue;
278
+ if (remaining <= 0) {
279
+ frag.text = "";
280
+ continue;
281
+ }
282
+ const pruned = pruneTextStart(frag.text, remaining, pad$1);
283
+ if (pruned !== frag.text) {
284
+ frag.text = pruned;
285
+ remaining = 0;
286
+ continue;
287
+ }
288
+ remaining -= width(frag.text);
289
+ }
290
+ return fragments.reverse().map((frag) => frag.text).join("");
291
+ }
292
+ /**
293
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
294
+ * @param str - the text to prune - ANSI is not supported
295
+ * @param maxWidth - the maximum width of the text
296
+ * @param pad - the string to use for padding, default is '…'
297
+ * @returns the pruned text
298
+ */
299
+ function pruneTextEnd(str, maxWidth, pad$1 = "…") {
300
+ if (!maxWidth || maxWidth <= 0) return str;
301
+ if (str.length <= maxWidth) return str;
302
+ if (isAnsiString(str)) return pruneAnsiTextEnd(str, maxWidth, pad$1);
303
+ const maxWidthWithPad = maxWidth - width(pad$1);
304
+ const letters = [...str];
305
+ let len = 0;
306
+ for (let i = 0; i < letters.length; i++) {
307
+ const c = letters[i];
308
+ len += width(c);
309
+ if (len > maxWidthWithPad) {
310
+ let j = i + 1;
311
+ while (j < letters.length && width(letters[j]) === 0) ++j;
312
+ return j === letters.length ? str : letters.slice(0, i).join("") + pad$1;
313
+ }
314
+ }
315
+ return str;
316
+ }
317
+ /**
318
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
319
+ * @param str - the text to prune - ANSI is not supported
320
+ * @param maxWidth - the maximum width of the text
321
+ * @param pad - the string to use for padding, default is '…'
322
+ * @returns the pruned text
323
+ */
324
+ function pruneTextStart(str, maxWidth, pad$1 = "…") {
325
+ if (!maxWidth || maxWidth <= 0) return str;
326
+ if (str.length <= maxWidth) return str;
327
+ const maxWidthWithPad = maxWidth - width(pad$1);
328
+ const letters = [...str];
329
+ let len = 0;
330
+ for (let i = letters.length - 1; i >= 1; i--) {
331
+ const c = letters[i];
332
+ len += width(c);
333
+ if (len > maxWidthWithPad) {
334
+ i += 1;
335
+ while (i < letters.length && width(letters[i]) === 0) ++i;
336
+ return pad$1 + letters.slice(i).join("");
337
+ }
338
+ }
339
+ return str;
340
+ }
341
+
342
+ //#endregion
343
+ //#region src/util/pad.ts
344
+ function pad(s, w) {
345
+ const p = padWidth(s, w);
346
+ if (!p) return s;
347
+ return s.padEnd(p + s.length);
348
+ }
349
+ function padWidth(s, target) {
350
+ const sWidth = ansiWidth(s);
351
+ return Math.max(target - sWidth, 0);
352
+ }
353
+ function padLeft(s, w) {
354
+ const p = padWidth(s, w);
355
+ if (!p) return s;
356
+ return s.padStart(p + s.length);
357
+ }
358
+
359
+ //#endregion
360
+ //#region src/util/table.ts
361
+ function tableToLines(table, deliminator) {
362
+ const del = deliminator || table.deliminator || " | ";
363
+ const columnWidths = [];
364
+ const columnAlignments = table.columnAlignments || [];
365
+ const maxColumnWidthsMap = table.maxColumnWidths || {};
366
+ const tableIndent = table.indent ? typeof table.indent === "number" ? " ".repeat(table.indent) : table.indent : "";
367
+ const { header, rows } = table;
368
+ const simpleHeader = header.map((col) => Array.isArray(col) ? col[1] : col);
369
+ const columnFieldNames = header.map((col) => Array.isArray(col) ? col[0] : col);
370
+ const maxColumnWidths = columnFieldNames.map((field, idx) => maxColumnWidthsMap[field] ?? maxColumnWidthsMap[idx]);
371
+ function getCell(row, col) {
372
+ return getCellFromRow(rows[row], col);
373
+ }
374
+ function getCellFromRow(row, col) {
375
+ if (!row) return void 0;
376
+ if (Array.isArray(row)) return row[col];
377
+ return row[columnFieldNames[col]];
378
+ }
379
+ function rowToCells(row) {
380
+ if (Array.isArray(row)) return row;
381
+ return columnFieldNames.map((fieldName) => row[fieldName]);
382
+ }
383
+ function getText(col, maxWidth) {
384
+ return !col ? "" : typeof col === "string" ? pruneTextEnd(col, maxWidth) : col(maxWidth);
385
+ }
386
+ function getRCText(row, col, maxWidth) {
387
+ return getText(getCell(row, col), maxWidth);
388
+ }
389
+ function recordHeaderWidths(header$1) {
390
+ header$1.forEach((col, idx) => {
391
+ columnWidths[idx] = Math.max(ansiWidth(col), columnWidths[idx] || 0);
392
+ });
393
+ }
394
+ function recordColWidths() {
395
+ 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);
396
+ }
397
+ function justifyRow(c, i) {
398
+ return columnAlignments[i] === "R" ? padLeft(c, columnWidths[i]) : pad(c, columnWidths[i]);
399
+ }
400
+ function toHeaderLine(header$1) {
401
+ return tableIndent + decorateRowWith(header$1.map((c, i) => getText(c, columnWidths[i])), justifyRow, headerDecorator).join(del);
402
+ }
403
+ function toLine(row) {
404
+ return tableIndent + decorateRowWith(rowToCells(row).map((c, i) => getText(c, columnWidths[i])), justifyRow).join(del);
405
+ }
406
+ function* process$1() {
407
+ if (table.title) yield table.title;
408
+ yield toHeaderLine(simpleHeader);
409
+ yield* rows.map(toLine);
410
+ }
411
+ function sumColumnWidths() {
412
+ return columnWidths.reduce((sum, width$1) => sum + width$1, 0);
413
+ }
414
+ function adjustColWidths() {
415
+ for (let i = 0; i < columnWidths.length; i++) {
416
+ const mw = maxColumnWidths[i];
417
+ if (!mw) continue;
418
+ columnWidths[i] = Math.min(columnWidths[i], mw);
419
+ }
420
+ if (!table.terminalWidth) return;
421
+ const dWidth = (columnWidths.length - 1) * ansiWidth(del);
422
+ const lineWidth = table.terminalWidth - dWidth;
423
+ if (lineWidth <= columnWidths.length * 2) {
424
+ const fixedWidth = Math.max(Math.min(...columnWidths), 5);
425
+ for (let i = 0; i < columnWidths.length; i++) columnWidths[i] = fixedWidth;
426
+ return;
427
+ }
428
+ if (columnWidths.length === 1) {
429
+ columnWidths[0] = lineWidth;
430
+ return;
431
+ }
432
+ function trimWidestColumn(neededToTrim) {
433
+ let first = 0;
434
+ let second = 0;
435
+ for (let i = 0; i < columnWidths.length; i++) if (columnWidths[i] > columnWidths[first]) {
436
+ second = first;
437
+ first = i;
438
+ } else if (columnWidths[i] > columnWidths[second]) second = i;
439
+ const diff = Math.max(columnWidths[first] - columnWidths[second], 1);
440
+ columnWidths[first] -= Math.min(diff, neededToTrim);
441
+ }
442
+ for (let sum = sumColumnWidths(); sum > lineWidth; sum = sumColumnWidths()) trimWidestColumn(sum - lineWidth);
443
+ }
444
+ recordHeaderWidths(simpleHeader);
445
+ recordColWidths();
446
+ adjustColWidths();
447
+ return [...process$1()];
448
+ }
449
+ function headerDecorator(t) {
450
+ return chalk.bold(chalk.underline(t));
451
+ }
452
+ function decorateRowWith(row, ...decorators) {
453
+ return decorators.reduce((row$1, decorator) => row$1.map(decorator), row);
454
+ }
455
+
116
456
  //#endregion
117
457
  //#region src/util/util.ts
118
458
  const uniqueFn = uniqueFilterFnGenerator;
@@ -222,7 +562,8 @@ function getReporter(options, config) {
222
562
  filesProcessed: 0,
223
563
  filesSkipped: 0,
224
564
  filesCached: 0,
225
- elapsedTimeMs: 0,
565
+ accumulatedTimeMs: 0,
566
+ startTime: performance.now(),
226
567
  perf: Object.create(null)
227
568
  };
228
569
  const noColor = options.color === false;
@@ -279,6 +620,7 @@ function getReporter(options, config) {
279
620
  if (!fileGlobs.length && !result.files) return;
280
621
  const { files, issues: issues$1, cachedFiles, filesWithIssues, errors, skippedFiles } = result;
281
622
  const numFilesWithIssues = filesWithIssues.size;
623
+ const chalk$1 = stderr.chalk;
282
624
  if (stderr.getColorLevel() > 0) {
283
625
  stderr.write("\r");
284
626
  stderr.clearLine(0);
@@ -299,20 +641,88 @@ function getReporter(options, config) {
299
641
  errorCollection.forEach((error) => consoleError(error));
300
642
  }
301
643
  if (options.showPerfSummary) {
644
+ const elapsedTotal = performance.now() - perfStats.startTime;
302
645
  consoleError("-------------------------------------------");
303
646
  consoleError("Performance Summary:");
304
- consoleError(` Files Processed: ${perfStats.filesProcessed.toString().padStart(6)}`);
305
- consoleError(` Files Skipped : ${perfStats.filesSkipped.toString().padStart(6)}`);
306
- consoleError(` Files Cached : ${perfStats.filesCached.toString().padStart(6)}`);
307
- 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`);
647
+ consoleError(` Files Processed : ${perfStats.filesProcessed.toString().padStart(11)}`);
648
+ consoleError(` Files Skipped : ${perfStats.filesSkipped.toString().padStart(11)}`);
649
+ consoleError(` Files Cached : ${perfStats.filesCached.toString().padStart(11)}`);
650
+ consoleError(` Processing Time : ${perfStats.accumulatedTimeMs.toFixed(2).padStart(9)}ms`);
651
+ consoleError(` Total Time : ${elapsedTotal.toFixed(2).padStart(9)}ms`);
652
+ const tableStats = {
653
+ title: chalk$1.bold("Perf Stats:"),
654
+ header: ["Name", "Time (ms)"],
655
+ columnAlignments: ["L", "R"],
656
+ indent: 2,
657
+ rows: Object.entries(perfStats.perf).filter((p) => !!p[1]).map(([key, value]) => [key, value.toFixed(2)])
658
+ };
659
+ consoleError("");
660
+ for (const line of tableToLines(tableStats)) consoleError(line);
661
+ if (options.verboseLevel) verbosePerfReport();
314
662
  }
315
663
  };
664
+ function verbosePerfReport() {
665
+ const perfMeasurements = getPerfMeasurements();
666
+ if (!perfMeasurements.length) return;
667
+ const notable = extractNotableBySelfTimeInGroup(perfMeasurements);
668
+ const chalk$1 = stderr.chalk;
669
+ const maxDepth = Math.max(...perfMeasurements.map((m) => m.depth));
670
+ const depthIndicator = (d) => "⋅".repeat(d) + " ".repeat(maxDepth - d);
671
+ const rows = perfMeasurements.map((m) => {
672
+ const cbd = (text) => colorByDepth(chalk$1, m.depth, text);
673
+ const cNotable = (text) => notable.has(m) ? chalk$1.yellow(text) : text;
674
+ return [
675
+ chalk$1.dim("⋅".repeat(m.depth)) + colorByDepthGrayscale(stderr.chalk, m.depth, m.name),
676
+ cbd(m.totalTimeMs.toFixed(2) + chalk$1.dim(depthIndicator(m.depth))),
677
+ cbd(cNotable((m.totalTimeMs - m.nestedTimeMs).toFixed(2))),
678
+ cbd(m.count.toString()),
679
+ cbd(m.minTimeMs.toFixed(2)),
680
+ cbd(m.maxTimeMs.toFixed(2)),
681
+ cbd((m.totalTimeMs / m.count).toFixed(2))
682
+ ];
683
+ });
684
+ const table = tableToLines({
685
+ title: chalk$1.bold("Detailed Measurements:"),
686
+ header: [
687
+ "Name",
688
+ "Total Time (ms)",
689
+ "Self (ms)",
690
+ "Count",
691
+ "Min (ms)",
692
+ "Max (ms)",
693
+ "Avg (ms)"
694
+ ],
695
+ rows,
696
+ columnAlignments: [
697
+ "L",
698
+ "R",
699
+ "R",
700
+ "R",
701
+ "R",
702
+ "R",
703
+ "R"
704
+ ],
705
+ indent: 2
706
+ });
707
+ consoleError("\n-------------------------------------------\n");
708
+ for (const line of table) consoleError(line);
709
+ }
710
+ function colorByDepth(chalk$1, depth, text) {
711
+ const colors = [
712
+ chalk$1.green,
713
+ chalk$1.cyan,
714
+ chalk$1.blue,
715
+ chalk$1.magenta,
716
+ chalk$1.red
717
+ ];
718
+ const color = colors[depth % colors.length];
719
+ if (depth / colors.length >= 1) return chalk$1.dim(color(text));
720
+ return color(text);
721
+ }
722
+ function colorByDepthGrayscale(chalk$1, depth, text) {
723
+ const grayLevel = Math.max(32, 255 - depth * 20);
724
+ return chalk$1.rgb(grayLevel, grayLevel, grayLevel)(text);
725
+ }
316
726
  function collectPerfStats(p) {
317
727
  if (p.cached) {
318
728
  perfStats.filesCached++;
@@ -320,7 +730,7 @@ function getReporter(options, config) {
320
730
  }
321
731
  perfStats.filesProcessed += p.processed ? 1 : 0;
322
732
  perfStats.filesSkipped += !p.processed ? 1 : 0;
323
- perfStats.elapsedTimeMs += p.elapsedTimeMs || 0;
733
+ perfStats.accumulatedTimeMs += p.elapsedTimeMs || 0;
324
734
  if (!p.perf) return;
325
735
  for (const [key, value] of Object.entries(p.perf)) if (typeof value === "number") perfStats.perf[key] = (perfStats.perf[key] || 0) + value;
326
736
  }
@@ -338,6 +748,27 @@ function getReporter(options, config) {
338
748
  features: void 0
339
749
  };
340
750
  }
751
+ function extractNotableBySelfTimeInGroup(measurements) {
752
+ const notable = /* @__PURE__ */ new Set();
753
+ if (!measurements.length) return notable;
754
+ let highest;
755
+ let highestSelfTime = 0;
756
+ for (const m of measurements) {
757
+ if (m.depth === 0 || !highest) {
758
+ if (highest) notable.add(highest);
759
+ highest = m;
760
+ highestSelfTime = m.totalTimeMs - m.nestedTimeMs;
761
+ continue;
762
+ }
763
+ const selfTime = m.totalTimeMs - m.nestedTimeMs;
764
+ if (selfTime > highestSelfTime) {
765
+ highest = m;
766
+ highestSelfTime = selfTime;
767
+ }
768
+ }
769
+ if (highest) notable.add(highest);
770
+ return notable;
771
+ }
341
772
  function formatIssue(io, templateStr, issue, maxIssueTextWidth) {
342
773
  function clean$1(t$1) {
343
774
  return t$1.replace(/\s+/, " ");
@@ -447,7 +878,7 @@ async function fileExists(url) {
447
878
  try {
448
879
  return (await promises.stat(url)).isFile();
449
880
  } catch (e) {
450
- if (toError(e).code === "ENOENT") return false;
881
+ if (toError$1(e).code === "ENOENT") return false;
451
882
  throw e;
452
883
  }
453
884
  }
@@ -770,8 +1201,8 @@ const pkgDir = _dirname;
770
1201
  //#endregion
771
1202
  //#region src/pkgInfo.ts
772
1203
  const name = "cspell";
773
- const version$1 = "9.4.0";
774
- const engines = { node: ">=20" };
1204
+ const version$1 = "9.6.1";
1205
+ const engines = { node: ">=20.18" };
775
1206
  const npmPackage = {
776
1207
  name,
777
1208
  version: version$1,
@@ -991,7 +1422,7 @@ function readFileInfo(filename, encoding = UTF8, handleNotFound = false) {
991
1422
  text,
992
1423
  filename
993
1424
  }), (e) => {
994
- const error = toError(e);
1425
+ const error = toError$1(e);
995
1426
  return handleNotFound && error.code === "EISDIR" ? Promise.resolve({
996
1427
  text: "",
997
1428
  filename,
@@ -1181,7 +1612,7 @@ var ImplFileEntryCache = class {
1181
1612
  return {
1182
1613
  key: file,
1183
1614
  notFound: true,
1184
- err: toError$1(error)
1615
+ err: toError(error)
1185
1616
  };
1186
1617
  }
1187
1618
  if (this.useChecksum) return this.#getFileDescriptorUsingChecksum(file);
@@ -1296,7 +1727,7 @@ var ImplFileEntryCache = class {
1296
1727
  function isNodeError(error) {
1297
1728
  return typeof error === "object" && error !== null && "code" in error;
1298
1729
  }
1299
- function toError$1(error) {
1730
+ function toError(error) {
1300
1731
  if (error instanceof Error) return error;
1301
1732
  if (typeof error === "string") return new Error(error);
1302
1733
  return new Error("Unknown error", { cause: error });
@@ -1648,109 +2079,38 @@ async function readConfigHandleError(filename) {
1648
2079
  }
1649
2080
 
1650
2081
  //#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;
2082
+ //#region src/util/reporters.ts
2083
+ function filterFeatureIssues(features, issue, reportOptions) {
2084
+ if (issue.issueType === IssueType.directive) return features?.issueType && reportOptions?.validateDirectives || false;
2085
+ if (features?.unknownWords) return true;
2086
+ if (!reportOptions) return true;
2087
+ if (issue.isFlagged || !reportOptions.unknownWords || reportOptions.unknownWords === unknownWordsChoices.ReportAll) return true;
2088
+ if (issue.hasPreferredSuggestions && reportOptions.unknownWords !== unknownWordsChoices.ReportFlagged) return true;
2089
+ if (issue.hasSimpleSuggestions && reportOptions.unknownWords === unknownWordsChoices.ReportSimple) return true;
2090
+ return false;
1660
2091
  }
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++;
2092
+ function handleIssue(reporter, issue, reportOptions) {
2093
+ if (!reporter.issue) return;
2094
+ if (!filterFeatureIssues(reporter.features, issue, reportOptions)) return;
2095
+ if (!reporter.features?.contextGeneration && !issue.context) {
2096
+ issue = { ...issue };
2097
+ issue.context = issue.line;
1667
2098
  }
1668
- return offset > text.length ? text.length : offset;
2099
+ return reporter.issue(issue, reportOptions);
1669
2100
  }
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
- //#endregion
1706
- //#region src/util/prefetch.ts
1707
- function* prefetchIterable(iterable, size) {
1708
- assert(size >= 0);
1709
- const buffer = [];
1710
- for (const value of iterable) {
1711
- buffer.push(value);
1712
- if (buffer.length >= size - 1) {
1713
- const value$1 = buffer[0];
1714
- buffer.shift();
1715
- yield value$1;
1716
- }
1717
- }
1718
- yield* buffer;
1719
- }
1720
-
1721
- //#endregion
1722
- //#region src/util/reporters.ts
1723
- function filterFeatureIssues(features, issue, reportOptions) {
1724
- if (issue.issueType === IssueType.directive) return features?.issueType && reportOptions?.validateDirectives || false;
1725
- if (features?.unknownWords) return true;
1726
- if (!reportOptions) return true;
1727
- if (issue.isFlagged || !reportOptions.unknownWords || reportOptions.unknownWords === unknownWordsChoices.ReportAll) return true;
1728
- if (issue.hasPreferredSuggestions && reportOptions.unknownWords !== unknownWordsChoices.ReportFlagged) return true;
1729
- if (issue.hasSimpleSuggestions && reportOptions.unknownWords === unknownWordsChoices.ReportSimple) return true;
1730
- return false;
1731
- }
1732
- function handleIssue(reporter, issue, reportOptions) {
1733
- if (!reporter.issue) return;
1734
- if (!filterFeatureIssues(reporter.features, issue, reportOptions)) return;
1735
- if (!reporter.features?.contextGeneration && !issue.context) {
1736
- issue = { ...issue };
1737
- issue.context = issue.line;
1738
- }
1739
- return reporter.issue(issue, reportOptions);
1740
- }
1741
- /**
1742
- * Loads reporter modules configured in cspell config file
1743
- */
1744
- async function loadReporters(reporters, defaultReporter, config) {
1745
- async function loadReporter(reporterSettings) {
1746
- if (reporterSettings === "default") return defaultReporter;
1747
- if (!Array.isArray(reporterSettings)) reporterSettings = [reporterSettings];
1748
- const [moduleName, settings] = reporterSettings;
1749
- try {
1750
- const { getReporter: getReporter$1 } = await dynamicImport(moduleName, [process.cwd(), pkgDir]);
1751
- return getReporter$1(settings, config);
1752
- } catch (e) {
1753
- throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError(e).message}`);
2101
+ /**
2102
+ * Loads reporter modules configured in cspell config file
2103
+ */
2104
+ async function loadReporters(reporters, defaultReporter, config) {
2105
+ async function loadReporter(reporterSettings) {
2106
+ if (reporterSettings === "default") return defaultReporter;
2107
+ if (!Array.isArray(reporterSettings)) reporterSettings = [reporterSettings];
2108
+ const [moduleName, settings] = reporterSettings;
2109
+ try {
2110
+ const { getReporter: getReporter$1 } = await dynamicImport(moduleName, [process.cwd(), pkgDir]);
2111
+ return getReporter$1(settings, config);
2112
+ } catch (e) {
2113
+ throw new ApplicationError(`Failed to load reporter ${moduleName}: ${toError$1(e).message}`);
1754
2114
  }
1755
2115
  }
1756
2116
  reporters = !reporters || !reporters.length ? ["default"] : [...reporters];
@@ -1892,11 +2252,11 @@ function getTimeMeasurer() {
1892
2252
  * @returns
1893
2253
  */
1894
2254
  function indent(str, padding, firstLinePadding = "") {
1895
- let pad = firstLinePadding;
2255
+ let pad$1 = firstLinePadding;
1896
2256
  const lines = [];
1897
2257
  for (const line of str.split("\n")) {
1898
- lines.push(pad + line);
1899
- pad = padding;
2258
+ lines.push(pad$1 + line);
2259
+ pad$1 = padding;
1900
2260
  }
1901
2261
  return lines.join("\n");
1902
2262
  }
@@ -1939,206 +2299,6 @@ function unindentString(str) {
1939
2299
  return lines.map((line) => line.slice(curPad)).join("\n");
1940
2300
  }
1941
2301
 
1942
- //#endregion
1943
- //#region src/util/unitNumbers.ts
1944
- const regexUnitNumber = /^((?:\d+(?:\.\d*)?)|(?:\.\d+))([a-z]*)$/i;
1945
- const unitSizes = {
1946
- "": 1,
1947
- b: 1,
1948
- k: 1024,
1949
- kb: 1024,
1950
- m: 1 << 20,
1951
- mb: 1 << 20,
1952
- g: 1 << 30,
1953
- gb: 1 << 30
1954
- };
1955
- function parseUnitSize(size) {
1956
- const match = size.match(regexUnitNumber);
1957
- const digits = match?.[1] || "";
1958
- const units = (match?.[2] || "").toLowerCase();
1959
- if (!match) return {
1960
- size,
1961
- digits,
1962
- units,
1963
- error: "Invalid size."
1964
- };
1965
- if (!units || units in unitSizes) return {
1966
- size,
1967
- digits,
1968
- units
1969
- };
1970
- return {
1971
- size,
1972
- digits,
1973
- units,
1974
- error: `Unknown units. Valid units are: ${Object.keys(unitSizes).filter(Boolean).join(", ").toUpperCase()}.`
1975
- };
1976
- }
1977
- function validateUnitSize(size) {
1978
- return parseUnitSize(size).error;
1979
- }
1980
- function sizeToNumber(size) {
1981
- const p = parseUnitSize(size);
1982
- if (p.error) return NaN;
1983
- return Number.parseFloat(p.digits) * (unitSizes[p.units] || 1);
1984
- }
1985
-
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
2302
  //#endregion
2143
2303
  //#region src/util/wrap.ts
2144
2304
  const wrapSep = /\s+|(?<=,)|\.(?=\w)/g;
@@ -2200,157 +2360,137 @@ function writeStream(stream, data) {
2200
2360
  }
2201
2361
 
2202
2362
  //#endregion
2203
- //#region src/lint/lint.ts
2204
- const version = npmPackage.version;
2205
- const BATCH_SIZE = 8;
2206
- const { opFilterAsync } = operators;
2207
- async function runLint(cfg) {
2208
- const reporter = new LintReporter(cfg.reporter, cfg.options);
2209
- const configErrors = /* @__PURE__ */ new Set();
2210
- const verboseLevel = calcVerboseLevel(cfg.options);
2211
- const useColor = cfg.options.color ?? true;
2212
- const timer = getTimeMeasurer();
2213
- const logDictRequests = truthy(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOGGING"));
2214
- if (logDictRequests) _debug.cacheDictionaryEnableLogging(true);
2215
- const lintResult = await run();
2216
- if (logDictRequests) await writeDictionaryLog();
2217
- await reporter.result(lintResult);
2218
- const elapsed = timer();
2219
- if (getFeatureFlags().getFlag("timer")) console.log(`Elapsed Time: ${elapsed.toFixed(2)}ms`);
2220
- return lintResult;
2221
- function prefetch(filename, configInfo, cache) {
2222
- if (isBinaryFile$1(filename, cfg.root)) return {
2223
- filename,
2224
- result: Promise.resolve({
2225
- skip: true,
2226
- skipReason: "Binary file."
2227
- })
2228
- };
2229
- const reportIssueOptions = extractReporterIssueOptions(configInfo.config);
2230
- async function fetch$1() {
2231
- const getElapsedTimeMs = getTimeMeasurer();
2232
- const cachedResult = await cache.getCachedLintResults(filename);
2233
- if (cachedResult) {
2234
- reporter.debug(`Filename: ${filename}, using cache`);
2235
- return { fileResult: {
2236
- ...cachedResult,
2237
- elapsedTimeMs: getElapsedTimeMs()
2238
- } };
2239
- }
2240
- const uri = filenameToUri(filename, cfg.root).href;
2241
- const checkResult = await shouldCheckDocument({ uri }, {}, configInfo.config);
2242
- if (!checkResult.shouldCheck) return {
2243
- skip: true,
2244
- skipReason: checkResult.reason || "Ignored by configuration."
2245
- };
2246
- const maxFileSize = processMaxFileSize(cfg.maxFileSize ?? checkResult.settings.maxFileSize);
2247
- if (maxFileSize) {
2248
- if (await getFileSize(filename) > maxFileSize) return {
2249
- skip: true,
2250
- skipReason: `File exceeded max file size of ${maxFileSize.toLocaleString()}`
2251
- };
2252
- }
2253
- return {
2254
- fileInfo: await readFileInfo(filename, void 0, true),
2255
- reportIssueOptions
2256
- };
2363
+ //#region src/util/extractContext.ts
2364
+ function prefCharIndex(text, offset, count = 1) {
2365
+ if (offset - count < 0) return 0;
2366
+ for (; count > 0 && offset > 0; count--) {
2367
+ let code = text.charCodeAt(--offset) || 0;
2368
+ if (code === 65039) code = text.charCodeAt(--offset) || 0;
2369
+ offset -= (code & 64512) === 56320 ? 1 : 0;
2370
+ }
2371
+ return offset < 0 ? 0 : offset;
2372
+ }
2373
+ function nextCharIndex(text, offset, count = 1) {
2374
+ if (offset + count >= text.length) return text.length;
2375
+ for (; count > 0 && offset < text.length; count--) {
2376
+ const code = text.charCodeAt(offset++) || 0;
2377
+ offset += (code & 64512) === 55296 ? 1 : 0;
2378
+ if (text.charCodeAt(offset) === 65039) offset++;
2379
+ }
2380
+ return offset > text.length ? text.length : offset;
2381
+ }
2382
+ function lineContext(lineText, start, end, contextRange) {
2383
+ let left = prefCharIndex(lineText, start, contextRange);
2384
+ let right = nextCharIndex(lineText, end, contextRange);
2385
+ const isLetter = /^\p{L}$/u;
2386
+ const isMark = /^\p{M}$/u;
2387
+ for (let n = contextRange / 2; n > 0 && left > 0; n--, left--) {
2388
+ const c = lineText[left - 1];
2389
+ if (isMark.test(c)) {
2390
+ if (!isLetter.test(lineText[left - 2])) break;
2391
+ left--;
2392
+ continue;
2257
2393
  }
2394
+ if (!isLetter.test(lineText[left - 1])) break;
2395
+ }
2396
+ for (let n = contextRange / 2; n > 0 && right < lineText.length; n--, right++) {
2397
+ if (!isLetter.test(lineText[right])) break;
2398
+ if (isMark.test(lineText[right + 1])) right++;
2399
+ }
2400
+ left = left < 0 ? 0 : left;
2401
+ const t0 = lineText.slice(left, right);
2402
+ const tLeft = t0.trimStart();
2403
+ left = Math.min(left + t0.length - tLeft.length, start);
2404
+ return {
2405
+ text: tLeft.trimEnd(),
2406
+ offset: left
2407
+ };
2408
+ }
2409
+ function extractContext(tdo, contextRange) {
2410
+ const { line, offset, text } = tdo;
2411
+ const start = offset - line.offset;
2412
+ const context = lineContext(line.text, start, start + text.length, contextRange);
2413
+ context.offset += line.offset;
2414
+ return context;
2415
+ }
2416
+
2417
+ //#endregion
2418
+ //#region src/lint/LinterError.ts
2419
+ var LinterError = class extends Error {
2420
+ constructor(message) {
2421
+ super(message);
2422
+ }
2423
+ toString() {
2424
+ return this.message;
2425
+ }
2426
+ };
2427
+
2428
+ //#endregion
2429
+ //#region src/lint/processFile.ts
2430
+ async function processFile(filename, cache, prefetch$1, processFileOptions) {
2431
+ if (prefetch$1?.fileResult) return prefetch$1.fileResult;
2432
+ const { reporter, cfg, configInfo, userSettings } = processFileOptions;
2433
+ const getElapsedTimeMs = getTimeMeasurer();
2434
+ const reportIssueOptions = prefetch$1?.reportIssueOptions;
2435
+ const cachedResult = await cache.getCachedLintResults(filename);
2436
+ if (cachedResult) {
2437
+ reporter.debug(`Filename: ${filename}, using cache`);
2258
2438
  return {
2259
- filename,
2260
- result: fetch$1().catch((e) => toApplicationError(e))
2439
+ ...cachedResult,
2440
+ elapsedTimeMs: getElapsedTimeMs(),
2441
+ reportIssueOptions: {
2442
+ ...cachedResult.reportIssueOptions,
2443
+ ...reportIssueOptions
2444
+ }
2261
2445
  };
2262
2446
  }
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));
2447
+ const result = {
2448
+ fileInfo: { filename },
2449
+ issues: [],
2450
+ processed: false,
2451
+ errors: 0,
2452
+ configErrors: 0,
2453
+ elapsedTimeMs: 0,
2454
+ reportIssueOptions
2455
+ };
2456
+ const fileInfo = prefetch$1?.fileInfo || await readFileInfo(filename, void 0, true);
2457
+ if (fileInfo.errorCode) {
2458
+ if (fileInfo.errorCode !== "EISDIR" && cfg.options.mustFindFiles) {
2459
+ const err = new LinterError(`File not found: "${filename}"`);
2460
+ reporter.error("Linter:", err);
2316
2461
  result.errors += 1;
2317
2462
  }
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
2463
  return result;
2326
2464
  }
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
- }
2465
+ const doc = fileInfoToDocument(fileInfo, cfg.options.languageId, cfg.locale);
2466
+ const { text } = fileInfo;
2467
+ result.fileInfo = fileInfo;
2468
+ let spellResult = {};
2469
+ try {
2470
+ const { showSuggestions: generateSuggestions, validateDirectives, skipValidation } = cfg.options;
2471
+ const numSuggestions = configInfo.config.numSuggestions ?? 5;
2472
+ const r = await spellCheckDocument(doc, clean({
2473
+ generateSuggestions,
2474
+ numSuggestions,
2475
+ validateDirectives,
2476
+ skipValidation
2477
+ }), userSettings);
2478
+ spellResult = r;
2479
+ result.processed = r.checked;
2480
+ result.perf = r.perf ? { ...r.perf } : void 0;
2481
+ result.issues = Text.calculateTextDocumentOffsets(doc.uri, text, r.issues).map(mapIssue);
2482
+ } catch (e) {
2483
+ reporter.error(`Failed to process "${filename}"`, toError$1(e));
2484
+ result.errors += 1;
2485
+ }
2486
+ result.elapsedTimeMs = getElapsedTimeMs();
2487
+ const config = spellResult.settingsUsed ?? {};
2488
+ result.reportIssueOptions = mergeReportIssueOptions(spellResult.settingsUsed || configInfo.config, reportIssueOptions);
2489
+ result.configErrors += reportSpellingResultConfigErrors(spellResult, processFileOptions);
2490
+ reportCheckResult(result, doc, spellResult, config, processFileOptions);
2491
+ const dep = calcDependencies(config);
2492
+ await cache.setCachedLintResults(result, dep.files);
2493
+ return result;
2354
2494
  function mapIssue({ doc: _, ...tdo }) {
2355
2495
  const context = cfg.showContext ? extractContext(tdo, cfg.showContext) : void 0;
2356
2496
  return clean({
@@ -2358,142 +2498,427 @@ async function runLint(cfg) {
2358
2498
  context
2359
2499
  });
2360
2500
  }
2361
- async function processFiles(files, configInfo, cacheSettings) {
2362
- const fileCount = Array.isArray(files) ? files.length : void 0;
2363
- const status = runResult();
2364
- const cache = await createCache(cacheSettings);
2365
- const failFast = cfg.options.failFast ?? configInfo.config.failFast ?? false;
2366
- function* prefetchFiles(files$1) {
2367
- const iter = prefetchIterable(pipe(files$1, opMap$1((filename) => prefetch(filename, configInfo, cache))), BATCH_SIZE);
2368
- for (const v of iter) yield v;
2501
+ }
2502
+ function reportCheckResult(result, _doc, spellResult, config, processFileOptions) {
2503
+ const { configInfo, reporter, verboseLevel, useColor, cfg, chalk: chalk$1 } = processFileOptions;
2504
+ const elapsed = result.elapsedTimeMs || 0;
2505
+ const dictionaries = config.dictionaries || [];
2506
+ if (verboseLevel > 1) {
2507
+ const dictsUsed = [...dictionaries].sort().map((name$1) => chalk$1.green(name$1)).join(", ");
2508
+ const msg = unindent`
2509
+ File type: ${config.languageId}, Language: ${config.language}, Issues: ${result.issues.length} ${elapsed.toFixed(2)}ms
2510
+ Config file Used: ${relativeToCwd(spellResult.localConfigFilepath || configInfo.source, cfg.root)}
2511
+ Dictionaries Used:
2512
+ ${wordWrapAnsiText(dictsUsed, 70)}`;
2513
+ reporter.info(indent(msg, " "), MessageTypes.Info);
2514
+ }
2515
+ if (cfg.options.debug) {
2516
+ const { enabled, language, languageId, dictionaries: dictionaries$1 } = config;
2517
+ const msg = unindent`\
2518
+ Debug Config: ${formatWithOptions({
2519
+ depth: 2,
2520
+ colors: useColor
2521
+ }, {
2522
+ languageId,
2523
+ enabled,
2524
+ language,
2525
+ dictionaries: dictionaries$1
2526
+ })}`;
2527
+ reporter.debug(msg);
2528
+ }
2529
+ }
2530
+ function calcDependencies(config) {
2531
+ const { configFiles, dictionaryFiles } = extractDependencies(config);
2532
+ return { files: [...configFiles, ...dictionaryFiles] };
2533
+ }
2534
+ function reportConfigurationErrors(config, processFileOptions) {
2535
+ return reportImportErrors(extractImportErrors(config), processFileOptions);
2536
+ }
2537
+ function reportImportErrors(errors, processFileOptions) {
2538
+ const { reporter, configErrors } = processFileOptions;
2539
+ let count = 0;
2540
+ errors.forEach((ref) => {
2541
+ const key = ref.error.toString();
2542
+ if (configErrors.has(key)) return;
2543
+ configErrors.add(key);
2544
+ count += 1;
2545
+ reporter.error("Configuration", ref.error);
2546
+ });
2547
+ return count;
2548
+ }
2549
+ function reportSpellingResultConfigErrors(spellResult, processFileOptions) {
2550
+ const { reporter, configErrors } = processFileOptions;
2551
+ let count = reportImportErrors(spellResult.configErrors || [], processFileOptions);
2552
+ const dictionaryErrors = [...spellResult.dictionaryErrors || []];
2553
+ for (const [dictName, dictErrors] of dictionaryErrors) {
2554
+ const msg = `Dictionary Error with (${dictName})`;
2555
+ dictErrors.forEach((error) => {
2556
+ const key = msg + error.toString();
2557
+ if (configErrors.has(key)) return;
2558
+ configErrors.add(key);
2559
+ count += 1;
2560
+ reporter.error(msg, error);
2561
+ });
2562
+ }
2563
+ return count;
2564
+ }
2565
+ function countConfigErrors(configInfo, processFileOptions) {
2566
+ return reportConfigurationErrors(configInfo.config, processFileOptions);
2567
+ }
2568
+
2569
+ //#endregion
2570
+ //#region src/util/prefetch.ts
2571
+ function* prefetchIterable(iterable, size) {
2572
+ assert(size >= 0);
2573
+ const buffer = [];
2574
+ for (const value of iterable) {
2575
+ buffer.push(value);
2576
+ if (buffer.length >= size - 1) {
2577
+ const value$1 = buffer[0];
2578
+ buffer.shift();
2579
+ yield value$1;
2369
2580
  }
2370
- async function* prefetchFilesAsync(files$1) {
2371
- for await (const filename of files$1) yield prefetch(filename, configInfo, cache);
2581
+ }
2582
+ yield* buffer;
2583
+ }
2584
+
2585
+ //#endregion
2586
+ //#region src/util/unitNumbers.ts
2587
+ const regexUnitNumber = /^((?:\d+(?:\.\d*)?)|(?:\.\d+))([a-z]*)$/i;
2588
+ const unitSizes = {
2589
+ "": 1,
2590
+ b: 1,
2591
+ k: 1024,
2592
+ kb: 1024,
2593
+ m: 1 << 20,
2594
+ mb: 1 << 20,
2595
+ g: 1 << 30,
2596
+ gb: 1 << 30
2597
+ };
2598
+ function parseUnitSize(size) {
2599
+ const match = size.match(regexUnitNumber);
2600
+ const digits = match?.[1] || "";
2601
+ const units = (match?.[2] || "").toLowerCase();
2602
+ if (!match) return {
2603
+ size,
2604
+ digits,
2605
+ units,
2606
+ error: "Invalid size."
2607
+ };
2608
+ if (!units || units in unitSizes) return {
2609
+ size,
2610
+ digits,
2611
+ units
2612
+ };
2613
+ return {
2614
+ size,
2615
+ digits,
2616
+ units,
2617
+ error: `Unknown units. Valid units are: ${Object.keys(unitSizes).filter(Boolean).join(", ").toUpperCase()}.`
2618
+ };
2619
+ }
2620
+ function validateUnitSize(size) {
2621
+ return parseUnitSize(size).error;
2622
+ }
2623
+ function sizeToNumber(size) {
2624
+ const p = parseUnitSize(size);
2625
+ if (p.error) return NaN;
2626
+ return Number.parseFloat(p.digits) * (unitSizes[p.units] || 1);
2627
+ }
2628
+
2629
+ //#endregion
2630
+ //#region src/lint/processFiles.ts
2631
+ const BATCH_FETCH_SIZE = 12;
2632
+ const BATCH_PROCESS_SIZE = 1;
2633
+ function prefetch(fileToProcess, cfg) {
2634
+ const { filename } = fileToProcess;
2635
+ if (isBinaryFile$1(filename, cfg.root)) return {
2636
+ ...fileToProcess,
2637
+ result: Promise.resolve({
2638
+ skip: true,
2639
+ skipReason: "Binary file."
2640
+ })
2641
+ };
2642
+ const reportIssueOptions = extractReporterIssueOptions(cfg.config);
2643
+ async function fetch$1() {
2644
+ const getElapsedTimeMs = getTimeMeasurer();
2645
+ const cachedResult = await cfg.cache.getCachedLintResults(filename);
2646
+ if (cachedResult) {
2647
+ cfg.reporter.debug(`Filename: ${filename}, using cache`);
2648
+ return { fileResult: {
2649
+ ...cachedResult,
2650
+ elapsedTimeMs: getElapsedTimeMs()
2651
+ } };
2372
2652
  }
2373
- const emptyResult = {
2374
- fileInfo: { filename: "" },
2375
- issues: [],
2376
- processed: false,
2377
- errors: 0,
2378
- configErrors: 0,
2379
- elapsedTimeMs: 1,
2380
- reportIssueOptions: void 0
2653
+ const uri = filenameToUri(filename, cfg.root).href;
2654
+ const checkResult = await shouldCheckDocument({ uri }, {}, cfg.config);
2655
+ if (!checkResult.shouldCheck) return {
2656
+ skip: true,
2657
+ skipReason: checkResult.reason || "Ignored by configuration."
2381
2658
  };
2382
- async function processPrefetchFileResult(pf, index) {
2383
- const { filename, result: pFetchResult } = pf;
2384
- const getElapsedTimeMs = getTimeMeasurer();
2385
- const fetchResult = await pFetchResult;
2386
- if (fetchResult instanceof Error) throw fetchResult;
2387
- reporter.emitProgressBegin(filename, index, fileCount ?? index);
2388
- if (fetchResult?.skip) return {
2389
- filename,
2390
- fileNum: index,
2391
- result: {
2392
- ...emptyResult,
2393
- fileInfo: { filename },
2394
- elapsedTimeMs: getElapsedTimeMs(),
2395
- skippedReason: fetchResult.skipReason
2396
- }
2397
- };
2398
- return {
2399
- filename,
2400
- fileNum: index,
2401
- result: await processFile(filename, configInfo, cache, fetchResult)
2659
+ const maxFileSize = processMaxFileSize(cfg.maxFileSize ?? checkResult.settings.maxFileSize);
2660
+ if (maxFileSize) {
2661
+ if (await getFileSize(filename) > maxFileSize) return {
2662
+ skip: true,
2663
+ skipReason: `File exceeded max file size of ${maxFileSize.toLocaleString()}`
2402
2664
  };
2403
2665
  }
2404
- async function* loadAndProcessFiles() {
2405
- let i = 0;
2406
- if (isAsyncIterable(files)) for await (const pf of prefetchFilesAsync(files)) yield processPrefetchFileResult(pf, ++i);
2407
- else for (const pf of prefetchFiles(files)) {
2408
- await pf.result;
2409
- yield processPrefetchFileResult(pf, ++i);
2666
+ return {
2667
+ fileInfo: await readFileInfo(filename, void 0, true),
2668
+ reportIssueOptions
2669
+ };
2670
+ }
2671
+ const result = fetch$1().catch((e) => toApplicationError(e));
2672
+ return {
2673
+ ...fileToProcess,
2674
+ result
2675
+ };
2676
+ }
2677
+ async function processFiles(files, options) {
2678
+ const status = runResult();
2679
+ const cache = await createCache(options.cacheSettings);
2680
+ const failFast = options.cfg.options.failFast ?? options.configInfo.config.failFast ?? false;
2681
+ const reporter = options.lintReporter;
2682
+ const prefetchConfig = {
2683
+ reporter,
2684
+ root: options.cfg.root,
2685
+ maxFileSize: options.cfg.maxFileSize,
2686
+ config: options.configInfo.config,
2687
+ cache
2688
+ };
2689
+ const processFileOptionsGeneral = {
2690
+ reporter,
2691
+ chalk: options.chalk,
2692
+ configInfo: options.configInfo,
2693
+ cfg: options.cfg,
2694
+ verboseLevel: options.verboseLevel,
2695
+ useColor: options.useColor,
2696
+ configErrors: options.configErrors,
2697
+ userSettings: options.configInfo.config
2698
+ };
2699
+ function* prefetchFiles(files$1) {
2700
+ yield* prefetchIterable(pipe(files$1, opMap$1((file) => prefetch(file, prefetchConfig))), BATCH_FETCH_SIZE);
2701
+ }
2702
+ async function* prefetchFilesAsync(files$1) {
2703
+ for await (const file of files$1) yield prefetch(file, prefetchConfig);
2704
+ }
2705
+ const emptyResult = {
2706
+ fileInfo: { filename: "" },
2707
+ issues: [],
2708
+ processed: false,
2709
+ errors: 0,
2710
+ configErrors: 0,
2711
+ elapsedTimeMs: 1,
2712
+ reportIssueOptions: void 0
2713
+ };
2714
+ async function processPrefetchFileResult(pf) {
2715
+ const { filename, sequence, sequenceSize, result: pFetchResult } = pf;
2716
+ const getElapsedTimeMs = getTimeMeasurer();
2717
+ const fetchResult = await pFetchResult;
2718
+ if (fetchResult instanceof Error) throw fetchResult;
2719
+ const fileNum = sequence + 1;
2720
+ reporter.emitProgressBegin(filename, fileNum, pf.sequenceSize ?? sequence);
2721
+ if (fetchResult?.skip) return {
2722
+ filename,
2723
+ sequence,
2724
+ sequenceSize,
2725
+ result: {
2726
+ ...emptyResult,
2727
+ fileInfo: { filename },
2728
+ elapsedTimeMs: getElapsedTimeMs(),
2729
+ skippedReason: fetchResult.skipReason
2410
2730
  }
2731
+ };
2732
+ return {
2733
+ filename,
2734
+ sequence,
2735
+ sequenceSize,
2736
+ result: await processFile(filename, cache, fetchResult, processFileOptionsGeneral)
2737
+ };
2738
+ }
2739
+ async function* loadAndProcessFiles() {
2740
+ if (isAsyncIterable(files)) {
2741
+ for await (const pf of prefetchFilesAsync(files)) yield processPrefetchFileResult(pf);
2742
+ return;
2411
2743
  }
2412
- for await (const fileP of loadAndProcessFiles()) {
2413
- const { filename, fileNum, result } = fileP;
2414
- status.files += 1;
2415
- status.cachedFiles = (status.cachedFiles || 0) + (result.cached ? 1 : 0);
2416
- status.skippedFiles = (status.skippedFiles || 0) + (result.processed ? 0 : 1);
2417
- const numIssues = reporter.emitProgressComplete(filename, fileNum, fileCount ?? fileNum, result);
2418
- if (numIssues || result.errors) {
2419
- status.filesWithIssues.add(relativeToCwd(filename, cfg.root));
2420
- status.issues += numIssues;
2421
- status.errors += result.errors;
2422
- if (failFast) return status;
2744
+ if (BATCH_PROCESS_SIZE <= 1) {
2745
+ for (const pf of prefetchFiles(files)) {
2746
+ await pf.result;
2747
+ yield processPrefetchFileResult(pf);
2423
2748
  }
2424
- status.errors += result.configErrors;
2749
+ return;
2425
2750
  }
2426
- await cache.reconcile();
2427
- return status;
2428
- }
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);
2751
+ yield* pipe(prefetchIterable(pipe(prefetchFiles(files), opMap$1(async (pf) => processPrefetchFileResult(pf))), BATCH_PROCESS_SIZE));
2752
+ }
2753
+ for await (const fileP of loadAndProcessFiles()) {
2754
+ const { filename, sequence, sequenceSize, result } = fileP;
2755
+ status.files += 1;
2756
+ status.cachedFiles = (status.cachedFiles || 0) + (result.cached ? 1 : 0);
2757
+ status.skippedFiles = (status.skippedFiles || 0) + (result.processed ? 0 : 1);
2758
+ const fileNum = sequence + 1;
2759
+ const numIssues = reporter.emitProgressComplete(filename, fileNum, sequenceSize ?? fileNum, result);
2760
+ if (numIssues || result.errors) {
2761
+ status.filesWithIssues.add(relativeToCwd(filename, options.cfg.root));
2762
+ status.issues += numIssues;
2763
+ status.errors += result.errors;
2764
+ if (failFast) return status;
2765
+ }
2766
+ status.errors += result.configErrors;
2767
+ }
2768
+ await cache.reconcile();
2769
+ return status;
2770
+ }
2771
+ function processMaxFileSize(value) {
2772
+ if (!value) return void 0;
2773
+ if (typeof value === "number") return value;
2774
+ const num = sizeToNumber(value);
2775
+ if (Number.isNaN(num)) throw new ApplicationError(`Invalid max file size: "${value}"`);
2776
+ return num;
2777
+ }
2778
+ function runResult(init = {}) {
2779
+ const { files = 0, filesWithIssues = /* @__PURE__ */ new Set(), issues = 0, errors = 0, cachedFiles = 0 } = init;
2780
+ return {
2781
+ files,
2782
+ filesWithIssues,
2783
+ issues,
2784
+ errors,
2785
+ cachedFiles
2786
+ };
2787
+ }
2788
+
2789
+ //#endregion
2790
+ //#region \0@oxc-project+runtime@0.107.0/helpers/usingCtx.js
2791
+ function _usingCtx() {
2792
+ var r = "function" == typeof SuppressedError ? SuppressedError : function(r$1, e$1) {
2793
+ var n$1 = Error();
2794
+ return n$1.name = "SuppressedError", n$1.error = r$1, n$1.suppressed = e$1, n$1;
2795
+ }, e = {}, n = [];
2796
+ function using(r$1, e$1) {
2797
+ if (null != e$1) {
2798
+ if (Object(e$1) !== e$1) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
2799
+ if (r$1) var o = e$1[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
2800
+ if (void 0 === o && (o = e$1[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r$1)) var t = o;
2801
+ if ("function" != typeof o) throw new TypeError("Object is not disposable.");
2802
+ t && (o = function o$1() {
2803
+ try {
2804
+ t.call(e$1);
2805
+ } catch (r$2) {
2806
+ return Promise.reject(r$2);
2807
+ }
2808
+ }), n.push({
2809
+ v: e$1,
2810
+ d: o,
2811
+ a: r$1
2452
2812
  });
2813
+ } else r$1 && n.push({
2814
+ d: e$1,
2815
+ a: r$1
2453
2816
  });
2454
- return count;
2455
- }
2456
- function countConfigErrors(configInfo) {
2457
- return reportConfigurationErrors(configInfo.config);
2817
+ return e$1;
2458
2818
  }
2819
+ return {
2820
+ e,
2821
+ u: using.bind(null, !1),
2822
+ a: using.bind(null, !0),
2823
+ d: function d() {
2824
+ var o, t = this.e, s = 0;
2825
+ function next() {
2826
+ for (; o = n.pop();) try {
2827
+ if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
2828
+ if (o.d) {
2829
+ var r$1 = o.d.call(o.v);
2830
+ if (o.a) return s |= 2, Promise.resolve(r$1).then(next, err);
2831
+ } else s |= 1;
2832
+ } catch (r$2) {
2833
+ return err(r$2);
2834
+ }
2835
+ if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
2836
+ if (t !== e) throw t;
2837
+ }
2838
+ function err(n$1) {
2839
+ return t = t !== e ? new r(n$1, t) : n$1, next();
2840
+ }
2841
+ return next();
2842
+ }
2843
+ };
2844
+ }
2845
+
2846
+ //#endregion
2847
+ //#region src/lint/lint.ts
2848
+ const version = npmPackage.version;
2849
+ const { opFilterAsync } = operators;
2850
+ async function runLint(cfg) {
2851
+ const reporter = new LintReporter(cfg.reporter, cfg.options);
2852
+ const configErrors = /* @__PURE__ */ new Set();
2853
+ const verboseLevel = calcVerboseLevel(cfg.options);
2854
+ const useColor = cfg.options.color ?? true;
2855
+ if (verboseLevel >= 1 && cfg.options.showPerfSummary) enablePerformanceMeasurements();
2856
+ const timer = getTimeMeasurer();
2857
+ const logDictRequests = truthy(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOGGING"));
2858
+ if (logDictRequests) dictionaryCacheEnableLogging(true);
2859
+ const lintResult = await run();
2860
+ if (logDictRequests) await writeDictionaryLog();
2861
+ await reporter.result(lintResult);
2862
+ const elapsed = timer();
2863
+ if (getFeatureFlags().getFlag("timer") || verboseLevel >= 1 || cfg.options.showPerfSummary) console.error(`Elapsed Time: ${elapsed.toFixed(2)}ms`);
2864
+ return lintResult;
2459
2865
  async function run() {
2460
- if (cfg.options.root) setEnvironmentVariable(ENV_CSPELL_GLOB_ROOT, cfg.root);
2461
- const configInfo = await readConfig(cfg.configFile, cfg.root, cfg.options.stopConfigSearchAt);
2462
- if (cfg.options.defaultConfiguration !== void 0) configInfo.config.loadDefaultConfiguration = cfg.options.defaultConfiguration;
2463
- configInfo.config = mergeSettings(configInfo.config, cfg.cspellSettingsFromCliOptions);
2464
- const reporterConfig = clean({
2465
- maxNumberOfProblems: configInfo.config.maxNumberOfProblems,
2466
- maxDuplicateProblems: configInfo.config.maxDuplicateProblems,
2467
- minWordLength: configInfo.config.minWordLength,
2468
- ...cfg.options,
2469
- console
2470
- });
2471
- const reporters = cfg.options.reporter ?? configInfo.config.reporters;
2472
- reporter.config = reporterConfig;
2473
- await reporter.loadReportersAndFinalize(reporters);
2474
- setLogger(getLoggerFromReporter(reporter, useColor));
2475
- const globInfo = await determineGlobs(configInfo, cfg);
2476
- const { fileGlobs, excludeGlobs } = globInfo;
2477
- const hasFileLists = !!cfg.fileLists.length;
2478
- if (!fileGlobs.length && !hasFileLists && !cfg.files?.length) return runResult();
2479
- header(fileGlobs, excludeGlobs);
2480
- 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);
2483
- if (configErrors$1 && cfg.options.exitCode !== false && !cfg.options.continueOnError) return runResult({ errors: configErrors$1 });
2484
- const { root } = cfg;
2485
2866
  try {
2486
- const cacheSettings = await calcCacheSettings(configInfo.config, {
2867
+ var _usingCtx$1 = _usingCtx();
2868
+ const _ = _usingCtx$1.u(measurePerf("runLint"));
2869
+ if (cfg.options.root) setEnvironmentVariable(ENV_CSPELL_GLOB_ROOT, cfg.root);
2870
+ const configInfo = await readConfig(cfg.configFile, cfg.root, cfg.options.stopConfigSearchAt);
2871
+ const processFileOptions = getProcessFileOptions(configInfo);
2872
+ if (cfg.options.defaultConfiguration !== void 0) configInfo.config.loadDefaultConfiguration = cfg.options.defaultConfiguration;
2873
+ configInfo.config = mergeSettings(configInfo.config, cfg.cspellSettingsFromCliOptions);
2874
+ const reporterConfig = clean({
2875
+ maxNumberOfProblems: configInfo.config.maxNumberOfProblems,
2876
+ maxDuplicateProblems: configInfo.config.maxDuplicateProblems,
2877
+ minWordLength: configInfo.config.minWordLength,
2487
2878
  ...cfg.options,
2488
- version
2489
- }, root);
2490
- const result = await processFiles(await determineFilesToCheck(configInfo, cfg, reporter, globInfo), configInfo, cacheSettings);
2491
- if (configErrors$1 && cfg.options.exitCode !== false) result.errors ||= configErrors$1;
2492
- return result;
2493
- } catch (e) {
2494
- const err = toApplicationError(e);
2495
- reporter.error("Linter", err);
2496
- return runResult({ errors: 1 });
2879
+ console
2880
+ });
2881
+ const reporters = cfg.options.reporter ?? configInfo.config.reporters;
2882
+ reporter.config = reporterConfig;
2883
+ await reporter.loadReportersAndFinalize(reporters);
2884
+ setLogger(getLoggerFromReporter(reporter, useColor));
2885
+ const globInfo = await determineGlobs(configInfo, cfg);
2886
+ const { fileGlobs, excludeGlobs } = globInfo;
2887
+ const hasFileLists = !!cfg.fileLists.length;
2888
+ if (!fileGlobs.length && !hasFileLists && !cfg.files?.length) return runResult();
2889
+ header(fileGlobs, excludeGlobs);
2890
+ checkGlobs(fileGlobs, reporter);
2891
+ if (verboseLevel > 1) reporter.info(`Config Files Found:\n ${relativeToCwd(configInfo.source)}\n`, MessageTypes$1.Info);
2892
+ const configErrorCount = countConfigErrors(configInfo, processFileOptions);
2893
+ if (configErrorCount && cfg.options.exitCode !== false && !cfg.options.continueOnError) return runResult({ errors: configErrorCount });
2894
+ const { root } = cfg;
2895
+ try {
2896
+ const cacheSettings = await calcCacheSettings(configInfo.config, {
2897
+ ...cfg.options,
2898
+ version
2899
+ }, root);
2900
+ const result = await processFiles(await determineFilesToCheck(configInfo, cfg, reporter, globInfo), {
2901
+ chalk,
2902
+ configInfo,
2903
+ cfg,
2904
+ verboseLevel,
2905
+ useColor,
2906
+ configErrors,
2907
+ userSettings: configInfo.config,
2908
+ lintReporter: reporter,
2909
+ cacheSettings
2910
+ });
2911
+ if (configErrorCount && cfg.options.exitCode !== false) result.errors ||= configErrorCount;
2912
+ return result;
2913
+ } catch (e) {
2914
+ const err = toApplicationError(e);
2915
+ reporter.error("Linter", err);
2916
+ return runResult({ errors: 1 });
2917
+ }
2918
+ } catch (_) {
2919
+ _usingCtx$1.e = _;
2920
+ } finally {
2921
+ _usingCtx$1.d();
2497
2922
  }
2498
2923
  }
2499
2924
  function header(files, cliExcludes) {
@@ -2509,7 +2934,19 @@ async function runLint(cfg) {
2509
2934
  files: ${formattedFiles}
2510
2935
  wordsOnly: ${yesNo(!!cfg.options.wordsOnly)}
2511
2936
  unique: ${yesNo(!!cfg.options.unique)}
2512
- `, MessageTypes.Info);
2937
+ `, MessageTypes$1.Info);
2938
+ }
2939
+ function getProcessFileOptions(configInfo) {
2940
+ return {
2941
+ reporter,
2942
+ chalk,
2943
+ configInfo,
2944
+ cfg,
2945
+ verboseLevel,
2946
+ useColor,
2947
+ configErrors,
2948
+ userSettings: configInfo.config
2949
+ };
2513
2950
  }
2514
2951
  }
2515
2952
  function checkGlobs(globs, reporter) {
@@ -2531,6 +2968,22 @@ async function determineGlobs(configInfo, cfg) {
2531
2968
  normalizedExcludes
2532
2969
  };
2533
2970
  }
2971
+ async function* filesToProcessAsync(filenames) {
2972
+ let sequence = 0;
2973
+ for await (const filename of filenames) yield {
2974
+ filename,
2975
+ sequence: sequence++
2976
+ };
2977
+ }
2978
+ function filesToProcess(files) {
2979
+ const filenames = [...files];
2980
+ const sequenceSize = filenames.length;
2981
+ return filenames.map((filename, sequence) => ({
2982
+ filename,
2983
+ sequence,
2984
+ sequenceSize
2985
+ }));
2986
+ }
2534
2987
  async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2535
2988
  async function _determineFilesToCheck() {
2536
2989
  const { fileLists } = cfg;
@@ -2541,7 +2994,7 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2541
2994
  const globsToExclude = globsToExcludeRaw.filter((g) => !globPattern(g).startsWith("!"));
2542
2995
  if (globsToExclude.length !== globsToExcludeRaw.length) {
2543
2996
  const msg = `Negative glob exclusions are not supported: ${globsToExcludeRaw.map((g) => globPattern(g)).filter((g) => g.startsWith("!")).join(", ")}`;
2544
- reporter.info(msg, MessageTypes.Warning);
2997
+ reporter.info(msg, MessageTypes$1.Warning);
2545
2998
  }
2546
2999
  const globMatcher = buildGlobMatcher(globsToExclude, root, true);
2547
3000
  const globOptions = {
@@ -2558,7 +3011,7 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2558
3011
  const cliFiles = cfg.options.mustFindFiles ? rawCliFiles : rawCliFiles && pipeAsync(rawCliFiles, opFilterAsync(isFile));
2559
3012
  const foundFiles = hasFileLists ? concatAsyncIterables(cliFiles, await useFileLists(fileLists, includeFilter)) : cliFiles || await findFiles(fileGlobs, globOptions);
2560
3013
  const filtered = gitIgnore ? await gitIgnore.filterOutIgnored(foundFiles) : foundFiles;
2561
- return isAsyncIterable(filtered) ? pipeAsync(filtered, opFilterExcludedFiles) : [...pipe(filtered, opFilterExcludedFiles)];
3014
+ return isAsyncIterable(filtered) ? pipeAsync(filtered, opFilterExcludedFiles, filesToProcessAsync) : filesToProcess(pipe(filtered, opFilterExcludedFiles));
2562
3015
  }
2563
3016
  function isExcluded(filename, globMatcherExclude) {
2564
3017
  if (isBinaryFile(toFileURL(filename))) return true;
@@ -2567,13 +3020,13 @@ async function determineFilesToCheck(configInfo, cfg, reporter, globInfo) {
2567
3020
  const r = globMatcherExclude.matchEx(absFilename);
2568
3021
  if (r.matched) {
2569
3022
  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);
3023
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Excluded File: ${path$1.relative(root, absFilename)}; Excluded by ${glob$2} from ${source}`, MessageTypes$1.Info);
2571
3024
  }
2572
3025
  return r.matched;
2573
3026
  }
2574
3027
  function filterOutExcludedFilesFn(globMatcherExclude) {
2575
3028
  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);
3029
+ if (calcVerboseLevel(cfg.options) > 1) reporter.info(`Exclusion Globs: \n ${excludeInfo.join("\n ")}\n`, MessageTypes$1.Info);
2577
3030
  return (filename) => !isExcluded(filename, globMatcherExclude);
2578
3031
  }
2579
3032
  return _determineFilesToCheck();
@@ -2585,16 +3038,6 @@ function extractGlobSource(g) {
2585
3038
  source
2586
3039
  };
2587
3040
  }
2588
- function runResult(init = {}) {
2589
- const { files = 0, filesWithIssues = /* @__PURE__ */ new Set(), issues = 0, errors = 0, cachedFiles = 0 } = init;
2590
- return {
2591
- files,
2592
- filesWithIssues,
2593
- issues,
2594
- errors,
2595
- cachedFiles
2596
- };
2597
- }
2598
3041
  function yesNo(value) {
2599
3042
  return value ? "Yes" : "No";
2600
3043
  }
@@ -2653,30 +3096,15 @@ async function* concatAsyncIterables(...iterables) {
2653
3096
  }
2654
3097
  async function writeDictionaryLog() {
2655
3098
  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";
3099
+ 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
3100
  await writeFileOrStream(getEnvironmentVariable("CSPELL_ENABLE_DICTIONARY_LOG_FILE") || "cspell-dictionary-log.csv", data);
2658
3101
  }
2659
3102
  function globPattern(g) {
2660
3103
  return typeof g === "string" ? g : g.glob;
2661
3104
  }
2662
- var LinterError = class extends Error {
2663
- constructor(message) {
2664
- super(message);
2665
- }
2666
- toString() {
2667
- return this.message;
2668
- }
2669
- };
2670
3105
  function calcVerboseLevel(options) {
2671
3106
  return options.verboseLevel ?? (options.verbose ? 1 : 0);
2672
3107
  }
2673
- function processMaxFileSize(value) {
2674
- if (!value) return void 0;
2675
- if (typeof value === "number") return value;
2676
- const num = sizeToNumber(value);
2677
- if (Number.isNaN(num)) throw new ApplicationError(`Invalid max file size: "${value}"`);
2678
- return num;
2679
- }
2680
3108
 
2681
3109
  //#endregion
2682
3110
  //#region src/lint/LintRequest.ts
@@ -3010,5 +3438,5 @@ function parseApplicationFeatureFlags(flags) {
3010
3438
  }
3011
3439
 
3012
3440
  //#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
3441
+ 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 };
3442
+ //# sourceMappingURL=application-DaQrXF9X.js.map