mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 14:00:13 +01:00
Refactor various code to do shallow/constrained search instead of deep search for better performance, make cache key for filterByTypesCached unique.
This commit is contained in:
parent
2fa7730a6b
commit
d22c1f19ef
12 changed files with 171 additions and 220 deletions
|
|
@ -7,7 +7,6 @@ const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
|||
|
||||
/** @type {Map<string, object>} */
|
||||
const map = new Map();
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let params = undefined;
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +45,8 @@ function getCached(name, getValue) {
|
|||
*/
|
||||
function filterByTypesCached(types, htmlFlow) {
|
||||
return getCached(
|
||||
types.join("|"),
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
JSON.stringify(arguments),
|
||||
() => filterByTypes(params.parsers.micromark.tokens, types, htmlFlow)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorContext } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
||||
const { filterByTypesCached } = require("./cache");
|
||||
|
||||
const dollarCommandRe = /^(\s*)(\$\s+)/;
|
||||
|
|
@ -17,10 +16,7 @@ module.exports = {
|
|||
"parser": "micromark",
|
||||
"function": function MD014(params, onError) {
|
||||
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
|
||||
const codeFlowValues = filterByTypes(
|
||||
codeBlock.children,
|
||||
[ "codeFlowValue" ]
|
||||
);
|
||||
const codeFlowValues = codeBlock.children.filter((child) => child.type === "codeFlowValue");
|
||||
const dollarMatches = codeFlowValues
|
||||
.map((codeFlowValue) => ({
|
||||
"result": codeFlowValue.text.match(dollarCommandRe),
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ module.exports = {
|
|||
const spaceRight = rightSpaceRe.test(endData.text);
|
||||
if (spaceLeft || spaceRight) {
|
||||
let lineNumber = startSequence.startLine;
|
||||
let range = null;
|
||||
let fixInfo = null;
|
||||
let range = undefined;
|
||||
let fixInfo = undefined;
|
||||
if (startSequence.startLine === endSequence.endLine) {
|
||||
range = [
|
||||
startSequence.startColumn,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorContext } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
||||
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
|
||||
|
||||
/**
|
||||
|
|
@ -62,10 +61,7 @@ module.exports = {
|
|||
const labels = filterByTypesCached([ "label" ])
|
||||
.filter((label) => label.parent?.type === "link");
|
||||
for (const label of labels) {
|
||||
const labelTexts = filterByTypes(
|
||||
label.children,
|
||||
[ "labelText" ]
|
||||
);
|
||||
const labelTexts = label.children.filter((child) => child.type === "labelText");
|
||||
for (const labelText of labelTexts) {
|
||||
if (
|
||||
(labelText.text.trimStart().length !== labelText.text.length) &&
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addError, getHtmlAttributeRe, nextLinesRe } = require("../helpers");
|
||||
const { filterByTypes, getHtmlTagInfo } = require("../helpers/micromark-helpers.cjs");
|
||||
const { getHtmlTagInfo, getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
|
||||
const { filterByTypesCached } = require("./cache");
|
||||
|
||||
const altRe = getHtmlAttributeRe("alt");
|
||||
|
|
@ -19,7 +19,7 @@ module.exports = {
|
|||
// Process Markdown images
|
||||
const images = filterByTypesCached([ "image" ]);
|
||||
for (const image of images) {
|
||||
const labelTexts = filterByTypes(image.children, [ "labelText" ]);
|
||||
const labelTexts = getDescendantsByType(image, [ "label", "labelText" ]);
|
||||
if (labelTexts.some((labelText) => labelText.text.length === 0)) {
|
||||
const range = (image.startLine === image.endLine) ?
|
||||
[ image.startColumn, image.endColumn - image.startColumn ] :
|
||||
|
|
|
|||
|
|
@ -124,11 +124,8 @@ module.exports = {
|
|||
!fragments.has(encodedText) &&
|
||||
!lineFragmentRe.test(encodedText)
|
||||
) {
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let context = undefined;
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let range = undefined;
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let fixInfo = undefined;
|
||||
if (link.startLine === link.endLine) {
|
||||
context = link.text;
|
||||
|
|
|
|||
|
|
@ -82,9 +82,8 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
if (isError) {
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let range = undefined;
|
||||
let fixInfo = null;
|
||||
let fixInfo = undefined;
|
||||
if (startLine === endLine) {
|
||||
range = [ startColumn, endColumn - startColumn ];
|
||||
let insertText = null;
|
||||
|
|
|
|||
85
lib/md055.js
85
lib/md055.js
|
|
@ -3,7 +3,6 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
||||
const { filterByTypesCached } = require("./cache");
|
||||
|
||||
const whitespaceTypes = new Set([ "linePrefix", "whitespace" ]);
|
||||
|
|
@ -28,51 +27,45 @@ module.exports = {
|
|||
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "trailing_only"));
|
||||
let expectedTrailingPipe =
|
||||
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "leading_only"));
|
||||
const tables = filterByTypesCached([ "table" ]);
|
||||
for (const table of tables) {
|
||||
const rows = filterByTypes(
|
||||
table.children,
|
||||
[ "tableDelimiterRow", "tableRow" ]
|
||||
);
|
||||
for (const row of rows) {
|
||||
// The following uses of first/lastOrNothing lack fallback handling
|
||||
// because it seems not to be possible (i.e., 0% coverage)
|
||||
const firstCell = firstOrNothing(row.children);
|
||||
const leadingToken = firstOrNothing(ignoreWhitespace(firstCell.children));
|
||||
const actualLeadingPipe = (leadingToken.type === "tableCellDivider");
|
||||
const lastCell = lastOrNothing(row.children);
|
||||
const trailingToken = lastOrNothing(ignoreWhitespace(lastCell.children));
|
||||
const actualTrailingPipe = (trailingToken.type === "tableCellDivider");
|
||||
const actualStyle = actualLeadingPipe ?
|
||||
(actualTrailingPipe ? "leading_and_trailing" : "leading_only") :
|
||||
(actualTrailingPipe ? "trailing_only" : "no_leading_or_trailing");
|
||||
if (expectedStyle === "consistent") {
|
||||
expectedStyle = actualStyle;
|
||||
expectedLeadingPipe = actualLeadingPipe;
|
||||
expectedTrailingPipe = actualTrailingPipe;
|
||||
}
|
||||
if (actualLeadingPipe !== expectedLeadingPipe) {
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
firstCell.startLine,
|
||||
expectedStyle,
|
||||
actualStyle,
|
||||
`${expectedLeadingPipe ? "Missing" : "Unexpected"} leading pipe`,
|
||||
undefined,
|
||||
makeRange(row.startColumn, firstCell.startColumn)
|
||||
);
|
||||
}
|
||||
if (actualTrailingPipe !== expectedTrailingPipe) {
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
lastCell.endLine,
|
||||
expectedStyle,
|
||||
actualStyle,
|
||||
`${expectedTrailingPipe ? "Missing" : "Unexpected"} trailing pipe`,
|
||||
undefined,
|
||||
makeRange(lastCell.endColumn - 1, row.endColumn - 1)
|
||||
);
|
||||
}
|
||||
const rows = filterByTypesCached([ "tableDelimiterRow", "tableRow" ]);
|
||||
for (const row of rows) {
|
||||
// The following uses of first/lastOrNothing lack fallback handling
|
||||
// because it seems not to be possible (i.e., 0% coverage)
|
||||
const firstCell = firstOrNothing(row.children);
|
||||
const leadingToken = firstOrNothing(ignoreWhitespace(firstCell.children));
|
||||
const actualLeadingPipe = (leadingToken.type === "tableCellDivider");
|
||||
const lastCell = lastOrNothing(row.children);
|
||||
const trailingToken = lastOrNothing(ignoreWhitespace(lastCell.children));
|
||||
const actualTrailingPipe = (trailingToken.type === "tableCellDivider");
|
||||
const actualStyle = actualLeadingPipe ?
|
||||
(actualTrailingPipe ? "leading_and_trailing" : "leading_only") :
|
||||
(actualTrailingPipe ? "trailing_only" : "no_leading_or_trailing");
|
||||
if (expectedStyle === "consistent") {
|
||||
expectedStyle = actualStyle;
|
||||
expectedLeadingPipe = actualLeadingPipe;
|
||||
expectedTrailingPipe = actualTrailingPipe;
|
||||
}
|
||||
if (actualLeadingPipe !== expectedLeadingPipe) {
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
firstCell.startLine,
|
||||
expectedStyle,
|
||||
actualStyle,
|
||||
`${expectedLeadingPipe ? "Missing" : "Unexpected"} leading pipe`,
|
||||
undefined,
|
||||
makeRange(row.startColumn, firstCell.startColumn)
|
||||
);
|
||||
}
|
||||
if (actualTrailingPipe !== expectedTrailingPipe) {
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
lastCell.endLine,
|
||||
expectedStyle,
|
||||
actualStyle,
|
||||
`${expectedTrailingPipe ? "Missing" : "Unexpected"} trailing pipe`,
|
||||
undefined,
|
||||
makeRange(lastCell.endColumn - 1, row.endColumn - 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
65
lib/md056.js
65
lib/md056.js
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
||||
const { getParentOfType } = require("../helpers/micromark-helpers.cjs");
|
||||
const { filterByTypesCached } = require("./cache");
|
||||
|
||||
const makeRange = (start, end) => [ start, end - start + 1 ];
|
||||
|
|
@ -16,41 +16,36 @@ module.exports = {
|
|||
"tags": [ "table" ],
|
||||
"parser": "micromark",
|
||||
"function": function MD056(params, onError) {
|
||||
const tables = filterByTypesCached([ "table" ]);
|
||||
for (const table of tables) {
|
||||
const rows = filterByTypes(
|
||||
table.children,
|
||||
[ "tableDelimiterRow", "tableRow" ]
|
||||
);
|
||||
let expectedCount = 0;
|
||||
for (const row of rows) {
|
||||
const cells = filterByTypes(
|
||||
row.children,
|
||||
[ "tableData", "tableDelimiter", "tableHeader" ]
|
||||
);
|
||||
const actualCount = cells.length;
|
||||
expectedCount ||= actualCount;
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let detail = undefined;
|
||||
// eslint-disable-next-line no-undef-init
|
||||
let range = undefined;
|
||||
if (actualCount < expectedCount) {
|
||||
detail = "Too few cells, row will be missing data";
|
||||
range = [ row.endColumn - 1, 1 ];
|
||||
} else if (expectedCount < actualCount) {
|
||||
detail = "Too many cells, extra data will be missing";
|
||||
range = makeRange(cells[expectedCount].startColumn, row.endColumn - 1);
|
||||
}
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
row.endLine,
|
||||
expectedCount,
|
||||
actualCount,
|
||||
detail,
|
||||
undefined,
|
||||
range
|
||||
);
|
||||
const rows = filterByTypesCached([ "tableDelimiterRow", "tableRow" ]);
|
||||
let expectedCount = 0;
|
||||
let currentTable = null;
|
||||
for (const row of rows) {
|
||||
const table = getParentOfType(row, [ "table" ]);
|
||||
if (currentTable !== table) {
|
||||
expectedCount = 0;
|
||||
currentTable = table;
|
||||
}
|
||||
const cells = row.children.filter((child) => [ "tableData", "tableDelimiter", "tableHeader" ].includes(child.type));
|
||||
const actualCount = cells.length;
|
||||
expectedCount ||= actualCount;
|
||||
let detail = undefined;
|
||||
let range = undefined;
|
||||
if (actualCount < expectedCount) {
|
||||
detail = "Too few cells, row will be missing data";
|
||||
range = [ row.endColumn - 1, 1 ];
|
||||
} else if (expectedCount < actualCount) {
|
||||
detail = "Too many cells, extra data will be missing";
|
||||
range = makeRange(cells[expectedCount].startColumn, row.endColumn - 1);
|
||||
}
|
||||
addErrorDetailIf(
|
||||
onError,
|
||||
row.endLine,
|
||||
expectedCount,
|
||||
actualCount,
|
||||
detail,
|
||||
undefined,
|
||||
range
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue