eslint-plugin-markdown-preferences 0.24.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 -49
- package/lib/index.d.ts +35 -1
- package/lib/index.js +996 -61
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -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,
|
|
@@ -700,7 +715,7 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
|
|
|
700
715
|
docs: {
|
|
701
716
|
description: "enforce consistent alignment of blockquote markers",
|
|
702
717
|
categories: ["recommended", "standard"],
|
|
703
|
-
listCategory: "
|
|
718
|
+
listCategory: "Whitespace"
|
|
704
719
|
},
|
|
705
720
|
fixable: "whitespace",
|
|
706
721
|
hasSuggestions: false,
|
|
@@ -783,7 +798,7 @@ function getOtherMarker(unavailableMarker) {
|
|
|
783
798
|
/**
|
|
784
799
|
* Parse rule options.
|
|
785
800
|
*/
|
|
786
|
-
function parseOptions$
|
|
801
|
+
function parseOptions$4(options) {
|
|
787
802
|
const primary = options.primary || "-";
|
|
788
803
|
const secondary = options.secondary || getOtherMarker(primary);
|
|
789
804
|
if (primary === secondary) throw new Error(`\`primary\` and \`secondary\` cannot be the same (primary: "${primary}", secondary: "${secondary}").`);
|
|
@@ -815,7 +830,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
|
815
830
|
docs: {
|
|
816
831
|
description: "enforce consistent bullet list (unordered list) marker style",
|
|
817
832
|
categories: ["standard"],
|
|
818
|
-
listCategory: "
|
|
833
|
+
listCategory: "Notation"
|
|
819
834
|
},
|
|
820
835
|
fixable: "code",
|
|
821
836
|
hasSuggestions: false,
|
|
@@ -851,7 +866,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
|
851
866
|
},
|
|
852
867
|
create(context) {
|
|
853
868
|
const sourceCode = context.sourceCode;
|
|
854
|
-
const options = parseOptions$
|
|
869
|
+
const options = parseOptions$4(context.options[0] || {});
|
|
855
870
|
let containerStack = {
|
|
856
871
|
node: sourceCode.ast,
|
|
857
872
|
level: 1,
|
|
@@ -1114,7 +1129,7 @@ var code_fence_length_default = createRule("code-fence-length", {
|
|
|
1114
1129
|
docs: {
|
|
1115
1130
|
description: "enforce consistent code fence length in fenced code blocks.",
|
|
1116
1131
|
categories: ["standard"],
|
|
1117
|
-
listCategory: "
|
|
1132
|
+
listCategory: "Decorative"
|
|
1118
1133
|
},
|
|
1119
1134
|
fixable: "code",
|
|
1120
1135
|
hasSuggestions: false,
|
|
@@ -1251,7 +1266,7 @@ var code_fence_style_default = createRule("code-fence-style", {
|
|
|
1251
1266
|
docs: {
|
|
1252
1267
|
description: "enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.",
|
|
1253
1268
|
categories: ["standard"],
|
|
1254
|
-
listCategory: "
|
|
1269
|
+
listCategory: "Notation"
|
|
1255
1270
|
},
|
|
1256
1271
|
fixable: "code",
|
|
1257
1272
|
hasSuggestions: false,
|
|
@@ -1298,7 +1313,7 @@ var definitions_last_default = createRule("definitions-last", {
|
|
|
1298
1313
|
docs: {
|
|
1299
1314
|
description: "require link definitions and footnote definitions to be placed at the end of the document",
|
|
1300
1315
|
categories: [],
|
|
1301
|
-
listCategory: "
|
|
1316
|
+
listCategory: "Notation"
|
|
1302
1317
|
},
|
|
1303
1318
|
fixable: "code",
|
|
1304
1319
|
hasSuggestions: false,
|
|
@@ -3422,7 +3437,7 @@ var emphasis_delimiters_style_default = createRule("emphasis-delimiters-style",
|
|
|
3422
3437
|
docs: {
|
|
3423
3438
|
description: "enforce a consistent delimiter style for emphasis and strong emphasis",
|
|
3424
3439
|
categories: ["standard"],
|
|
3425
|
-
listCategory: "
|
|
3440
|
+
listCategory: "Notation"
|
|
3426
3441
|
},
|
|
3427
3442
|
fixable: "code",
|
|
3428
3443
|
hasSuggestions: false,
|
|
@@ -3604,7 +3619,7 @@ var hard_linebreak_style_default = createRule("hard-linebreak-style", {
|
|
|
3604
3619
|
docs: {
|
|
3605
3620
|
description: "enforce consistent hard linebreak style.",
|
|
3606
3621
|
categories: ["recommended", "standard"],
|
|
3607
|
-
listCategory: "
|
|
3622
|
+
listCategory: "Notation"
|
|
3608
3623
|
},
|
|
3609
3624
|
fixable: "code",
|
|
3610
3625
|
hasSuggestions: false,
|
|
@@ -5037,7 +5052,7 @@ function parseListItem(sourceCode, node) {
|
|
|
5037
5052
|
/**
|
|
5038
5053
|
* Parse options.
|
|
5039
5054
|
*/
|
|
5040
|
-
function parseOptions$
|
|
5055
|
+
function parseOptions$3(options) {
|
|
5041
5056
|
const listItems = options?.listItems;
|
|
5042
5057
|
return { listItems: {
|
|
5043
5058
|
first: listItems?.first ?? 1,
|
|
@@ -5051,7 +5066,7 @@ var indent_default = createRule("indent", {
|
|
|
5051
5066
|
docs: {
|
|
5052
5067
|
description: "enforce consistent indentation in Markdown files",
|
|
5053
5068
|
categories: ["standard"],
|
|
5054
|
-
listCategory: "
|
|
5069
|
+
listCategory: "Whitespace"
|
|
5055
5070
|
},
|
|
5056
5071
|
fixable: "whitespace",
|
|
5057
5072
|
hasSuggestions: false,
|
|
@@ -5090,7 +5105,7 @@ var indent_default = createRule("indent", {
|
|
|
5090
5105
|
},
|
|
5091
5106
|
create(context) {
|
|
5092
5107
|
const sourceCode = context.sourceCode;
|
|
5093
|
-
const options = parseOptions$
|
|
5108
|
+
const options = parseOptions$3(context.options[0]);
|
|
5094
5109
|
class AbsBlockStack {
|
|
5095
5110
|
violations = [];
|
|
5096
5111
|
getCurrentBlockquote() {
|
|
@@ -6039,7 +6054,7 @@ var level1_heading_style_default = createRule("level1-heading-style", {
|
|
|
6039
6054
|
docs: {
|
|
6040
6055
|
description: "enforce consistent style for level 1 headings",
|
|
6041
6056
|
categories: ["standard"],
|
|
6042
|
-
listCategory: "
|
|
6057
|
+
listCategory: "Notation"
|
|
6043
6058
|
},
|
|
6044
6059
|
fixable: "code",
|
|
6045
6060
|
hasSuggestions: false,
|
|
@@ -6116,7 +6131,7 @@ var level2_heading_style_default = createRule("level2-heading-style", {
|
|
|
6116
6131
|
docs: {
|
|
6117
6132
|
description: "enforce consistent style for level 2 headings",
|
|
6118
6133
|
categories: ["standard"],
|
|
6119
|
-
listCategory: "
|
|
6134
|
+
listCategory: "Notation"
|
|
6120
6135
|
},
|
|
6121
6136
|
fixable: "code",
|
|
6122
6137
|
hasSuggestions: false,
|
|
@@ -6193,7 +6208,7 @@ var link_bracket_newline_default = createRule("link-bracket-newline", {
|
|
|
6193
6208
|
docs: {
|
|
6194
6209
|
description: "enforce linebreaks after opening and before closing link brackets",
|
|
6195
6210
|
categories: ["standard"],
|
|
6196
|
-
listCategory: "
|
|
6211
|
+
listCategory: "Whitespace"
|
|
6197
6212
|
},
|
|
6198
6213
|
fixable: "whitespace",
|
|
6199
6214
|
hasSuggestions: false,
|
|
@@ -6218,11 +6233,11 @@ var link_bracket_newline_default = createRule("link-bracket-newline", {
|
|
|
6218
6233
|
},
|
|
6219
6234
|
create(context) {
|
|
6220
6235
|
const sourceCode = context.sourceCode;
|
|
6221
|
-
const optionProvider = parseOptions$
|
|
6236
|
+
const optionProvider = parseOptions$5(context.options[0]);
|
|
6222
6237
|
/**
|
|
6223
6238
|
* Parse the options.
|
|
6224
6239
|
*/
|
|
6225
|
-
function parseOptions$
|
|
6240
|
+
function parseOptions$5(option) {
|
|
6226
6241
|
const newline = option?.newline ?? "never";
|
|
6227
6242
|
const multiline = option?.multiline ?? false;
|
|
6228
6243
|
return (bracketsRange) => {
|
|
@@ -6353,7 +6368,7 @@ var link_bracket_newline_default = createRule("link-bracket-newline", {
|
|
|
6353
6368
|
/**
|
|
6354
6369
|
* The basic option for links and images.
|
|
6355
6370
|
*/
|
|
6356
|
-
function parseOptions$
|
|
6371
|
+
function parseOptions$2(option) {
|
|
6357
6372
|
const space = option?.space ?? "never";
|
|
6358
6373
|
const imagesInLinks = option?.imagesInLinks;
|
|
6359
6374
|
return {
|
|
@@ -6382,7 +6397,7 @@ var link_bracket_spacing_default = createRule("link-bracket-spacing", {
|
|
|
6382
6397
|
docs: {
|
|
6383
6398
|
description: "enforce consistent spacing inside link brackets",
|
|
6384
6399
|
categories: ["standard"],
|
|
6385
|
-
listCategory: "
|
|
6400
|
+
listCategory: "Whitespace"
|
|
6386
6401
|
},
|
|
6387
6402
|
fixable: "whitespace",
|
|
6388
6403
|
hasSuggestions: false,
|
|
@@ -6403,7 +6418,7 @@ var link_bracket_spacing_default = createRule("link-bracket-spacing", {
|
|
|
6403
6418
|
},
|
|
6404
6419
|
create(context) {
|
|
6405
6420
|
const sourceCode = context.sourceCode;
|
|
6406
|
-
const options = parseOptions$
|
|
6421
|
+
const options = parseOptions$2(context.options[0]);
|
|
6407
6422
|
/**
|
|
6408
6423
|
* Verify the space after the opening bracket and before the closing bracket.
|
|
6409
6424
|
*/
|
|
@@ -6546,7 +6561,7 @@ var link_destination_style_default = createRule("link-destination-style", {
|
|
|
6546
6561
|
docs: {
|
|
6547
6562
|
description: "enforce a consistent style for link destinations",
|
|
6548
6563
|
categories: ["standard"],
|
|
6549
|
-
listCategory: "
|
|
6564
|
+
listCategory: "Notation"
|
|
6550
6565
|
},
|
|
6551
6566
|
fixable: "code",
|
|
6552
6567
|
hasSuggestions: false,
|
|
@@ -6671,7 +6686,7 @@ var link_paren_newline_default = createRule("link-paren-newline", {
|
|
|
6671
6686
|
docs: {
|
|
6672
6687
|
description: "enforce linebreaks after opening and before closing link parentheses",
|
|
6673
6688
|
categories: ["standard"],
|
|
6674
|
-
listCategory: "
|
|
6689
|
+
listCategory: "Whitespace"
|
|
6675
6690
|
},
|
|
6676
6691
|
fixable: "whitespace",
|
|
6677
6692
|
hasSuggestions: false,
|
|
@@ -6696,11 +6711,11 @@ var link_paren_newline_default = createRule("link-paren-newline", {
|
|
|
6696
6711
|
},
|
|
6697
6712
|
create(context) {
|
|
6698
6713
|
const sourceCode = context.sourceCode;
|
|
6699
|
-
const optionProvider = parseOptions$
|
|
6714
|
+
const optionProvider = parseOptions$5(context.options[0]);
|
|
6700
6715
|
/**
|
|
6701
6716
|
* Parse the options.
|
|
6702
6717
|
*/
|
|
6703
|
-
function parseOptions$
|
|
6718
|
+
function parseOptions$5(option) {
|
|
6704
6719
|
const newline = option?.newline ?? "never";
|
|
6705
6720
|
const multiline = option?.multiline ?? false;
|
|
6706
6721
|
return (openingParenIndex, closingParenIndex) => {
|
|
@@ -6815,7 +6830,7 @@ var link_paren_spacing_default = createRule("link-paren-spacing", {
|
|
|
6815
6830
|
docs: {
|
|
6816
6831
|
description: "enforce consistent spacing inside link parentheses",
|
|
6817
6832
|
categories: ["standard"],
|
|
6818
|
-
listCategory: "
|
|
6833
|
+
listCategory: "Whitespace"
|
|
6819
6834
|
},
|
|
6820
6835
|
fixable: "whitespace",
|
|
6821
6836
|
hasSuggestions: false,
|
|
@@ -6950,7 +6965,7 @@ var link_title_style_default = createRule("link-title-style", {
|
|
|
6950
6965
|
docs: {
|
|
6951
6966
|
description: "enforce a consistent style for link titles",
|
|
6952
6967
|
categories: ["standard"],
|
|
6953
|
-
listCategory: "
|
|
6968
|
+
listCategory: "Notation"
|
|
6954
6969
|
},
|
|
6955
6970
|
fixable: "code",
|
|
6956
6971
|
hasSuggestions: false,
|
|
@@ -7032,7 +7047,7 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
|
|
|
7032
7047
|
docs: {
|
|
7033
7048
|
description: "enforce consistent alignment of list markers",
|
|
7034
7049
|
categories: ["recommended", "standard"],
|
|
7035
|
-
listCategory: "
|
|
7050
|
+
listCategory: "Whitespace"
|
|
7036
7051
|
},
|
|
7037
7052
|
fixable: "whitespace",
|
|
7038
7053
|
hasSuggestions: false,
|
|
@@ -7131,7 +7146,7 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
|
|
|
7131
7146
|
docs: {
|
|
7132
7147
|
description: "disallow laziness in blockquotes",
|
|
7133
7148
|
categories: ["recommended", "standard"],
|
|
7134
|
-
listCategory: "
|
|
7149
|
+
listCategory: "Decorative"
|
|
7135
7150
|
},
|
|
7136
7151
|
fixable: void 0,
|
|
7137
7152
|
hasSuggestions: true,
|
|
@@ -7230,6 +7245,193 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
|
|
|
7230
7245
|
}
|
|
7231
7246
|
});
|
|
7232
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
|
+
}
|
|
7434
|
+
|
|
7233
7435
|
//#endregion
|
|
7234
7436
|
//#region src/rules/no-multi-spaces.ts
|
|
7235
7437
|
var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
@@ -7238,7 +7440,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
7238
7440
|
docs: {
|
|
7239
7441
|
description: "disallow multiple spaces",
|
|
7240
7442
|
categories: ["standard"],
|
|
7241
|
-
listCategory: "
|
|
7443
|
+
listCategory: "Whitespace"
|
|
7242
7444
|
},
|
|
7243
7445
|
fixable: "whitespace",
|
|
7244
7446
|
hasSuggestions: false,
|
|
@@ -7258,7 +7460,8 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
7258
7460
|
linkReference: verifyLinkReference,
|
|
7259
7461
|
listItem: verifyListItem,
|
|
7260
7462
|
blockquote: processBlockquote,
|
|
7261
|
-
text: verifyText
|
|
7463
|
+
text: verifyText,
|
|
7464
|
+
table: verifyTable
|
|
7262
7465
|
};
|
|
7263
7466
|
/**
|
|
7264
7467
|
* Verify a text node.
|
|
@@ -7267,6 +7470,14 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
|
|
|
7267
7470
|
verifyTextInNode(node);
|
|
7268
7471
|
}
|
|
7269
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
|
+
/**
|
|
7270
7481
|
* Verify a definition node.
|
|
7271
7482
|
*/
|
|
7272
7483
|
function verifyLinkDefinition(node) {
|
|
@@ -7442,7 +7653,7 @@ var no_multiple_empty_lines_default = createRule("no-multiple-empty-lines", {
|
|
|
7442
7653
|
docs: {
|
|
7443
7654
|
description: "disallow multiple empty lines in Markdown files.",
|
|
7444
7655
|
categories: ["standard"],
|
|
7445
|
-
listCategory: "
|
|
7656
|
+
listCategory: "Whitespace"
|
|
7446
7657
|
},
|
|
7447
7658
|
fixable: "whitespace",
|
|
7448
7659
|
hasSuggestions: false,
|
|
@@ -7601,7 +7812,7 @@ var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebrea
|
|
|
7601
7812
|
docs: {
|
|
7602
7813
|
description: "disallow text backslash at the end of a line.",
|
|
7603
7814
|
categories: ["recommended", "standard"],
|
|
7604
|
-
listCategory: "
|
|
7815
|
+
listCategory: "Notation"
|
|
7605
7816
|
},
|
|
7606
7817
|
fixable: void 0,
|
|
7607
7818
|
hasSuggestions: true,
|
|
@@ -7646,7 +7857,7 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
|
|
|
7646
7857
|
docs: {
|
|
7647
7858
|
description: "disallow trailing whitespace at the end of lines in Markdown files.",
|
|
7648
7859
|
categories: ["standard"],
|
|
7649
|
-
listCategory: "
|
|
7860
|
+
listCategory: "Whitespace"
|
|
7650
7861
|
},
|
|
7651
7862
|
fixable: "whitespace",
|
|
7652
7863
|
hasSuggestions: false,
|
|
@@ -7772,7 +7983,7 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
|
|
|
7772
7983
|
docs: {
|
|
7773
7984
|
description: "enforce that ordered list markers use sequential numbers",
|
|
7774
7985
|
categories: ["standard"],
|
|
7775
|
-
listCategory: "
|
|
7986
|
+
listCategory: "Decorative"
|
|
7776
7987
|
},
|
|
7777
7988
|
fixable: "code",
|
|
7778
7989
|
hasSuggestions: true,
|
|
@@ -7986,7 +8197,7 @@ function markerToKind(marker) {
|
|
|
7986
8197
|
/**
|
|
7987
8198
|
* Parse rule options.
|
|
7988
8199
|
*/
|
|
7989
|
-
function parseOptions(options) {
|
|
8200
|
+
function parseOptions$1(options) {
|
|
7990
8201
|
const prefer = markerToKind(options.prefer) || ".";
|
|
7991
8202
|
const overrides = (options.overrides ?? []).map((override) => {
|
|
7992
8203
|
const preferForOverride = markerToKind(override.prefer) || ".";
|
|
@@ -8013,7 +8224,7 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
|
|
|
8013
8224
|
docs: {
|
|
8014
8225
|
description: "enforce consistent ordered list marker style",
|
|
8015
8226
|
categories: ["standard"],
|
|
8016
|
-
listCategory: "
|
|
8227
|
+
listCategory: "Notation"
|
|
8017
8228
|
},
|
|
8018
8229
|
fixable: "code",
|
|
8019
8230
|
hasSuggestions: false,
|
|
@@ -8047,7 +8258,7 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
|
|
|
8047
8258
|
},
|
|
8048
8259
|
create(context) {
|
|
8049
8260
|
const sourceCode = context.sourceCode;
|
|
8050
|
-
const options = parseOptions(context.options[0] || {});
|
|
8261
|
+
const options = parseOptions$1(context.options[0] || {});
|
|
8051
8262
|
let containerStack = {
|
|
8052
8263
|
node: sourceCode.ast,
|
|
8053
8264
|
level: 1,
|
|
@@ -8232,7 +8443,7 @@ var padding_line_between_blocks_default = createRule("padding-line-between-block
|
|
|
8232
8443
|
docs: {
|
|
8233
8444
|
description: "require or disallow padding lines between blocks",
|
|
8234
8445
|
categories: ["standard"],
|
|
8235
|
-
listCategory: "
|
|
8446
|
+
listCategory: "Whitespace"
|
|
8236
8447
|
},
|
|
8237
8448
|
fixable: "whitespace",
|
|
8238
8449
|
hasSuggestions: false,
|
|
@@ -8430,7 +8641,7 @@ var prefer_autolinks_default = createRule("prefer-autolinks", {
|
|
|
8430
8641
|
docs: {
|
|
8431
8642
|
description: "enforce the use of autolinks for URLs",
|
|
8432
8643
|
categories: ["recommended", "standard"],
|
|
8433
|
-
listCategory: "
|
|
8644
|
+
listCategory: "Notation"
|
|
8434
8645
|
},
|
|
8435
8646
|
fixable: "code",
|
|
8436
8647
|
hasSuggestions: false,
|
|
@@ -8468,7 +8679,7 @@ var prefer_fenced_code_blocks_default = createRule("prefer-fenced-code-blocks",
|
|
|
8468
8679
|
docs: {
|
|
8469
8680
|
description: "enforce the use of fenced code blocks over indented code blocks",
|
|
8470
8681
|
categories: ["recommended", "standard"],
|
|
8471
|
-
listCategory: "
|
|
8682
|
+
listCategory: "Notation"
|
|
8472
8683
|
},
|
|
8473
8684
|
fixable: "code",
|
|
8474
8685
|
hasSuggestions: false,
|
|
@@ -8698,7 +8909,7 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
|
|
|
8698
8909
|
docs: {
|
|
8699
8910
|
description: "enforce using link reference definitions instead of inline links",
|
|
8700
8911
|
categories: [],
|
|
8701
|
-
listCategory: "
|
|
8912
|
+
listCategory: "Notation"
|
|
8702
8913
|
},
|
|
8703
8914
|
fixable: "code",
|
|
8704
8915
|
hasSuggestions: false,
|
|
@@ -8977,9 +9188,9 @@ var setext_heading_underline_length_default = createRule("setext-heading-underli
|
|
|
8977
9188
|
docs: {
|
|
8978
9189
|
description: "enforce setext heading underline length",
|
|
8979
9190
|
categories: ["standard"],
|
|
8980
|
-
listCategory: "
|
|
9191
|
+
listCategory: "Decorative"
|
|
8981
9192
|
},
|
|
8982
|
-
fixable: "
|
|
9193
|
+
fixable: "code",
|
|
8983
9194
|
schema: [{
|
|
8984
9195
|
type: "object",
|
|
8985
9196
|
properties: {
|
|
@@ -9215,7 +9426,7 @@ var sort_definitions_default = createRule("sort-definitions", {
|
|
|
9215
9426
|
docs: {
|
|
9216
9427
|
description: "enforce a specific order for link definitions and footnote definitions",
|
|
9217
9428
|
categories: ["standard"],
|
|
9218
|
-
listCategory: "
|
|
9429
|
+
listCategory: "Decorative"
|
|
9219
9430
|
},
|
|
9220
9431
|
fixable: "code",
|
|
9221
9432
|
hasSuggestions: false,
|
|
@@ -9488,7 +9699,7 @@ var strikethrough_delimiters_style_default = createRule("strikethrough-delimiter
|
|
|
9488
9699
|
docs: {
|
|
9489
9700
|
description: "enforce a consistent delimiter style for strikethrough",
|
|
9490
9701
|
categories: ["standard"],
|
|
9491
|
-
listCategory: "
|
|
9702
|
+
listCategory: "Notation"
|
|
9492
9703
|
},
|
|
9493
9704
|
fixable: "code",
|
|
9494
9705
|
hasSuggestions: false,
|
|
@@ -9655,6 +9866,724 @@ var table_header_casing_default = createRule("table-header-casing", {
|
|
|
9655
9866
|
}
|
|
9656
9867
|
});
|
|
9657
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",
|
|
10073
|
+
data: {
|
|
10074
|
+
expected: String(expected),
|
|
10075
|
+
count: String(Math.abs(diff)),
|
|
10076
|
+
plural: Math.abs(diff) === 1 ? "" : "s"
|
|
10077
|
+
},
|
|
10078
|
+
fix(fixer) {
|
|
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
|
+
}
|
|
10108
|
+
}
|
|
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);
|
|
10210
|
+
} };
|
|
10211
|
+
}
|
|
10212
|
+
});
|
|
10213
|
+
|
|
10214
|
+
//#endregion
|
|
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", {
|
|
10258
|
+
meta: {
|
|
10259
|
+
type: "layout",
|
|
10260
|
+
docs: {
|
|
10261
|
+
description: "enforce consistent spacing around table pipes",
|
|
10262
|
+
categories: ["standard"],
|
|
10263
|
+
listCategory: "Whitespace"
|
|
10264
|
+
},
|
|
10265
|
+
fixable: "whitespace",
|
|
10266
|
+
hasSuggestions: false,
|
|
10267
|
+
schema: [{
|
|
10268
|
+
type: "object",
|
|
10269
|
+
properties: {
|
|
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
|
+
}] }
|
|
10312
|
+
},
|
|
10313
|
+
additionalProperties: false
|
|
10314
|
+
}],
|
|
10315
|
+
messages: {
|
|
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."
|
|
10325
|
+
}
|
|
10326
|
+
},
|
|
10327
|
+
create(context) {
|
|
10328
|
+
const sourceCode = context.sourceCode;
|
|
10329
|
+
const options = parseOptions(context.options[0]);
|
|
10330
|
+
/**
|
|
10331
|
+
* Verify for the leading pipe.
|
|
10332
|
+
*/
|
|
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, " ");
|
|
10341
|
+
}
|
|
10342
|
+
});
|
|
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;
|
|
10357
|
+
}
|
|
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;
|
|
10366
|
+
context.report({
|
|
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
|
|
10380
|
+
},
|
|
10381
|
+
messageId: "expectedNoSpaceBefore",
|
|
10382
|
+
fix(fixer) {
|
|
10383
|
+
return fixer.removeRange([prevToken.range[1], pipe.range[0]]);
|
|
10384
|
+
}
|
|
10385
|
+
});
|
|
10386
|
+
return false;
|
|
10387
|
+
}
|
|
10388
|
+
return true;
|
|
10389
|
+
}
|
|
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
|
+
}
|
|
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);
|
|
10560
|
+
}
|
|
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
|
+
}
|
|
10584
|
+
}
|
|
10585
|
+
});
|
|
10586
|
+
|
|
9658
10587
|
//#endregion
|
|
9659
10588
|
//#region src/rules/thematic-break-character-style.ts
|
|
9660
10589
|
var thematic_break_character_style_default = createRule("thematic-break-character-style", {
|
|
@@ -9663,7 +10592,7 @@ var thematic_break_character_style_default = createRule("thematic-break-characte
|
|
|
9663
10592
|
docs: {
|
|
9664
10593
|
description: "enforce consistent character style for thematic breaks (horizontal rules) in Markdown.",
|
|
9665
10594
|
categories: ["standard"],
|
|
9666
|
-
listCategory: "
|
|
10595
|
+
listCategory: "Notation"
|
|
9667
10596
|
},
|
|
9668
10597
|
fixable: "code",
|
|
9669
10598
|
hasSuggestions: false,
|
|
@@ -9742,7 +10671,7 @@ var thematic_break_length_default = createRule("thematic-break-length", {
|
|
|
9742
10671
|
docs: {
|
|
9743
10672
|
description: "enforce consistent length for thematic breaks (horizontal rules) in Markdown.",
|
|
9744
10673
|
categories: ["standard"],
|
|
9745
|
-
listCategory: "
|
|
10674
|
+
listCategory: "Decorative"
|
|
9746
10675
|
},
|
|
9747
10676
|
fixable: "code",
|
|
9748
10677
|
hasSuggestions: false,
|
|
@@ -9808,7 +10737,7 @@ var thematic_break_sequence_pattern_default = createRule("thematic-break-sequenc
|
|
|
9808
10737
|
docs: {
|
|
9809
10738
|
description: "enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown.",
|
|
9810
10739
|
categories: ["standard"],
|
|
9811
|
-
listCategory: "
|
|
10740
|
+
listCategory: "Decorative"
|
|
9812
10741
|
},
|
|
9813
10742
|
fixable: "code",
|
|
9814
10743
|
hasSuggestions: false,
|
|
@@ -9905,6 +10834,9 @@ const rules$1 = [
|
|
|
9905
10834
|
sort_definitions_default,
|
|
9906
10835
|
strikethrough_delimiters_style_default,
|
|
9907
10836
|
table_header_casing_default,
|
|
10837
|
+
table_leading_trailing_pipes_default,
|
|
10838
|
+
table_pipe_alignment_default,
|
|
10839
|
+
table_pipe_spacing_default,
|
|
9908
10840
|
thematic_break_character_style_default,
|
|
9909
10841
|
thematic_break_length_default,
|
|
9910
10842
|
thematic_break_sequence_pattern_default
|
|
@@ -9993,6 +10925,9 @@ const rules$2 = {
|
|
|
9993
10925
|
"markdown-preferences/setext-heading-underline-length": "error",
|
|
9994
10926
|
"markdown-preferences/sort-definitions": "error",
|
|
9995
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",
|
|
9996
10931
|
"markdown-preferences/thematic-break-character-style": "error",
|
|
9997
10932
|
"markdown-preferences/thematic-break-length": "error",
|
|
9998
10933
|
"markdown-preferences/thematic-break-sequence-pattern": "error"
|
|
@@ -10005,7 +10940,7 @@ var meta_exports = __export({
|
|
|
10005
10940
|
version: () => version
|
|
10006
10941
|
});
|
|
10007
10942
|
const name = "eslint-plugin-markdown-preferences";
|
|
10008
|
-
const version = "0.
|
|
10943
|
+
const version = "0.25.0";
|
|
10009
10944
|
|
|
10010
10945
|
//#endregion
|
|
10011
10946
|
//#region src/index.ts
|