eslint-plugin-markdown-preferences 0.16.0 → 0.18.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 +10 -2
- package/lib/index.d.ts +87 -7
- package/lib/index.js +1307 -149
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -73,23 +73,30 @@ function getListItemMarker(sourceCode, node) {
|
|
|
73
73
|
if (matchDot) return {
|
|
74
74
|
kind: ".",
|
|
75
75
|
raw: matchDot[0],
|
|
76
|
-
sequence:
|
|
76
|
+
sequence: {
|
|
77
|
+
value: Number(matchDot[1]),
|
|
78
|
+
raw: matchDot[1]
|
|
79
|
+
}
|
|
77
80
|
};
|
|
78
81
|
const matchParen = /^(\d+)\)/.exec(text);
|
|
79
82
|
return {
|
|
80
83
|
kind: ")",
|
|
81
84
|
raw: matchParen[0],
|
|
82
|
-
sequence:
|
|
85
|
+
sequence: {
|
|
86
|
+
value: Number(matchParen[1]),
|
|
87
|
+
raw: matchParen[1]
|
|
88
|
+
}
|
|
83
89
|
};
|
|
84
90
|
}
|
|
85
91
|
/**
|
|
86
92
|
* Get the marker for a thematic break.
|
|
87
93
|
*/
|
|
88
94
|
function getThematicBreakMarker(sourceCode, node) {
|
|
89
|
-
const text = sourceCode.getText(node);
|
|
95
|
+
const text = sourceCode.getText(node).trimEnd();
|
|
90
96
|
return {
|
|
91
|
-
kind: text.startsWith("-") ? "-" : "*",
|
|
92
|
-
hasSpaces: /\s/u.test(text)
|
|
97
|
+
kind: text.startsWith("-") ? "-" : text.startsWith("*") ? "*" : "_",
|
|
98
|
+
hasSpaces: /\s/u.test(text),
|
|
99
|
+
text
|
|
93
100
|
};
|
|
94
101
|
}
|
|
95
102
|
/**
|
|
@@ -126,55 +133,138 @@ function getSourceLocationFromRange(sourceCode, node, range) {
|
|
|
126
133
|
//#endregion
|
|
127
134
|
//#region src/utils/atx-heading.ts
|
|
128
135
|
/**
|
|
129
|
-
* Parse the
|
|
136
|
+
* Parse the ATX heading.
|
|
130
137
|
*/
|
|
131
|
-
function
|
|
138
|
+
function parseATXHeading(sourceCode, node) {
|
|
132
139
|
if (getHeadingKind(sourceCode, node) !== "atx") return null;
|
|
133
140
|
const loc = sourceCode.getLoc(node);
|
|
134
141
|
const range = sourceCode.getRange(node);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
const text = sourceCode.text.slice(...range);
|
|
143
|
+
const parsedOpening = parseATXHeadingOpeningSequenceFromText(text);
|
|
144
|
+
if (parsedOpening === null) return null;
|
|
145
|
+
const openingSequence = {
|
|
146
|
+
text: parsedOpening.openingSequence,
|
|
147
|
+
range: [range[0], range[0] + parsedOpening.openingSequence.length],
|
|
148
|
+
loc: {
|
|
149
|
+
start: loc.start,
|
|
150
|
+
end: {
|
|
151
|
+
line: loc.start.line,
|
|
152
|
+
column: loc.start.column + parsedOpening.openingSequence.length
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const spaceAfterOpening = {
|
|
157
|
+
text: parsedOpening.rawAfter,
|
|
158
|
+
range: [openingSequence.range[1], openingSequence.range[1] + parsedOpening.rawAfter.length],
|
|
159
|
+
loc: {
|
|
160
|
+
start: openingSequence.loc.end,
|
|
161
|
+
end: {
|
|
162
|
+
line: openingSequence.loc.end.line,
|
|
163
|
+
column: openingSequence.loc.end.column + parsedOpening.rawAfter.length
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const parsedClosing = parseATXHeadingClosingSequenceFromText(text);
|
|
168
|
+
if (parsedClosing == null) {
|
|
169
|
+
const textAfterOpening = sourceCode.text.slice(spaceAfterOpening.range[1], range[1]);
|
|
170
|
+
const contentText$1 = textAfterOpening.trimEnd();
|
|
171
|
+
return {
|
|
172
|
+
openingSequence: {
|
|
173
|
+
...openingSequence,
|
|
174
|
+
raws: { spaceAfter: spaceAfterOpening }
|
|
175
|
+
},
|
|
176
|
+
content: {
|
|
177
|
+
text: contentText$1,
|
|
178
|
+
range: [spaceAfterOpening.range[1], spaceAfterOpening.range[1] + contentText$1.length],
|
|
179
|
+
loc: {
|
|
180
|
+
start: spaceAfterOpening.loc.end,
|
|
181
|
+
end: {
|
|
182
|
+
line: loc.end.line,
|
|
183
|
+
column: loc.end.column - (textAfterOpening.length - contentText$1.length)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
closingSequence: null
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const spaceAfterClosing = {
|
|
191
|
+
text: parsedClosing.rawAfter,
|
|
192
|
+
range: [range[1] - parsedClosing.rawAfter.length, range[1]],
|
|
140
193
|
loc: {
|
|
141
194
|
start: {
|
|
142
195
|
line: loc.end.line,
|
|
143
|
-
column: loc.end.column -
|
|
196
|
+
column: loc.end.column - parsedClosing.rawAfter.length
|
|
144
197
|
},
|
|
145
|
-
end:
|
|
146
|
-
line: loc.end.line,
|
|
147
|
-
column: loc.end.column
|
|
148
|
-
}
|
|
198
|
+
end: loc.end
|
|
149
199
|
}
|
|
150
200
|
};
|
|
151
201
|
const closingSequence = {
|
|
152
|
-
text:
|
|
153
|
-
range: [
|
|
202
|
+
text: parsedClosing.closingSequence,
|
|
203
|
+
range: [spaceAfterClosing.range[0] - parsedClosing.closingSequence.length, spaceAfterClosing.range[0]],
|
|
154
204
|
loc: {
|
|
155
205
|
start: {
|
|
156
|
-
line:
|
|
157
|
-
column:
|
|
206
|
+
line: spaceAfterClosing.loc.start.line,
|
|
207
|
+
column: spaceAfterClosing.loc.start.column - parsedClosing.closingSequence.length
|
|
158
208
|
},
|
|
159
|
-
end:
|
|
209
|
+
end: spaceAfterClosing.loc.start
|
|
160
210
|
}
|
|
161
211
|
};
|
|
162
|
-
const
|
|
163
|
-
text:
|
|
164
|
-
range: [closingSequence.range[0] -
|
|
212
|
+
const spaceBeforeClosing = {
|
|
213
|
+
text: parsedClosing.rawBefore,
|
|
214
|
+
range: [closingSequence.range[0] - parsedClosing.rawBefore.length, closingSequence.range[0]],
|
|
165
215
|
loc: {
|
|
166
216
|
start: {
|
|
167
217
|
line: closingSequence.loc.start.line,
|
|
168
|
-
column: closingSequence.loc.start.column -
|
|
218
|
+
column: closingSequence.loc.start.column - parsedClosing.rawBefore.length
|
|
169
219
|
},
|
|
170
220
|
end: closingSequence.loc.start
|
|
171
221
|
}
|
|
172
222
|
};
|
|
223
|
+
const contentText = sourceCode.text.slice(spaceAfterOpening.range[1], spaceBeforeClosing.range[0]);
|
|
224
|
+
return {
|
|
225
|
+
openingSequence: {
|
|
226
|
+
...openingSequence,
|
|
227
|
+
raws: { spaceAfter: spaceAfterOpening }
|
|
228
|
+
},
|
|
229
|
+
content: {
|
|
230
|
+
text: contentText,
|
|
231
|
+
range: [spaceAfterOpening.range[1], spaceBeforeClosing.range[0]],
|
|
232
|
+
loc: {
|
|
233
|
+
start: spaceAfterOpening.loc.end,
|
|
234
|
+
end: spaceBeforeClosing.loc.start
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
closingSequence: {
|
|
238
|
+
...closingSequence,
|
|
239
|
+
raws: {
|
|
240
|
+
spaceBefore: spaceBeforeClosing,
|
|
241
|
+
spaceAfter: spaceAfterClosing
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Parse the opening sequence from a text string.
|
|
248
|
+
*/
|
|
249
|
+
function parseATXHeadingOpeningSequenceFromText(text) {
|
|
250
|
+
if (!text.startsWith("#")) return null;
|
|
251
|
+
let openingSequenceAfterOffset = 1;
|
|
252
|
+
while (openingSequenceAfterOffset < text.length && text[openingSequenceAfterOffset] === "#") openingSequenceAfterOffset++;
|
|
253
|
+
const afterOffset = skipWhitespace(openingSequenceAfterOffset);
|
|
254
|
+
if (afterOffset === openingSequenceAfterOffset || afterOffset >= text.length) return null;
|
|
173
255
|
return {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
rawAfter
|
|
256
|
+
openingSequence: text.slice(0, openingSequenceAfterOffset),
|
|
257
|
+
rawAfter: text.slice(openingSequenceAfterOffset, afterOffset)
|
|
177
258
|
};
|
|
259
|
+
/**
|
|
260
|
+
* Skip whitespace characters at the start of the text.
|
|
261
|
+
*/
|
|
262
|
+
function skipWhitespace(index) {
|
|
263
|
+
let result = index;
|
|
264
|
+
let c;
|
|
265
|
+
while (result < text.length && (c = text[result]) && (c === " " || c === " ")) result++;
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
178
268
|
}
|
|
179
269
|
/**
|
|
180
270
|
* Parse the closing sequence from a text string.
|
|
@@ -255,9 +345,23 @@ function getParsedLines(sourceCode) {
|
|
|
255
345
|
}
|
|
256
346
|
|
|
257
347
|
//#endregion
|
|
258
|
-
//#region src/
|
|
348
|
+
//#region src/utils/get-text-width.ts
|
|
259
349
|
let segmenter;
|
|
260
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Get the width of a text string.
|
|
352
|
+
*/
|
|
353
|
+
function getTextWidth(text) {
|
|
354
|
+
if (!text.includes(" ")) return stringWidth(text);
|
|
355
|
+
if (!segmenter) segmenter = new Intl.Segmenter("en");
|
|
356
|
+
let width = 0;
|
|
357
|
+
for (const { segment: c } of segmenter.segment(text)) if (c === " ") width += 4 - width % 4;
|
|
358
|
+
else width += stringWidth(c);
|
|
359
|
+
return width;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region src/rules/atx-heading-closing-sequence-length.ts
|
|
364
|
+
var atx_heading_closing_sequence_length_default = createRule("atx-heading-closing-sequence-length", {
|
|
261
365
|
meta: {
|
|
262
366
|
type: "layout",
|
|
263
367
|
docs: {
|
|
@@ -292,14 +396,11 @@ var atx_headings_closing_sequence_length_default = createRule("atx-headings-clos
|
|
|
292
396
|
/**
|
|
293
397
|
* Verify the closing sequence length of an ATX heading.
|
|
294
398
|
*/
|
|
295
|
-
function verifyATXHeadingClosingSequenceLength(
|
|
296
|
-
const parsed = parseATXHeadingClosingSequence(sourceCode, node);
|
|
297
|
-
if (!parsed || parsed.closingSequence == null) return;
|
|
399
|
+
function verifyATXHeadingClosingSequenceLength(parsed, reportNode, expectedLength) {
|
|
298
400
|
const actualLength = parsed.closingSequence.text.length;
|
|
299
|
-
const expectedLength = getExpected(node, parsed);
|
|
300
401
|
if (expectedLength === actualLength) return;
|
|
301
402
|
context.report({
|
|
302
|
-
node,
|
|
403
|
+
node: reportNode,
|
|
303
404
|
loc: parsed.closingSequence.loc,
|
|
304
405
|
messageId: "wrongClosingLength",
|
|
305
406
|
data: {
|
|
@@ -311,40 +412,41 @@ var atx_headings_closing_sequence_length_default = createRule("atx-headings-clos
|
|
|
311
412
|
}
|
|
312
413
|
});
|
|
313
414
|
}
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
415
|
+
if (option.mode === "match-opening") return { heading(node) {
|
|
416
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
417
|
+
if (!parsed || parsed.closingSequence == null) return;
|
|
418
|
+
verifyATXHeadingClosingSequenceLength(parsed, node, node.depth);
|
|
419
|
+
} };
|
|
420
|
+
if (option.mode === "length") {
|
|
320
421
|
const expected = option.length || 2;
|
|
321
|
-
const getExpected = () => expected;
|
|
322
422
|
return { heading(node) {
|
|
323
|
-
|
|
423
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
424
|
+
if (!parsed || parsed.closingSequence == null) return;
|
|
425
|
+
verifyATXHeadingClosingSequenceLength(parsed, node, expected);
|
|
324
426
|
} };
|
|
325
|
-
}
|
|
427
|
+
}
|
|
428
|
+
if (option.mode === "fixed-line-length") {
|
|
326
429
|
const totalLength = option.length || 80;
|
|
327
|
-
const getExpected = (_node, parsed) => {
|
|
328
|
-
return totalLength - getContentLength(parsed);
|
|
329
|
-
};
|
|
330
430
|
return { heading(node) {
|
|
331
|
-
|
|
431
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
432
|
+
if (!parsed || parsed.closingSequence == null) return;
|
|
433
|
+
verifyATXHeadingClosingSequenceLength(parsed, node, totalLength - getContentLength(parsed));
|
|
332
434
|
} };
|
|
333
|
-
}
|
|
334
|
-
|
|
435
|
+
}
|
|
436
|
+
if (option.mode === "consistent") {
|
|
437
|
+
let expectedLength = null;
|
|
335
438
|
return { heading(node) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
getExpected = () => expected;
|
|
341
|
-
} else verifyATXHeadingClosingSequenceLength(node, getExpected);
|
|
439
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
440
|
+
if (!parsed || parsed.closingSequence == null) return;
|
|
441
|
+
if (expectedLength == null) expectedLength = parsed.closingSequence.text.length;
|
|
442
|
+
else verifyATXHeadingClosingSequenceLength(parsed, node, expectedLength);
|
|
342
443
|
} };
|
|
343
|
-
}
|
|
444
|
+
}
|
|
445
|
+
if (option.mode === "consistent-line-length") {
|
|
344
446
|
const headings = [];
|
|
345
447
|
return {
|
|
346
448
|
heading(node) {
|
|
347
|
-
const parsed =
|
|
449
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
348
450
|
if (!parsed || !parsed.closingSequence) return;
|
|
349
451
|
headings.push({
|
|
350
452
|
node,
|
|
@@ -370,13 +472,11 @@ var atx_headings_closing_sequence_length_default = createRule("atx-headings-clos
|
|
|
370
472
|
const lineLength = getLineLength(heading.parsed);
|
|
371
473
|
if (mostLongContentHeading.contentLength < lineLength && lineLength < minLineLength) minLineLength = Math.min(minLineLength, lineLength);
|
|
372
474
|
}
|
|
373
|
-
const
|
|
374
|
-
return minLineLength - getContentLength(parsed);
|
|
375
|
-
};
|
|
376
|
-
for (const { node } of headings) verifyATXHeadingClosingSequenceLength(node, getExpected);
|
|
475
|
+
for (const heading of headings) verifyATXHeadingClosingSequenceLength(heading.parsed, heading.node, minLineLength - getContentLength(heading.parsed));
|
|
377
476
|
}
|
|
378
477
|
};
|
|
379
|
-
}
|
|
478
|
+
}
|
|
479
|
+
return {};
|
|
380
480
|
/**
|
|
381
481
|
* Get the content length of the heading.
|
|
382
482
|
*/
|
|
@@ -397,21 +497,10 @@ var atx_headings_closing_sequence_length_default = createRule("atx-headings-clos
|
|
|
397
497
|
}
|
|
398
498
|
}
|
|
399
499
|
});
|
|
400
|
-
/**
|
|
401
|
-
* Get the width of a text string.
|
|
402
|
-
*/
|
|
403
|
-
function getTextWidth(text) {
|
|
404
|
-
if (!text.includes(" ")) return stringWidth(text);
|
|
405
|
-
if (!segmenter) segmenter = new Intl.Segmenter("en");
|
|
406
|
-
let width = 0;
|
|
407
|
-
for (const { segment: c } of segmenter.segment(text)) if (c === " ") width += 4 - width % 4;
|
|
408
|
-
else width += stringWidth(c);
|
|
409
|
-
return width;
|
|
410
|
-
}
|
|
411
500
|
|
|
412
501
|
//#endregion
|
|
413
|
-
//#region src/rules/atx-
|
|
414
|
-
var
|
|
502
|
+
//#region src/rules/atx-heading-closing-sequence.ts
|
|
503
|
+
var atx_heading_closing_sequence_default = createRule("atx-heading-closing-sequence", {
|
|
415
504
|
meta: {
|
|
416
505
|
type: "layout",
|
|
417
506
|
docs: {
|
|
@@ -439,7 +528,7 @@ var atx_headings_closing_sequence_default = createRule("atx-headings-closing-seq
|
|
|
439
528
|
if (opt.closingSequence === "always" || opt.closingSequence === "never") closingSequence = opt.closingSequence;
|
|
440
529
|
}
|
|
441
530
|
return { heading(node) {
|
|
442
|
-
const parsed =
|
|
531
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
443
532
|
if (!parsed) return;
|
|
444
533
|
if (closingSequence === "always") {
|
|
445
534
|
if (parsed.closingSequence) return;
|
|
@@ -456,12 +545,12 @@ var atx_headings_closing_sequence_default = createRule("atx-headings-closing-seq
|
|
|
456
545
|
context.report({
|
|
457
546
|
node,
|
|
458
547
|
loc: {
|
|
459
|
-
start: parsed.
|
|
548
|
+
start: parsed.closingSequence.raws.spaceBefore.loc.start,
|
|
460
549
|
end: parsed.closingSequence.loc.end
|
|
461
550
|
},
|
|
462
551
|
messageId: "forbidClosing",
|
|
463
552
|
*fix(fixer) {
|
|
464
|
-
const removeRange = [parsed.
|
|
553
|
+
const removeRange = [parsed.closingSequence.raws.spaceBefore.range[0], parsed.closingSequence.range[1]];
|
|
465
554
|
const newHeadingText = sourceCode.text.slice(sourceCode.getRange(node)[0], removeRange[0]);
|
|
466
555
|
const newHeadingParsed = parseATXHeadingClosingSequenceFromText(newHeadingText);
|
|
467
556
|
if (newHeadingParsed) {
|
|
@@ -593,6 +682,176 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
|
|
|
593
682
|
}
|
|
594
683
|
});
|
|
595
684
|
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region src/rules/bullet-list-marker-style.ts
|
|
687
|
+
const MARKERS$1 = [
|
|
688
|
+
"-",
|
|
689
|
+
"*",
|
|
690
|
+
"+"
|
|
691
|
+
];
|
|
692
|
+
/**
|
|
693
|
+
* Get the other marker.
|
|
694
|
+
*/
|
|
695
|
+
function getOtherMarker(unavailableMarker) {
|
|
696
|
+
return MARKERS$1.find((mark) => unavailableMarker !== mark);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Parse rule options.
|
|
700
|
+
*/
|
|
701
|
+
function parseOptions$1(options) {
|
|
702
|
+
const primary = options.primary || "-";
|
|
703
|
+
const secondary = options.secondary || getOtherMarker(primary);
|
|
704
|
+
if (primary === secondary) throw new Error(`\`primary\` and \`secondary\` cannot be the same (primary: "${primary}", secondary: "${secondary}").`);
|
|
705
|
+
const overrides = (options.overrides ?? []).map((override, index) => {
|
|
706
|
+
const primaryForOverride = override.primary || "-";
|
|
707
|
+
const secondaryForOverride = override.secondary || getOtherMarker(primaryForOverride);
|
|
708
|
+
if (primaryForOverride === secondaryForOverride) throw new Error(`overrides[${index}]: \`primary\` and \`secondary\` cannot be the same (primary: "${primaryForOverride}", secondary: "${secondaryForOverride}").`);
|
|
709
|
+
return {
|
|
710
|
+
level: override.level,
|
|
711
|
+
parentMarker: override.parentMarker ?? "any",
|
|
712
|
+
primary: primaryForOverride,
|
|
713
|
+
secondary: secondaryForOverride
|
|
714
|
+
};
|
|
715
|
+
}).reverse();
|
|
716
|
+
return { get(level, parentMarker) {
|
|
717
|
+
for (const o of overrides) if ((o.level == null || o.level === level) && (o.parentMarker === "any" || o.parentMarker === parentMarker)) return {
|
|
718
|
+
primary: o.primary,
|
|
719
|
+
secondary: o.secondary
|
|
720
|
+
};
|
|
721
|
+
return {
|
|
722
|
+
primary,
|
|
723
|
+
secondary
|
|
724
|
+
};
|
|
725
|
+
} };
|
|
726
|
+
}
|
|
727
|
+
var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
728
|
+
meta: {
|
|
729
|
+
type: "layout",
|
|
730
|
+
docs: {
|
|
731
|
+
description: "enforce consistent bullet list (unordered list) marker style",
|
|
732
|
+
categories: [],
|
|
733
|
+
listCategory: "Stylistic"
|
|
734
|
+
},
|
|
735
|
+
fixable: "code",
|
|
736
|
+
hasSuggestions: false,
|
|
737
|
+
schema: [{
|
|
738
|
+
type: "object",
|
|
739
|
+
properties: {
|
|
740
|
+
primary: { enum: MARKERS$1 },
|
|
741
|
+
secondary: { enum: [...MARKERS$1, "any"] },
|
|
742
|
+
overrides: {
|
|
743
|
+
type: "array",
|
|
744
|
+
items: {
|
|
745
|
+
type: "object",
|
|
746
|
+
properties: {
|
|
747
|
+
level: {
|
|
748
|
+
type: "integer",
|
|
749
|
+
minimum: 1
|
|
750
|
+
},
|
|
751
|
+
parentMarker: { enum: [
|
|
752
|
+
...MARKERS$1,
|
|
753
|
+
"any",
|
|
754
|
+
"ordered"
|
|
755
|
+
] },
|
|
756
|
+
primary: { enum: MARKERS$1 },
|
|
757
|
+
secondary: { enum: [...MARKERS$1, "any"] }
|
|
758
|
+
},
|
|
759
|
+
additionalProperties: false
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
additionalProperties: false
|
|
764
|
+
}],
|
|
765
|
+
messages: { unexpected: "Bullet list marker should be '{{marker}}'." }
|
|
766
|
+
},
|
|
767
|
+
create(context) {
|
|
768
|
+
const sourceCode = context.sourceCode;
|
|
769
|
+
const options = parseOptions$1(context.options[0] || {});
|
|
770
|
+
let containerStack = {
|
|
771
|
+
node: sourceCode.ast,
|
|
772
|
+
level: 1,
|
|
773
|
+
upper: null
|
|
774
|
+
};
|
|
775
|
+
/**
|
|
776
|
+
* Check bullet list marker style
|
|
777
|
+
*/
|
|
778
|
+
function checkBulletList(node) {
|
|
779
|
+
let parentMarker;
|
|
780
|
+
if (containerStack.node.type === "listItem") {
|
|
781
|
+
const parentMarkerKind = getListItemMarker(sourceCode, containerStack.node).kind;
|
|
782
|
+
parentMarker = parentMarkerKind === "." || parentMarkerKind === ")" ? "ordered" : parentMarkerKind;
|
|
783
|
+
} else parentMarker = "top";
|
|
784
|
+
const { primary, secondary } = options.get(containerStack.level, parentMarker);
|
|
785
|
+
const nodeIndex = containerStack.node.children.indexOf(node);
|
|
786
|
+
if (nodeIndex === -1) return;
|
|
787
|
+
const prevNode = nodeIndex > 0 ? containerStack.node.children[nodeIndex - 1] : null;
|
|
788
|
+
const prevBulletList = prevNode && (prevNode.type === "list" ? prevNode : null);
|
|
789
|
+
const prevBulletListMarker = prevBulletList && getListItemMarker(sourceCode, prevBulletList);
|
|
790
|
+
const expectedMarker = prevBulletListMarker?.kind !== primary ? primary : secondary;
|
|
791
|
+
if (expectedMarker === "any") return;
|
|
792
|
+
const marker = getListItemMarker(sourceCode, node);
|
|
793
|
+
if (marker.kind === expectedMarker) return;
|
|
794
|
+
const loc = sourceCode.getLoc(node);
|
|
795
|
+
context.report({
|
|
796
|
+
node,
|
|
797
|
+
loc: {
|
|
798
|
+
start: loc.start,
|
|
799
|
+
end: {
|
|
800
|
+
line: loc.start.line,
|
|
801
|
+
column: loc.start.column + marker.raw.length
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
messageId: "unexpected",
|
|
805
|
+
data: { marker: expectedMarker },
|
|
806
|
+
*fix(fixer) {
|
|
807
|
+
if (prevNode?.type === "list" && prevBulletListMarker && (prevBulletListMarker.kind === "-" || prevBulletListMarker.kind === "*" || prevBulletListMarker.kind === "+")) yield fixMarker(prevNode.children[0], prevBulletListMarker.kind);
|
|
808
|
+
yield* fixMarkers(node, expectedMarker);
|
|
809
|
+
let prevMarker = expectedMarker;
|
|
810
|
+
for (let index = nodeIndex + 1; index < containerStack.node.children.length; index++) {
|
|
811
|
+
const nextNode = containerStack.node.children[index];
|
|
812
|
+
if (nextNode.type !== "list") break;
|
|
813
|
+
const nextMarker = getListItemMarker(sourceCode, nextNode);
|
|
814
|
+
if (nextMarker.kind !== prevMarker) break;
|
|
815
|
+
let expectedNextMarker = prevMarker === primary ? secondary : primary;
|
|
816
|
+
if (expectedNextMarker === "any") expectedNextMarker = getOtherMarker(prevMarker);
|
|
817
|
+
yield* fixMarkers(nextNode, expectedNextMarker);
|
|
818
|
+
prevMarker = expectedNextMarker;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Fix bullet list markers
|
|
822
|
+
*/
|
|
823
|
+
function* fixMarkers(list, replacementMarker) {
|
|
824
|
+
for (const item of list.children) yield fixMarker(item, replacementMarker);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Fix bullet list item marker
|
|
828
|
+
*/
|
|
829
|
+
function fixMarker(item, replacementMarker) {
|
|
830
|
+
const range = sourceCode.getRange(item);
|
|
831
|
+
return fixer.replaceTextRange([range[0], range[0] + 1], replacementMarker);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
list(node) {
|
|
838
|
+
if (node.ordered) return;
|
|
839
|
+
checkBulletList(node);
|
|
840
|
+
},
|
|
841
|
+
"root, blockquote, listItem, footnoteDefinition"(node) {
|
|
842
|
+
containerStack = {
|
|
843
|
+
node,
|
|
844
|
+
level: node.type === "listItem" ? containerStack.level + 1 : 1,
|
|
845
|
+
upper: containerStack
|
|
846
|
+
};
|
|
847
|
+
},
|
|
848
|
+
"root, blockquote, listItem, footnoteDefinition:exit"() {
|
|
849
|
+
containerStack = containerStack.upper;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
|
|
596
855
|
//#endregion
|
|
597
856
|
//#region src/rules/canonical-code-block-language.ts
|
|
598
857
|
const DEFAULT_LANGUAGES = {
|
|
@@ -3594,84 +3853,358 @@ var heading_casing_default = createRule("heading-casing", {
|
|
|
3594
3853
|
});
|
|
3595
3854
|
|
|
3596
3855
|
//#endregion
|
|
3597
|
-
//#region src/
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3856
|
+
//#region src/utils/setext-heading.ts
|
|
3857
|
+
/**
|
|
3858
|
+
* Parse the setext heading.
|
|
3859
|
+
*/
|
|
3860
|
+
function parseSetextHeading(sourceCode, node) {
|
|
3861
|
+
if (getHeadingKind(sourceCode, node) !== "setext") return null;
|
|
3862
|
+
const lines = getParsedLines(sourceCode);
|
|
3863
|
+
const contentLines = [];
|
|
3864
|
+
const nodeLoc = sourceCode.getLoc(node);
|
|
3865
|
+
for (let lineNumber = nodeLoc.start.line; lineNumber < nodeLoc.end.line; lineNumber++) {
|
|
3866
|
+
const content = parseContent(lines.get(lineNumber));
|
|
3867
|
+
contentLines.push(content);
|
|
3868
|
+
}
|
|
3869
|
+
const underline = parseUnderline(lines.get(nodeLoc.end.line));
|
|
3870
|
+
if (!underline) return null;
|
|
3871
|
+
return {
|
|
3872
|
+
contentLines,
|
|
3873
|
+
underline
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
/**
|
|
3877
|
+
* Parse the content line of a setext heading.
|
|
3878
|
+
*/
|
|
3879
|
+
function parseContent(line) {
|
|
3880
|
+
let prefix = "";
|
|
3881
|
+
let spaceBefore = "";
|
|
3882
|
+
let suffix = "";
|
|
3883
|
+
for (let index = 0; index < line.text.length; index++) {
|
|
3884
|
+
const c = line.text[index];
|
|
3885
|
+
if (!c.trim()) {
|
|
3886
|
+
spaceBefore += c;
|
|
3887
|
+
continue;
|
|
3888
|
+
}
|
|
3889
|
+
if (c === ">" && spaceBefore.length < 4) {
|
|
3890
|
+
prefix += spaceBefore + c;
|
|
3891
|
+
spaceBefore = "";
|
|
3892
|
+
continue;
|
|
3893
|
+
}
|
|
3894
|
+
suffix = line.text.slice(index);
|
|
3895
|
+
break;
|
|
3896
|
+
}
|
|
3897
|
+
const content = suffix.trimEnd();
|
|
3898
|
+
const spaceAfter = suffix.slice(content.length);
|
|
3899
|
+
return {
|
|
3900
|
+
text: content,
|
|
3901
|
+
range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
|
|
3902
|
+
loc: {
|
|
3903
|
+
start: {
|
|
3904
|
+
line: line.line,
|
|
3905
|
+
column: prefix.length + spaceBefore.length + 1
|
|
3906
|
+
},
|
|
3907
|
+
end: {
|
|
3908
|
+
line: line.line,
|
|
3909
|
+
column: prefix.length + spaceBefore.length + content.length + 1
|
|
3910
|
+
}
|
|
3911
|
+
},
|
|
3912
|
+
raws: {
|
|
3913
|
+
prefix,
|
|
3914
|
+
spaceBefore,
|
|
3915
|
+
spaceAfter
|
|
3916
|
+
}
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
/**
|
|
3920
|
+
* Parse the underline of a setext heading.
|
|
3921
|
+
*/
|
|
3922
|
+
function parseUnderline(line) {
|
|
3923
|
+
let marker = null;
|
|
3924
|
+
let underlineText = "";
|
|
3925
|
+
let prefix = "";
|
|
3926
|
+
let spaceBefore = "";
|
|
3927
|
+
let spaceAfter = "";
|
|
3928
|
+
for (let index = line.text.length - 1; index >= 0; index--) {
|
|
3929
|
+
const c = line.text[index];
|
|
3930
|
+
if (!marker) {
|
|
3931
|
+
if (c === "=" || c === "-") {
|
|
3932
|
+
underlineText = c + underlineText;
|
|
3933
|
+
marker = c;
|
|
3934
|
+
} else if (!c.trim()) spaceAfter = c + spaceAfter;
|
|
3935
|
+
else return null;
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
if (c === marker) {
|
|
3939
|
+
underlineText = c + spaceBefore + underlineText;
|
|
3940
|
+
spaceBefore = "";
|
|
3941
|
+
} else if (!c.trim()) spaceBefore = c + spaceBefore;
|
|
3942
|
+
else {
|
|
3943
|
+
prefix = line.text.slice(0, index + 1);
|
|
3944
|
+
break;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
if (!marker) return null;
|
|
3948
|
+
const underlineLoc = {
|
|
3949
|
+
start: {
|
|
3950
|
+
line: line.line,
|
|
3951
|
+
column: prefix.length + spaceBefore.length + 1
|
|
3952
|
+
},
|
|
3953
|
+
end: {
|
|
3954
|
+
line: line.line,
|
|
3955
|
+
column: prefix.length + spaceBefore.length + underlineText.length + 1
|
|
3956
|
+
}
|
|
3957
|
+
};
|
|
3958
|
+
return {
|
|
3959
|
+
text: underlineText,
|
|
3960
|
+
range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
|
|
3961
|
+
loc: underlineLoc,
|
|
3962
|
+
marker,
|
|
3963
|
+
raws: {
|
|
3964
|
+
prefix,
|
|
3965
|
+
spaceBefore,
|
|
3966
|
+
spaceAfter
|
|
3967
|
+
}
|
|
3968
|
+
};
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
//#endregion
|
|
3972
|
+
//#region src/rules/level1-heading-style.ts
|
|
3973
|
+
var level1_heading_style_default = createRule("level1-heading-style", {
|
|
3603
3974
|
meta: {
|
|
3604
3975
|
type: "layout",
|
|
3605
3976
|
docs: {
|
|
3606
|
-
description: "enforce consistent
|
|
3607
|
-
categories: [
|
|
3977
|
+
description: "enforce consistent style for level 1 headings",
|
|
3978
|
+
categories: [],
|
|
3608
3979
|
listCategory: "Stylistic"
|
|
3609
3980
|
},
|
|
3610
|
-
fixable: "
|
|
3981
|
+
fixable: "code",
|
|
3611
3982
|
hasSuggestions: false,
|
|
3612
3983
|
schema: [{
|
|
3613
3984
|
type: "object",
|
|
3614
|
-
properties: {
|
|
3985
|
+
properties: {
|
|
3986
|
+
style: { enum: ["atx", "setext"] },
|
|
3987
|
+
allowMultilineSetext: { type: "boolean" }
|
|
3988
|
+
},
|
|
3615
3989
|
additionalProperties: false
|
|
3616
3990
|
}],
|
|
3617
|
-
messages: {
|
|
3991
|
+
messages: {
|
|
3992
|
+
expectedAtx: "Expected ATX style heading (# Heading).",
|
|
3993
|
+
expectedSetext: "Expected Setext style heading (Heading\\n======).",
|
|
3994
|
+
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
3995
|
+
}
|
|
3618
3996
|
},
|
|
3619
3997
|
create(context) {
|
|
3620
3998
|
const sourceCode = context.sourceCode;
|
|
3621
|
-
const
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
const
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
if (items.length <= 1) return;
|
|
3641
|
-
const referenceMarkerLocation = getMarkerLocation(items[0]);
|
|
3642
|
-
for (const item of items.slice(1)) {
|
|
3643
|
-
const markerLocation = getMarkerLocation(item);
|
|
3644
|
-
const diff = markerLocation[alignPositionName] - referenceMarkerLocation[alignPositionName];
|
|
3645
|
-
if (diff === 0) continue;
|
|
3999
|
+
const opt = context.options[0] || {};
|
|
4000
|
+
const style = opt.style ?? "atx";
|
|
4001
|
+
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
4002
|
+
return { heading(node) {
|
|
4003
|
+
if (node.depth !== 1) return;
|
|
4004
|
+
const headingKind = getHeadingKind(sourceCode, node);
|
|
4005
|
+
if (style === "atx") {
|
|
4006
|
+
if (headingKind !== "setext") return;
|
|
4007
|
+
const parsed = parseSetextHeading(sourceCode, node);
|
|
4008
|
+
if (!parsed) return;
|
|
4009
|
+
const isMultiline = parsed.contentLines.length > 1;
|
|
4010
|
+
if (isMultiline) {
|
|
4011
|
+
if (allowMultilineSetext) return;
|
|
4012
|
+
context.report({
|
|
4013
|
+
node,
|
|
4014
|
+
messageId: "multilineSetextNotAllowed"
|
|
4015
|
+
});
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
3646
4018
|
context.report({
|
|
3647
|
-
node
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
},
|
|
3653
|
-
end: {
|
|
3654
|
-
line: markerLocation.line,
|
|
3655
|
-
column: markerLocation.end + 1
|
|
3656
|
-
}
|
|
3657
|
-
},
|
|
3658
|
-
messageId: "incorrectAlignment",
|
|
3659
|
-
data: {
|
|
3660
|
-
expected: String(markerLocation.start - diff),
|
|
3661
|
-
actual: String(markerLocation.start)
|
|
3662
|
-
},
|
|
3663
|
-
fix(fixer) {
|
|
4019
|
+
node,
|
|
4020
|
+
messageId: "expectedAtx",
|
|
4021
|
+
*fix(fixer) {
|
|
4022
|
+
const heading = parsed.contentLines[0];
|
|
4023
|
+
yield fixer.insertTextBeforeRange(heading.range, "# ");
|
|
3664
4024
|
const lines = getParsedLines(sourceCode);
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
4025
|
+
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
4026
|
+
}
|
|
4027
|
+
});
|
|
4028
|
+
} else if (style === "setext") {
|
|
4029
|
+
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
4030
|
+
context.report({
|
|
4031
|
+
node,
|
|
4032
|
+
messageId: "expectedSetext",
|
|
4033
|
+
*fix(fixer) {
|
|
4034
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
4035
|
+
if (!parsed) return;
|
|
4036
|
+
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.openingSequence.raws.spaceAfter.range[1]]);
|
|
4037
|
+
if (parsed.closingSequence) yield fixer.removeRange([parsed.closingSequence.raws.spaceBefore.range[0], parsed.closingSequence.raws.spaceAfter.range[1]]);
|
|
4038
|
+
const lines = getParsedLines(sourceCode);
|
|
4039
|
+
const underline = "=".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
4040
|
+
const prefix = lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1);
|
|
4041
|
+
const appendingText = `\n${prefix}${underline}`;
|
|
4042
|
+
yield fixer.insertTextAfter(node, appendingText);
|
|
4043
|
+
}
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
4046
|
+
} };
|
|
4047
|
+
}
|
|
4048
|
+
});
|
|
4049
|
+
|
|
4050
|
+
//#endregion
|
|
4051
|
+
//#region src/rules/level2-heading-style.ts
|
|
4052
|
+
var level2_heading_style_default = createRule("level2-heading-style", {
|
|
4053
|
+
meta: {
|
|
4054
|
+
type: "layout",
|
|
4055
|
+
docs: {
|
|
4056
|
+
description: "enforce consistent style for level 2 headings",
|
|
4057
|
+
categories: [],
|
|
4058
|
+
listCategory: "Stylistic"
|
|
4059
|
+
},
|
|
4060
|
+
fixable: "code",
|
|
4061
|
+
hasSuggestions: false,
|
|
4062
|
+
schema: [{
|
|
4063
|
+
type: "object",
|
|
4064
|
+
properties: {
|
|
4065
|
+
style: { enum: ["atx", "setext"] },
|
|
4066
|
+
allowMultilineSetext: { type: "boolean" }
|
|
4067
|
+
},
|
|
4068
|
+
additionalProperties: false
|
|
4069
|
+
}],
|
|
4070
|
+
messages: {
|
|
4071
|
+
expectedAtx: "Expected ATX style heading (## Heading).",
|
|
4072
|
+
expectedSetext: "Expected Setext style heading (Heading\\n------).",
|
|
4073
|
+
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
4074
|
+
}
|
|
4075
|
+
},
|
|
4076
|
+
create(context) {
|
|
4077
|
+
const sourceCode = context.sourceCode;
|
|
4078
|
+
const opt = context.options[0] || {};
|
|
4079
|
+
const style = opt.style ?? "atx";
|
|
4080
|
+
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
4081
|
+
return { heading(node) {
|
|
4082
|
+
if (node.depth !== 2) return;
|
|
4083
|
+
const headingKind = getHeadingKind(sourceCode, node);
|
|
4084
|
+
if (style === "atx") {
|
|
4085
|
+
if (headingKind !== "setext") return;
|
|
4086
|
+
const parsed = parseSetextHeading(sourceCode, node);
|
|
4087
|
+
if (!parsed) return;
|
|
4088
|
+
const isMultiline = parsed.contentLines.length > 1;
|
|
4089
|
+
if (isMultiline) {
|
|
4090
|
+
if (allowMultilineSetext) return;
|
|
4091
|
+
context.report({
|
|
4092
|
+
node,
|
|
4093
|
+
messageId: "multilineSetextNotAllowed"
|
|
4094
|
+
});
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
4097
|
+
context.report({
|
|
4098
|
+
node,
|
|
4099
|
+
messageId: "expectedAtx",
|
|
4100
|
+
*fix(fixer) {
|
|
4101
|
+
const heading = parsed.contentLines[0];
|
|
4102
|
+
yield fixer.insertTextBeforeRange(heading.range, "## ");
|
|
4103
|
+
const lines = getParsedLines(sourceCode);
|
|
4104
|
+
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
4105
|
+
}
|
|
4106
|
+
});
|
|
4107
|
+
} else if (style === "setext") {
|
|
4108
|
+
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
4109
|
+
context.report({
|
|
4110
|
+
node,
|
|
4111
|
+
messageId: "expectedSetext",
|
|
4112
|
+
*fix(fixer) {
|
|
4113
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
4114
|
+
if (!parsed) return;
|
|
4115
|
+
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.openingSequence.raws.spaceAfter.range[1]]);
|
|
4116
|
+
if (parsed.closingSequence) yield fixer.removeRange([parsed.closingSequence.raws.spaceBefore.range[0], parsed.closingSequence.raws.spaceAfter.range[1]]);
|
|
4117
|
+
const lines = getParsedLines(sourceCode);
|
|
4118
|
+
const underline = "-".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
4119
|
+
const prefix = lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1);
|
|
4120
|
+
const appendingText = `\n${prefix}${underline}`;
|
|
4121
|
+
yield fixer.insertTextAfter(node, appendingText);
|
|
4122
|
+
}
|
|
4123
|
+
});
|
|
4124
|
+
}
|
|
4125
|
+
} };
|
|
4126
|
+
}
|
|
4127
|
+
});
|
|
4128
|
+
|
|
4129
|
+
//#endregion
|
|
4130
|
+
//#region src/rules/list-marker-alignment.ts
|
|
4131
|
+
const ALIGN_TO_POSITION_NAME = {
|
|
4132
|
+
left: "start",
|
|
4133
|
+
right: "end"
|
|
4134
|
+
};
|
|
4135
|
+
var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
4136
|
+
meta: {
|
|
4137
|
+
type: "layout",
|
|
4138
|
+
docs: {
|
|
4139
|
+
description: "enforce consistent alignment of list markers",
|
|
4140
|
+
categories: ["recommended"],
|
|
4141
|
+
listCategory: "Stylistic"
|
|
4142
|
+
},
|
|
4143
|
+
fixable: "whitespace",
|
|
4144
|
+
hasSuggestions: false,
|
|
4145
|
+
schema: [{
|
|
4146
|
+
type: "object",
|
|
4147
|
+
properties: { align: { enum: ["left", "right"] } },
|
|
4148
|
+
additionalProperties: false
|
|
4149
|
+
}],
|
|
4150
|
+
messages: { incorrectAlignment: "List marker alignment is inconsistent. Expected {{expected}} characters of indentation, but got {{actual}}." }
|
|
4151
|
+
},
|
|
4152
|
+
create(context) {
|
|
4153
|
+
const sourceCode = context.sourceCode;
|
|
4154
|
+
const alignPositionName = ALIGN_TO_POSITION_NAME[context.options[0]?.align ?? "left"];
|
|
4155
|
+
/**
|
|
4156
|
+
* Get the marker location of a list item
|
|
4157
|
+
*/
|
|
4158
|
+
function getMarkerLocation(node) {
|
|
4159
|
+
const start = sourceCode.getLoc(node).start;
|
|
4160
|
+
const startColumnIndex = start.column - 1;
|
|
4161
|
+
const marker = getListItemMarker(sourceCode, node);
|
|
4162
|
+
return {
|
|
4163
|
+
line: start.line,
|
|
4164
|
+
start: startColumnIndex,
|
|
4165
|
+
end: startColumnIndex + marker.raw.length
|
|
4166
|
+
};
|
|
4167
|
+
}
|
|
4168
|
+
/**
|
|
4169
|
+
* Check if list items have consistent alignment
|
|
4170
|
+
*/
|
|
4171
|
+
function checkListAlignment(listNode) {
|
|
4172
|
+
const items = listNode.children;
|
|
4173
|
+
if (items.length <= 1) return;
|
|
4174
|
+
const referenceMarkerLocation = getMarkerLocation(items[0]);
|
|
4175
|
+
for (const item of items.slice(1)) {
|
|
4176
|
+
const markerLocation = getMarkerLocation(item);
|
|
4177
|
+
const diff = markerLocation[alignPositionName] - referenceMarkerLocation[alignPositionName];
|
|
4178
|
+
if (diff === 0) continue;
|
|
4179
|
+
context.report({
|
|
4180
|
+
node: item,
|
|
4181
|
+
loc: {
|
|
4182
|
+
start: {
|
|
4183
|
+
line: markerLocation.line,
|
|
4184
|
+
column: markerLocation.start + 1
|
|
4185
|
+
},
|
|
4186
|
+
end: {
|
|
4187
|
+
line: markerLocation.line,
|
|
4188
|
+
column: markerLocation.end + 1
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
messageId: "incorrectAlignment",
|
|
4192
|
+
data: {
|
|
4193
|
+
expected: String(markerLocation.start - diff),
|
|
4194
|
+
actual: String(markerLocation.start)
|
|
4195
|
+
},
|
|
4196
|
+
fix(fixer) {
|
|
4197
|
+
const lines = getParsedLines(sourceCode);
|
|
4198
|
+
const line = lines.get(markerLocation.line);
|
|
4199
|
+
if (diff < 0) {
|
|
4200
|
+
const addSpaces = " ".repeat(-diff);
|
|
4201
|
+
return fixer.insertTextBeforeRange([line.range[0] + markerLocation.start, line.range[0] + markerLocation.start], addSpaces);
|
|
4202
|
+
}
|
|
4203
|
+
const itemBefore = line.text.slice(0, markerLocation.start - diff);
|
|
4204
|
+
if (itemBefore.includes(" ")) return null;
|
|
4205
|
+
const referenceMarkerBefore = lines.get(referenceMarkerLocation.line).text.slice(0, referenceMarkerLocation.start);
|
|
4206
|
+
if (referenceMarkerBefore === itemBefore) {
|
|
4207
|
+
const removeEndIndex = line.range[0] + markerLocation.start;
|
|
3675
4208
|
const removeStartIndex = removeEndIndex - diff;
|
|
3676
4209
|
return fixer.removeRange([removeStartIndex, removeEndIndex]);
|
|
3677
4210
|
}
|
|
@@ -4146,7 +4679,7 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
|
|
|
4146
4679
|
messageId: "inconsistentStart",
|
|
4147
4680
|
data: {
|
|
4148
4681
|
expected: new Intl.ListFormat("en-US", { type: "disjunction" }).format(expected.map((n) => `'${n}'`)),
|
|
4149
|
-
actual:
|
|
4682
|
+
actual: marker.sequence.raw
|
|
4150
4683
|
},
|
|
4151
4684
|
fix: scope.last == null ? (fixer) => {
|
|
4152
4685
|
const expectedMarker = `1${marker.kind}`;
|
|
@@ -4167,7 +4700,7 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
|
|
|
4167
4700
|
const marker = getListItemMarker(sourceCode, item);
|
|
4168
4701
|
if (marker.kind !== "." && marker.kind !== ")") continue;
|
|
4169
4702
|
const expectedSequence = node.start + i;
|
|
4170
|
-
if (marker.sequence !== expectedSequence) {
|
|
4703
|
+
if (marker.sequence.value !== expectedSequence) {
|
|
4171
4704
|
const expectedMarker = `${expectedSequence}${marker.kind}`;
|
|
4172
4705
|
context.report({
|
|
4173
4706
|
node: item,
|
|
@@ -4302,6 +4835,174 @@ var ordered_list_marker_start_default = createRule("ordered-list-marker-start",
|
|
|
4302
4835
|
}
|
|
4303
4836
|
});
|
|
4304
4837
|
|
|
4838
|
+
//#endregion
|
|
4839
|
+
//#region src/rules/ordered-list-marker-style.ts
|
|
4840
|
+
const MARKER_KINDS = [".", ")"];
|
|
4841
|
+
const MARKERS = MARKER_KINDS.map((kind) => `n${kind}`);
|
|
4842
|
+
/**
|
|
4843
|
+
* Get the other marker kind.
|
|
4844
|
+
*/
|
|
4845
|
+
function getOtherMarkerKind(unavailableMarker) {
|
|
4846
|
+
return MARKER_KINDS.find((mark) => unavailableMarker !== mark);
|
|
4847
|
+
}
|
|
4848
|
+
function markerToKind(marker) {
|
|
4849
|
+
if (marker === "n.") return ".";
|
|
4850
|
+
if (marker === "n)") return ")";
|
|
4851
|
+
return marker;
|
|
4852
|
+
}
|
|
4853
|
+
/**
|
|
4854
|
+
* Parse rule options.
|
|
4855
|
+
*/
|
|
4856
|
+
function parseOptions(options) {
|
|
4857
|
+
const prefer = markerToKind(options.prefer) || ".";
|
|
4858
|
+
const overrides = (options.overrides ?? []).map((override) => {
|
|
4859
|
+
const preferForOverride = markerToKind(override.prefer) || ".";
|
|
4860
|
+
return {
|
|
4861
|
+
level: override.level,
|
|
4862
|
+
parentMarker: markerToKind(override.parentMarker) ?? "any",
|
|
4863
|
+
prefer: preferForOverride
|
|
4864
|
+
};
|
|
4865
|
+
}).reverse();
|
|
4866
|
+
return { get(level, parentMarker) {
|
|
4867
|
+
for (const o of overrides) if ((o.level == null || o.level === level) && (o.parentMarker === "any" || o.parentMarker === parentMarker)) return { prefer: o.prefer };
|
|
4868
|
+
return { prefer };
|
|
4869
|
+
} };
|
|
4870
|
+
}
|
|
4871
|
+
/**
|
|
4872
|
+
* Check if a item marker is an ordered list marker.
|
|
4873
|
+
*/
|
|
4874
|
+
function isOrderedListItemMarker(itemMarker) {
|
|
4875
|
+
return itemMarker.kind === "." || itemMarker.kind === ")";
|
|
4876
|
+
}
|
|
4877
|
+
var ordered_list_marker_style_default = createRule("ordered-list-marker-style", {
|
|
4878
|
+
meta: {
|
|
4879
|
+
type: "layout",
|
|
4880
|
+
docs: {
|
|
4881
|
+
description: "enforce consistent ordered list marker style",
|
|
4882
|
+
categories: [],
|
|
4883
|
+
listCategory: "Stylistic"
|
|
4884
|
+
},
|
|
4885
|
+
fixable: "code",
|
|
4886
|
+
hasSuggestions: false,
|
|
4887
|
+
schema: [{
|
|
4888
|
+
type: "object",
|
|
4889
|
+
properties: {
|
|
4890
|
+
prefer: { enum: MARKERS },
|
|
4891
|
+
overrides: {
|
|
4892
|
+
type: "array",
|
|
4893
|
+
items: {
|
|
4894
|
+
type: "object",
|
|
4895
|
+
properties: {
|
|
4896
|
+
level: {
|
|
4897
|
+
type: "integer",
|
|
4898
|
+
minimum: 1
|
|
4899
|
+
},
|
|
4900
|
+
parentMarker: { enum: [
|
|
4901
|
+
...MARKERS,
|
|
4902
|
+
"any",
|
|
4903
|
+
"bullet"
|
|
4904
|
+
] },
|
|
4905
|
+
prefer: { enum: MARKERS }
|
|
4906
|
+
},
|
|
4907
|
+
additionalProperties: false
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
},
|
|
4911
|
+
additionalProperties: false
|
|
4912
|
+
}],
|
|
4913
|
+
messages: { unexpected: "Ordered list marker should be '{{sequence}}{{markerKind}}'." }
|
|
4914
|
+
},
|
|
4915
|
+
create(context) {
|
|
4916
|
+
const sourceCode = context.sourceCode;
|
|
4917
|
+
const options = parseOptions(context.options[0] || {});
|
|
4918
|
+
let containerStack = {
|
|
4919
|
+
node: sourceCode.ast,
|
|
4920
|
+
level: 1,
|
|
4921
|
+
upper: null
|
|
4922
|
+
};
|
|
4923
|
+
/**
|
|
4924
|
+
* Check ordered list marker style
|
|
4925
|
+
*/
|
|
4926
|
+
function checkOrderedList(node) {
|
|
4927
|
+
let parentMarker;
|
|
4928
|
+
if (containerStack.node.type === "listItem") {
|
|
4929
|
+
const parentMarkerKind = getListItemMarker(sourceCode, containerStack.node).kind;
|
|
4930
|
+
parentMarker = parentMarkerKind === "-" || parentMarkerKind === "*" || parentMarkerKind === "+" ? "bullet" : parentMarkerKind;
|
|
4931
|
+
} else parentMarker = "top";
|
|
4932
|
+
const { prefer } = options.get(containerStack.level, parentMarker);
|
|
4933
|
+
const nodeIndex = containerStack.node.children.indexOf(node);
|
|
4934
|
+
if (nodeIndex === -1) return;
|
|
4935
|
+
const prevNode = nodeIndex > 0 ? containerStack.node.children[nodeIndex - 1] : null;
|
|
4936
|
+
const prevBulletList = prevNode && (prevNode.type === "list" ? prevNode : null);
|
|
4937
|
+
const prevBulletListMarker = prevBulletList && getListItemMarker(sourceCode, prevBulletList);
|
|
4938
|
+
const expectedMarker = prevBulletListMarker?.kind !== prefer ? prefer : getOtherMarkerKind(prefer);
|
|
4939
|
+
const marker = getListItemMarker(sourceCode, node);
|
|
4940
|
+
if (marker.kind === expectedMarker || !isOrderedListItemMarker(marker)) return;
|
|
4941
|
+
const loc = sourceCode.getLoc(node);
|
|
4942
|
+
context.report({
|
|
4943
|
+
node,
|
|
4944
|
+
loc: {
|
|
4945
|
+
start: loc.start,
|
|
4946
|
+
end: {
|
|
4947
|
+
line: loc.start.line,
|
|
4948
|
+
column: loc.start.column + marker.raw.length
|
|
4949
|
+
}
|
|
4950
|
+
},
|
|
4951
|
+
messageId: "unexpected",
|
|
4952
|
+
data: {
|
|
4953
|
+
sequence: marker.sequence.raw,
|
|
4954
|
+
markerKind: expectedMarker
|
|
4955
|
+
},
|
|
4956
|
+
*fix(fixer) {
|
|
4957
|
+
if (prevNode?.type === "list" && prevBulletListMarker && isOrderedListItemMarker(prevBulletListMarker)) yield* fixMarker(prevNode.children[0], prevBulletListMarker.kind);
|
|
4958
|
+
yield* fixMarkers(node, expectedMarker);
|
|
4959
|
+
let prevMarker = expectedMarker;
|
|
4960
|
+
for (let index = nodeIndex + 1; index < containerStack.node.children.length; index++) {
|
|
4961
|
+
const nextNode = containerStack.node.children[index];
|
|
4962
|
+
if (nextNode.type !== "list") break;
|
|
4963
|
+
const nextMarker = getListItemMarker(sourceCode, nextNode);
|
|
4964
|
+
if (nextMarker.kind !== prevMarker) break;
|
|
4965
|
+
const expectedNextMarker = prevMarker === prefer ? getOtherMarkerKind(prefer) : prefer;
|
|
4966
|
+
yield* fixMarkers(nextNode, expectedNextMarker);
|
|
4967
|
+
prevMarker = expectedNextMarker;
|
|
4968
|
+
}
|
|
4969
|
+
/**
|
|
4970
|
+
* Fix ordered list markers
|
|
4971
|
+
*/
|
|
4972
|
+
function* fixMarkers(list, replacementMarker) {
|
|
4973
|
+
for (const item of list.children) yield* fixMarker(item, replacementMarker);
|
|
4974
|
+
}
|
|
4975
|
+
/**
|
|
4976
|
+
* Fix ordered list item marker
|
|
4977
|
+
*/
|
|
4978
|
+
function* fixMarker(item, replacementMarker) {
|
|
4979
|
+
const range = sourceCode.getRange(item);
|
|
4980
|
+
const itemMarker = getListItemMarker(sourceCode, item);
|
|
4981
|
+
if (!isOrderedListItemMarker(itemMarker)) return;
|
|
4982
|
+
yield fixer.replaceTextRange([range[0] + itemMarker.raw.length - 1, range[0] + itemMarker.raw.length], replacementMarker);
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4987
|
+
return {
|
|
4988
|
+
list(node) {
|
|
4989
|
+
if (!node.ordered) return;
|
|
4990
|
+
checkOrderedList(node);
|
|
4991
|
+
},
|
|
4992
|
+
"root, blockquote, listItem, footnoteDefinition"(node) {
|
|
4993
|
+
containerStack = {
|
|
4994
|
+
node,
|
|
4995
|
+
level: node.type === "listItem" ? containerStack.level + 1 : 1,
|
|
4996
|
+
upper: containerStack
|
|
4997
|
+
};
|
|
4998
|
+
},
|
|
4999
|
+
"root, blockquote, listItem, footnoteDefinition:exit"() {
|
|
5000
|
+
containerStack = containerStack.upper;
|
|
5001
|
+
}
|
|
5002
|
+
};
|
|
5003
|
+
}
|
|
5004
|
+
});
|
|
5005
|
+
|
|
4305
5006
|
//#endregion
|
|
4306
5007
|
//#region src/rules/padding-line-between-blocks.ts
|
|
4307
5008
|
/**
|
|
@@ -5119,6 +5820,244 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
|
|
|
5119
5820
|
}
|
|
5120
5821
|
});
|
|
5121
5822
|
|
|
5823
|
+
//#endregion
|
|
5824
|
+
//#region src/rules/setext-heading-underline-length.ts
|
|
5825
|
+
var setext_heading_underline_length_default = createRule("setext-heading-underline-length", {
|
|
5826
|
+
meta: {
|
|
5827
|
+
type: "layout",
|
|
5828
|
+
docs: {
|
|
5829
|
+
description: "enforce setext heading underline length",
|
|
5830
|
+
categories: [],
|
|
5831
|
+
listCategory: "Stylistic"
|
|
5832
|
+
},
|
|
5833
|
+
fixable: "whitespace",
|
|
5834
|
+
schema: [{
|
|
5835
|
+
type: "object",
|
|
5836
|
+
properties: {
|
|
5837
|
+
mode: {
|
|
5838
|
+
type: "string",
|
|
5839
|
+
enum: [
|
|
5840
|
+
"exact",
|
|
5841
|
+
"minimum",
|
|
5842
|
+
"consistent",
|
|
5843
|
+
"consistent-line-length"
|
|
5844
|
+
]
|
|
5845
|
+
},
|
|
5846
|
+
align: {
|
|
5847
|
+
type: "string",
|
|
5848
|
+
enum: [
|
|
5849
|
+
"any",
|
|
5850
|
+
"exact",
|
|
5851
|
+
"minimum",
|
|
5852
|
+
"length"
|
|
5853
|
+
]
|
|
5854
|
+
},
|
|
5855
|
+
length: {
|
|
5856
|
+
type: "integer",
|
|
5857
|
+
minimum: 1
|
|
5858
|
+
}
|
|
5859
|
+
},
|
|
5860
|
+
additionalProperties: false
|
|
5861
|
+
}],
|
|
5862
|
+
messages: {
|
|
5863
|
+
exactLength: "Setext heading underline should be exactly the same length as the heading text.",
|
|
5864
|
+
minimumLength: "Setext heading underline should be at least as long as the heading text.",
|
|
5865
|
+
consistentAny: "Setext heading underline should be consistent with other underlines in the document.",
|
|
5866
|
+
consistentExact: "Setext heading underline should be exactly the same length as the longest heading text in the document.",
|
|
5867
|
+
consistentMinimum: "Setext heading underline should be at least as long as the longest heading text in the document.",
|
|
5868
|
+
consistentLength: "Setext heading underline should be {{expectedLength}} characters long for consistency.",
|
|
5869
|
+
consistentLineLengthAny: "Setext heading underline should be consistent in line length with other underlines in the document.",
|
|
5870
|
+
consistentLineLengthExact: "Setext heading underline should be exactly the same line length as the longest heading line in the document.",
|
|
5871
|
+
consistentLineLengthMinimum: "Setext heading underline should be at least as long as the longest heading line in the document.",
|
|
5872
|
+
consistentLineLengthLength: "Setext heading underline should be {{expectedLength}} characters long for line length consistency."
|
|
5873
|
+
}
|
|
5874
|
+
},
|
|
5875
|
+
create(context) {
|
|
5876
|
+
const sourceCode = context.sourceCode;
|
|
5877
|
+
const options = context.options[0] || {};
|
|
5878
|
+
const mode = options.mode || "exact";
|
|
5879
|
+
const parsedSetextHeadings = /* @__PURE__ */ new Map();
|
|
5880
|
+
/**
|
|
5881
|
+
* Get the parsed setext heading for a specific heading.
|
|
5882
|
+
*/
|
|
5883
|
+
function getParsedSetextHeading(heading) {
|
|
5884
|
+
const cached = parsedSetextHeadings.get(heading);
|
|
5885
|
+
if (cached) return cached;
|
|
5886
|
+
const underline = parseSetextHeading(sourceCode, heading);
|
|
5887
|
+
if (!underline) return null;
|
|
5888
|
+
parsedSetextHeadings.set(heading, underline);
|
|
5889
|
+
return underline;
|
|
5890
|
+
}
|
|
5891
|
+
/**
|
|
5892
|
+
* Helper function to report errors for both regular and blockquote headings
|
|
5893
|
+
*/
|
|
5894
|
+
function reportError(node, messageId, expectedLength) {
|
|
5895
|
+
const parsed = getParsedSetextHeading(node);
|
|
5896
|
+
if (!parsed) return;
|
|
5897
|
+
context.report({
|
|
5898
|
+
node,
|
|
5899
|
+
messageId,
|
|
5900
|
+
loc: parsed.underline.loc,
|
|
5901
|
+
data: { expectedLength: String(expectedLength) },
|
|
5902
|
+
fix(fixer) {
|
|
5903
|
+
const newUnderline = parsed.underline.marker.repeat(expectedLength);
|
|
5904
|
+
return fixer.replaceTextRange(parsed.underline.range, newUnderline);
|
|
5905
|
+
}
|
|
5906
|
+
});
|
|
5907
|
+
}
|
|
5908
|
+
if (mode === "exact" || mode === "minimum") return { heading(node) {
|
|
5909
|
+
const parsed = getParsedSetextHeading(node);
|
|
5910
|
+
if (!parsed) return;
|
|
5911
|
+
const expectedLength = getMaxHeaderTextWidth(parsed);
|
|
5912
|
+
if (expectedLength < 1) return;
|
|
5913
|
+
if (mode === "exact") {
|
|
5914
|
+
if (parsed.underline.text.length !== expectedLength) reportError(node, "exactLength", expectedLength);
|
|
5915
|
+
} else if (mode === "minimum") {
|
|
5916
|
+
if (parsed.underline.text.length < expectedLength) reportError(node, "minimumLength", expectedLength);
|
|
5917
|
+
}
|
|
5918
|
+
} };
|
|
5919
|
+
if (mode === "consistent") {
|
|
5920
|
+
const align = options.align || "exact";
|
|
5921
|
+
const fixedLength = options.length || 0;
|
|
5922
|
+
const setextHeadings = [];
|
|
5923
|
+
return {
|
|
5924
|
+
heading(node) {
|
|
5925
|
+
if (getHeadingKind(sourceCode, node) !== "setext") return;
|
|
5926
|
+
setextHeadings.push(node);
|
|
5927
|
+
},
|
|
5928
|
+
"root:exit"() {
|
|
5929
|
+
if (setextHeadings.length === 0) return;
|
|
5930
|
+
let expectedLength = 0;
|
|
5931
|
+
if (align === "any") {
|
|
5932
|
+
if (setextHeadings.length < 2) return;
|
|
5933
|
+
for (const node of setextHeadings) {
|
|
5934
|
+
const parsed = getParsedSetextHeading(node);
|
|
5935
|
+
if (!parsed) continue;
|
|
5936
|
+
expectedLength = parsed.underline.text.length;
|
|
5937
|
+
break;
|
|
5938
|
+
}
|
|
5939
|
+
} else if (align === "exact") for (const node of setextHeadings) {
|
|
5940
|
+
const parsed = getParsedSetextHeading(node);
|
|
5941
|
+
if (!parsed) continue;
|
|
5942
|
+
expectedLength = Math.max(expectedLength, getMaxHeaderTextWidth(parsed));
|
|
5943
|
+
}
|
|
5944
|
+
else if (align === "minimum") {
|
|
5945
|
+
let maxTextWidth = 0;
|
|
5946
|
+
for (const node of setextHeadings) {
|
|
5947
|
+
const parsed = getParsedSetextHeading(node);
|
|
5948
|
+
if (!parsed) continue;
|
|
5949
|
+
maxTextWidth = Math.max(maxTextWidth, getMaxHeaderTextWidth(parsed));
|
|
5950
|
+
expectedLength = Math.max(expectedLength, parsed.underline.text.length);
|
|
5951
|
+
}
|
|
5952
|
+
if (expectedLength < maxTextWidth) expectedLength = maxTextWidth;
|
|
5953
|
+
else for (const node of setextHeadings) {
|
|
5954
|
+
const parsed = getParsedSetextHeading(node);
|
|
5955
|
+
if (!parsed) continue;
|
|
5956
|
+
if (maxTextWidth <= parsed.underline.text.length) expectedLength = Math.min(expectedLength, parsed.underline.text.length);
|
|
5957
|
+
}
|
|
5958
|
+
} else if (align === "length") expectedLength = fixedLength;
|
|
5959
|
+
else return;
|
|
5960
|
+
if (!expectedLength || expectedLength < 1) return;
|
|
5961
|
+
for (const node of setextHeadings) {
|
|
5962
|
+
const parsed = getParsedSetextHeading(node);
|
|
5963
|
+
if (!parsed) continue;
|
|
5964
|
+
if (parsed.underline.text.length === expectedLength) continue;
|
|
5965
|
+
if (align === "any") reportError(node, "consistentAny", expectedLength);
|
|
5966
|
+
else if (align === "exact") reportError(node, "consistentExact", expectedLength);
|
|
5967
|
+
else if (align === "minimum") reportError(node, "consistentMinimum", expectedLength);
|
|
5968
|
+
else if (align === "length") reportError(node, "consistentLength", expectedLength);
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
};
|
|
5972
|
+
}
|
|
5973
|
+
if (mode === "consistent-line-length") {
|
|
5974
|
+
const align = options.align || "exact";
|
|
5975
|
+
const fixedLength = options.length || 0;
|
|
5976
|
+
const setextHeadings = [];
|
|
5977
|
+
return {
|
|
5978
|
+
heading(node) {
|
|
5979
|
+
if (getHeadingKind(sourceCode, node) !== "setext") return;
|
|
5980
|
+
setextHeadings.push(node);
|
|
5981
|
+
},
|
|
5982
|
+
"root:exit"() {
|
|
5983
|
+
if (setextHeadings.length === 0) return;
|
|
5984
|
+
let minimumRequiredLineLength = 1;
|
|
5985
|
+
for (const node of setextHeadings) {
|
|
5986
|
+
const parsed = getParsedSetextHeading(node);
|
|
5987
|
+
if (!parsed) continue;
|
|
5988
|
+
minimumRequiredLineLength = Math.max(minimumRequiredLineLength, parsed.underline.raws.prefix.length + parsed.underline.raws.spaceBefore.length + 1);
|
|
5989
|
+
}
|
|
5990
|
+
let expectedLineLength = minimumRequiredLineLength;
|
|
5991
|
+
if (align === "any") {
|
|
5992
|
+
if (setextHeadings.length < 2) return;
|
|
5993
|
+
for (const node of setextHeadings) {
|
|
5994
|
+
const parsed = getParsedSetextHeading(node);
|
|
5995
|
+
if (!parsed) continue;
|
|
5996
|
+
expectedLineLength = Math.max(parsed.underline.loc.end.column - 1, minimumRequiredLineLength);
|
|
5997
|
+
break;
|
|
5998
|
+
}
|
|
5999
|
+
} else if (align === "exact") for (const node of setextHeadings) {
|
|
6000
|
+
const parsed = getParsedSetextHeading(node);
|
|
6001
|
+
if (!parsed) continue;
|
|
6002
|
+
expectedLineLength = Math.max(expectedLineLength, getMaxHeaderLineWidth(parsed));
|
|
6003
|
+
}
|
|
6004
|
+
else if (align === "minimum") {
|
|
6005
|
+
let maxLineWidth = 0;
|
|
6006
|
+
for (const node of setextHeadings) {
|
|
6007
|
+
const parsed = getParsedSetextHeading(node);
|
|
6008
|
+
if (!parsed) continue;
|
|
6009
|
+
maxLineWidth = Math.max(maxLineWidth, getMaxHeaderLineWidth(parsed));
|
|
6010
|
+
expectedLineLength = Math.max(expectedLineLength, parsed.underline.loc.end.column - 1);
|
|
6011
|
+
}
|
|
6012
|
+
if (expectedLineLength < maxLineWidth) expectedLineLength = maxLineWidth;
|
|
6013
|
+
else for (const node of setextHeadings) {
|
|
6014
|
+
const parsed = getParsedSetextHeading(node);
|
|
6015
|
+
if (!parsed) continue;
|
|
6016
|
+
if (maxLineWidth <= parsed.underline.loc.end.column - 1) expectedLineLength = Math.min(expectedLineLength, parsed.underline.loc.end.column - 1);
|
|
6017
|
+
}
|
|
6018
|
+
} else if (align === "length") expectedLineLength = Math.max(fixedLength, minimumRequiredLineLength);
|
|
6019
|
+
else return;
|
|
6020
|
+
if (!expectedLineLength || expectedLineLength < 1) return;
|
|
6021
|
+
for (const node of setextHeadings) {
|
|
6022
|
+
const parsed = getParsedSetextHeading(node);
|
|
6023
|
+
if (!parsed) continue;
|
|
6024
|
+
const expectedLength = expectedLineLength - parsed.underline.raws.prefix.length - parsed.underline.raws.spaceBefore.length;
|
|
6025
|
+
if (parsed.underline.text.length === expectedLength) continue;
|
|
6026
|
+
if (align === "any") reportError(node, "consistentLineLengthAny", expectedLength);
|
|
6027
|
+
else if (align === "exact") reportError(node, "consistentLineLengthExact", expectedLength);
|
|
6028
|
+
else if (align === "minimum") reportError(node, "consistentLineLengthMinimum", expectedLength);
|
|
6029
|
+
else if (align === "length") reportError(node, "consistentLineLengthLength", expectedLength);
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6034
|
+
return {};
|
|
6035
|
+
/**
|
|
6036
|
+
* Get the maximum width of header lines.
|
|
6037
|
+
*/
|
|
6038
|
+
function getMaxHeaderTextWidth(parsed) {
|
|
6039
|
+
let maxWidth = 0;
|
|
6040
|
+
for (const contentLine of parsed.contentLines) {
|
|
6041
|
+
const lineWidth = getTextWidth(contentLine.raws.prefix + contentLine.raws.spaceBefore + contentLine.text);
|
|
6042
|
+
const prefixWidth = getTextWidth(contentLine.raws.prefix + contentLine.raws.spaceBefore);
|
|
6043
|
+
maxWidth = Math.max(maxWidth, lineWidth - prefixWidth);
|
|
6044
|
+
}
|
|
6045
|
+
return maxWidth;
|
|
6046
|
+
}
|
|
6047
|
+
/**
|
|
6048
|
+
* Get the maximum width of header lines.
|
|
6049
|
+
*/
|
|
6050
|
+
function getMaxHeaderLineWidth(parsed) {
|
|
6051
|
+
let maxLineWidth = 0;
|
|
6052
|
+
for (const contentLine of parsed.contentLines) {
|
|
6053
|
+
const lineWidth = getTextWidth(contentLine.raws.prefix + contentLine.raws.spaceBefore + contentLine.text);
|
|
6054
|
+
maxLineWidth = Math.max(maxLineWidth, lineWidth);
|
|
6055
|
+
}
|
|
6056
|
+
return maxLineWidth;
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
});
|
|
6060
|
+
|
|
5122
6061
|
//#endregion
|
|
5123
6062
|
//#region src/rules/sort-definitions.ts
|
|
5124
6063
|
var sort_definitions_default = createRule("sort-definitions", {
|
|
@@ -5536,17 +6475,231 @@ var table_header_casing_default = createRule("table-header-casing", {
|
|
|
5536
6475
|
}
|
|
5537
6476
|
});
|
|
5538
6477
|
|
|
6478
|
+
//#endregion
|
|
6479
|
+
//#region src/rules/thematic-break-character-style.ts
|
|
6480
|
+
var thematic_break_character_style_default = createRule("thematic-break-character-style", {
|
|
6481
|
+
meta: {
|
|
6482
|
+
type: "layout",
|
|
6483
|
+
docs: {
|
|
6484
|
+
description: "enforce consistent character style for thematic breaks (horizontal rules) in Markdown.",
|
|
6485
|
+
categories: [],
|
|
6486
|
+
listCategory: "Stylistic"
|
|
6487
|
+
},
|
|
6488
|
+
fixable: "code",
|
|
6489
|
+
hasSuggestions: false,
|
|
6490
|
+
schema: [{
|
|
6491
|
+
type: "object",
|
|
6492
|
+
properties: { style: {
|
|
6493
|
+
type: "string",
|
|
6494
|
+
enum: [
|
|
6495
|
+
"-",
|
|
6496
|
+
"*",
|
|
6497
|
+
"_"
|
|
6498
|
+
]
|
|
6499
|
+
} },
|
|
6500
|
+
additionalProperties: false
|
|
6501
|
+
}],
|
|
6502
|
+
messages: { unexpected: "Thematic break should use '{{expected}}' but found '{{actual}}'." }
|
|
6503
|
+
},
|
|
6504
|
+
create(context) {
|
|
6505
|
+
const option = context.options[0];
|
|
6506
|
+
const style = option?.style || "-";
|
|
6507
|
+
return { thematicBreak(node) {
|
|
6508
|
+
const marker = getThematicBreakMarker(context.sourceCode, node);
|
|
6509
|
+
if (marker.kind !== style) context.report({
|
|
6510
|
+
node,
|
|
6511
|
+
messageId: "unexpected",
|
|
6512
|
+
data: {
|
|
6513
|
+
expected: style,
|
|
6514
|
+
actual: marker.kind
|
|
6515
|
+
},
|
|
6516
|
+
fix(fixer) {
|
|
6517
|
+
const range = context.sourceCode.getRange(node);
|
|
6518
|
+
const text = context.sourceCode.getText(node);
|
|
6519
|
+
const rep = text.replaceAll(marker.kind, style);
|
|
6520
|
+
return fixer.replaceTextRange(range, rep);
|
|
6521
|
+
}
|
|
6522
|
+
});
|
|
6523
|
+
} };
|
|
6524
|
+
}
|
|
6525
|
+
});
|
|
6526
|
+
|
|
6527
|
+
//#endregion
|
|
6528
|
+
//#region src/utils/thematic-break.ts
|
|
6529
|
+
/**
|
|
6530
|
+
* Check if the pattern is valid within the thematic break string.
|
|
6531
|
+
*/
|
|
6532
|
+
function isValidThematicBreakPattern(pattern, text) {
|
|
6533
|
+
for (let i = 0; i < text.length; i += pattern.length) {
|
|
6534
|
+
const subSequence = text.slice(i, i + pattern.length);
|
|
6535
|
+
if (subSequence === pattern) continue;
|
|
6536
|
+
if (subSequence.length < pattern.length && pattern.startsWith(subSequence)) continue;
|
|
6537
|
+
return false;
|
|
6538
|
+
}
|
|
6539
|
+
return true;
|
|
6540
|
+
}
|
|
6541
|
+
/**
|
|
6542
|
+
* Create a thematic break string from a pattern and length.
|
|
6543
|
+
*/
|
|
6544
|
+
function createThematicBreakFromPattern(pattern, length) {
|
|
6545
|
+
const mark = pattern[0];
|
|
6546
|
+
let candidate = pattern.repeat(Math.floor(length / pattern.length));
|
|
6547
|
+
if (candidate.length < length) candidate += pattern.slice(0, length - candidate.length);
|
|
6548
|
+
candidate = candidate.trim();
|
|
6549
|
+
if (candidate.length !== length) return null;
|
|
6550
|
+
let markCount = 0;
|
|
6551
|
+
for (const c of candidate) {
|
|
6552
|
+
if (c !== mark) continue;
|
|
6553
|
+
markCount++;
|
|
6554
|
+
if (markCount >= 3) return candidate;
|
|
6555
|
+
}
|
|
6556
|
+
return null;
|
|
6557
|
+
}
|
|
6558
|
+
|
|
6559
|
+
//#endregion
|
|
6560
|
+
//#region src/rules/thematic-break-length.ts
|
|
6561
|
+
var thematic_break_length_default = createRule("thematic-break-length", {
|
|
6562
|
+
meta: {
|
|
6563
|
+
type: "layout",
|
|
6564
|
+
docs: {
|
|
6565
|
+
description: "enforce consistent length for thematic breaks (horizontal rules) in Markdown.",
|
|
6566
|
+
categories: [],
|
|
6567
|
+
listCategory: "Stylistic"
|
|
6568
|
+
},
|
|
6569
|
+
fixable: "code",
|
|
6570
|
+
hasSuggestions: false,
|
|
6571
|
+
schema: [{
|
|
6572
|
+
type: "object",
|
|
6573
|
+
properties: { length: {
|
|
6574
|
+
type: "integer",
|
|
6575
|
+
minimum: 3
|
|
6576
|
+
} },
|
|
6577
|
+
additionalProperties: false
|
|
6578
|
+
}],
|
|
6579
|
+
messages: { unexpected: "Thematic break should be {{expected}} characters, but found {{actual}}." }
|
|
6580
|
+
},
|
|
6581
|
+
create(context) {
|
|
6582
|
+
const option = context.options[0] || {};
|
|
6583
|
+
const expectedLength = option.length ?? 3;
|
|
6584
|
+
const sourceCode = context.sourceCode;
|
|
6585
|
+
return { thematicBreak(node) {
|
|
6586
|
+
const marker = getThematicBreakMarker(sourceCode, node);
|
|
6587
|
+
if (marker.text.length === expectedLength) return;
|
|
6588
|
+
context.report({
|
|
6589
|
+
node,
|
|
6590
|
+
messageId: "unexpected",
|
|
6591
|
+
data: {
|
|
6592
|
+
expected: String(expectedLength),
|
|
6593
|
+
actual: String(marker.text.length)
|
|
6594
|
+
},
|
|
6595
|
+
fix(fixer) {
|
|
6596
|
+
const sequence = replacementSequence(marker);
|
|
6597
|
+
if (!sequence) return null;
|
|
6598
|
+
return fixer.replaceText(node, sequence);
|
|
6599
|
+
}
|
|
6600
|
+
});
|
|
6601
|
+
} };
|
|
6602
|
+
/**
|
|
6603
|
+
* Replace the sequence in the thematic break marker with the expected length.
|
|
6604
|
+
*/
|
|
6605
|
+
function replacementSequence(marker) {
|
|
6606
|
+
if (marker.hasSpaces) {
|
|
6607
|
+
const pattern = inferSequencePattern(marker.text);
|
|
6608
|
+
if (pattern) return createThematicBreakFromPattern(pattern, expectedLength);
|
|
6609
|
+
return null;
|
|
6610
|
+
}
|
|
6611
|
+
return marker.kind.repeat(expectedLength);
|
|
6612
|
+
}
|
|
6613
|
+
/**
|
|
6614
|
+
* Infer sequence pattern from the original string.
|
|
6615
|
+
*/
|
|
6616
|
+
function inferSequencePattern(original) {
|
|
6617
|
+
for (let length = 2; length < original.length; length++) {
|
|
6618
|
+
const pattern = original.slice(0, length);
|
|
6619
|
+
if (isValidThematicBreakPattern(pattern, original)) return pattern;
|
|
6620
|
+
}
|
|
6621
|
+
return null;
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
});
|
|
6625
|
+
|
|
6626
|
+
//#endregion
|
|
6627
|
+
//#region src/rules/thematic-break-sequence-pattern.ts
|
|
6628
|
+
var thematic_break_sequence_pattern_default = createRule("thematic-break-sequence-pattern", {
|
|
6629
|
+
meta: {
|
|
6630
|
+
type: "layout",
|
|
6631
|
+
docs: {
|
|
6632
|
+
description: "enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown.",
|
|
6633
|
+
categories: [],
|
|
6634
|
+
listCategory: "Stylistic"
|
|
6635
|
+
},
|
|
6636
|
+
fixable: "code",
|
|
6637
|
+
hasSuggestions: false,
|
|
6638
|
+
schema: [{
|
|
6639
|
+
type: "object",
|
|
6640
|
+
properties: { pattern: { anyOf: [
|
|
6641
|
+
{
|
|
6642
|
+
type: "string",
|
|
6643
|
+
minLength: 1,
|
|
6644
|
+
pattern: "^\\-[ \\-]*$"
|
|
6645
|
+
},
|
|
6646
|
+
{
|
|
6647
|
+
type: "string",
|
|
6648
|
+
minLength: 1,
|
|
6649
|
+
pattern: "^\\*[ *]*$"
|
|
6650
|
+
},
|
|
6651
|
+
{
|
|
6652
|
+
type: "string",
|
|
6653
|
+
minLength: 1,
|
|
6654
|
+
pattern: "^_[ _]*$"
|
|
6655
|
+
}
|
|
6656
|
+
] } },
|
|
6657
|
+
required: ["pattern"],
|
|
6658
|
+
additionalProperties: false
|
|
6659
|
+
}],
|
|
6660
|
+
messages: { inconsistentPattern: "Thematic break does not match the preferred repeating pattern '{{pattern}}'." }
|
|
6661
|
+
},
|
|
6662
|
+
create(context) {
|
|
6663
|
+
const option = context.options[0] || {};
|
|
6664
|
+
const pattern = option.pattern ?? "-";
|
|
6665
|
+
const sourceCode = context.sourceCode;
|
|
6666
|
+
const patterns = {
|
|
6667
|
+
"-": pattern.replaceAll(/[*_]/gu, "-"),
|
|
6668
|
+
"*": pattern.replaceAll(/[-_]/gu, "*"),
|
|
6669
|
+
_: pattern.replaceAll(/[*-]/gu, "_")
|
|
6670
|
+
};
|
|
6671
|
+
return { thematicBreak(node) {
|
|
6672
|
+
const marker = getThematicBreakMarker(sourceCode, node);
|
|
6673
|
+
const patternForKind = patterns[marker.kind];
|
|
6674
|
+
if (isValidThematicBreakPattern(patternForKind, marker.text)) return;
|
|
6675
|
+
context.report({
|
|
6676
|
+
node,
|
|
6677
|
+
messageId: "inconsistentPattern",
|
|
6678
|
+
data: { pattern },
|
|
6679
|
+
fix(fixer) {
|
|
6680
|
+
const replacement = createThematicBreakFromPattern(patternForKind, marker.text.length);
|
|
6681
|
+
if (!replacement) return null;
|
|
6682
|
+
return fixer.replaceText(node, replacement);
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
} };
|
|
6686
|
+
}
|
|
6687
|
+
});
|
|
6688
|
+
|
|
5539
6689
|
//#endregion
|
|
5540
6690
|
//#region src/utils/rules.ts
|
|
5541
6691
|
const rules$1 = [
|
|
5542
|
-
|
|
5543
|
-
|
|
6692
|
+
atx_heading_closing_sequence_length_default,
|
|
6693
|
+
atx_heading_closing_sequence_default,
|
|
5544
6694
|
blockquote_marker_alignment_default,
|
|
6695
|
+
bullet_list_marker_style_default,
|
|
5545
6696
|
canonical_code_block_language_default,
|
|
5546
6697
|
definitions_last_default,
|
|
5547
6698
|
emoji_notation_default,
|
|
5548
6699
|
hard_linebreak_style_default,
|
|
5549
6700
|
heading_casing_default,
|
|
6701
|
+
level1_heading_style_default,
|
|
6702
|
+
level2_heading_style_default,
|
|
5550
6703
|
list_marker_alignment_default,
|
|
5551
6704
|
no_laziness_blockquotes_default,
|
|
5552
6705
|
no_multiple_empty_lines_default,
|
|
@@ -5554,14 +6707,19 @@ const rules$1 = [
|
|
|
5554
6707
|
no_trailing_spaces_default,
|
|
5555
6708
|
ordered_list_marker_sequence_default,
|
|
5556
6709
|
ordered_list_marker_start_default,
|
|
6710
|
+
ordered_list_marker_style_default,
|
|
5557
6711
|
padding_line_between_blocks_default,
|
|
5558
6712
|
prefer_autolinks_default,
|
|
5559
6713
|
prefer_fenced_code_blocks_default,
|
|
5560
6714
|
prefer_inline_code_words_default,
|
|
5561
6715
|
prefer_link_reference_definitions_default,
|
|
5562
6716
|
prefer_linked_words_default,
|
|
6717
|
+
setext_heading_underline_length_default,
|
|
5563
6718
|
sort_definitions_default,
|
|
5564
|
-
table_header_casing_default
|
|
6719
|
+
table_header_casing_default,
|
|
6720
|
+
thematic_break_character_style_default,
|
|
6721
|
+
thematic_break_length_default,
|
|
6722
|
+
thematic_break_sequence_pattern_default
|
|
5565
6723
|
];
|
|
5566
6724
|
|
|
5567
6725
|
//#endregion
|
|
@@ -5604,7 +6762,7 @@ __export(meta_exports, {
|
|
|
5604
6762
|
version: () => version
|
|
5605
6763
|
});
|
|
5606
6764
|
const name = "eslint-plugin-markdown-preferences";
|
|
5607
|
-
const version = "0.
|
|
6765
|
+
const version = "0.18.0";
|
|
5608
6766
|
|
|
5609
6767
|
//#endregion
|
|
5610
6768
|
//#region src/index.ts
|