mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Introduce concurrency to asynchronous file linting for improved performance.
This commit is contained in:
parent
2851a691ba
commit
4286f68152
3 changed files with 101 additions and 54 deletions
|
@ -80,7 +80,7 @@ module.exports.includesSorted = function includesSorted(array, element) {
|
|||
let left = 0;
|
||||
let right = array.length - 1;
|
||||
while (left <= right) {
|
||||
/* eslint-disable no-bitwise */
|
||||
// eslint-disable-next-line no-bitwise
|
||||
const mid = (left + right) >> 1;
|
||||
if (array[mid] < element) {
|
||||
left = mid + 1;
|
||||
|
|
|
@ -90,7 +90,9 @@ function newResults(ruleList) {
|
|||
function toString(useAlias) {
|
||||
let ruleNameToRule = null;
|
||||
const results = [];
|
||||
Object.keys(lintResults).forEach(function forFile(file) {
|
||||
const keys = Object.keys(lintResults);
|
||||
keys.sort();
|
||||
keys.forEach(function forFile(file) {
|
||||
const fileResults = lintResults[file];
|
||||
if (Array.isArray(fileResults)) {
|
||||
fileResults.forEach(function forResult(result) {
|
||||
|
@ -727,39 +729,38 @@ function lintInput(options, synchronous, callback) {
|
|||
md.use(...plugin);
|
||||
});
|
||||
const results = newResults(ruleList);
|
||||
// Helper to lint the next string or file
|
||||
/* eslint-disable consistent-return */
|
||||
let done = false;
|
||||
// Linting of strings is always synchronous
|
||||
let syncItem = null;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function lintNextItem() {
|
||||
let iterating = true;
|
||||
let item = null;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function lintNextItemCallback(err, result) {
|
||||
function syncCallback(err, result) {
|
||||
if (err) {
|
||||
iterating = false;
|
||||
done = true;
|
||||
return callback(err);
|
||||
}
|
||||
results[item] = result;
|
||||
return iterating || lintNextItem();
|
||||
results[syncItem] = result;
|
||||
return null;
|
||||
}
|
||||
while (iterating) {
|
||||
if ((item = stringsKeys.shift())) {
|
||||
while (!done && (syncItem = stringsKeys.shift())) {
|
||||
lintContent(
|
||||
ruleList,
|
||||
item,
|
||||
strings[item] || "",
|
||||
syncItem,
|
||||
strings[syncItem] || "",
|
||||
md,
|
||||
config,
|
||||
frontMatter,
|
||||
handleRuleFailures,
|
||||
noInlineConfig,
|
||||
resultVersion,
|
||||
lintNextItemCallback);
|
||||
} else if ((item = files.shift())) {
|
||||
iterating = synchronous;
|
||||
syncCallback
|
||||
);
|
||||
}
|
||||
if (synchronous) {
|
||||
// Lint files synchronously
|
||||
while (!done && (syncItem = files.shift())) {
|
||||
lintFile(
|
||||
ruleList,
|
||||
item,
|
||||
syncItem,
|
||||
md,
|
||||
config,
|
||||
frontMatter,
|
||||
|
@ -767,13 +768,59 @@ function lintInput(options, synchronous, callback) {
|
|||
noInlineConfig,
|
||||
resultVersion,
|
||||
synchronous,
|
||||
lintNextItemCallback);
|
||||
} else {
|
||||
syncCallback
|
||||
);
|
||||
}
|
||||
return done || callback(null, results);
|
||||
}
|
||||
// Lint files asynchronously
|
||||
let concurrency = 0;
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function lintConcurrently() {
|
||||
const asyncItem = files.shift();
|
||||
if (done) {
|
||||
// Nothing to do
|
||||
} else if (asyncItem) {
|
||||
concurrency++;
|
||||
lintFile(
|
||||
ruleList,
|
||||
asyncItem,
|
||||
md,
|
||||
config,
|
||||
frontMatter,
|
||||
handleRuleFailures,
|
||||
noInlineConfig,
|
||||
resultVersion,
|
||||
synchronous,
|
||||
(err, result) => {
|
||||
concurrency--;
|
||||
if (err) {
|
||||
done = true;
|
||||
return callback(err);
|
||||
}
|
||||
results[asyncItem] = result;
|
||||
lintConcurrently();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
} else if (concurrency === 0) {
|
||||
done = true;
|
||||
return callback(null, results);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return lintNextItem();
|
||||
// Testing on a Raspberry Pi 4 Model B with an artificial 5ms file access
|
||||
// delay suggests that a concurrency factor of 8 can eliminate the impact
|
||||
// of that delay (i.e., total time is the same as with no delay).
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
lintConcurrently();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -440,9 +440,6 @@ tape("resultFormattingV1", (test) => {
|
|||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||
const actualMessage = actualResult.toString();
|
||||
const expectedMessage =
|
||||
"truncate: 1: MD021/no-multiple-space-closed-atx" +
|
||||
" Multiple spaces inside hashes on closed atx style heading" +
|
||||
" [Context: \"# Multiple spa...tyle heading #\"]\n" +
|
||||
"./test/atx_heading_spacing.md: 3: MD002/first-heading-h1" +
|
||||
" First heading should be a top level heading" +
|
||||
" [Expected: h1; Actual: h2]\n" +
|
||||
|
@ -457,7 +454,10 @@ tape("resultFormattingV1", (test) => {
|
|||
" [Context: \"## Heading 3 {MD019}\"]\n" +
|
||||
"./test/first_heading_bad_atx.md: 1: MD002/first-heading-h1" +
|
||||
" First heading should be a top level heading" +
|
||||
" [Expected: h1; Actual: h2]";
|
||||
" [Expected: h1; Actual: h2]\n" +
|
||||
"truncate: 1: MD021/no-multiple-space-closed-atx" +
|
||||
" Multiple spaces inside hashes on closed atx style heading" +
|
||||
" [Context: \"# Multiple spa...tyle heading #\"]";
|
||||
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
||||
test.end();
|
||||
});
|
||||
|
@ -535,9 +535,6 @@ tape("resultFormattingV2", (test) => {
|
|||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||
const actualMessage = actualResult.toString();
|
||||
const expectedMessage =
|
||||
"truncate: 1: MD021/no-multiple-space-closed-atx" +
|
||||
" Multiple spaces inside hashes on closed atx style heading" +
|
||||
" [Context: \"# Multiple spa...tyle heading #\"]\n" +
|
||||
"./test/atx_heading_spacing.md: 3:" +
|
||||
" MD002/first-heading-h1/first-header-h1" +
|
||||
" First heading should be a top level heading" +
|
||||
|
@ -554,7 +551,10 @@ tape("resultFormattingV2", (test) => {
|
|||
"./test/first_heading_bad_atx.md: 1:" +
|
||||
" MD002/first-heading-h1/first-header-h1" +
|
||||
" First heading should be a top level heading" +
|
||||
" [Expected: h1; Actual: h2]";
|
||||
" [Expected: h1; Actual: h2]\n" +
|
||||
"truncate: 1: MD021/no-multiple-space-closed-atx" +
|
||||
" Multiple spaces inside hashes on closed atx style heading" +
|
||||
" [Context: \"# Multiple spa...tyle heading #\"]";
|
||||
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
||||
test.end();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue