Reimplement MD003/heading-style using micromark tokens.

This commit is contained in:
David Anson 2024-06-08 14:44:49 -07:00
parent e447db33c9
commit 6daaa43410
10 changed files with 132 additions and 148 deletions

View file

@ -1539,6 +1539,26 @@ function getHeadingLevel(heading) {
return level; return level;
} }
/**
* Gets the heading style of a Micromark heading tokan.
*
* @param {Token} heading Micromark heading token.
* @returns {"atx" | "atx_closed" | "setext"} Heading style.
*/
function getHeadingStyle(heading) {
if (heading.type === "setextHeading") {
return "setext";
}
const atxHeadingSequenceLength = filterByTypes(
heading.children,
[ "atxHeadingSequence" ]
).length;
if (atxHeadingSequenceLength === 1) {
return "atx";
}
return "atx_closed";
}
/** /**
* HTML tag information. * HTML tag information.
* *
@ -1663,6 +1683,7 @@ module.exports = {
filterByPredicate, filterByPredicate,
filterByTypes, filterByTypes,
getHeadingLevel, getHeadingLevel,
getHeadingStyle,
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getTokenParentOfType, getTokenParentOfType,
@ -3352,8 +3373,9 @@ module.exports = {
const { addErrorDetailIf, filterTokens, headingStyleFor } = const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
__webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { filterByTypes, getHeadingLevel, getHeadingStyle, inHtmlFlow } =
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -3361,16 +3383,20 @@ module.exports = {
"names": [ "MD003", "heading-style" ], "names": [ "MD003", "heading-style" ],
"description": "Heading style", "description": "Heading style",
"tags": [ "headings" ], "tags": [ "headings" ],
"parser": "markdownit", "parser": "micromark",
"function": function MD003(params, onError) { "function": function MD003(params, onError) {
let style = String(params.config.style || "consistent"); let style = String(params.config.style || "consistent");
filterTokens(params, "heading_open", function forToken(token) { const headings = filterByTypes(
const styleForToken = headingStyleFor(token); params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
const styleForToken = getHeadingStyle(heading);
if (style === "consistent") { if (style === "consistent") {
style = styleForToken; style = styleForToken;
} }
if (styleForToken !== style) { if ((styleForToken !== style) && !inHtmlFlow(heading)) {
const h12 = /h[12]/.test(token.tag); const h12 = getHeadingLevel(heading) <= 2;
const setextWithAtx = const setextWithAtx =
(style === "setext_with_atx") && (style === "setext_with_atx") &&
((h12 && (styleForToken === "setext")) || ((h12 && (styleForToken === "setext")) ||
@ -3386,11 +3412,15 @@ module.exports = {
} else if (style === "setext_with_atx_closed") { } else if (style === "setext_with_atx_closed") {
expected = h12 ? "setext" : "atx_closed"; expected = h12 ? "setext" : "atx_closed";
} }
addErrorDetailIf(onError, token.lineNumber, addErrorDetailIf(
expected, styleForToken); onError,
heading.startLine,
expected,
styleForToken
);
}
} }
} }
});
} }
}; };
@ -4061,8 +4091,8 @@ module.exports = {
lineNumber, lineNumber,
length, length,
line.length, line.length,
null, undefined,
null, undefined,
[ length + 1, line.length - length ]); [ length + 1, line.length - length ]);
} }
}); });
@ -4226,8 +4256,8 @@ module.exports = {
onError, onError,
lineNumber, lineNumber,
line.trim(), line.trim(),
null, undefined,
null, undefined,
[ 1, hashLength + spacesLength + 1 ], [ 1, hashLength + spacesLength + 1 ],
{ {
"editColumn": hashLength + 1, "editColumn": hashLength + 1,

View file

@ -309,6 +309,26 @@ function getHeadingLevel(heading) {
return level; return level;
} }
/**
* Gets the heading style of a Micromark heading tokan.
*
* @param {Token} heading Micromark heading token.
* @returns {"atx" | "atx_closed" | "setext"} Heading style.
*/
function getHeadingStyle(heading) {
if (heading.type === "setextHeading") {
return "setext";
}
const atxHeadingSequenceLength = filterByTypes(
heading.children,
[ "atxHeadingSequence" ]
).length;
if (atxHeadingSequenceLength === 1) {
return "atx";
}
return "atx_closed";
}
/** /**
* HTML tag information. * HTML tag information.
* *
@ -433,6 +453,7 @@ module.exports = {
filterByPredicate, filterByPredicate,
filterByTypes, filterByTypes,
getHeadingLevel, getHeadingLevel,
getHeadingStyle,
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getTokenParentOfType, getTokenParentOfType,

View file

@ -2,8 +2,9 @@
"use strict"; "use strict";
const { addErrorDetailIf, filterTokens, headingStyleFor } = const { addErrorDetailIf } = require("../helpers");
require("../helpers"); const { filterByTypes, getHeadingLevel, getHeadingStyle, inHtmlFlow } =
require("../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -11,16 +12,20 @@ module.exports = {
"names": [ "MD003", "heading-style" ], "names": [ "MD003", "heading-style" ],
"description": "Heading style", "description": "Heading style",
"tags": [ "headings" ], "tags": [ "headings" ],
"parser": "markdownit", "parser": "micromark",
"function": function MD003(params, onError) { "function": function MD003(params, onError) {
let style = String(params.config.style || "consistent"); let style = String(params.config.style || "consistent");
filterTokens(params, "heading_open", function forToken(token) { const headings = filterByTypes(
const styleForToken = headingStyleFor(token); params.parsers.micromark.tokens,
[ "atxHeading", "setextHeading" ]
);
for (const heading of headings) {
const styleForToken = getHeadingStyle(heading);
if (style === "consistent") { if (style === "consistent") {
style = styleForToken; style = styleForToken;
} }
if (styleForToken !== style) { if ((styleForToken !== style) && !inHtmlFlow(heading)) {
const h12 = /h[12]/.test(token.tag); const h12 = getHeadingLevel(heading) <= 2;
const setextWithAtx = const setextWithAtx =
(style === "setext_with_atx") && (style === "setext_with_atx") &&
((h12 && (styleForToken === "setext")) || ((h12 && (styleForToken === "setext")) ||
@ -36,10 +41,14 @@ module.exports = {
} else if (style === "setext_with_atx_closed") { } else if (style === "setext_with_atx_closed") {
expected = h12 ? "setext" : "atx_closed"; expected = h12 ? "setext" : "atx_closed";
} }
addErrorDetailIf(onError, token.lineNumber, addErrorDetailIf(
expected, styleForToken); onError,
heading.startLine,
expected,
styleForToken
);
}
} }
} }
});
} }
}; };

View file

@ -90,8 +90,8 @@ module.exports = {
lineNumber, lineNumber,
length, length,
line.length, line.length,
null, undefined,
null, undefined,
[ length + 1, line.length - length ]); [ length + 1, line.length - length ]);
} }
}); });

View file

@ -27,8 +27,8 @@ module.exports = {
onError, onError,
lineNumber, lineNumber,
line.trim(), line.trim(),
null, undefined,
null, undefined,
[ 1, hashLength + spacesLength + 1 ], [ 1, hashLength + spacesLength + 1 ],
{ {
"editColumn": hashLength + 1, "editColumn": hashLength + 1,

View file

@ -12,12 +12,13 @@
## Heading 7 {MD021} ## ## Heading 7 {MD021} ##
## Heading 8 {MD003}\# ## Heading 8\#
## Heading 9 {MD003} \# ## Heading 9 \#
## Heading 10 {MD003} \# ## Heading 10 \#
<!-- markdownlint-configure-file { <!-- markdownlint-configure-file {
"first-line-heading": false "first-line-heading": false,
"heading-style": false
} --> } -->

View file

@ -34,4 +34,4 @@ Text
</p> </p>
<!-- markdownlint-disable-file MD013 MD033 --> <!-- markdownlint-disable-file line-length no-inline-html -->

View file

@ -14,8 +14,8 @@
### Heading with trailing hash {MD003} ## ### Heading with trailing hash {MD003} ##
### Heading with trailing hash no space{MD003}{MD020}## ### Heading with trailing hash no space{MD020}##
### Heading with trailing hash {MD003}{MD020} \## ### Heading with trailing hash {MD020} \##
### Heading with trailing hash no space{MD003}{MD020}\## ### Heading with trailing hash no space{MD020}\##

View file

@ -2786,45 +2786,6 @@ Generated by [AVA](https://avajs.dev).
{ {
errors: [ errors: [
{
errorContext: null,
errorDetail: 'Expected: atx_closed; Actual: atx',
errorRange: null,
fixInfo: null,
lineNumber: 15,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{
errorContext: null,
errorDetail: 'Expected: atx_closed; Actual: atx',
errorRange: null,
fixInfo: null,
lineNumber: 17,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{
errorContext: null,
errorDetail: 'Expected: atx_closed; Actual: atx',
errorRange: null,
fixInfo: null,
lineNumber: 19,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{ {
errorContext: '#Heading 1 {MD020} #', errorContext: '#Heading 1 {MD020} #',
errorDetail: null, errorDetail: null,
@ -2980,14 +2941,15 @@ Generated by [AVA](https://avajs.dev).
## Heading 7 {MD021} ##␊ ## Heading 7 {MD021} ##␊
## Heading 8 {MD003}\\#␊ ## Heading 8\\#␊
## Heading 9 {MD003} \\#␊ ## Heading 9 \\#␊
## Heading 10 {MD003} \\#␊ ## Heading 10 \\#␊
<!-- markdownlint-configure-file {␊ <!-- markdownlint-configure-file {␊
"first-line-heading": false␊ "first-line-heading": false,␊
"heading-style": false␊
} -->␊ } -->␊
`, `,
} }
@ -38202,7 +38164,7 @@ Generated by [AVA](https://avajs.dev).
</p> </p>
<!-- markdownlint-disable-file MD013 MD033 --> <!-- markdownlint-disable-file line-length no-inline-html -->
`, `,
} }
@ -38250,55 +38212,16 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
{ {
errorContext: null, errorContext: '...railing hash no space{MD020}##',
errorDetail: 'Expected: atx; Actual: atx_closed',
errorRange: null,
fixInfo: null,
lineNumber: 17,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{
errorContext: null,
errorDetail: 'Expected: atx; Actual: atx_closed',
errorRange: null,
fixInfo: null,
lineNumber: 19,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{
errorContext: null,
errorDetail: 'Expected: atx; Actual: atx_closed',
errorRange: null,
fixInfo: null,
lineNumber: 21,
ruleDescription: 'Heading style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md003.md',
ruleNames: [
'MD003',
'heading-style',
],
},
{
errorContext: '... hash no space{MD003}{MD020}##',
errorDetail: null, errorDetail: null,
errorRange: [ errorRange: [
53, 46,
3, 3,
], ],
fixInfo: { fixInfo: {
deleteCount: 55, deleteCount: 48,
editColumn: 1, editColumn: 1,
insertText: '### Heading with trailing hash no space{MD003}{MD020} ##', insertText: '### Heading with trailing hash no space{MD020} ##',
}, },
lineNumber: 17, lineNumber: 17,
ruleDescription: 'No space inside hashes on closed atx style heading', ruleDescription: 'No space inside hashes on closed atx style heading',
@ -38309,7 +38232,27 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
{ {
errorContext: '...ailing hash {MD003}{MD020} \\##', errorContext: '...with trailing hash {MD020} \\##',
errorDetail: null,
errorRange: [
41,
2,
],
fixInfo: {
deleteCount: 42,
editColumn: 1,
insertText: '### Heading with trailing hash {MD020} \\# #',
},
lineNumber: 19,
ruleDescription: 'No space inside hashes on closed atx style heading',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md020.md',
ruleNames: [
'MD020',
'no-missing-space-closed-atx',
],
},
{
errorContext: '...ailing hash no space{MD020}\\##',
errorDetail: null, errorDetail: null,
errorRange: [ errorRange: [
48, 48,
@ -38318,27 +38261,7 @@ Generated by [AVA](https://avajs.dev).
fixInfo: { fixInfo: {
deleteCount: 49, deleteCount: 49,
editColumn: 1, editColumn: 1,
insertText: '### Heading with trailing hash {MD003}{MD020} \\# #', insertText: '### Heading with trailing hash no space{MD020} \\# #',
},
lineNumber: 19,
ruleDescription: 'No space inside hashes on closed atx style heading',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md020.md',
ruleNames: [
'MD020',
'no-missing-space-closed-atx',
],
},
{
errorContext: '...hash no space{MD003}{MD020}\\##',
errorDetail: null,
errorRange: [
55,
2,
],
fixInfo: {
deleteCount: 56,
editColumn: 1,
insertText: '### Heading with trailing hash no space{MD003}{MD020} \\# #',
}, },
lineNumber: 21, lineNumber: 21,
ruleDescription: 'No space inside hashes on closed atx style heading', ruleDescription: 'No space inside hashes on closed atx style heading',
@ -38365,11 +38288,11 @@ Generated by [AVA](https://avajs.dev).
### Heading with trailing hash {MD003} ##␊ ### Heading with trailing hash {MD003} ##␊
### Heading with trailing hash no space{MD003}{MD020} ##␊ ### Heading with trailing hash no space{MD020} ##␊
### Heading with trailing hash {MD003}{MD020} \\# #␊ ### Heading with trailing hash {MD020} \\# #␊
### Heading with trailing hash no space{MD003}{MD020} \\# #␊ ### Heading with trailing hash no space{MD020} \\# #␊
`, `,
} }