Reimplement helpers.forEachInlineCodeSpan for ~11% time reduction measured via profile-fixture.mjs on GitHub Codespaces.

This commit is contained in:
David Anson 2022-06-19 02:14:03 +00:00 committed by GitHub
parent d177ee2fc5
commit 6e8a0737b5
3 changed files with 67 additions and 116 deletions

View file

@ -437,67 +437,39 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
* @returns {void} * @returns {void}
*/ */
function forEachInlineCodeSpan(input, handler) { function forEachInlineCodeSpan(input, handler) {
let currentLine = 0; const backtickRe = /`+/g;
let currentColumn = 0; let match = null;
let index = 0; const backticksLengthAndIndex = [];
while (index < input.length) { while ((match = backtickRe.exec(input)) !== null) {
let startIndex = -1; backticksLengthAndIndex.push([match[0].length, match.index]);
let startLine = -1; }
let startColumn = -1; const newLinesIndex = [];
let tickCount = 0; while ((match = newLineRe.exec(input)) !== null) {
let currentTicks = 0; newLinesIndex.push(match.index);
// Deliberate <= so trailing 0 completes the last span (ex: "text `code`") }
// False-positive for js/index-out-of-bounds let lineIndex = 0;
for (; index <= input.length; index++) { let lineStartIndex = 0;
const char = input[index]; let k = 0;
// Parse backtick open/close for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
if (char === "`") { const [startLength, startIndex] = backticksLengthAndIndex[i];
// Count backticks at start or end of code span if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
currentTicks++; for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
if ((startIndex === -1) || (startColumn === -1)) { const [endLength, endIndex] = backticksLengthAndIndex[j];
startIndex = index + 1; if (startLength === endLength) {
for (; k < newLinesIndex.length; k++) {
const newLineIndex = newLinesIndex[k];
if (startIndex < newLineIndex) {
break;
}
lineIndex++;
lineStartIndex = newLineIndex + 1;
}
const columnIndex = startIndex - lineStartIndex + startLength;
handler(input.slice(startIndex + startLength, endIndex), lineIndex, columnIndex, startLength);
i = j;
break;
} }
} }
else {
if ((startIndex >= 0) &&
(startColumn >= 0) &&
(tickCount === currentTicks)) {
// Found end backticks; invoke callback for code span
handler(input.substring(startIndex, index - currentTicks), startLine, startColumn, tickCount);
startIndex = -1;
startColumn = -1;
}
else if ((startIndex >= 0) && (startColumn === -1)) {
// Found start backticks
tickCount = currentTicks;
startLine = currentLine;
startColumn = currentColumn;
}
// Not in backticks
currentTicks = 0;
}
if (char === "\n") {
// On next line
currentLine++;
currentColumn = 0;
}
else if ((char === "\\") &&
((startIndex === -1) || (startColumn === -1)) &&
(input[index + 1] !== "\n")) {
// Escape character outside code, skip next
index++;
currentColumn += 2;
}
else {
// On next column
currentColumn++;
}
}
if (startIndex >= 0) {
// Restart loop after unmatched start backticks (ex: "`text``code``")
index = startIndex;
currentLine = startLine;
currentColumn = startColumn;
} }
} }
} }

View file

@ -437,65 +437,44 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
* @returns {void} * @returns {void}
*/ */
function forEachInlineCodeSpan(input, handler) { function forEachInlineCodeSpan(input, handler) {
let currentLine = 0; const backtickRe = /`+/g;
let currentColumn = 0; let match = null;
let index = 0; const backticksLengthAndIndex = [];
while (index < input.length) { while ((match = backtickRe.exec(input)) !== null) {
let startIndex = -1; backticksLengthAndIndex.push([ match[0].length, match.index ]);
let startLine = -1; }
let startColumn = -1; const newLinesIndex = [];
let tickCount = 0; while ((match = newLineRe.exec(input)) !== null) {
let currentTicks = 0; newLinesIndex.push(match.index);
// Deliberate <= so trailing 0 completes the last span (ex: "text `code`") }
// False-positive for js/index-out-of-bounds let lineIndex = 0;
for (; index <= input.length; index++) { let lineStartIndex = 0;
const char = input[index]; let k = 0;
// Parse backtick open/close for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
if (char === "`") { const [ startLength, startIndex ] = backticksLengthAndIndex[i];
// Count backticks at start or end of code span if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
currentTicks++; for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
if ((startIndex === -1) || (startColumn === -1)) { const [ endLength, endIndex ] = backticksLengthAndIndex[j];
startIndex = index + 1; if (startLength === endLength) {
} for (; k < newLinesIndex.length; k++) {
} else { const newLineIndex = newLinesIndex[k];
if ((startIndex >= 0) && if (startIndex < newLineIndex) {
(startColumn >= 0) && break;
(tickCount === currentTicks)) { }
// Found end backticks; invoke callback for code span lineIndex++;
lineStartIndex = newLineIndex + 1;
}
const columnIndex = startIndex - lineStartIndex + startLength;
handler( handler(
input.substring(startIndex, index - currentTicks), input.slice(startIndex + startLength, endIndex),
startLine, startColumn, tickCount); lineIndex,
startIndex = -1; columnIndex,
startColumn = -1; startLength
} else if ((startIndex >= 0) && (startColumn === -1)) { );
// Found start backticks i = j;
tickCount = currentTicks; break;
startLine = currentLine;
startColumn = currentColumn;
} }
// Not in backticks
currentTicks = 0;
} }
if (char === "\n") {
// On next line
currentLine++;
currentColumn = 0;
} else if ((char === "\\") &&
((startIndex === -1) || (startColumn === -1)) &&
(input[index + 1] !== "\n")) {
// Escape character outside code, skip next
index++;
currentColumn += 2;
} else {
// On next column
currentColumn++;
}
}
if (startIndex >= 0) {
// Restart loop after unmatched start backticks (ex: "`text``code``")
index = startIndex;
currentLine = startLine;
currentColumn = startColumn;
} }
} }
} }

View file

@ -391,7 +391,7 @@ test("forEachInlineCodeSpan", (t) => {
t.is(column, expectedColumn, input); t.is(column, expectedColumn, input);
t.is(ticks, expectedTicks, input); t.is(ticks, expectedTicks, input);
}); });
t.is(expecteds.length, 0, "length"); t.is(expecteds.shift(), undefined, input);
} }
}); });