eslint-plugin-markdown-preferences 0.23.0 → 0.25.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 +74 -48
- package/lib/{chunk-Cl8Af3a2.js → chunk-CTAAG5j7.js} +3 -1
- package/lib/index.d.ts +47 -1
- package/lib/index.js +2898 -901
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __export } from "./chunk-
|
|
1
|
+
import { __export } from "./chunk-CTAAG5j7.js";
|
|
2
2
|
import stringWidth from "string-width";
|
|
3
3
|
import emojiRegex from "emoji-regex-xs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -109,13 +109,20 @@ function getThematicBreakMarker(sourceCode, node) {
|
|
|
109
109
|
* Get the source location from a range in a node.
|
|
110
110
|
*/
|
|
111
111
|
function getSourceLocationFromRange(sourceCode, node, range) {
|
|
112
|
-
const
|
|
112
|
+
const nodeRange = sourceCode.getRange(node);
|
|
113
|
+
const loc = sourceCode.getLoc(node);
|
|
114
|
+
if (nodeRange[1] <= range[0]) return getSourceLocationFromRangeAndSourcePosition(sourceCode, nodeRange[1], loc.end, range);
|
|
115
|
+
return getSourceLocationFromRangeAndSourcePosition(sourceCode, nodeRange[0], loc.start, range);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the source location from a range
|
|
119
|
+
*/
|
|
120
|
+
function getSourceLocationFromRangeAndSourcePosition(sourceCode, startIndex, startLoc, range) {
|
|
113
121
|
let startLine, startColumn;
|
|
114
|
-
if (
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
startColumn = (beforeLines.length === 1 ? loc.start.column : 1) + (beforeLines.at(-1) || "").length;
|
|
122
|
+
if (startIndex <= range[0]) {
|
|
123
|
+
const beforeLines = sourceCode.text.slice(startIndex, range[0]).split(/\n/u);
|
|
124
|
+
startLine = startLoc.line + beforeLines.length - 1;
|
|
125
|
+
startColumn = (beforeLines.length === 1 ? startLoc.column : 1) + (beforeLines.at(-1) || "").length;
|
|
119
126
|
} else {
|
|
120
127
|
const beforeLines = sourceCode.text.slice(0, range[0]).split(/\n/u);
|
|
121
128
|
startLine = beforeLines.length;
|
|
@@ -364,15 +371,23 @@ function getParsedLines(sourceCode) {
|
|
|
364
371
|
}
|
|
365
372
|
|
|
366
373
|
//#endregion
|
|
367
|
-
//#region src/utils/
|
|
374
|
+
//#region src/utils/text-width.ts
|
|
368
375
|
let segmenter;
|
|
369
376
|
/**
|
|
370
377
|
* Get the width of a text string.
|
|
371
378
|
*/
|
|
372
|
-
function getTextWidth(text) {
|
|
373
|
-
if (!text.includes(" ")) return stringWidth(text);
|
|
379
|
+
function getTextWidth(text, start = 0, end = text.length) {
|
|
380
|
+
if (!text.includes(" ")) return stringWidth(text.slice(start, end));
|
|
374
381
|
if (!segmenter) segmenter = new Intl.Segmenter("en");
|
|
375
|
-
|
|
382
|
+
const prefixWidth = getTextWidthBySegment(text.slice(0, start), 0);
|
|
383
|
+
return getTextWidthBySegment(text.slice(start, end), prefixWidth);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Get the width of a text string by segmenter.
|
|
387
|
+
*/
|
|
388
|
+
function getTextWidthBySegment(text, startWidth) {
|
|
389
|
+
if (!segmenter) segmenter = new Intl.Segmenter("en");
|
|
390
|
+
let width = startWidth;
|
|
376
391
|
for (const { segment: c } of segmenter.segment(text)) if (c === " ") width += 4 - width % 4;
|
|
377
392
|
else width += stringWidth(c);
|
|
378
393
|
return width;
|
|
@@ -386,7 +401,7 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
|
|
|
386
401
|
docs: {
|
|
387
402
|
description: "enforce consistent length for the closing sequence (trailing #s) in ATX headings.",
|
|
388
403
|
categories: ["standard"],
|
|
389
|
-
listCategory: "
|
|
404
|
+
listCategory: "Decorative"
|
|
390
405
|
},
|
|
391
406
|
fixable: "code",
|
|
392
407
|
hasSuggestions: false,
|
|
@@ -524,7 +539,7 @@ var atx_heading_closing_sequence_default = createRule("atx-heading-closing-seque
|
|
|
524
539
|
docs: {
|
|
525
540
|
description: "enforce consistent use of closing sequence in ATX headings.",
|
|
526
541
|
categories: ["standard"],
|
|
527
|
-
listCategory: "
|
|
542
|
+
listCategory: "Decorative"
|
|
528
543
|
},
|
|
529
544
|
fixable: "code",
|
|
530
545
|
hasSuggestions: false,
|
|
@@ -600,12 +615,28 @@ function getBlockquoteLevelFromLine(sourceCode, lineNumber) {
|
|
|
600
615
|
const lineText = sourceCode.lines[lineNumber - 1];
|
|
601
616
|
let prefix = "";
|
|
602
617
|
let level = 0;
|
|
618
|
+
let width = 0;
|
|
619
|
+
let leadingMarkerOffset = 0;
|
|
603
620
|
const blockquoteMarkers = /* @__PURE__ */ new Map();
|
|
604
621
|
for (const c of lineText) {
|
|
605
622
|
if (c === ">") {
|
|
623
|
+
if (width - leadingMarkerOffset > 3) break;
|
|
624
|
+
leadingMarkerOffset = width + 1;
|
|
606
625
|
level++;
|
|
607
|
-
blockquoteMarkers.set(level, {
|
|
608
|
-
|
|
626
|
+
blockquoteMarkers.set(level, { loc: {
|
|
627
|
+
start: {
|
|
628
|
+
line: lineNumber,
|
|
629
|
+
column: prefix.length + 1
|
|
630
|
+
},
|
|
631
|
+
end: {
|
|
632
|
+
line: lineNumber,
|
|
633
|
+
column: prefix.length + 2
|
|
634
|
+
}
|
|
635
|
+
} });
|
|
636
|
+
} else if (!isSpaceOrTab(c)) break;
|
|
637
|
+
if (c === " ") width += 4 - width % 4;
|
|
638
|
+
else width++;
|
|
639
|
+
if (c !== ">" && prefix.at(-1) === ">") leadingMarkerOffset++;
|
|
609
640
|
prefix += c;
|
|
610
641
|
}
|
|
611
642
|
const result = {
|
|
@@ -618,6 +649,64 @@ function getBlockquoteLevelFromLine(sourceCode, lineNumber) {
|
|
|
618
649
|
return result;
|
|
619
650
|
}
|
|
620
651
|
|
|
652
|
+
//#endregion
|
|
653
|
+
//#region src/utils/width.ts
|
|
654
|
+
/**
|
|
655
|
+
* Get the visual width of the string.
|
|
656
|
+
*/
|
|
657
|
+
function getWidth(str) {
|
|
658
|
+
let width = 0;
|
|
659
|
+
for (const c of str) if (c === " ") width += 4 - width % 4;
|
|
660
|
+
else width++;
|
|
661
|
+
return width;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Get a slice of the string by visual width.
|
|
665
|
+
*/
|
|
666
|
+
function sliceWidth(str, start, end) {
|
|
667
|
+
const buffer = [...str];
|
|
668
|
+
let width = 0;
|
|
669
|
+
let c;
|
|
670
|
+
while (c = buffer.shift()) {
|
|
671
|
+
if (start <= width) {
|
|
672
|
+
buffer.unshift(c);
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
if (c === " ") width += 4 - width % 4;
|
|
676
|
+
else width++;
|
|
677
|
+
}
|
|
678
|
+
if (buffer.length === 0) return "";
|
|
679
|
+
let result = " ".repeat(width - start);
|
|
680
|
+
if (end == null) return `${result}${buffer.join("")}`;
|
|
681
|
+
while (c = buffer.shift()) {
|
|
682
|
+
let newWidth;
|
|
683
|
+
if (c === " ") newWidth = width + 4 - width % 4;
|
|
684
|
+
else newWidth = width + 1;
|
|
685
|
+
if (end < newWidth) {
|
|
686
|
+
buffer.unshift(c);
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
result += c;
|
|
690
|
+
width = newWidth;
|
|
691
|
+
}
|
|
692
|
+
if (buffer.length === 0) return result;
|
|
693
|
+
result += " ".repeat(end - width);
|
|
694
|
+
return result;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Get the character at the visual width.
|
|
698
|
+
*/
|
|
699
|
+
function atWidth(str, target) {
|
|
700
|
+
let width = 0;
|
|
701
|
+
for (const c of str) {
|
|
702
|
+
if (target === width) return c;
|
|
703
|
+
if (target < width) return " ";
|
|
704
|
+
if (c === " ") width += 4 - width % 4;
|
|
705
|
+
else width++;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
|
|
621
710
|
//#endregion
|
|
622
711
|
//#region src/rules/blockquote-marker-alignment.ts
|
|
623
712
|
var blockquote_marker_alignment_default = createRule("blockquote-marker-alignment", {
|
|
@@ -626,7 +715,7 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
|
|
|
626
715
|
docs: {
|
|
627
716
|
description: "enforce consistent alignment of blockquote markers",
|
|
628
717
|
categories: ["recommended", "standard"],
|
|
629
|
-
listCategory: "
|
|
718
|
+
listCategory: "Whitespace"
|
|
630
719
|
},
|
|
631
720
|
fixable: "whitespace",
|
|
632
721
|
hasSuggestions: false,
|
|
@@ -655,38 +744,33 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
|
|
|
655
744
|
const endLine = loc.end.line;
|
|
656
745
|
const base = getBlockquoteLevelFromLine(sourceCode, startLine).blockquoteMarkers.get(blockquoteLevel);
|
|
657
746
|
if (!base) return;
|
|
747
|
+
const baseBeforeMarker = sourceCode.lines[startLine - 1].slice(0, base.loc.start.column - 1);
|
|
748
|
+
const baseIndentWidth = getWidth(baseBeforeMarker);
|
|
658
749
|
for (let lineNumber = startLine + 1; lineNumber <= endLine; lineNumber++) {
|
|
659
750
|
const marker = getBlockquoteLevelFromLine(sourceCode, lineNumber).blockquoteMarkers.get(blockquoteLevel);
|
|
660
751
|
if (!marker) continue;
|
|
661
|
-
|
|
752
|
+
const indentWidth = getWidth(sourceCode.lines[lineNumber - 1].slice(0, marker.loc.start.column - 1));
|
|
753
|
+
if (baseIndentWidth === indentWidth) continue;
|
|
662
754
|
blockquoteStack.reported = true;
|
|
663
755
|
context.report({
|
|
664
756
|
node,
|
|
665
|
-
loc:
|
|
666
|
-
start: {
|
|
667
|
-
line: lineNumber,
|
|
668
|
-
column: marker.index + 1
|
|
669
|
-
},
|
|
670
|
-
end: {
|
|
671
|
-
line: lineNumber,
|
|
672
|
-
column: marker.index + 2
|
|
673
|
-
}
|
|
674
|
-
},
|
|
757
|
+
loc: marker.loc,
|
|
675
758
|
messageId: "inconsistentAlignment",
|
|
676
759
|
fix(fixer) {
|
|
677
760
|
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
678
|
-
if (
|
|
679
|
-
const addSpaces = " ".repeat(
|
|
680
|
-
return fixer.insertTextBeforeRange([line.range[0] + marker.
|
|
761
|
+
if (indentWidth < baseIndentWidth) {
|
|
762
|
+
const addSpaces = " ".repeat(baseIndentWidth - indentWidth);
|
|
763
|
+
return fixer.insertTextBeforeRange([line.range[0] + marker.loc.start.column - 1, line.range[0] + marker.loc.start.column - 1], addSpaces);
|
|
681
764
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
765
|
+
let newBeforeMarker = line.text.slice(0, marker.loc.start.column - 1);
|
|
766
|
+
while (getWidth(newBeforeMarker) > baseIndentWidth) {
|
|
767
|
+
const last = newBeforeMarker.at(-1);
|
|
768
|
+
if (last && isWhitespace(last)) newBeforeMarker = newBeforeMarker.slice(0, -1);
|
|
769
|
+
else return null;
|
|
685
770
|
}
|
|
686
|
-
if (
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
return fixer.removeRange([line.range[0] + removeStartIndex, line.range[0] + marker.index]);
|
|
771
|
+
if (getWidth(newBeforeMarker) < baseIndentWidth) newBeforeMarker += " ".repeat(baseIndentWidth - getWidth(newBeforeMarker));
|
|
772
|
+
if (!baseBeforeMarker.includes(">") || baseBeforeMarker === newBeforeMarker) return fixer.replaceTextRange([line.range[0], line.range[0] + marker.loc.start.column - 1], newBeforeMarker);
|
|
773
|
+
return null;
|
|
690
774
|
}
|
|
691
775
|
});
|
|
692
776
|
}
|
|
@@ -714,7 +798,7 @@ function getOtherMarker(unavailableMarker) {
|
|
|
714
798
|
/**
|
|
715
799
|
* Parse rule options.
|
|
716
800
|
*/
|
|
717
|
-
function parseOptions$
|
|
801
|
+
function parseOptions$4(options) {
|
|
718
802
|
const primary = options.primary || "-";
|
|
719
803
|
const secondary = options.secondary || getOtherMarker(primary);
|
|
720
804
|
if (primary === secondary) throw new Error(`\`primary\` and \`secondary\` cannot be the same (primary: "${primary}", secondary: "${secondary}").`);
|
|
@@ -746,7 +830,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
|
746
830
|
docs: {
|
|
747
831
|
description: "enforce consistent bullet list (unordered list) marker style",
|
|
748
832
|
categories: ["standard"],
|
|
749
|
-
listCategory: "
|
|
833
|
+
listCategory: "Notation"
|
|
750
834
|
},
|
|
751
835
|
fixable: "code",
|
|
752
836
|
hasSuggestions: false,
|
|
@@ -782,7 +866,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
|
782
866
|
},
|
|
783
867
|
create(context) {
|
|
784
868
|
const sourceCode = context.sourceCode;
|
|
785
|
-
const options = parseOptions$
|
|
869
|
+
const options = parseOptions$4(context.options[0] || {});
|
|
786
870
|
let containerStack = {
|
|
787
871
|
node: sourceCode.ast,
|
|
788
872
|
level: 1,
|
|
@@ -1045,7 +1129,7 @@ var code_fence_length_default = createRule("code-fence-length", {
|
|
|
1045
1129
|
docs: {
|
|
1046
1130
|
description: "enforce consistent code fence length in fenced code blocks.",
|
|
1047
1131
|
categories: ["standard"],
|
|
1048
|
-
listCategory: "
|
|
1132
|
+
listCategory: "Decorative"
|
|
1049
1133
|
},
|
|
1050
1134
|
fixable: "code",
|
|
1051
1135
|
hasSuggestions: false,
|
|
@@ -1182,7 +1266,7 @@ var code_fence_style_default = createRule("code-fence-style", {
|
|
|
1182
1266
|
docs: {
|
|
1183
1267
|
description: "enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.",
|
|
1184
1268
|
categories: ["standard"],
|
|
1185
|
-
listCategory: "
|
|
1269
|
+
listCategory: "Notation"
|
|
1186
1270
|
},
|
|
1187
1271
|
fixable: "code",
|
|
1188
1272
|
hasSuggestions: false,
|
|
@@ -1229,7 +1313,7 @@ var definitions_last_default = createRule("definitions-last", {
|
|
|
1229
1313
|
docs: {
|
|
1230
1314
|
description: "require link definitions and footnote definitions to be placed at the end of the document",
|
|
1231
1315
|
categories: [],
|
|
1232
|
-
listCategory: "
|
|
1316
|
+
listCategory: "Notation"
|
|
1233
1317
|
},
|
|
1234
1318
|
fixable: "code",
|
|
1235
1319
|
hasSuggestions: false,
|
|
@@ -3353,7 +3437,7 @@ var emphasis_delimiters_style_default = createRule("emphasis-delimiters-style",
|
|
|
3353
3437
|
docs: {
|
|
3354
3438
|
description: "enforce a consistent delimiter style for emphasis and strong emphasis",
|
|
3355
3439
|
categories: ["standard"],
|
|
3356
|
-
listCategory: "
|
|
3440
|
+
listCategory: "Notation"
|
|
3357
3441
|
},
|
|
3358
3442
|
fixable: "code",
|
|
3359
3443
|
hasSuggestions: false,
|
|
@@ -3535,7 +3619,7 @@ var hard_linebreak_style_default = createRule("hard-linebreak-style", {
|
|
|
3535
3619
|
docs: {
|
|
3536
3620
|
description: "enforce consistent hard linebreak style.",
|
|
3537
3621
|
categories: ["recommended", "standard"],
|
|
3538
|
-
listCategory: "
|
|
3622
|
+
listCategory: "Notation"
|
|
3539
3623
|
},
|
|
3540
3624
|
fixable: "code",
|
|
3541
3625
|
hasSuggestions: false,
|
|
@@ -4334,275 +4418,224 @@ var heading_casing_default = createRule("heading-casing", {
|
|
|
4334
4418
|
});
|
|
4335
4419
|
|
|
4336
4420
|
//#endregion
|
|
4337
|
-
//#region src/utils/
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
if (getHeadingKind(sourceCode, node) !== "setext") return null;
|
|
4343
|
-
const lines = getParsedLines(sourceCode);
|
|
4344
|
-
const contentLines = [];
|
|
4345
|
-
const nodeLoc = sourceCode.getLoc(node);
|
|
4346
|
-
for (let lineNumber = nodeLoc.start.line; lineNumber < nodeLoc.end.line; lineNumber++) {
|
|
4347
|
-
const content = parseContent(lines.get(lineNumber));
|
|
4348
|
-
contentLines.push(content);
|
|
4421
|
+
//#region src/utils/character-cursor.ts
|
|
4422
|
+
var CharacterCursor = class {
|
|
4423
|
+
text;
|
|
4424
|
+
constructor(text) {
|
|
4425
|
+
this.text = text;
|
|
4349
4426
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
}
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4427
|
+
curr() {
|
|
4428
|
+
return this.text[this.index];
|
|
4429
|
+
}
|
|
4430
|
+
currIndex() {
|
|
4431
|
+
return this.index;
|
|
4432
|
+
}
|
|
4433
|
+
setCurrIndex(index) {
|
|
4434
|
+
this.index = index;
|
|
4435
|
+
}
|
|
4436
|
+
isWhitespace(index) {
|
|
4437
|
+
if (index >= this.text.length) return false;
|
|
4438
|
+
const ch = this.text[index];
|
|
4439
|
+
if (isWhitespace(ch)) return true;
|
|
4440
|
+
if (ch !== ">") return false;
|
|
4441
|
+
const prefix = [ch];
|
|
4442
|
+
for (let prev = index - 1; prev >= 0; prev--) {
|
|
4443
|
+
const prevCh$1 = this.text[prev];
|
|
4444
|
+
if (prevCh$1 === "\n") break;
|
|
4445
|
+
if (isSpaceOrTab(prevCh$1) || prevCh$1 === ">") {
|
|
4446
|
+
prefix.unshift(prevCh$1);
|
|
4447
|
+
continue;
|
|
4448
|
+
}
|
|
4449
|
+
return false;
|
|
4369
4450
|
}
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4451
|
+
let width = 0;
|
|
4452
|
+
let leadingMarkerOffset = 0;
|
|
4453
|
+
let prevCh;
|
|
4454
|
+
for (const currCh of prefix) {
|
|
4455
|
+
if (currCh === ">") {
|
|
4456
|
+
if (width - leadingMarkerOffset > 3) return false;
|
|
4457
|
+
leadingMarkerOffset = width + 1;
|
|
4458
|
+
}
|
|
4459
|
+
if (currCh === " ") width += 4 - width % 4;
|
|
4460
|
+
else width++;
|
|
4461
|
+
if (prevCh === ">" && isSpaceOrTab(currCh)) leadingMarkerOffset++;
|
|
4462
|
+
prevCh = currCh;
|
|
4374
4463
|
}
|
|
4375
|
-
|
|
4376
|
-
break;
|
|
4464
|
+
return true;
|
|
4377
4465
|
}
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4466
|
+
};
|
|
4467
|
+
var ForwardCharacterCursor = class extends CharacterCursor {
|
|
4468
|
+
index;
|
|
4469
|
+
constructor(text) {
|
|
4470
|
+
super(text);
|
|
4471
|
+
this.index = 0;
|
|
4472
|
+
}
|
|
4473
|
+
next() {
|
|
4474
|
+
this.index++;
|
|
4475
|
+
return this.text[this.index];
|
|
4476
|
+
}
|
|
4477
|
+
finished() {
|
|
4478
|
+
return this.index >= this.text.length;
|
|
4479
|
+
}
|
|
4480
|
+
skipSpaces() {
|
|
4481
|
+
while (this.index < this.text.length && this.isWhitespace(this.index)) this.index++;
|
|
4482
|
+
}
|
|
4483
|
+
/**
|
|
4484
|
+
* Skip until the end by the given condition
|
|
4485
|
+
*/
|
|
4486
|
+
skipUntilEnd(checkEnd) {
|
|
4487
|
+
while (this.index < this.text.length) {
|
|
4488
|
+
const c = this.text[this.index];
|
|
4489
|
+
if (checkEnd(c, this.index)) return true;
|
|
4490
|
+
this.index++;
|
|
4491
|
+
if (c !== "\\") continue;
|
|
4492
|
+
if (this.index < this.text.length && (this.text[this.index] === "\\" || checkEnd(this.text[this.index], this.index)) && !isWhitespace(this.text[this.index])) this.index++;
|
|
4493
|
+
}
|
|
4494
|
+
return false;
|
|
4495
|
+
}
|
|
4496
|
+
};
|
|
4497
|
+
var BackwardCharacterCursor = class extends CharacterCursor {
|
|
4498
|
+
index;
|
|
4499
|
+
constructor(text) {
|
|
4500
|
+
super(text);
|
|
4501
|
+
this.index = text.length - 1;
|
|
4502
|
+
}
|
|
4503
|
+
prev() {
|
|
4504
|
+
this.index--;
|
|
4505
|
+
return this.text[this.index];
|
|
4506
|
+
}
|
|
4507
|
+
finished() {
|
|
4508
|
+
return this.index < 0;
|
|
4509
|
+
}
|
|
4510
|
+
skipSpaces() {
|
|
4511
|
+
while (this.index >= 0 && this.isWhitespace(this.index)) this.index--;
|
|
4512
|
+
}
|
|
4513
|
+
/**
|
|
4514
|
+
* Skip until the start by the given condition
|
|
4515
|
+
*/
|
|
4516
|
+
skipUntilStart(checkStart) {
|
|
4517
|
+
while (this.index >= 0) {
|
|
4518
|
+
const c = this.text[this.index];
|
|
4519
|
+
if (checkStart(c, this.index)) {
|
|
4520
|
+
if (this.index > 1 && !isWhitespace(c)) {
|
|
4521
|
+
let escapeText = "";
|
|
4522
|
+
while (this.text.endsWith(`${escapeText}\\`, this.index)) escapeText += "\\";
|
|
4523
|
+
if (escapeText.length % 2 === 1) {
|
|
4524
|
+
this.index -= escapeText.length + 1;
|
|
4525
|
+
continue;
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
return true;
|
|
4391
4529
|
}
|
|
4392
|
-
|
|
4393
|
-
raws: {
|
|
4394
|
-
prefix,
|
|
4395
|
-
spaceBefore,
|
|
4396
|
-
spaceAfter
|
|
4530
|
+
this.index--;
|
|
4397
4531
|
}
|
|
4532
|
+
return false;
|
|
4533
|
+
}
|
|
4534
|
+
};
|
|
4535
|
+
|
|
4536
|
+
//#endregion
|
|
4537
|
+
//#region src/utils/link-definition.ts
|
|
4538
|
+
/**
|
|
4539
|
+
* Parse the link definition.
|
|
4540
|
+
*/
|
|
4541
|
+
function parseLinkDefinition(sourceCode, node) {
|
|
4542
|
+
const text = sourceCode.getText(node);
|
|
4543
|
+
const parsed = parseLinkDefinitionFromText(text);
|
|
4544
|
+
if (!parsed) return null;
|
|
4545
|
+
const nodeRange = sourceCode.getRange(node);
|
|
4546
|
+
const labelRange = [nodeRange[0] + parsed.label.range[0], nodeRange[0] + parsed.label.range[1]];
|
|
4547
|
+
const destinationRange = [nodeRange[0] + parsed.destination.range[0], nodeRange[0] + parsed.destination.range[1]];
|
|
4548
|
+
return {
|
|
4549
|
+
label: {
|
|
4550
|
+
text: parsed.label.text,
|
|
4551
|
+
range: labelRange,
|
|
4552
|
+
loc: getSourceLocationFromRange(sourceCode, node, labelRange)
|
|
4553
|
+
},
|
|
4554
|
+
destination: {
|
|
4555
|
+
type: parsed.destination.type,
|
|
4556
|
+
text: parsed.destination.text,
|
|
4557
|
+
range: destinationRange,
|
|
4558
|
+
loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
|
|
4559
|
+
},
|
|
4560
|
+
title: parsed.title ? {
|
|
4561
|
+
type: parsed.title.type,
|
|
4562
|
+
text: parsed.title.text,
|
|
4563
|
+
range: [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]],
|
|
4564
|
+
loc: getSourceLocationFromRange(sourceCode, node, [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]])
|
|
4565
|
+
} : null
|
|
4398
4566
|
};
|
|
4399
4567
|
}
|
|
4400
4568
|
/**
|
|
4401
|
-
* Parse the
|
|
4569
|
+
* Parse the link definition from the given text.
|
|
4402
4570
|
*/
|
|
4403
|
-
function
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4571
|
+
function parseLinkDefinitionFromText(text) {
|
|
4572
|
+
if (!text.startsWith("[")) return null;
|
|
4573
|
+
const cursor = new ForwardCharacterCursor(text);
|
|
4574
|
+
const labelStartIndex = 0;
|
|
4575
|
+
cursor.next();
|
|
4576
|
+
if (!cursor.skipUntilEnd((c) => c === "]")) return null;
|
|
4577
|
+
cursor.next();
|
|
4578
|
+
const labelRange = [labelStartIndex, cursor.currIndex()];
|
|
4579
|
+
if (!text.slice(labelRange[0] + 1, labelRange[1] - 1).trim()) return null;
|
|
4580
|
+
if (cursor.curr() !== ":") return null;
|
|
4581
|
+
const label = {
|
|
4582
|
+
text: text.slice(...labelRange),
|
|
4583
|
+
range: labelRange
|
|
4584
|
+
};
|
|
4585
|
+
cursor.next();
|
|
4586
|
+
cursor.skipSpaces();
|
|
4587
|
+
let destination;
|
|
4588
|
+
const destinationStartIndex = cursor.currIndex();
|
|
4589
|
+
if (cursor.curr() === "<") {
|
|
4590
|
+
cursor.next();
|
|
4591
|
+
if (!cursor.skipUntilEnd((c) => c === ">")) return null;
|
|
4592
|
+
cursor.next();
|
|
4593
|
+
const destinationRange = [destinationStartIndex, cursor.currIndex()];
|
|
4594
|
+
destination = {
|
|
4595
|
+
type: "pointy-bracketed",
|
|
4596
|
+
text: text.slice(...destinationRange),
|
|
4597
|
+
range: destinationRange
|
|
4598
|
+
};
|
|
4599
|
+
} else {
|
|
4600
|
+
if (cursor.finished()) return null;
|
|
4601
|
+
cursor.skipUntilEnd((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c));
|
|
4602
|
+
const destinationRange = [destinationStartIndex, cursor.currIndex()];
|
|
4603
|
+
destination = {
|
|
4604
|
+
type: "bare",
|
|
4605
|
+
text: text.slice(...destinationRange),
|
|
4606
|
+
range: destinationRange
|
|
4607
|
+
};
|
|
4427
4608
|
}
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
},
|
|
4434
|
-
end: {
|
|
4435
|
-
line: line.line,
|
|
4436
|
-
column: prefix.length + spaceBefore.length + underlineText.length + 1
|
|
4437
|
-
}
|
|
4609
|
+
cursor.skipSpaces();
|
|
4610
|
+
if (cursor.finished()) return {
|
|
4611
|
+
label,
|
|
4612
|
+
destination,
|
|
4613
|
+
title: null
|
|
4438
4614
|
};
|
|
4615
|
+
let title;
|
|
4616
|
+
const titleStartIndex = cursor.currIndex();
|
|
4617
|
+
const startChar = cursor.curr();
|
|
4618
|
+
if (startChar === "'" || startChar === "\"" || startChar === "(") {
|
|
4619
|
+
cursor.next();
|
|
4620
|
+
const endChar = startChar === "(" ? ")" : startChar;
|
|
4621
|
+
if (!cursor.skipUntilEnd((c) => c === endChar)) return null;
|
|
4622
|
+
cursor.next();
|
|
4623
|
+
const titleRange = [titleStartIndex, cursor.currIndex()];
|
|
4624
|
+
title = {
|
|
4625
|
+
type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
|
|
4626
|
+
text: text.slice(...titleRange),
|
|
4627
|
+
range: titleRange
|
|
4628
|
+
};
|
|
4629
|
+
} else return null;
|
|
4630
|
+
cursor.skipSpaces();
|
|
4631
|
+
if (!cursor.finished()) return null;
|
|
4439
4632
|
return {
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
marker,
|
|
4444
|
-
raws: {
|
|
4445
|
-
prefix,
|
|
4446
|
-
spaceBefore,
|
|
4447
|
-
spaceAfter
|
|
4448
|
-
}
|
|
4633
|
+
label,
|
|
4634
|
+
destination,
|
|
4635
|
+
title
|
|
4449
4636
|
};
|
|
4450
4637
|
}
|
|
4451
4638
|
|
|
4452
|
-
//#endregion
|
|
4453
|
-
//#region src/rules/level1-heading-style.ts
|
|
4454
|
-
var level1_heading_style_default = createRule("level1-heading-style", {
|
|
4455
|
-
meta: {
|
|
4456
|
-
type: "layout",
|
|
4457
|
-
docs: {
|
|
4458
|
-
description: "enforce consistent style for level 1 headings",
|
|
4459
|
-
categories: ["standard"],
|
|
4460
|
-
listCategory: "Stylistic"
|
|
4461
|
-
},
|
|
4462
|
-
fixable: "code",
|
|
4463
|
-
hasSuggestions: false,
|
|
4464
|
-
schema: [{
|
|
4465
|
-
type: "object",
|
|
4466
|
-
properties: {
|
|
4467
|
-
style: { enum: ["atx", "setext"] },
|
|
4468
|
-
allowMultilineSetext: { type: "boolean" }
|
|
4469
|
-
},
|
|
4470
|
-
additionalProperties: false
|
|
4471
|
-
}],
|
|
4472
|
-
messages: {
|
|
4473
|
-
expectedAtx: "Expected ATX style heading (# Heading).",
|
|
4474
|
-
expectedSetext: "Expected Setext style heading (Heading\\n======).",
|
|
4475
|
-
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
4476
|
-
}
|
|
4477
|
-
},
|
|
4478
|
-
create(context) {
|
|
4479
|
-
const sourceCode = context.sourceCode;
|
|
4480
|
-
const opt = context.options[0] || {};
|
|
4481
|
-
const style = opt.style ?? "atx";
|
|
4482
|
-
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
4483
|
-
return { heading(node) {
|
|
4484
|
-
if (node.depth !== 1) return;
|
|
4485
|
-
const headingKind = getHeadingKind(sourceCode, node);
|
|
4486
|
-
if (style === "atx") {
|
|
4487
|
-
if (headingKind !== "setext") return;
|
|
4488
|
-
const parsed = parseSetextHeading(sourceCode, node);
|
|
4489
|
-
if (!parsed) return;
|
|
4490
|
-
if (parsed.contentLines.length > 1) {
|
|
4491
|
-
if (allowMultilineSetext) return;
|
|
4492
|
-
context.report({
|
|
4493
|
-
node,
|
|
4494
|
-
messageId: "multilineSetextNotAllowed"
|
|
4495
|
-
});
|
|
4496
|
-
return;
|
|
4497
|
-
}
|
|
4498
|
-
context.report({
|
|
4499
|
-
node,
|
|
4500
|
-
messageId: "expectedAtx",
|
|
4501
|
-
*fix(fixer) {
|
|
4502
|
-
const heading = parsed.contentLines[0];
|
|
4503
|
-
yield fixer.insertTextBeforeRange(heading.range, "# ");
|
|
4504
|
-
const lines = getParsedLines(sourceCode);
|
|
4505
|
-
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
4506
|
-
}
|
|
4507
|
-
});
|
|
4508
|
-
} else if (style === "setext") {
|
|
4509
|
-
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
4510
|
-
context.report({
|
|
4511
|
-
node,
|
|
4512
|
-
messageId: "expectedSetext",
|
|
4513
|
-
*fix(fixer) {
|
|
4514
|
-
const parsed = parseATXHeading(sourceCode, node);
|
|
4515
|
-
if (!parsed) return;
|
|
4516
|
-
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
|
|
4517
|
-
if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
|
|
4518
|
-
const lines = getParsedLines(sourceCode);
|
|
4519
|
-
const underline = "=".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
4520
|
-
const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
|
|
4521
|
-
yield fixer.insertTextAfter(node, appendingText);
|
|
4522
|
-
}
|
|
4523
|
-
});
|
|
4524
|
-
}
|
|
4525
|
-
} };
|
|
4526
|
-
}
|
|
4527
|
-
});
|
|
4528
|
-
|
|
4529
|
-
//#endregion
|
|
4530
|
-
//#region src/rules/level2-heading-style.ts
|
|
4531
|
-
var level2_heading_style_default = createRule("level2-heading-style", {
|
|
4532
|
-
meta: {
|
|
4533
|
-
type: "layout",
|
|
4534
|
-
docs: {
|
|
4535
|
-
description: "enforce consistent style for level 2 headings",
|
|
4536
|
-
categories: ["standard"],
|
|
4537
|
-
listCategory: "Stylistic"
|
|
4538
|
-
},
|
|
4539
|
-
fixable: "code",
|
|
4540
|
-
hasSuggestions: false,
|
|
4541
|
-
schema: [{
|
|
4542
|
-
type: "object",
|
|
4543
|
-
properties: {
|
|
4544
|
-
style: { enum: ["atx", "setext"] },
|
|
4545
|
-
allowMultilineSetext: { type: "boolean" }
|
|
4546
|
-
},
|
|
4547
|
-
additionalProperties: false
|
|
4548
|
-
}],
|
|
4549
|
-
messages: {
|
|
4550
|
-
expectedAtx: "Expected ATX style heading (## Heading).",
|
|
4551
|
-
expectedSetext: "Expected Setext style heading (Heading\\n------).",
|
|
4552
|
-
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
4553
|
-
}
|
|
4554
|
-
},
|
|
4555
|
-
create(context) {
|
|
4556
|
-
const sourceCode = context.sourceCode;
|
|
4557
|
-
const opt = context.options[0] || {};
|
|
4558
|
-
const style = opt.style ?? "atx";
|
|
4559
|
-
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
4560
|
-
return { heading(node) {
|
|
4561
|
-
if (node.depth !== 2) return;
|
|
4562
|
-
const headingKind = getHeadingKind(sourceCode, node);
|
|
4563
|
-
if (style === "atx") {
|
|
4564
|
-
if (headingKind !== "setext") return;
|
|
4565
|
-
const parsed = parseSetextHeading(sourceCode, node);
|
|
4566
|
-
if (!parsed) return;
|
|
4567
|
-
if (parsed.contentLines.length > 1) {
|
|
4568
|
-
if (allowMultilineSetext) return;
|
|
4569
|
-
context.report({
|
|
4570
|
-
node,
|
|
4571
|
-
messageId: "multilineSetextNotAllowed"
|
|
4572
|
-
});
|
|
4573
|
-
return;
|
|
4574
|
-
}
|
|
4575
|
-
context.report({
|
|
4576
|
-
node,
|
|
4577
|
-
messageId: "expectedAtx",
|
|
4578
|
-
*fix(fixer) {
|
|
4579
|
-
const heading = parsed.contentLines[0];
|
|
4580
|
-
yield fixer.insertTextBeforeRange(heading.range, "## ");
|
|
4581
|
-
const lines = getParsedLines(sourceCode);
|
|
4582
|
-
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
4583
|
-
}
|
|
4584
|
-
});
|
|
4585
|
-
} else if (style === "setext") {
|
|
4586
|
-
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
4587
|
-
context.report({
|
|
4588
|
-
node,
|
|
4589
|
-
messageId: "expectedSetext",
|
|
4590
|
-
*fix(fixer) {
|
|
4591
|
-
const parsed = parseATXHeading(sourceCode, node);
|
|
4592
|
-
if (!parsed) return;
|
|
4593
|
-
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
|
|
4594
|
-
if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
|
|
4595
|
-
const lines = getParsedLines(sourceCode);
|
|
4596
|
-
const underline = "-".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
4597
|
-
const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
|
|
4598
|
-
yield fixer.insertTextAfter(node, appendingText);
|
|
4599
|
-
}
|
|
4600
|
-
});
|
|
4601
|
-
}
|
|
4602
|
-
} };
|
|
4603
|
-
}
|
|
4604
|
-
});
|
|
4605
|
-
|
|
4606
4639
|
//#endregion
|
|
4607
4640
|
//#region src/utils/link.ts
|
|
4608
4641
|
/**
|
|
@@ -4653,92 +4686,73 @@ function parseInlineLink(sourceCode, node) {
|
|
|
4653
4686
|
* Parse the inline link destination and link title from the given text.
|
|
4654
4687
|
*/
|
|
4655
4688
|
function parseInlineLinkDestAndTitleFromText(text) {
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
skipSpaces();
|
|
4689
|
+
if (!text.startsWith("(")) return null;
|
|
4690
|
+
const cursor = new ForwardCharacterCursor(text);
|
|
4691
|
+
cursor.next();
|
|
4692
|
+
cursor.skipSpaces();
|
|
4660
4693
|
let destination;
|
|
4661
|
-
const destinationStartIndex =
|
|
4662
|
-
if (
|
|
4663
|
-
|
|
4664
|
-
if (!skipUntilEnd((c) => c === ">")) return null;
|
|
4665
|
-
|
|
4666
|
-
const destinationRange = [destinationStartIndex,
|
|
4694
|
+
const destinationStartIndex = cursor.currIndex();
|
|
4695
|
+
if (cursor.curr() === "<") {
|
|
4696
|
+
cursor.next();
|
|
4697
|
+
if (!cursor.skipUntilEnd((c) => c === ">")) return null;
|
|
4698
|
+
cursor.next();
|
|
4699
|
+
const destinationRange = [destinationStartIndex, cursor.currIndex()];
|
|
4667
4700
|
destination = {
|
|
4668
4701
|
type: "pointy-bracketed",
|
|
4669
4702
|
text: text.slice(...destinationRange),
|
|
4670
4703
|
range: destinationRange
|
|
4671
4704
|
};
|
|
4672
4705
|
} else {
|
|
4673
|
-
if (
|
|
4674
|
-
skipUntilEnd((c) => isWhitespace(
|
|
4675
|
-
const destinationRange = [destinationStartIndex,
|
|
4706
|
+
if (cursor.finished()) return null;
|
|
4707
|
+
cursor.skipUntilEnd((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c) || c === ")");
|
|
4708
|
+
const destinationRange = [destinationStartIndex, cursor.currIndex()];
|
|
4676
4709
|
destination = {
|
|
4677
4710
|
type: "bare",
|
|
4678
4711
|
text: text.slice(...destinationRange),
|
|
4679
4712
|
range: destinationRange
|
|
4680
4713
|
};
|
|
4681
4714
|
}
|
|
4682
|
-
skipSpaces();
|
|
4683
|
-
if (
|
|
4684
|
-
const closingParenStartIndex$1 =
|
|
4685
|
-
|
|
4686
|
-
skipSpaces();
|
|
4687
|
-
if (
|
|
4715
|
+
cursor.skipSpaces();
|
|
4716
|
+
if (cursor.curr() === ")") {
|
|
4717
|
+
const closingParenStartIndex$1 = cursor.currIndex();
|
|
4718
|
+
cursor.next();
|
|
4719
|
+
cursor.skipSpaces();
|
|
4720
|
+
if (!cursor.finished()) return null;
|
|
4688
4721
|
return {
|
|
4689
4722
|
openingParen: { range: [0, 1] },
|
|
4690
4723
|
destination,
|
|
4691
4724
|
title: null,
|
|
4692
|
-
closingParen: { range: [closingParenStartIndex$1,
|
|
4725
|
+
closingParen: { range: [closingParenStartIndex$1, closingParenStartIndex$1 + 1] }
|
|
4693
4726
|
};
|
|
4694
4727
|
}
|
|
4695
|
-
if (
|
|
4728
|
+
if (cursor.finished()) return null;
|
|
4696
4729
|
let title;
|
|
4697
|
-
const titleStartIndex =
|
|
4698
|
-
const startChar =
|
|
4730
|
+
const titleStartIndex = cursor.currIndex();
|
|
4731
|
+
const startChar = cursor.curr();
|
|
4699
4732
|
if (startChar === "'" || startChar === "\"" || startChar === "(") {
|
|
4700
|
-
|
|
4733
|
+
cursor.next();
|
|
4701
4734
|
const endChar = startChar === "(" ? ")" : startChar;
|
|
4702
|
-
if (!skipUntilEnd((c) => c === endChar)) return null;
|
|
4703
|
-
|
|
4704
|
-
const titleRange = [titleStartIndex,
|
|
4735
|
+
if (!cursor.skipUntilEnd((c) => c === endChar)) return null;
|
|
4736
|
+
cursor.next();
|
|
4737
|
+
const titleRange = [titleStartIndex, cursor.currIndex()];
|
|
4705
4738
|
title = {
|
|
4706
4739
|
type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
|
|
4707
4740
|
text: text.slice(...titleRange),
|
|
4708
4741
|
range: titleRange
|
|
4709
4742
|
};
|
|
4710
4743
|
} else return null;
|
|
4711
|
-
skipSpaces();
|
|
4712
|
-
if (
|
|
4713
|
-
const closingParenStartIndex =
|
|
4714
|
-
|
|
4715
|
-
skipSpaces();
|
|
4716
|
-
if (
|
|
4744
|
+
cursor.skipSpaces();
|
|
4745
|
+
if (cursor.curr() !== ")") return null;
|
|
4746
|
+
const closingParenStartIndex = cursor.currIndex();
|
|
4747
|
+
cursor.next();
|
|
4748
|
+
cursor.skipSpaces();
|
|
4749
|
+
if (!cursor.finished()) return null;
|
|
4717
4750
|
return {
|
|
4718
4751
|
openingParen: { range: [0, 1] },
|
|
4719
4752
|
destination,
|
|
4720
4753
|
title,
|
|
4721
|
-
closingParen: { range: [closingParenStartIndex,
|
|
4754
|
+
closingParen: { range: [closingParenStartIndex, closingParenStartIndex + 1] }
|
|
4722
4755
|
};
|
|
4723
|
-
/**
|
|
4724
|
-
* Skip spaces
|
|
4725
|
-
*/
|
|
4726
|
-
function skipSpaces() {
|
|
4727
|
-
while (index < text.length && isWhitespace(text[index])) index++;
|
|
4728
|
-
}
|
|
4729
|
-
/**
|
|
4730
|
-
* Skip until the end by the given condition
|
|
4731
|
-
*/
|
|
4732
|
-
function skipUntilEnd(checkEnd) {
|
|
4733
|
-
while (index < text.length) {
|
|
4734
|
-
const c = text[index];
|
|
4735
|
-
if (checkEnd(c)) return true;
|
|
4736
|
-
index++;
|
|
4737
|
-
if (c !== "\\") continue;
|
|
4738
|
-
if (index < text.length && (text[index] === "\\" || checkEnd(text[index])) && !isWhitespace(text[index])) index++;
|
|
4739
|
-
}
|
|
4740
|
-
return false;
|
|
4741
|
-
}
|
|
4742
4756
|
}
|
|
4743
4757
|
|
|
4744
4758
|
//#endregion
|
|
@@ -4845,58 +4859,57 @@ function parseImage(sourceCode, node) {
|
|
|
4845
4859
|
*/
|
|
4846
4860
|
function parseImageFromText(text) {
|
|
4847
4861
|
if (!text.startsWith("![")) return null;
|
|
4848
|
-
|
|
4849
|
-
skipSpaces();
|
|
4850
|
-
if (
|
|
4851
|
-
const closingParenStartIndex =
|
|
4852
|
-
|
|
4853
|
-
skipSpaces();
|
|
4862
|
+
const cursor = new BackwardCharacterCursor(text);
|
|
4863
|
+
cursor.skipSpaces();
|
|
4864
|
+
if (cursor.curr() !== ")") return null;
|
|
4865
|
+
const closingParenStartIndex = cursor.currIndex();
|
|
4866
|
+
cursor.prev();
|
|
4867
|
+
cursor.skipSpaces();
|
|
4854
4868
|
let title = null;
|
|
4855
|
-
const titleEndIndex =
|
|
4856
|
-
const endChar =
|
|
4869
|
+
const titleEndIndex = cursor.currIndex() + 1;
|
|
4870
|
+
const endChar = cursor.curr();
|
|
4857
4871
|
if (endChar === "'" || endChar === "\"" || endChar === ")") {
|
|
4858
|
-
|
|
4872
|
+
cursor.prev();
|
|
4859
4873
|
const startChar = endChar === ")" ? "(" : endChar;
|
|
4860
|
-
if (skipUntilStart((c) => c === startChar)) {
|
|
4861
|
-
const titleRange = [
|
|
4862
|
-
|
|
4874
|
+
if (cursor.skipUntilStart((c) => c === startChar)) {
|
|
4875
|
+
const titleRange = [cursor.currIndex(), titleEndIndex];
|
|
4876
|
+
cursor.prev();
|
|
4863
4877
|
title = {
|
|
4864
4878
|
type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
|
|
4865
4879
|
text: text.slice(...titleRange),
|
|
4866
4880
|
range: titleRange
|
|
4867
4881
|
};
|
|
4868
|
-
skipSpaces();
|
|
4882
|
+
cursor.skipSpaces();
|
|
4869
4883
|
}
|
|
4870
4884
|
}
|
|
4871
|
-
if (title == null)
|
|
4885
|
+
if (title == null) cursor.setCurrIndex(titleEndIndex - 1);
|
|
4872
4886
|
let destination;
|
|
4873
|
-
const destinationEndIndex =
|
|
4874
|
-
if (
|
|
4875
|
-
|
|
4876
|
-
if (!skipUntilStart((c) => c === "<")) return null;
|
|
4877
|
-
const destinationRange = [
|
|
4878
|
-
|
|
4887
|
+
const destinationEndIndex = cursor.currIndex() + 1;
|
|
4888
|
+
if (cursor.curr() === ">") {
|
|
4889
|
+
cursor.prev();
|
|
4890
|
+
if (!cursor.skipUntilStart((c) => c === "<")) return null;
|
|
4891
|
+
const destinationRange = [cursor.currIndex(), destinationEndIndex];
|
|
4892
|
+
cursor.prev();
|
|
4879
4893
|
destination = {
|
|
4880
4894
|
type: "pointy-bracketed",
|
|
4881
4895
|
text: text.slice(...destinationRange),
|
|
4882
4896
|
range: destinationRange
|
|
4883
4897
|
};
|
|
4884
4898
|
} else {
|
|
4885
|
-
if (
|
|
4886
|
-
skipUntilStart((c) => isWhitespace(
|
|
4887
|
-
const destinationRange = [
|
|
4899
|
+
if (cursor.finished()) return null;
|
|
4900
|
+
cursor.skipUntilStart((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c) || c === "(");
|
|
4901
|
+
const destinationRange = [cursor.currIndex() + 1, destinationEndIndex];
|
|
4888
4902
|
destination = {
|
|
4889
4903
|
type: "bare",
|
|
4890
4904
|
text: text.slice(...destinationRange),
|
|
4891
4905
|
range: destinationRange
|
|
4892
4906
|
};
|
|
4893
4907
|
}
|
|
4894
|
-
skipSpaces();
|
|
4895
|
-
if (
|
|
4896
|
-
const openingParenStartIndex =
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
const textRange = [1, index + 1];
|
|
4908
|
+
cursor.skipSpaces();
|
|
4909
|
+
if (cursor.curr() !== "(") return null;
|
|
4910
|
+
const openingParenStartIndex = cursor.currIndex();
|
|
4911
|
+
if (cursor.prev() !== "]") return null;
|
|
4912
|
+
const textRange = [1, cursor.currIndex() + 1];
|
|
4900
4913
|
return {
|
|
4901
4914
|
openingParen: { range: [openingParenStartIndex, openingParenStartIndex + 1] },
|
|
4902
4915
|
text: { range: textRange },
|
|
@@ -4904,33 +4917,6 @@ function parseImageFromText(text) {
|
|
|
4904
4917
|
title,
|
|
4905
4918
|
closingParen: { range: [closingParenStartIndex, closingParenStartIndex + 1] }
|
|
4906
4919
|
};
|
|
4907
|
-
/**
|
|
4908
|
-
* Skip spaces
|
|
4909
|
-
*/
|
|
4910
|
-
function skipSpaces() {
|
|
4911
|
-
while (index >= 0 && isWhitespace(text[index])) index--;
|
|
4912
|
-
}
|
|
4913
|
-
/**
|
|
4914
|
-
* Skip until the start by the given condition
|
|
4915
|
-
*/
|
|
4916
|
-
function skipUntilStart(checkStart) {
|
|
4917
|
-
while (index >= 0) {
|
|
4918
|
-
const c = text[index];
|
|
4919
|
-
if (checkStart(c)) {
|
|
4920
|
-
if (index > 1 && !isWhitespace(c)) {
|
|
4921
|
-
let escapeText = "";
|
|
4922
|
-
while (text.endsWith(`${escapeText}\\`, index)) escapeText += "\\";
|
|
4923
|
-
if (escapeText.length % 2 === 1) {
|
|
4924
|
-
index -= escapeText.length + 1;
|
|
4925
|
-
continue;
|
|
4926
|
-
}
|
|
4927
|
-
}
|
|
4928
|
-
return true;
|
|
4929
|
-
}
|
|
4930
|
-
index--;
|
|
4931
|
-
}
|
|
4932
|
-
return false;
|
|
4933
|
-
}
|
|
4934
4920
|
}
|
|
4935
4921
|
|
|
4936
4922
|
//#endregion
|
|
@@ -4990,21 +4976,20 @@ function parseImageReference(sourceCode, node) {
|
|
|
4990
4976
|
*/
|
|
4991
4977
|
function parseFullImageReferenceFromText(text) {
|
|
4992
4978
|
if (!text.startsWith("![")) return null;
|
|
4993
|
-
|
|
4994
|
-
skipSpaces();
|
|
4995
|
-
if (
|
|
4996
|
-
const labelEndIndex =
|
|
4997
|
-
|
|
4998
|
-
skipSpaces();
|
|
4999
|
-
if (!skipUntilStart((c) => c === "[")) return null;
|
|
5000
|
-
const labelRange = [
|
|
5001
|
-
index--;
|
|
4979
|
+
const cursor = new BackwardCharacterCursor(text);
|
|
4980
|
+
cursor.skipSpaces();
|
|
4981
|
+
if (cursor.curr() !== "]") return null;
|
|
4982
|
+
const labelEndIndex = cursor.currIndex() + 1;
|
|
4983
|
+
cursor.prev();
|
|
4984
|
+
cursor.skipSpaces();
|
|
4985
|
+
if (!cursor.skipUntilStart((c) => c === "[")) return null;
|
|
4986
|
+
const labelRange = [cursor.currIndex(), labelEndIndex];
|
|
5002
4987
|
const label = {
|
|
5003
4988
|
text: text.slice(...labelRange),
|
|
5004
4989
|
range: labelRange
|
|
5005
4990
|
};
|
|
5006
|
-
if (
|
|
5007
|
-
const textRange = [1,
|
|
4991
|
+
if (cursor.prev() !== "]") return null;
|
|
4992
|
+
const textRange = [1, cursor.currIndex() + 1];
|
|
5008
4993
|
return {
|
|
5009
4994
|
text: {
|
|
5010
4995
|
text: text.slice(...textRange),
|
|
@@ -5012,386 +4997,1438 @@ function parseFullImageReferenceFromText(text) {
|
|
|
5012
4997
|
},
|
|
5013
4998
|
label
|
|
5014
4999
|
};
|
|
5015
|
-
/**
|
|
5016
|
-
* Skip spaces
|
|
5017
|
-
*/
|
|
5018
|
-
function skipSpaces() {
|
|
5019
|
-
while (index >= 0 && isWhitespace(text[index])) index--;
|
|
5020
|
-
}
|
|
5021
|
-
/**
|
|
5022
|
-
* Skip until the start by the given condition
|
|
5023
|
-
*/
|
|
5024
|
-
function skipUntilStart(checkStart) {
|
|
5025
|
-
while (index >= 0) {
|
|
5026
|
-
const c = text[index];
|
|
5027
|
-
if (checkStart(c)) {
|
|
5028
|
-
if (index > 1) {
|
|
5029
|
-
let escapeText = "";
|
|
5030
|
-
while (text.endsWith(`${escapeText}\\`, index)) escapeText += "\\";
|
|
5031
|
-
if (escapeText.length % 2 === 1) {
|
|
5032
|
-
index -= escapeText.length + 1;
|
|
5033
|
-
continue;
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
return true;
|
|
5037
|
-
}
|
|
5038
|
-
index--;
|
|
5039
|
-
}
|
|
5040
|
-
return false;
|
|
5041
|
-
}
|
|
5042
5000
|
}
|
|
5043
5001
|
|
|
5044
5002
|
//#endregion
|
|
5045
|
-
//#region src/utils/
|
|
5003
|
+
//#region src/utils/list-item.ts
|
|
5046
5004
|
/**
|
|
5047
|
-
* Parse the
|
|
5005
|
+
* Parse the list item.
|
|
5048
5006
|
*/
|
|
5049
|
-
function
|
|
5050
|
-
const
|
|
5051
|
-
const parsed = parseLinkDefinitionFromText(text);
|
|
5052
|
-
if (!parsed) return null;
|
|
5007
|
+
function parseListItem(sourceCode, node) {
|
|
5008
|
+
const marker = getListItemMarker(sourceCode, node);
|
|
5053
5009
|
const nodeRange = sourceCode.getRange(node);
|
|
5054
|
-
const
|
|
5055
|
-
const
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
|
|
5067
|
-
},
|
|
5068
|
-
title: parsed.title ? {
|
|
5069
|
-
type: parsed.title.type,
|
|
5070
|
-
text: parsed.title.text,
|
|
5071
|
-
range: [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]],
|
|
5072
|
-
loc: getSourceLocationFromRange(sourceCode, node, [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]])
|
|
5073
|
-
} : null
|
|
5010
|
+
const markerRange = [nodeRange[0], nodeRange[0] + marker.raw.length];
|
|
5011
|
+
const parsedMarker = marker.kind === "." || marker.kind === ")" ? {
|
|
5012
|
+
kind: marker.kind,
|
|
5013
|
+
text: marker.raw,
|
|
5014
|
+
value: marker.sequence.value,
|
|
5015
|
+
range: markerRange,
|
|
5016
|
+
loc: getSourceLocationFromRange(sourceCode, node, markerRange)
|
|
5017
|
+
} : {
|
|
5018
|
+
kind: marker.kind,
|
|
5019
|
+
text: marker.raw,
|
|
5020
|
+
range: markerRange,
|
|
5021
|
+
loc: getSourceLocationFromRange(sourceCode, node, markerRange)
|
|
5074
5022
|
};
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
*/
|
|
5079
|
-
function parseLinkDefinitionFromText(text) {
|
|
5080
|
-
let index = 0;
|
|
5081
|
-
if (text[index] !== "[") return null;
|
|
5082
|
-
const labelStartIndex = index;
|
|
5083
|
-
index++;
|
|
5084
|
-
if (!skipUntilEnd((c) => c === "]")) return null;
|
|
5085
|
-
index++;
|
|
5086
|
-
const labelRange = [labelStartIndex, index];
|
|
5087
|
-
if (!text.slice(labelRange[0] + 1, labelRange[1] - 1).trim()) return null;
|
|
5088
|
-
if (text[index] !== ":") return null;
|
|
5089
|
-
const label = {
|
|
5090
|
-
text: text.slice(...labelRange),
|
|
5091
|
-
range: labelRange
|
|
5023
|
+
if (node.checked == null) return {
|
|
5024
|
+
marker: parsedMarker,
|
|
5025
|
+
taskListItemMarker: null
|
|
5092
5026
|
};
|
|
5093
|
-
index
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
if (
|
|
5100
|
-
index
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
if (text.length <= index) return null;
|
|
5109
|
-
skipUntilEnd((c) => isWhitespace(c) || isAsciiControlCharacter(c));
|
|
5110
|
-
const destinationRange = [destinationStartIndex, index];
|
|
5111
|
-
destination = {
|
|
5112
|
-
type: "bare",
|
|
5113
|
-
text: text.slice(...destinationRange),
|
|
5114
|
-
range: destinationRange
|
|
5027
|
+
for (let index = nodeRange[0] + marker.raw.length; index < nodeRange[1]; index++) {
|
|
5028
|
+
const c = sourceCode.text[index];
|
|
5029
|
+
if (isSpaceOrTab(c)) continue;
|
|
5030
|
+
if (c !== "[") break;
|
|
5031
|
+
const middle = sourceCode.text[index + 1];
|
|
5032
|
+
if (middle !== "x" && middle !== " ") break;
|
|
5033
|
+
if (sourceCode.text[index + 2] !== "]") break;
|
|
5034
|
+
const taskListItemMarkerRange = [index, index + 3];
|
|
5035
|
+
return {
|
|
5036
|
+
marker: parsedMarker,
|
|
5037
|
+
taskListItemMarker: {
|
|
5038
|
+
text: sourceCode.text.slice(...taskListItemMarkerRange),
|
|
5039
|
+
range: taskListItemMarkerRange,
|
|
5040
|
+
loc: getSourceLocationFromRange(sourceCode, node, taskListItemMarkerRange)
|
|
5041
|
+
}
|
|
5115
5042
|
};
|
|
5116
5043
|
}
|
|
5117
|
-
skipSpaces();
|
|
5118
|
-
if (text.length <= index) return {
|
|
5119
|
-
label,
|
|
5120
|
-
destination,
|
|
5121
|
-
title: null
|
|
5122
|
-
};
|
|
5123
|
-
let title;
|
|
5124
|
-
const titleStartIndex = index;
|
|
5125
|
-
const startChar = text[index];
|
|
5126
|
-
if (startChar === "'" || startChar === "\"" || startChar === "(") {
|
|
5127
|
-
index++;
|
|
5128
|
-
const endChar = startChar === "(" ? ")" : startChar;
|
|
5129
|
-
if (!skipUntilEnd((c) => c === endChar)) return null;
|
|
5130
|
-
index++;
|
|
5131
|
-
const titleRange = [titleStartIndex, index];
|
|
5132
|
-
title = {
|
|
5133
|
-
type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
|
|
5134
|
-
text: text.slice(...titleRange),
|
|
5135
|
-
range: titleRange
|
|
5136
|
-
};
|
|
5137
|
-
} else return null;
|
|
5138
|
-
skipSpaces();
|
|
5139
|
-
if (index < text.length) return null;
|
|
5140
5044
|
return {
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
title
|
|
5045
|
+
marker: parsedMarker,
|
|
5046
|
+
taskListItemMarker: null
|
|
5144
5047
|
};
|
|
5145
|
-
/**
|
|
5146
|
-
* Skip spaces
|
|
5147
|
-
*/
|
|
5148
|
-
function skipSpaces() {
|
|
5149
|
-
while (index < text.length && isWhitespace(text[index])) index++;
|
|
5150
|
-
}
|
|
5151
|
-
/**
|
|
5152
|
-
* Skip until the end by the given condition
|
|
5153
|
-
*/
|
|
5154
|
-
function skipUntilEnd(checkEnd) {
|
|
5155
|
-
while (index < text.length) {
|
|
5156
|
-
const c = text[index];
|
|
5157
|
-
if (checkEnd(c)) return true;
|
|
5158
|
-
index++;
|
|
5159
|
-
if (c !== "\\") continue;
|
|
5160
|
-
if (index < text.length && (text[index] === "\\" || checkEnd(text[index])) && !isWhitespace(text[index])) index++;
|
|
5161
|
-
}
|
|
5162
|
-
return false;
|
|
5163
|
-
}
|
|
5164
5048
|
}
|
|
5165
5049
|
|
|
5166
5050
|
//#endregion
|
|
5167
|
-
//#region src/rules/
|
|
5168
|
-
|
|
5051
|
+
//#region src/rules/indent.ts
|
|
5052
|
+
/**
|
|
5053
|
+
* Parse options.
|
|
5054
|
+
*/
|
|
5055
|
+
function parseOptions$3(options) {
|
|
5056
|
+
const listItems = options?.listItems;
|
|
5057
|
+
return { listItems: {
|
|
5058
|
+
first: listItems?.first ?? 1,
|
|
5059
|
+
other: listItems?.other ?? "first",
|
|
5060
|
+
relativeTo: listItems?.relativeTo ?? "taskListMarkerEnd"
|
|
5061
|
+
} };
|
|
5062
|
+
}
|
|
5063
|
+
var indent_default = createRule("indent", {
|
|
5169
5064
|
meta: {
|
|
5170
5065
|
type: "layout",
|
|
5171
5066
|
docs: {
|
|
5172
|
-
description: "enforce
|
|
5067
|
+
description: "enforce consistent indentation in Markdown files",
|
|
5173
5068
|
categories: ["standard"],
|
|
5174
|
-
listCategory: "
|
|
5069
|
+
listCategory: "Whitespace"
|
|
5175
5070
|
},
|
|
5176
5071
|
fixable: "whitespace",
|
|
5177
5072
|
hasSuggestions: false,
|
|
5178
5073
|
schema: [{
|
|
5179
5074
|
type: "object",
|
|
5180
|
-
properties: {
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
"
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5075
|
+
properties: { listItems: {
|
|
5076
|
+
type: "object",
|
|
5077
|
+
properties: {
|
|
5078
|
+
first: { anyOf: [{ const: "ignore" }, {
|
|
5079
|
+
type: "number",
|
|
5080
|
+
minimum: 1
|
|
5081
|
+
}] },
|
|
5082
|
+
other: { anyOf: [{ enum: ["first", "minimum"] }, {
|
|
5083
|
+
type: "number",
|
|
5084
|
+
minimum: 1
|
|
5085
|
+
}] },
|
|
5086
|
+
relativeTo: { enum: [
|
|
5087
|
+
"markerStart",
|
|
5088
|
+
"markerEnd",
|
|
5089
|
+
"taskListMarkerStart",
|
|
5090
|
+
"taskListMarkerEnd"
|
|
5091
|
+
] }
|
|
5092
|
+
},
|
|
5093
|
+
additionalProperties: false
|
|
5094
|
+
} },
|
|
5188
5095
|
additionalProperties: false
|
|
5189
5096
|
}],
|
|
5190
5097
|
messages: {
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5098
|
+
wrongIndentation: "Expected indentation of {{expected}} but found {{actual}}.",
|
|
5099
|
+
wrongIndentationFromBlockquoteMarker: "Expected indentation of {{expected}} from the blockquote marker but found {{actual}}.",
|
|
5100
|
+
wrongIndentationFromListMarker: "Expected indentation of {{expected}} from the list marker but found {{actual}}.",
|
|
5101
|
+
wrongIndentationWithTab: "Expected indentation of {{expected}} but found {{actual}} (tab width is 4).",
|
|
5102
|
+
wrongIndentationFromBlockquoteMarkerWithTab: "Expected indentation of {{expected}} from the blockquote marker but found {{actual}} (tab width is 4).",
|
|
5103
|
+
wrongIndentationFromListMarkerWithTab: "Expected indentation of {{expected}} from the list marker but found {{actual}} (tab width is 4)."
|
|
5195
5104
|
}
|
|
5196
5105
|
},
|
|
5197
5106
|
create(context) {
|
|
5198
5107
|
const sourceCode = context.sourceCode;
|
|
5199
|
-
const
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
if (multiline) {
|
|
5208
|
-
if (sourceCode.text.slice(bracketsRange[0] + 1, bracketsRange[1] - 1).trim().includes("\n")) return "always";
|
|
5108
|
+
const options = parseOptions$3(context.options[0]);
|
|
5109
|
+
class AbsBlockStack {
|
|
5110
|
+
violations = [];
|
|
5111
|
+
getCurrentBlockquote() {
|
|
5112
|
+
let block = this;
|
|
5113
|
+
while (block) {
|
|
5114
|
+
if (block.type === "blockquote") return block;
|
|
5115
|
+
block = block.upper;
|
|
5209
5116
|
}
|
|
5210
|
-
return
|
|
5211
|
-
}
|
|
5117
|
+
return null;
|
|
5118
|
+
}
|
|
5119
|
+
getCurrentListItemAtLine(lineNumber) {
|
|
5120
|
+
let block = this;
|
|
5121
|
+
while (block) {
|
|
5122
|
+
if (block.type === "blockquote") return null;
|
|
5123
|
+
if (block.type === "listItem") {
|
|
5124
|
+
if (sourceCode.getLoc(block.node).start.line === lineNumber) return block;
|
|
5125
|
+
}
|
|
5126
|
+
block = block.upper;
|
|
5127
|
+
}
|
|
5128
|
+
return null;
|
|
5129
|
+
}
|
|
5212
5130
|
}
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5131
|
+
class RootStack extends AbsBlockStack {
|
|
5132
|
+
type = "root";
|
|
5133
|
+
node;
|
|
5134
|
+
upper = null;
|
|
5135
|
+
constructor(node) {
|
|
5136
|
+
super();
|
|
5137
|
+
this.node = node;
|
|
5138
|
+
}
|
|
5139
|
+
getExpectedIndent() {
|
|
5140
|
+
return 0;
|
|
5141
|
+
}
|
|
5224
5142
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
loc
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5143
|
+
class BlockquoteStack extends AbsBlockStack {
|
|
5144
|
+
type = "blockquote";
|
|
5145
|
+
node;
|
|
5146
|
+
upper;
|
|
5147
|
+
_expectedIndent;
|
|
5148
|
+
_markerIndent;
|
|
5149
|
+
constructor(node) {
|
|
5150
|
+
super();
|
|
5151
|
+
this.node = node;
|
|
5152
|
+
this.upper = blockStack;
|
|
5153
|
+
}
|
|
5154
|
+
getExpectedIndent() {
|
|
5155
|
+
if (this._expectedIndent == null) this._expectedIndent = this.getMarkerIndent() + 1 + 1;
|
|
5156
|
+
return this._expectedIndent;
|
|
5157
|
+
}
|
|
5158
|
+
getMarkerIndent() {
|
|
5159
|
+
if (this._markerIndent == null) {
|
|
5160
|
+
const loc = sourceCode.getLoc(this.node);
|
|
5161
|
+
this._markerIndent = getWidth(sourceCode.lines[loc.start.line - 1].slice(0, loc.start.column - 1));
|
|
5162
|
+
}
|
|
5163
|
+
return this._markerIndent;
|
|
5246
5164
|
}
|
|
5247
5165
|
}
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5166
|
+
class ListItemStack extends AbsBlockStack {
|
|
5167
|
+
type = "listItem";
|
|
5168
|
+
node;
|
|
5169
|
+
upper;
|
|
5170
|
+
_expectedIndentForFirstLine;
|
|
5171
|
+
_expectedIndentForOtherLines;
|
|
5172
|
+
_parsed;
|
|
5173
|
+
nodeLoc;
|
|
5174
|
+
constructor(node) {
|
|
5175
|
+
super();
|
|
5176
|
+
this.node = node;
|
|
5177
|
+
this.upper = blockStack;
|
|
5178
|
+
this.nodeLoc = sourceCode.getLoc(this.node);
|
|
5179
|
+
}
|
|
5180
|
+
getParsedListItem() {
|
|
5181
|
+
return this._parsed ??= parseListItem(sourceCode, this.node);
|
|
5182
|
+
}
|
|
5183
|
+
getExpectedIndent({ lineNumber, block }) {
|
|
5184
|
+
const loc = this.nodeLoc;
|
|
5185
|
+
if (lineNumber === loc.start.line) return this.getExpectedIndentForFirstLine();
|
|
5186
|
+
const expected = this.getExpectedIndentAfterFirstLine();
|
|
5187
|
+
if (expected === "ignore") return "ignore";
|
|
5188
|
+
if (!block) return expected;
|
|
5189
|
+
const actualFirstLineIndent = this.getActualFirstLineIndent({ withoutTaskListMarker: true });
|
|
5190
|
+
if (actualFirstLineIndent != null) return Math.min(expected, actualFirstLineIndent + 3);
|
|
5191
|
+
return expected;
|
|
5192
|
+
}
|
|
5193
|
+
getExpectedIndentForFirstLine() {
|
|
5194
|
+
if (options.listItems.first === "ignore") return "ignore";
|
|
5195
|
+
if (this._expectedIndentForFirstLine != null) return this._expectedIndentForFirstLine;
|
|
5196
|
+
const loc = this.nodeLoc;
|
|
5197
|
+
const parsed = this.getParsedListItem();
|
|
5198
|
+
const lineText = sourceCode.lines[loc.start.line - 1];
|
|
5199
|
+
if (options.listItems.relativeTo === "markerStart") {
|
|
5200
|
+
const baseIndent = getWidth(lineText.slice(0, loc.start.column - 1));
|
|
5201
|
+
return this._expectedIndentForFirstLine = Math.max(baseIndent + options.listItems.first, baseIndent + parsed.marker.text.length + 1);
|
|
5202
|
+
}
|
|
5203
|
+
if (options.listItems.relativeTo === "taskListMarkerEnd" && parsed.taskListItemMarker) return this._expectedIndentForFirstLine = getWidth(lineText.slice(0, parsed.taskListItemMarker.loc.end.column - 1)) + options.listItems.first;
|
|
5204
|
+
return this._expectedIndentForFirstLine = getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + options.listItems.first;
|
|
5205
|
+
}
|
|
5206
|
+
getExpectedIndentAfterFirstLine() {
|
|
5207
|
+
if (this._expectedIndentForOtherLines != null) return this._expectedIndentForOtherLines;
|
|
5208
|
+
const loc = this.nodeLoc;
|
|
5209
|
+
if (options.listItems.other === "first") {
|
|
5210
|
+
const firstLineIndent = this.getExpectedIndentForFirstLine();
|
|
5211
|
+
if (firstLineIndent === "ignore") {
|
|
5212
|
+
const actualFirstLineIndent = this.getActualFirstLineIndent();
|
|
5213
|
+
if (actualFirstLineIndent != null) return this._expectedIndentForOtherLines = actualFirstLineIndent;
|
|
5214
|
+
for (const child of this.node.children) {
|
|
5215
|
+
const childLoc = sourceCode.getLoc(child);
|
|
5216
|
+
if (loc.start.line < childLoc.start.line) return this._expectedIndentForOtherLines = getWidth(sourceCode.lines[childLoc.start.line - 1].slice(0, childLoc.start.column - 1));
|
|
5217
|
+
}
|
|
5262
5218
|
}
|
|
5219
|
+
return this._expectedIndentForOtherLines = firstLineIndent;
|
|
5220
|
+
}
|
|
5221
|
+
if (options.listItems.other === "minimum") return this._expectedIndentForOtherLines = this.getMinimumLineIndent();
|
|
5222
|
+
const lineText = sourceCode.lines[loc.start.line - 1];
|
|
5223
|
+
if (options.listItems.relativeTo === "markerStart") {
|
|
5224
|
+
const baseIndent = getWidth(lineText.slice(0, loc.start.column - 1));
|
|
5225
|
+
const minimumLineIndent = this.getMinimumLineIndent();
|
|
5226
|
+
return this._expectedIndentForOtherLines = Math.max(baseIndent + options.listItems.other, minimumLineIndent);
|
|
5227
|
+
}
|
|
5228
|
+
const parsed = this.getParsedListItem();
|
|
5229
|
+
if (options.listItems.relativeTo === "taskListMarkerEnd" && parsed.taskListItemMarker) return this._expectedIndentForOtherLines = getWidth(lineText.slice(0, parsed.taskListItemMarker.loc.end.column - 1)) + options.listItems.other;
|
|
5230
|
+
return this._expectedIndentForOtherLines = getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + options.listItems.other;
|
|
5231
|
+
}
|
|
5232
|
+
getMinimumLineIndent() {
|
|
5233
|
+
const actualFirstLineIndent = this.getActualFirstLineIndent({ withoutTaskListMarker: true });
|
|
5234
|
+
if (actualFirstLineIndent != null) return this._expectedIndentForOtherLines = actualFirstLineIndent;
|
|
5235
|
+
const parsed = this.getParsedListItem();
|
|
5236
|
+
const lineText = sourceCode.lines[parsed.marker.loc.end.line - 1];
|
|
5237
|
+
return getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + 1;
|
|
5238
|
+
}
|
|
5239
|
+
getActualFirstLineIndent({ withoutTaskListMarker = false } = {}) {
|
|
5240
|
+
const parsed = this.getParsedListItem();
|
|
5241
|
+
const markerEndPos = withoutTaskListMarker ? parsed.marker.loc.end : parsed.taskListItemMarker?.loc.end ?? parsed.marker.loc.end;
|
|
5242
|
+
const lineText = sourceCode.lines[markerEndPos.line - 1];
|
|
5243
|
+
const afterMarkerText = lineText.slice(markerEndPos.column - 1);
|
|
5244
|
+
const trimmedAfterMarkerText = afterMarkerText.trimStart();
|
|
5245
|
+
if (trimmedAfterMarkerText) {
|
|
5246
|
+
const afterMarkerSpacesLength = afterMarkerText.length - trimmedAfterMarkerText.length;
|
|
5247
|
+
return getWidth(lineText.slice(0, markerEndPos.column - 1 + afterMarkerSpacesLength));
|
|
5248
|
+
}
|
|
5249
|
+
return null;
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
class FootnoteDefinitionStack extends AbsBlockStack {
|
|
5253
|
+
type = "footnoteDefinition";
|
|
5254
|
+
node;
|
|
5255
|
+
upper;
|
|
5256
|
+
_expectedIndent;
|
|
5257
|
+
constructor(node) {
|
|
5258
|
+
super();
|
|
5259
|
+
this.node = node;
|
|
5260
|
+
this.upper = blockStack;
|
|
5261
|
+
}
|
|
5262
|
+
getExpectedIndent() {
|
|
5263
|
+
if (this._expectedIndent == null) {
|
|
5264
|
+
const loc = sourceCode.getLoc(this.node);
|
|
5265
|
+
this._expectedIndent = getWidth(sourceCode.lines[loc.start.line - 1].slice(0, loc.start.column - 1)) + 4;
|
|
5266
|
+
}
|
|
5267
|
+
return this._expectedIndent;
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
class LinkStack extends AbsBlockStack {
|
|
5271
|
+
type = "link";
|
|
5272
|
+
node;
|
|
5273
|
+
upper;
|
|
5274
|
+
nodeLoc;
|
|
5275
|
+
constructor(node) {
|
|
5276
|
+
super();
|
|
5277
|
+
this.node = node;
|
|
5278
|
+
this.upper = blockStack;
|
|
5279
|
+
this.nodeLoc = sourceCode.getLoc(this.node);
|
|
5280
|
+
}
|
|
5281
|
+
getExpectedIndent(ctx) {
|
|
5282
|
+
const loc = this.nodeLoc;
|
|
5283
|
+
if (ctx.lineNumber === loc.start.line) return this.upper.getExpectedIndent(ctx);
|
|
5284
|
+
const base = this.upper.getExpectedIndent({
|
|
5285
|
+
lineNumber: loc.start.line,
|
|
5286
|
+
block: ctx.block
|
|
5263
5287
|
});
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
context.report({
|
|
5267
|
-
node,
|
|
5268
|
-
loc: getSourceLocationFromRange(sourceCode, node, [closingBracketIndex - spaceBeforeClosingBracket.length, closingBracketIndex]),
|
|
5269
|
-
messageId: "unexpectedNewlineBeforeClosingBracket",
|
|
5270
|
-
fix: (fixer) => fixer.replaceTextRange([closingBracketIndex - spaceBeforeClosingBracket.length, closingBracketIndex], " ")
|
|
5271
|
-
});
|
|
5288
|
+
if (base === "ignore") return "ignore";
|
|
5289
|
+
return base + 2;
|
|
5272
5290
|
}
|
|
5273
5291
|
}
|
|
5292
|
+
let blockStack = new RootStack(sourceCode.ast);
|
|
5293
|
+
const reportedLocations = Object.create(null);
|
|
5294
|
+
/**
|
|
5295
|
+
* Reported locations (line and column) to avoid duplicate reports.
|
|
5296
|
+
*/
|
|
5297
|
+
function reportIncorrectIndent(violation) {
|
|
5298
|
+
const reportedColumns = reportedLocations[violation.loc.start.line] ??= /* @__PURE__ */ new Set();
|
|
5299
|
+
if (reportedColumns.has(violation.loc.start.column)) return;
|
|
5300
|
+
reportedColumns.add(violation.loc.start.column);
|
|
5301
|
+
blockStack.violations.push(violation);
|
|
5302
|
+
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Flush violations to the context.
|
|
5305
|
+
*/
|
|
5306
|
+
function flushViolations({ violations }) {
|
|
5307
|
+
for (const violation of violations) context.report({
|
|
5308
|
+
loc: violation.loc,
|
|
5309
|
+
messageId: violation.messageId,
|
|
5310
|
+
data: {
|
|
5311
|
+
expected: String(violation.data.expected),
|
|
5312
|
+
actual: String(violation.data.actual)
|
|
5313
|
+
},
|
|
5314
|
+
fix(fixer) {
|
|
5315
|
+
const result = [];
|
|
5316
|
+
for (const { fix } of violations) result.push(...fix(fixer));
|
|
5317
|
+
return result;
|
|
5318
|
+
}
|
|
5319
|
+
});
|
|
5320
|
+
}
|
|
5274
5321
|
return {
|
|
5322
|
+
thematicBreak: verifyThematicBreak,
|
|
5323
|
+
heading: verifyHeading,
|
|
5324
|
+
code: verifyCodeBlock,
|
|
5325
|
+
html: verifyHtml,
|
|
5326
|
+
definition: verifyLinkDefinition,
|
|
5327
|
+
table: verifyTable,
|
|
5328
|
+
list: verifyList,
|
|
5329
|
+
inlineCode: verifyInlineCode,
|
|
5330
|
+
emphasis: verifyEmphasisOrStrongOrDelete,
|
|
5331
|
+
strong: verifyEmphasisOrStrongOrDelete,
|
|
5332
|
+
delete: verifyEmphasisOrStrongOrDelete,
|
|
5333
|
+
image: verifyImage,
|
|
5334
|
+
imageReference: verifyImageReference,
|
|
5335
|
+
footnoteReference: verifyInline,
|
|
5336
|
+
break: verifyInline,
|
|
5337
|
+
text: verifyText,
|
|
5338
|
+
blockquote(node) {
|
|
5339
|
+
verifyBlockquote(node);
|
|
5340
|
+
blockStack = new BlockquoteStack(node);
|
|
5341
|
+
},
|
|
5342
|
+
listItem(node) {
|
|
5343
|
+
blockStack = new ListItemStack(node);
|
|
5344
|
+
},
|
|
5345
|
+
footnoteDefinition(node) {
|
|
5346
|
+
verifyFootnoteDefinition(node);
|
|
5347
|
+
blockStack = new FootnoteDefinitionStack(node);
|
|
5348
|
+
},
|
|
5275
5349
|
link(node) {
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
if (!parsed) return;
|
|
5279
|
-
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
5350
|
+
verifyLink(node);
|
|
5351
|
+
blockStack = new LinkStack(node);
|
|
5280
5352
|
},
|
|
5281
5353
|
linkReference(node) {
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
5285
|
-
if (parsed.label?.type === "full") verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
5286
|
-
},
|
|
5287
|
-
image(node) {
|
|
5288
|
-
const parsed = parseImage(sourceCode, node);
|
|
5289
|
-
if (!parsed) return;
|
|
5290
|
-
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
5354
|
+
verifyLinkReference(node);
|
|
5355
|
+
blockStack = new LinkStack(node);
|
|
5291
5356
|
},
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
5296
|
-
if (parsed.label?.type === "full") verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
5357
|
+
"blockquote, listItem, footnoteDefinition, link, linkReference:exit"() {
|
|
5358
|
+
flushViolations(blockStack);
|
|
5359
|
+
blockStack = blockStack.upper;
|
|
5297
5360
|
},
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
if (!parsed) return;
|
|
5301
|
-
verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
5361
|
+
"root:exit"() {
|
|
5362
|
+
flushViolations(blockStack);
|
|
5302
5363
|
}
|
|
5303
5364
|
};
|
|
5304
5365
|
/**
|
|
5305
|
-
*
|
|
5366
|
+
* Verify a thematic break node.
|
|
5306
5367
|
*/
|
|
5307
|
-
function
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
}
|
|
5313
|
-
return sourceCode.text.slice(openingBracketIndex + 1);
|
|
5368
|
+
function verifyThematicBreak(node) {
|
|
5369
|
+
const loc = sourceCode.getLoc(node);
|
|
5370
|
+
verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
|
|
5371
|
+
lineNumber,
|
|
5372
|
+
block: true
|
|
5373
|
+
}));
|
|
5314
5374
|
}
|
|
5315
5375
|
/**
|
|
5316
|
-
*
|
|
5376
|
+
* Verify a heading node.
|
|
5317
5377
|
*/
|
|
5318
|
-
function
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
}
|
|
5324
|
-
return sourceCode.text.slice(0, closingBracketIndex);
|
|
5325
|
-
}
|
|
5326
|
-
}
|
|
5327
|
-
});
|
|
5328
|
-
|
|
5329
|
-
//#endregion
|
|
5330
|
-
//#region src/rules/link-bracket-spacing.ts
|
|
5331
|
-
/**
|
|
5332
|
-
* The basic option for links and images.
|
|
5333
|
-
*/
|
|
5334
|
-
function parseOptions$1(option) {
|
|
5335
|
-
const space = option?.space ?? "never";
|
|
5336
|
-
const imagesInLinks = option?.imagesInLinks;
|
|
5337
|
-
return {
|
|
5338
|
-
space,
|
|
5339
|
-
spaceForText: getOptionForText
|
|
5340
|
-
};
|
|
5341
|
-
/**
|
|
5342
|
-
* Get the spacing option for the given node.
|
|
5343
|
-
*/
|
|
5344
|
-
function getOptionForText(node) {
|
|
5345
|
-
if (imagesInLinks != null) {
|
|
5346
|
-
const children = [...node.children];
|
|
5347
|
-
let child;
|
|
5348
|
-
while (children.length && (child = children[0]) && child.type === "text" && isWhitespace(child.value)) children.shift();
|
|
5349
|
-
while (children.length && (child = children[children.length - 1]) && child.type === "text" && isWhitespace(child.value)) children.pop();
|
|
5350
|
-
const loneChild = children.length === 1 ? children[0] : null;
|
|
5351
|
-
if (loneChild?.type === "image" || loneChild?.type === "imageReference") return imagesInLinks ? "always" : "never";
|
|
5352
|
-
}
|
|
5353
|
-
if (node.children.length === 0 || node.children.length === 1 && node.children[0]?.type === "text" && isWhitespace(node.children[0].value)) return "ignore";
|
|
5354
|
-
return space;
|
|
5355
|
-
}
|
|
5356
|
-
}
|
|
5357
|
-
var link_bracket_spacing_default = createRule("link-bracket-spacing", {
|
|
5358
|
-
meta: {
|
|
5359
|
-
type: "layout",
|
|
5360
|
-
docs: {
|
|
5361
|
-
description: "enforce consistent spacing inside link brackets",
|
|
5362
|
-
categories: ["standard"],
|
|
5363
|
-
listCategory: "Stylistic"
|
|
5364
|
-
},
|
|
5365
|
-
fixable: "whitespace",
|
|
5366
|
-
hasSuggestions: false,
|
|
5367
|
-
schema: [{
|
|
5368
|
-
type: "object",
|
|
5369
|
-
properties: {
|
|
5370
|
-
space: { enum: ["always", "never"] },
|
|
5371
|
-
imagesInLinks: { type: "boolean" }
|
|
5372
|
-
},
|
|
5373
|
-
additionalProperties: false
|
|
5374
|
-
}],
|
|
5375
|
-
messages: {
|
|
5376
|
-
unexpectedSpaceAfterOpeningBracket: "Unexpected space after opening bracket.",
|
|
5377
|
-
expectedSpaceAfterOpeningBracket: "Expected space after opening bracket.",
|
|
5378
|
-
unexpectedSpaceBeforeClosingBracket: "Unexpected space before closing bracket.",
|
|
5379
|
-
expectedSpaceBeforeClosingBracket: "Expected space before closing bracket."
|
|
5378
|
+
function verifyHeading(node) {
|
|
5379
|
+
const loc = sourceCode.getLoc(node);
|
|
5380
|
+
verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
|
|
5381
|
+
lineNumber,
|
|
5382
|
+
block: true
|
|
5383
|
+
}));
|
|
5380
5384
|
}
|
|
5381
|
-
},
|
|
5382
|
-
create(context) {
|
|
5383
|
-
const sourceCode = context.sourceCode;
|
|
5384
|
-
const options = parseOptions$1(context.options[0]);
|
|
5385
5385
|
/**
|
|
5386
|
-
* Verify
|
|
5386
|
+
* Verify a code block node.
|
|
5387
5387
|
*/
|
|
5388
|
-
function
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5388
|
+
function verifyCodeBlock(node) {
|
|
5389
|
+
if (getCodeBlockKind(sourceCode, node) === "indented") return;
|
|
5390
|
+
const loc = sourceCode.getLoc(node);
|
|
5391
|
+
verifyLinesIndent([loc.start.line, loc.end.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5392
|
+
lineNumber,
|
|
5393
|
+
block: true
|
|
5394
|
+
}), additionalFixes);
|
|
5395
|
+
/**
|
|
5396
|
+
* Additional fixes for code block content lines.
|
|
5397
|
+
*/
|
|
5398
|
+
function* additionalFixes(fixer, info) {
|
|
5399
|
+
if (info.loc.start.line !== loc.start.line) return;
|
|
5400
|
+
for (let lineNumber = loc.start.line + 1; lineNumber < loc.end.line; lineNumber++) {
|
|
5401
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5402
|
+
if (!line) continue;
|
|
5403
|
+
if (info.expectedIndentWidth > info.actualIndentWidth) {
|
|
5404
|
+
const before = sliceWidth(line.text, 0, info.actualIndentWidth);
|
|
5405
|
+
const after = sliceWidth(line.text, info.actualIndentWidth);
|
|
5406
|
+
const diffWidth = info.expectedIndentWidth - info.actualIndentWidth;
|
|
5407
|
+
yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + " ".repeat(diffWidth) + after);
|
|
5408
|
+
} else {
|
|
5409
|
+
let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
|
|
5410
|
+
let between = sliceWidth(line.text, info.expectedIndentWidth, info.actualIndentWidth);
|
|
5411
|
+
const after = sliceWidth(line.text, info.actualIndentWidth);
|
|
5412
|
+
while (between && !isSpaceOrTab(between)) {
|
|
5413
|
+
before += between[0];
|
|
5414
|
+
between = between.slice(1);
|
|
5415
|
+
}
|
|
5416
|
+
yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + after);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
/**
|
|
5422
|
+
* Verify an HTML node.
|
|
5423
|
+
*/
|
|
5424
|
+
function verifyHtml(node) {
|
|
5425
|
+
const loc = sourceCode.getLoc(node);
|
|
5426
|
+
if (!inlineToBeChecked(loc.start)) return;
|
|
5427
|
+
verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5428
|
+
lineNumber,
|
|
5429
|
+
block: true
|
|
5430
|
+
}));
|
|
5431
|
+
}
|
|
5432
|
+
/**
|
|
5433
|
+
* Verify a link definition node.
|
|
5434
|
+
*/
|
|
5435
|
+
function verifyLinkDefinition(node) {
|
|
5436
|
+
const parsed = parseLinkDefinition(sourceCode, node);
|
|
5437
|
+
if (!parsed) return;
|
|
5438
|
+
const loc = sourceCode.getLoc(node);
|
|
5439
|
+
verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber, column) => {
|
|
5440
|
+
const baseIndent = blockStack.getExpectedIndent({
|
|
5441
|
+
lineNumber,
|
|
5442
|
+
block: true
|
|
5443
|
+
});
|
|
5444
|
+
if (baseIndent === "ignore") return "ignore";
|
|
5445
|
+
if (lineNumber <= loc.start.line) return baseIndent;
|
|
5446
|
+
if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
|
|
5447
|
+
if (lineNumber === parsed.label.loc.end.line) {
|
|
5448
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5449
|
+
if (!line) return baseIndent;
|
|
5450
|
+
const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
|
|
5451
|
+
return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
|
|
5452
|
+
}
|
|
5453
|
+
if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 4;
|
|
5454
|
+
if (!parsed.title) return baseIndent;
|
|
5455
|
+
if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 4;
|
|
5456
|
+
if (lineNumber < parsed.title.loc.end.line) return baseIndent + 6;
|
|
5457
|
+
if (lineNumber === parsed.title.loc.end.line) {
|
|
5458
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5459
|
+
if (!line) return baseIndent;
|
|
5460
|
+
const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
|
|
5461
|
+
return !between || isWhitespace(between) ? baseIndent + 4 : baseIndent + 6;
|
|
5462
|
+
}
|
|
5463
|
+
return baseIndent;
|
|
5464
|
+
});
|
|
5465
|
+
}
|
|
5466
|
+
/**
|
|
5467
|
+
* Verify a blockquote node.
|
|
5468
|
+
*/
|
|
5469
|
+
function verifyBlockquote(node) {
|
|
5470
|
+
const loc = sourceCode.getLoc(node);
|
|
5471
|
+
verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
|
|
5472
|
+
lineNumber,
|
|
5473
|
+
block: true
|
|
5474
|
+
}));
|
|
5475
|
+
}
|
|
5476
|
+
/**
|
|
5477
|
+
* Verify a table node.
|
|
5478
|
+
*/
|
|
5479
|
+
function verifyTable(node) {
|
|
5480
|
+
const loc = sourceCode.getLoc(node);
|
|
5481
|
+
verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
|
|
5482
|
+
lineNumber,
|
|
5483
|
+
block: true
|
|
5484
|
+
}));
|
|
5485
|
+
}
|
|
5486
|
+
/**
|
|
5487
|
+
* Verify a list node.
|
|
5488
|
+
*/
|
|
5489
|
+
function verifyList(node) {
|
|
5490
|
+
const loc = sourceCode.getLoc(node);
|
|
5491
|
+
verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5492
|
+
lineNumber,
|
|
5493
|
+
block: true
|
|
5494
|
+
}), additionalFixes);
|
|
5495
|
+
/**
|
|
5496
|
+
* Additional fixes for list item lines.
|
|
5497
|
+
*/
|
|
5498
|
+
function* additionalFixes(fixer, info) {
|
|
5499
|
+
const [firstItem, ...otherItems] = node.children;
|
|
5500
|
+
yield* fixAfterFirstLine(firstItem, info.actualIndentWidth);
|
|
5501
|
+
for (const listItem of otherItems) {
|
|
5502
|
+
const listItemLoc = sourceCode.getLoc(listItem);
|
|
5503
|
+
const listItemIndentWidth = getWidth(sourceCode.lines[listItemLoc.start.line - 1].slice(0, listItemLoc.start.column - 1));
|
|
5504
|
+
if (listItemIndentWidth === info.expectedIndentWidth) continue;
|
|
5505
|
+
yield getFixForLine(listItemLoc.start.line, listItemIndentWidth);
|
|
5506
|
+
yield* fixAfterFirstLine(listItem, listItemIndentWidth);
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Get fixes for lines after the first line of the list item.
|
|
5510
|
+
*/
|
|
5511
|
+
function* fixAfterFirstLine(item, actualIndentWidth) {
|
|
5512
|
+
const itemLoc = sourceCode.getLoc(item);
|
|
5513
|
+
for (let lineNumber = itemLoc.start.line + 1; lineNumber <= itemLoc.end.line; lineNumber++) yield getFixForLine(lineNumber, actualIndentWidth);
|
|
5514
|
+
}
|
|
5515
|
+
/**
|
|
5516
|
+
* Get a fix for a line.
|
|
5517
|
+
*/
|
|
5518
|
+
function getFixForLine(lineNumber, actualIndentWidth) {
|
|
5519
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5520
|
+
if (info.expectedIndentWidth > actualIndentWidth) {
|
|
5521
|
+
const before$1 = sliceWidth(line.text, 0, actualIndentWidth);
|
|
5522
|
+
const diffWidth = info.expectedIndentWidth - actualIndentWidth;
|
|
5523
|
+
return fixer.insertTextAfterRange([line.range[0], line.range[0] + before$1.length], " ".repeat(diffWidth));
|
|
5524
|
+
}
|
|
5525
|
+
let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
|
|
5526
|
+
let between = sliceWidth(line.text, info.expectedIndentWidth, actualIndentWidth);
|
|
5527
|
+
while (between && !isSpaceOrTab(between)) {
|
|
5528
|
+
before += between[0];
|
|
5529
|
+
between = between.slice(1);
|
|
5530
|
+
}
|
|
5531
|
+
return fixer.replaceTextRange([line.range[0], line.range[0] + before.length + between.length], before);
|
|
5532
|
+
}
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
/**
|
|
5536
|
+
* Verify a footnote definition node.
|
|
5537
|
+
*/
|
|
5538
|
+
function verifyFootnoteDefinition(node) {
|
|
5539
|
+
const loc = sourceCode.getLoc(node);
|
|
5540
|
+
verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5541
|
+
lineNumber,
|
|
5542
|
+
block: true
|
|
5543
|
+
}));
|
|
5544
|
+
}
|
|
5545
|
+
/**
|
|
5546
|
+
* Verify an inline code node.
|
|
5547
|
+
*/
|
|
5548
|
+
function verifyInlineCode(node) {
|
|
5549
|
+
const loc = sourceCode.getLoc(node);
|
|
5550
|
+
if (!inlineToBeChecked(loc.start)) return;
|
|
5551
|
+
verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5552
|
+
lineNumber,
|
|
5553
|
+
block: false
|
|
5554
|
+
}));
|
|
5555
|
+
}
|
|
5556
|
+
/**
|
|
5557
|
+
* Verify an emphasis, strong, or delete node.
|
|
5558
|
+
*/
|
|
5559
|
+
function verifyEmphasisOrStrongOrDelete(node) {
|
|
5560
|
+
const loc = sourceCode.getLoc(node);
|
|
5561
|
+
if (!inlineToBeChecked(loc.start)) return;
|
|
5562
|
+
verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
|
|
5563
|
+
lineNumber,
|
|
5564
|
+
block: false
|
|
5565
|
+
}));
|
|
5566
|
+
}
|
|
5567
|
+
/**
|
|
5568
|
+
* Verify a link node.
|
|
5569
|
+
*/
|
|
5570
|
+
function verifyLink(node) {
|
|
5571
|
+
const loc = sourceCode.getLoc(node);
|
|
5572
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5573
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5574
|
+
const kind = getLinkKind(sourceCode, node);
|
|
5575
|
+
if (kind === "autolink" || kind === "gfm-autolink") verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
|
|
5576
|
+
lineNumber,
|
|
5577
|
+
block: false
|
|
5578
|
+
}));
|
|
5579
|
+
else if (kind === "inline") {
|
|
5580
|
+
const parsed = parseInlineLink(sourceCode, node);
|
|
5581
|
+
if (!parsed) return;
|
|
5582
|
+
const lastChild = node.children.at(-1);
|
|
5583
|
+
if (lastChild && parsed.text.loc.start.line < parsed.text.loc.end.line) {
|
|
5584
|
+
const lastChildLoc = sourceCode.getLoc(lastChild);
|
|
5585
|
+
const lastChildEndLine = lastChild.type === "text" && lastChild.value.endsWith("\n") ? lastChildLoc.end.line - 1 : lastChildLoc.end.line;
|
|
5586
|
+
lines = lines.filter((lineNumber) => lineNumber <= parsed.text.loc.start.line || lastChildEndLine < lineNumber);
|
|
5587
|
+
}
|
|
5588
|
+
verifyLinesIndent(lines, (lineNumber, column) => {
|
|
5589
|
+
const baseIndent = blockStack.getExpectedIndent({
|
|
5590
|
+
lineNumber,
|
|
5591
|
+
block: false
|
|
5592
|
+
});
|
|
5593
|
+
if (baseIndent === "ignore") return "ignore";
|
|
5594
|
+
if (lineNumber <= loc.start.line) return baseIndent;
|
|
5595
|
+
if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
|
|
5596
|
+
if (lineNumber <= parsed.openingParen.loc.end.line) return baseIndent;
|
|
5597
|
+
if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 2;
|
|
5598
|
+
if (!parsed.title) return baseIndent;
|
|
5599
|
+
if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 2;
|
|
5600
|
+
if (lineNumber < parsed.title.loc.end.line) return baseIndent + 4;
|
|
5601
|
+
if (lineNumber === parsed.title.loc.end.line) {
|
|
5602
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5603
|
+
if (!line) return baseIndent;
|
|
5604
|
+
const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
|
|
5605
|
+
return !between || isWhitespace(between) ? baseIndent + 2 : baseIndent + 4;
|
|
5606
|
+
}
|
|
5607
|
+
return baseIndent;
|
|
5608
|
+
});
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
/**
|
|
5612
|
+
* Verify a link reference node.
|
|
5613
|
+
*/
|
|
5614
|
+
function verifyLinkReference(node) {
|
|
5615
|
+
const loc = sourceCode.getLoc(node);
|
|
5616
|
+
const parsed = parseLinkReference(sourceCode, node);
|
|
5617
|
+
if (!parsed) return;
|
|
5618
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5619
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5620
|
+
const lastChild = node.children.at(-1);
|
|
5621
|
+
if (lastChild && parsed.text.loc.start.line < parsed.text.loc.end.line) {
|
|
5622
|
+
const lastChildLoc = sourceCode.getLoc(lastChild);
|
|
5623
|
+
const lastChildEndLine = lastChild.type === "text" && lastChild.value.endsWith("\n") ? lastChildLoc.end.line - 1 : lastChildLoc.end.line;
|
|
5624
|
+
lines = lines.filter((lineNumber) => lineNumber <= parsed.text.loc.start.line || lastChildEndLine < lineNumber);
|
|
5625
|
+
}
|
|
5626
|
+
verifyLinesIndent(lines, (lineNumber, column) => {
|
|
5627
|
+
const baseIndent = blockStack.getExpectedIndent({
|
|
5628
|
+
lineNumber,
|
|
5629
|
+
block: false
|
|
5630
|
+
});
|
|
5631
|
+
if (baseIndent === "ignore") return "ignore";
|
|
5632
|
+
if (lineNumber <= loc.start.line) return baseIndent;
|
|
5633
|
+
if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
|
|
5634
|
+
if (!parsed.label) return baseIndent;
|
|
5635
|
+
if (lineNumber <= parsed.label.loc.start.line) return baseIndent;
|
|
5636
|
+
if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
|
|
5637
|
+
if (lineNumber === parsed.label.loc.end.line) {
|
|
5638
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5639
|
+
if (!line) return baseIndent;
|
|
5640
|
+
const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
|
|
5641
|
+
return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
|
|
5642
|
+
}
|
|
5643
|
+
return baseIndent;
|
|
5644
|
+
});
|
|
5645
|
+
}
|
|
5646
|
+
/**
|
|
5647
|
+
* Verify an image node.
|
|
5648
|
+
*/
|
|
5649
|
+
function verifyImage(node) {
|
|
5650
|
+
const loc = sourceCode.getLoc(node);
|
|
5651
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5652
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5653
|
+
const parsed = parseImage(sourceCode, node);
|
|
5654
|
+
if (!parsed) return;
|
|
5655
|
+
verifyLinesIndent(lines, (lineNumber, column) => {
|
|
5656
|
+
const baseIndent = blockStack.getExpectedIndent({
|
|
5657
|
+
lineNumber,
|
|
5658
|
+
block: false
|
|
5659
|
+
});
|
|
5660
|
+
if (baseIndent === "ignore") return "ignore";
|
|
5661
|
+
if (lineNumber <= loc.start.line) return baseIndent;
|
|
5662
|
+
if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
|
|
5663
|
+
if (lineNumber === parsed.text.loc.end.line) {
|
|
5664
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5665
|
+
if (!line) return baseIndent;
|
|
5666
|
+
const between = line.text.slice(column - 1, parsed.text.loc.end.column - 2);
|
|
5667
|
+
return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
|
|
5668
|
+
}
|
|
5669
|
+
if (lineNumber <= parsed.openingParen.loc.end.line) return baseIndent;
|
|
5670
|
+
if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 2;
|
|
5671
|
+
if (!parsed.title) return baseIndent;
|
|
5672
|
+
if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 2;
|
|
5673
|
+
if (lineNumber < parsed.title.loc.end.line) return baseIndent + 4;
|
|
5674
|
+
if (lineNumber === parsed.title.loc.end.line) {
|
|
5675
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5676
|
+
if (!line) return baseIndent;
|
|
5677
|
+
const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
|
|
5678
|
+
return !between || isWhitespace(between) ? baseIndent + 2 : baseIndent + 4;
|
|
5679
|
+
}
|
|
5680
|
+
return baseIndent;
|
|
5681
|
+
});
|
|
5682
|
+
}
|
|
5683
|
+
/**
|
|
5684
|
+
* Verify an image reference node.
|
|
5685
|
+
*/
|
|
5686
|
+
function verifyImageReference(node) {
|
|
5687
|
+
const loc = sourceCode.getLoc(node);
|
|
5688
|
+
const parsed = parseImageReference(sourceCode, node);
|
|
5689
|
+
if (!parsed) return;
|
|
5690
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5691
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5692
|
+
verifyLinesIndent(lines, (lineNumber, column) => {
|
|
5693
|
+
const baseIndent = blockStack.getExpectedIndent({
|
|
5694
|
+
lineNumber,
|
|
5695
|
+
block: false
|
|
5696
|
+
});
|
|
5697
|
+
if (baseIndent === "ignore") return "ignore";
|
|
5698
|
+
if (lineNumber <= loc.start.line) return baseIndent;
|
|
5699
|
+
if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
|
|
5700
|
+
if (lineNumber === parsed.text.loc.end.line) {
|
|
5701
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5702
|
+
if (!line) return baseIndent;
|
|
5703
|
+
const between = line.text.slice(column - 1, parsed.text.loc.end.column - 2);
|
|
5704
|
+
return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
|
|
5705
|
+
}
|
|
5706
|
+
if (!parsed.label) return baseIndent;
|
|
5707
|
+
if (lineNumber <= parsed.label.loc.start.line) return baseIndent;
|
|
5708
|
+
if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
|
|
5709
|
+
if (lineNumber === parsed.label.loc.end.line) {
|
|
5710
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5711
|
+
if (!line) return baseIndent;
|
|
5712
|
+
const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
|
|
5713
|
+
return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
|
|
5714
|
+
}
|
|
5715
|
+
return baseIndent;
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
/**
|
|
5719
|
+
* Verify a text node.
|
|
5720
|
+
*/
|
|
5721
|
+
function verifyText(node) {
|
|
5722
|
+
const loc = sourceCode.getLoc(node);
|
|
5723
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5724
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5725
|
+
if (node.value.endsWith("\n")) lines = lines.slice(0, -1);
|
|
5726
|
+
verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
|
|
5727
|
+
lineNumber,
|
|
5728
|
+
block: false
|
|
5729
|
+
}));
|
|
5730
|
+
}
|
|
5731
|
+
/**
|
|
5732
|
+
* Verify an inline node.
|
|
5733
|
+
*/
|
|
5734
|
+
function verifyInline(node) {
|
|
5735
|
+
const loc = sourceCode.getLoc(node);
|
|
5736
|
+
let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
|
|
5737
|
+
if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
|
|
5738
|
+
verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
|
|
5739
|
+
lineNumber,
|
|
5740
|
+
block: false
|
|
5741
|
+
}));
|
|
5742
|
+
}
|
|
5743
|
+
/**
|
|
5744
|
+
* Check whether the inline node should be checked.
|
|
5745
|
+
*/
|
|
5746
|
+
function inlineToBeChecked(position) {
|
|
5747
|
+
const blockquote = blockStack.getCurrentBlockquote();
|
|
5748
|
+
const listItem = blockStack.getCurrentListItemAtLine(position.line);
|
|
5749
|
+
const lineText = sourceCode.lines[position.line - 1];
|
|
5750
|
+
if (listItem) {
|
|
5751
|
+
const parsed = listItem.getParsedListItem();
|
|
5752
|
+
const indentText = lineText.slice((parsed.taskListItemMarker?.loc ?? parsed.marker.loc).end.column - 1, position.column - 1);
|
|
5753
|
+
if (indentText && !isSpaceOrTab(indentText)) return false;
|
|
5754
|
+
} else if (blockquote) {
|
|
5755
|
+
if (atWidth(lineText, blockquote.getMarkerIndent()) !== ">") return false;
|
|
5756
|
+
const indentText = sliceWidth(lineText.slice(0, position.column - 1), blockquote.getMarkerIndent() + 1);
|
|
5757
|
+
if (indentText && !isSpaceOrTab(indentText)) return false;
|
|
5758
|
+
} else {
|
|
5759
|
+
const indentText = lineText.slice(0, position.column - 1);
|
|
5760
|
+
if (indentText && !isSpaceOrTab(indentText)) return false;
|
|
5761
|
+
}
|
|
5762
|
+
return true;
|
|
5763
|
+
}
|
|
5764
|
+
/**
|
|
5765
|
+
* Get line numbers from the range.
|
|
5766
|
+
*/
|
|
5767
|
+
function lineNumbersFromRange(lineNumberFrom, lineNumberTo) {
|
|
5768
|
+
const lines = [];
|
|
5769
|
+
for (let lineNumber = lineNumberFrom; lineNumber <= lineNumberTo; lineNumber++) lines.push(lineNumber);
|
|
5770
|
+
return lines;
|
|
5771
|
+
}
|
|
5772
|
+
/**
|
|
5773
|
+
* Verify the indentation of the lines.
|
|
5774
|
+
*/
|
|
5775
|
+
function verifyLinesIndent(lineNumbers, expectedIndentWidthProvider, additionalFixes) {
|
|
5776
|
+
const blockquote = blockStack.getCurrentBlockquote();
|
|
5777
|
+
if (!blockquote) {
|
|
5778
|
+
verifyLinesIndentFromRoot(lineNumbers, expectedIndentWidthProvider, additionalFixes);
|
|
5779
|
+
return;
|
|
5780
|
+
}
|
|
5781
|
+
verifyLinesIndentFromBlockquoteMarker(lineNumbers, blockquote, expectedIndentWidthProvider, additionalFixes);
|
|
5782
|
+
}
|
|
5783
|
+
/**
|
|
5784
|
+
* Verify the indentation of the lines from the root.
|
|
5785
|
+
*/
|
|
5786
|
+
function verifyLinesIndentFromRoot(lineNumbers, expectedIndentWidthProvider, additionalFixes) {
|
|
5787
|
+
const reports = [];
|
|
5788
|
+
for (const lineNumber of lineNumbers) {
|
|
5789
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5790
|
+
if (!line) return;
|
|
5791
|
+
const listItem = blockStack.getCurrentListItemAtLine(lineNumber);
|
|
5792
|
+
if (!listItem) {
|
|
5793
|
+
const indentText = /^\s*/u.exec(line.text)[0];
|
|
5794
|
+
const actualIndentWidth = getWidth(indentText);
|
|
5795
|
+
const expectedIndentWidth = expectedIndentWidthProvider(lineNumber, 1);
|
|
5796
|
+
if (expectedIndentWidth === "ignore") continue;
|
|
5797
|
+
if (actualIndentWidth === expectedIndentWidth) continue;
|
|
5798
|
+
reports.push({
|
|
5799
|
+
loc: {
|
|
5800
|
+
start: {
|
|
5801
|
+
line: line.line,
|
|
5802
|
+
column: 1
|
|
5803
|
+
},
|
|
5804
|
+
end: {
|
|
5805
|
+
line: line.line,
|
|
5806
|
+
column: indentText.length + 1
|
|
5807
|
+
}
|
|
5808
|
+
},
|
|
5809
|
+
messageId: indentText.includes(" ") ? "wrongIndentationWithTab" : "wrongIndentation",
|
|
5810
|
+
data: {
|
|
5811
|
+
expected: expectedIndentWidth,
|
|
5812
|
+
actual: actualIndentWidth
|
|
5813
|
+
},
|
|
5814
|
+
fix(fixer) {
|
|
5815
|
+
return fixer.replaceTextRange([line.range[0], line.range[0] + indentText.length], " ".repeat(expectedIndentWidth));
|
|
5816
|
+
},
|
|
5817
|
+
expectedIndentWidth,
|
|
5818
|
+
actualIndentWidth
|
|
5819
|
+
});
|
|
5820
|
+
continue;
|
|
5821
|
+
}
|
|
5822
|
+
const report = verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider);
|
|
5823
|
+
if (!report) continue;
|
|
5824
|
+
reports.push(report);
|
|
5825
|
+
}
|
|
5826
|
+
if (!reports.length) return;
|
|
5827
|
+
for (const report of reports) reportIncorrectIndent({
|
|
5828
|
+
loc: report.loc,
|
|
5829
|
+
messageId: report.messageId,
|
|
5830
|
+
data: report.data,
|
|
5831
|
+
*fix(fixer) {
|
|
5832
|
+
yield report.fix(fixer);
|
|
5833
|
+
if (additionalFixes) yield* additionalFixes(fixer, report) ?? [];
|
|
5834
|
+
}
|
|
5835
|
+
});
|
|
5836
|
+
}
|
|
5837
|
+
/**
|
|
5838
|
+
* Verify the indentation of the lines from the blockquote marker.
|
|
5839
|
+
*/
|
|
5840
|
+
function verifyLinesIndentFromBlockquoteMarker(lineNumbers, blockquote, expectedIndentWidthProvider, additionalFixes) {
|
|
5841
|
+
const blockquoteLoc = sourceCode.getLoc(blockquote.node);
|
|
5842
|
+
const reports = [];
|
|
5843
|
+
let cannotFix = false;
|
|
5844
|
+
for (const lineNumber of lineNumbers) {
|
|
5845
|
+
const line = getParsedLines(sourceCode).get(lineNumber);
|
|
5846
|
+
if (!line) return;
|
|
5847
|
+
if (atWidth(line.text, blockquote.getMarkerIndent()) !== ">") {
|
|
5848
|
+
cannotFix = true;
|
|
5849
|
+
continue;
|
|
5850
|
+
}
|
|
5851
|
+
const listItem = blockStack.getCurrentListItemAtLine(lineNumber);
|
|
5852
|
+
if (!listItem) {
|
|
5853
|
+
const before = line.text.slice(0, blockquoteLoc.start.column);
|
|
5854
|
+
const after = line.text.slice(blockquoteLoc.start.column);
|
|
5855
|
+
const indentText = /^\s*/u.exec(after)[0];
|
|
5856
|
+
const actualIndentWidth = getWidth(before + indentText);
|
|
5857
|
+
const expectedIndentWidth = expectedIndentWidthProvider(lineNumber, blockquoteLoc.start.column + 1);
|
|
5858
|
+
if (expectedIndentWidth === "ignore") continue;
|
|
5859
|
+
if (actualIndentWidth === expectedIndentWidth) continue;
|
|
5860
|
+
const linePrefixWidth = getWidth(before);
|
|
5861
|
+
reports.push({
|
|
5862
|
+
loc: {
|
|
5863
|
+
start: {
|
|
5864
|
+
line: line.line,
|
|
5865
|
+
column: blockquoteLoc.start.column + 1
|
|
5866
|
+
},
|
|
5867
|
+
end: {
|
|
5868
|
+
line: line.line,
|
|
5869
|
+
column: blockquoteLoc.start.column + 1 + indentText.length
|
|
5870
|
+
}
|
|
5871
|
+
},
|
|
5872
|
+
messageId: indentText.includes(" ") ? "wrongIndentationFromBlockquoteMarkerWithTab" : "wrongIndentationFromBlockquoteMarker",
|
|
5873
|
+
data: {
|
|
5874
|
+
expected: expectedIndentWidth - linePrefixWidth,
|
|
5875
|
+
actual: actualIndentWidth - linePrefixWidth
|
|
5876
|
+
},
|
|
5877
|
+
fix(fixer) {
|
|
5878
|
+
return fixer.replaceTextRange([line.range[0] + blockquoteLoc.start.column, line.range[0] + blockquoteLoc.start.column + indentText.length], " ".repeat(expectedIndentWidth - linePrefixWidth));
|
|
5879
|
+
},
|
|
5880
|
+
expectedIndentWidth,
|
|
5881
|
+
actualIndentWidth
|
|
5882
|
+
});
|
|
5883
|
+
continue;
|
|
5884
|
+
}
|
|
5885
|
+
const report = verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider);
|
|
5886
|
+
if (!report) continue;
|
|
5887
|
+
reports.push(report);
|
|
5888
|
+
}
|
|
5889
|
+
if (!reports.length) return;
|
|
5890
|
+
for (const report of reports) reportIncorrectIndent({
|
|
5891
|
+
loc: report.loc,
|
|
5892
|
+
messageId: report.messageId,
|
|
5893
|
+
data: report.data,
|
|
5894
|
+
*fix(fixer) {
|
|
5895
|
+
if (cannotFix) return;
|
|
5896
|
+
yield report.fix(fixer);
|
|
5897
|
+
if (additionalFixes) yield* additionalFixes(fixer, report) ?? [];
|
|
5898
|
+
}
|
|
5899
|
+
});
|
|
5900
|
+
}
|
|
5901
|
+
/**
|
|
5902
|
+
* Verify the indentation of the line from the list item marker.
|
|
5903
|
+
*/
|
|
5904
|
+
function verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider) {
|
|
5905
|
+
const parsed = listItem.getParsedListItem();
|
|
5906
|
+
const markerAfterColumn = (parsed.taskListItemMarker?.loc ?? parsed.marker.loc).end.column;
|
|
5907
|
+
const before = line.text.slice(0, markerAfterColumn - 1);
|
|
5908
|
+
const after = line.text.slice(markerAfterColumn - 1);
|
|
5909
|
+
const indentText = /^\s*/u.exec(after)[0];
|
|
5910
|
+
const actualIndentWidth = getWidth(before + indentText);
|
|
5911
|
+
const expectedIndentWidth = expectedIndentWidthProvider(line.line, markerAfterColumn);
|
|
5912
|
+
if (expectedIndentWidth === "ignore") return null;
|
|
5913
|
+
if (actualIndentWidth === expectedIndentWidth) return null;
|
|
5914
|
+
const linePrefixWidth = getWidth(before);
|
|
5915
|
+
const range = [line.range[0] + before.length, line.range[0] + before.length + indentText.length];
|
|
5916
|
+
return {
|
|
5917
|
+
loc: getSourceLocationFromRange(sourceCode, listItem.node, range),
|
|
5918
|
+
messageId: indentText.includes(" ") ? "wrongIndentationFromListMarkerWithTab" : "wrongIndentationFromListMarker",
|
|
5919
|
+
data: {
|
|
5920
|
+
expected: expectedIndentWidth - linePrefixWidth,
|
|
5921
|
+
actual: actualIndentWidth - linePrefixWidth
|
|
5922
|
+
},
|
|
5923
|
+
fix(fixer) {
|
|
5924
|
+
return fixer.replaceTextRange(range, " ".repeat(expectedIndentWidth - linePrefixWidth));
|
|
5925
|
+
},
|
|
5926
|
+
expectedIndentWidth,
|
|
5927
|
+
actualIndentWidth
|
|
5928
|
+
};
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
});
|
|
5932
|
+
|
|
5933
|
+
//#endregion
|
|
5934
|
+
//#region src/utils/setext-heading.ts
|
|
5935
|
+
/**
|
|
5936
|
+
* Parse the setext heading.
|
|
5937
|
+
*/
|
|
5938
|
+
function parseSetextHeading(sourceCode, node) {
|
|
5939
|
+
if (getHeadingKind(sourceCode, node) !== "setext") return null;
|
|
5940
|
+
const lines = getParsedLines(sourceCode);
|
|
5941
|
+
const contentLines = [];
|
|
5942
|
+
const nodeLoc = sourceCode.getLoc(node);
|
|
5943
|
+
for (let lineNumber = nodeLoc.start.line; lineNumber < nodeLoc.end.line; lineNumber++) {
|
|
5944
|
+
const content = parseContent(lines.get(lineNumber));
|
|
5945
|
+
contentLines.push(content);
|
|
5946
|
+
}
|
|
5947
|
+
const underline = parseUnderline(lines.get(nodeLoc.end.line));
|
|
5948
|
+
if (!underline) return null;
|
|
5949
|
+
return {
|
|
5950
|
+
contentLines,
|
|
5951
|
+
underline
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
/**
|
|
5955
|
+
* Parse the content line of a setext heading.
|
|
5956
|
+
*/
|
|
5957
|
+
function parseContent(line) {
|
|
5958
|
+
let prefix = "";
|
|
5959
|
+
let spaceBefore = "";
|
|
5960
|
+
let suffix = "";
|
|
5961
|
+
for (let index = 0; index < line.text.length; index++) {
|
|
5962
|
+
const c = line.text[index];
|
|
5963
|
+
if (!c.trim()) {
|
|
5964
|
+
spaceBefore += c;
|
|
5965
|
+
continue;
|
|
5966
|
+
}
|
|
5967
|
+
if (c === ">" && spaceBefore.length < 4) {
|
|
5968
|
+
prefix += spaceBefore + c;
|
|
5969
|
+
spaceBefore = "";
|
|
5970
|
+
continue;
|
|
5971
|
+
}
|
|
5972
|
+
suffix = line.text.slice(index);
|
|
5973
|
+
break;
|
|
5974
|
+
}
|
|
5975
|
+
const content = suffix.trimEnd();
|
|
5976
|
+
const spaceAfter = suffix.slice(content.length);
|
|
5977
|
+
return {
|
|
5978
|
+
text: content,
|
|
5979
|
+
range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
|
|
5980
|
+
loc: {
|
|
5981
|
+
start: {
|
|
5982
|
+
line: line.line,
|
|
5983
|
+
column: prefix.length + spaceBefore.length + 1
|
|
5984
|
+
},
|
|
5985
|
+
end: {
|
|
5986
|
+
line: line.line,
|
|
5987
|
+
column: prefix.length + spaceBefore.length + content.length + 1
|
|
5988
|
+
}
|
|
5989
|
+
},
|
|
5990
|
+
raws: {
|
|
5991
|
+
prefix,
|
|
5992
|
+
spaceBefore,
|
|
5993
|
+
spaceAfter
|
|
5994
|
+
}
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
/**
|
|
5998
|
+
* Parse the underline of a setext heading.
|
|
5999
|
+
*/
|
|
6000
|
+
function parseUnderline(line) {
|
|
6001
|
+
let marker = null;
|
|
6002
|
+
let underlineText = "";
|
|
6003
|
+
let prefix = "";
|
|
6004
|
+
let spaceBefore = "";
|
|
6005
|
+
let spaceAfter = "";
|
|
6006
|
+
for (let index = line.text.length - 1; index >= 0; index--) {
|
|
6007
|
+
const c = line.text[index];
|
|
6008
|
+
if (!marker) {
|
|
6009
|
+
if (c === "=" || c === "-") {
|
|
6010
|
+
underlineText = c + underlineText;
|
|
6011
|
+
marker = c;
|
|
6012
|
+
} else if (!c.trim()) spaceAfter = c + spaceAfter;
|
|
6013
|
+
else return null;
|
|
6014
|
+
continue;
|
|
6015
|
+
}
|
|
6016
|
+
if (c === marker) {
|
|
6017
|
+
underlineText = c + spaceBefore + underlineText;
|
|
6018
|
+
spaceBefore = "";
|
|
6019
|
+
} else if (!c.trim()) spaceBefore = c + spaceBefore;
|
|
6020
|
+
else {
|
|
6021
|
+
prefix = line.text.slice(0, index + 1);
|
|
6022
|
+
break;
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
if (!marker) return null;
|
|
6026
|
+
const underlineLoc = {
|
|
6027
|
+
start: {
|
|
6028
|
+
line: line.line,
|
|
6029
|
+
column: prefix.length + spaceBefore.length + 1
|
|
6030
|
+
},
|
|
6031
|
+
end: {
|
|
6032
|
+
line: line.line,
|
|
6033
|
+
column: prefix.length + spaceBefore.length + underlineText.length + 1
|
|
6034
|
+
}
|
|
6035
|
+
};
|
|
6036
|
+
return {
|
|
6037
|
+
text: underlineText,
|
|
6038
|
+
range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
|
|
6039
|
+
loc: underlineLoc,
|
|
6040
|
+
marker,
|
|
6041
|
+
raws: {
|
|
6042
|
+
prefix,
|
|
6043
|
+
spaceBefore,
|
|
6044
|
+
spaceAfter
|
|
6045
|
+
}
|
|
6046
|
+
};
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
//#endregion
|
|
6050
|
+
//#region src/rules/level1-heading-style.ts
|
|
6051
|
+
var level1_heading_style_default = createRule("level1-heading-style", {
|
|
6052
|
+
meta: {
|
|
6053
|
+
type: "layout",
|
|
6054
|
+
docs: {
|
|
6055
|
+
description: "enforce consistent style for level 1 headings",
|
|
6056
|
+
categories: ["standard"],
|
|
6057
|
+
listCategory: "Notation"
|
|
6058
|
+
},
|
|
6059
|
+
fixable: "code",
|
|
6060
|
+
hasSuggestions: false,
|
|
6061
|
+
schema: [{
|
|
6062
|
+
type: "object",
|
|
6063
|
+
properties: {
|
|
6064
|
+
style: { enum: ["atx", "setext"] },
|
|
6065
|
+
allowMultilineSetext: { type: "boolean" }
|
|
6066
|
+
},
|
|
6067
|
+
additionalProperties: false
|
|
6068
|
+
}],
|
|
6069
|
+
messages: {
|
|
6070
|
+
expectedAtx: "Expected ATX style heading (# Heading).",
|
|
6071
|
+
expectedSetext: "Expected Setext style heading (Heading\\n======).",
|
|
6072
|
+
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
6073
|
+
}
|
|
6074
|
+
},
|
|
6075
|
+
create(context) {
|
|
6076
|
+
const sourceCode = context.sourceCode;
|
|
6077
|
+
const opt = context.options[0] || {};
|
|
6078
|
+
const style = opt.style ?? "atx";
|
|
6079
|
+
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
6080
|
+
return { heading(node) {
|
|
6081
|
+
if (node.depth !== 1) return;
|
|
6082
|
+
const headingKind = getHeadingKind(sourceCode, node);
|
|
6083
|
+
if (style === "atx") {
|
|
6084
|
+
if (headingKind !== "setext") return;
|
|
6085
|
+
const parsed = parseSetextHeading(sourceCode, node);
|
|
6086
|
+
if (!parsed) return;
|
|
6087
|
+
if (parsed.contentLines.length > 1) {
|
|
6088
|
+
if (allowMultilineSetext) return;
|
|
6089
|
+
context.report({
|
|
6090
|
+
node,
|
|
6091
|
+
messageId: "multilineSetextNotAllowed"
|
|
6092
|
+
});
|
|
6093
|
+
return;
|
|
6094
|
+
}
|
|
6095
|
+
context.report({
|
|
6096
|
+
node,
|
|
6097
|
+
messageId: "expectedAtx",
|
|
6098
|
+
*fix(fixer) {
|
|
6099
|
+
const heading = parsed.contentLines[0];
|
|
6100
|
+
yield fixer.insertTextBeforeRange(heading.range, "# ");
|
|
6101
|
+
const lines = getParsedLines(sourceCode);
|
|
6102
|
+
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
6103
|
+
}
|
|
6104
|
+
});
|
|
6105
|
+
} else if (style === "setext") {
|
|
6106
|
+
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
6107
|
+
context.report({
|
|
6108
|
+
node,
|
|
6109
|
+
messageId: "expectedSetext",
|
|
6110
|
+
*fix(fixer) {
|
|
6111
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
6112
|
+
if (!parsed) return;
|
|
6113
|
+
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
|
|
6114
|
+
if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
|
|
6115
|
+
const lines = getParsedLines(sourceCode);
|
|
6116
|
+
const underline = "=".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
6117
|
+
const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
|
|
6118
|
+
yield fixer.insertTextAfter(node, appendingText);
|
|
6119
|
+
}
|
|
6120
|
+
});
|
|
6121
|
+
}
|
|
6122
|
+
} };
|
|
6123
|
+
}
|
|
6124
|
+
});
|
|
6125
|
+
|
|
6126
|
+
//#endregion
|
|
6127
|
+
//#region src/rules/level2-heading-style.ts
|
|
6128
|
+
var level2_heading_style_default = createRule("level2-heading-style", {
|
|
6129
|
+
meta: {
|
|
6130
|
+
type: "layout",
|
|
6131
|
+
docs: {
|
|
6132
|
+
description: "enforce consistent style for level 2 headings",
|
|
6133
|
+
categories: ["standard"],
|
|
6134
|
+
listCategory: "Notation"
|
|
6135
|
+
},
|
|
6136
|
+
fixable: "code",
|
|
6137
|
+
hasSuggestions: false,
|
|
6138
|
+
schema: [{
|
|
6139
|
+
type: "object",
|
|
6140
|
+
properties: {
|
|
6141
|
+
style: { enum: ["atx", "setext"] },
|
|
6142
|
+
allowMultilineSetext: { type: "boolean" }
|
|
6143
|
+
},
|
|
6144
|
+
additionalProperties: false
|
|
6145
|
+
}],
|
|
6146
|
+
messages: {
|
|
6147
|
+
expectedAtx: "Expected ATX style heading (## Heading).",
|
|
6148
|
+
expectedSetext: "Expected Setext style heading (Heading\\n------).",
|
|
6149
|
+
multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
|
|
6150
|
+
}
|
|
6151
|
+
},
|
|
6152
|
+
create(context) {
|
|
6153
|
+
const sourceCode = context.sourceCode;
|
|
6154
|
+
const opt = context.options[0] || {};
|
|
6155
|
+
const style = opt.style ?? "atx";
|
|
6156
|
+
const allowMultilineSetext = opt.allowMultilineSetext;
|
|
6157
|
+
return { heading(node) {
|
|
6158
|
+
if (node.depth !== 2) return;
|
|
6159
|
+
const headingKind = getHeadingKind(sourceCode, node);
|
|
6160
|
+
if (style === "atx") {
|
|
6161
|
+
if (headingKind !== "setext") return;
|
|
6162
|
+
const parsed = parseSetextHeading(sourceCode, node);
|
|
6163
|
+
if (!parsed) return;
|
|
6164
|
+
if (parsed.contentLines.length > 1) {
|
|
6165
|
+
if (allowMultilineSetext) return;
|
|
6166
|
+
context.report({
|
|
6167
|
+
node,
|
|
6168
|
+
messageId: "multilineSetextNotAllowed"
|
|
6169
|
+
});
|
|
6170
|
+
return;
|
|
6171
|
+
}
|
|
6172
|
+
context.report({
|
|
6173
|
+
node,
|
|
6174
|
+
messageId: "expectedAtx",
|
|
6175
|
+
*fix(fixer) {
|
|
6176
|
+
const heading = parsed.contentLines[0];
|
|
6177
|
+
yield fixer.insertTextBeforeRange(heading.range, "## ");
|
|
6178
|
+
const lines = getParsedLines(sourceCode);
|
|
6179
|
+
yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
|
|
6180
|
+
}
|
|
6181
|
+
});
|
|
6182
|
+
} else if (style === "setext") {
|
|
6183
|
+
if (headingKind !== "atx" || node.children.length === 0) return;
|
|
6184
|
+
context.report({
|
|
6185
|
+
node,
|
|
6186
|
+
messageId: "expectedSetext",
|
|
6187
|
+
*fix(fixer) {
|
|
6188
|
+
const parsed = parseATXHeading(sourceCode, node);
|
|
6189
|
+
if (!parsed) return;
|
|
6190
|
+
yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
|
|
6191
|
+
if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
|
|
6192
|
+
const lines = getParsedLines(sourceCode);
|
|
6193
|
+
const underline = "-".repeat(Math.max(getTextWidth(parsed.content.text), 3));
|
|
6194
|
+
const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
|
|
6195
|
+
yield fixer.insertTextAfter(node, appendingText);
|
|
6196
|
+
}
|
|
6197
|
+
});
|
|
6198
|
+
}
|
|
6199
|
+
} };
|
|
6200
|
+
}
|
|
6201
|
+
});
|
|
6202
|
+
|
|
6203
|
+
//#endregion
|
|
6204
|
+
//#region src/rules/link-bracket-newline.ts
|
|
6205
|
+
var link_bracket_newline_default = createRule("link-bracket-newline", {
|
|
6206
|
+
meta: {
|
|
6207
|
+
type: "layout",
|
|
6208
|
+
docs: {
|
|
6209
|
+
description: "enforce linebreaks after opening and before closing link brackets",
|
|
6210
|
+
categories: ["standard"],
|
|
6211
|
+
listCategory: "Whitespace"
|
|
6212
|
+
},
|
|
6213
|
+
fixable: "whitespace",
|
|
6214
|
+
hasSuggestions: false,
|
|
6215
|
+
schema: [{
|
|
6216
|
+
type: "object",
|
|
6217
|
+
properties: {
|
|
6218
|
+
newline: { enum: [
|
|
6219
|
+
"always",
|
|
6220
|
+
"never",
|
|
6221
|
+
"consistent"
|
|
6222
|
+
] },
|
|
6223
|
+
multiline: { type: "boolean" }
|
|
6224
|
+
},
|
|
6225
|
+
additionalProperties: false
|
|
6226
|
+
}],
|
|
6227
|
+
messages: {
|
|
6228
|
+
expectedNewlineAfterOpeningBracket: "Expected a linebreak after this opening bracket.",
|
|
6229
|
+
unexpectedNewlineAfterOpeningBracket: "Unexpected linebreak after this opening bracket.",
|
|
6230
|
+
expectedNewlineBeforeClosingBracket: "Expected a linebreak before this closing bracket.",
|
|
6231
|
+
unexpectedNewlineBeforeClosingBracket: "Unexpected linebreak before this closing bracket."
|
|
6232
|
+
}
|
|
6233
|
+
},
|
|
6234
|
+
create(context) {
|
|
6235
|
+
const sourceCode = context.sourceCode;
|
|
6236
|
+
const optionProvider = parseOptions$5(context.options[0]);
|
|
6237
|
+
/**
|
|
6238
|
+
* Parse the options.
|
|
6239
|
+
*/
|
|
6240
|
+
function parseOptions$5(option) {
|
|
6241
|
+
const newline = option?.newline ?? "never";
|
|
6242
|
+
const multiline = option?.multiline ?? false;
|
|
6243
|
+
return (bracketsRange) => {
|
|
6244
|
+
if (multiline) {
|
|
6245
|
+
if (sourceCode.text.slice(bracketsRange[0] + 1, bracketsRange[1] - 1).trim().includes("\n")) return "always";
|
|
6246
|
+
}
|
|
6247
|
+
return newline;
|
|
6248
|
+
};
|
|
6249
|
+
}
|
|
6250
|
+
/**
|
|
6251
|
+
* Verify the newline around the brackets.
|
|
6252
|
+
*/
|
|
6253
|
+
function verifyNewlineAroundBrackets(node, bracketsRange) {
|
|
6254
|
+
const newline = optionProvider(bracketsRange);
|
|
6255
|
+
const openingBracketIndex = bracketsRange[0];
|
|
6256
|
+
const spaceAfterOpeningBracket = getSpaceAfterOpeningBracket(openingBracketIndex);
|
|
6257
|
+
verifyNewlineAfterOpeningBracket(node, spaceAfterOpeningBracket, openingBracketIndex, newline);
|
|
6258
|
+
const closingBracketIndex = bracketsRange[1] - 1;
|
|
6259
|
+
const newlineOptionBeforeClosingBracket = newline === "consistent" ? getSpaceAfterOpeningBracket(bracketsRange[0]).includes("\n") ? "always" : "never" : newline;
|
|
6260
|
+
verifyNewlineBeforeClosingBracket(node, getSpaceBeforeClosingBracket(closingBracketIndex), openingBracketIndex, closingBracketIndex, newlineOptionBeforeClosingBracket);
|
|
6261
|
+
}
|
|
6262
|
+
/**
|
|
6263
|
+
* Verify the newline after the opening bracket and before the closing bracket.
|
|
6264
|
+
*/
|
|
6265
|
+
function verifyNewlineAfterOpeningBracket(node, spaceAfterOpeningBracket, openingBracketIndex, newline) {
|
|
6266
|
+
if (newline === "always") {
|
|
6267
|
+
if (spaceAfterOpeningBracket.includes("\n")) return;
|
|
6268
|
+
const loc = getSourceLocationFromRange(sourceCode, node, [openingBracketIndex, openingBracketIndex + 1]);
|
|
6269
|
+
context.report({
|
|
6270
|
+
node,
|
|
6271
|
+
loc,
|
|
6272
|
+
messageId: "expectedNewlineAfterOpeningBracket",
|
|
6273
|
+
fix: (fixer) => fixer.insertTextAfterRange([openingBracketIndex, openingBracketIndex + 1], `\n${" ".repeat(loc.start.column)}${spaceAfterOpeningBracket}`)
|
|
6274
|
+
});
|
|
6275
|
+
} else if (newline === "never") {
|
|
6276
|
+
if (!spaceAfterOpeningBracket.includes("\n")) return;
|
|
6277
|
+
context.report({
|
|
6278
|
+
node,
|
|
6279
|
+
loc: getSourceLocationFromRange(sourceCode, node, [openingBracketIndex + 1, openingBracketIndex + 1 + spaceAfterOpeningBracket.length]),
|
|
6280
|
+
messageId: "unexpectedNewlineAfterOpeningBracket",
|
|
6281
|
+
fix: (fixer) => fixer.replaceTextRange([openingBracketIndex + 1, openingBracketIndex + 1 + spaceAfterOpeningBracket.length], " ")
|
|
6282
|
+
});
|
|
6283
|
+
}
|
|
6284
|
+
}
|
|
6285
|
+
/**
|
|
6286
|
+
* Verify the newline before the closing bracket.
|
|
6287
|
+
*/
|
|
6288
|
+
function verifyNewlineBeforeClosingBracket(node, spaceBeforeClosingBracket, openingBracketIndex, closingBracketIndex, newline) {
|
|
6289
|
+
if (openingBracketIndex + 1 === closingBracketIndex - spaceBeforeClosingBracket.length) return;
|
|
6290
|
+
if (newline === "always") {
|
|
6291
|
+
if (spaceBeforeClosingBracket.includes("\n")) return;
|
|
6292
|
+
context.report({
|
|
6293
|
+
node,
|
|
6294
|
+
loc: getSourceLocationFromRange(sourceCode, node, [closingBracketIndex, closingBracketIndex + 1]),
|
|
6295
|
+
messageId: "expectedNewlineBeforeClosingBracket",
|
|
6296
|
+
fix: (fixer) => {
|
|
6297
|
+
const openingBracketLoc = getSourceLocationFromRange(sourceCode, node, [openingBracketIndex, openingBracketIndex + 1]);
|
|
6298
|
+
return fixer.insertTextBeforeRange([closingBracketIndex, closingBracketIndex + 1], `\n${" ".repeat(openingBracketLoc.start.column)}`);
|
|
6299
|
+
}
|
|
6300
|
+
});
|
|
6301
|
+
} else if (newline === "never") {
|
|
6302
|
+
if (!spaceBeforeClosingBracket.includes("\n")) return;
|
|
6303
|
+
context.report({
|
|
6304
|
+
node,
|
|
6305
|
+
loc: getSourceLocationFromRange(sourceCode, node, [closingBracketIndex - spaceBeforeClosingBracket.length, closingBracketIndex]),
|
|
6306
|
+
messageId: "unexpectedNewlineBeforeClosingBracket",
|
|
6307
|
+
fix: (fixer) => fixer.replaceTextRange([closingBracketIndex - spaceBeforeClosingBracket.length, closingBracketIndex], " ")
|
|
6308
|
+
});
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
return {
|
|
6312
|
+
link(node) {
|
|
6313
|
+
if (getLinkKind(sourceCode, node) !== "inline") return;
|
|
6314
|
+
const parsed = parseInlineLink(sourceCode, node);
|
|
6315
|
+
if (!parsed) return;
|
|
6316
|
+
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
6317
|
+
},
|
|
6318
|
+
linkReference(node) {
|
|
6319
|
+
const parsed = parseLinkReference(sourceCode, node);
|
|
6320
|
+
if (!parsed) return;
|
|
6321
|
+
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
6322
|
+
if (parsed.label?.type === "full") verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
6323
|
+
},
|
|
6324
|
+
image(node) {
|
|
6325
|
+
const parsed = parseImage(sourceCode, node);
|
|
6326
|
+
if (!parsed) return;
|
|
6327
|
+
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
6328
|
+
},
|
|
6329
|
+
imageReference(node) {
|
|
6330
|
+
const parsed = parseImageReference(sourceCode, node);
|
|
6331
|
+
if (!parsed) return;
|
|
6332
|
+
verifyNewlineAroundBrackets(node, parsed.text.range);
|
|
6333
|
+
if (parsed.label?.type === "full") verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
6334
|
+
},
|
|
6335
|
+
definition(node) {
|
|
6336
|
+
const parsed = parseLinkDefinition(sourceCode, node);
|
|
6337
|
+
if (!parsed) return;
|
|
6338
|
+
verifyNewlineAroundBrackets(node, parsed.label.range);
|
|
6339
|
+
}
|
|
6340
|
+
};
|
|
6341
|
+
/**
|
|
6342
|
+
* Get the spaces after the opening bracket.
|
|
6343
|
+
*/
|
|
6344
|
+
function getSpaceAfterOpeningBracket(openingBracketIndex) {
|
|
6345
|
+
for (let i = openingBracketIndex + 1; i < sourceCode.text.length; i++) {
|
|
6346
|
+
const char = sourceCode.text[i];
|
|
6347
|
+
if (isWhitespace(char)) continue;
|
|
6348
|
+
return sourceCode.text.slice(openingBracketIndex + 1, i);
|
|
6349
|
+
}
|
|
6350
|
+
return sourceCode.text.slice(openingBracketIndex + 1);
|
|
6351
|
+
}
|
|
6352
|
+
/**
|
|
6353
|
+
* Get the spaces before the closing bracket.
|
|
6354
|
+
*/
|
|
6355
|
+
function getSpaceBeforeClosingBracket(closingBracketIndex) {
|
|
6356
|
+
for (let i = closingBracketIndex - 1; i >= 0; i--) {
|
|
6357
|
+
const char = sourceCode.text[i];
|
|
6358
|
+
if (isWhitespace(char)) continue;
|
|
6359
|
+
return sourceCode.text.slice(i + 1, closingBracketIndex);
|
|
6360
|
+
}
|
|
6361
|
+
return sourceCode.text.slice(0, closingBracketIndex);
|
|
6362
|
+
}
|
|
6363
|
+
}
|
|
6364
|
+
});
|
|
6365
|
+
|
|
6366
|
+
//#endregion
|
|
6367
|
+
//#region src/rules/link-bracket-spacing.ts
|
|
6368
|
+
/**
|
|
6369
|
+
* The basic option for links and images.
|
|
6370
|
+
*/
|
|
6371
|
+
function parseOptions$2(option) {
|
|
6372
|
+
const space = option?.space ?? "never";
|
|
6373
|
+
const imagesInLinks = option?.imagesInLinks;
|
|
6374
|
+
return {
|
|
6375
|
+
space,
|
|
6376
|
+
spaceForText: getOptionForText
|
|
6377
|
+
};
|
|
6378
|
+
/**
|
|
6379
|
+
* Get the spacing option for the given node.
|
|
6380
|
+
*/
|
|
6381
|
+
function getOptionForText(node) {
|
|
6382
|
+
if (imagesInLinks != null) {
|
|
6383
|
+
const children = [...node.children];
|
|
6384
|
+
let child;
|
|
6385
|
+
while (children.length && (child = children[0]) && child.type === "text" && isWhitespace(child.value)) children.shift();
|
|
6386
|
+
while (children.length && (child = children[children.length - 1]) && child.type === "text" && isWhitespace(child.value)) children.pop();
|
|
6387
|
+
const loneChild = children.length === 1 ? children[0] : null;
|
|
6388
|
+
if (loneChild?.type === "image" || loneChild?.type === "imageReference") return imagesInLinks ? "always" : "never";
|
|
6389
|
+
}
|
|
6390
|
+
if (node.children.length === 0 || node.children.length === 1 && node.children[0]?.type === "text" && isWhitespace(node.children[0].value)) return "ignore";
|
|
6391
|
+
return space;
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
var link_bracket_spacing_default = createRule("link-bracket-spacing", {
|
|
6395
|
+
meta: {
|
|
6396
|
+
type: "layout",
|
|
6397
|
+
docs: {
|
|
6398
|
+
description: "enforce consistent spacing inside link brackets",
|
|
6399
|
+
categories: ["standard"],
|
|
6400
|
+
listCategory: "Whitespace"
|
|
6401
|
+
},
|
|
6402
|
+
fixable: "whitespace",
|
|
6403
|
+
hasSuggestions: false,
|
|
6404
|
+
schema: [{
|
|
6405
|
+
type: "object",
|
|
6406
|
+
properties: {
|
|
6407
|
+
space: { enum: ["always", "never"] },
|
|
6408
|
+
imagesInLinks: { type: "boolean" }
|
|
6409
|
+
},
|
|
6410
|
+
additionalProperties: false
|
|
6411
|
+
}],
|
|
6412
|
+
messages: {
|
|
6413
|
+
unexpectedSpaceAfterOpeningBracket: "Unexpected space after opening bracket.",
|
|
6414
|
+
expectedSpaceAfterOpeningBracket: "Expected space after opening bracket.",
|
|
6415
|
+
unexpectedSpaceBeforeClosingBracket: "Unexpected space before closing bracket.",
|
|
6416
|
+
expectedSpaceBeforeClosingBracket: "Expected space before closing bracket."
|
|
6417
|
+
}
|
|
6418
|
+
},
|
|
6419
|
+
create(context) {
|
|
6420
|
+
const sourceCode = context.sourceCode;
|
|
6421
|
+
const options = parseOptions$2(context.options[0]);
|
|
6422
|
+
/**
|
|
6423
|
+
* Verify the space after the opening bracket and before the closing bracket.
|
|
6424
|
+
*/
|
|
6425
|
+
function verifySpaceAfterOpeningBracket(node, openingBracketIndex, spaceOption) {
|
|
6426
|
+
const space = getSpaceAfterOpeningBracket(openingBracketIndex);
|
|
6427
|
+
if (space.includes("\n")) return;
|
|
6428
|
+
if (spaceOption === "always") {
|
|
6429
|
+
if (space.length > 0) return;
|
|
6430
|
+
context.report({
|
|
6431
|
+
node,
|
|
5395
6432
|
loc: getSourceLocationFromRange(sourceCode, node, [openingBracketIndex, openingBracketIndex + 1]),
|
|
5396
6433
|
messageId: "expectedSpaceAfterOpeningBracket",
|
|
5397
6434
|
fix: (fixer) => fixer.insertTextAfterRange([openingBracketIndex, openingBracketIndex + 1], " ")
|
|
@@ -5524,7 +6561,7 @@ var link_destination_style_default = createRule("link-destination-style", {
|
|
|
5524
6561
|
docs: {
|
|
5525
6562
|
description: "enforce a consistent style for link destinations",
|
|
5526
6563
|
categories: ["standard"],
|
|
5527
|
-
listCategory: "
|
|
6564
|
+
listCategory: "Notation"
|
|
5528
6565
|
},
|
|
5529
6566
|
fixable: "code",
|
|
5530
6567
|
hasSuggestions: false,
|
|
@@ -5649,7 +6686,7 @@ var link_paren_newline_default = createRule("link-paren-newline", {
|
|
|
5649
6686
|
docs: {
|
|
5650
6687
|
description: "enforce linebreaks after opening and before closing link parentheses",
|
|
5651
6688
|
categories: ["standard"],
|
|
5652
|
-
listCategory: "
|
|
6689
|
+
listCategory: "Whitespace"
|
|
5653
6690
|
},
|
|
5654
6691
|
fixable: "whitespace",
|
|
5655
6692
|
hasSuggestions: false,
|
|
@@ -5674,11 +6711,11 @@ var link_paren_newline_default = createRule("link-paren-newline", {
|
|
|
5674
6711
|
},
|
|
5675
6712
|
create(context) {
|
|
5676
6713
|
const sourceCode = context.sourceCode;
|
|
5677
|
-
const optionProvider = parseOptions$
|
|
6714
|
+
const optionProvider = parseOptions$5(context.options[0]);
|
|
5678
6715
|
/**
|
|
5679
6716
|
* Parse the options.
|
|
5680
6717
|
*/
|
|
5681
|
-
function parseOptions$
|
|
6718
|
+
function parseOptions$5(option) {
|
|
5682
6719
|
const newline = option?.newline ?? "never";
|
|
5683
6720
|
const multiline = option?.multiline ?? false;
|
|
5684
6721
|
return (openingParenIndex, closingParenIndex) => {
|
|
@@ -5793,7 +6830,7 @@ var link_paren_spacing_default = createRule("link-paren-spacing", {
|
|
|
5793
6830
|
docs: {
|
|
5794
6831
|
description: "enforce consistent spacing inside link parentheses",
|
|
5795
6832
|
categories: ["standard"],
|
|
5796
|
-
listCategory: "
|
|
6833
|
+
listCategory: "Whitespace"
|
|
5797
6834
|
},
|
|
5798
6835
|
fixable: "whitespace",
|
|
5799
6836
|
hasSuggestions: false,
|
|
@@ -5928,7 +6965,7 @@ var link_title_style_default = createRule("link-title-style", {
|
|
|
5928
6965
|
docs: {
|
|
5929
6966
|
description: "enforce a consistent style for link titles",
|
|
5930
6967
|
categories: ["standard"],
|
|
5931
|
-
listCategory: "
|
|
6968
|
+
listCategory: "Notation"
|
|
5932
6969
|
},
|
|
5933
6970
|
fixable: "code",
|
|
5934
6971
|
hasSuggestions: false,
|
|
@@ -6010,7 +7047,7 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
|
6010
7047
|
docs: {
|
|
6011
7048
|
description: "enforce consistent alignment of list markers",
|
|
6012
7049
|
categories: ["recommended", "standard"],
|
|
6013
|
-
listCategory: "
|
|
7050
|
+
listCategory: "Whitespace"
|
|
6014
7051
|
},
|
|
6015
7052
|
fixable: "whitespace",
|
|
6016
7053
|
hasSuggestions: false,
|
|
@@ -6044,10 +7081,22 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
|
6044
7081
|
const items = listNode.children;
|
|
6045
7082
|
if (items.length <= 1) return;
|
|
6046
7083
|
const referenceMarkerLocation = getMarkerLocation(items[0]);
|
|
7084
|
+
const expectedWidth = getWidth(sourceCode.lines[referenceMarkerLocation.line - 1].slice(0, referenceMarkerLocation[alignPositionName]));
|
|
6047
7085
|
for (const item of items.slice(1)) {
|
|
6048
7086
|
const markerLocation = getMarkerLocation(item);
|
|
6049
|
-
const
|
|
7087
|
+
const actualWidth = getWidth(sourceCode.lines[markerLocation.line - 1].slice(0, markerLocation[alignPositionName]));
|
|
7088
|
+
const diff = actualWidth - expectedWidth;
|
|
6050
7089
|
if (diff === 0) continue;
|
|
7090
|
+
const messageData = alignPositionName === "start" ? {
|
|
7091
|
+
expected: String(expectedWidth),
|
|
7092
|
+
actual: String(actualWidth)
|
|
7093
|
+
} : (() => {
|
|
7094
|
+
const start = getWidth(sourceCode.lines[markerLocation.line - 1].slice(0, markerLocation.start));
|
|
7095
|
+
return {
|
|
7096
|
+
expected: String(start - diff),
|
|
7097
|
+
actual: String(start)
|
|
7098
|
+
};
|
|
7099
|
+
})();
|
|
6051
7100
|
context.report({
|
|
6052
7101
|
node: item,
|
|
6053
7102
|
loc: {
|
|
@@ -6061,10 +7110,7 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
|
6061
7110
|
}
|
|
6062
7111
|
},
|
|
6063
7112
|
messageId: "incorrectAlignment",
|
|
6064
|
-
data:
|
|
6065
|
-
expected: String(markerLocation.start - diff),
|
|
6066
|
-
actual: String(markerLocation.start)
|
|
6067
|
-
},
|
|
7113
|
+
data: messageData,
|
|
6068
7114
|
fix(fixer) {
|
|
6069
7115
|
const lines = getParsedLines(sourceCode);
|
|
6070
7116
|
const line = lines.get(markerLocation.line);
|
|
@@ -6072,13 +7118,17 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
|
6072
7118
|
const addSpaces = " ".repeat(-diff);
|
|
6073
7119
|
return fixer.insertTextBeforeRange([line.range[0] + markerLocation.start, line.range[0] + markerLocation.start], addSpaces);
|
|
6074
7120
|
}
|
|
6075
|
-
const
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
const
|
|
6080
|
-
|
|
7121
|
+
const beforeItemMarker = line.text.slice(0, markerLocation.start);
|
|
7122
|
+
const newWidth = getWidth(beforeItemMarker) - diff;
|
|
7123
|
+
let newBeforeItemMarker = beforeItemMarker;
|
|
7124
|
+
while (getWidth(newBeforeItemMarker) > newWidth) {
|
|
7125
|
+
const last = newBeforeItemMarker.at(-1);
|
|
7126
|
+
if (last && isWhitespace(last)) newBeforeItemMarker = newBeforeItemMarker.slice(0, -1);
|
|
7127
|
+
else return null;
|
|
6081
7128
|
}
|
|
7129
|
+
if (getWidth(newBeforeItemMarker) < newWidth) newBeforeItemMarker += " ".repeat(newWidth - getWidth(newBeforeItemMarker));
|
|
7130
|
+
const referenceBeforeItemMarker = lines.get(referenceMarkerLocation.line).text.slice(0, referenceMarkerLocation.start);
|
|
7131
|
+
if (!referenceBeforeItemMarker.includes(">") || referenceBeforeItemMarker === newBeforeItemMarker) return fixer.replaceTextRange([line.range[0], line.range[0] + markerLocation.start], newBeforeItemMarker);
|
|
6082
7132
|
return null;
|
|
6083
7133
|
}
|
|
6084
7134
|
});
|
|
@@ -6096,7 +7146,7 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
|
|
|
6096
7146
|
docs: {
|
|
6097
7147
|
description: "disallow laziness in blockquotes",
|
|
6098
7148
|
categories: ["recommended", "standard"],
|
|
6099
|
-
listCategory: "
|
|
7149
|
+
listCategory: "Decorative"
|
|
6100
7150
|
},
|
|
6101
7151
|
fixable: void 0,
|
|
6102
7152
|
hasSuggestions: true,
|
|
@@ -6193,7 +7243,194 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
|
|
|
6193
7243
|
reportInvalidLines(invalidLines, base);
|
|
6194
7244
|
} };
|
|
6195
7245
|
}
|
|
6196
|
-
});
|
|
7246
|
+
});
|
|
7247
|
+
|
|
7248
|
+
//#endregion
|
|
7249
|
+
//#region src/utils/table.ts
|
|
7250
|
+
/**
|
|
7251
|
+
* Parse the table.
|
|
7252
|
+
*/
|
|
7253
|
+
function parseTable(sourceCode, node) {
|
|
7254
|
+
const headerRow = parseTableRow(sourceCode, node.children[0]);
|
|
7255
|
+
if (!headerRow) return null;
|
|
7256
|
+
const delimiterRow = parseTableDelimiterRow(sourceCode, node);
|
|
7257
|
+
if (!delimiterRow) return null;
|
|
7258
|
+
const bodyRows = [];
|
|
7259
|
+
for (const child of node.children.slice(1)) {
|
|
7260
|
+
const bodyRow = parseTableRow(sourceCode, child);
|
|
7261
|
+
if (!bodyRow) return null;
|
|
7262
|
+
bodyRows.push(bodyRow);
|
|
7263
|
+
}
|
|
7264
|
+
return {
|
|
7265
|
+
headerRow,
|
|
7266
|
+
delimiterRow,
|
|
7267
|
+
bodyRows
|
|
7268
|
+
};
|
|
7269
|
+
}
|
|
7270
|
+
/**
|
|
7271
|
+
* Parse the table delimiter row.
|
|
7272
|
+
*/
|
|
7273
|
+
function parseTableDelimiterRow(sourceCode, node) {
|
|
7274
|
+
const headerRow = node.children[0];
|
|
7275
|
+
const headerRange = sourceCode.getRange(headerRow);
|
|
7276
|
+
const delimiterEndIndex = node.children.length > 1 ? sourceCode.getRange(node.children[1])[0] : sourceCode.getRange(node)[1];
|
|
7277
|
+
const delimiterText = sourceCode.text.slice(headerRange[1], delimiterEndIndex);
|
|
7278
|
+
const parsed = parseTableDelimiterRowFromText(delimiterText);
|
|
7279
|
+
if (!parsed) return null;
|
|
7280
|
+
const delimiters = parsed.delimiters.map((d) => {
|
|
7281
|
+
let leadingPipe = null;
|
|
7282
|
+
if (d.leadingPipe) {
|
|
7283
|
+
const leadingPipeRange = [headerRange[1] + d.leadingPipe.range[0], headerRange[1] + d.leadingPipe.range[1]];
|
|
7284
|
+
leadingPipe = {
|
|
7285
|
+
text: d.leadingPipe.text,
|
|
7286
|
+
range: leadingPipeRange,
|
|
7287
|
+
loc: getSourceLocationFromRange(sourceCode, headerRow, leadingPipeRange)
|
|
7288
|
+
};
|
|
7289
|
+
}
|
|
7290
|
+
const delimiterRange = [headerRange[1] + d.delimiter.range[0], headerRange[1] + d.delimiter.range[1]];
|
|
7291
|
+
return {
|
|
7292
|
+
leadingPipe,
|
|
7293
|
+
delimiter: {
|
|
7294
|
+
text: d.delimiter.text,
|
|
7295
|
+
align: d.delimiter.text.startsWith(":") ? d.delimiter.text.endsWith(":") ? "center" : "left" : d.delimiter.text.endsWith(":") ? "right" : "none",
|
|
7296
|
+
range: delimiterRange,
|
|
7297
|
+
loc: getSourceLocationFromRange(sourceCode, headerRow, delimiterRange)
|
|
7298
|
+
}
|
|
7299
|
+
};
|
|
7300
|
+
});
|
|
7301
|
+
let trailingPipe = null;
|
|
7302
|
+
if (parsed.trailingPipe) {
|
|
7303
|
+
const trailingPipeRange = [headerRange[1] + parsed.trailingPipe.range[0], headerRange[1] + parsed.trailingPipe.range[1]];
|
|
7304
|
+
trailingPipe = {
|
|
7305
|
+
text: parsed.trailingPipe.text,
|
|
7306
|
+
range: trailingPipeRange,
|
|
7307
|
+
loc: getSourceLocationFromRange(sourceCode, headerRow, trailingPipeRange)
|
|
7308
|
+
};
|
|
7309
|
+
}
|
|
7310
|
+
const firstToken = delimiters[0].leadingPipe ?? delimiters[0].delimiter;
|
|
7311
|
+
const lastToken = trailingPipe ?? delimiters[delimiters.length - 1].delimiter;
|
|
7312
|
+
return {
|
|
7313
|
+
delimiters,
|
|
7314
|
+
trailingPipe,
|
|
7315
|
+
range: [firstToken.range[0], lastToken.range[1]],
|
|
7316
|
+
loc: {
|
|
7317
|
+
start: firstToken.loc.start,
|
|
7318
|
+
end: lastToken.loc.end
|
|
7319
|
+
}
|
|
7320
|
+
};
|
|
7321
|
+
}
|
|
7322
|
+
/**
|
|
7323
|
+
* Parse the table row.
|
|
7324
|
+
*/
|
|
7325
|
+
function parseTableRow(sourceCode, node) {
|
|
7326
|
+
const cells = [];
|
|
7327
|
+
let trailingPipe = null;
|
|
7328
|
+
for (const cell of node.children) {
|
|
7329
|
+
const cellRange = sourceCode.getRange(cell);
|
|
7330
|
+
const cellLoc = sourceCode.getLoc(cell);
|
|
7331
|
+
const leadingPipe = sourceCode.text[cellRange[0]] === "|" ? {
|
|
7332
|
+
text: "|",
|
|
7333
|
+
range: [cellRange[0], cellRange[0] + 1],
|
|
7334
|
+
loc: {
|
|
7335
|
+
start: cellLoc.start,
|
|
7336
|
+
end: {
|
|
7337
|
+
line: cellLoc.start.line,
|
|
7338
|
+
column: cellLoc.start.column + 1
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
} : null;
|
|
7342
|
+
if (trailingPipe && leadingPipe) return null;
|
|
7343
|
+
let parsedCell = null;
|
|
7344
|
+
if (cell.children.length > 0) {
|
|
7345
|
+
const firstChild = cell.children[0];
|
|
7346
|
+
const lastChild = cell.children[cell.children.length - 1];
|
|
7347
|
+
parsedCell = {
|
|
7348
|
+
range: [sourceCode.getRange(firstChild)[0], sourceCode.getRange(lastChild)[1]],
|
|
7349
|
+
loc: {
|
|
7350
|
+
start: sourceCode.getLoc(firstChild).start,
|
|
7351
|
+
end: sourceCode.getLoc(lastChild).end
|
|
7352
|
+
}
|
|
7353
|
+
};
|
|
7354
|
+
}
|
|
7355
|
+
cells.push({
|
|
7356
|
+
leadingPipe,
|
|
7357
|
+
cell: parsedCell
|
|
7358
|
+
});
|
|
7359
|
+
trailingPipe = sourceCode.text[cellRange[1] - 1] === "|" ? {
|
|
7360
|
+
text: "|",
|
|
7361
|
+
range: [cellRange[1] - 1, cellRange[1]],
|
|
7362
|
+
loc: {
|
|
7363
|
+
start: {
|
|
7364
|
+
line: cellLoc.end.line,
|
|
7365
|
+
column: cellLoc.end.column - 1
|
|
7366
|
+
},
|
|
7367
|
+
end: cellLoc.end
|
|
7368
|
+
}
|
|
7369
|
+
} : null;
|
|
7370
|
+
}
|
|
7371
|
+
const firstToken = cells[0].leadingPipe ?? cells[0].cell;
|
|
7372
|
+
const lastToken = trailingPipe ?? cells[cells.length - 1].cell ?? cells[cells.length - 1].leadingPipe;
|
|
7373
|
+
return {
|
|
7374
|
+
cells,
|
|
7375
|
+
trailingPipe,
|
|
7376
|
+
range: [firstToken.range[0], lastToken.range[1]],
|
|
7377
|
+
loc: {
|
|
7378
|
+
start: firstToken.loc.start,
|
|
7379
|
+
end: lastToken.loc.end
|
|
7380
|
+
}
|
|
7381
|
+
};
|
|
7382
|
+
}
|
|
7383
|
+
/**
|
|
7384
|
+
* Parse the table delimiter row from the text.
|
|
7385
|
+
*/
|
|
7386
|
+
function parseTableDelimiterRowFromText(text) {
|
|
7387
|
+
const cursor = new ForwardCharacterCursor(text);
|
|
7388
|
+
cursor.skipSpaces();
|
|
7389
|
+
while (cursor.curr() === ">") {
|
|
7390
|
+
cursor.next();
|
|
7391
|
+
cursor.skipSpaces();
|
|
7392
|
+
}
|
|
7393
|
+
const delimiters = [];
|
|
7394
|
+
let pipe = consumePipe();
|
|
7395
|
+
while (!cursor.finished()) {
|
|
7396
|
+
const delimiterStart = cursor.currIndex();
|
|
7397
|
+
cursor.skipUntilEnd((c) => c === "|" || isSpaceOrTab(c) || c === "\n" || c === "\r");
|
|
7398
|
+
const delimiterRange = [delimiterStart, cursor.currIndex()];
|
|
7399
|
+
const delimiterText = text.slice(...delimiterRange);
|
|
7400
|
+
if (!/^:?-+:?$/u.test(delimiterText)) return null;
|
|
7401
|
+
if (delimiters.length > 0 && pipe == null) return null;
|
|
7402
|
+
delimiters.push({
|
|
7403
|
+
leadingPipe: pipe,
|
|
7404
|
+
delimiter: {
|
|
7405
|
+
text: delimiterText,
|
|
7406
|
+
range: delimiterRange
|
|
7407
|
+
}
|
|
7408
|
+
});
|
|
7409
|
+
pipe = consumePipe();
|
|
7410
|
+
}
|
|
7411
|
+
return {
|
|
7412
|
+
delimiters,
|
|
7413
|
+
trailingPipe: pipe
|
|
7414
|
+
};
|
|
7415
|
+
/**
|
|
7416
|
+
* Consume a pipe if exists.
|
|
7417
|
+
*/
|
|
7418
|
+
function consumePipe() {
|
|
7419
|
+
cursor.skipSpaces();
|
|
7420
|
+
if (cursor.curr() === "|") {
|
|
7421
|
+
const pipeStart = cursor.currIndex();
|
|
7422
|
+
cursor.next();
|
|
7423
|
+
const pipeRange = [pipeStart, cursor.currIndex()];
|
|
7424
|
+
const result = {
|
|
7425
|
+
text: text.slice(...pipeRange),
|
|
7426
|
+
range: pipeRange
|
|
7427
|
+
};
|
|
7428
|
+
cursor.skipSpaces();
|
|
7429
|
+
return result;
|
|
7430
|
+
}
|
|
7431
|
+
return null;
|
|
7432
|
+
}
|
|
7433
|
+
}
|
|
6197
7434
|
|
|
6198
7435
|
//#endregion
|
|
6199
7436
|
//#region src/rules/no-multi-spaces.ts
|
|
@@ -6203,7 +7440,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6203
7440
|
docs: {
|
|
6204
7441
|
description: "disallow multiple spaces",
|
|
6205
7442
|
categories: ["standard"],
|
|
6206
|
-
listCategory: "
|
|
7443
|
+
listCategory: "Whitespace"
|
|
6207
7444
|
},
|
|
6208
7445
|
fixable: "whitespace",
|
|
6209
7446
|
hasSuggestions: false,
|
|
@@ -6212,6 +7449,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6212
7449
|
},
|
|
6213
7450
|
create(context) {
|
|
6214
7451
|
const sourceCode = context.sourceCode;
|
|
7452
|
+
let codeText = sourceCode.text;
|
|
6215
7453
|
return {
|
|
6216
7454
|
definition: verifyLinkDefinition,
|
|
6217
7455
|
footnoteDefinition: verifyFootnoteDefinition,
|
|
@@ -6221,7 +7459,9 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6221
7459
|
link: verifyLink,
|
|
6222
7460
|
linkReference: verifyLinkReference,
|
|
6223
7461
|
listItem: verifyListItem,
|
|
6224
|
-
|
|
7462
|
+
blockquote: processBlockquote,
|
|
7463
|
+
text: verifyText,
|
|
7464
|
+
table: verifyTable
|
|
6225
7465
|
};
|
|
6226
7466
|
/**
|
|
6227
7467
|
* Verify a text node.
|
|
@@ -6230,6 +7470,14 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6230
7470
|
verifyTextInNode(node);
|
|
6231
7471
|
}
|
|
6232
7472
|
/**
|
|
7473
|
+
* Verify a table node.
|
|
7474
|
+
*/
|
|
7475
|
+
function verifyTable(node) {
|
|
7476
|
+
const parsedDelimiterRow = parseTableDelimiterRow(sourceCode, node);
|
|
7477
|
+
if (!parsedDelimiterRow) return;
|
|
7478
|
+
verifyTextInRange(node, parsedDelimiterRow.range);
|
|
7479
|
+
}
|
|
7480
|
+
/**
|
|
6233
7481
|
* Verify a definition node.
|
|
6234
7482
|
*/
|
|
6235
7483
|
function verifyLinkDefinition(node) {
|
|
@@ -6294,7 +7542,13 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6294
7542
|
* Verify a list item node
|
|
6295
7543
|
*/
|
|
6296
7544
|
function verifyListItem(node) {
|
|
6297
|
-
|
|
7545
|
+
const nodeRange = sourceCode.getRange(node);
|
|
7546
|
+
const parsed = parseListItem(sourceCode, node);
|
|
7547
|
+
if (parsed.taskListItemMarker) verifyTextInRange(node, [nodeRange[0], parsed.taskListItemMarker.range[0]]);
|
|
7548
|
+
let newCodeText = codeText.slice(0, parsed.marker.range[0]) + " ".repeat(parsed.marker.range[1] - parsed.marker.range[0]);
|
|
7549
|
+
if (parsed.taskListItemMarker) newCodeText += codeText.slice(parsed.marker.range[1], parsed.taskListItemMarker.range[0]) + " ".repeat(parsed.taskListItemMarker.range[1] - parsed.taskListItemMarker.range[0]) + codeText.slice(parsed.taskListItemMarker.range[1]);
|
|
7550
|
+
else newCodeText += codeText.slice(parsed.marker.range[1]);
|
|
7551
|
+
codeText = newCodeText;
|
|
6298
7552
|
}
|
|
6299
7553
|
/**
|
|
6300
7554
|
* Verify spaces in a node
|
|
@@ -6304,6 +7558,26 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6304
7558
|
verifyTextInRange(node, nodeRange);
|
|
6305
7559
|
}
|
|
6306
7560
|
/**
|
|
7561
|
+
* Process a blockquote node
|
|
7562
|
+
*/
|
|
7563
|
+
function processBlockquote(node) {
|
|
7564
|
+
const nodeRange = sourceCode.getRange(node);
|
|
7565
|
+
let newCodeText = "";
|
|
7566
|
+
let inIndent = true;
|
|
7567
|
+
for (let index = nodeRange[0]; index < nodeRange[1]; index++) {
|
|
7568
|
+
const c = codeText[index];
|
|
7569
|
+
if (c === "\n") {
|
|
7570
|
+
inIndent = true;
|
|
7571
|
+
continue;
|
|
7572
|
+
}
|
|
7573
|
+
if (isWhitespace(c)) continue;
|
|
7574
|
+
if (c === ">" && inIndent) newCodeText += `${codeText.slice(newCodeText.length, index)} `;
|
|
7575
|
+
inIndent = false;
|
|
7576
|
+
}
|
|
7577
|
+
newCodeText += codeText.slice(newCodeText.length);
|
|
7578
|
+
codeText = newCodeText;
|
|
7579
|
+
}
|
|
7580
|
+
/**
|
|
6307
7581
|
* Verify spaces in a node excluding children
|
|
6308
7582
|
*/
|
|
6309
7583
|
function verifyTextOutsideChildren(node) {
|
|
@@ -6322,14 +7596,14 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6322
7596
|
*/
|
|
6323
7597
|
function verifyTextInRange(node, textRange) {
|
|
6324
7598
|
const nodeRange = sourceCode.getRange(node);
|
|
6325
|
-
const text =
|
|
7599
|
+
const text = codeText.slice(...textRange);
|
|
6326
7600
|
const reSpaces = /\s{2,}|\n/gu;
|
|
6327
7601
|
let match;
|
|
6328
7602
|
while ((match = reSpaces.exec(text)) !== null) {
|
|
6329
7603
|
const spaces = match[0];
|
|
6330
7604
|
if (spaces.includes("\n")) {
|
|
6331
7605
|
let c = "";
|
|
6332
|
-
while ((c = text[reSpaces.lastIndex]) && (c
|
|
7606
|
+
while ((c = text[reSpaces.lastIndex]) && isWhitespace(c)) reSpaces.lastIndex++;
|
|
6333
7607
|
continue;
|
|
6334
7608
|
}
|
|
6335
7609
|
if (spaces.length < 2) continue;
|
|
@@ -6339,7 +7613,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6339
7613
|
if (nodeRange[0] === range[0]) {
|
|
6340
7614
|
let isIndentation = true;
|
|
6341
7615
|
for (let index = nodeRange[0] - 1; index >= 0; index--) {
|
|
6342
|
-
const c =
|
|
7616
|
+
const c = codeText[index];
|
|
6343
7617
|
if (c === "\n") break;
|
|
6344
7618
|
if (isWhitespace(c)) continue;
|
|
6345
7619
|
isIndentation = false;
|
|
@@ -6349,8 +7623,8 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
6349
7623
|
}
|
|
6350
7624
|
if (nodeRange[1] === range[1]) {
|
|
6351
7625
|
let isTrailingSpaces = true;
|
|
6352
|
-
for (let index = nodeRange[1]; index <
|
|
6353
|
-
const c =
|
|
7626
|
+
for (let index = nodeRange[1]; index < codeText.length; index++) {
|
|
7627
|
+
const c = codeText[index];
|
|
6354
7628
|
if (c === "\n") break;
|
|
6355
7629
|
if (isWhitespace(c)) continue;
|
|
6356
7630
|
isTrailingSpaces = false;
|
|
@@ -6379,7 +7653,7 @@ var no_multiple_empty_lines_default = createRule("no-multiple-empty-lines", {
|
|
|
6379
7653
|
docs: {
|
|
6380
7654
|
description: "disallow multiple empty lines in Markdown files.",
|
|
6381
7655
|
categories: ["standard"],
|
|
6382
|
-
listCategory: "
|
|
7656
|
+
listCategory: "Whitespace"
|
|
6383
7657
|
},
|
|
6384
7658
|
fixable: "whitespace",
|
|
6385
7659
|
hasSuggestions: false,
|
|
@@ -6538,7 +7812,7 @@ var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebrea
|
|
|
6538
7812
|
docs: {
|
|
6539
7813
|
description: "disallow text backslash at the end of a line.",
|
|
6540
7814
|
categories: ["recommended", "standard"],
|
|
6541
|
-
listCategory: "
|
|
7815
|
+
listCategory: "Notation"
|
|
6542
7816
|
},
|
|
6543
7817
|
fixable: void 0,
|
|
6544
7818
|
hasSuggestions: true,
|
|
@@ -6583,7 +7857,7 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
|
|
|
6583
7857
|
docs: {
|
|
6584
7858
|
description: "disallow trailing whitespace at the end of lines in Markdown files.",
|
|
6585
7859
|
categories: ["standard"],
|
|
6586
|
-
listCategory: "
|
|
7860
|
+
listCategory: "Whitespace"
|
|
6587
7861
|
},
|
|
6588
7862
|
fixable: "whitespace",
|
|
6589
7863
|
hasSuggestions: false,
|
|
@@ -6709,7 +7983,7 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
|
|
|
6709
7983
|
docs: {
|
|
6710
7984
|
description: "enforce that ordered list markers use sequential numbers",
|
|
6711
7985
|
categories: ["standard"],
|
|
6712
|
-
listCategory: "
|
|
7986
|
+
listCategory: "Decorative"
|
|
6713
7987
|
},
|
|
6714
7988
|
fixable: "code",
|
|
6715
7989
|
hasSuggestions: true,
|
|
@@ -6923,7 +8197,7 @@ function markerToKind(marker) {
|
|
|
6923
8197
|
/**
|
|
6924
8198
|
* Parse rule options.
|
|
6925
8199
|
*/
|
|
6926
|
-
function parseOptions(options) {
|
|
8200
|
+
function parseOptions$1(options) {
|
|
6927
8201
|
const prefer = markerToKind(options.prefer) || ".";
|
|
6928
8202
|
const overrides = (options.overrides ?? []).map((override) => {
|
|
6929
8203
|
const preferForOverride = markerToKind(override.prefer) || ".";
|
|
@@ -6950,7 +8224,7 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
|
|
|
6950
8224
|
docs: {
|
|
6951
8225
|
description: "enforce consistent ordered list marker style",
|
|
6952
8226
|
categories: ["standard"],
|
|
6953
|
-
listCategory: "
|
|
8227
|
+
listCategory: "Notation"
|
|
6954
8228
|
},
|
|
6955
8229
|
fixable: "code",
|
|
6956
8230
|
hasSuggestions: false,
|
|
@@ -6984,7 +8258,7 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
|
|
|
6984
8258
|
},
|
|
6985
8259
|
create(context) {
|
|
6986
8260
|
const sourceCode = context.sourceCode;
|
|
6987
|
-
const options = parseOptions(context.options[0] || {});
|
|
8261
|
+
const options = parseOptions$1(context.options[0] || {});
|
|
6988
8262
|
let containerStack = {
|
|
6989
8263
|
node: sourceCode.ast,
|
|
6990
8264
|
level: 1,
|
|
@@ -7169,7 +8443,7 @@ var padding_line_between_blocks_default = createRule("padding-line-between-block
|
|
|
7169
8443
|
docs: {
|
|
7170
8444
|
description: "require or disallow padding lines between blocks",
|
|
7171
8445
|
categories: ["standard"],
|
|
7172
|
-
listCategory: "
|
|
8446
|
+
listCategory: "Whitespace"
|
|
7173
8447
|
},
|
|
7174
8448
|
fixable: "whitespace",
|
|
7175
8449
|
hasSuggestions: false,
|
|
@@ -7367,7 +8641,7 @@ var prefer_autolinks_default = createRule("prefer-autolinks", {
|
|
|
7367
8641
|
docs: {
|
|
7368
8642
|
description: "enforce the use of autolinks for URLs",
|
|
7369
8643
|
categories: ["recommended", "standard"],
|
|
7370
|
-
listCategory: "
|
|
8644
|
+
listCategory: "Notation"
|
|
7371
8645
|
},
|
|
7372
8646
|
fixable: "code",
|
|
7373
8647
|
hasSuggestions: false,
|
|
@@ -7405,7 +8679,7 @@ var prefer_fenced_code_blocks_default = createRule("prefer-fenced-code-blocks",
|
|
|
7405
8679
|
docs: {
|
|
7406
8680
|
description: "enforce the use of fenced code blocks over indented code blocks",
|
|
7407
8681
|
categories: ["recommended", "standard"],
|
|
7408
|
-
listCategory: "
|
|
8682
|
+
listCategory: "Notation"
|
|
7409
8683
|
},
|
|
7410
8684
|
fixable: "code",
|
|
7411
8685
|
hasSuggestions: false,
|
|
@@ -7635,7 +8909,7 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
|
|
|
7635
8909
|
docs: {
|
|
7636
8910
|
description: "enforce using link reference definitions instead of inline links",
|
|
7637
8911
|
categories: [],
|
|
7638
|
-
listCategory: "
|
|
8912
|
+
listCategory: "Notation"
|
|
7639
8913
|
},
|
|
7640
8914
|
fixable: "code",
|
|
7641
8915
|
hasSuggestions: false,
|
|
@@ -7914,9 +9188,9 @@ var setext_heading_underline_length_default = createRule("setext-heading-underli
|
|
|
7914
9188
|
docs: {
|
|
7915
9189
|
description: "enforce setext heading underline length",
|
|
7916
9190
|
categories: ["standard"],
|
|
7917
|
-
listCategory: "
|
|
9191
|
+
listCategory: "Decorative"
|
|
7918
9192
|
},
|
|
7919
|
-
fixable: "
|
|
9193
|
+
fixable: "code",
|
|
7920
9194
|
schema: [{
|
|
7921
9195
|
type: "object",
|
|
7922
9196
|
properties: {
|
|
@@ -8152,7 +9426,7 @@ var sort_definitions_default = createRule("sort-definitions", {
|
|
|
8152
9426
|
docs: {
|
|
8153
9427
|
description: "enforce a specific order for link definitions and footnote definitions",
|
|
8154
9428
|
categories: ["standard"],
|
|
8155
|
-
listCategory: "
|
|
9429
|
+
listCategory: "Decorative"
|
|
8156
9430
|
},
|
|
8157
9431
|
fixable: "code",
|
|
8158
9432
|
hasSuggestions: false,
|
|
@@ -8425,7 +9699,7 @@ var strikethrough_delimiters_style_default = createRule("strikethrough-delimiter
|
|
|
8425
9699
|
docs: {
|
|
8426
9700
|
description: "enforce a consistent delimiter style for strikethrough",
|
|
8427
9701
|
categories: ["standard"],
|
|
8428
|
-
listCategory: "
|
|
9702
|
+
listCategory: "Notation"
|
|
8429
9703
|
},
|
|
8430
9704
|
fixable: "code",
|
|
8431
9705
|
hasSuggestions: false,
|
|
@@ -8444,151 +9718,869 @@ var strikethrough_delimiters_style_default = createRule("strikethrough-delimiter
|
|
|
8444
9718
|
const actualDelimiter = ["~~", "~"].find((d) => sourceCode.text.startsWith(d, range[0]) && sourceCode.text.endsWith(d, range[1]));
|
|
8445
9719
|
if (!actualDelimiter || actualDelimiter === delimiter) return;
|
|
8446
9720
|
context.report({
|
|
8447
|
-
node,
|
|
8448
|
-
messageId: "wrongDelimiter",
|
|
9721
|
+
node,
|
|
9722
|
+
messageId: "wrongDelimiter",
|
|
9723
|
+
data: {
|
|
9724
|
+
expected: delimiter,
|
|
9725
|
+
actual: actualDelimiter
|
|
9726
|
+
},
|
|
9727
|
+
fix(fixer) {
|
|
9728
|
+
return [fixer.replaceTextRange([range[0], range[0] + actualDelimiter.length], delimiter), fixer.replaceTextRange([range[1] - actualDelimiter.length, range[1]], delimiter)];
|
|
9729
|
+
}
|
|
9730
|
+
});
|
|
9731
|
+
} };
|
|
9732
|
+
}
|
|
9733
|
+
});
|
|
9734
|
+
|
|
9735
|
+
//#endregion
|
|
9736
|
+
//#region src/rules/table-header-casing.ts
|
|
9737
|
+
var table_header_casing_default = createRule("table-header-casing", {
|
|
9738
|
+
meta: {
|
|
9739
|
+
type: "suggestion",
|
|
9740
|
+
fixable: "code",
|
|
9741
|
+
docs: {
|
|
9742
|
+
description: "enforce consistent casing in table header cells.",
|
|
9743
|
+
categories: [],
|
|
9744
|
+
listCategory: "Preference"
|
|
9745
|
+
},
|
|
9746
|
+
schema: [{
|
|
9747
|
+
type: "object",
|
|
9748
|
+
properties: {
|
|
9749
|
+
style: { enum: ["Title Case", "Sentence case"] },
|
|
9750
|
+
preserveWords: {
|
|
9751
|
+
type: "array",
|
|
9752
|
+
items: { type: "string" },
|
|
9753
|
+
description: "Words that should be preserved as-is (case-insensitive matching)"
|
|
9754
|
+
},
|
|
9755
|
+
ignorePatterns: {
|
|
9756
|
+
type: "array",
|
|
9757
|
+
items: { type: "string" },
|
|
9758
|
+
description: "Regular expression patterns for words to ignore during casing checks"
|
|
9759
|
+
},
|
|
9760
|
+
minorWords: {
|
|
9761
|
+
type: "array",
|
|
9762
|
+
items: { type: "string" },
|
|
9763
|
+
description: "Words that should not be capitalized in Title Case (unless they're the first or last word)"
|
|
9764
|
+
}
|
|
9765
|
+
},
|
|
9766
|
+
additionalProperties: false
|
|
9767
|
+
}],
|
|
9768
|
+
messages: {
|
|
9769
|
+
expectedTitleCase: "Expected \"{{actual}}\" to be \"{{expected}}\" (Title Case).",
|
|
9770
|
+
expectedTitleCaseMinorWord: "Expected \"{{actual}}\" to be \"{{expected}}\" (Title Case - minor word).",
|
|
9771
|
+
expectedSentenceCase: "Expected \"{{actual}}\" to be \"{{expected}}\" (Sentence case).",
|
|
9772
|
+
expectedPreserveWord: "Expected \"{{actual}}\" to be \"{{expected}}\" (preserved word)."
|
|
9773
|
+
}
|
|
9774
|
+
},
|
|
9775
|
+
create(context) {
|
|
9776
|
+
const sourceCode = context.sourceCode;
|
|
9777
|
+
const caseStyle = context.options[0]?.style || "Title Case";
|
|
9778
|
+
const preserveWordsOption = parsePreserveWordsOption(context.options[0]?.preserveWords || defaultPreserveWords);
|
|
9779
|
+
const minorWords = context.options[0]?.minorWords || defaultMinorWords;
|
|
9780
|
+
const ignorePatterns = (context.options[0]?.ignorePatterns || [
|
|
9781
|
+
"/^v\\d+/u",
|
|
9782
|
+
"/\\w+\\.[a-z\\d]+$/u",
|
|
9783
|
+
"/\\w+(?:API|Api)$/u",
|
|
9784
|
+
"/\\w+(?:SDK|Sdk)$/u",
|
|
9785
|
+
"/\\w+(?:CLI|Cli)$/u"
|
|
9786
|
+
]).map((pattern) => {
|
|
9787
|
+
if (isRegExp(pattern)) return toRegExp(pattern);
|
|
9788
|
+
try {
|
|
9789
|
+
return new RegExp(pattern, "v");
|
|
9790
|
+
} catch {}
|
|
9791
|
+
try {
|
|
9792
|
+
return new RegExp(pattern, "u");
|
|
9793
|
+
} catch {}
|
|
9794
|
+
return new RegExp(pattern);
|
|
9795
|
+
});
|
|
9796
|
+
/**
|
|
9797
|
+
* Check text node and report word-level errors
|
|
9798
|
+
*/
|
|
9799
|
+
function checkTextNode(node, firstNode, lastNode) {
|
|
9800
|
+
const text = sourceCode.getText(node);
|
|
9801
|
+
const wordAndOffsets = parseWordsFromText(text, firstNode, lastNode);
|
|
9802
|
+
const processed = /* @__PURE__ */ new Set();
|
|
9803
|
+
for (let index = 0; index < wordAndOffsets.length; index++) {
|
|
9804
|
+
if (processed.has(index)) continue;
|
|
9805
|
+
processed.add(index);
|
|
9806
|
+
const wordAndOffset = wordAndOffsets[index];
|
|
9807
|
+
if (wordAndOffset.punctuation) continue;
|
|
9808
|
+
if (ignorePatterns.some((pattern) => pattern.test(wordAndOffset.word))) continue;
|
|
9809
|
+
const preservePhrase = preserveWordsOption.findPreservePhrase((function* () {
|
|
9810
|
+
const firstWord = wordAndOffsets[index];
|
|
9811
|
+
if (firstWord.punctuation) return;
|
|
9812
|
+
yield firstWord.word;
|
|
9813
|
+
for (let next = index + 1; next < wordAndOffsets.length; next++) yield wordAndOffsets[next].word;
|
|
9814
|
+
})());
|
|
9815
|
+
if (preservePhrase) {
|
|
9816
|
+
for (let wordIndex = 0; wordIndex < preservePhrase.length; wordIndex++) {
|
|
9817
|
+
processed.add(index + wordIndex);
|
|
9818
|
+
verifyWord(wordAndOffsets[index + wordIndex], preservePhrase[wordIndex], "preserved");
|
|
9819
|
+
}
|
|
9820
|
+
continue;
|
|
9821
|
+
}
|
|
9822
|
+
const preserveWordList = preserveWordsOption.findPreserveWord(wordAndOffset.word);
|
|
9823
|
+
if (preserveWordList) {
|
|
9824
|
+
if (!preserveWordList.some((w) => w === wordAndOffset.word)) verifyWord(wordAndOffset, preserveWordList[0], "preserved");
|
|
9825
|
+
continue;
|
|
9826
|
+
}
|
|
9827
|
+
const expectedWordResult = convertWordCasing(wordAndOffset, {
|
|
9828
|
+
caseStyle,
|
|
9829
|
+
minorWords
|
|
9830
|
+
});
|
|
9831
|
+
verifyWord(wordAndOffset, expectedWordResult.word, expectedWordResult.isMinorWord ? "minor" : "normal");
|
|
9832
|
+
}
|
|
9833
|
+
/**
|
|
9834
|
+
* Verify a single word against the expected casing
|
|
9835
|
+
*/
|
|
9836
|
+
function verifyWord(wordAndOffset, expectedWord, wordType = "normal") {
|
|
9837
|
+
const { word, offset } = wordAndOffset;
|
|
9838
|
+
if (word === expectedWord) return;
|
|
9839
|
+
const [nodeStart] = sourceCode.getRange(node);
|
|
9840
|
+
const range = [nodeStart + offset, nodeStart + offset + word.length];
|
|
9841
|
+
context.report({
|
|
9842
|
+
node,
|
|
9843
|
+
messageId: wordType === "preserved" ? "expectedPreserveWord" : caseStyle === "Title Case" ? wordType === "minor" ? "expectedTitleCaseMinorWord" : "expectedTitleCase" : "expectedSentenceCase",
|
|
9844
|
+
data: {
|
|
9845
|
+
actual: word,
|
|
9846
|
+
expected: expectedWord
|
|
9847
|
+
},
|
|
9848
|
+
loc: getSourceLocationFromRange(sourceCode, node, range),
|
|
9849
|
+
fix(fixer) {
|
|
9850
|
+
return fixer.replaceTextRange(range, expectedWord);
|
|
9851
|
+
}
|
|
9852
|
+
});
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9855
|
+
return { table(node) {
|
|
9856
|
+
if (!node.children.length) return;
|
|
9857
|
+
const headerRow = node.children[0];
|
|
9858
|
+
if (headerRow.type !== "tableRow") return;
|
|
9859
|
+
for (const cell of headerRow.children) {
|
|
9860
|
+
const children = cell.children.filter((child) => child.type !== "text" || child.value.trim());
|
|
9861
|
+
children.forEach((child, i) => {
|
|
9862
|
+
if (child.type === "text") checkTextNode(child, i === 0, i === children.length - 1);
|
|
9863
|
+
});
|
|
9864
|
+
}
|
|
9865
|
+
} };
|
|
9866
|
+
}
|
|
9867
|
+
});
|
|
9868
|
+
|
|
9869
|
+
//#endregion
|
|
9870
|
+
//#region src/rules/table-leading-trailing-pipes.ts
|
|
9871
|
+
var table_leading_trailing_pipes_default = createRule("table-leading-trailing-pipes", {
|
|
9872
|
+
meta: {
|
|
9873
|
+
type: "layout",
|
|
9874
|
+
docs: {
|
|
9875
|
+
description: "enforce consistent use of leading and trailing pipes in tables.",
|
|
9876
|
+
categories: ["standard"],
|
|
9877
|
+
listCategory: "Decorative"
|
|
9878
|
+
},
|
|
9879
|
+
fixable: "code",
|
|
9880
|
+
hasSuggestions: false,
|
|
9881
|
+
schema: [{ anyOf: [{ enum: ["always", "never"] }, {
|
|
9882
|
+
type: "object",
|
|
9883
|
+
properties: {
|
|
9884
|
+
leading: { enum: ["always", "never"] },
|
|
9885
|
+
trailing: { enum: ["always", "never"] }
|
|
9886
|
+
},
|
|
9887
|
+
additionalProperties: false
|
|
9888
|
+
}] }],
|
|
9889
|
+
messages: {
|
|
9890
|
+
missingLeadingPipe: "Table line should start with a leading pipe.",
|
|
9891
|
+
unexpectedLeadingPipe: "Table line should not start with a leading pipe.",
|
|
9892
|
+
missingTrailingPipe: "Table line should end with a trailing pipe.",
|
|
9893
|
+
unexpectedTrailingPipe: "Table line should not end with a trailing pipe."
|
|
9894
|
+
}
|
|
9895
|
+
},
|
|
9896
|
+
create(context) {
|
|
9897
|
+
const sourceCode = context.sourceCode;
|
|
9898
|
+
const preferOption = context.options[0] ?? "always";
|
|
9899
|
+
const leadingOption = typeof preferOption === "string" ? preferOption : preferOption.leading ?? "always";
|
|
9900
|
+
const trailingOption = typeof preferOption === "string" ? preferOption : preferOption.trailing ?? "always";
|
|
9901
|
+
/**
|
|
9902
|
+
* Verify the table pipes
|
|
9903
|
+
*/
|
|
9904
|
+
function verifyTablePipes(node) {
|
|
9905
|
+
for (const row of node.children) verifyTableRowPipes(row);
|
|
9906
|
+
const parsedDelimiterRow = parseTableDelimiterRow(sourceCode, node);
|
|
9907
|
+
if (parsedDelimiterRow) verifyTableLinePipes(parsedDelimiterRow.range, parsedDelimiterRow.loc, parsedDelimiterRow.delimiters.length);
|
|
9908
|
+
}
|
|
9909
|
+
/**
|
|
9910
|
+
* Verify the table row pipes
|
|
9911
|
+
*/
|
|
9912
|
+
function verifyTableRowPipes(node) {
|
|
9913
|
+
const loc = sourceCode.getLoc(node);
|
|
9914
|
+
const range = sourceCode.getRange(node);
|
|
9915
|
+
verifyTableLinePipes(range, loc, node.children.length);
|
|
9916
|
+
}
|
|
9917
|
+
/**
|
|
9918
|
+
* Verify the table line pipes
|
|
9919
|
+
*/
|
|
9920
|
+
function verifyTableLinePipes(lineContentRange, lineLocation, columnCount) {
|
|
9921
|
+
verifyTableLeadingPipe(lineContentRange, lineLocation, columnCount);
|
|
9922
|
+
verifyTableTrailingPipe(lineContentRange, lineLocation, columnCount);
|
|
9923
|
+
}
|
|
9924
|
+
/**
|
|
9925
|
+
* Verify the table leading pipe
|
|
9926
|
+
*/
|
|
9927
|
+
function verifyTableLeadingPipe(lineContentRange, lineLocation, columnCount) {
|
|
9928
|
+
if (leadingOption === "always") {
|
|
9929
|
+
if (sourceCode.text.startsWith("|", lineContentRange[0])) return;
|
|
9930
|
+
context.report({
|
|
9931
|
+
messageId: "missingLeadingPipe",
|
|
9932
|
+
loc: lineLocation.start,
|
|
9933
|
+
fix(fixer) {
|
|
9934
|
+
return fixer.insertTextBeforeRange(lineContentRange, "| ");
|
|
9935
|
+
}
|
|
9936
|
+
});
|
|
9937
|
+
} else if (leadingOption === "never") {
|
|
9938
|
+
if (columnCount < 2) {
|
|
9939
|
+
if (!(trailingOption === "always" && sourceCode.text.endsWith("|", lineContentRange[1]))) return;
|
|
9940
|
+
}
|
|
9941
|
+
if (!sourceCode.text.startsWith("|", lineContentRange[0])) return;
|
|
9942
|
+
let endIndex = lineContentRange[0] + 1;
|
|
9943
|
+
while (endIndex < lineContentRange[1] && isSpaceOrTab(sourceCode.text[endIndex])) endIndex++;
|
|
9944
|
+
context.report({
|
|
9945
|
+
messageId: "unexpectedLeadingPipe",
|
|
9946
|
+
loc: {
|
|
9947
|
+
start: lineLocation.start,
|
|
9948
|
+
end: {
|
|
9949
|
+
line: lineLocation.start.line,
|
|
9950
|
+
column: lineLocation.start.column + (endIndex - lineContentRange[0])
|
|
9951
|
+
}
|
|
9952
|
+
},
|
|
9953
|
+
fix(fixer) {
|
|
9954
|
+
return fixer.removeRange([lineContentRange[0], endIndex]);
|
|
9955
|
+
}
|
|
9956
|
+
});
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
/**
|
|
9960
|
+
* Verify the table trailing pipe
|
|
9961
|
+
*/
|
|
9962
|
+
function verifyTableTrailingPipe(lineContentRange, lineLocation, columnCount) {
|
|
9963
|
+
if (trailingOption === "always") {
|
|
9964
|
+
if (sourceCode.text.endsWith("|", lineContentRange[1])) return;
|
|
9965
|
+
context.report({
|
|
9966
|
+
messageId: "missingTrailingPipe",
|
|
9967
|
+
loc: lineLocation.end,
|
|
9968
|
+
fix(fixer) {
|
|
9969
|
+
return fixer.insertTextAfterRange(lineContentRange, " |");
|
|
9970
|
+
}
|
|
9971
|
+
});
|
|
9972
|
+
} else if (trailingOption === "never") {
|
|
9973
|
+
if (columnCount < 2) {
|
|
9974
|
+
if (!(leadingOption === "always" && sourceCode.text.startsWith("|", lineContentRange[0]))) return;
|
|
9975
|
+
}
|
|
9976
|
+
if (!sourceCode.text.endsWith("|", lineContentRange[1])) return;
|
|
9977
|
+
let startIndex = lineContentRange[1] - 1;
|
|
9978
|
+
while (startIndex - 1 > lineContentRange[0] && isSpaceOrTab(sourceCode.text[startIndex - 1])) startIndex--;
|
|
9979
|
+
context.report({
|
|
9980
|
+
messageId: "unexpectedTrailingPipe",
|
|
9981
|
+
loc: {
|
|
9982
|
+
start: {
|
|
9983
|
+
line: lineLocation.end.line,
|
|
9984
|
+
column: lineLocation.end.column - (lineContentRange[1] - startIndex)
|
|
9985
|
+
},
|
|
9986
|
+
end: lineLocation.end
|
|
9987
|
+
},
|
|
9988
|
+
fix(fixer) {
|
|
9989
|
+
return fixer.removeRange([startIndex, lineContentRange[1]]);
|
|
9990
|
+
}
|
|
9991
|
+
});
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
return { table(node) {
|
|
9995
|
+
verifyTablePipes(node);
|
|
9996
|
+
} };
|
|
9997
|
+
}
|
|
9998
|
+
});
|
|
9999
|
+
|
|
10000
|
+
//#endregion
|
|
10001
|
+
//#region src/rules/table-pipe-alignment.ts
|
|
10002
|
+
var table_pipe_alignment_default = createRule("table-pipe-alignment", {
|
|
10003
|
+
meta: {
|
|
10004
|
+
type: "layout",
|
|
10005
|
+
docs: {
|
|
10006
|
+
description: "enforce consistent alignment of table pipes",
|
|
10007
|
+
categories: ["standard"],
|
|
10008
|
+
listCategory: "Decorative"
|
|
10009
|
+
},
|
|
10010
|
+
fixable: "code",
|
|
10011
|
+
hasSuggestions: false,
|
|
10012
|
+
schema: [{
|
|
10013
|
+
type: "object",
|
|
10014
|
+
properties: { column: { enum: ["minimum", "consistent"] } },
|
|
10015
|
+
additionalProperties: false
|
|
10016
|
+
}],
|
|
10017
|
+
messages: {
|
|
10018
|
+
addSpaces: "Table pipe should be aligned at column {{expected}} (add {{count}} character{{plural}}).",
|
|
10019
|
+
removeSpaces: "Table pipe should be aligned at column {{expected}} (remove {{count}} character{{plural}})."
|
|
10020
|
+
}
|
|
10021
|
+
},
|
|
10022
|
+
create(context) {
|
|
10023
|
+
const sourceCode = context.sourceCode;
|
|
10024
|
+
const columnOption = (context.options[0] || {}).column || "minimum";
|
|
10025
|
+
/**
|
|
10026
|
+
* Verify the table pipes
|
|
10027
|
+
*/
|
|
10028
|
+
function verifyTablePipes(rows) {
|
|
10029
|
+
let columnCount = 0;
|
|
10030
|
+
for (const row of rows) columnCount = Math.max(columnCount, row.cells.length);
|
|
10031
|
+
let targetRows = [...rows];
|
|
10032
|
+
for (let pipeIndex = 0; pipeIndex <= columnCount; pipeIndex++) {
|
|
10033
|
+
const expected = getExpectedPipePosition(rows, pipeIndex);
|
|
10034
|
+
if (expected == null) continue;
|
|
10035
|
+
const unreportedRows = [];
|
|
10036
|
+
for (const row of targetRows) if (verifyRowPipe(row, pipeIndex, expected)) unreportedRows.push(row);
|
|
10037
|
+
targetRows = unreportedRows;
|
|
10038
|
+
if (targetRows.length === 0) break;
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
/**
|
|
10042
|
+
* Verify the pipe in the row
|
|
10043
|
+
*/
|
|
10044
|
+
function verifyRowPipe(row, pipeIndex, expected) {
|
|
10045
|
+
let cellIndex;
|
|
10046
|
+
let pipe;
|
|
10047
|
+
if (pipeIndex === 0) {
|
|
10048
|
+
cellIndex = 0;
|
|
10049
|
+
pipe = "leadingPipe";
|
|
10050
|
+
} else {
|
|
10051
|
+
cellIndex = pipeIndex - 1;
|
|
10052
|
+
pipe = "trailingPipe";
|
|
10053
|
+
}
|
|
10054
|
+
if (row.cells.length <= cellIndex) return true;
|
|
10055
|
+
const cell = row.cells[cellIndex];
|
|
10056
|
+
const pipeToken = cell[pipe];
|
|
10057
|
+
if (!pipeToken) return true;
|
|
10058
|
+
return verifyPipe(pipeToken, expected, {
|
|
10059
|
+
cell,
|
|
10060
|
+
pipeIndex
|
|
10061
|
+
});
|
|
10062
|
+
}
|
|
10063
|
+
/**
|
|
10064
|
+
* Verify the pipe position
|
|
10065
|
+
*/
|
|
10066
|
+
function verifyPipe(pipe, expected, ctx) {
|
|
10067
|
+
const actual = getTextWidth(sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1));
|
|
10068
|
+
const diff = expected - actual;
|
|
10069
|
+
if (diff === 0) return true;
|
|
10070
|
+
context.report({
|
|
10071
|
+
loc: pipe.loc,
|
|
10072
|
+
messageId: diff > 0 ? "addSpaces" : "removeSpaces",
|
|
8449
10073
|
data: {
|
|
8450
|
-
expected:
|
|
8451
|
-
|
|
10074
|
+
expected: String(expected),
|
|
10075
|
+
count: String(Math.abs(diff)),
|
|
10076
|
+
plural: Math.abs(diff) === 1 ? "" : "s"
|
|
8452
10077
|
},
|
|
8453
10078
|
fix(fixer) {
|
|
8454
|
-
|
|
10079
|
+
if (diff > 0) {
|
|
10080
|
+
if (ctx.pipeIndex === 0 || ctx.cell.type === "cell") return fixer.insertTextBeforeRange(pipe.range, " ".repeat(diff));
|
|
10081
|
+
return fixer.insertTextAfterRange([ctx.cell.delimiter.range[0], ctx.cell.delimiter.range[0] + 1], "-".repeat(diff));
|
|
10082
|
+
}
|
|
10083
|
+
const baseEdit = fixRemoveSpaces();
|
|
10084
|
+
if (baseEdit) return baseEdit;
|
|
10085
|
+
if (ctx.pipeIndex === 0 || ctx.cell.type === "cell") return null;
|
|
10086
|
+
const beforeDelimiter = sourceCode.lines[ctx.cell.delimiter.loc.start.line - 1].slice(0, ctx.cell.delimiter.loc.start.column - 1);
|
|
10087
|
+
const widthBeforeDelimiter = getTextWidth(beforeDelimiter);
|
|
10088
|
+
const newLength = expected - widthBeforeDelimiter;
|
|
10089
|
+
const minimumDelimiterLength = getMinimumDelimiterLength(ctx.cell.align);
|
|
10090
|
+
const spaceAfter = isNeedSpaceAfterContent(ctx.cell) ? " " : "";
|
|
10091
|
+
if (newLength < minimumDelimiterLength + spaceAfter.length) return null;
|
|
10092
|
+
const delimiterPrefix = ctx.cell.align === "left" || ctx.cell.align === "center" ? ":" : "";
|
|
10093
|
+
const delimiterSuffix = (ctx.cell.align === "right" || ctx.cell.align === "center" ? ":" : "") + spaceAfter;
|
|
10094
|
+
const newDelimiter = "-".repeat(newLength - delimiterPrefix.length - delimiterSuffix.length);
|
|
10095
|
+
return fixer.replaceTextRange([ctx.cell.delimiter.range[0], pipe.range[0]], delimiterPrefix + newDelimiter + delimiterSuffix);
|
|
10096
|
+
/**
|
|
10097
|
+
* Fixer to remove spaces before the pipe
|
|
10098
|
+
*/
|
|
10099
|
+
function fixRemoveSpaces() {
|
|
10100
|
+
const beforePipe = sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1);
|
|
10101
|
+
const trimmedBeforePipe = beforePipe.trimEnd();
|
|
10102
|
+
const spacesBeforePipeLength = beforePipe.length - trimmedBeforePipe.length;
|
|
10103
|
+
const widthBeforePipe = getTextWidth(trimmedBeforePipe);
|
|
10104
|
+
const newSpacesLength = expected - widthBeforePipe;
|
|
10105
|
+
if (newSpacesLength < (ctx.pipeIndex > 0 && isNeedSpaceAfterContent(ctx.cell) ? 1 : 0)) return null;
|
|
10106
|
+
return fixer.replaceTextRange([pipe.range[0] - spacesBeforePipeLength, pipe.range[0]], " ".repeat(newSpacesLength));
|
|
10107
|
+
}
|
|
8455
10108
|
}
|
|
8456
10109
|
});
|
|
10110
|
+
return false;
|
|
10111
|
+
}
|
|
10112
|
+
/**
|
|
10113
|
+
* Get the expected pipe position for the index
|
|
10114
|
+
*/
|
|
10115
|
+
function getExpectedPipePosition(rows, pipeIndex) {
|
|
10116
|
+
if (pipeIndex === 0) {
|
|
10117
|
+
const firstCell = rows[0].cells[0];
|
|
10118
|
+
const firstToken = firstCell.leadingPipe ?? firstCell.content;
|
|
10119
|
+
if (!firstToken) return null;
|
|
10120
|
+
return getTextWidth(sourceCode.lines[firstToken.loc.start.line - 1].slice(0, firstToken.loc.start.column - 1));
|
|
10121
|
+
}
|
|
10122
|
+
if (columnOption === "minimum") return getMinimumPipePosition(rows, pipeIndex);
|
|
10123
|
+
else if (columnOption === "consistent") {
|
|
10124
|
+
const columnIndex = pipeIndex - 1;
|
|
10125
|
+
for (const row of rows) {
|
|
10126
|
+
if (row.cells.length <= columnIndex) continue;
|
|
10127
|
+
const cell = row.cells[columnIndex];
|
|
10128
|
+
if (cell.type === "delimiter" || !cell.trailingPipe) continue;
|
|
10129
|
+
const width = getTextWidth(sourceCode.lines[cell.trailingPipe.loc.start.line - 1].slice(0, cell.trailingPipe.loc.start.column - 1));
|
|
10130
|
+
return Math.max(width, getMinimumPipePosition(rows, pipeIndex) || 0);
|
|
10131
|
+
}
|
|
10132
|
+
}
|
|
10133
|
+
return null;
|
|
10134
|
+
}
|
|
10135
|
+
/**
|
|
10136
|
+
* Get the minimum pipe position for the index
|
|
10137
|
+
*/
|
|
10138
|
+
function getMinimumPipePosition(rows, pipeIndex) {
|
|
10139
|
+
let maxWidth = 0;
|
|
10140
|
+
const columnIndex = pipeIndex - 1;
|
|
10141
|
+
for (const row of rows) {
|
|
10142
|
+
if (row.cells.length <= columnIndex) continue;
|
|
10143
|
+
const cell = row.cells[columnIndex];
|
|
10144
|
+
let width;
|
|
10145
|
+
if (cell.type === "delimiter") {
|
|
10146
|
+
const minimumDelimiterLength = getMinimumDelimiterLength(cell.align);
|
|
10147
|
+
width = getTextWidth(sourceCode.lines[cell.delimiter.loc.start.line - 1].slice(0, cell.delimiter.loc.start.column - 1)) + minimumDelimiterLength;
|
|
10148
|
+
} else {
|
|
10149
|
+
if (!cell.content) continue;
|
|
10150
|
+
width = getTextWidth(sourceCode.lines[cell.content.loc.end.line - 1].slice(0, cell.content.loc.end.column - 1));
|
|
10151
|
+
}
|
|
10152
|
+
if (isNeedSpaceAfterContent(cell)) width += 1;
|
|
10153
|
+
maxWidth = Math.max(maxWidth, width);
|
|
10154
|
+
}
|
|
10155
|
+
return maxWidth;
|
|
10156
|
+
}
|
|
10157
|
+
/**
|
|
10158
|
+
* Get the minimum delimiter length based on alignment
|
|
10159
|
+
*/
|
|
10160
|
+
function getMinimumDelimiterLength(align) {
|
|
10161
|
+
return align === "none" ? 1 : align === "center" ? 3 : 2;
|
|
10162
|
+
}
|
|
10163
|
+
/**
|
|
10164
|
+
* Check if a cell needs a space after its content
|
|
10165
|
+
*/
|
|
10166
|
+
function isNeedSpaceAfterContent(cell) {
|
|
10167
|
+
let content;
|
|
10168
|
+
if (cell.type === "delimiter") content = cell.delimiter;
|
|
10169
|
+
else {
|
|
10170
|
+
if (!cell.content) return false;
|
|
10171
|
+
content = cell.content;
|
|
10172
|
+
}
|
|
10173
|
+
return cell.trailingPipe && content.range[1] < cell.trailingPipe.range[0];
|
|
10174
|
+
}
|
|
10175
|
+
/**
|
|
10176
|
+
* Convert a parsed table row to row data
|
|
10177
|
+
*/
|
|
10178
|
+
function parsedTableRowToRowData(parsedRow) {
|
|
10179
|
+
return { cells: parsedRow.cells.map((cell, index) => {
|
|
10180
|
+
const nextCell = index + 1 < parsedRow.cells.length ? parsedRow.cells[index + 1] : null;
|
|
10181
|
+
return {
|
|
10182
|
+
type: "cell",
|
|
10183
|
+
leadingPipe: cell.leadingPipe,
|
|
10184
|
+
content: cell.cell,
|
|
10185
|
+
trailingPipe: nextCell ? nextCell.leadingPipe : parsedRow.trailingPipe
|
|
10186
|
+
};
|
|
10187
|
+
}) };
|
|
10188
|
+
}
|
|
10189
|
+
/**
|
|
10190
|
+
* Convert a parsed table delimiter row to row data
|
|
10191
|
+
*/
|
|
10192
|
+
function parsedTableDelimiterRowToRowData(parsedDelimiterRow) {
|
|
10193
|
+
return { cells: parsedDelimiterRow.delimiters.map((cell, index) => {
|
|
10194
|
+
const nextCell = index + 1 < parsedDelimiterRow.delimiters.length ? parsedDelimiterRow.delimiters[index + 1] : null;
|
|
10195
|
+
return {
|
|
10196
|
+
type: "delimiter",
|
|
10197
|
+
leadingPipe: cell.leadingPipe,
|
|
10198
|
+
delimiter: cell.delimiter,
|
|
10199
|
+
align: cell.delimiter.align,
|
|
10200
|
+
trailingPipe: nextCell ? nextCell.leadingPipe : parsedDelimiterRow.trailingPipe
|
|
10201
|
+
};
|
|
10202
|
+
}) };
|
|
10203
|
+
}
|
|
10204
|
+
return { table(node) {
|
|
10205
|
+
const parsed = parseTable(sourceCode, node);
|
|
10206
|
+
if (!parsed) return;
|
|
10207
|
+
const rows = [parsedTableRowToRowData(parsed.headerRow), parsedTableDelimiterRowToRowData(parsed.delimiterRow)];
|
|
10208
|
+
for (const bodyRow of parsed.bodyRows) rows.push(parsedTableRowToRowData(bodyRow));
|
|
10209
|
+
verifyTablePipes(rows);
|
|
8457
10210
|
} };
|
|
8458
10211
|
}
|
|
8459
10212
|
});
|
|
8460
10213
|
|
|
8461
10214
|
//#endregion
|
|
8462
|
-
//#region src/rules/table-
|
|
8463
|
-
|
|
10215
|
+
//#region src/rules/table-pipe-spacing.ts
|
|
10216
|
+
/**
|
|
10217
|
+
* Parsed options
|
|
10218
|
+
*/
|
|
10219
|
+
function parseOptions(options) {
|
|
10220
|
+
const spaceOption = options?.space;
|
|
10221
|
+
const leadingSpace = (typeof spaceOption === "object" ? spaceOption.leading : spaceOption) || "always";
|
|
10222
|
+
const trailingSpace = (typeof spaceOption === "object" ? spaceOption.trailing : spaceOption) || "always";
|
|
10223
|
+
const cellAlignOption = options?.cellAlign;
|
|
10224
|
+
const defaultDelimiterCellAlign = (typeof cellAlignOption === "object" ? cellAlignOption.defaultDelimiter : cellAlignOption) || "left";
|
|
10225
|
+
const leftAlignmentDelimiterCellAlign = (typeof cellAlignOption === "object" ? cellAlignOption.leftAlignmentDelimiter : cellAlignOption) || "left";
|
|
10226
|
+
const centerAlignmentDelimiterCellAlign = (typeof cellAlignOption === "object" ? cellAlignOption.centerAlignmentDelimiter : cellAlignOption) || "center";
|
|
10227
|
+
const rightAlignmentDelimiterCellAlign = (typeof cellAlignOption === "object" ? cellAlignOption.rightAlignmentDelimiter : cellAlignOption) || "right";
|
|
10228
|
+
return {
|
|
10229
|
+
leadingSpace,
|
|
10230
|
+
trailingSpace,
|
|
10231
|
+
cellAlignByDelimiter: {
|
|
10232
|
+
none: adjustAlign(defaultDelimiterCellAlign),
|
|
10233
|
+
left: adjustAlign(leftAlignmentDelimiterCellAlign),
|
|
10234
|
+
center: adjustAlign(centerAlignmentDelimiterCellAlign),
|
|
10235
|
+
right: adjustAlign(rightAlignmentDelimiterCellAlign)
|
|
10236
|
+
}
|
|
10237
|
+
};
|
|
10238
|
+
/**
|
|
10239
|
+
* Adjust the alignment option based on the spacing options.
|
|
10240
|
+
*/
|
|
10241
|
+
function adjustAlign(align) {
|
|
10242
|
+
if (align === "left") {
|
|
10243
|
+
if (trailingSpace === "always") return "left";
|
|
10244
|
+
return "ignore";
|
|
10245
|
+
}
|
|
10246
|
+
if (align === "center") {
|
|
10247
|
+
if (leadingSpace === "always" && trailingSpace === "always") return "center";
|
|
10248
|
+
return "ignore";
|
|
10249
|
+
}
|
|
10250
|
+
if (align === "right") {
|
|
10251
|
+
if (leadingSpace === "always") return "right";
|
|
10252
|
+
return "ignore";
|
|
10253
|
+
}
|
|
10254
|
+
return align;
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
var table_pipe_spacing_default = createRule("table-pipe-spacing", {
|
|
8464
10258
|
meta: {
|
|
8465
|
-
type: "
|
|
8466
|
-
fixable: "code",
|
|
10259
|
+
type: "layout",
|
|
8467
10260
|
docs: {
|
|
8468
|
-
description: "enforce consistent
|
|
8469
|
-
categories: [],
|
|
8470
|
-
listCategory: "
|
|
10261
|
+
description: "enforce consistent spacing around table pipes",
|
|
10262
|
+
categories: ["standard"],
|
|
10263
|
+
listCategory: "Whitespace"
|
|
8471
10264
|
},
|
|
10265
|
+
fixable: "whitespace",
|
|
10266
|
+
hasSuggestions: false,
|
|
8472
10267
|
schema: [{
|
|
8473
10268
|
type: "object",
|
|
8474
10269
|
properties: {
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
10270
|
+
space: { anyOf: [{ enum: ["always", "never"] }, {
|
|
10271
|
+
type: "object",
|
|
10272
|
+
properties: {
|
|
10273
|
+
leading: { enum: ["always", "never"] },
|
|
10274
|
+
trailing: { enum: ["always", "never"] }
|
|
10275
|
+
},
|
|
10276
|
+
additionalProperties: false
|
|
10277
|
+
}] },
|
|
10278
|
+
cellAlign: { anyOf: [{ enum: [
|
|
10279
|
+
"left",
|
|
10280
|
+
"center",
|
|
10281
|
+
"right"
|
|
10282
|
+
] }, {
|
|
10283
|
+
type: "object",
|
|
10284
|
+
properties: {
|
|
10285
|
+
defaultDelimiter: { enum: [
|
|
10286
|
+
"left",
|
|
10287
|
+
"center",
|
|
10288
|
+
"right",
|
|
10289
|
+
"ignore"
|
|
10290
|
+
] },
|
|
10291
|
+
leftAlignmentDelimiter: { enum: [
|
|
10292
|
+
"left",
|
|
10293
|
+
"center",
|
|
10294
|
+
"right",
|
|
10295
|
+
"ignore"
|
|
10296
|
+
] },
|
|
10297
|
+
centerAlignmentDelimiter: { enum: [
|
|
10298
|
+
"left",
|
|
10299
|
+
"center",
|
|
10300
|
+
"right",
|
|
10301
|
+
"ignore"
|
|
10302
|
+
] },
|
|
10303
|
+
rightAlignmentDelimiter: { enum: [
|
|
10304
|
+
"left",
|
|
10305
|
+
"center",
|
|
10306
|
+
"right",
|
|
10307
|
+
"ignore"
|
|
10308
|
+
] }
|
|
10309
|
+
},
|
|
10310
|
+
additionalProperties: false
|
|
10311
|
+
}] }
|
|
8491
10312
|
},
|
|
8492
10313
|
additionalProperties: false
|
|
8493
10314
|
}],
|
|
8494
10315
|
messages: {
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
10316
|
+
expectedSpaceBefore: "Expected 1 space before \"|\".",
|
|
10317
|
+
expectedNoSpaceBefore: "Expected no space before \"|\".",
|
|
10318
|
+
expectedSpaceAfter: "Expected 1 space after \"|\".",
|
|
10319
|
+
expectedNoSpaceAfter: "Expected no space after \"|\".",
|
|
10320
|
+
expectedAlignLeft: "Expected 1 space after \"|\" for left-aligned column.",
|
|
10321
|
+
expectedNoSpaceAlignLeft: "Expected no space after \"|\" for left-aligned column.",
|
|
10322
|
+
expectedAlignRight: "Expected 1 space before \"|\" for right-aligned column.",
|
|
10323
|
+
expectedNoSpaceAlignRight: "Expected no space before \"|\" for right-aligned column.",
|
|
10324
|
+
expectedAlignCenter: "Expected the number of spaces before and after the content to be the same or differ by 1 at most for center-aligned column."
|
|
8499
10325
|
}
|
|
8500
10326
|
},
|
|
8501
10327
|
create(context) {
|
|
8502
10328
|
const sourceCode = context.sourceCode;
|
|
8503
|
-
const
|
|
8504
|
-
const preserveWordsOption = parsePreserveWordsOption(context.options[0]?.preserveWords || defaultPreserveWords);
|
|
8505
|
-
const minorWords = context.options[0]?.minorWords || defaultMinorWords;
|
|
8506
|
-
const ignorePatterns = (context.options[0]?.ignorePatterns || [
|
|
8507
|
-
"/^v\\d+/u",
|
|
8508
|
-
"/\\w+\\.[a-z\\d]+$/u",
|
|
8509
|
-
"/\\w+(?:API|Api)$/u",
|
|
8510
|
-
"/\\w+(?:SDK|Sdk)$/u",
|
|
8511
|
-
"/\\w+(?:CLI|Cli)$/u"
|
|
8512
|
-
]).map((pattern) => {
|
|
8513
|
-
if (isRegExp(pattern)) return toRegExp(pattern);
|
|
8514
|
-
try {
|
|
8515
|
-
return new RegExp(pattern, "v");
|
|
8516
|
-
} catch {}
|
|
8517
|
-
try {
|
|
8518
|
-
return new RegExp(pattern, "u");
|
|
8519
|
-
} catch {}
|
|
8520
|
-
return new RegExp(pattern);
|
|
8521
|
-
});
|
|
10329
|
+
const options = parseOptions(context.options[0]);
|
|
8522
10330
|
/**
|
|
8523
|
-
*
|
|
10331
|
+
* Verify for the leading pipe.
|
|
8524
10332
|
*/
|
|
8525
|
-
function
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
if (wordAndOffset.punctuation) continue;
|
|
8534
|
-
if (ignorePatterns.some((pattern) => pattern.test(wordAndOffset.word))) continue;
|
|
8535
|
-
const preservePhrase = preserveWordsOption.findPreservePhrase((function* () {
|
|
8536
|
-
const firstWord = wordAndOffsets[index];
|
|
8537
|
-
if (firstWord.punctuation) return;
|
|
8538
|
-
yield firstWord.word;
|
|
8539
|
-
for (let next = index + 1; next < wordAndOffsets.length; next++) yield wordAndOffsets[next].word;
|
|
8540
|
-
})());
|
|
8541
|
-
if (preservePhrase) {
|
|
8542
|
-
for (let wordIndex = 0; wordIndex < preservePhrase.length; wordIndex++) {
|
|
8543
|
-
processed.add(index + wordIndex);
|
|
8544
|
-
verifyWord(wordAndOffsets[index + wordIndex], preservePhrase[wordIndex], "preserved");
|
|
10333
|
+
function verifyLeadingPipe(pipe, nextToken) {
|
|
10334
|
+
if (options.leadingSpace === "always") {
|
|
10335
|
+
if (pipe.range[1] < nextToken.range[0]) return true;
|
|
10336
|
+
context.report({
|
|
10337
|
+
loc: pipe.loc,
|
|
10338
|
+
messageId: "expectedSpaceAfter",
|
|
10339
|
+
fix(fixer) {
|
|
10340
|
+
return fixer.insertTextAfterRange(pipe.range, " ");
|
|
8545
10341
|
}
|
|
8546
|
-
continue;
|
|
8547
|
-
}
|
|
8548
|
-
const preserveWordList = preserveWordsOption.findPreserveWord(wordAndOffset.word);
|
|
8549
|
-
if (preserveWordList) {
|
|
8550
|
-
if (!preserveWordList.some((w) => w === wordAndOffset.word)) verifyWord(wordAndOffset, preserveWordList[0], "preserved");
|
|
8551
|
-
continue;
|
|
8552
|
-
}
|
|
8553
|
-
const expectedWordResult = convertWordCasing(wordAndOffset, {
|
|
8554
|
-
caseStyle,
|
|
8555
|
-
minorWords
|
|
8556
10342
|
});
|
|
8557
|
-
|
|
10343
|
+
return false;
|
|
10344
|
+
} else if (options.leadingSpace === "never") {
|
|
10345
|
+
if (pipe.range[1] === nextToken.range[0]) return true;
|
|
10346
|
+
context.report({
|
|
10347
|
+
loc: {
|
|
10348
|
+
start: pipe.loc.end,
|
|
10349
|
+
end: nextToken.loc.start
|
|
10350
|
+
},
|
|
10351
|
+
messageId: "expectedNoSpaceAfter",
|
|
10352
|
+
fix(fixer) {
|
|
10353
|
+
return fixer.removeRange([pipe.range[1], nextToken.range[0]]);
|
|
10354
|
+
}
|
|
10355
|
+
});
|
|
10356
|
+
return false;
|
|
8558
10357
|
}
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
10358
|
+
return true;
|
|
10359
|
+
}
|
|
10360
|
+
/**
|
|
10361
|
+
* Verify for the trailing pipe.
|
|
10362
|
+
*/
|
|
10363
|
+
function verifyTrailingPipe(prevToken, pipe) {
|
|
10364
|
+
if (options.trailingSpace === "always") {
|
|
10365
|
+
if (prevToken.range[1] < pipe.range[0]) return true;
|
|
8567
10366
|
context.report({
|
|
8568
|
-
|
|
8569
|
-
messageId:
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
10367
|
+
loc: pipe.loc,
|
|
10368
|
+
messageId: "expectedSpaceBefore",
|
|
10369
|
+
fix(fixer) {
|
|
10370
|
+
return fixer.insertTextBeforeRange(pipe.range, " ");
|
|
10371
|
+
}
|
|
10372
|
+
});
|
|
10373
|
+
return false;
|
|
10374
|
+
} else if (options.trailingSpace === "never") {
|
|
10375
|
+
if (prevToken.range[1] === pipe.range[0]) return true;
|
|
10376
|
+
context.report({
|
|
10377
|
+
loc: {
|
|
10378
|
+
start: prevToken.loc.end,
|
|
10379
|
+
end: pipe.loc.start
|
|
8573
10380
|
},
|
|
8574
|
-
|
|
10381
|
+
messageId: "expectedNoSpaceBefore",
|
|
8575
10382
|
fix(fixer) {
|
|
8576
|
-
return fixer.
|
|
10383
|
+
return fixer.removeRange([prevToken.range[1], pipe.range[0]]);
|
|
8577
10384
|
}
|
|
8578
10385
|
});
|
|
10386
|
+
return false;
|
|
8579
10387
|
}
|
|
10388
|
+
return true;
|
|
8580
10389
|
}
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
10390
|
+
/**
|
|
10391
|
+
* Verify for the alignment of the pipe according to the delimiter alignment.
|
|
10392
|
+
* Return "leading" if the leading pipe is reported, "trailing" if the trailing pipe is reported, "both" if both are reported, or null if nothing is reported.
|
|
10393
|
+
*/
|
|
10394
|
+
function verifyAlignPipe({ leadingPipe, content, trailingPipe }, cellAlign) {
|
|
10395
|
+
if (!leadingPipe || !trailingPipe || !content) return null;
|
|
10396
|
+
const lineText = sourceCode.lines[leadingPipe.loc.start.line - 1];
|
|
10397
|
+
if (cellAlign === "left") {
|
|
10398
|
+
const expectedWidth = options.leadingSpace === "always" ? 1 : 0;
|
|
10399
|
+
if (getLeadingSpacesWidth() === expectedWidth) return null;
|
|
10400
|
+
context.report({
|
|
10401
|
+
loc: leadingPipe.range[1] < content.range[0] ? {
|
|
10402
|
+
start: leadingPipe.loc.end,
|
|
10403
|
+
end: content.loc.start
|
|
10404
|
+
} : leadingPipe.loc,
|
|
10405
|
+
messageId: expectedWidth >= 1 ? "expectedAlignLeft" : "expectedNoSpaceAlignLeft",
|
|
10406
|
+
*fix(fixer) {
|
|
10407
|
+
const cellWidth = getCellWidth();
|
|
10408
|
+
const contentTextWidth = getContentTextWidth();
|
|
10409
|
+
const newLeadingSpaces = " ".repeat(expectedWidth);
|
|
10410
|
+
const newTrailingSpaces = " ".repeat(Math.max(cellWidth - contentTextWidth - expectedWidth, 0));
|
|
10411
|
+
const contentText = getNormalizedContentText();
|
|
10412
|
+
yield fixer.replaceTextRange([leadingPipe.range[1], trailingPipe.range[0]], `${newLeadingSpaces}${contentText}${newTrailingSpaces}`);
|
|
10413
|
+
}
|
|
10414
|
+
});
|
|
10415
|
+
return "leading";
|
|
10416
|
+
} else if (cellAlign === "right") {
|
|
10417
|
+
const expectedWidth = options.trailingSpace === "always" ? 1 : 0;
|
|
10418
|
+
if (getTrailingSpacesWidth() === expectedWidth) return null;
|
|
10419
|
+
context.report({
|
|
10420
|
+
loc: content.range[1] < trailingPipe.range[0] ? {
|
|
10421
|
+
start: content.loc.end,
|
|
10422
|
+
end: trailingPipe.loc.start
|
|
10423
|
+
} : trailingPipe.loc,
|
|
10424
|
+
messageId: expectedWidth >= 1 ? "expectedAlignRight" : "expectedNoSpaceAlignRight",
|
|
10425
|
+
*fix(fixer) {
|
|
10426
|
+
const cellWidth = getCellWidth();
|
|
10427
|
+
const contentTextWidth = getContentTextWidth();
|
|
10428
|
+
const newLeadingSpaces = " ".repeat(Math.max(cellWidth - contentTextWidth - expectedWidth, 0));
|
|
10429
|
+
const newTrailingSpaces = " ".repeat(expectedWidth);
|
|
10430
|
+
const contentText = getNormalizedContentText();
|
|
10431
|
+
yield fixer.replaceTextRange([leadingPipe.range[1], trailingPipe.range[0]], `${newLeadingSpaces}${contentText}${newTrailingSpaces}`);
|
|
10432
|
+
}
|
|
10433
|
+
});
|
|
10434
|
+
return "trailing";
|
|
10435
|
+
} else if (cellAlign === "center") {
|
|
10436
|
+
const leadingSpacesWidth = getLeadingSpacesWidth();
|
|
10437
|
+
const trailingSpacesWidth = getTrailingSpacesWidth();
|
|
10438
|
+
if (leadingSpacesWidth === trailingSpacesWidth || leadingSpacesWidth + 1 === trailingSpacesWidth) return null;
|
|
10439
|
+
const leadingReportLoc = leadingPipe.range[1] < content.range[0] ? {
|
|
10440
|
+
start: leadingPipe.loc.end,
|
|
10441
|
+
end: content.loc.start
|
|
10442
|
+
} : leadingPipe.loc;
|
|
10443
|
+
const trailingReportLoc = content.range[1] < trailingPipe.range[0] ? {
|
|
10444
|
+
start: content.loc.end,
|
|
10445
|
+
end: trailingPipe.loc.start
|
|
10446
|
+
} : trailingPipe.loc;
|
|
10447
|
+
for (const reportLoc of [leadingReportLoc, trailingReportLoc]) context.report({
|
|
10448
|
+
loc: reportLoc,
|
|
10449
|
+
messageId: "expectedAlignCenter",
|
|
10450
|
+
*fix(fixer) {
|
|
10451
|
+
const cellWidth = getCellWidth();
|
|
10452
|
+
const contentTextWidth = getContentTextWidth();
|
|
10453
|
+
const spacesLength = cellWidth - contentTextWidth;
|
|
10454
|
+
const leadingSpacesLength = Math.floor(spacesLength / 2);
|
|
10455
|
+
const trailingSpacesLength = spacesLength - leadingSpacesLength;
|
|
10456
|
+
const newLeadingSpaces = " ".repeat(leadingSpacesLength);
|
|
10457
|
+
const newTrailingSpaces = " ".repeat(trailingSpacesLength);
|
|
10458
|
+
const contentText = getNormalizedContentText();
|
|
10459
|
+
yield fixer.replaceTextRange([leadingPipe.range[1], trailingPipe.range[0]], `${newLeadingSpaces}${contentText}${newTrailingSpaces}`);
|
|
10460
|
+
}
|
|
8589
10461
|
});
|
|
10462
|
+
return "both";
|
|
10463
|
+
}
|
|
10464
|
+
return null;
|
|
10465
|
+
/**
|
|
10466
|
+
* Get the width of the leading spaces in the cell.
|
|
10467
|
+
*/
|
|
10468
|
+
function getLeadingSpacesWidth() {
|
|
10469
|
+
return getTextWidth(lineText, leadingPipe.loc.end.column - 1, content.loc.start.column - 1);
|
|
10470
|
+
}
|
|
10471
|
+
/**
|
|
10472
|
+
* Get the width of the trailing spaces in the cell.
|
|
10473
|
+
*/
|
|
10474
|
+
function getTrailingSpacesWidth() {
|
|
10475
|
+
return getTextWidth(lineText, content.loc.end.column - 1, trailingPipe.loc.start.column - 1);
|
|
10476
|
+
}
|
|
10477
|
+
/**
|
|
10478
|
+
* Get the width of the whole cell (including leading and trailing spaces)
|
|
10479
|
+
*/
|
|
10480
|
+
function getCellWidth() {
|
|
10481
|
+
return getTextWidth(lineText, leadingPipe.loc.end.column - 1, trailingPipe.loc.start.column - 1);
|
|
10482
|
+
}
|
|
10483
|
+
/**
|
|
10484
|
+
* Get the width of the content text (excluding leading and trailing spaces)
|
|
10485
|
+
*/
|
|
10486
|
+
function getContentTextWidth() {
|
|
10487
|
+
return getTextWidth(lineText, content.loc.start.column - 1, content.loc.end.column - 1);
|
|
10488
|
+
}
|
|
10489
|
+
/**
|
|
10490
|
+
* Get the normalized content text (with normalized spaces)
|
|
10491
|
+
*/
|
|
10492
|
+
function getNormalizedContentText() {
|
|
10493
|
+
const prefixWidth = getWidth(lineText.slice(0, content.loc.start.column - 1));
|
|
10494
|
+
let result = "";
|
|
10495
|
+
for (const c of lineText.slice(content.loc.start.column - 1, content.loc.end.column - 1)) if (c === " ") result += " ".repeat(4 - (prefixWidth + result.length) % 4);
|
|
10496
|
+
else result += c;
|
|
10497
|
+
return result;
|
|
10498
|
+
}
|
|
10499
|
+
}
|
|
10500
|
+
/**
|
|
10501
|
+
* Convert a parsed table row to cell data list
|
|
10502
|
+
*/
|
|
10503
|
+
function parsedTableRowToCellDataList(parsedRow) {
|
|
10504
|
+
return parsedRow.cells.map((cell, index) => {
|
|
10505
|
+
const nextCell = index + 1 < parsedRow.cells.length ? parsedRow.cells[index + 1] : null;
|
|
10506
|
+
return {
|
|
10507
|
+
type: "cell",
|
|
10508
|
+
leadingPipe: cell.leadingPipe,
|
|
10509
|
+
content: cell.cell,
|
|
10510
|
+
trailingPipe: nextCell ? nextCell.leadingPipe : parsedRow.trailingPipe
|
|
10511
|
+
};
|
|
10512
|
+
});
|
|
10513
|
+
}
|
|
10514
|
+
/**
|
|
10515
|
+
* Convert a parsed table delimiter row to delimiter data list
|
|
10516
|
+
*/
|
|
10517
|
+
function parsedTableDelimiterRowToDelimiterDataList(parsedDelimiterRow) {
|
|
10518
|
+
return parsedDelimiterRow.delimiters.map((cell, index) => {
|
|
10519
|
+
const nextCell = index + 1 < parsedDelimiterRow.delimiters.length ? parsedDelimiterRow.delimiters[index + 1] : null;
|
|
10520
|
+
return {
|
|
10521
|
+
type: "delimiter",
|
|
10522
|
+
leadingPipe: cell.leadingPipe,
|
|
10523
|
+
content: cell.delimiter,
|
|
10524
|
+
align: cell.delimiter.align,
|
|
10525
|
+
trailingPipe: nextCell ? nextCell.leadingPipe : parsedDelimiterRow.trailingPipe
|
|
10526
|
+
};
|
|
10527
|
+
});
|
|
10528
|
+
}
|
|
10529
|
+
return { table(node) {
|
|
10530
|
+
const parsedDelimiterRow = parseTableDelimiterRow(sourceCode, node);
|
|
10531
|
+
const delimiters = parsedDelimiterRow && parsedTableDelimiterRowToDelimiterDataList(parsedDelimiterRow);
|
|
10532
|
+
for (const row of node.children) {
|
|
10533
|
+
const parsedRow = parseTableRow(sourceCode, row);
|
|
10534
|
+
if (!parsedRow) continue;
|
|
10535
|
+
const cells = parsedTableRowToCellDataList(parsedRow);
|
|
10536
|
+
for (let columnIndex = 0; columnIndex < cells.length; columnIndex++) {
|
|
10537
|
+
const cell = cells[columnIndex];
|
|
10538
|
+
const delimiter = delimiters && columnIndex < delimiters.length ? delimiters[columnIndex] : null;
|
|
10539
|
+
const alignReportedPoint = delimiter ? verifyAlignPipe(cell, options.cellAlignByDelimiter[delimiter.align]) : null;
|
|
10540
|
+
if (alignReportedPoint === "both") continue;
|
|
10541
|
+
if (cell.leadingPipe && alignReportedPoint !== "leading") {
|
|
10542
|
+
if (options.leadingSpace !== "never" || cell.content) {
|
|
10543
|
+
const nextToken = getNextToken(cells, columnIndex);
|
|
10544
|
+
if (nextToken) verifyLeadingPipe(cell.leadingPipe, nextToken);
|
|
10545
|
+
}
|
|
10546
|
+
}
|
|
10547
|
+
if (cell.trailingPipe && options.trailingSpace !== "never" && alignReportedPoint !== "trailing") {
|
|
10548
|
+
const prevToken = getPrevToken(cells, columnIndex);
|
|
10549
|
+
if (prevToken) verifyTrailingPipe(prevToken, cell.trailingPipe);
|
|
10550
|
+
}
|
|
10551
|
+
}
|
|
10552
|
+
}
|
|
10553
|
+
if (!delimiters) return;
|
|
10554
|
+
for (let columnIndex = 0; columnIndex < delimiters.length; columnIndex++) {
|
|
10555
|
+
const delimiter = delimiters[columnIndex];
|
|
10556
|
+
const alignReportedPoint = verifyAlignPipe(delimiter, options.cellAlignByDelimiter[delimiter.align]);
|
|
10557
|
+
if (alignReportedPoint === "both") continue;
|
|
10558
|
+
if (delimiter.leadingPipe && alignReportedPoint !== "leading") verifyLeadingPipe(delimiter.leadingPipe, delimiter.content);
|
|
10559
|
+
if (delimiter.trailingPipe && alignReportedPoint !== "trailing") verifyTrailingPipe(delimiter.content, delimiter.trailingPipe);
|
|
8590
10560
|
}
|
|
8591
10561
|
} };
|
|
10562
|
+
/**
|
|
10563
|
+
* Get the next token (pipe or cell) after the given column index.
|
|
10564
|
+
*/
|
|
10565
|
+
function getNextToken(cells, columnIndex) {
|
|
10566
|
+
for (let i = columnIndex; i < cells.length; i++) {
|
|
10567
|
+
const cell = cells[i];
|
|
10568
|
+
const token = cell.content ?? cell.trailingPipe;
|
|
10569
|
+
if (token) return token;
|
|
10570
|
+
}
|
|
10571
|
+
return null;
|
|
10572
|
+
}
|
|
10573
|
+
/**
|
|
10574
|
+
* Get the prev token (pipe or cell) after the given column index.
|
|
10575
|
+
*/
|
|
10576
|
+
function getPrevToken(cells, columnIndex) {
|
|
10577
|
+
for (let i = columnIndex; i >= 0; i--) {
|
|
10578
|
+
const cell = cells[i];
|
|
10579
|
+
const token = cell.content ?? cell.leadingPipe;
|
|
10580
|
+
if (token) return token;
|
|
10581
|
+
}
|
|
10582
|
+
return null;
|
|
10583
|
+
}
|
|
8592
10584
|
}
|
|
8593
10585
|
});
|
|
8594
10586
|
|
|
@@ -8600,7 +10592,7 @@ var thematic_break_character_style_default = createRule("thematic-break-characte
|
|
|
8600
10592
|
docs: {
|
|
8601
10593
|
description: "enforce consistent character style for thematic breaks (horizontal rules) in Markdown.",
|
|
8602
10594
|
categories: ["standard"],
|
|
8603
|
-
listCategory: "
|
|
10595
|
+
listCategory: "Notation"
|
|
8604
10596
|
},
|
|
8605
10597
|
fixable: "code",
|
|
8606
10598
|
hasSuggestions: false,
|
|
@@ -8679,7 +10671,7 @@ var thematic_break_length_default = createRule("thematic-break-length", {
|
|
|
8679
10671
|
docs: {
|
|
8680
10672
|
description: "enforce consistent length for thematic breaks (horizontal rules) in Markdown.",
|
|
8681
10673
|
categories: ["standard"],
|
|
8682
|
-
listCategory: "
|
|
10674
|
+
listCategory: "Decorative"
|
|
8683
10675
|
},
|
|
8684
10676
|
fixable: "code",
|
|
8685
10677
|
hasSuggestions: false,
|
|
@@ -8745,7 +10737,7 @@ var thematic_break_sequence_pattern_default = createRule("thematic-break-sequenc
|
|
|
8745
10737
|
docs: {
|
|
8746
10738
|
description: "enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown.",
|
|
8747
10739
|
categories: ["standard"],
|
|
8748
|
-
listCategory: "
|
|
10740
|
+
listCategory: "Decorative"
|
|
8749
10741
|
},
|
|
8750
10742
|
fixable: "code",
|
|
8751
10743
|
hasSuggestions: false,
|
|
@@ -8814,6 +10806,7 @@ const rules$1 = [
|
|
|
8814
10806
|
emphasis_delimiters_style_default,
|
|
8815
10807
|
hard_linebreak_style_default,
|
|
8816
10808
|
heading_casing_default,
|
|
10809
|
+
indent_default,
|
|
8817
10810
|
level1_heading_style_default,
|
|
8818
10811
|
level2_heading_style_default,
|
|
8819
10812
|
link_bracket_newline_default,
|
|
@@ -8841,6 +10834,9 @@ const rules$1 = [
|
|
|
8841
10834
|
sort_definitions_default,
|
|
8842
10835
|
strikethrough_delimiters_style_default,
|
|
8843
10836
|
table_header_casing_default,
|
|
10837
|
+
table_leading_trailing_pipes_default,
|
|
10838
|
+
table_pipe_alignment_default,
|
|
10839
|
+
table_pipe_spacing_default,
|
|
8844
10840
|
thematic_break_character_style_default,
|
|
8845
10841
|
thematic_break_length_default,
|
|
8846
10842
|
thematic_break_sequence_pattern_default
|
|
@@ -8848,8 +10844,7 @@ const rules$1 = [
|
|
|
8848
10844
|
|
|
8849
10845
|
//#endregion
|
|
8850
10846
|
//#region src/configs/recommended.ts
|
|
8851
|
-
var recommended_exports = {
|
|
8852
|
-
__export(recommended_exports, {
|
|
10847
|
+
var recommended_exports = __export({
|
|
8853
10848
|
files: () => files$1,
|
|
8854
10849
|
language: () => language$1,
|
|
8855
10850
|
languageOptions: () => languageOptions$1,
|
|
@@ -8879,8 +10874,7 @@ const rules$3 = {
|
|
|
8879
10874
|
|
|
8880
10875
|
//#endregion
|
|
8881
10876
|
//#region src/configs/standard.ts
|
|
8882
|
-
var standard_exports = {
|
|
8883
|
-
__export(standard_exports, {
|
|
10877
|
+
var standard_exports = __export({
|
|
8884
10878
|
files: () => files,
|
|
8885
10879
|
language: () => language,
|
|
8886
10880
|
languageOptions: () => languageOptions,
|
|
@@ -8907,6 +10901,7 @@ const rules$2 = {
|
|
|
8907
10901
|
"markdown-preferences/code-fence-style": "error",
|
|
8908
10902
|
"markdown-preferences/emphasis-delimiters-style": "error",
|
|
8909
10903
|
"markdown-preferences/hard-linebreak-style": "error",
|
|
10904
|
+
"markdown-preferences/indent": "error",
|
|
8910
10905
|
"markdown-preferences/level1-heading-style": "error",
|
|
8911
10906
|
"markdown-preferences/level2-heading-style": "error",
|
|
8912
10907
|
"markdown-preferences/link-bracket-newline": "error",
|
|
@@ -8930,6 +10925,9 @@ const rules$2 = {
|
|
|
8930
10925
|
"markdown-preferences/setext-heading-underline-length": "error",
|
|
8931
10926
|
"markdown-preferences/sort-definitions": "error",
|
|
8932
10927
|
"markdown-preferences/strikethrough-delimiters-style": "error",
|
|
10928
|
+
"markdown-preferences/table-leading-trailing-pipes": "error",
|
|
10929
|
+
"markdown-preferences/table-pipe-alignment": "error",
|
|
10930
|
+
"markdown-preferences/table-pipe-spacing": "error",
|
|
8933
10931
|
"markdown-preferences/thematic-break-character-style": "error",
|
|
8934
10932
|
"markdown-preferences/thematic-break-length": "error",
|
|
8935
10933
|
"markdown-preferences/thematic-break-sequence-pattern": "error"
|
|
@@ -8937,13 +10935,12 @@ const rules$2 = {
|
|
|
8937
10935
|
|
|
8938
10936
|
//#endregion
|
|
8939
10937
|
//#region src/meta.ts
|
|
8940
|
-
var meta_exports = {
|
|
8941
|
-
__export(meta_exports, {
|
|
10938
|
+
var meta_exports = __export({
|
|
8942
10939
|
name: () => name,
|
|
8943
10940
|
version: () => version
|
|
8944
10941
|
});
|
|
8945
10942
|
const name = "eslint-plugin-markdown-preferences";
|
|
8946
|
-
const version = "0.
|
|
10943
|
+
const version = "0.25.0";
|
|
8947
10944
|
|
|
8948
10945
|
//#endregion
|
|
8949
10946
|
//#region src/index.ts
|