eslint-plugin-markdown-preferences 0.23.0 → 0.24.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/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { __export } from "./chunk-Cl8Af3a2.js";
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";
@@ -600,12 +600,28 @@ function getBlockquoteLevelFromLine(sourceCode, lineNumber) {
600
600
  const lineText = sourceCode.lines[lineNumber - 1];
601
601
  let prefix = "";
602
602
  let level = 0;
603
+ let width = 0;
604
+ let leadingMarkerOffset = 0;
603
605
  const blockquoteMarkers = /* @__PURE__ */ new Map();
604
606
  for (const c of lineText) {
605
607
  if (c === ">") {
608
+ if (width - leadingMarkerOffset > 3) break;
609
+ leadingMarkerOffset = width + 1;
606
610
  level++;
607
- blockquoteMarkers.set(level, { index: prefix.length });
608
- } else if (c.trim()) break;
611
+ blockquoteMarkers.set(level, { loc: {
612
+ start: {
613
+ line: lineNumber,
614
+ column: prefix.length + 1
615
+ },
616
+ end: {
617
+ line: lineNumber,
618
+ column: prefix.length + 2
619
+ }
620
+ } });
621
+ } else if (!isSpaceOrTab(c)) break;
622
+ if (c === " ") width += 4 - width % 4;
623
+ else width++;
624
+ if (c !== ">" && prefix.at(-1) === ">") leadingMarkerOffset++;
609
625
  prefix += c;
610
626
  }
