cspell 9.1.0 → 9.1.2

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,7 +1,7 @@
1
1
  import * as iPath from 'node:path';
2
2
  import chalk from 'chalk';
3
3
  import { console } from '../console.js';
4
- import { pruneTextEnd, pruneTextStart } from '../util/pad.js';
4
+ import { pruneAnsiTextEnd, pruneAnsiTextStart } from '../util/pad.js';
5
5
  import { tableToLines } from '../util/table.js';
6
6
  import { formatDictionaryLocation } from './helpers.js';
7
7
  const maxWidth = 120;
@@ -52,10 +52,10 @@ function calcHeaders(options) {
52
52
  function emitDictResult(r, options) {
53
53
  const a = r.enabled ? '*' : ' ';
54
54
  const dictColor = r.enabled ? chalk.yellowBright : chalk.rgb(200, 128, 50);
55
- const n = (width) => dictColor(pruneTextEnd(r.name, width && width - a.length) + a);
55
+ const n = (width) => dictColor(pruneAnsiTextEnd(r.name, width && width - a.length) + a);
56
56
  const c = colorize(chalk.white);
57
- const locales = (width) => c(pruneTextEnd(r.locales?.join(',') || '', width));
58
- const fileTypes = (width) => c(pruneTextEnd(r.fileTypes?.join(',') || '', width));
57
+ const locales = (width) => c(pruneAnsiTextEnd(r.locales?.join(',') || '', width));
58
+ const fileTypes = (width) => c(pruneAnsiTextEnd(r.fileTypes?.join(',') || '', width));
59
59
  if (!r.path) {
60
60
  return {
61
61
  name: n,
@@ -67,7 +67,7 @@ function emitDictResult(r, options) {
67
67
  return {
68
68
  name: n,
69
69
  location: (widthSrc) => c((r.path &&
70
- pruneTextStart(formatDictionaryLocation(r.path, widthSrc ?? maxWidth, { iPath, ...options }), widthSrc ?? maxWidth)) ||
70
+ pruneAnsiTextStart(formatDictionaryLocation(r.path, widthSrc ?? maxWidth, { iPath, ...options }), widthSrc ?? maxWidth)) ||
71
71
  ''),
72
72
  locales,
73
73
  fileTypes,
@@ -1,6 +1,6 @@
1
1
  export { pkgDir } from './dirname.js';
2
2
  export declare const name = "cspell";
3
- export declare const version = "9.1.0";
3
+ export declare const version = "9.1.2";
4
4
  export declare const engines: {
5
5
  node: string;
6
6
  };
@@ -1,7 +1,7 @@
1
1
  // This file is generated by tools/patch-version.mjs
2
2
  export { pkgDir } from './dirname.js';
3
3
  export const name = 'cspell';
4
- export const version = '9.1.0';
4
+ export const version = '9.1.2';
5
5
  export const engines = { node: '>=20' };
6
6
  export const npmPackage = { name, version, engines };
7
7
  //# sourceMappingURL=pkgInfo.js.map
@@ -1,6 +1,7 @@
1
1
  export declare function pad(s: string, w: number): string;
2
2
  export declare function padWidth(s: string, target: number): number;
3
3
  export declare function padLeft(s: string, w: number): string;
4
+ export declare function isAnsiString(s: string): boolean;
4
5
  export declare function width(s: string): number;
5
6
  export declare function ansiWidth(s: string): number;
6
7
  /**
@@ -19,4 +20,26 @@ export declare function pruneTextEnd(str: string, maxWidth: number | undefined,
19
20
  * @returns the pruned text
20
21
  */
21
22
  export declare function pruneTextStart(str: string, maxWidth: number | undefined, pad?: string): string;
23
+ interface AnsiStrFragment {
24
+ type: 'text' | 'ansi';
25
+ text: string;
26
+ }
27
+ export declare function parseAnsiStr(str: string): AnsiStrFragment[];
28
+ /**
29
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
30
+ * @param str - the text to prune - ANSI is supported
31
+ * @param maxWidth - the maximum width of the text
32
+ * @param pad - the string to use for padding, default is '…'
33
+ * @returns the pruned text
34
+ */
35
+ export declare function pruneAnsiTextEnd(str: string, maxWidth: number | undefined, pad?: string): string;
36
+ /**
37
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
38
+ * @param str - the text to prune - ANSI is supported
39
+ * @param maxWidth - the maximum width of the text
40
+ * @param pad - the string to use for padding, default is '…'
41
+ * @returns the pruned text
42
+ */
43
+ export declare function pruneAnsiTextStart(str: string, maxWidth: number | undefined, pad?: string): string;
44
+ export {};
22
45
  //# sourceMappingURL=pad.d.ts.map
@@ -1,3 +1,4 @@
1
+ import assert from 'node:assert';
1
2
  import { stripVTControlCharacters } from 'node:util';
2
3
  export function pad(s, w) {
3
4
  const p = padWidth(s, w);
@@ -15,8 +16,13 @@ export function padLeft(s, w) {
15
16
  return s;
16
17
  return s.padStart(p + s.length);
17
18
  }
19
+ export function isAnsiString(s) {
20
+ // Check if the string contains ANSI control characters.
21
+ return s.includes('\u001B') || s.includes('\u009B');
22
+ }
18
23
  export function width(s) {
19
24
  // Remove control codes and high surrogates to get the approximate width.
25
+ assert(!s.includes('\u001B'), 'String contains ANSI control characters');
20
26
  return (s
21
27
  // eslint-disable-next-line no-control-regex, no-misleading-character-class
22
28
  .replaceAll(/[\u0000-\u001F\u0300-\u036F]/g, '')
@@ -39,6 +45,8 @@ export function pruneTextEnd(str, maxWidth, pad = '…') {
39
45
  return str;
40
46
  if (str.length <= maxWidth)
41
47
  return str;
48
+ if (isAnsiString(str))
49
+ return pruneAnsiTextEnd(str, maxWidth, pad);
42
50
  const padWidth = width(pad);
43
51
  const maxWidthWithPad = maxWidth - padWidth;
44
52
  const letters = [...str];
@@ -85,4 +93,99 @@ export function pruneTextStart(str, maxWidth, pad = '…') {
85
93
  }
86
94
  return str;
87
95
  }
96
+ // https://github.com/nodejs/node/blob/0ab50c2768453e932b5402eaea3df813e725e330/lib/internal/util/inspect.js#L283
97
+ const ansi = new RegExp('[\\u001B\\u009B][[\\]()#;?]*' +
98
+ '(?:(?:(?:(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]+)*' +
99
+ '|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]*)*)?' +
100
+ '(?:\\u0007|\\u001B\\u005C|\\u009C))' +
101
+ '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?' +
102
+ '[\\dA-PR-TZcf-nq-uy=><~]))', 'g');
103
+ export function parseAnsiStr(str) {
104
+ const fragments = [];
105
+ let lastIndex = 0;
106
+ for (const match of str.matchAll(ansi)) {
107
+ if (match.index > lastIndex) {
108
+ fragments.push({ type: 'text', text: str.slice(lastIndex, match.index) });
109
+ }
110
+ fragments.push({ type: 'ansi', text: match[0] });
111
+ lastIndex = match.index + match[0].length;
112
+ }
113
+ if (lastIndex < str.length) {
114
+ fragments.push({ type: 'text', text: str.slice(lastIndex) });
115
+ }
116
+ return fragments;
117
+ }
118
+ /**
119
+ * Prune the end of a string to fit within a specified width, adding an ellipsis if necessary.
120
+ * @param str - the text to prune - ANSI is supported
121
+ * @param maxWidth - the maximum width of the text
122
+ * @param pad - the string to use for padding, default is '…'
123
+ * @returns the pruned text
124
+ */
125
+ export function pruneAnsiTextEnd(str, maxWidth, pad = '…') {
126
+ if (!maxWidth || maxWidth <= 0)
127
+ return str;
128
+ if (str.length <= maxWidth)
129
+ return str;
130
+ if (ansiWidth(str) <= maxWidth)
131
+ return str;
132
+ const padWidth = ansiWidth(pad);
133
+ const fragments = parseAnsiStr(str);
134
+ const maxWidthWithPad = maxWidth - padWidth;
135
+ let remaining = maxWidthWithPad;
136
+ for (const frag of fragments) {
137
+ if (frag.type !== 'text')
138
+ continue;
139
+ if (remaining <= 0) {
140
+ frag.text = '';
141
+ continue;
142
+ }
143
+ const pruned = pruneTextEnd(frag.text, remaining, pad);
144
+ if (pruned !== frag.text) {
145
+ frag.text = pruned;
146
+ remaining = 0; // Stop processing further fragments
147
+ continue;
148
+ }
149
+ remaining -= width(frag.text);
150
+ }
151
+ return fragments.map((frag) => frag.text).join('');
152
+ }
153
+ /**
154
+ * Prune the start of a string to fit within a specified width, adding an ellipsis if necessary.
155
+ * @param str - the text to prune - ANSI is supported
156
+ * @param maxWidth - the maximum width of the text
157
+ * @param pad - the string to use for padding, default is '…'
158
+ * @returns the pruned text
159
+ */
160
+ export function pruneAnsiTextStart(str, maxWidth, pad = '…') {
161
+ if (!maxWidth || maxWidth <= 0)
162
+ return str;
163
+ if (str.length <= maxWidth)
164
+ return str;
165
+ if (ansiWidth(str) <= maxWidth)
166
+ return str;
167
+ const padWidth = ansiWidth(pad);
168
+ const fragments = parseAnsiStr(str);
169
+ const maxWidthWithPad = maxWidth - padWidth;
170
+ let remaining = maxWidthWithPad;
171
+ for (const frag of fragments.reverse()) {
172
+ if (frag.type !== 'text')
173
+ continue;
174
+ if (remaining <= 0) {
175
+ frag.text = '';
176
+ continue;
177
+ }
178
+ const pruned = pruneTextStart(frag.text, remaining, pad);
179
+ if (pruned !== frag.text) {
180
+ frag.text = pruned;
181
+ remaining = 0; // Stop processing further fragments
182
+ continue;
183
+ }
184
+ remaining -= width(frag.text);
185
+ }
186
+ return fragments
187
+ .reverse()
188
+ .map((frag) => frag.text)
189
+ .join('');
190
+ }
88
191
  //# sourceMappingURL=pad.js.map
@@ -15,11 +15,12 @@ export function isStdinUrl(url) {
15
15
  */
16
16
  export function resolveStdinUrl(url, cwd) {
17
17
  assert(url.startsWith(STDINProtocol), `Expected url to start with ${STDINProtocol}`);
18
- const path = url
18
+ const path = decodeURIComponent(url)
19
19
  .slice(STDINProtocol.length)
20
20
  .replace(/^\/\//, '')
21
21
  .replace(/^\/([a-z]:)/i, '$1');
22
22
  const fileUrl = toFileURL(path, cwd);
23
+ // If the path is empty,
23
24
  return fileUrl.toString().replace(/^file:/, STDINProtocol) + (path ? '' : '/');
24
25
  }
25
26
  //# sourceMappingURL=stdinUrl.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cspell",
3
- "version": "9.1.0",
3
+ "version": "9.1.2",
4
4
  "description": "A Spelling Checker for Code!",
5
5
  "funding": "https://github.com/streetsidesoftware/cspell?sponsor=1",
6
6
  "bin": {
@@ -81,20 +81,20 @@
81
81
  },
82
82
  "homepage": "https://cspell.org/",
83
83
  "dependencies": {
84
- "@cspell/cspell-json-reporter": "9.1.0",
85
- "@cspell/cspell-pipe": "9.1.0",
86
- "@cspell/cspell-types": "9.1.0",
87
- "@cspell/dynamic-import": "9.1.0",
88
- "@cspell/url": "9.1.0",
84
+ "@cspell/cspell-json-reporter": "9.1.2",
85
+ "@cspell/cspell-pipe": "9.1.2",
86
+ "@cspell/cspell-types": "9.1.2",
87
+ "@cspell/dynamic-import": "9.1.2",
88
+ "@cspell/url": "9.1.2",
89
89
  "chalk": "^5.4.1",
90
90
  "chalk-template": "^1.1.0",
91
91
  "commander": "^14.0.0",
92
- "cspell-config-lib": "9.1.0",
93
- "cspell-dictionary": "9.1.0",
94
- "cspell-gitignore": "9.1.0",
95
- "cspell-glob": "9.1.0",
96
- "cspell-io": "9.1.0",
97
- "cspell-lib": "9.1.0",
92
+ "cspell-config-lib": "9.1.2",
93
+ "cspell-dictionary": "9.1.2",
94
+ "cspell-gitignore": "9.1.2",
95
+ "cspell-glob": "9.1.2",
96
+ "cspell-io": "9.1.2",
97
+ "cspell-lib": "9.1.2",
98
98
  "fast-json-stable-stringify": "^2.1.0",
99
99
  "file-entry-cache": "^9.1.0",
100
100
  "semver": "^7.7.2",
@@ -111,5 +111,5 @@
111
111
  "micromatch": "^4.0.8",
112
112
  "minimatch": "^9.0.5"
113
113
  },
114
- "gitHead": "a7ed42a31debbc86faa4a4ac2c686bdffe5b26b6"
114
+ "gitHead": "231a39f34c2c586ebb596ae7b2a832a996a80a1e"
115
115
  }