@d3lm/lint-preset 1.5.5 → 1.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.
- package/README.md +42 -7
- package/dist/bin/comments.js +559 -123
- package/dist/bin/comments.js.map +1 -1
- package/dist/comments.cjs +462 -77
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.d.cts +46 -20
- package/dist/comments.d.ts +46 -20
- package/dist/comments.js +464 -81
- package/dist/comments.js.map +1 -1
- package/native/comments.darwin-arm64.node +0 -0
- package/native/comments.darwin-x64.node +0 -0
- package/native/comments.linux-arm64-gnu.node +0 -0
- package/native/comments.linux-x64-gnu.node +0 -0
- package/package.json +9 -6
package/dist/comments.cjs
CHANGED
|
@@ -1,8 +1,75 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunk56WU3HSDcjs = require('./chunk-56WU3HSD.cjs');
|
|
2
4
|
|
|
3
5
|
// src/comments/analyze.ts
|
|
4
6
|
var _oxcparser = require('oxc-parser');
|
|
5
7
|
|
|
8
|
+
// src/comments/rust.ts
|
|
9
|
+
var _fs = require('fs');
|
|
10
|
+
var _module = require('module');
|
|
11
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
12
|
+
var _url = require('url');
|
|
13
|
+
var BINARY_NAME = "comments";
|
|
14
|
+
function platformSuffixes() {
|
|
15
|
+
const { platform, arch } = process;
|
|
16
|
+
switch (platform) {
|
|
17
|
+
case "darwin": {
|
|
18
|
+
return [`darwin-${arch}`, "darwin-universal"];
|
|
19
|
+
}
|
|
20
|
+
case "linux": {
|
|
21
|
+
return arch === "arm" ? ["linux-arm-gnueabihf", "linux-arm-musleabihf"] : [`linux-${arch}-gnu`, `linux-${arch}-musl`];
|
|
22
|
+
}
|
|
23
|
+
case "win32": {
|
|
24
|
+
return [`win32-${arch}-msvc`, `win32-${arch}-gnu`];
|
|
25
|
+
}
|
|
26
|
+
default: {
|
|
27
|
+
return [`${platform}-${arch}`];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function findBinaries() {
|
|
32
|
+
const names = platformSuffixes().map((suffix) => `${BINARY_NAME}.${suffix}.node`);
|
|
33
|
+
for (let directory = _path2.default.dirname(_url.fileURLToPath.call(void 0, _chunk56WU3HSDcjs.importMetaUrl)); ; directory = _path2.default.dirname(directory)) {
|
|
34
|
+
const found = names.map((name) => _path2.default.join(directory, "native", name)).filter((file) => _fs.existsSync.call(void 0, file));
|
|
35
|
+
if (found.length > 0) {
|
|
36
|
+
return found;
|
|
37
|
+
}
|
|
38
|
+
if (_path2.default.dirname(directory) === directory) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
var binding;
|
|
44
|
+
function loadBinding() {
|
|
45
|
+
if (binding) {
|
|
46
|
+
return binding;
|
|
47
|
+
}
|
|
48
|
+
const require2 = _module.createRequire.call(void 0, _chunk56WU3HSDcjs.importMetaUrl);
|
|
49
|
+
const errors = [];
|
|
50
|
+
for (const file of findBinaries()) {
|
|
51
|
+
try {
|
|
52
|
+
binding = require2(file);
|
|
53
|
+
return binding;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
errors.push(`${file}: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const reason = errors.length > 0 ? `, and loading it failed (${errors.join("; ")})` : "";
|
|
59
|
+
throw new Error(
|
|
60
|
+
`comments: Rust files need the native addon for ${process.platform}-${process.arch}${reason}. Build it with \`pnpm run build:native\`, or leave \`.rs\` out of \`extensions\`.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function scanRust(source) {
|
|
64
|
+
return loadBinding().scanRust(source);
|
|
65
|
+
}
|
|
66
|
+
function rustFingerprint(source) {
|
|
67
|
+
return loadBinding().rustFingerprint(source);
|
|
68
|
+
}
|
|
69
|
+
function isRustCode(text) {
|
|
70
|
+
return loadBinding().isRustCode(text);
|
|
71
|
+
}
|
|
72
|
+
|
|
6
73
|
// src/comments/style.ts
|
|
7
74
|
var BANNER = /^(?!#{1,6}\s)([-=*#_~+/\\])\1{3,}|([-=*#_~])\2{3,}\s*$/;
|
|
8
75
|
var COLUMN_RULE = /^([-=_~])\1*(?:\s+\1+)+$/;
|
|
@@ -126,12 +193,94 @@ function findCodeSpanEnd(text, start) {
|
|
|
126
193
|
}
|
|
127
194
|
return -1;
|
|
128
195
|
}
|
|
196
|
+
function isEscaped(text, index) {
|
|
197
|
+
let backslashes = 0;
|
|
198
|
+
while (text[index - 1 - backslashes] === "\\") {
|
|
199
|
+
backslashes += 1;
|
|
200
|
+
}
|
|
201
|
+
return backslashes % 2 === 1;
|
|
202
|
+
}
|
|
203
|
+
function findUnescaped(text, char, start) {
|
|
204
|
+
for (let index = start; index < text.length; index += 1) {
|
|
205
|
+
if (text[index] === "\\") {
|
|
206
|
+
index += 1;
|
|
207
|
+
} else if (text[index] === char) {
|
|
208
|
+
return index;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return -1;
|
|
212
|
+
}
|
|
213
|
+
function findClosingBracket(text, start) {
|
|
214
|
+
let depth = 0;
|
|
215
|
+
for (let index = start; index < text.length; index += 1) {
|
|
216
|
+
switch (text[index]) {
|
|
217
|
+
case "\\": {
|
|
218
|
+
index += 1;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case "`": {
|
|
222
|
+
const close = findCodeSpanEnd(text, index);
|
|
223
|
+
index = close === -1 ? index + backtickRun(text, index) - 1 : close;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case "[": {
|
|
227
|
+
depth += 1;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "]": {
|
|
231
|
+
depth -= 1;
|
|
232
|
+
if (depth === 0) {
|
|
233
|
+
return index;
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return -1;
|
|
240
|
+
}
|
|
241
|
+
function findDestinationEnd(text, start) {
|
|
242
|
+
let depth = 0;
|
|
243
|
+
for (let index = start; index < text.length; index += 1) {
|
|
244
|
+
const char = text[index];
|
|
245
|
+
let close = -1;
|
|
246
|
+
if (char === "\\") {
|
|
247
|
+
index += 1;
|
|
248
|
+
} else if (char === "<" && text.slice(start + 1, index).trim() === "") {
|
|
249
|
+
close = findUnescaped(text, ">", index + 1);
|
|
250
|
+
} else if ((char === '"' || char === "'") && /\s/.test(text[index - 1])) {
|
|
251
|
+
close = findUnescaped(text, char, index + 1);
|
|
252
|
+
} else if (char === "(") {
|
|
253
|
+
depth += 1;
|
|
254
|
+
} else if (char === ")" && --depth === 0) {
|
|
255
|
+
return index;
|
|
256
|
+
}
|
|
257
|
+
index = close === -1 ? index : close;
|
|
258
|
+
}
|
|
259
|
+
return -1;
|
|
260
|
+
}
|
|
261
|
+
function findLinkEnd(text, start) {
|
|
262
|
+
const close = findClosingBracket(text, start);
|
|
263
|
+
if (close === -1 || text[close + 1] !== "(") {
|
|
264
|
+
return close;
|
|
265
|
+
}
|
|
266
|
+
const end = findDestinationEnd(text, close + 1);
|
|
267
|
+
return end === -1 ? close : end;
|
|
268
|
+
}
|
|
129
269
|
function tokenize(text) {
|
|
270
|
+
return splitWords(text, true);
|
|
271
|
+
}
|
|
272
|
+
function splitWords(text, keepLinks) {
|
|
130
273
|
const tokens = [];
|
|
131
274
|
let current = "";
|
|
132
275
|
let index = 0;
|
|
133
276
|
while (index < text.length) {
|
|
134
277
|
const char = text[index];
|
|
278
|
+
const linkEnd = keepLinks && char === "[" && !isEscaped(text, index) ? findLinkEnd(text, index) : -1;
|
|
279
|
+
if (linkEnd !== -1) {
|
|
280
|
+
current += splitWords(text.slice(index, linkEnd + 1), false).join(" ");
|
|
281
|
+
index = linkEnd + 1;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
135
284
|
const close = char === "`" ? findCodeSpanEnd(text, index) : char === "{" ? findClosingBrace(text, index) : -1;
|
|
136
285
|
if (char === "`" && close === -1) {
|
|
137
286
|
const run = backtickRun(text, index);
|
|
@@ -227,7 +376,7 @@ function isOrphan(previous, token) {
|
|
|
227
376
|
return opensAside(token) || endsClause(previous) && !CLAUSE_END.test(token);
|
|
228
377
|
}
|
|
229
378
|
function startsBlock(token) {
|
|
230
|
-
return /^(
|
|
379
|
+
return /^(?:\+|-+|=+|\*+|_{3,}|\d+[.)]|#{1,6})$/.test(token) || /^(?:>|@[A-Za-z]|`{3}|~{3}|\||<(?:\/?[A-Za-z]|[!?]))/.test(token);
|
|
231
380
|
}
|
|
232
381
|
function bindLineStarts(tokens, startsLine2 = startsBlock) {
|
|
233
382
|
const units = [];
|
|
@@ -665,6 +814,7 @@ var NAMED_TAGS = /* @__PURE__ */ new Set(["arg", "argument", "param", "prop", "p
|
|
|
665
814
|
var LINE_DIRECTIVE = /^\s*(?:(?:eslint|oxlint|stylelint)-(?:disable(?:-next-line|-line)?|enable)\b|eslint-env\b|@ts-(?:ignore|expect-error|nocheck|check)\b|@jsx\w*\b|@vite-ignore\b|[@#]__[A-Z_]+__|@(?:vitest|jest)-environment\b|(?:istanbul|c8|v8)\s+ignore\b|prettier-ignore\b|biome-ignore\b|deno-(?:lint-ignore|fmt-ignore)(?:-file)?\b|tslint:|(?:c[sS]pell|spell-?checker)\s*:|(?:webpack|turbopack)[A-Z][a-zA-Z]*\s*:|vim?:\s|#(?:region|endregion)\b|<reference\s)/;
|
|
666
815
|
var BLOCK_DIRECTIVE = /^\s*(?:eslint\s|globals?\s|exported\s|jshint\s|jslint\s|type\s*:)/;
|
|
667
816
|
var LEGAL_COMMENT = /^!|@license\b|@preserve\b|\bcopyright\s+(?:\(c\)|©|\d{4})/i;
|
|
817
|
+
var RUST_LEGAL_COMMENT = /@license\b|@preserve\b|\bcopyright\s+(?:\(c\)|©|\d{4})/i;
|
|
668
818
|
var DEFINITION = /^(?:``|[\w$.[\]]+(?:[ -][\w$.[\]]+)?)(?: [-–]|:) \S/;
|
|
669
819
|
var BOX_DRAWING = /[\u2500-\u257F]/;
|
|
670
820
|
var TABLE_DELIMITER = /^\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*)+\|?$/;
|
|
@@ -739,12 +889,35 @@ ${text}
|
|
|
739
889
|
}
|
|
740
890
|
return properties.length > 1 || text.trimEnd().endsWith(",") || properties.some((property) => property.type === "Property" && CODE_VALUES.has(property.value.type));
|
|
741
891
|
}
|
|
742
|
-
function isCode(texts) {
|
|
743
|
-
|
|
744
|
-
return parsesAsStatements(text) || parsesAsProperties(text);
|
|
892
|
+
function isCode(texts, language) {
|
|
893
|
+
return language.isCode(texts.map((line) => line.replace(LIST_MARKER, "")).join("\n"));
|
|
745
894
|
}
|
|
746
895
|
function isCodeLine(text) {
|
|
747
|
-
return text.endsWith("{") || text.endsWith(";") && (/[()[\]{}=<>]/.test(text) || !/\s/.test(text)) || /^[}\])]/.test(text)
|
|
896
|
+
return text.endsWith("{") || text.endsWith(";") && (/[()[\]{}=<>]/.test(text) || !/\s/.test(text)) || /^[}\])]/.test(text);
|
|
897
|
+
}
|
|
898
|
+
var JAVASCRIPT = {
|
|
899
|
+
name: "javascript",
|
|
900
|
+
isCode: (text) => parsesAsStatements(text) || parsesAsProperties(text),
|
|
901
|
+
isCodeLine: (text) => {
|
|
902
|
+
return isCodeLine(text) || /^(?:const|let|var)\s+[\w$]+\s*[:=]/.test(text) || /^(?:import|export)\s.*(?:\sfrom\s|[{}])/.test(text);
|
|
903
|
+
},
|
|
904
|
+
legal: LEGAL_COMMENT,
|
|
905
|
+
blockDirective: BLOCK_DIRECTIVE,
|
|
906
|
+
tags: true,
|
|
907
|
+
joinsCodeSpans: true
|
|
908
|
+
};
|
|
909
|
+
var RUST = {
|
|
910
|
+
name: "rust",
|
|
911
|
+
isCode: (text) => isRustCode(text),
|
|
912
|
+
isCodeLine: (text) => {
|
|
913
|
+
return isCodeLine(text) || /^let\s+(?:mut\s+)?\w+\s*[:=]/.test(text) || /^(?:pub(?:\([\w:\s]+\))?\s+)?(?:const|static(?:\s+mut)?)\s+[A-Z_\d]+\s*:/.test(text) || /^(?:pub(?:\([\w:\s]+\))?\s+)?use\s+[\w:]*::/.test(text) || /^#!?\[/.test(text);
|
|
914
|
+
},
|
|
915
|
+
legal: RUST_LEGAL_COMMENT,
|
|
916
|
+
tags: false,
|
|
917
|
+
joinsCodeSpans: false
|
|
918
|
+
};
|
|
919
|
+
function languageOf(filename) {
|
|
920
|
+
return filename.endsWith(".rs") ? RUST : JAVASCRIPT;
|
|
748
921
|
}
|
|
749
922
|
function layoutReason(prose, raw) {
|
|
750
923
|
if (prose.startsWith("|") || TABLE_DELIMITER.test(prose)) {
|
|
@@ -758,8 +931,10 @@ function layoutReason(prose, raw) {
|
|
|
758
931
|
}
|
|
759
932
|
return void 0;
|
|
760
933
|
}
|
|
934
|
+
var THEMATIC_BREAK = /^(?:(?:\*\s*){3,}|(?:-\s*){3,}|(?:_\s*){3,})$/;
|
|
761
935
|
function isMarkup(prose) {
|
|
762
|
-
|
|
936
|
+
const text = prose.replace(LIST_MARKER, "");
|
|
937
|
+
return /^#{1,6}(?:\s|$)/.test(text) || /^\[[^\]]+\]:/.test(text) || /^<(?:\/?[a-z]|[!?])/i.test(text) || text.startsWith(">") || /^(?:=+|-+)$/.test(prose) || THEMATIC_BREAK.test(prose);
|
|
763
938
|
}
|
|
764
939
|
function mask(text) {
|
|
765
940
|
return text.replaceAll(/[^\n]/g, "x");
|
|
@@ -786,11 +961,14 @@ function hasAlignedColumns(lines) {
|
|
|
786
961
|
}
|
|
787
962
|
return false;
|
|
788
963
|
}
|
|
789
|
-
function structureOf(lines) {
|
|
964
|
+
function structureOf(lines, language) {
|
|
790
965
|
const trimmed = lines.map((line) => line.text.trim()).join("\n");
|
|
791
966
|
const texts = replaceKeepingLines(trimmed, CODE_SPAN, "``").split("\n");
|
|
792
967
|
const proseLines = stripNonProse(trimmed).split("\n");
|
|
793
|
-
if (isCode(
|
|
968
|
+
if (isCode(
|
|
969
|
+
lines.map((line) => line.text.trim()),
|
|
970
|
+
language
|
|
971
|
+
)) {
|
|
794
972
|
return { tier: "code", reason: "code" };
|
|
795
973
|
}
|
|
796
974
|
let layout;
|
|
@@ -799,7 +977,7 @@ function structureOf(lines) {
|
|
|
799
977
|
for (const [position, line] of texts.entries()) {
|
|
800
978
|
const prose = proseLines[position].trim();
|
|
801
979
|
layout ??= layoutReason(prose, _nullishCoalesce(_optionalChain([lines, 'access', _6 => _6[position], 'optionalAccess', _7 => _7.text]), () => ( "")));
|
|
802
|
-
if (isCodeLine(line.trim())) {
|
|
980
|
+
if (language.isCodeLine(line.trim())) {
|
|
803
981
|
codeLike = true;
|
|
804
982
|
} else if (isMarkup(prose)) {
|
|
805
983
|
manual ??= "markup";
|
|
@@ -838,7 +1016,7 @@ var FenceTracker = class {
|
|
|
838
1016
|
return true;
|
|
839
1017
|
}
|
|
840
1018
|
};
|
|
841
|
-
function segment(lines) {
|
|
1019
|
+
function segment(lines, language) {
|
|
842
1020
|
const paragraphs = [];
|
|
843
1021
|
const fence = new FenceTracker();
|
|
844
1022
|
let current;
|
|
@@ -875,6 +1053,10 @@ function segment(lines) {
|
|
|
875
1053
|
continue;
|
|
876
1054
|
}
|
|
877
1055
|
if (line.verbatim) {
|
|
1056
|
+
if (fence.next(text)) {
|
|
1057
|
+
start("code", line, "", "code block");
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
878
1060
|
start("code", line, "", "directive");
|
|
879
1061
|
flush();
|
|
880
1062
|
continue;
|
|
@@ -899,7 +1081,7 @@ function segment(lines) {
|
|
|
899
1081
|
}
|
|
900
1082
|
continue;
|
|
901
1083
|
}
|
|
902
|
-
const tag = JSDOC_TAG.exec(text);
|
|
1084
|
+
const tag = language.tags ? JSDOC_TAG.exec(text) : null;
|
|
903
1085
|
if (tag) {
|
|
904
1086
|
start("tag", line, "", PROSE_TAGS.has(tag[1]) ? void 0 : `@${tag[1]} tag`);
|
|
905
1087
|
inCodeTag = CODE_TAGS.has(tag[1]);
|
|
@@ -1006,7 +1188,19 @@ function byLine(issues) {
|
|
|
1006
1188
|
function sameLayout(a, b) {
|
|
1007
1189
|
return a.length === b.length && a.every((words, position) => words.join(" ") === b[position].join(" "));
|
|
1008
1190
|
}
|
|
1009
|
-
function
|
|
1191
|
+
function splitTokenLine(lines, tokens) {
|
|
1192
|
+
let next = 0;
|
|
1193
|
+
return lines.findIndex((words) => {
|
|
1194
|
+
const line = words.join(" ");
|
|
1195
|
+
let text = "";
|
|
1196
|
+
while (text.length < line.length && next < tokens.length) {
|
|
1197
|
+
text += `${text ? " " : ""}${tokens[next]}`;
|
|
1198
|
+
next += 1;
|
|
1199
|
+
}
|
|
1200
|
+
return text !== line;
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
function analyzeParagraph(paragraph, options, language) {
|
|
1010
1204
|
const first = paragraph.lines[0];
|
|
1011
1205
|
const last = _nullishCoalesce(paragraph.lines.at(-1), () => ( first));
|
|
1012
1206
|
const base = { kind: paragraph.kind, startLine: first.index + 1, endLine: last.index + 1 };
|
|
@@ -1033,7 +1227,8 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1033
1227
|
const structure2 = structureOf(
|
|
1034
1228
|
prose.lines.map((line) => {
|
|
1035
1229
|
return { ...line, text: stripBlockSyntax(line.text) };
|
|
1036
|
-
})
|
|
1230
|
+
}),
|
|
1231
|
+
language
|
|
1037
1232
|
);
|
|
1038
1233
|
const issues2 = keptIssues(prose, structure2, options);
|
|
1039
1234
|
return { ...base, reflowable: false, reason: paragraph.reason, issues: issues2, suggestion: null };
|
|
@@ -1042,7 +1237,7 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1042
1237
|
const issues2 = keptIssues(paragraph, structure2, options);
|
|
1043
1238
|
return { ...base, reflowable: false, reason: structure2.reason, issues: issues2, suggestion: null };
|
|
1044
1239
|
};
|
|
1045
|
-
const structure = structureOf(paragraph.lines);
|
|
1240
|
+
const structure = structureOf(paragraph.lines, language);
|
|
1046
1241
|
if (structure) {
|
|
1047
1242
|
return keep(structure);
|
|
1048
1243
|
}
|
|
@@ -1061,9 +1256,9 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1061
1256
|
parentheticalOverflow: options.maxWidth === void 0 ? void 0 : options.maxWidth - options.width,
|
|
1062
1257
|
keepFirstLine: paragraph.kind === "tag"
|
|
1063
1258
|
};
|
|
1064
|
-
const layOut = (from, settings) => {
|
|
1065
|
-
const even2 = layoutTokens(from, firstLimit, restLimit, settings);
|
|
1066
|
-
return { even: even2, wrapped: breakAfterSentence(even2, firstLimit, restLimit, settings) };
|
|
1259
|
+
const layOut = (from, settings, extra) => {
|
|
1260
|
+
const even2 = layoutTokens(from, firstLimit + extra, restLimit + extra, settings);
|
|
1261
|
+
return { even: even2, wrapped: breakAfterSentence(even2, firstLimit + extra, restLimit + extra, settings) };
|
|
1067
1262
|
};
|
|
1068
1263
|
const units = bindLineStarts(paragraph.kind === "tag" ? bindTagHead(tokens) : tokens, startsLine);
|
|
1069
1264
|
const hardLimit = restLimit + (options.maxWidth === void 0 ? options.tolerance : options.maxWidth - options.width);
|
|
@@ -1074,22 +1269,26 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1074
1269
|
if (!units.every((unit) => staysInLimit(unit))) {
|
|
1075
1270
|
return keep({ tier: "manual", reason: "unbreakable text across lines" });
|
|
1076
1271
|
}
|
|
1272
|
+
const text = currentLines.map((words) => words.join(" ")).join("\n");
|
|
1273
|
+
if (!language.joinsCodeSpans && [...text.matchAll(CODE_SPAN)].some((span) => span[0].includes("\n"))) {
|
|
1274
|
+
return keep({ tier: "manual", reason: "code span across lines" });
|
|
1275
|
+
}
|
|
1077
1276
|
const bodyOf = (lines) => {
|
|
1078
1277
|
return lines.map((words, position) => {
|
|
1079
|
-
const
|
|
1080
|
-
return { ...first, text, verbatim: false, hardBreak: HARD_BREAK.test(
|
|
1278
|
+
const text2 = `${position === 0 ? paragraph.marker : hang}${words.join(" ")}`;
|
|
1279
|
+
return { ...first, text: text2, verbatim: false, hardBreak: HARD_BREAK.test(text2) };
|
|
1081
1280
|
});
|
|
1082
1281
|
};
|
|
1083
|
-
const settle = (settings) => {
|
|
1282
|
+
const settle = (settings, extra = 0) => {
|
|
1084
1283
|
let current = units;
|
|
1085
|
-
let { even: even2, wrapped: wrapped2 } = layOut(current, settings);
|
|
1284
|
+
let { even: even2, wrapped: wrapped2 } = layOut(current, settings, extra);
|
|
1086
1285
|
for (; ; ) {
|
|
1087
1286
|
const body = bodyOf(wrapped2);
|
|
1088
|
-
const rewrap = structureOf(body);
|
|
1287
|
+
const rewrap = structureOf(body, language);
|
|
1089
1288
|
if (!rewrap) {
|
|
1090
1289
|
return { even: even2, wrapped: wrapped2 };
|
|
1091
1290
|
}
|
|
1092
|
-
const tipping = body.findIndex((_, position) => structureOf(body.slice(0, position + 1)) !== void 0);
|
|
1291
|
+
const tipping = body.findIndex((_, position) => structureOf(body.slice(0, position + 1), language) !== void 0);
|
|
1093
1292
|
const start = wrapped2.slice(0, Math.max(tipping, 1)).flat().length;
|
|
1094
1293
|
const pair = `${current[start - 1]} ${current[start]}`;
|
|
1095
1294
|
if (body.length < 2 || !staysInLimit(pair)) {
|
|
@@ -1097,16 +1296,25 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1097
1296
|
return { tier: "manual", reason: `rewrap would read as ${reading}` };
|
|
1098
1297
|
}
|
|
1099
1298
|
current = current.toSpliced(start - 1, 2, pair);
|
|
1100
|
-
({ even: even2, wrapped: wrapped2 } = layOut(current, settings));
|
|
1299
|
+
({ even: even2, wrapped: wrapped2 } = layOut(current, settings, extra));
|
|
1101
1300
|
}
|
|
1102
1301
|
};
|
|
1103
|
-
const
|
|
1104
|
-
|
|
1302
|
+
const layoutAt = (extra) => {
|
|
1303
|
+
const settings = {
|
|
1304
|
+
...layoutOptions,
|
|
1305
|
+
tolerance: layoutOptions.tolerance - extra,
|
|
1306
|
+
parentheticalOverflow: layoutOptions.parentheticalOverflow === void 0 ? void 0 : layoutOptions.parentheticalOverflow - extra
|
|
1307
|
+
};
|
|
1308
|
+
const settled = settle(settings, extra);
|
|
1309
|
+
return "wrapped" in settled ? settled : settle({ ...settings, keepSteps: true }, extra);
|
|
1310
|
+
};
|
|
1311
|
+
const layout = layoutAt(0);
|
|
1105
1312
|
if (!("wrapped" in layout)) {
|
|
1106
1313
|
return keep(layout);
|
|
1107
1314
|
}
|
|
1108
1315
|
const { even, wrapped } = layout;
|
|
1109
1316
|
const matchesLayout = sameLayout(wrapped, currentLines);
|
|
1317
|
+
const isVerdict = (issue) => ISSUE_TIERS[issue.type] === "verdict";
|
|
1110
1318
|
const lineIssues = [];
|
|
1111
1319
|
const overflow = options.maxWidth === void 0 ? [] : overflowIssues(paragraph.lines, options);
|
|
1112
1320
|
if (!matchesLayout) {
|
|
@@ -1165,14 +1373,41 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1165
1373
|
lineIssues.push({ type: "unbalanced", line });
|
|
1166
1374
|
}
|
|
1167
1375
|
}
|
|
1376
|
+
const splitLine = splitTokenLine(currentLines, tokens);
|
|
1377
|
+
if (splitLine !== -1 && overflow.length === 0 && !lineIssues.some((issue) => isVerdict(issue))) {
|
|
1378
|
+
lineIssues.push({ type: "unbalanced", line: splitLine });
|
|
1379
|
+
}
|
|
1168
1380
|
}
|
|
1169
|
-
const
|
|
1381
|
+
const matchesWiderLayout = () => {
|
|
1382
|
+
for (let extra = 1; extra <= layoutOptions.tolerance; extra += 1) {
|
|
1383
|
+
const wider = layoutAt(extra);
|
|
1384
|
+
if ("wrapped" in wider && sameLayout(wider.wrapped, currentLines)) {
|
|
1385
|
+
return true;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return false;
|
|
1389
|
+
};
|
|
1390
|
+
const accepted = lineIssues.some((issue) => isVerdict(issue)) && matchesWiderLayout();
|
|
1391
|
+
const suggested = accepted ? currentLines : wrapped;
|
|
1392
|
+
const suggestion = suggested.map(
|
|
1170
1393
|
(words, position) => `${prefix}${position === 0 ? paragraph.marker : hang}${words.join(" ")}`
|
|
1171
1394
|
);
|
|
1172
|
-
const wrapIssues = lineIssues.map((issue) => lineIssue(issue.type, paragraph.lines[issue.line], options));
|
|
1395
|
+
const wrapIssues = lineIssues.filter((issue) => !accepted || !isVerdict(issue)).map((issue) => lineIssue(issue.type, paragraph.lines[issue.line], options));
|
|
1173
1396
|
const issues = byLine([...overflow, ...wrapIssues, ...styleIssues(paragraph, options)]);
|
|
1174
1397
|
return { ...base, reflowable: true, issues, suggestion };
|
|
1175
1398
|
}
|
|
1399
|
+
var LINE_COMMENT = "line comment";
|
|
1400
|
+
function analyzeLineParagraph(paragraph, options, language) {
|
|
1401
|
+
const first = paragraph.lines[0];
|
|
1402
|
+
const last = _nullishCoalesce(paragraph.lines.at(-1), () => ( first));
|
|
1403
|
+
const base = { kind: paragraph.kind, startLine: first.index + 1, endLine: last.index + 1 };
|
|
1404
|
+
if (paragraph.kind === "code") {
|
|
1405
|
+
return { ...base, reflowable: false, reason: paragraph.reason, issues: [], suggestion: null };
|
|
1406
|
+
}
|
|
1407
|
+
const tier = _optionalChain([structureOf, 'call', _41 => _41(paragraph.lines, language), 'optionalAccess', _42 => _42.tier]);
|
|
1408
|
+
const issues = tier === "code" || tier === "layout" ? [] : overflowIssues(paragraph.lines, options);
|
|
1409
|
+
return { ...base, reflowable: false, reason: LINE_COMMENT, issues, suggestion: null };
|
|
1410
|
+
}
|
|
1176
1411
|
function computeLineStarts(source) {
|
|
1177
1412
|
const starts = [0];
|
|
1178
1413
|
for (let index = 0; index < source.length; index += 1) {
|
|
@@ -1198,31 +1433,42 @@ function lineOf(lineStarts, offset) {
|
|
|
1198
1433
|
function stripCarriageReturn(line) {
|
|
1199
1434
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
1200
1435
|
}
|
|
1201
|
-
function
|
|
1436
|
+
function blockKind(comment, language) {
|
|
1437
|
+
if (language.name === "rust") {
|
|
1438
|
+
return comment.doc ? `${comment.doc}-block-doc` : "block";
|
|
1439
|
+
}
|
|
1440
|
+
return comment.value.startsWith("*") ? "jsdoc" : "block";
|
|
1441
|
+
}
|
|
1442
|
+
function wholeComment(shape, lines, paragraphKind, reason) {
|
|
1443
|
+
const otherLines = lines.map((text, offset) => {
|
|
1444
|
+
return { index: shape.startLine + offset, prefix: "", text, verbatim: false, hardBreak: false };
|
|
1445
|
+
});
|
|
1446
|
+
return { ...shape, paragraphs: [{ kind: paragraphKind, marker: "", reason, lines: otherLines }] };
|
|
1447
|
+
}
|
|
1448
|
+
function isLegal(value, language) {
|
|
1449
|
+
return language.legal.test(replaceKeepingLines(value, CODE_SPAN, "``"));
|
|
1450
|
+
}
|
|
1451
|
+
function shapeBlock(comment, sourceLines, lineStarts, language) {
|
|
1202
1452
|
const startLine = lineOf(lineStarts, comment.start);
|
|
1203
1453
|
const endLine = lineOf(lineStarts, comment.end - 1);
|
|
1204
|
-
const
|
|
1454
|
+
const shape = { kind: blockKind(comment, language), startLine, endLine };
|
|
1205
1455
|
const lines = sourceLines.slice(startLine, endLine + 1);
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
});
|
|
1210
|
-
return { kind, startLine, endLine, paragraphs: [{ kind: paragraphKind, marker: "", reason, lines: otherLines }] };
|
|
1211
|
-
};
|
|
1212
|
-
const value = comment.value.replace(/^\*/, "");
|
|
1213
|
-
if (LEGAL_COMMENT.test(replaceKeepingLines(comment.value, CODE_SPAN, "``"))) {
|
|
1214
|
-
return wholeComment("code", "legal comment");
|
|
1456
|
+
const value = comment.doc ? comment.value.slice(1) : comment.value.replace(/^\*/, "");
|
|
1457
|
+
if (isLegal(comment.value, language)) {
|
|
1458
|
+
return wholeComment(shape, lines, "code", "legal comment");
|
|
1215
1459
|
}
|
|
1216
|
-
if (LINE_DIRECTIVE.test(value) ||
|
|
1217
|
-
return wholeComment("code", "directive");
|
|
1460
|
+
if (LINE_DIRECTIVE.test(value) || _optionalChain([language, 'access', _43 => _43.blockDirective, 'optionalAccess', _44 => _44.test, 'call', _45 => _45(value)])) {
|
|
1461
|
+
return wholeComment(shape, lines, "code", "directive");
|
|
1218
1462
|
}
|
|
1219
1463
|
const other = () => {
|
|
1220
1464
|
return wholeComment(
|
|
1465
|
+
shape,
|
|
1466
|
+
lines,
|
|
1221
1467
|
"other",
|
|
1222
1468
|
startLine === endLine ? "single-line block comment" : "unsupported block comment layout"
|
|
1223
1469
|
);
|
|
1224
1470
|
};
|
|
1225
|
-
if (startLine === endLine || !/^\s
|
|
1471
|
+
if (startLine === endLine || !/^\s*\/\*[*!]?$/.test(lines[0]) || !/^\s*\*\/$/.test(_nullishCoalesce(lines.at(-1), () => ( "")))) {
|
|
1226
1472
|
return other();
|
|
1227
1473
|
}
|
|
1228
1474
|
const body = [];
|
|
@@ -1241,7 +1487,52 @@ function shapeBlock(comment, sourceLines, lineStarts) {
|
|
|
1241
1487
|
hardBreak: HARD_BREAK.test(text)
|
|
1242
1488
|
});
|
|
1243
1489
|
}
|
|
1244
|
-
return {
|
|
1490
|
+
return { ...shape, paragraphs: segment(body, language) };
|
|
1491
|
+
}
|
|
1492
|
+
function shapeLineRun(group, sourceLines, lineStarts, language) {
|
|
1493
|
+
const startLine = lineOf(lineStarts, group[0].start);
|
|
1494
|
+
const endLine = startLine + group.length - 1;
|
|
1495
|
+
const kind = group[0].doc === "inner" ? "inner-line-doc" : "outer-line-doc";
|
|
1496
|
+
const shape = { kind, startLine, endLine };
|
|
1497
|
+
const lines = sourceLines.slice(startLine, endLine + 1);
|
|
1498
|
+
const values = group.map((comment) => comment.value.slice(1));
|
|
1499
|
+
if (isLegal(values.join("\n"), language)) {
|
|
1500
|
+
return wholeComment(shape, lines, "code", "legal comment");
|
|
1501
|
+
}
|
|
1502
|
+
if (LINE_DIRECTIVE.test(values[0])) {
|
|
1503
|
+
return wholeComment(shape, lines, "code", "directive");
|
|
1504
|
+
}
|
|
1505
|
+
const body = lines.map((line, offset) => {
|
|
1506
|
+
const [, marker, space, text] = _nullishCoalesce(/^(\s*\/\/[/!])( ?)(.*)$/.exec(line), () => ( ["", "", "", line]));
|
|
1507
|
+
return {
|
|
1508
|
+
index: startLine + offset,
|
|
1509
|
+
prefix: `${marker} `,
|
|
1510
|
+
text: text.trimEnd(),
|
|
1511
|
+
verbatim: !space && text.length > 0 || LINE_DIRECTIVE.test(text),
|
|
1512
|
+
hardBreak: HARD_BREAK.test(text)
|
|
1513
|
+
};
|
|
1514
|
+
});
|
|
1515
|
+
return { ...shape, paragraphs: segment(body, language) };
|
|
1516
|
+
}
|
|
1517
|
+
function shapeLineComment(group, sourceLines, lineStarts, language) {
|
|
1518
|
+
const startLine = lineOf(lineStarts, group[0].start);
|
|
1519
|
+
const endLine = startLine + group.length - 1;
|
|
1520
|
+
const shape = { kind: "line", startLine, endLine };
|
|
1521
|
+
const lines = sourceLines.slice(startLine, endLine + 1);
|
|
1522
|
+
if (isLegal(group.map((comment) => comment.value).join("\n"), language)) {
|
|
1523
|
+
return wholeComment(shape, lines, "code", "legal comment");
|
|
1524
|
+
}
|
|
1525
|
+
const body = lines.map((line, offset) => {
|
|
1526
|
+
const [, marker, space, text] = _nullishCoalesce(/^(\s*\/\/[/!]*)( ?)(.*)$/.exec(line), () => ( ["", "", "", line]));
|
|
1527
|
+
return {
|
|
1528
|
+
index: startLine + offset,
|
|
1529
|
+
prefix: `${marker}${space}`,
|
|
1530
|
+
text: text.trimEnd(),
|
|
1531
|
+
verbatim: LINE_DIRECTIVE.test(text),
|
|
1532
|
+
hardBreak: HARD_BREAK.test(text)
|
|
1533
|
+
};
|
|
1534
|
+
});
|
|
1535
|
+
return { ...shape, paragraphs: segment(body, { ...language, tags: false }) };
|
|
1245
1536
|
}
|
|
1246
1537
|
function ownsLines(comment, source, lineStarts) {
|
|
1247
1538
|
const startLine = lineOf(lineStarts, comment.start);
|
|
@@ -1251,12 +1542,12 @@ function ownsLines(comment, source, lineStarts) {
|
|
|
1251
1542
|
const after = source.slice(comment.end, lineEnd);
|
|
1252
1543
|
return /^[\t ]*$/.test(before) && /^[\t ]*\r?\n?$/.test(after);
|
|
1253
1544
|
}
|
|
1254
|
-
function singleLineLength(paragraph, options) {
|
|
1545
|
+
function singleLineLength(paragraph, options, language) {
|
|
1255
1546
|
const [first] = paragraph.lines;
|
|
1256
1547
|
if (paragraph.kind === "other" && paragraph.reason === "single-line block comment") {
|
|
1257
1548
|
return sourceLineLength(first, options.tabWidth);
|
|
1258
1549
|
}
|
|
1259
|
-
if (paragraph.kind !== "prose" || structureOf(paragraph.lines)) {
|
|
1550
|
+
if (paragraph.kind !== "prose" || structureOf(paragraph.lines, language)) {
|
|
1260
1551
|
return Infinity;
|
|
1261
1552
|
}
|
|
1262
1553
|
const text = tokenize(paragraph.lines.map((line) => line.text).join(" ")).join(" ");
|
|
@@ -1268,9 +1559,61 @@ function parseSource(filename, source) {
|
|
|
1268
1559
|
}
|
|
1269
1560
|
function describeFailure(errors, lineStarts) {
|
|
1270
1561
|
const [first] = errors;
|
|
1271
|
-
const offset = _optionalChain([first, 'access',
|
|
1562
|
+
const offset = _optionalChain([first, 'access', _46 => _46.labels, 'access', _47 => _47.at, 'call', _48 => _48(0), 'optionalAccess', _49 => _49.start]);
|
|
1272
1563
|
return { message: first.message, line: offset === void 0 ? void 0 : lineOf(lineStarts, offset) + 1 };
|
|
1273
1564
|
}
|
|
1565
|
+
function scanJavaScript(filename, text, lineStarts) {
|
|
1566
|
+
const { program, comments, errors } = parseSource(filename, text);
|
|
1567
|
+
if (errors.length > 0 && program.body.length === 0) {
|
|
1568
|
+
return { comments: [], syntaxError: describeFailure(errors, lineStarts) };
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
comments: comments.filter((comment) => !(comment.start === 0 && text.startsWith("#!")))
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
function scanRustSource(text, lineStarts) {
|
|
1575
|
+
const { comments, error } = scanRust(text);
|
|
1576
|
+
if (error) {
|
|
1577
|
+
return { comments: [], syntaxError: { message: error.message, line: lineOf(lineStarts, error.offset) + 1 } };
|
|
1578
|
+
}
|
|
1579
|
+
return {
|
|
1580
|
+
comments: comments.map((comment) => {
|
|
1581
|
+
return {
|
|
1582
|
+
type: comment.block ? "Block" : "Line",
|
|
1583
|
+
value: text.slice(comment.start + 2, comment.block ? comment.end - 2 : comment.end),
|
|
1584
|
+
start: comment.start,
|
|
1585
|
+
end: comment.end,
|
|
1586
|
+
doc: comment.doc
|
|
1587
|
+
};
|
|
1588
|
+
})
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function groupLineRuns(comments, text, lineStarts, markerOf) {
|
|
1592
|
+
const groups = [];
|
|
1593
|
+
let current;
|
|
1594
|
+
let previousLine = -1;
|
|
1595
|
+
let indent = "";
|
|
1596
|
+
let marker;
|
|
1597
|
+
for (const comment of comments) {
|
|
1598
|
+
const next = comment.type === "Line" ? markerOf(comment) : void 0;
|
|
1599
|
+
if (next === void 0 || !ownsLines(comment, text, lineStarts)) {
|
|
1600
|
+
current = void 0;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
const line = lineOf(lineStarts, comment.start);
|
|
1604
|
+
const before = text.slice(lineStarts[line], comment.start);
|
|
1605
|
+
if (current && line === previousLine + 1 && next === marker && before === indent) {
|
|
1606
|
+
current.push(comment);
|
|
1607
|
+
} else {
|
|
1608
|
+
current = [comment];
|
|
1609
|
+
indent = before;
|
|
1610
|
+
marker = next;
|
|
1611
|
+
groups.push(current);
|
|
1612
|
+
}
|
|
1613
|
+
previousLine = line;
|
|
1614
|
+
}
|
|
1615
|
+
return groups;
|
|
1616
|
+
}
|
|
1274
1617
|
function analyzeSource(source, filename, options) {
|
|
1275
1618
|
return inspectSource(source, filename, options).comments;
|
|
1276
1619
|
}
|
|
@@ -1279,39 +1622,66 @@ function inspectSource(source, filename, options) {
|
|
|
1279
1622
|
throw new Error("comments: maxWidth must be an integer at least as large as width");
|
|
1280
1623
|
}
|
|
1281
1624
|
const text = source.startsWith(BYTE_ORDER_MARK) ? source.slice(1) : source;
|
|
1282
|
-
const
|
|
1625
|
+
const language = languageOf(filename);
|
|
1283
1626
|
const lineStarts = computeLineStarts(text);
|
|
1284
|
-
|
|
1285
|
-
|
|
1627
|
+
const scanned = language.name === "rust" ? scanRustSource(text, lineStarts) : scanJavaScript(filename, text, lineStarts);
|
|
1628
|
+
if (scanned.syntaxError) {
|
|
1629
|
+
return { comments: [], syntaxError: scanned.syntaxError };
|
|
1286
1630
|
}
|
|
1287
1631
|
const sourceLines = text.split("\n").map((line) => stripCarriageReturn(line));
|
|
1288
|
-
const parsed = comments.filter((comment) => !(comment.start === 0 && text.startsWith("#!")));
|
|
1289
1632
|
const reports = [];
|
|
1290
|
-
const
|
|
1291
|
-
return
|
|
1292
|
-
});
|
|
1293
|
-
for (const block of blocks) {
|
|
1294
|
-
const shape = shapeBlock(block, sourceLines, lineStarts);
|
|
1295
|
-
const singleLineWidth = Math.max(
|
|
1296
|
-
options.width,
|
|
1297
|
-
Math.min(_nullishCoalesce(options.singleLineWidth, () => ( options.width)), _nullishCoalesce(options.maxWidth, () => ( Infinity)))
|
|
1298
|
-
);
|
|
1299
|
-
const singleLineLimit = Math.min(singleLineWidth + options.tolerance, _nullishCoalesce(options.maxWidth, () => ( Infinity)));
|
|
1300
|
-
const singleLine = shape.paragraphs.length === 1 ? singleLineLength(shape.paragraphs[0], options) : Infinity;
|
|
1301
|
-
const paragraphOptions = singleLine <= singleLineLimit ? { ...options, width: Math.max(singleLineWidth, singleLine) } : options;
|
|
1302
|
-
const lines = sourceLines.slice(shape.startLine, shape.endLine + 1);
|
|
1303
|
-
reports.push({
|
|
1633
|
+
const report = (shape, lines, paragraphs) => {
|
|
1634
|
+
return {
|
|
1304
1635
|
file: filename,
|
|
1305
1636
|
kind: shape.kind,
|
|
1306
1637
|
startLine: shape.startLine + 1,
|
|
1307
1638
|
endLine: shape.endLine + 1,
|
|
1308
|
-
indent: _nullishCoalesce(_optionalChain([/^[\t ]*/, 'access',
|
|
1639
|
+
indent: _nullishCoalesce(_optionalChain([/^[\t ]*/, 'access', _50 => _50.exec, 'call', _51 => _51(lines[0]), 'optionalAccess', _52 => _52[0]]), () => ( "")),
|
|
1309
1640
|
width: options.width,
|
|
1310
1641
|
maxWidth: options.maxWidth,
|
|
1311
1642
|
lengths: lines.map((line) => visualLength(line.trimEnd(), options.tabWidth)),
|
|
1312
1643
|
text: lines.join("\n"),
|
|
1313
|
-
paragraphs
|
|
1644
|
+
paragraphs
|
|
1645
|
+
};
|
|
1646
|
+
};
|
|
1647
|
+
const blocks = scanned.comments.filter((comment) => {
|
|
1648
|
+
return comment.type === "Block" && ownsLines(comment, text, lineStarts) && !comment.value.includes(IGNORE_DIRECTIVE);
|
|
1649
|
+
});
|
|
1650
|
+
const runs = (markerOf) => {
|
|
1651
|
+
return groupLineRuns(scanned.comments, text, lineStarts, markerOf).filter((group) => {
|
|
1652
|
+
return !group.some((comment) => comment.value.includes(IGNORE_DIRECTIVE));
|
|
1314
1653
|
});
|
|
1654
|
+
};
|
|
1655
|
+
const shapes = [
|
|
1656
|
+
...blocks.map((block) => shapeBlock(block, sourceLines, lineStarts, language)),
|
|
1657
|
+
...runs((comment) => comment.doc).map((group) => shapeLineRun(group, sourceLines, lineStarts, language)),
|
|
1658
|
+
...runs((comment) => comment.doc ? void 0 : "//").map(
|
|
1659
|
+
(group) => shapeLineComment(group, sourceLines, lineStarts, language)
|
|
1660
|
+
)
|
|
1661
|
+
].toSorted((a, b) => a.startLine - b.startLine);
|
|
1662
|
+
for (const shape of shapes) {
|
|
1663
|
+
const lines = sourceLines.slice(shape.startLine, shape.endLine + 1);
|
|
1664
|
+
if (shape.kind === "line") {
|
|
1665
|
+
const paragraphs = shape.paragraphs.map((paragraph) => analyzeLineParagraph(paragraph, options, language));
|
|
1666
|
+
if (paragraphs.some((paragraph) => paragraph.issues.length > 0)) {
|
|
1667
|
+
reports.push(report(shape, lines, paragraphs));
|
|
1668
|
+
}
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
const singleLineWidth = Math.max(
|
|
1672
|
+
options.width,
|
|
1673
|
+
Math.min(_nullishCoalesce(options.singleLineWidth, () => ( options.width)), _nullishCoalesce(options.maxWidth, () => ( Infinity)))
|
|
1674
|
+
);
|
|
1675
|
+
const singleLineLimit = Math.min(singleLineWidth + options.tolerance, _nullishCoalesce(options.maxWidth, () => ( Infinity)));
|
|
1676
|
+
const singleLine = shape.paragraphs.length === 1 ? singleLineLength(shape.paragraphs[0], options, language) : Infinity;
|
|
1677
|
+
const paragraphOptions = singleLine <= singleLineLimit ? { ...options, width: Math.max(singleLineWidth, singleLine) } : options;
|
|
1678
|
+
reports.push(
|
|
1679
|
+
report(
|
|
1680
|
+
shape,
|
|
1681
|
+
lines,
|
|
1682
|
+
shape.paragraphs.map((paragraph) => analyzeParagraph(paragraph, paragraphOptions, language))
|
|
1683
|
+
)
|
|
1684
|
+
);
|
|
1315
1685
|
}
|
|
1316
1686
|
return { comments: reports };
|
|
1317
1687
|
}
|
|
@@ -1332,6 +1702,9 @@ function rewrapComment(report) {
|
|
|
1332
1702
|
}
|
|
1333
1703
|
var POSITION_KEYS = /* @__PURE__ */ new Set(["start", "end", "range", "loc"]);
|
|
1334
1704
|
function codeFingerprint(source, filename) {
|
|
1705
|
+
if (languageOf(filename).name === "rust") {
|
|
1706
|
+
return rustFingerprint(source);
|
|
1707
|
+
}
|
|
1335
1708
|
const { program, errors } = parseSource(filename, source);
|
|
1336
1709
|
if (errors.length > 0) {
|
|
1337
1710
|
return JSON.stringify({ source, errors: errors.map((error) => error.message) });
|
|
@@ -1344,8 +1717,8 @@ function codeFingerprint(source, filename) {
|
|
|
1344
1717
|
|
|
1345
1718
|
// src/comments/agent.ts
|
|
1346
1719
|
var _child_process = require('child_process');
|
|
1347
|
-
|
|
1348
|
-
|
|
1720
|
+
|
|
1721
|
+
|
|
1349
1722
|
var DEFAULT_RULES = `
|
|
1350
1723
|
# Comment rules
|
|
1351
1724
|
|
|
@@ -1353,7 +1726,7 @@ var DEFAULT_RULES = `
|
|
|
1353
1726
|
|
|
1354
1727
|
When rules pull in different directions, the earlier one wins.
|
|
1355
1728
|
|
|
1356
|
-
1. Keep code and exact text byte for byte, including every punctuation character in it. That covers code blocks, \`@example\`, code spans, identifiers, string literals like error messages, regexes, globs and other patterns like \`*test*\`, URLs, quoted text, titles,
|
|
1729
|
+
1. Keep code and exact text byte for byte, including every punctuation character in it. That covers code blocks, \`@example\`, code spans, identifiers, string literals like error messages, regexes, globs and other patterns like \`*test*\`, URLs, quoted text, titles, notation like \`host:port\`, and Rust intra-doc links (see below).
|
|
1357
1730
|
2. Keep the meaning. An edit may make a comment clearer, never different.
|
|
1358
1731
|
3. Follow the style preferences below.
|
|
1359
1732
|
4. Change as little as possible. Wording that already reads clearly stays, even when a note points at it.
|
|
@@ -1366,10 +1739,12 @@ When rules pull in different directions, the earlier one wins.
|
|
|
1366
1739
|
- A comment whose whole text is one paragraph stays on a single line when that line fits a separate, larger single-line width, even past the width. Only a longer comment wraps to the width.
|
|
1367
1740
|
- Separate paragraphs with an empty comment line.
|
|
1368
1741
|
- Never split a backtick code span across lines.
|
|
1742
|
+
- Never split a Markdown link across lines. A link is the text in brackets and the target right after it, like \`[the docs](https://example.com)\`, \`[the docs][label]\`, or \`[the docs]\`, and all of it stays on one line, from its first bracket to its last bracket or parenthesis. An editor can't follow a link that continues on the next line. When a line is too long, break it before or after the link.
|
|
1369
1743
|
- Keep code blocks, \`@example\`, lists, tables, box drawings, aligned columns, and Markdown hard line breaks (a line that ends with two spaces or a backslash) laid out as they are. List items wrap with their continuation lines indented to the item text, and a JSDoc tag description that doesn't fit continues on the next line without indentation.
|
|
1370
1744
|
- The tool measures every line, taking the tolerance and the parts it keeps as written into account, and the notes on each comment list what is still wrong. Act on those notes. Don't count columns, and don't apply a stricter limit than the notes show.
|
|
1371
1745
|
- The maximum is a hard limit for measured prose, including long tokens. If a URL, code span, or other exact text exceeds it, preserve that text and explain the unresolved overflow instead of changing its contents.
|
|
1372
1746
|
- The tool never rewraps the parts it keeps as written, like a single-line block comment, a paragraph with hard line breaks, or lines that may be laid out by hand. When a note says one of their prose lines is too long, break or shorten it yourself. Exact text and intentional layout win over the width, so keep code, code spans, and the layout in them intact, and if a note can only be fixed by changing them, leave it and say why in your reply.
|
|
1747
|
+
- The tool never rewraps a line comment (\`//\`) either. When a note says one of its lines is too long, shorten the wording so the comment fits on its line and still says the same thing. When it can't be shortened without losing meaning, turn it into a multi-line block comment instead: \`/**\` in JavaScript and TypeScript, or \`/*\` in Rust, on its own line, then the text on lines that start with \` * \`, then \` */\` on its own line, all at the indentation of the line comment. Like other block comments, it starts with a capital letter and ends with punctuation. Leave its line breaks to the tool, which rewraps it like any other block comment. Never split a line comment into more \`//\` lines.
|
|
1373
1748
|
|
|
1374
1749
|
## Style
|
|
1375
1750
|
|
|
@@ -1380,8 +1755,16 @@ These are preferences for comment prose. They never apply to code or exact text
|
|
|
1380
1755
|
- Prefer a sentence over a "Label: explanation" fragment, like \`...the socket. Optional: without it...\` or \`Default: refuse\` in the middle of prose. A colon is fine where it introduces a list, code block, or example, in a term's definition, in notation, in a quotation, and in a \`Note:\` or \`Optional:\` that starts a comment. A JSDoc tag like \`@note\` or \`@todo\` can replace a label, and \`@default\` fits a default value, not a description.
|
|
1381
1756
|
- Avoid asterisk emphasis like \`*this*\`, and use plain wording instead. A name or pattern in asterisks, like \`*test*\` or \`*.spec.ts\`, is a pattern, so keep its asterisks and wrap it in a code span, like \`\` \`*test*\` \`\`.
|
|
1382
1757
|
- Avoid section banner comments, i.e. dividers or headings made of dashes, equals signs, asterisks, or other repeated characters that split a file into visual sections. Let the code structure carry the grouping, or write one short, plain sentence comment (e.g. \`// handlers for control mode\`). Rules that belong to a diagram, a table, a box drawing, a Markdown document, an example, or a legal header aren't banners.
|
|
1383
|
-
- Block and
|
|
1384
|
-
- Never turn a block or JSDoc comment into \`//\` line comments, and never write a run of consecutive \`//\` lines.
|
|
1758
|
+
- Block, JSDoc, and Rust doc comments are capitalized and end with punctuation.
|
|
1759
|
+
- Never change the kind of a comment, except to turn a line comment that is too long into a block comment (see Layout). Don't turn a block or JSDoc comment into \`//\` line comments, and never write a run of consecutive \`//\` lines. In Rust, a run of \`///\` or \`//!\` lines is one doc comment, so keep its markers, and never turn a doc comment (\`///\`, \`//!\`, \`/**\`, \`/*!\`) into another kind, since that changes or removes documentation.
|
|
1760
|
+
|
|
1761
|
+
## Rust doc comments
|
|
1762
|
+
|
|
1763
|
+
Rust doc comments are Markdown that rustdoc renders.
|
|
1764
|
+
|
|
1765
|
+
- Intra-doc links are exact text. Keep their brackets, backticks, and targets, like \`\` [\`count_provider_reads\`] \`\`, \`\` [\`permissions\`](crate::permissions) \`\`, \`\` [the parser][\`Parser\`] \`\`, or \`[Vec<T>]\`, since rustdoc resolves the text in brackets, or the target after it, as a path. Each link stays on one line (see Layout), including a link with spaces in its text, like \`[the provider module](crate::provider)\`.
|
|
1766
|
+
- A link reference definition, like \`\` [\`Foo\`]: crate::Foo \`\`, stays on its own line as it is. Its colon is syntax, not a label.
|
|
1767
|
+
- Headings like \`# Examples\`, \`# Panics\`, \`# Errors\`, and \`# Safety\` are sections that rustdoc and readers look for, so keep them. Code blocks are doctests, so keep their lines, including hidden lines that start with \`#\`, exactly as they are.
|
|
1385
1768
|
|
|
1386
1769
|
## Editing
|
|
1387
1770
|
|
|
@@ -1391,10 +1774,10 @@ These are preferences for comment prose. They never apply to code or exact text
|
|
|
1391
1774
|
|
|
1392
1775
|
// src/comments/config.ts
|
|
1393
1776
|
|
|
1394
|
-
var _module = require('module');
|
|
1395
1777
|
|
|
1396
|
-
|
|
1397
|
-
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
var DEFAULT_EXTENSIONS = Object.freeze([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts", ".rs"]);
|
|
1398
1781
|
var CONFIG_FILES = [
|
|
1399
1782
|
"comments.config.ts",
|
|
1400
1783
|
"comments.config.mts",
|
|
@@ -1445,10 +1828,12 @@ function validateConfig(raw, filepath) {
|
|
|
1445
1828
|
assertType(config.avoidSentenceOrphans, "boolean", "avoidSentenceOrphans", filepath);
|
|
1446
1829
|
assertType(config.style, "boolean", "style", filepath);
|
|
1447
1830
|
assertType(config.extensions, "array", "extensions", filepath);
|
|
1831
|
+
assertType(config.include, "array", "include", filepath);
|
|
1448
1832
|
assertType(config.ignore, "array", "ignore", filepath);
|
|
1449
1833
|
assertType(config.agent, "object", "agent", filepath);
|
|
1450
1834
|
assertType(config.hook, "object", "hook", filepath);
|
|
1451
1835
|
assertStrings(config.extensions, "extensions", filepath);
|
|
1836
|
+
assertStrings(config.include, "include", filepath);
|
|
1452
1837
|
assertStrings(config.ignore, "ignore", filepath);
|
|
1453
1838
|
for (const key of ["width", "maxWidth", "singleLineWidth", "tolerance"]) {
|
|
1454
1839
|
const value = config[key];
|
|
@@ -1481,8 +1866,8 @@ function validateConfig(raw, filepath) {
|
|
|
1481
1866
|
}
|
|
1482
1867
|
assertType(agent.rules, "string", "agent.rules", filepath);
|
|
1483
1868
|
assertType(agent.instructions, "string", "agent.instructions", filepath);
|
|
1484
|
-
if (agent.scope !== void 0 &&
|
|
1485
|
-
throw new Error(`comments: \`agent.scope\` in ${filepath} must be
|
|
1869
|
+
if (agent.scope !== void 0 && !["suspicious", "failing", "all"].includes(agent.scope)) {
|
|
1870
|
+
throw new Error(`comments: \`agent.scope\` in ${filepath} must be one of suspicious, failing, all`);
|
|
1486
1871
|
}
|
|
1487
1872
|
const hook = _nullishCoalesce(config.hook, () => ( {}));
|
|
1488
1873
|
if (hook.mode !== void 0 && !["agent", "report", "off"].includes(hook.mode)) {
|