611
627
  const result = {
@@ -618,6 +634,64 @@ function getBlockquoteLevelFromLine(sourceCode, lineNumber) {
618
634
  return result;
619
635
  }
620
636
 
637
+ //#endregion
638
+ //#region src/utils/width.ts
639
+ /**
640
+ * Get the visual width of the string.
641
+ */
642
+ function getWidth(str) {
643
+ let width = 0;
644
+ for (const c of str) if (c === " ") width += 4 - width % 4;
645
+ else width++;
646
+ return width;
647
+ }
648
+ /**
649
+ * Get a slice of the string by visual width.
650
+ */
651
+ function sliceWidth(str, start, end) {
652
+ const buffer = [...str];
653
+ let width = 0;
654
+ let c;
655
+ while (c = buffer.shift()) {
656
+ if (start <= width) {
657
+ buffer.unshift(c);
658
+ break;
659
+ }
660
+ if (c === " ") width += 4 - width % 4;
661
+ else width++;
662
+ }
663
+ if (buffer.length === 0) return "";
664
+ let result = " ".repeat(width - start);
665
+ if (end == null) return `${result}${buffer.join("")}`;
666
+ while (c = buffer.shift()) {
667
+ let newWidth;
668
+ if (c === " ") newWidth = width + 4 - width % 4;
669
+ else newWidth = width + 1;
670
+ if (end < newWidth) {
671
+ buffer.unshift(c);
672
+ break;
673
+ }
674
+ result += c;
675
+ width = newWidth;
676
+ }
677
+ if (buffer.length === 0) return result;
678
+ result += " ".repeat(end - width);
679
+ return result;
680
+ }
681
+ /**
682
+ * Get the character at the visual width.
683
+ */
684
+ function atWidth(str, target) {
685
+ let width = 0;
686
+ for (const c of str) {
687
+ if (target === width) return c;
688
+ if (target < width) return " ";
689
+ if (c === " ") width += 4 - width % 4;
690
+ else width++;
691
+ }
692
+ return null;
693
+ }
694
+
621
695
  //#endregion
622
696
  //#region src/rules/blockquote-marker-alignment.ts
623
697
  var blockquote_marker_alignment_default = createRule("blockquote-marker-alignment", {
@@ -655,38 +729,33 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
655
729
  const endLine = loc.end.line;
656
730
  const base = getBlockquoteLevelFromLine(sourceCode, startLine).blockquoteMarkers.get(blockquoteLevel);
657
731
  if (!base) return;
732
+ const baseBeforeMarker = sourceCode.lines[startLine - 1].slice(0, base.loc.start.column - 1);
733
+ const baseIndentWidth = getWidth(baseBeforeMarker);
658
734
  for (let lineNumber = startLine + 1; lineNumber <= endLine; lineNumber++) {
659
735
  const marker = getBlockquoteLevelFromLine(sourceCode, lineNumber).blockquoteMarkers.get(blockquoteLevel);
660
736
  if (!marker) continue;
661
- if (base.index === marker.index) continue;
737
+ const indentWidth = getWidth(sourceCode.lines[lineNumber - 1].slice(0, marker.loc.start.column - 1));
738
+ if (baseIndentWidth === indentWidth) continue;
662
739
  blockquoteStack.reported = true;
663
740
  context.report({
664
741
  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
- },
742
+ loc: marker.loc,
675
743
  messageId: "inconsistentAlignment",
676
744
  fix(fixer) {
677
745
  const line = getParsedLines(sourceCode).get(lineNumber);
678
- if (marker.index < base.index) {
679
- const addSpaces = " ".repeat(base.index - marker.index);
680
- return fixer.insertTextBeforeRange([line.range[0] + marker.index, line.range[0] + marker.index], addSpaces);
746
+ if (indentWidth < baseIndentWidth) {
747
+ const addSpaces = " ".repeat(baseIndentWidth - indentWidth);
748
+ return fixer.insertTextBeforeRange([line.range[0] + marker.loc.start.column - 1, line.range[0] + marker.loc.start.column - 1], addSpaces);
681
749
  }
682
- if (blockquoteLevel === 1) {
683
- const expectedSpaces = " ".repeat(base.index);
684
- return fixer.replaceTextRange([line.range[0], line.range[0] + marker.index], expectedSpaces);
750
+ let newBeforeMarker = line.text.slice(0, marker.loc.start.column - 1);
751
+ while (getWidth(newBeforeMarker) > baseIndentWidth) {
752
+ const last = newBeforeMarker.at(-1);
753
+ if (last && isWhitespace(last)) newBeforeMarker = newBeforeMarker.slice(0, -1);
754
+ else return null;
685
755
  }
686
- if (line.text.slice(0, line.range[0] + marker.index).includes(" ")) return null;
687
- let removeStartIndex = marker.index;
688
- for (; removeStartIndex > base.index; removeStartIndex--) if (line.text[removeStartIndex - 1] !== " ") break;
689
- return fixer.removeRange([line.range[0] + removeStartIndex, line.range[0] + marker.index]);
756
+ if (getWidth(newBeforeMarker) < baseIndentWidth) newBeforeMarker += " ".repeat(baseIndentWidth - getWidth(newBeforeMarker));
757
+ if (!baseBeforeMarker.includes(">") || baseBeforeMarker === newBeforeMarker) return fixer.replaceTextRange([line.range[0], line.range[0] + marker.loc.start.column - 1], newBeforeMarker);
758
+ return null;
690
759
  }
691
760
  });
692
761
  }
@@ -714,7 +783,7 @@ function getOtherMarker(unavailableMarker) {
714
783
  /**
715
784
  * Parse rule options.
716
785
  */
717
- function parseOptions$2(options) {
786
+ function parseOptions$3(options) {
718
787
  const primary = options.primary || "-";
719
788
  const secondary = options.secondary || getOtherMarker(primary);
720
789
  if (primary === secondary) throw new Error(`\`primary\` and \`secondary\` cannot be the same (primary: "${primary}", secondary: "${secondary}").`);
@@ -782,7 +851,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
782
851
  },
783
852
  create(context) {
784
853
  const sourceCode = context.sourceCode;
785
- const options = parseOptions$2(context.options[0] || {});
854
+ const options = parseOptions$3(context.options[0] || {});
786
855
  let containerStack = {
787
856
  node: sourceCode.ast,
788
857
  level: 1,
@@ -4334,275 +4403,224 @@ var heading_casing_default = createRule("heading-casing", {
4334
4403
  });
4335
4404
 
4336
4405
  //#endregion
4337
- //#region src/utils/setext-heading.ts
4338
- /**
4339
- * Parse the setext heading.
4340
- */
4341
- function parseSetextHeading(sourceCode, node) {
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);
4406
+ //#region src/utils/character-cursor.ts
4407
+ var CharacterCursor = class {
4408
+ text;
4409
+ constructor(text) {
4410
+ this.text = text;
4349
4411
  }
4350
- const underline = parseUnderline(lines.get(nodeLoc.end.line));
4351
- if (!underline) return null;
4352
- return {
4353
- contentLines,
4354
- underline
4355
- };
4356
- }
4357
- /**
4358
- * Parse the content line of a setext heading.
4359
- */
4360
- function parseContent(line) {
4361
- let prefix = "";
4362
- let spaceBefore = "";
4363
- let suffix = "";
4364
- for (let index = 0; index < line.text.length; index++) {
4365
- const c = line.text[index];
4366
- if (!c.trim()) {
4367
- spaceBefore += c;
4368
- continue;
4412
+ curr() {
4413
+ return this.text[this.index];
4414
+ }
4415
+ currIndex() {
4416
+ return this.index;
4417
+ }
4418
+ setCurrIndex(index) {
4419
+ this.index = index;
4420
+ }
4421
+ isWhitespace(index) {
4422
+ if (index >= this.text.length) return false;
4423
+ const ch = this.text[index];
4424
+ if (isWhitespace(ch)) return true;
4425
+ if (ch !== ">") return false;
4426
+ const prefix = [ch];
4427
+ for (let prev = index - 1; prev >= 0; prev--) {
4428
+ const prevCh$1 = this.text[prev];
4429
+ if (prevCh$1 === "\n") break;
4430
+ if (isSpaceOrTab(prevCh$1) || prevCh$1 === ">") {
4431
+ prefix.unshift(prevCh$1);
4432
+ continue;
4433
+ }
4434
+ return false;
4369
4435
  }
4370
- if (c === ">" && spaceBefore.length < 4) {
4371
- prefix += spaceBefore + c;
4372
- spaceBefore = "";
4373
- continue;
4436
+ let width = 0;
4437
+ let leadingMarkerOffset = 0;
4438
+ let prevCh;
4439
+ for (const currCh of prefix) {
4440
+ if (currCh === ">") {
4441
+ if (width - leadingMarkerOffset > 3) return false;
4442
+ leadingMarkerOffset = width + 1;
4443
+ }
4444
+ if (currCh === " ") width += 4 - width % 4;
4445
+ else width++;
4446
+ if (prevCh === ">" && isSpaceOrTab(currCh)) leadingMarkerOffset++;
4447
+ prevCh = currCh;
4374
4448
  }
4375
- suffix = line.text.slice(index);
4376
- break;
4449
+ return true;
4377
4450
  }
4378
- const content = suffix.trimEnd();
4379
- const spaceAfter = suffix.slice(content.length);
4380
- return {
4381
- text: content,
4382
- range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
4383
- loc: {
4384
- start: {
4385
- line: line.line,
4386
- column: prefix.length + spaceBefore.length + 1
4387
- },
4388
- end: {
4389
- line: line.line,
4390
- column: prefix.length + spaceBefore.length + content.length + 1
4451
+ };
4452
+ var ForwardCharacterCursor = class extends CharacterCursor {
4453
+ index;
4454
+ constructor(text) {
4455
+ super(text);
4456
+ this.index = 0;
4457
+ }
4458
+ next() {
4459
+ this.index++;
4460
+ return this.text[this.index];
4461
+ }
4462
+ finished() {
4463
+ return this.index >= this.text.length;
4464
+ }
4465
+ skipSpaces() {
4466
+ while (this.index < this.text.length && this.isWhitespace(this.index)) this.index++;
4467
+ }
4468
+ /**
4469
+ * Skip until the end by the given condition
4470
+ */
4471
+ skipUntilEnd(checkEnd) {
4472
+ while (this.index < this.text.length) {
4473
+ const c = this.text[this.index];
4474
+ if (checkEnd(c, this.index)) return true;
4475
+ this.index++;
4476
+ if (c !== "\\") continue;
4477
+ if (this.index < this.text.length && (this.text[this.index] === "\\" || checkEnd(this.text[this.index], this.index)) && !isWhitespace(this.text[this.index])) this.index++;
4478
+ }
4479
+ return false;
4480
+ }
4481
+ };
4482
+ var BackwardCharacterCursor = class extends CharacterCursor {
4483
+ index;
4484
+ constructor(text) {
4485
+ super(text);
4486
+ this.index = text.length - 1;
4487
+ }
4488
+ prev() {
4489
+ this.index--;
4490
+ return this.text[this.index];
4491
+ }
4492
+ finished() {
4493
+ return this.index < 0;
4494
+ }
4495
+ skipSpaces() {
4496
+ while (this.index >= 0 && this.isWhitespace(this.index)) this.index--;
4497
+ }
4498
+ /**
4499
+ * Skip until the start by the given condition
4500
+ */
4501
+ skipUntilStart(checkStart) {
4502
+ while (this.index >= 0) {
4503
+ const c = this.text[this.index];
4504
+ if (checkStart(c, this.index)) {
4505
+ if (this.index > 1 && !isWhitespace(c)) {
4506
+ let escapeText = "";
4507
+ while (this.text.endsWith(`${escapeText}\\`, this.index)) escapeText += "\\";
4508
+ if (escapeText.length % 2 === 1) {
4509
+ this.index -= escapeText.length + 1;
4510
+ continue;
4511
+ }
4512
+ }
4513
+ return true;
4391
4514
  }
4392
- },
4393
- raws: {
4394
- prefix,
4395
- spaceBefore,
4396
- spaceAfter
4515
+ this.index--;
4397
4516
  }
4517
+ return false;
4518
+ }
4519
+ };
4520
+
4521
+ //#endregion
4522
+ //#region src/utils/link-definition.ts
4523
+ /**
4524
+ * Parse the link definition.
4525
+ */
4526
+ function parseLinkDefinition(sourceCode, node) {
4527
+ const text = sourceCode.getText(node);
4528
+ const parsed = parseLinkDefinitionFromText(text);
4529
+ if (!parsed) return null;
4530
+ const nodeRange = sourceCode.getRange(node);
4531
+ const labelRange = [nodeRange[0] + parsed.label.range[0], nodeRange[0] + parsed.label.range[1]];
4532
+ const destinationRange = [nodeRange[0] + parsed.destination.range[0], nodeRange[0] + parsed.destination.range[1]];
4533
+ return {
4534
+ label: {
4535
+ text: parsed.label.text,
4536
+ range: labelRange,
4537
+ loc: getSourceLocationFromRange(sourceCode, node, labelRange)
4538
+ },
4539
+ destination: {
4540
+ type: parsed.destination.type,
4541
+ text: parsed.destination.text,
4542
+ range: destinationRange,
4543
+ loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
4544
+ },
4545
+ title: parsed.title ? {
4546
+ type: parsed.title.type,
4547
+ text: parsed.title.text,
4548
+ range: [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]],
4549
+ loc: getSourceLocationFromRange(sourceCode, node, [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]])
4550
+ } : null
4398
4551
  };
4399
4552
  }
4400
4553
  /**
4401
- * Parse the underline of a setext heading.
4554
+ * Parse the link definition from the given text.
4402
4555
  */
4403
- function parseUnderline(line) {
4404
- let marker = null;
4405
- let underlineText = "";
4406
- let prefix = "";
4407
- let spaceBefore = "";
4408
- let spaceAfter = "";
4409
- for (let index = line.text.length - 1; index >= 0; index--) {
4410
- const c = line.text[index];
4411
- if (!marker) {
4412
- if (c === "=" || c === "-") {
4413
- underlineText = c + underlineText;
4414
- marker = c;
4415
- } else if (!c.trim()) spaceAfter = c + spaceAfter;
4416
- else return null;
4417
- continue;
4418
- }
4419
- if (c === marker) {
4420
- underlineText = c + spaceBefore + underlineText;
4421
- spaceBefore = "";
4422
- } else if (!c.trim()) spaceBefore = c + spaceBefore;
4423
- else {
4424
- prefix = line.text.slice(0, index + 1);
4425
- break;
4426
- }
4556
+ function parseLinkDefinitionFromText(text) {
4557
+ if (!text.startsWith("[")) return null;
4558
+ const cursor = new ForwardCharacterCursor(text);
4559
+ const labelStartIndex = 0;
4560
+ cursor.next();
4561
+ if (!cursor.skipUntilEnd((c) => c === "]")) return null;
4562
+ cursor.next();
4563
+ const labelRange = [labelStartIndex, cursor.currIndex()];
4564
+ if (!text.slice(labelRange[0] + 1, labelRange[1] - 1).trim()) return null;
4565
+ if (cursor.curr() !== ":") return null;
4566
+ const label = {
4567
+ text: text.slice(...labelRange),
4568
+ range: labelRange
4569
+ };
4570
+ cursor.next();
4571
+ cursor.skipSpaces();
4572
+ let destination;
4573
+ const destinationStartIndex = cursor.currIndex();
4574
+ if (cursor.curr() === "<") {
4575
+ cursor.next();
4576
+ if (!cursor.skipUntilEnd((c) => c === ">")) return null;
4577
+ cursor.next();
4578
+ const destinationRange = [destinationStartIndex, cursor.currIndex()];
4579
+ destination = {
4580
+ type: "pointy-bracketed",
4581
+ text: text.slice(...destinationRange),
4582
+ range: destinationRange
4583
+ };
4584
+ } else {
4585
+ if (cursor.finished()) return null;
4586
+ cursor.skipUntilEnd((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c));
4587
+ const destinationRange = [destinationStartIndex, cursor.currIndex()];
4588
+ destination = {
4589
+ type: "bare",
4590
+ text: text.slice(...destinationRange),
4591
+ range: destinationRange
4592
+ };
4427
4593
  }
4428
- if (!marker) return null;
4429
- const underlineLoc = {
4430
- start: {
4431
- line: line.line,
4432
- column: prefix.length + spaceBefore.length + 1
4433
- },
4434
- end: {
4435
- line: line.line,
4436
- column: prefix.length + spaceBefore.length + underlineText.length + 1
4437
- }
4594
+ cursor.skipSpaces();
4595
+ if (cursor.finished()) return {
4596
+ label,
4597
+ destination,
4598
+ title: null
4438
4599
  };
4600
+ let title;
4601
+ const titleStartIndex = cursor.currIndex();
4602
+ const startChar = cursor.curr();
4603
+ if (startChar === "'" || startChar === "\"" || startChar === "(") {
4604
+ cursor.next();
4605
+ const endChar = startChar === "(" ? ")" : startChar;
4606
+ if (!cursor.skipUntilEnd((c) => c === endChar)) return null;
4607
+ cursor.next();
4608
+ const titleRange = [titleStartIndex, cursor.currIndex()];
4609
+ title = {
4610
+ type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
4611
+ text: text.slice(...titleRange),
4612
+ range: titleRange
4613
+ };
4614
+ } else return null;
4615
+ cursor.skipSpaces();
4616
+ if (!cursor.finished()) return null;
4439
4617
  return {
4440
- text: underlineText,
4441
- range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
4442
- loc: underlineLoc,
4443
- marker,
4444
- raws: {
4445
- prefix,
4446
- spaceBefore,
4447
- spaceAfter
4448
- }
4618
+ label,
4619
+ destination,
4620
+ title
4449
4621
  };
4450
4622
  }
4451
4623
 
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
4624
  //#endregion
4607
4625
  //#region src/utils/link.ts
4608
4626
  /**
@@ -4653,92 +4671,73 @@ function parseInlineLink(sourceCode, node) {
4653
4671
  * Parse the inline link destination and link title from the given text.
4654
4672
  */
4655
4673
  function parseInlineLinkDestAndTitleFromText(text) {
4656
- let index = 0;
4657
- if (text[index] !== "(") return null;
4658
- index++;
4659
- skipSpaces();
4674
+ if (!text.startsWith("(")) return null;
4675
+ const cursor = new ForwardCharacterCursor(text);
4676
+ cursor.next();
4677
+ cursor.skipSpaces();
4660
4678
  let destination;
4661
- const destinationStartIndex = index;
4662
- if (text[index] === "<") {
4663
- index++;
4664
- if (!skipUntilEnd((c) => c === ">")) return null;
4665
- index++;
4666
- const destinationRange = [destinationStartIndex, index];
4679
+ const destinationStartIndex = cursor.currIndex();
4680
+ if (cursor.curr() === "<") {
4681
+ cursor.next();
4682
+ if (!cursor.skipUntilEnd((c) => c === ">")) return null;
4683
+ cursor.next();
4684
+ const destinationRange = [destinationStartIndex, cursor.currIndex()];
4667
4685
  destination = {
4668
4686
  type: "pointy-bracketed",
4669
4687
  text: text.slice(...destinationRange),
4670
4688
  range: destinationRange
4671
4689
  };
4672
4690
  } else {
4673
- if (text.length <= index) return null;
4674
- skipUntilEnd((c) => isWhitespace(c) || isAsciiControlCharacter(c) || c === ")");
4675
- const destinationRange = [destinationStartIndex, index];
4691
+ if (cursor.finished()) return null;
4692
+ cursor.skipUntilEnd((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c) || c === ")");
4693
+ const destinationRange = [destinationStartIndex, cursor.currIndex()];
4676
4694
  destination = {
4677
4695
  type: "bare",
4678
4696
  text: text.slice(...destinationRange),
4679
4697
  range: destinationRange
4680
4698
  };
4681
4699
  }
4682
- skipSpaces();
4683
- if (text[index] === ")") {
4684
- const closingParenStartIndex$1 = index;
4685
- index++;
4686
- skipSpaces();
4687
- if (index < text.length) return null;
4700
+ cursor.skipSpaces();
4701
+ if (cursor.curr() === ")") {
4702
+ const closingParenStartIndex$1 = cursor.currIndex();
4703
+ cursor.next();
4704
+ cursor.skipSpaces();
4705
+ if (!cursor.finished()) return null;
4688
4706
  return {
4689
4707
  openingParen: { range: [0, 1] },
4690
4708
  destination,
4691
4709
  title: null,
4692
- closingParen: { range: [closingParenStartIndex$1, index] }
4710
+ closingParen: { range: [closingParenStartIndex$1, closingParenStartIndex$1 + 1] }
4693
4711
  };
4694
4712
  }
4695
- if (text.length <= index) return null;
4713
+ if (cursor.finished()) return null;
4696
4714
  let title;
4697
- const titleStartIndex = index;
4698
- const startChar = text[index];
4715
+ const titleStartIndex = cursor.currIndex();
4716
+ const startChar = cursor.curr();
4699
4717
  if (startChar === "'" || startChar === "\"" || startChar === "(") {
4700
- index++;
4718
+ cursor.next();
4701
4719
  const endChar = startChar === "(" ? ")" : startChar;
4702
- if (!skipUntilEnd((c) => c === endChar)) return null;
4703
- index++;
4704
- const titleRange = [titleStartIndex, index];
4720
+ if (!cursor.skipUntilEnd((c) => c === endChar)) return null;
4721
+ cursor.next();
4722
+ const titleRange = [titleStartIndex, cursor.currIndex()];
4705
4723
  title = {
4706
4724
  type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
4707
4725
  text: text.slice(...titleRange),
4708
4726
  range: titleRange
4709
4727
  };
4710
4728
  } else return null;
4711
- skipSpaces();
4712
- if (text[index] !== ")") return null;
4713
- const closingParenStartIndex = index;
4714
- index++;
4715
- skipSpaces();
4716
- if (index < text.length) return null;
4729
+ cursor.skipSpaces();
4730
+ if (cursor.curr() !== ")") return null;
4731
+ const closingParenStartIndex = cursor.currIndex();
4732
+ cursor.next();
4733
+ cursor.skipSpaces();
4734
+ if (!cursor.finished()) return null;
4717
4735
  return {
4718
4736
  openingParen: { range: [0, 1] },
4719
4737
  destination,
4720
4738
  title,
4721
- closingParen: { range: [closingParenStartIndex, index] }
4739
+ closingParen: { range: [closingParenStartIndex, closingParenStartIndex + 1] }
4722
4740
  };
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
4741
  }
4743
4742
 
4744
4743
  //#endregion
@@ -4845,58 +4844,57 @@ function parseImage(sourceCode, node) {
4845
4844
  */
4846
4845
  function parseImageFromText(text) {
4847
4846
  if (!text.startsWith("![")) return null;
4848
- let index = text.length - 1;
4849
- skipSpaces();
4850
- if (text[index] !== ")") return null;
4851
- const closingParenStartIndex = index;
4852
- index--;
4853
- skipSpaces();
4847
+ const cursor = new BackwardCharacterCursor(text);
4848
+ cursor.skipSpaces();
4849
+ if (cursor.curr() !== ")") return null;
4850
+ const closingParenStartIndex = cursor.currIndex();
4851
+ cursor.prev();
4852
+ cursor.skipSpaces();
4854
4853
  let title = null;
4855
- const titleEndIndex = index + 1;
4856
- const endChar = text[index];
4854
+ const titleEndIndex = cursor.currIndex() + 1;
4855
+ const endChar = cursor.curr();
4857
4856
  if (endChar === "'" || endChar === "\"" || endChar === ")") {
4858
- index--;
4857
+ cursor.prev();
4859
4858
  const startChar = endChar === ")" ? "(" : endChar;
4860
- if (skipUntilStart((c) => c === startChar)) {
4861
- const titleRange = [index, titleEndIndex];
4862
- index--;
4859
+ if (cursor.skipUntilStart((c) => c === startChar)) {
4860
+ const titleRange = [cursor.currIndex(), titleEndIndex];
4861
+ cursor.prev();
4863
4862
  title = {
4864
4863
  type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
4865
4864
  text: text.slice(...titleRange),
4866
4865
  range: titleRange
4867
4866
  };
4868
- skipSpaces();
4867
+ cursor.skipSpaces();
4869
4868
  }
4870
4869
  }
4871
- if (title == null) index = titleEndIndex - 1;
4870
+ if (title == null) cursor.setCurrIndex(titleEndIndex - 1);
4872
4871
  let destination;
4873
- const destinationEndIndex = index + 1;
4874
- if (text[index] === ">") {
4875
- index--;
4876
- if (!skipUntilStart((c) => c === "<")) return null;
4877
- const destinationRange = [index, destinationEndIndex];
4878
- index--;
4872
+ const destinationEndIndex = cursor.currIndex() + 1;
4873
+ if (cursor.curr() === ">") {
4874
+ cursor.prev();
4875
+ if (!cursor.skipUntilStart((c) => c === "<")) return null;
4876
+ const destinationRange = [cursor.currIndex(), destinationEndIndex];
4877
+ cursor.prev();
4879
4878
  destination = {
4880
4879
  type: "pointy-bracketed",
4881
4880
  text: text.slice(...destinationRange),
4882
4881
  range: destinationRange
4883
4882
  };
4884
4883
  } else {
4885
- if (text.length <= index) return null;
4886
- skipUntilStart((c) => isWhitespace(c) || isAsciiControlCharacter(c) || c === "(");
4887
- const destinationRange = [index + 1, destinationEndIndex];
4884
+ if (cursor.finished()) return null;
4885
+ cursor.skipUntilStart((c, i) => cursor.isWhitespace(i) || isAsciiControlCharacter(c) || c === "(");
4886
+ const destinationRange = [cursor.currIndex() + 1, destinationEndIndex];
4888
4887
  destination = {
4889
4888
  type: "bare",
4890
4889
  text: text.slice(...destinationRange),
4891
4890
  range: destinationRange
4892
4891
  };
4893
4892
  }
4894
- skipSpaces();
4895
- if (text[index] !== "(") return null;
4896
- const openingParenStartIndex = index;
4897
- index--;
4898
- if (text[index] !== "]") return null;
4899
- const textRange = [1, index + 1];
4893
+ cursor.skipSpaces();
4894
+ if (cursor.curr() !== "(") return null;
4895
+ const openingParenStartIndex = cursor.currIndex();
4896
+ if (cursor.prev() !== "]") return null;
4897
+ const textRange = [1, cursor.currIndex() + 1];
4900
4898
  return {
4901
4899
  openingParen: { range: [openingParenStartIndex, openingParenStartIndex + 1] },
4902
4900
  text: { range: textRange },
@@ -4904,33 +4902,6 @@ function parseImageFromText(text) {
4904
4902
  title,
4905
4903
  closingParen: { range: [closingParenStartIndex, closingParenStartIndex + 1] }
4906
4904
  };
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
4905
  }
4935
4906
 
4936
4907
  //#endregion
@@ -4990,21 +4961,20 @@ function parseImageReference(sourceCode, node) {
4990
4961
  */
4991
4962
  function parseFullImageReferenceFromText(text) {
4992
4963
  if (!text.startsWith("![")) return null;
4993
- let index = text.length - 1;
4994
- skipSpaces();
4995
- if (text[index] !== "]") return null;
4996
- const labelEndIndex = index + 1;
4997
- index--;
4998
- skipSpaces();
4999
- if (!skipUntilStart((c) => c === "[")) return null;
5000
- const labelRange = [index, labelEndIndex];
5001
- index--;
4964
+ const cursor = new BackwardCharacterCursor(text);
4965
+ cursor.skipSpaces();
4966
+ if (cursor.curr() !== "]") return null;
4967
+ const labelEndIndex = cursor.currIndex() + 1;
4968
+ cursor.prev();
4969
+ cursor.skipSpaces();
4970
+ if (!cursor.skipUntilStart((c) => c === "[")) return null;
4971
+ const labelRange = [cursor.currIndex(), labelEndIndex];
5002
4972
  const label = {
5003
4973
  text: text.slice(...labelRange),
5004
4974
  range: labelRange
5005
4975
  };
5006
- if (text[index] !== "]") return null;
5007
- const textRange = [1, index + 1];
4976
+ if (cursor.prev() !== "]") return null;
4977
+ const textRange = [1, cursor.currIndex() + 1];
5008
4978
  return {
5009
4979
  text: {
5010
4980
  text: text.slice(...textRange),
@@ -5012,156 +4982,1208 @@ function parseFullImageReferenceFromText(text) {
5012
4982
  },
5013
4983
  label
5014
4984
  };
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
4985
  }
5043
4986
 
5044
4987
  //#endregion
5045
- //#region src/utils/link-definition.ts
4988
+ //#region src/utils/list-item.ts
5046
4989
  /**
5047
- * Parse the link definition.
4990
+ * Parse the list item.
5048
4991
  */
5049
- function parseLinkDefinition(sourceCode, node) {
5050
- const text = sourceCode.getText(node);
5051
- const parsed = parseLinkDefinitionFromText(text);
5052
- if (!parsed) return null;
4992
+ function parseListItem(sourceCode, node) {
4993
+ const marker = getListItemMarker(sourceCode, node);
5053
4994
  const nodeRange = sourceCode.getRange(node);
5054
- const labelRange = [nodeRange[0] + parsed.label.range[0], nodeRange[0] + parsed.label.range[1]];
5055
- const destinationRange = [nodeRange[0] + parsed.destination.range[0], nodeRange[0] + parsed.destination.range[1]];
4995
+ const markerRange = [nodeRange[0], nodeRange[0] + marker.raw.length];
4996
+ const parsedMarker = marker.kind === "." || marker.kind === ")" ? {
4997
+ kind: marker.kind,
4998
+ text: marker.raw,
4999
+ value: marker.sequence.value,
5000
+ range: markerRange,
5001
+ loc: getSourceLocationFromRange(sourceCode, node, markerRange)
5002
+ } : {
5003
+ kind: marker.kind,
5004
+ text: marker.raw,
5005
+ range: markerRange,
5006
+ loc: getSourceLocationFromRange(sourceCode, node, markerRange)
5007
+ };
5008
+ if (node.checked == null) return {
5009
+ marker: parsedMarker,
5010
+ taskListItemMarker: null
5011
+ };
5012
+ for (let index = nodeRange[0] + marker.raw.length; index < nodeRange[1]; index++) {
5013
+ const c = sourceCode.text[index];
5014
+ if (isSpaceOrTab(c)) continue;
5015
+ if (c !== "[") break;
5016
+ const middle = sourceCode.text[index + 1];
5017
+ if (middle !== "x" && middle !== " ") break;
5018
+ if (sourceCode.text[index + 2] !== "]") break;
5019
+ const taskListItemMarkerRange = [index, index + 3];
5020
+ return {
5021
+ marker: parsedMarker,
5022
+ taskListItemMarker: {
5023
+ text: sourceCode.text.slice(...taskListItemMarkerRange),
5024
+ range: taskListItemMarkerRange,
5025
+ loc: getSourceLocationFromRange(sourceCode, node, taskListItemMarkerRange)
5026
+ }
5027
+ };
5028
+ }
5056
5029
  return {
5057
- label: {
5058
- text: parsed.label.text,
5059
- range: labelRange,
5060
- loc: getSourceLocationFromRange(sourceCode, node, labelRange)
5061
- },
5062
- destination: {
5063
- type: parsed.destination.type,
5064
- text: parsed.destination.text,
5065
- range: destinationRange,
5066
- loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
5030
+ marker: parsedMarker,
5031
+ taskListItemMarker: null
5032
+ };
5033
+ }
5034
+
5035
+ //#endregion
5036
+ //#region src/rules/indent.ts
5037
+ /**
5038
+ * Parse options.
5039
+ */
5040
+ function parseOptions$2(options) {
5041
+ const listItems = options?.listItems;
5042
+ return { listItems: {
5043
+ first: listItems?.first ?? 1,
5044
+ other: listItems?.other ?? "first",
5045
+ relativeTo: listItems?.relativeTo ?? "taskListMarkerEnd"
5046
+ } };
5047
+ }
5048
+ var indent_default = createRule("indent", {
5049
+ meta: {
5050
+ type: "layout",
5051
+ docs: {
5052
+ description: "enforce consistent indentation in Markdown files",
5053
+ categories: ["standard"],
5054
+ listCategory: "Stylistic"
5067
5055
  },
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
5056
+ fixable: "whitespace",
5057
+ hasSuggestions: false,
5058
+ schema: [{
5059
+ type: "object",
5060
+ properties: { listItems: {
5061
+ type: "object",
5062
+ properties: {
5063
+ first: { anyOf: [{ const: "ignore" }, {
5064
+ type: "number",
5065
+ minimum: 1
5066
+ }] },
5067
+ other: { anyOf: [{ enum: ["first", "minimum"] }, {
5068
+ type: "number",
5069
+ minimum: 1
5070
+ }] },
5071
+ relativeTo: { enum: [
5072
+ "markerStart",
5073
+ "markerEnd",
5074
+ "taskListMarkerStart",
5075
+ "taskListMarkerEnd"
5076
+ ] }
5077
+ },
5078
+ additionalProperties: false
5079
+ } },
5080
+ additionalProperties: false
5081
+ }],
5082
+ messages: {
5083
+ wrongIndentation: "Expected indentation of {{expected}} but found {{actual}}.",
5084
+ wrongIndentationFromBlockquoteMarker: "Expected indentation of {{expected}} from the blockquote marker but found {{actual}}.",
5085
+ wrongIndentationFromListMarker: "Expected indentation of {{expected}} from the list marker but found {{actual}}.",
5086
+ wrongIndentationWithTab: "Expected indentation of {{expected}} but found {{actual}} (tab width is 4).",
5087
+ wrongIndentationFromBlockquoteMarkerWithTab: "Expected indentation of {{expected}} from the blockquote marker but found {{actual}} (tab width is 4).",
5088
+ wrongIndentationFromListMarkerWithTab: "Expected indentation of {{expected}} from the list marker but found {{actual}} (tab width is 4)."
5089
+ }
5090
+ },
5091
+ create(context) {
5092
+ const sourceCode = context.sourceCode;
5093
+ const options = parseOptions$2(context.options[0]);
5094
+ class AbsBlockStack {
5095
+ violations = [];
5096
+ getCurrentBlockquote() {
5097
+ let block = this;
5098
+ while (block) {
5099
+ if (block.type === "blockquote") return block;
5100
+ block = block.upper;
5101
+ }
5102
+ return null;
5103
+ }
5104
+ getCurrentListItemAtLine(lineNumber) {
5105
+ let block = this;
5106
+ while (block) {
5107
+ if (block.type === "blockquote") return null;
5108
+ if (block.type === "listItem") {
5109
+ if (sourceCode.getLoc(block.node).start.line === lineNumber) return block;
5110
+ }
5111
+ block = block.upper;
5112
+ }
5113
+ return null;
5114
+ }
5115
+ }
5116
+ class RootStack extends AbsBlockStack {
5117
+ type = "root";
5118
+ node;
5119
+ upper = null;
5120
+ constructor(node) {
5121
+ super();
5122
+ this.node = node;
5123
+ }
5124
+ getExpectedIndent() {
5125
+ return 0;
5126
+ }
5127
+ }
5128
+ class BlockquoteStack extends AbsBlockStack {
5129
+ type = "blockquote";
5130
+ node;
5131
+ upper;
5132
+ _expectedIndent;
5133
+ _markerIndent;
5134
+ constructor(node) {
5135
+ super();
5136
+ this.node = node;
5137
+ this.upper = blockStack;
5138
+ }
5139
+ getExpectedIndent() {
5140
+ if (this._expectedIndent == null) this._expectedIndent = this.getMarkerIndent() + 1 + 1;
5141
+ return this._expectedIndent;
5142
+ }
5143
+ getMarkerIndent() {
5144
+ if (this._markerIndent == null) {
5145
+ const loc = sourceCode.getLoc(this.node);
5146
+ this._markerIndent = getWidth(sourceCode.lines[loc.start.line - 1].slice(0, loc.start.column - 1));
5147
+ }
5148
+ return this._markerIndent;
5149
+ }
5150
+ }
5151
+ class ListItemStack extends AbsBlockStack {
5152
+ type = "listItem";
5153
+ node;
5154
+ upper;
5155
+ _expectedIndentForFirstLine;
5156
+ _expectedIndentForOtherLines;
5157
+ _parsed;
5158
+ nodeLoc;
5159
+ constructor(node) {
5160
+ super();
5161
+ this.node = node;
5162
+ this.upper = blockStack;
5163
+ this.nodeLoc = sourceCode.getLoc(this.node);
5164
+ }
5165
+ getParsedListItem() {
5166
+ return this._parsed ??= parseListItem(sourceCode, this.node);
5167
+ }
5168
+ getExpectedIndent({ lineNumber, block }) {
5169
+ const loc = this.nodeLoc;
5170
+ if (lineNumber === loc.start.line) return this.getExpectedIndentForFirstLine();
5171
+ const expected = this.getExpectedIndentAfterFirstLine();
5172
+ if (expected === "ignore") return "ignore";
5173
+ if (!block) return expected;
5174
+ const actualFirstLineIndent = this.getActualFirstLineIndent({ withoutTaskListMarker: true });
5175
+ if (actualFirstLineIndent != null) return Math.min(expected, actualFirstLineIndent + 3);
5176
+ return expected;
5177
+ }
5178
+ getExpectedIndentForFirstLine() {
5179
+ if (options.listItems.first === "ignore") return "ignore";
5180
+ if (this._expectedIndentForFirstLine != null) return this._expectedIndentForFirstLine;
5181
+ const loc = this.nodeLoc;
5182
+ const parsed = this.getParsedListItem();
5183
+ const lineText = sourceCode.lines[loc.start.line - 1];
5184
+ if (options.listItems.relativeTo === "markerStart") {
5185
+ const baseIndent = getWidth(lineText.slice(0, loc.start.column - 1));
5186
+ return this._expectedIndentForFirstLine = Math.max(baseIndent + options.listItems.first, baseIndent + parsed.marker.text.length + 1);
5187
+ }
5188
+ if (options.listItems.relativeTo === "taskListMarkerEnd" && parsed.taskListItemMarker) return this._expectedIndentForFirstLine = getWidth(lineText.slice(0, parsed.taskListItemMarker.loc.end.column - 1)) + options.listItems.first;
5189
+ return this._expectedIndentForFirstLine = getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + options.listItems.first;
5190
+ }
5191
+ getExpectedIndentAfterFirstLine() {
5192
+ if (this._expectedIndentForOtherLines != null) return this._expectedIndentForOtherLines;
5193
+ const loc = this.nodeLoc;
5194
+ if (options.listItems.other === "first") {
5195
+ const firstLineIndent = this.getExpectedIndentForFirstLine();
5196
+ if (firstLineIndent === "ignore") {
5197
+ const actualFirstLineIndent = this.getActualFirstLineIndent();
5198
+ if (actualFirstLineIndent != null) return this._expectedIndentForOtherLines = actualFirstLineIndent;
5199
+ for (const child of this.node.children) {
5200
+ const childLoc = sourceCode.getLoc(child);
5201
+ if (loc.start.line < childLoc.start.line) return this._expectedIndentForOtherLines = getWidth(sourceCode.lines[childLoc.start.line - 1].slice(0, childLoc.start.column - 1));
5202
+ }
5203
+ }
5204
+ return this._expectedIndentForOtherLines = firstLineIndent;
5205
+ }
5206
+ if (options.listItems.other === "minimum") return this._expectedIndentForOtherLines = this.getMinimumLineIndent();
5207
+ const lineText = sourceCode.lines[loc.start.line - 1];
5208
+ if (options.listItems.relativeTo === "markerStart") {
5209
+ const baseIndent = getWidth(lineText.slice(0, loc.start.column - 1));
5210
+ const minimumLineIndent = this.getMinimumLineIndent();
5211
+ return this._expectedIndentForOtherLines = Math.max(baseIndent + options.listItems.other, minimumLineIndent);
5212
+ }
5213
+ const parsed = this.getParsedListItem();
5214
+ if (options.listItems.relativeTo === "taskListMarkerEnd" && parsed.taskListItemMarker) return this._expectedIndentForOtherLines = getWidth(lineText.slice(0, parsed.taskListItemMarker.loc.end.column - 1)) + options.listItems.other;
5215
+ return this._expectedIndentForOtherLines = getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + options.listItems.other;
5216
+ }
5217
+ getMinimumLineIndent() {
5218
+ const actualFirstLineIndent = this.getActualFirstLineIndent({ withoutTaskListMarker: true });
5219
+ if (actualFirstLineIndent != null) return this._expectedIndentForOtherLines = actualFirstLineIndent;
5220
+ const parsed = this.getParsedListItem();
5221
+ const lineText = sourceCode.lines[parsed.marker.loc.end.line - 1];
5222
+ return getWidth(lineText.slice(0, parsed.marker.loc.end.column - 1)) + 1;
5223
+ }
5224
+ getActualFirstLineIndent({ withoutTaskListMarker = false } = {}) {
5225
+ const parsed = this.getParsedListItem();
5226
+ const markerEndPos = withoutTaskListMarker ? parsed.marker.loc.end : parsed.taskListItemMarker?.loc.end ?? parsed.marker.loc.end;
5227
+ const lineText = sourceCode.lines[markerEndPos.line - 1];
5228
+ const afterMarkerText = lineText.slice(markerEndPos.column - 1);
5229
+ const trimmedAfterMarkerText = afterMarkerText.trimStart();
5230
+ if (trimmedAfterMarkerText) {
5231
+ const afterMarkerSpacesLength = afterMarkerText.length - trimmedAfterMarkerText.length;
5232
+ return getWidth(lineText.slice(0, markerEndPos.column - 1 + afterMarkerSpacesLength));
5233
+ }
5234
+ return null;
5235
+ }
5236
+ }
5237
+ class FootnoteDefinitionStack extends AbsBlockStack {
5238
+ type = "footnoteDefinition";
5239
+ node;
5240
+ upper;
5241
+ _expectedIndent;
5242
+ constructor(node) {
5243
+ super();
5244
+ this.node = node;
5245
+ this.upper = blockStack;
5246
+ }
5247
+ getExpectedIndent() {
5248
+ if (this._expectedIndent == null) {
5249
+ const loc = sourceCode.getLoc(this.node);
5250
+ this._expectedIndent = getWidth(sourceCode.lines[loc.start.line - 1].slice(0, loc.start.column - 1)) + 4;
5251
+ }
5252
+ return this._expectedIndent;
5253
+ }
5254
+ }
5255
+ class LinkStack extends AbsBlockStack {
5256
+ type = "link";
5257
+ node;
5258
+ upper;
5259
+ nodeLoc;
5260
+ constructor(node) {
5261
+ super();
5262
+ this.node = node;
5263
+ this.upper = blockStack;
5264
+ this.nodeLoc = sourceCode.getLoc(this.node);
5265
+ }
5266
+ getExpectedIndent(ctx) {
5267
+ const loc = this.nodeLoc;
5268
+ if (ctx.lineNumber === loc.start.line) return this.upper.getExpectedIndent(ctx);
5269
+ const base = this.upper.getExpectedIndent({
5270
+ lineNumber: loc.start.line,
5271
+ block: ctx.block
5272
+ });
5273
+ if (base === "ignore") return "ignore";
5274
+ return base + 2;
5275
+ }
5276
+ }
5277
+ let blockStack = new RootStack(sourceCode.ast);
5278
+ const reportedLocations = Object.create(null);
5279
+ /**
5280
+ * Reported locations (line and column) to avoid duplicate reports.
5281
+ */
5282
+ function reportIncorrectIndent(violation) {
5283
+ const reportedColumns = reportedLocations[violation.loc.start.line] ??= /* @__PURE__ */ new Set();
5284
+ if (reportedColumns.has(violation.loc.start.column)) return;
5285
+ reportedColumns.add(violation.loc.start.column);
5286
+ blockStack.violations.push(violation);
5287
+ }
5288
+ /**
5289
+ * Flush violations to the context.
5290
+ */
5291
+ function flushViolations({ violations }) {
5292
+ for (const violation of violations) context.report({
5293
+ loc: violation.loc,
5294
+ messageId: violation.messageId,
5295
+ data: {
5296
+ expected: String(violation.data.expected),
5297
+ actual: String(violation.data.actual)
5298
+ },
5299
+ fix(fixer) {
5300
+ const result = [];
5301
+ for (const { fix } of violations) result.push(...fix(fixer));
5302
+ return result;
5303
+ }
5304
+ });
5305
+ }
5306
+ return {
5307
+ thematicBreak: verifyThematicBreak,
5308
+ heading: verifyHeading,
5309
+ code: verifyCodeBlock,
5310
+ html: verifyHtml,
5311
+ definition: verifyLinkDefinition,
5312
+ table: verifyTable,
5313
+ list: verifyList,
5314
+ inlineCode: verifyInlineCode,
5315
+ emphasis: verifyEmphasisOrStrongOrDelete,
5316
+ strong: verifyEmphasisOrStrongOrDelete,
5317
+ delete: verifyEmphasisOrStrongOrDelete,
5318
+ image: verifyImage,
5319
+ imageReference: verifyImageReference,
5320
+ footnoteReference: verifyInline,
5321
+ break: verifyInline,
5322
+ text: verifyText,
5323
+ blockquote(node) {
5324
+ verifyBlockquote(node);
5325
+ blockStack = new BlockquoteStack(node);
5326
+ },
5327
+ listItem(node) {
5328
+ blockStack = new ListItemStack(node);
5329
+ },
5330
+ footnoteDefinition(node) {
5331
+ verifyFootnoteDefinition(node);
5332
+ blockStack = new FootnoteDefinitionStack(node);
5333
+ },
5334
+ link(node) {
5335
+ verifyLink(node);
5336
+ blockStack = new LinkStack(node);
5337
+ },
5338
+ linkReference(node) {
5339
+ verifyLinkReference(node);
5340
+ blockStack = new LinkStack(node);
5341
+ },
5342
+ "blockquote, listItem, footnoteDefinition, link, linkReference:exit"() {
5343
+ flushViolations(blockStack);
5344
+ blockStack = blockStack.upper;
5345
+ },
5346
+ "root:exit"() {
5347
+ flushViolations(blockStack);
5348
+ }
5349
+ };
5350
+ /**
5351
+ * Verify a thematic break node.
5352
+ */
5353
+ function verifyThematicBreak(node) {
5354
+ const loc = sourceCode.getLoc(node);
5355
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
5356
+ lineNumber,
5357
+ block: true
5358
+ }));
5359
+ }
5360
+ /**
5361
+ * Verify a heading node.
5362
+ */
5363
+ function verifyHeading(node) {
5364
+ const loc = sourceCode.getLoc(node);
5365
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
5366
+ lineNumber,
5367
+ block: true
5368
+ }));
5369
+ }
5370
+ /**
5371
+ * Verify a code block node.
5372
+ */
5373
+ function verifyCodeBlock(node) {
5374
+ if (getCodeBlockKind(sourceCode, node) === "indented") return;
5375
+ const loc = sourceCode.getLoc(node);
5376
+ verifyLinesIndent([loc.start.line, loc.end.line], (lineNumber) => blockStack.getExpectedIndent({
5377
+ lineNumber,
5378
+ block: true
5379
+ }), additionalFixes);
5380
+ /**
5381
+ * Additional fixes for code block content lines.
5382
+ */
5383
+ function* additionalFixes(fixer, info) {
5384
+ if (info.loc.start.line !== loc.start.line) return;
5385
+ for (let lineNumber = loc.start.line + 1; lineNumber < loc.end.line; lineNumber++) {
5386
+ const line = getParsedLines(sourceCode).get(lineNumber);
5387
+ if (!line) continue;
5388
+ if (info.expectedIndentWidth > info.actualIndentWidth) {
5389
+ const before = sliceWidth(line.text, 0, info.actualIndentWidth);
5390
+ const after = sliceWidth(line.text, info.actualIndentWidth);
5391
+ const diffWidth = info.expectedIndentWidth - info.actualIndentWidth;
5392
+ yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + " ".repeat(diffWidth) + after);
5393
+ } else {
5394
+ let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
5395
+ let between = sliceWidth(line.text, info.expectedIndentWidth, info.actualIndentWidth);
5396
+ const after = sliceWidth(line.text, info.actualIndentWidth);
5397
+ while (between && !isSpaceOrTab(between)) {
5398
+ before += between[0];
5399
+ between = between.slice(1);
5400
+ }
5401
+ yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + after);
5402
+ }
5403
+ }
5404
+ }
5405
+ }
5406
+ /**
5407
+ * Verify an HTML node.
5408
+ */
5409
+ function verifyHtml(node) {
5410
+ const loc = sourceCode.getLoc(node);
5411
+ if (!inlineToBeChecked(loc.start)) return;
5412
+ verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5413
+ lineNumber,
5414
+ block: true
5415
+ }));
5416
+ }
5417
+ /**
5418
+ * Verify a link definition node.
5419
+ */
5420
+ function verifyLinkDefinition(node) {
5421
+ const parsed = parseLinkDefinition(sourceCode, node);
5422
+ if (!parsed) return;
5423
+ const loc = sourceCode.getLoc(node);
5424
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber, column) => {
5425
+ const baseIndent = blockStack.getExpectedIndent({
5426
+ lineNumber,
5427
+ block: true
5428
+ });
5429
+ if (baseIndent === "ignore") return "ignore";
5430
+ if (lineNumber <= loc.start.line) return baseIndent;
5431
+ if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
5432
+ if (lineNumber === parsed.label.loc.end.line) {
5433
+ const line = getParsedLines(sourceCode).get(lineNumber);
5434
+ if (!line) return baseIndent;
5435
+ const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
5436
+ return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
5437
+ }
5438
+ if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 4;
5439
+ if (!parsed.title) return baseIndent;
5440
+ if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 4;
5441
+ if (lineNumber < parsed.title.loc.end.line) return baseIndent + 6;
5442
+ if (lineNumber === parsed.title.loc.end.line) {
5443
+ const line = getParsedLines(sourceCode).get(lineNumber);
5444
+ if (!line) return baseIndent;
5445
+ const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
5446
+ return !between || isWhitespace(between) ? baseIndent + 4 : baseIndent + 6;
5447
+ }
5448
+ return baseIndent;
5449
+ });
5450
+ }
5451
+ /**
5452
+ * Verify a blockquote node.
5453
+ */
5454
+ function verifyBlockquote(node) {
5455
+ const loc = sourceCode.getLoc(node);
5456
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
5457
+ lineNumber,
5458
+ block: true
5459
+ }));
5460
+ }
5461
+ /**
5462
+ * Verify a table node.
5463
+ */
5464
+ function verifyTable(node) {
5465
+ const loc = sourceCode.getLoc(node);
5466
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
5467
+ lineNumber,
5468
+ block: true
5469
+ }));
5470
+ }
5471
+ /**
5472
+ * Verify a list node.
5473
+ */
5474
+ function verifyList(node) {
5475
+ const loc = sourceCode.getLoc(node);
5476
+ verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5477
+ lineNumber,
5478
+ block: true
5479
+ }), additionalFixes);
5480
+ /**
5481
+ * Additional fixes for list item lines.
5482
+ */
5483
+ function* additionalFixes(fixer, info) {
5484
+ const [firstItem, ...otherItems] = node.children;
5485
+ yield* fixAfterFirstLine(firstItem, info.actualIndentWidth);
5486
+ for (const listItem of otherItems) {
5487
+ const listItemLoc = sourceCode.getLoc(listItem);
5488
+ const listItemIndentWidth = getWidth(sourceCode.lines[listItemLoc.start.line - 1].slice(0, listItemLoc.start.column - 1));
5489
+ if (listItemIndentWidth === info.expectedIndentWidth) continue;
5490
+ yield getFixForLine(listItemLoc.start.line, listItemIndentWidth);
5491
+ yield* fixAfterFirstLine(listItem, listItemIndentWidth);
5492
+ }
5493
+ /**
5494
+ * Get fixes for lines after the first line of the list item.
5495
+ */
5496
+ function* fixAfterFirstLine(item, actualIndentWidth) {
5497
+ const itemLoc = sourceCode.getLoc(item);
5498
+ for (let lineNumber = itemLoc.start.line + 1; lineNumber <= itemLoc.end.line; lineNumber++) yield getFixForLine(lineNumber, actualIndentWidth);
5499
+ }
5500
+ /**
5501
+ * Get a fix for a line.
5502
+ */
5503
+ function getFixForLine(lineNumber, actualIndentWidth) {
5504
+ const line = getParsedLines(sourceCode).get(lineNumber);
5505
+ if (info.expectedIndentWidth > actualIndentWidth) {
5506
+ const before$1 = sliceWidth(line.text, 0, actualIndentWidth);
5507
+ const diffWidth = info.expectedIndentWidth - actualIndentWidth;
5508
+ return fixer.insertTextAfterRange([line.range[0], line.range[0] + before$1.length], " ".repeat(diffWidth));
5509
+ }
5510
+ let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
5511
+ let between = sliceWidth(line.text, info.expectedIndentWidth, actualIndentWidth);
5512
+ while (between && !isSpaceOrTab(between)) {
5513
+ before += between[0];
5514
+ between = between.slice(1);
5515
+ }
5516
+ return fixer.replaceTextRange([line.range[0], line.range[0] + before.length + between.length], before);
5517
+ }
5518
+ }
5519
+ }
5520
+ /**
5521
+ * Verify a footnote definition node.
5522
+ */
5523
+ function verifyFootnoteDefinition(node) {
5524
+ const loc = sourceCode.getLoc(node);
5525
+ verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5526
+ lineNumber,
5527
+ block: true
5528
+ }));
5529
+ }
5530
+ /**
5531
+ * Verify an inline code node.
5532
+ */
5533
+ function verifyInlineCode(node) {
5534
+ const loc = sourceCode.getLoc(node);
5535
+ if (!inlineToBeChecked(loc.start)) return;
5536
+ verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5537
+ lineNumber,
5538
+ block: false
5539
+ }));
5540
+ }
5541
+ /**
5542
+ * Verify an emphasis, strong, or delete node.
5543
+ */
5544
+ function verifyEmphasisOrStrongOrDelete(node) {
5545
+ const loc = sourceCode.getLoc(node);
5546
+ if (!inlineToBeChecked(loc.start)) return;
5547
+ verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5548
+ lineNumber,
5549
+ block: false
5550
+ }));
5551
+ }
5552
+ /**
5553
+ * Verify a link node.
5554
+ */
5555
+ function verifyLink(node) {
5556
+ const loc = sourceCode.getLoc(node);
5557
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5558
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5559
+ const kind = getLinkKind(sourceCode, node);
5560
+ if (kind === "autolink" || kind === "gfm-autolink") verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
5561
+ lineNumber,
5562
+ block: false
5563
+ }));
5564
+ else if (kind === "inline") {
5565
+ const parsed = parseInlineLink(sourceCode, node);
5566
+ if (!parsed) return;
5567
+ const lastChild = node.children.at(-1);
5568
+ if (lastChild && parsed.text.loc.start.line < parsed.text.loc.end.line) {
5569
+ const lastChildLoc = sourceCode.getLoc(lastChild);
5570
+ const lastChildEndLine = lastChild.type === "text" && lastChild.value.endsWith("\n") ? lastChildLoc.end.line - 1 : lastChildLoc.end.line;
5571
+ lines = lines.filter((lineNumber) => lineNumber <= parsed.text.loc.start.line || lastChildEndLine < lineNumber);
5572
+ }
5573
+ verifyLinesIndent(lines, (lineNumber, column) => {
5574
+ const baseIndent = blockStack.getExpectedIndent({
5575
+ lineNumber,
5576
+ block: false
5577
+ });
5578
+ if (baseIndent === "ignore") return "ignore";
5579
+ if (lineNumber <= loc.start.line) return baseIndent;
5580
+ if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
5581
+ if (lineNumber <= parsed.openingParen.loc.end.line) return baseIndent;
5582
+ if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 2;
5583
+ if (!parsed.title) return baseIndent;
5584
+ if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 2;
5585
+ if (lineNumber < parsed.title.loc.end.line) return baseIndent + 4;
5586
+ if (lineNumber === parsed.title.loc.end.line) {
5587
+ const line = getParsedLines(sourceCode).get(lineNumber);
5588
+ if (!line) return baseIndent;
5589
+ const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
5590
+ return !between || isWhitespace(between) ? baseIndent + 2 : baseIndent + 4;
5591
+ }
5592
+ return baseIndent;
5593
+ });
5594
+ }
5595
+ }
5596
+ /**
5597
+ * Verify a link reference node.
5598
+ */
5599
+ function verifyLinkReference(node) {
5600
+ const loc = sourceCode.getLoc(node);
5601
+ const parsed = parseLinkReference(sourceCode, node);
5602
+ if (!parsed) return;
5603
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5604
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5605
+ const lastChild = node.children.at(-1);
5606
+ if (lastChild && parsed.text.loc.start.line < parsed.text.loc.end.line) {
5607
+ const lastChildLoc = sourceCode.getLoc(lastChild);
5608
+ const lastChildEndLine = lastChild.type === "text" && lastChild.value.endsWith("\n") ? lastChildLoc.end.line - 1 : lastChildLoc.end.line;
5609
+ lines = lines.filter((lineNumber) => lineNumber <= parsed.text.loc.start.line || lastChildEndLine < lineNumber);
5610
+ }
5611
+ verifyLinesIndent(lines, (lineNumber, column) => {
5612
+ const baseIndent = blockStack.getExpectedIndent({
5613
+ lineNumber,
5614
+ block: false
5615
+ });
5616
+ if (baseIndent === "ignore") return "ignore";
5617
+ if (lineNumber <= loc.start.line) return baseIndent;
5618
+ if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
5619
+ if (!parsed.label) return baseIndent;
5620
+ if (lineNumber <= parsed.label.loc.start.line) return baseIndent;
5621
+ if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
5622
+ if (lineNumber === parsed.label.loc.end.line) {
5623
+ const line = getParsedLines(sourceCode).get(lineNumber);
5624
+ if (!line) return baseIndent;
5625
+ const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
5626
+ return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
5627
+ }
5628
+ return baseIndent;
5629
+ });
5630
+ }
5631
+ /**
5632
+ * Verify an image node.
5633
+ */
5634
+ function verifyImage(node) {
5635
+ const loc = sourceCode.getLoc(node);
5636
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5637
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5638
+ const parsed = parseImage(sourceCode, node);
5639
+ if (!parsed) return;
5640
+ verifyLinesIndent(lines, (lineNumber, column) => {
5641
+ const baseIndent = blockStack.getExpectedIndent({
5642
+ lineNumber,
5643
+ block: false
5644
+ });
5645
+ if (baseIndent === "ignore") return "ignore";
5646
+ if (lineNumber <= loc.start.line) return baseIndent;
5647
+ if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
5648
+ if (lineNumber === parsed.text.loc.end.line) {
5649
+ const line = getParsedLines(sourceCode).get(lineNumber);
5650
+ if (!line) return baseIndent;
5651
+ const between = line.text.slice(column - 1, parsed.text.loc.end.column - 2);
5652
+ return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
5653
+ }
5654
+ if (lineNumber <= parsed.openingParen.loc.end.line) return baseIndent;
5655
+ if (lineNumber <= parsed.destination.loc.end.line) return baseIndent + 2;
5656
+ if (!parsed.title) return baseIndent;
5657
+ if (lineNumber <= parsed.title.loc.start.line) return baseIndent + 2;
5658
+ if (lineNumber < parsed.title.loc.end.line) return baseIndent + 4;
5659
+ if (lineNumber === parsed.title.loc.end.line) {
5660
+ const line = getParsedLines(sourceCode).get(lineNumber);
5661
+ if (!line) return baseIndent;
5662
+ const between = line.text.slice(column - 1, parsed.title.loc.end.column - 2);
5663
+ return !between || isWhitespace(between) ? baseIndent + 2 : baseIndent + 4;
5664
+ }
5665
+ return baseIndent;
5666
+ });
5667
+ }
5668
+ /**
5669
+ * Verify an image reference node.
5670
+ */
5671
+ function verifyImageReference(node) {
5672
+ const loc = sourceCode.getLoc(node);
5673
+ const parsed = parseImageReference(sourceCode, node);
5674
+ if (!parsed) return;
5675
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5676
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5677
+ verifyLinesIndent(lines, (lineNumber, column) => {
5678
+ const baseIndent = blockStack.getExpectedIndent({
5679
+ lineNumber,
5680
+ block: false
5681
+ });
5682
+ if (baseIndent === "ignore") return "ignore";
5683
+ if (lineNumber <= loc.start.line) return baseIndent;
5684
+ if (lineNumber < parsed.text.loc.end.line) return baseIndent + 2;
5685
+ if (lineNumber === parsed.text.loc.end.line) {
5686
+ const line = getParsedLines(sourceCode).get(lineNumber);
5687
+ if (!line) return baseIndent;
5688
+ const between = line.text.slice(column - 1, parsed.text.loc.end.column - 2);
5689
+ return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
5690
+ }
5691
+ if (!parsed.label) return baseIndent;
5692
+ if (lineNumber <= parsed.label.loc.start.line) return baseIndent;
5693
+ if (lineNumber < parsed.label.loc.end.line) return baseIndent + 2;
5694
+ if (lineNumber === parsed.label.loc.end.line) {
5695
+ const line = getParsedLines(sourceCode).get(lineNumber);
5696
+ if (!line) return baseIndent;
5697
+ const between = line.text.slice(column - 1, parsed.label.loc.end.column - 2);
5698
+ return !between || isWhitespace(between) ? baseIndent : baseIndent + 2;
5699
+ }
5700
+ return baseIndent;
5701
+ });
5702
+ }
5703
+ /**
5704
+ * Verify a text node.
5705
+ */
5706
+ function verifyText(node) {
5707
+ const loc = sourceCode.getLoc(node);
5708
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5709
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5710
+ if (node.value.endsWith("\n")) lines = lines.slice(0, -1);
5711
+ verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
5712
+ lineNumber,
5713
+ block: false
5714
+ }));
5715
+ }
5716
+ /**
5717
+ * Verify an inline node.
5718
+ */
5719
+ function verifyInline(node) {
5720
+ const loc = sourceCode.getLoc(node);
5721
+ let lines = lineNumbersFromRange(loc.start.line, loc.end.line);
5722
+ if (!inlineToBeChecked(loc.start)) lines = lines.slice(1);
5723
+ verifyLinesIndent(lines, (lineNumber) => blockStack.getExpectedIndent({
5724
+ lineNumber,
5725
+ block: false
5726
+ }));
5727
+ }
5728
+ /**
5729
+ * Check whether the inline node should be checked.
5730
+ */
5731
+ function inlineToBeChecked(position) {
5732
+ const blockquote = blockStack.getCurrentBlockquote();
5733
+ const listItem = blockStack.getCurrentListItemAtLine(position.line);
5734
+ const lineText = sourceCode.lines[position.line - 1];
5735
+ if (listItem) {
5736
+ const parsed = listItem.getParsedListItem();
5737
+ const indentText = lineText.slice((parsed.taskListItemMarker?.loc ?? parsed.marker.loc).end.column - 1, position.column - 1);
5738
+ if (indentText && !isSpaceOrTab(indentText)) return false;
5739
+ } else if (blockquote) {
5740
+ if (atWidth(lineText, blockquote.getMarkerIndent()) !== ">") return false;
5741
+ const indentText = sliceWidth(lineText.slice(0, position.column - 1), blockquote.getMarkerIndent() + 1);
5742
+ if (indentText && !isSpaceOrTab(indentText)) return false;
5743
+ } else {
5744
+ const indentText = lineText.slice(0, position.column - 1);
5745
+ if (indentText && !isSpaceOrTab(indentText)) return false;
5746
+ }
5747
+ return true;
5748
+ }
5749
+ /**
5750
+ * Get line numbers from the range.
5751
+ */
5752
+ function lineNumbersFromRange(lineNumberFrom, lineNumberTo) {
5753
+ const lines = [];
5754
+ for (let lineNumber = lineNumberFrom; lineNumber <= lineNumberTo; lineNumber++) lines.push(lineNumber);
5755
+ return lines;
5756
+ }
5757
+ /**
5758
+ * Verify the indentation of the lines.
5759
+ */
5760
+ function verifyLinesIndent(lineNumbers, expectedIndentWidthProvider, additionalFixes) {
5761
+ const blockquote = blockStack.getCurrentBlockquote();
5762
+ if (!blockquote) {
5763
+ verifyLinesIndentFromRoot(lineNumbers, expectedIndentWidthProvider, additionalFixes);
5764
+ return;
5765
+ }
5766
+ verifyLinesIndentFromBlockquoteMarker(lineNumbers, blockquote, expectedIndentWidthProvider, additionalFixes);
5767
+ }
5768
+ /**
5769
+ * Verify the indentation of the lines from the root.
5770
+ */
5771
+ function verifyLinesIndentFromRoot(lineNumbers, expectedIndentWidthProvider, additionalFixes) {
5772
+ const reports = [];
5773
+ for (const lineNumber of lineNumbers) {
5774
+ const line = getParsedLines(sourceCode).get(lineNumber);
5775
+ if (!line) return;
5776
+ const listItem = blockStack.getCurrentListItemAtLine(lineNumber);
5777
+ if (!listItem) {
5778
+ const indentText = /^\s*/u.exec(line.text)[0];
5779
+ const actualIndentWidth = getWidth(indentText);
5780
+ const expectedIndentWidth = expectedIndentWidthProvider(lineNumber, 1);
5781
+ if (expectedIndentWidth === "ignore") continue;
5782
+ if (actualIndentWidth === expectedIndentWidth) continue;
5783
+ reports.push({
5784
+ loc: {
5785
+ start: {
5786
+ line: line.line,
5787
+ column: 1
5788
+ },
5789
+ end: {
5790
+ line: line.line,
5791
+ column: indentText.length + 1
5792
+ }
5793
+ },
5794
+ messageId: indentText.includes(" ") ? "wrongIndentationWithTab" : "wrongIndentation",
5795
+ data: {
5796
+ expected: expectedIndentWidth,
5797
+ actual: actualIndentWidth
5798
+ },
5799
+ fix(fixer) {
5800
+ return fixer.replaceTextRange([line.range[0], line.range[0] + indentText.length], " ".repeat(expectedIndentWidth));
5801
+ },
5802
+ expectedIndentWidth,
5803
+ actualIndentWidth
5804
+ });
5805
+ continue;
5806
+ }
5807
+ const report = verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider);
5808
+ if (!report) continue;
5809
+ reports.push(report);
5810
+ }
5811
+ if (!reports.length) return;
5812
+ for (const report of reports) reportIncorrectIndent({
5813
+ loc: report.loc,
5814
+ messageId: report.messageId,
5815
+ data: report.data,
5816
+ *fix(fixer) {
5817
+ yield report.fix(fixer);
5818
+ if (additionalFixes) yield* additionalFixes(fixer, report) ?? [];
5819
+ }
5820
+ });
5821
+ }
5822
+ /**
5823
+ * Verify the indentation of the lines from the blockquote marker.
5824
+ */
5825
+ function verifyLinesIndentFromBlockquoteMarker(lineNumbers, blockquote, expectedIndentWidthProvider, additionalFixes) {
5826
+ const blockquoteLoc = sourceCode.getLoc(blockquote.node);
5827
+ const reports = [];
5828
+ let cannotFix = false;
5829
+ for (const lineNumber of lineNumbers) {
5830
+ const line = getParsedLines(sourceCode).get(lineNumber);
5831
+ if (!line) return;
5832
+ if (atWidth(line.text, blockquote.getMarkerIndent()) !== ">") {
5833
+ cannotFix = true;
5834
+ continue;
5835
+ }
5836
+ const listItem = blockStack.getCurrentListItemAtLine(lineNumber);
5837
+ if (!listItem) {
5838
+ const before = line.text.slice(0, blockquoteLoc.start.column);
5839
+ const after = line.text.slice(blockquoteLoc.start.column);
5840
+ const indentText = /^\s*/u.exec(after)[0];
5841
+ const actualIndentWidth = getWidth(before + indentText);
5842
+ const expectedIndentWidth = expectedIndentWidthProvider(lineNumber, blockquoteLoc.start.column + 1);
5843
+ if (expectedIndentWidth === "ignore") continue;
5844
+ if (actualIndentWidth === expectedIndentWidth) continue;
5845
+ const linePrefixWidth = getWidth(before);
5846
+ reports.push({
5847
+ loc: {
5848
+ start: {
5849
+ line: line.line,
5850
+ column: blockquoteLoc.start.column + 1
5851
+ },
5852
+ end: {
5853
+ line: line.line,
5854
+ column: blockquoteLoc.start.column + 1 + indentText.length
5855
+ }
5856
+ },
5857
+ messageId: indentText.includes(" ") ? "wrongIndentationFromBlockquoteMarkerWithTab" : "wrongIndentationFromBlockquoteMarker",
5858
+ data: {
5859
+ expected: expectedIndentWidth - linePrefixWidth,
5860
+ actual: actualIndentWidth - linePrefixWidth
5861
+ },
5862
+ fix(fixer) {
5863
+ return fixer.replaceTextRange([line.range[0] + blockquoteLoc.start.column, line.range[0] + blockquoteLoc.start.column + indentText.length], " ".repeat(expectedIndentWidth - linePrefixWidth));
5864
+ },
5865
+ expectedIndentWidth,
5866
+ actualIndentWidth
5867
+ });
5868
+ continue;
5869
+ }
5870
+ const report = verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider);
5871
+ if (!report) continue;
5872
+ reports.push(report);
5873
+ }
5874
+ if (!reports.length) return;
5875
+ for (const report of reports) reportIncorrectIndent({
5876
+ loc: report.loc,
5877
+ messageId: report.messageId,
5878
+ data: report.data,
5879
+ *fix(fixer) {
5880
+ if (cannotFix) return;
5881
+ yield report.fix(fixer);
5882
+ if (additionalFixes) yield* additionalFixes(fixer, report) ?? [];
5883
+ }
5884
+ });
5885
+ }
5886
+ /**
5887
+ * Verify the indentation of the line from the list item marker.
5888
+ */
5889
+ function verifyLineIndentFromListItemMarker(line, listItem, expectedIndentWidthProvider) {
5890
+ const parsed = listItem.getParsedListItem();
5891
+ const markerAfterColumn = (parsed.taskListItemMarker?.loc ?? parsed.marker.loc).end.column;
5892
+ const before = line.text.slice(0, markerAfterColumn - 1);
5893
+ const after = line.text.slice(markerAfterColumn - 1);
5894
+ const indentText = /^\s*/u.exec(after)[0];
5895
+ const actualIndentWidth = getWidth(before + indentText);
5896
+ const expectedIndentWidth = expectedIndentWidthProvider(line.line, markerAfterColumn);
5897
+ if (expectedIndentWidth === "ignore") return null;
5898
+ if (actualIndentWidth === expectedIndentWidth) return null;
5899
+ const linePrefixWidth = getWidth(before);
5900
+ const range = [line.range[0] + before.length, line.range[0] + before.length + indentText.length];
5901
+ return {
5902
+ loc: getSourceLocationFromRange(sourceCode, listItem.node, range),
5903
+ messageId: indentText.includes(" ") ? "wrongIndentationFromListMarkerWithTab" : "wrongIndentationFromListMarker",
5904
+ data: {
5905
+ expected: expectedIndentWidth - linePrefixWidth,
5906
+ actual: actualIndentWidth - linePrefixWidth
5907
+ },
5908
+ fix(fixer) {
5909
+ return fixer.replaceTextRange(range, " ".repeat(expectedIndentWidth - linePrefixWidth));
5910
+ },
5911
+ expectedIndentWidth,
5912
+ actualIndentWidth
5913
+ };
5914
+ }
5915
+ }
5916
+ });
5917
+
5918
+ //#endregion
5919
+ //#region src/utils/setext-heading.ts
5920
+ /**
5921
+ * Parse the setext heading.
5922
+ */
5923
+ function parseSetextHeading(sourceCode, node) {
5924
+ if (getHeadingKind(sourceCode, node) !== "setext") return null;
5925
+ const lines = getParsedLines(sourceCode);
5926
+ const contentLines = [];
5927
+ const nodeLoc = sourceCode.getLoc(node);
5928
+ for (let lineNumber = nodeLoc.start.line; lineNumber < nodeLoc.end.line; lineNumber++) {
5929
+ const content = parseContent(lines.get(lineNumber));
5930
+ contentLines.push(content);
5931
+ }
5932
+ const underline = parseUnderline(lines.get(nodeLoc.end.line));
5933
+ if (!underline) return null;
5934
+ return {
5935
+ contentLines,
5936
+ underline
5937
+ };
5938
+ }
5939
+ /**
5940
+ * Parse the content line of a setext heading.
5941
+ */
5942
+ function parseContent(line) {
5943
+ let prefix = "";
5944
+ let spaceBefore = "";
5945
+ let suffix = "";
5946
+ for (let index = 0; index < line.text.length; index++) {
5947
+ const c = line.text[index];
5948
+ if (!c.trim()) {
5949
+ spaceBefore += c;
5950
+ continue;
5951
+ }
5952
+ if (c === ">" && spaceBefore.length < 4) {
5953
+ prefix += spaceBefore + c;
5954
+ spaceBefore = "";
5955
+ continue;
5956
+ }
5957
+ suffix = line.text.slice(index);
5958
+ break;
5959
+ }
5960
+ const content = suffix.trimEnd();
5961
+ const spaceAfter = suffix.slice(content.length);
5962
+ return {
5963
+ text: content,
5964
+ range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
5965
+ loc: {
5966
+ start: {
5967
+ line: line.line,
5968
+ column: prefix.length + spaceBefore.length + 1
5969
+ },
5970
+ end: {
5971
+ line: line.line,
5972
+ column: prefix.length + spaceBefore.length + content.length + 1
5973
+ }
5974
+ },
5975
+ raws: {
5976
+ prefix,
5977
+ spaceBefore,
5978
+ spaceAfter
5979
+ }
5074
5980
  };
5075
5981
  }
5076
5982
  /**
5077
- * Parse the link definition from the given text.
5983
+ * Parse the underline of a setext heading.
5078
5984
  */
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
5092
- };
5093
- index++;
5094
- skipSpaces();
5095
- let destination;
5096
- const destinationStartIndex = index;
5097
- if (text[index] === "<") {
5098
- index++;
5099
- if (!skipUntilEnd((c) => c === ">")) return null;
5100
- index++;
5101
- const destinationRange = [destinationStartIndex, index];
5102
- destination = {
5103
- type: "pointy-bracketed",
5104
- text: text.slice(...destinationRange),
5105
- range: destinationRange
5106
- };
5107
- } else {
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
5115
- };
5985
+ function parseUnderline(line) {
5986
+ let marker = null;
5987
+ let underlineText = "";
5988
+ let prefix = "";
5989
+ let spaceBefore = "";
5990
+ let spaceAfter = "";
5991
+ for (let index = line.text.length - 1; index >= 0; index--) {
5992
+ const c = line.text[index];
5993
+ if (!marker) {
5994
+ if (c === "=" || c === "-") {
5995
+ underlineText = c + underlineText;
5996
+ marker = c;
5997
+ } else if (!c.trim()) spaceAfter = c + spaceAfter;
5998
+ else return null;
5999
+ continue;
6000
+ }
6001
+ if (c === marker) {
6002
+ underlineText = c + spaceBefore + underlineText;
6003
+ spaceBefore = "";
6004
+ } else if (!c.trim()) spaceBefore = c + spaceBefore;
6005
+ else {
6006
+ prefix = line.text.slice(0, index + 1);
6007
+ break;
6008
+ }
5116
6009
  }
5117
- skipSpaces();
5118
- if (text.length <= index) return {
5119
- label,
5120
- destination,
5121
- title: null
6010
+ if (!marker) return null;
6011
+ const underlineLoc = {
6012
+ start: {
6013
+ line: line.line,
6014
+ column: prefix.length + spaceBefore.length + 1
6015
+ },
6016
+ end: {
6017
+ line: line.line,
6018
+ column: prefix.length + spaceBefore.length + underlineText.length + 1
6019
+ }
5122
6020
  };
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
6021
  return {
5141
- label,
5142
- destination,
5143
- title
6022
+ text: underlineText,
6023
+ range: [line.range[0] + prefix.length + spaceBefore.length, line.range[1] - line.linebreak.length - spaceAfter.length],
6024
+ loc: underlineLoc,
6025
+ marker,
6026
+ raws: {
6027
+ prefix,
6028
+ spaceBefore,
6029
+ spaceAfter
6030
+ }
5144
6031
  };
5145
- /**
5146
- * Skip spaces
5147
- */
5148
- function skipSpaces() {
5149
- while (index < text.length && isWhitespace(text[index])) index++;
6032
+ }
6033
+
6034
+ //#endregion
6035
+ //#region src/rules/level1-heading-style.ts
6036
+ var level1_heading_style_default = createRule("level1-heading-style", {
6037
+ meta: {
6038
+ type: "layout",
6039
+ docs: {
6040
+ description: "enforce consistent style for level 1 headings",
6041
+ categories: ["standard"],
6042
+ listCategory: "Stylistic"
6043
+ },
6044
+ fixable: "code",
6045
+ hasSuggestions: false,
6046
+ schema: [{
6047
+ type: "object",
6048
+ properties: {
6049
+ style: { enum: ["atx", "setext"] },
6050
+ allowMultilineSetext: { type: "boolean" }
6051
+ },
6052
+ additionalProperties: false
6053
+ }],
6054
+ messages: {
6055
+ expectedAtx: "Expected ATX style heading (# Heading).",
6056
+ expectedSetext: "Expected Setext style heading (Heading\\n======).",
6057
+ multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
6058
+ }
6059
+ },
6060
+ create(context) {
6061
+ const sourceCode = context.sourceCode;
6062
+ const opt = context.options[0] || {};
6063
+ const style = opt.style ?? "atx";
6064
+ const allowMultilineSetext = opt.allowMultilineSetext;
6065
+ return { heading(node) {
6066
+ if (node.depth !== 1) return;
6067
+ const headingKind = getHeadingKind(sourceCode, node);
6068
+ if (style === "atx") {
6069
+ if (headingKind !== "setext") return;
6070
+ const parsed = parseSetextHeading(sourceCode, node);
6071
+ if (!parsed) return;
6072
+ if (parsed.contentLines.length > 1) {
6073
+ if (allowMultilineSetext) return;
6074
+ context.report({
6075
+ node,
6076
+ messageId: "multilineSetextNotAllowed"
6077
+ });
6078
+ return;
6079
+ }
6080
+ context.report({
6081
+ node,
6082
+ messageId: "expectedAtx",
6083
+ *fix(fixer) {
6084
+ const heading = parsed.contentLines[0];
6085
+ yield fixer.insertTextBeforeRange(heading.range, "# ");
6086
+ const lines = getParsedLines(sourceCode);
6087
+ yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
6088
+ }
6089
+ });
6090
+ } else if (style === "setext") {
6091
+ if (headingKind !== "atx" || node.children.length === 0) return;
6092
+ context.report({
6093
+ node,
6094
+ messageId: "expectedSetext",
6095
+ *fix(fixer) {
6096
+ const parsed = parseATXHeading(sourceCode, node);
6097
+ if (!parsed) return;
6098
+ yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
6099
+ if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
6100
+ const lines = getParsedLines(sourceCode);
6101
+ const underline = "=".repeat(Math.max(getTextWidth(parsed.content.text), 3));
6102
+ const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
6103
+ yield fixer.insertTextAfter(node, appendingText);
6104
+ }
6105
+ });
6106
+ }
6107
+ } };
5150
6108
  }
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++;
6109
+ });
6110
+
6111
+ //#endregion
6112
+ //#region src/rules/level2-heading-style.ts
6113
+ var level2_heading_style_default = createRule("level2-heading-style", {
6114
+ meta: {
6115
+ type: "layout",
6116
+ docs: {
6117
+ description: "enforce consistent style for level 2 headings",
6118
+ categories: ["standard"],
6119
+ listCategory: "Stylistic"
6120
+ },
6121
+ fixable: "code",
6122
+ hasSuggestions: false,
6123
+ schema: [{
6124
+ type: "object",
6125
+ properties: {
6126
+ style: { enum: ["atx", "setext"] },
6127
+ allowMultilineSetext: { type: "boolean" }
6128
+ },
6129
+ additionalProperties: false
6130
+ }],
6131
+ messages: {
6132
+ expectedAtx: "Expected ATX style heading (## Heading).",
6133
+ expectedSetext: "Expected Setext style heading (Heading\\n------).",
6134
+ multilineSetextNotAllowed: "Multiline Setext headings are not allowed."
5161
6135
  }
5162
- return false;
6136
+ },
6137
+ create(context) {
6138
+ const sourceCode = context.sourceCode;
6139
+ const opt = context.options[0] || {};
6140
+ const style = opt.style ?? "atx";
6141
+ const allowMultilineSetext = opt.allowMultilineSetext;
6142
+ return { heading(node) {
6143
+ if (node.depth !== 2) return;
6144
+ const headingKind = getHeadingKind(sourceCode, node);
6145
+ if (style === "atx") {
6146
+ if (headingKind !== "setext") return;
6147
+ const parsed = parseSetextHeading(sourceCode, node);
6148
+ if (!parsed) return;
6149
+ if (parsed.contentLines.length > 1) {
6150
+ if (allowMultilineSetext) return;
6151
+ context.report({
6152
+ node,
6153
+ messageId: "multilineSetextNotAllowed"
6154
+ });
6155
+ return;
6156
+ }
6157
+ context.report({
6158
+ node,
6159
+ messageId: "expectedAtx",
6160
+ *fix(fixer) {
6161
+ const heading = parsed.contentLines[0];
6162
+ yield fixer.insertTextBeforeRange(heading.range, "## ");
6163
+ const lines = getParsedLines(sourceCode);
6164
+ yield fixer.removeRange(lines.get(parsed.underline.loc.start.line).range);
6165
+ }
6166
+ });
6167
+ } else if (style === "setext") {
6168
+ if (headingKind !== "atx" || node.children.length === 0) return;
6169
+ context.report({
6170
+ node,
6171
+ messageId: "expectedSetext",
6172
+ *fix(fixer) {
6173
+ const parsed = parseATXHeading(sourceCode, node);
6174
+ if (!parsed) return;
6175
+ yield fixer.removeRange([parsed.openingSequence.range[0], parsed.content.range[0]]);
6176
+ if (parsed.closingSequence) yield fixer.removeRange([parsed.content.range[1], parsed.after?.range[1] ?? parsed.closingSequence.range[1]]);
6177
+ const lines = getParsedLines(sourceCode);
6178
+ const underline = "-".repeat(Math.max(getTextWidth(parsed.content.text), 3));
6179
+ const appendingText = `\n${lines.get(parsed.openingSequence.loc.start.line).text.slice(0, parsed.openingSequence.loc.start.column - 1)}${underline}`;
6180
+ yield fixer.insertTextAfter(node, appendingText);
6181
+ }
6182
+ });
6183
+ }
6184
+ } };
5163
6185
  }
5164
- }
6186
+ });
5165
6187
 
5166
6188
  //#endregion
5167
6189
  //#region src/rules/link-bracket-newline.ts
@@ -5196,11 +6218,11 @@ var link_bracket_newline_default = createRule("link-bracket-newline", {
5196
6218
  },
5197
6219
  create(context) {
5198
6220
  const sourceCode = context.sourceCode;
5199
- const optionProvider = parseOptions$3(context.options[0]);
6221
+ const optionProvider = parseOptions$4(context.options[0]);
5200
6222
  /**
5201
6223
  * Parse the options.
5202
6224
  */
5203
- function parseOptions$3(option) {
6225
+ function parseOptions$4(option) {
5204
6226
  const newline = option?.newline ?? "never";
5205
6227
  const multiline = option?.multiline ?? false;
5206
6228
  return (bracketsRange) => {
@@ -5674,11 +6696,11 @@ var link_paren_newline_default = createRule("link-paren-newline", {
5674
6696
  },
5675
6697
  create(context) {
5676
6698
  const sourceCode = context.sourceCode;
5677
- const optionProvider = parseOptions$3(context.options[0]);
6699
+ const optionProvider = parseOptions$4(context.options[0]);
5678
6700
  /**
5679
6701
  * Parse the options.
5680
6702
  */
5681
- function parseOptions$3(option) {
6703
+ function parseOptions$4(option) {
5682
6704
  const newline = option?.newline ?? "never";
5683
6705
  const multiline = option?.multiline ?? false;
5684
6706
  return (openingParenIndex, closingParenIndex) => {
@@ -6044,10 +7066,22 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
6044
7066
  const items = listNode.children;
6045
7067
  if (items.length <= 1) return;
6046
7068
  const referenceMarkerLocation = getMarkerLocation(items[0]);
7069
+ const expectedWidth = getWidth(sourceCode.lines[referenceMarkerLocation.line - 1].slice(0, referenceMarkerLocation[alignPositionName]));
6047
7070
  for (const item of items.slice(1)) {
6048
7071
  const markerLocation = getMarkerLocation(item);
6049
- const diff = markerLocation[alignPositionName] - referenceMarkerLocation[alignPositionName];
7072
+ const actualWidth = getWidth(sourceCode.lines[markerLocation.line - 1].slice(0, markerLocation[alignPositionName]));
7073
+ const diff = actualWidth - expectedWidth;
6050
7074
  if (diff === 0) continue;
7075
+ const messageData = alignPositionName === "start" ? {
7076
+ expected: String(expectedWidth),
7077
+ actual: String(actualWidth)
7078
+ } : (() => {
7079
+ const start = getWidth(sourceCode.lines[markerLocation.line - 1].slice(0, markerLocation.start));
7080
+ return {
7081
+ expected: String(start - diff),
7082
+ actual: String(start)
7083
+ };
7084
+ })();
6051
7085
  context.report({
6052
7086
  node: item,
6053
7087
  loc: {
@@ -6061,10 +7095,7 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
6061
7095
  }
6062
7096
  },
6063
7097
  messageId: "incorrectAlignment",
6064
- data: {
6065
- expected: String(markerLocation.start - diff),
6066
- actual: String(markerLocation.start)
6067
- },
7098
+ data: messageData,
6068
7099
  fix(fixer) {
6069
7100
  const lines = getParsedLines(sourceCode);
6070
7101
  const line = lines.get(markerLocation.line);
@@ -6072,13 +7103,17 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
6072
7103
  const addSpaces = " ".repeat(-diff);
6073
7104
  return fixer.insertTextBeforeRange([line.range[0] + markerLocation.start, line.range[0] + markerLocation.start], addSpaces);
6074
7105
  }
6075
- const itemBefore = line.text.slice(0, markerLocation.start - diff);
6076
- if (itemBefore.includes(" ")) return null;
6077
- if (lines.get(referenceMarkerLocation.line).text.slice(0, referenceMarkerLocation.start) === itemBefore) {
6078
- const removeEndIndex = line.range[0] + markerLocation.start;
6079
- const removeStartIndex = removeEndIndex - diff;
6080
- return fixer.removeRange([removeStartIndex, removeEndIndex]);
7106
+ const beforeItemMarker = line.text.slice(0, markerLocation.start);
7107
+ const newWidth = getWidth(beforeItemMarker) - diff;
7108
+ let newBeforeItemMarker = beforeItemMarker;
7109
+ while (getWidth(newBeforeItemMarker) > newWidth) {
7110
+ const last = newBeforeItemMarker.at(-1);
7111
+ if (last && isWhitespace(last)) newBeforeItemMarker = newBeforeItemMarker.slice(0, -1);
7112
+ else return null;
6081
7113
  }
7114
+ if (getWidth(newBeforeItemMarker) < newWidth) newBeforeItemMarker += " ".repeat(newWidth - getWidth(newBeforeItemMarker));
7115
+ const referenceBeforeItemMarker = lines.get(referenceMarkerLocation.line).text.slice(0, referenceMarkerLocation.start);
7116
+ if (!referenceBeforeItemMarker.includes(">") || referenceBeforeItemMarker === newBeforeItemMarker) return fixer.replaceTextRange([line.range[0], line.range[0] + markerLocation.start], newBeforeItemMarker);
6082
7117
  return null;
6083
7118
  }
6084
7119
  });
@@ -6212,6 +7247,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6212
7247
  },
6213
7248
  create(context) {
6214
7249
  const sourceCode = context.sourceCode;
7250
+ let codeText = sourceCode.text;
6215
7251
  return {
6216
7252
  definition: verifyLinkDefinition,
6217
7253
  footnoteDefinition: verifyFootnoteDefinition,
@@ -6221,6 +7257,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6221
7257
  link: verifyLink,
6222
7258
  linkReference: verifyLinkReference,
6223
7259
  listItem: verifyListItem,
7260
+ blockquote: processBlockquote,
6224
7261
  text: verifyText
6225
7262
  };
6226
7263
  /**
@@ -6294,7 +7331,13 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6294
7331
  * Verify a list item node
6295
7332
  */
6296
7333
  function verifyListItem(node) {
6297
- verifyTextOutsideChildren(node);
7334
+ const nodeRange = sourceCode.getRange(node);
7335
+ const parsed = parseListItem(sourceCode, node);
7336
+ if (parsed.taskListItemMarker) verifyTextInRange(node, [nodeRange[0], parsed.taskListItemMarker.range[0]]);
7337
+ let newCodeText = codeText.slice(0, parsed.marker.range[0]) + " ".repeat(parsed.marker.range[1] - parsed.marker.range[0]);
7338
+ 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]);
7339
+ else newCodeText += codeText.slice(parsed.marker.range[1]);
7340
+ codeText = newCodeText;
6298
7341
  }
6299
7342
  /**
6300
7343
  * Verify spaces in a node
@@ -6304,6 +7347,26 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6304
7347
  verifyTextInRange(node, nodeRange);
6305
7348
  }
6306
7349
  /**
7350
+ * Process a blockquote node
7351
+ */
7352
+ function processBlockquote(node) {
7353
+ const nodeRange = sourceCode.getRange(node);
7354
+ let newCodeText = "";
7355
+ let inIndent = true;
7356
+ for (let index = nodeRange[0]; index < nodeRange[1]; index++) {
7357
+ const c = codeText[index];
7358
+ if (c === "\n") {
7359
+ inIndent = true;
7360
+ continue;
7361
+ }
7362
+ if (isWhitespace(c)) continue;
7363
+ if (c === ">" && inIndent) newCodeText += `${codeText.slice(newCodeText.length, index)} `;
7364
+ inIndent = false;
7365
+ }
7366
+ newCodeText += codeText.slice(newCodeText.length);
7367
+ codeText = newCodeText;
7368
+ }
7369
+ /**
6307
7370
  * Verify spaces in a node excluding children
6308
7371
  */
6309
7372
  function verifyTextOutsideChildren(node) {
@@ -6322,14 +7385,14 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6322
7385
  */
6323
7386
  function verifyTextInRange(node, textRange) {
6324
7387
  const nodeRange = sourceCode.getRange(node);
6325
- const text = sourceCode.text.slice(...textRange);
7388
+ const text = codeText.slice(...textRange);
6326
7389
  const reSpaces = /\s{2,}|\n/gu;
6327
7390
  let match;
6328
7391
  while ((match = reSpaces.exec(text)) !== null) {
6329
7392
  const spaces = match[0];
6330
7393
  if (spaces.includes("\n")) {
6331
7394
  let c = "";
6332
- while ((c = text[reSpaces.lastIndex]) && (c === ">" || !c.trim())) reSpaces.lastIndex++;
7395
+ while ((c = text[reSpaces.lastIndex]) && isWhitespace(c)) reSpaces.lastIndex++;
6333
7396
  continue;
6334
7397
  }
6335
7398
  if (spaces.length < 2) continue;
@@ -6339,7 +7402,7 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6339
7402
  if (nodeRange[0] === range[0]) {
6340
7403
  let isIndentation = true;
6341
7404
  for (let index = nodeRange[0] - 1; index >= 0; index--) {
6342
- const c = sourceCode.text[index];
7405
+ const c = codeText[index];
6343
7406
  if (c === "\n") break;
6344
7407
  if (isWhitespace(c)) continue;
6345
7408
  isIndentation = false;
@@ -6349,8 +7412,8 @@ var no_multi_spaces_default = createRule("no-multi-spaces", {
6349
7412
  }
6350
7413
  if (nodeRange[1] === range[1]) {
6351
7414
  let isTrailingSpaces = true;
6352
- for (let index = nodeRange[1]; index < sourceCode.text.length; index++) {
6353
- const c = sourceCode.text[index];
7415
+ for (let index = nodeRange[1]; index < codeText.length; index++) {
7416
+ const c = codeText[index];
6354
7417
  if (c === "\n") break;
6355
7418
  if (isWhitespace(c)) continue;
6356
7419
  isTrailingSpaces = false;
@@ -8814,6 +9877,7 @@ const rules$1 = [
8814
9877
  emphasis_delimiters_style_default,
8815
9878
  hard_linebreak_style_default,
8816
9879
  heading_casing_default,
9880
+ indent_default,
8817
9881
  level1_heading_style_default,
8818
9882
  level2_heading_style_default,
8819
9883
  link_bracket_newline_default,
@@ -8848,8 +9912,7 @@ const rules$1 = [
8848
9912
 
8849
9913
  //#endregion
8850
9914
  //#region src/configs/recommended.ts
8851
- var recommended_exports = {};
8852
- __export(recommended_exports, {
9915
+ var recommended_exports = __export({
8853
9916
  files: () => files$1,
8854
9917
  language: () => language$1,
8855
9918
  languageOptions: () => languageOptions$1,
@@ -8879,8 +9942,7 @@ const rules$3 = {
8879
9942
 
8880
9943
  //#endregion
8881
9944
  //#region src/configs/standard.ts
8882
- var standard_exports = {};
8883
- __export(standard_exports, {
9945
+ var standard_exports = __export({
8884
9946
  files: () => files,
8885
9947
  language: () => language,
8886
9948
  languageOptions: () => languageOptions,
@@ -8907,6 +9969,7 @@ const rules$2 = {
8907
9969
  "markdown-preferences/code-fence-style": "error",
8908
9970
  "markdown-preferences/emphasis-delimiters-style": "error",
8909
9971
  "markdown-preferences/hard-linebreak-style": "error",
9972
+ "markdown-preferences/indent": "error",
8910
9973
  "markdown-preferences/level1-heading-style": "error",
8911
9974
  "markdown-preferences/level2-heading-style": "error",
8912
9975
  "markdown-preferences/link-bracket-newline": "error",
@@ -8937,13 +10000,12 @@ const rules$2 = {
8937
10000
 
8938
10001
  //#endregion
8939
10002
  //#region src/meta.ts
8940
- var meta_exports = {};
8941
- __export(meta_exports, {
10003
+ var meta_exports = __export({
8942
10004
  name: () => name,
8943
10005
  version: () => version
8944
10006
  });
8945
10007
  const name = "eslint-plugin-markdown-preferences";
8946
- const version = "0.23.0";
10008
+ const version = "0.24.0";
8947
10009
 
8948
10010
  //#endregion
8949
10011
  //#region src/index.ts