Refactor lintContent to do less processing of errors for each rule and instead handle all errors at the end.

This commit is contained in:
David Anson 2021-12-04 17:02:11 -08:00
parent e531bd6359
commit d3c56d3ab8
2 changed files with 119 additions and 143 deletions

View file

@ -1306,35 +1306,6 @@ function getEnabledRulesPerLineNumber(ruleList, lines, frontMatterLines, noInlin
enabledRulesPerLineNumber: enabledRulesPerLineNumber enabledRulesPerLineNumber: enabledRulesPerLineNumber
}; };
} }
/**
* Compare function for Array.prototype.sort for ascending order of errors.
*
* @param {LintError} a First error.
* @param {LintError} b Second error.
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
*/
function lineNumberComparison(a, b) {
return a.lineNumber - b.lineNumber;
}
/**
* Filter function to include everything.
*
* @returns {boolean} True.
*/
function filterAllValues() {
return true;
}
/**
* Function to return unique values from a sorted errors array.
*
* @param {LintError} value Error instance.
* @param {number} index Index in array.
* @param {LintError[]} array Array of errors.
* @returns {boolean} Filter value.
*/
function uniqueFilterForSortedErrors(value, index, array) {
return (index === 0) || (value.lineNumber > array[index - 1].lineNumber);
}
/** /**
* Lints a string containing Markdown content. * Lints a string containing Markdown content.
* *
@ -1375,7 +1346,7 @@ function lintContent(ruleList, name, content, md, config, frontMatter, handleRul
cache.flattenedLists(helpers.flattenLists(params.tokens)); cache.flattenedLists(helpers.flattenLists(params.tokens));
cache.codeBlockAndSpanRanges(helpers.codeBlockAndSpanRanges(params, cache.lineMetadata())); cache.codeBlockAndSpanRanges(helpers.codeBlockAndSpanRanges(params, cache.lineMetadata()));
// Function to run for each rule // Function to run for each rule
var result = (resultVersion === 0) ? {} : []; var results = [];
// eslint-disable-next-line jsdoc/require-jsdoc // eslint-disable-next-line jsdoc/require-jsdoc
function forRule(rule) { function forRule(rule) {
// Configure rule // Configure rule
@ -1463,7 +1434,7 @@ function lintContent(ruleList, name, content, md, config, frontMatter, handleRul
"fixInfo": fixInfo ? cleanFixInfo : null "fixInfo": fixInfo ? cleanFixInfo : null
}); });
} }
// Call (possibly external) rule function // Call (possibly external) rule function to report errors
if (handleRuleFailures) { if (handleRuleFailures) {
try { try {
rule.function(params, onError); rule.function(params, onError);
@ -1480,46 +1451,20 @@ function lintContent(ruleList, name, content, md, config, frontMatter, handleRul
} }
// Record any errors (significant performance benefit from length check) // Record any errors (significant performance benefit from length check)
if (errors.length > 0) { if (errors.length > 0) {
errors.sort(lineNumberComparison);
var filteredErrors = errors var filteredErrors = errors
.filter((resultVersion === 3) ? .filter(function (error) { return (enabledRulesPerLineNumber[error.lineNumber][ruleName]); })
filterAllValues : .map(function (error) { return ({
uniqueFilterForSortedErrors) "lineNumber": error.lineNumber,
.filter(function removeDisabledRules(error) { "ruleName": rule.names[0],
return enabledRulesPerLineNumber[error.lineNumber][ruleName]; "ruleNames": rule.names,
}) "ruleDescription": rule.description,
.map(function formatResults(error) { "ruleInformation": rule.information ? rule.information.href : null,
if (resultVersion === 0) { "errorDetail": error.detail,
return error.lineNumber; "errorContext": error.context,
} "errorRange": error.range,
var errorObject = {}; "fixInfo": error.fixInfo
errorObject.lineNumber = error.lineNumber; }); });
if (resultVersion === 1) { Array.prototype.push.apply(results, filteredErrors);
errorObject.ruleName = ruleNameFriendly;
errorObject.ruleAlias = rule.names[1] || rule.names[0];
}
else {
errorObject.ruleNames = rule.names;
}
errorObject.ruleDescription = rule.description;
errorObject.ruleInformation =
rule.information ? rule.information.href : null;
errorObject.errorDetail = error.detail;
errorObject.errorContext = error.context;
errorObject.errorRange = error.range;
if (resultVersion === 3) {
errorObject.fixInfo = error.fixInfo;
}
return errorObject;
});
if (filteredErrors.length > 0) {
if (resultVersion === 0) {
result[ruleNameFriendly] = filteredErrors;
}
else {
Array.prototype.push.apply(result, filteredErrors);
}
}
} }
} }
// Run all rules // Run all rules
@ -1531,7 +1476,50 @@ function lintContent(ruleList, name, content, md, config, frontMatter, handleRul
return callback(error); return callback(error);
} }
cache.clear(); cache.clear();
return callback(null, result); // Sort results by rule name by line number
results.sort(function (a, b) { return (a.ruleName.localeCompare(b.ruleName) ||
a.lineNumber - b.lineNumber); });
if (resultVersion < 3) {
// Remove fixInfo and multiple errors for the same rule and line number
var noPrevious_1 = {
"ruleName": null,
"lineNumber": -1
};
results = results.filter(function (error, index, array) {
delete error.fixInfo;
var previous = array[index - 1] || noPrevious_1;
return ((error.ruleName !== previous.ruleName) ||
(error.lineNumber !== previous.lineNumber));
});
}
if (resultVersion === 0) {
// Return a dictionary of rule->[line numbers]
var dictionary = {};
for (var _i = 0, results_1 = results; _i < results_1.length; _i++) {
var error = results_1[_i];
var ruleLines = dictionary[error.ruleName] || [];
ruleLines.push(error.lineNumber);
dictionary[error.ruleName] = ruleLines;
}
// @ts-ignore
results = dictionary;
}
else if (resultVersion === 1) {
// Use ruleAlias instead of ruleNames
for (var _b = 0, results_2 = results; _b < results_2.length; _b++) {
var error = results_2[_b];
error.ruleAlias = error.ruleNames[1] || error.ruleName;
delete error.ruleNames;
}
}
else {
// resultVersion 2 or 3: Remove unwanted ruleName
for (var _c = 0, results_3 = results; _c < results_3.length; _c++) {
var error = results_3[_c];
delete error.ruleName;
}
}
return callback(null, results);
} }
/** /**
* Lints a file containing Markdown content. * Lints a file containing Markdown content.

View file

@ -416,38 +416,6 @@ function getEnabledRulesPerLineNumber(
}; };
} }
/**
* Compare function for Array.prototype.sort for ascending order of errors.
*
* @param {LintError} a First error.
* @param {LintError} b Second error.
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
*/
function lineNumberComparison(a, b) {
return a.lineNumber - b.lineNumber;
}
/**
* Filter function to include everything.
*
* @returns {boolean} True.
*/
function filterAllValues() {
return true;
}
/**
* Function to return unique values from a sorted errors array.
*
* @param {LintError} value Error instance.
* @param {number} index Index in array.
* @param {LintError[]} array Array of errors.
* @returns {boolean} Filter value.
*/
function uniqueFilterForSortedErrors(value, index, array) {
return (index === 0) || (value.lineNumber > array[index - 1].lineNumber);
}
/** /**
* Lints a string containing Markdown content. * Lints a string containing Markdown content.
* *
@ -508,7 +476,7 @@ function lintContent(
helpers.codeBlockAndSpanRanges(params, cache.lineMetadata()) helpers.codeBlockAndSpanRanges(params, cache.lineMetadata())
); );
// Function to run for each rule // Function to run for each rule
const result = (resultVersion === 0) ? {} : []; let results = [];
// eslint-disable-next-line jsdoc/require-jsdoc // eslint-disable-next-line jsdoc/require-jsdoc
function forRule(rule) { function forRule(rule) {
// Configure rule // Configure rule
@ -597,7 +565,7 @@ function lintContent(
"fixInfo": fixInfo ? cleanFixInfo : null "fixInfo": fixInfo ? cleanFixInfo : null
}); });
} }
// Call (possibly external) rule function // Call (possibly external) rule function to report errors
if (handleRuleFailures) { if (handleRuleFailures) {
try { try {
rule.function(params, onError); rule.function(params, onError);
@ -612,44 +580,22 @@ function lintContent(
} }
// Record any errors (significant performance benefit from length check) // Record any errors (significant performance benefit from length check)
if (errors.length > 0) { if (errors.length > 0) {
errors.sort(lineNumberComparison);
const filteredErrors = errors const filteredErrors = errors
.filter((resultVersion === 3) ? .filter((error) => (
filterAllValues : enabledRulesPerLineNumber[error.lineNumber][ruleName]
uniqueFilterForSortedErrors) ))
.filter(function removeDisabledRules(error) { .map((error) => ({
return enabledRulesPerLineNumber[error.lineNumber][ruleName]; "lineNumber": error.lineNumber,
}) "ruleName": rule.names[0],
.map(function formatResults(error) { "ruleNames": rule.names,
if (resultVersion === 0) { "ruleDescription": rule.description,
return error.lineNumber; "ruleInformation": rule.information ? rule.information.href : null,
} "errorDetail": error.detail,
const errorObject = {}; "errorContext": error.context,
errorObject.lineNumber = error.lineNumber; "errorRange": error.range,
if (resultVersion === 1) { "fixInfo": error.fixInfo
errorObject.ruleName = ruleNameFriendly; }));
errorObject.ruleAlias = rule.names[1] || rule.names[0]; Array.prototype.push.apply(results, filteredErrors);
} else {
errorObject.ruleNames = rule.names;
}
errorObject.ruleDescription = rule.description;
errorObject.ruleInformation =
rule.information ? rule.information.href : null;
errorObject.errorDetail = error.detail;
errorObject.errorContext = error.context;
errorObject.errorRange = error.range;
if (resultVersion === 3) {
errorObject.fixInfo = error.fixInfo;
}
return errorObject;
});
if (filteredErrors.length > 0) {
if (resultVersion === 0) {
result[ruleNameFriendly] = filteredErrors;
} else {
Array.prototype.push.apply(result, filteredErrors);
}
}
} }
} }
// Run all rules // Run all rules
@ -660,7 +606,49 @@ function lintContent(
return callback(error); return callback(error);
} }
cache.clear(); cache.clear();
return callback(null, result); // Sort results by rule name by line number
results.sort((a, b) => (
a.ruleName.localeCompare(b.ruleName) ||
a.lineNumber - b.lineNumber
));
if (resultVersion < 3) {
// Remove fixInfo and multiple errors for the same rule and line number
const noPrevious = {
"ruleName": null,
"lineNumber": -1
};
results = results.filter((error, index, array) => {
delete error.fixInfo;
const previous = array[index - 1] || noPrevious;
return (
(error.ruleName !== previous.ruleName) ||
(error.lineNumber !== previous.lineNumber)
);
});
}
if (resultVersion === 0) {
// Return a dictionary of rule->[line numbers]
const dictionary = {};
for (const error of results) {
const ruleLines = dictionary[error.ruleName] || [];
ruleLines.push(error.lineNumber);
dictionary[error.ruleName] = ruleLines;
}
// @ts-ignore
results = dictionary;
} else if (resultVersion === 1) {
// Use ruleAlias instead of ruleNames
for (const error of results) {
error.ruleAlias = error.ruleNames[1] || error.ruleName;
delete error.ruleNames;
}
} else {
// resultVersion 2 or 3: Remove unwanted ruleName
for (const error of results) {
delete error.ruleName;
}
}
return callback(null, results);
} }
/** /**