Make inHtmlFlow more efficient and simplify filterByTypes implementation.

This commit is contained in:
David Anson 2024-06-10 22:16:20 -07:00
parent 12502f6571
commit 106ca0bc90
2 changed files with 64 additions and 78 deletions

View file

@ -1240,8 +1240,8 @@ const {
} = __webpack_require__(/*! markdownlint-micromark */ "markdownlint-micromark"); } = __webpack_require__(/*! markdownlint-micromark */ "markdownlint-micromark");
const { newLineRe } = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js"); const { newLineRe } = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js");
const flatTokensWithHtmlFlowSymbol = Symbol("flat-tokens-with-html-flow"); const flatTokensSymbol = Symbol("flat-tokens");
const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow"); const reparseSymbol = Symbol("reparse");
/** @typedef {import("markdownlint-micromark").Event} Event */ /** @typedef {import("markdownlint-micromark").Event} Event */
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */ /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
@ -1249,29 +1249,13 @@ const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow");
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
/** /**
* Gets the nearest parent of the specified type for a Micromark token. * Determines if a Micromark token is within an htmlFlow.
* *
* @param {Token} token Micromark token. * @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow. * @returns {boolean} True iff the token is within an htmlFlow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/ */
function inHtmlFlow(token) { function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null; return token[reparseSymbol];
} }
/** /**
@ -1364,8 +1348,7 @@ function micromarkParseWithOffset(
// Create Token objects // Create Token objects
const document = []; const document = [];
let flatTokensWithHtmlFlow = []; let flatTokens = [];
const flatTokensWithoutHtmlFlow = [];
/** @type {Token} */ /** @type {Token} */
const root = { const root = {
"type": "data", "type": "data",
@ -1403,9 +1386,11 @@ function micromarkParseWithOffset(
"children": [], "children": [],
"parent": ((previous === root) ? (ancestor || null) : previous) "parent": ((previous === root) ? (ancestor || null) : previous)
}; };
if (ancestor) {
Object.defineProperty(current, reparseSymbol, { "value": true });
}
previous.children.push(current); previous.children.push(current);
flatTokensWithHtmlFlow.push(current); flatTokens.push(current);
flatTokensWithoutHtmlFlow.push(current);
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) { if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
skipHtmlFlowChildren = true; skipHtmlFlowChildren = true;
if (!reparseOptions || !lines) { if (!reparseOptions || !lines) {
@ -1434,7 +1419,7 @@ function micromarkParseWithOffset(
current.children = tokens; current.children = tokens;
// Avoid stack overflow of Array.push(...spread) // Avoid stack overflow of Array.push(...spread)
// eslint-disable-next-line unicorn/prefer-spread // eslint-disable-next-line unicorn/prefer-spread
flatTokensWithHtmlFlow = flatTokensWithHtmlFlow.concat(tokens[flatTokensWithHtmlFlowSymbol]); flatTokens = flatTokens.concat(tokens[flatTokensSymbol]);
} }
} else if (kind === "exit") { } else if (kind === "exit") {
if (type === "htmlFlow") { if (type === "htmlFlow") {
@ -1450,8 +1435,7 @@ function micromarkParseWithOffset(
} }
// Return document // Return document
Object.defineProperty(document, flatTokensWithHtmlFlowSymbol, { "value": flatTokensWithHtmlFlow }); Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens });
Object.defineProperty(document, flatTokensWithoutHtmlFlowSymbol, { "value": flatTokensWithoutHtmlFlow });
Object.freeze(document); Object.freeze(document);
return document; return document;
} }
@ -1541,20 +1525,13 @@ function filterByPredicate(tokens, allowed, transformChildren) {
* @returns {Token[]} Filtered tokens. * @returns {Token[]} Filtered tokens.
*/ */
function filterByTypes(tokens, types, htmlFlow) { function filterByTypes(tokens, types, htmlFlow) {
const predicate = (token) => types.includes(token.type); const predicate = (token) =>
const flatTokens = tokens[ (htmlFlow || !inHtmlFlow(token)) && types.includes(token.type);
htmlFlow ? const flatTokens = tokens[flatTokensSymbol];
flatTokensWithHtmlFlowSymbol :
flatTokensWithoutHtmlFlowSymbol
];
if (flatTokens) { if (flatTokens) {
return flatTokens.filter(predicate); return flatTokens.filter(predicate);
} }
const result = filterByPredicate(tokens, predicate); return filterByPredicate(tokens, predicate);
if (htmlFlow) {
return result;
}
return result.filter((token) => !inHtmlFlow(token));
} }
/** /**
@ -1628,6 +1605,22 @@ function getHtmlTagInfo(token) {
return null; return null;
} }
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/** /**
* Get the text of the first match from a list of Micromark tokens by type. * Get the text of the first match from a list of Micromark tokens by type.
* *

View file

@ -10,8 +10,8 @@ const {
} = require("markdownlint-micromark"); } = require("markdownlint-micromark");
const { newLineRe } = require("./shared.js"); const { newLineRe } = require("./shared.js");
const flatTokensWithHtmlFlowSymbol = Symbol("flat-tokens-with-html-flow"); const flatTokensSymbol = Symbol("flat-tokens");
const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow"); const reparseSymbol = Symbol("reparse");
/** @typedef {import("markdownlint-micromark").Event} Event */ /** @typedef {import("markdownlint-micromark").Event} Event */
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */ /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
@ -19,29 +19,13 @@ const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow");
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
/** /**
* Gets the nearest parent of the specified type for a Micromark token. * Determines if a Micromark token is within an htmlFlow.
* *
* @param {Token} token Micromark token. * @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow. * @returns {boolean} True iff the token is within an htmlFlow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/ */
function inHtmlFlow(token) { function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null; return token[reparseSymbol];
} }
/** /**
@ -134,8 +118,7 @@ function micromarkParseWithOffset(
// Create Token objects // Create Token objects
const document = []; const document = [];
let flatTokensWithHtmlFlow = []; let flatTokens = [];
const flatTokensWithoutHtmlFlow = [];
/** @type {Token} */ /** @type {Token} */
const root = { const root = {
"type": "data", "type": "data",
@ -173,9 +156,11 @@ function micromarkParseWithOffset(
"children": [], "children": [],
"parent": ((previous === root) ? (ancestor || null) : previous) "parent": ((previous === root) ? (ancestor || null) : previous)
}; };
if (ancestor) {
Object.defineProperty(current, reparseSymbol, { "value": true });
}
previous.children.push(current); previous.children.push(current);
flatTokensWithHtmlFlow.push(current); flatTokens.push(current);
flatTokensWithoutHtmlFlow.push(current);
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) { if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
skipHtmlFlowChildren = true; skipHtmlFlowChildren = true;
if (!reparseOptions || !lines) { if (!reparseOptions || !lines) {
@ -204,7 +189,7 @@ function micromarkParseWithOffset(
current.children = tokens; current.children = tokens;
// Avoid stack overflow of Array.push(...spread) // Avoid stack overflow of Array.push(...spread)
// eslint-disable-next-line unicorn/prefer-spread // eslint-disable-next-line unicorn/prefer-spread
flatTokensWithHtmlFlow = flatTokensWithHtmlFlow.concat(tokens[flatTokensWithHtmlFlowSymbol]); flatTokens = flatTokens.concat(tokens[flatTokensSymbol]);
} }
} else if (kind === "exit") { } else if (kind === "exit") {
if (type === "htmlFlow") { if (type === "htmlFlow") {
@ -220,8 +205,7 @@ function micromarkParseWithOffset(
} }
// Return document // Return document
Object.defineProperty(document, flatTokensWithHtmlFlowSymbol, { "value": flatTokensWithHtmlFlow }); Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens });
Object.defineProperty(document, flatTokensWithoutHtmlFlowSymbol, { "value": flatTokensWithoutHtmlFlow });
Object.freeze(document); Object.freeze(document);
return document; return document;
} }
@ -311,20 +295,13 @@ function filterByPredicate(tokens, allowed, transformChildren) {
* @returns {Token[]} Filtered tokens. * @returns {Token[]} Filtered tokens.
*/ */
function filterByTypes(tokens, types, htmlFlow) { function filterByTypes(tokens, types, htmlFlow) {
const predicate = (token) => types.includes(token.type); const predicate = (token) =>
const flatTokens = tokens[ (htmlFlow || !inHtmlFlow(token)) && types.includes(token.type);
htmlFlow ? const flatTokens = tokens[flatTokensSymbol];
flatTokensWithHtmlFlowSymbol :
flatTokensWithoutHtmlFlowSymbol
];
if (flatTokens) { if (flatTokens) {
return flatTokens.filter(predicate); return flatTokens.filter(predicate);
} }
const result = filterByPredicate(tokens, predicate); return filterByPredicate(tokens, predicate);
if (htmlFlow) {
return result;
}
return result.filter((token) => !inHtmlFlow(token));
} }
/** /**
@ -398,6 +375,22 @@ function getHtmlTagInfo(token) {
return null; return null;
} }
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/** /**
* Get the text of the first match from a list of Micromark tokens by type. * Get the text of the first match from a list of Micromark tokens by type.
* *