2018-01-21 21:44:25 -08:00
|
|
|
// @ts-check
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
2022-06-16 05:19:27 +00:00
|
|
|
const { addError, filterTokens, forEachLine, includesSorted,
|
|
|
|
numericSortAscending } = require("../helpers");
|
2019-04-10 21:26:59 -07:00
|
|
|
const { lineMetadata } = require("./cache");
|
2018-01-21 21:44:25 -08:00
|
|
|
|
2024-02-27 20:42:09 -08:00
|
|
|
// eslint-disable-next-line jsdoc/valid-types
|
|
|
|
/** @type import("./markdownlint").Rule */
|
2018-01-21 21:44:25 -08:00
|
|
|
module.exports = {
|
|
|
|
"names": [ "MD009", "no-trailing-spaces" ],
|
|
|
|
"description": "Trailing spaces",
|
|
|
|
"tags": [ "whitespace" ],
|
|
|
|
"function": function MD009(params, onError) {
|
2018-04-30 21:34:19 -07:00
|
|
|
let brSpaces = params.config.br_spaces;
|
2020-01-25 18:40:39 -08:00
|
|
|
brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
|
2019-12-09 22:05:57 -08:00
|
|
|
const listItemEmptyLines = !!params.config.list_item_empty_lines;
|
|
|
|
const strict = !!params.config.strict;
|
2018-04-27 22:05:34 -07:00
|
|
|
const listItemLineNumbers = [];
|
2019-12-09 22:05:57 -08:00
|
|
|
if (listItemEmptyLines) {
|
2019-03-28 22:06:42 -07:00
|
|
|
filterTokens(params, "list_item_open", (token) => {
|
2018-04-27 22:05:34 -07:00
|
|
|
for (let i = token.map[0]; i < token.map[1]; i++) {
|
2018-01-21 21:44:25 -08:00
|
|
|
listItemLineNumbers.push(i + 1);
|
|
|
|
}
|
|
|
|
});
|
2019-12-09 22:05:57 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
2022-06-16 05:19:27 +00:00
|
|
|
const addLineNumberRange = (start, end) => {
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
|
codeInlineLineNumbers.push(i);
|
|
|
|
}
|
|
|
|
};
|
2019-12-09 22:05:57 -08:00
|
|
|
filterTokens(params, "inline", (token) => {
|
2022-06-16 05:19:27 +00:00
|
|
|
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]);
|
2019-12-09 22:05:57 -08:00
|
|
|
}
|
|
|
|
});
|
2018-01-21 21:44:25 -08:00
|
|
|
}
|
2018-04-30 21:34:19 -07:00
|
|
|
const expected = (brSpaces < 2) ? 0 : brSpaces;
|
2020-11-21 16:53:31 -08:00
|
|
|
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
2018-04-27 22:05:34 -07:00
|
|
|
const lineNumber = lineIndex + 1;
|
2021-02-06 19:55:22 -08:00
|
|
|
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,
|
2022-06-13 22:53:48 -07:00
|
|
|
undefined,
|
2021-02-06 19:55:22 -08:00
|
|
|
[ column, trailingSpaces ],
|
|
|
|
{
|
|
|
|
"editColumn": column,
|
|
|
|
"deleteCount": trailingSpaces
|
|
|
|
});
|
2018-01-21 21:44:25 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|