mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 14:00:13 +01:00
Reimplement MD038/no-space-in-code using micromark tokens.
This commit is contained in:
parent
b787758a48
commit
962547ecc8
6 changed files with 219 additions and 93 deletions
135
lib/md038.js
135
lib/md038.js
|
|
@ -2,70 +2,95 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addErrorContext, filterTokens, forEachInlineCodeSpan, newLineRe } =
|
||||
require("../helpers");
|
||||
const { addErrorContext } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark.cjs");
|
||||
|
||||
const leftSpaceRe = /^\s(?:[^`]|$)/;
|
||||
const rightSpaceRe = /[^`]\s$/;
|
||||
|
||||
const spaceInsideCodeInline = (token) => (
|
||||
(token.type === "code_inline") &&
|
||||
(leftSpaceRe.test(token.content) || rightSpaceRe.test(token.content))
|
||||
);
|
||||
const trimCodeText = (text, start, end) => {
|
||||
text = text.replace(/^\s+$/, "");
|
||||
if (start) {
|
||||
text = text.replace(/^\s+?(\s`|\S)/, "$1");
|
||||
}
|
||||
if (end) {
|
||||
text = text.replace(/(`\s|\S)\s+$/, "$1");
|
||||
}
|
||||
return text;
|
||||
};
|
||||
const tokenIfType = (token, type) => token && (token.type === type) && token;
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD038", "no-space-in-code" ],
|
||||
"description": "Spaces inside code span elements",
|
||||
"tags": [ "whitespace", "code" ],
|
||||
"function": function MD038(params, onError) {
|
||||
filterTokens(params, "inline", (token) => {
|
||||
if (token.children.some(spaceInsideCodeInline)) {
|
||||
const tokenLines = params.lines.slice(token.map[0], token.map[1]);
|
||||
forEachInlineCodeSpan(
|
||||
tokenLines.join("\n"),
|
||||
(code, lineIndex, columnIndex, tickCount) => {
|
||||
let rangeIndex = columnIndex - tickCount;
|
||||
let rangeLength = code.length + (2 * tickCount);
|
||||
let rangeLineOffset = 0;
|
||||
let fixIndex = columnIndex;
|
||||
let fixLength = code.length;
|
||||
const codeLines = code.split(newLineRe);
|
||||
const left = leftSpaceRe.test(code);
|
||||
const right = !left && rightSpaceRe.test(code);
|
||||
if (right && (codeLines.length > 1)) {
|
||||
rangeIndex = 0;
|
||||
rangeLineOffset = codeLines.length - 1;
|
||||
fixIndex = 0;
|
||||
}
|
||||
if (left || right) {
|
||||
const codeLinesRange = codeLines[rangeLineOffset];
|
||||
if (codeLines.length > 1) {
|
||||
rangeLength = codeLinesRange.length + tickCount;
|
||||
fixLength = codeLinesRange.length;
|
||||
}
|
||||
const context = tokenLines[lineIndex + rangeLineOffset]
|
||||
.substring(rangeIndex, rangeIndex + rangeLength);
|
||||
const codeLinesRangeTrim = codeLinesRange.trim();
|
||||
const fixText =
|
||||
(codeLinesRangeTrim.startsWith("`") ? " " : "") +
|
||||
codeLinesRangeTrim +
|
||||
(codeLinesRangeTrim.endsWith("`") ? " " : "");
|
||||
addErrorContext(
|
||||
onError,
|
||||
token.lineNumber + lineIndex + rangeLineOffset,
|
||||
context,
|
||||
left,
|
||||
right,
|
||||
[ rangeIndex + 1, rangeLength ],
|
||||
{
|
||||
"editColumn": fixIndex + 1,
|
||||
"deleteCount": fixLength,
|
||||
"insertText": fixText
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
const codeTextTokens =
|
||||
filterByTypes(params.parsers.micromark.tokens, [ "codeText" ]);
|
||||
for (const token of codeTextTokens) {
|
||||
const { tokens } = token;
|
||||
const first = 0;
|
||||
const last = tokens.length - 1;
|
||||
const startSequence = tokenIfType(tokens[first], "codeTextSequence");
|
||||
const endSequence = tokenIfType(tokens[last], "codeTextSequence");
|
||||
const startData =
|
||||
tokenIfType(tokens[first + 1], "codeTextData") ||
|
||||
tokenIfType(tokens[first + 2], "codeTextData");
|
||||
const endData =
|
||||
tokenIfType(tokens[last - 1], "codeTextData") ||
|
||||
tokenIfType(tokens[last - 2], "codeTextData");
|
||||
if (startSequence && endSequence && startData && endData) {
|
||||
const spaceLeft = leftSpaceRe.test(startData.text);
|
||||
const spaceRight = !spaceLeft && rightSpaceRe.test(endData.text);
|
||||
if (spaceLeft || spaceRight) {
|
||||
let lineNumber = startSequence.startLine;
|
||||
let range = null;
|
||||
let fixInfo = null;
|
||||
if (startSequence.startLine === endSequence.endLine) {
|
||||
range = [
|
||||
startSequence.startColumn,
|
||||
endSequence.endColumn - startSequence.startColumn
|
||||
];
|
||||
fixInfo = {
|
||||
"editColumn": startSequence.endColumn,
|
||||
"deleteCount": endSequence.startColumn - startSequence.endColumn,
|
||||
"insertText": trimCodeText(startData.text, true, true)
|
||||
};
|
||||
} else if (spaceLeft) {
|
||||
range = [
|
||||
startSequence.startColumn,
|
||||
startData.endColumn - startSequence.startColumn
|
||||
];
|
||||
fixInfo = {
|
||||
"editColumn": startSequence.endColumn,
|
||||
"deleteCount": startData.endColumn - startData.startColumn,
|
||||
"insertText": trimCodeText(startData.text, true, false)
|
||||
};
|
||||
} else {
|
||||
lineNumber = endSequence.endLine;
|
||||
range = [
|
||||
endData.startColumn,
|
||||
endSequence.endColumn - endData.startColumn
|
||||
];
|
||||
fixInfo = {
|
||||
"editColumn": endData.startColumn,
|
||||
"deleteCount": endData.endColumn - endData.startColumn,
|
||||
"insertText": trimCodeText(endData.text, false, true)
|
||||
};
|
||||
}
|
||||
const context = params
|
||||
.lines[lineNumber - 1]
|
||||
.substring(range[0] - 1, range[0] - 1 + range[1]);
|
||||
addErrorContext(
|
||||
onError,
|
||||
lineNumber,
|
||||
context,
|
||||
spaceLeft,
|
||||
spaceRight,
|
||||
range,
|
||||
fixInfo
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue