markdownlint/lib/md009.js

89 lines
2.8 KiB
JavaScript

// @ts-check
"use strict";
const { addError, filterTokens, forEachLine, includesSorted,
numericSortAscending } = require("../helpers");
const { lineMetadata } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */
module.exports = {
"names": [ "MD009", "no-trailing-spaces" ],
"description": "Trailing spaces",
"tags": [ "whitespace" ],
"parser": "markdownit",
"function": function MD009(params, onError) {
let brSpaces = params.config.br_spaces;
brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
const listItemEmptyLines = !!params.config.list_item_empty_lines;
const strict = !!params.config.strict;
const listItemLineNumbers = [];
if (listItemEmptyLines) {
filterTokens(params, "list_item_open", (token) => {
for (let i = token.map[0]; i < token.map[1]; i++) {
listItemLineNumbers.push(i + 1);
}
});
listItemLineNumbers.sort(numericSortAscending);
}
const paragraphLineNumbers = [];
const codeInlineLineNumbers = [];
if (strict) {
filterTokens(params, "paragraph_open", (token) => {
for (let i = token.map[0]; i < token.map[1] - 1; i++) {
paragraphLineNumbers.push(i + 1);
}
});
const addLineNumberRange = (start, end) => {
for (let i = start; i < end; i++) {
codeInlineLineNumbers.push(i);
}
};
filterTokens(params, "inline", (token) => {
let start = 0;
for (const child of token.children) {
if (start > 0) {
addLineNumberRange(start, child.lineNumber);
start = 0;
}
if (child.type === "code_inline") {
start = child.lineNumber;
}
}
if (start > 0) {
addLineNumberRange(start, token.map[1]);
}
});
}
const expected = (brSpaces < 2) ? 0 : brSpaces;
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
const lineNumber = lineIndex + 1;
const trailingSpaces = line.length - line.trimEnd().length;
if (
trailingSpaces &&
!inCode &&
!includesSorted(listItemLineNumbers, lineNumber) &&
(
(expected !== trailingSpaces) ||
(strict &&
(!includesSorted(paragraphLineNumbers, lineNumber) ||
includesSorted(codeInlineLineNumbers, lineNumber)))
)
) {
const column = line.length - trailingSpaces + 1;
addError(
onError,
lineNumber,
"Expected: " + (expected === 0 ? "" : "0 or ") +
expected + "; Actual: " + trailingSpaces,
undefined,
[ column, trailingSpaces ],
{
"editColumn": column,
"deleteCount": trailingSpaces
});
}
});
}
};