Reimplement MD028/no-blanks-blockquote using micromark tokens.

This commit is contained in:
David Anson 2024-02-18 16:46:13 -08:00
parent 89eef68dbd
commit f5b5773b50
4 changed files with 94 additions and 30 deletions

View file

@ -1473,6 +1473,28 @@ function filterByTypes(tokens, types) {
return filterByPredicate(tokens, predicate); return filterByPredicate(tokens, predicate);
} }
/**
* Gets all sibling token groups for a list of Micromark tokens.
*
* @param {Token[]} tokens Micromark tokens.
* @returns {Token[][]} Sibling token groups.
*/
function getSiblingTokens(tokens) {
const result = [];
const queue = [ tokens ];
// eslint-disable-next-line init-declarations
let current;
while ((current = queue.shift())) {
result.push(current);
for (const token of current) {
if (token.children.length > 0) {
queue.push(token.children);
}
}
}
return result;
}
/** /**
* Gets the heading level of a Micromark heading tokan. * Gets the heading level of a Micromark heading tokan.
* *
@ -1597,6 +1619,7 @@ module.exports = {
getHeadingLevel, getHeadingLevel,
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getSiblingLists: getSiblingTokens,
getTokenParentOfType, getTokenParentOfType,
getTokenTextByType, getTokenTextByType,
inHtmlFlow, inHtmlFlow,
@ -4464,7 +4487,6 @@ const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
endOfLineHtmlEntityRe, escapeForRegExp } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); endOfLineHtmlEntityRe, escapeForRegExp } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
module.exports = { module.exports = {
"names": [ "MD026", "no-trailing-punctuation" ], "names": [ "MD026", "no-trailing-punctuation" ],
"description": "Trailing punctuation in heading", "description": "Trailing punctuation in heading",
@ -4589,28 +4611,38 @@ module.exports = {
const { addError } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addError } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { getSiblingLists } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
module.exports = { module.exports = {
"names": [ "MD028", "no-blanks-blockquote" ], "names": [ "MD028", "no-blanks-blockquote" ],
"description": "Blank line inside blockquote", "description": "Blank line inside blockquote",
"tags": [ "blockquote", "whitespace" ], "tags": [ "blockquote", "whitespace" ],
"function": function MD028(params, onError) { "function": function MD028(params, onError) {
let prevToken = {}; for (const siblings of getSiblingLists(params.parsers.micromark.tokens)) {
let prevLineNumber = null; let errorLineNumbers = null;
for (const token of params.parsers.markdownit.tokens) { for (const sibling of siblings) {
if ((token.type === "blockquote_open") && switch (sibling.type) {
(prevToken.type === "blockquote_close")) { case "blockQuote":
for ( for (const lineNumber of (errorLineNumbers || [])) {
let lineNumber = prevLineNumber; addError(onError, lineNumber);
lineNumber < token.lineNumber; }
lineNumber++) { errorLineNumbers = [];
addError(onError, lineNumber); break;
case "lineEnding":
case "linePrefix":
case "listItemIndent":
// Ignore
break;
case "lineEndingBlank":
if (errorLineNumbers) {
errorLineNumbers.push(sibling.startLine);
}
break;
default:
errorLineNumbers = null;
break;
} }
} }
prevToken = token;
if (token.type === "blockquote_open") {
prevLineNumber = token.map[1] + 1;
}
} }
} }
}; };

View file

@ -295,6 +295,28 @@ function filterByTypes(tokens, types) {
return filterByPredicate(tokens, predicate); return filterByPredicate(tokens, predicate);
} }
/**
* Gets all sibling token groups for a list of Micromark tokens.
*
* @param {Token[]} tokens Micromark tokens.
* @returns {Token[][]} Sibling token groups.
*/
function getSiblingTokens(tokens) {
const result = [];
const queue = [ tokens ];
// eslint-disable-next-line init-declarations
let current;
while ((current = queue.shift())) {
result.push(current);
for (const token of current) {
if (token.children.length > 0) {
queue.push(token.children);
}
}
}
return result;
}
/** /**
* Gets the heading level of a Micromark heading tokan. * Gets the heading level of a Micromark heading tokan.
* *
@ -419,6 +441,7 @@ module.exports = {
getHeadingLevel, getHeadingLevel,
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getSiblingLists: getSiblingTokens,
getTokenParentOfType, getTokenParentOfType,
getTokenTextByType, getTokenTextByType,
inHtmlFlow, inHtmlFlow,

View file

@ -6,7 +6,6 @@ const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
endOfLineHtmlEntityRe, escapeForRegExp } = require("../helpers"); endOfLineHtmlEntityRe, escapeForRegExp } = require("../helpers");
const { filterByTypes } = require("../helpers/micromark.cjs"); const { filterByTypes } = require("../helpers/micromark.cjs");
module.exports = { module.exports = {
"names": [ "MD026", "no-trailing-punctuation" ], "names": [ "MD026", "no-trailing-punctuation" ],
"description": "Trailing punctuation in heading", "description": "Trailing punctuation in heading",

View file

@ -3,28 +3,38 @@
"use strict"; "use strict";
const { addError } = require("../helpers"); const { addError } = require("../helpers");
const { getSiblingLists } = require("../helpers/micromark.cjs");
module.exports = { module.exports = {
"names": [ "MD028", "no-blanks-blockquote" ], "names": [ "MD028", "no-blanks-blockquote" ],
"description": "Blank line inside blockquote", "description": "Blank line inside blockquote",
"tags": [ "blockquote", "whitespace" ], "tags": [ "blockquote", "whitespace" ],
"function": function MD028(params, onError) { "function": function MD028(params, onError) {
let prevToken = {}; for (const siblings of getSiblingLists(params.parsers.micromark.tokens)) {
let prevLineNumber = null; let errorLineNumbers = null;
for (const token of params.parsers.markdownit.tokens) { for (const sibling of siblings) {
if ((token.type === "blockquote_open") && switch (sibling.type) {
(prevToken.type === "blockquote_close")) { case "blockQuote":
for ( for (const lineNumber of (errorLineNumbers || [])) {
let lineNumber = prevLineNumber; addError(onError, lineNumber);
lineNumber < token.lineNumber; }
lineNumber++) { errorLineNumbers = [];
addError(onError, lineNumber); break;
case "lineEnding":
case "linePrefix":
case "listItemIndent":
// Ignore
break;
case "lineEndingBlank":
if (errorLineNumbers) {
errorLineNumbers.push(sibling.startLine);
}
break;
default:
errorLineNumbers = null;
break;
} }
} }
prevToken = token;
if (token.type === "blockquote_open") {
prevLineNumber = token.map[1] + 1;
}
} }
} }
}; };