mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Update MD009/no-trailing-spaces to include strict mode (fixes #216).
This commit is contained in:
parent
a9251c533f
commit
6f3c67f760
10 changed files with 193 additions and 11 deletions
13
doc/Rules.md
13
doc/Rules.md
|
@ -318,7 +318,7 @@ Tags: whitespace
|
|||
|
||||
Aliases: no-trailing-spaces
|
||||
|
||||
Parameters: br_spaces, list_item_empty_lines (number; default 2, boolean; default false)
|
||||
Parameters: br_spaces, list_item_empty_lines, strict (number; default 2, boolean; default false, boolean; default false)
|
||||
|
||||
This rule is triggered on any lines that end with unexpected whitespace. To fix this,
|
||||
remove the trailing space from the end of the line.
|
||||
|
@ -330,9 +330,18 @@ value allows 2 spaces to indicate a hard break (\<br> element).
|
|||
Note: You must set `br_spaces` to a value >= 2 for this parameter to take effect.
|
||||
Setting `br_spaces` to 1 behaves the same as 0, disallowing any trailing spaces.
|
||||
|
||||
By default, this rule will not trigger when the allowed number of spaces is used,
|
||||
even when it doesn't create a hard break (for example, at the end of a paragraph).
|
||||
To report such instances as well, set the `strict` parameter to `true`.
|
||||
|
||||
```markdown
|
||||
Text text text
|
||||
text[2 spaces]
|
||||
```
|
||||
|
||||
Using spaces to indent blank lines inside a list item is usually not necessary,
|
||||
but some parsers require it. Set the `list_item_empty_lines` parameter to `true`
|
||||
to allow this:
|
||||
to allow this (even when `strict` is `true`):
|
||||
|
||||
```markdown
|
||||
- list item text
|
||||
|
|
44
lib/md009.js
44
lib/md009.js
|
@ -2,10 +2,14 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addError, filterTokens, forEachLine, includesSorted } =
|
||||
require("../helpers");
|
||||
const { addError, filterTokens, forEachInlineCodeSpan, forEachLine,
|
||||
includesSorted, newLineRe } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
function numericSortAscending(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD009", "no-trailing-spaces" ],
|
||||
"description": "Trailing spaces",
|
||||
|
@ -15,17 +19,38 @@ module.exports = {
|
|||
if (brSpaces === undefined) {
|
||||
brSpaces = 2;
|
||||
}
|
||||
const listItemEmptyLines = params.config.list_item_empty_lines;
|
||||
const allowListItemEmptyLines =
|
||||
(listItemEmptyLines === undefined) ? false : !!listItemEmptyLines;
|
||||
const listItemEmptyLines = !!params.config.list_item_empty_lines;
|
||||
const strict = !!params.config.strict;
|
||||
const listItemLineNumbers = [];
|
||||
if (allowListItemEmptyLines) {
|
||||
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((a, b) => a - b);
|
||||
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);
|
||||
}
|
||||
});
|
||||
paragraphLineNumbers.sort(numericSortAscending);
|
||||
filterTokens(params, "inline", (token) => {
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
const tokenLines = params.lines.slice(token.map[0], token.map[1]);
|
||||
forEachInlineCodeSpan(tokenLines.join("\n"), (code, lineIndex) => {
|
||||
const codeLineCount = code.split(newLineRe).length;
|
||||
for (let i = 0; i < codeLineCount; i++) {
|
||||
codeInlineLineNumbers.push(token.lineNumber + lineIndex + i);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
codeInlineLineNumbers.sort(numericSortAscending);
|
||||
}
|
||||
const expected = (brSpaces < 2) ? 0 : brSpaces;
|
||||
let inFencedCode = 0;
|
||||
|
@ -35,7 +60,10 @@ module.exports = {
|
|||
const trailingSpaces = line.length - line.trimRight().length;
|
||||
if ((!inCode || inFencedCode) && trailingSpaces &&
|
||||
!includesSorted(listItemLineNumbers, lineNumber)) {
|
||||
if (expected !== trailingSpaces) {
|
||||
if ((expected !== trailingSpaces) ||
|
||||
(strict &&
|
||||
(!includesSorted(paragraphLineNumbers, lineNumber) ||
|
||||
includesSorted(codeInlineLineNumbers, lineNumber)))) {
|
||||
const column = line.length - trailingSpaces + 1;
|
||||
addError(
|
||||
onError,
|
||||
|
|
|
@ -15,7 +15,6 @@ module.exports = {
|
|||
if (token.type === "paragraph_open") {
|
||||
return function inParagraph(t) {
|
||||
// Always paragraph_open/inline/paragraph_close,
|
||||
// omit (t.type === "inline")
|
||||
const children = t.children.filter(function notEmptyText(child) {
|
||||
return (child.type !== "text") || (child.content !== "");
|
||||
});
|
||||
|
|
|
@ -109,6 +109,11 @@ rules.forEach(function forRule(rule) {
|
|||
"description": "Allow spaces for empty lines in list items",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"strict": {
|
||||
"description": "Include unnecessary breaks",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
|
|
@ -267,6 +267,11 @@
|
|||
"description": "Allow spaces for empty lines in list items",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"strict": {
|
||||
"description": "Include unnecessary breaks",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -288,6 +293,11 @@
|
|||
"description": "Allow spaces for empty lines in list items",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"strict": {
|
||||
"description": "Include unnecessary breaks",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
6
test/hard-line-breaks.json
Normal file
6
test/hard-line-breaks.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD009": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
64
test/hard-line-breaks.md
Normal file
64
test/hard-line-breaks.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Hard Line Breaks
|
||||
|
||||
hard
|
||||
break
|
||||
|
||||
hard\
|
||||
break
|
||||
|
||||
hard
|
||||
break
|
||||
|
||||
hard
|
||||
break
|
||||
|
||||
hard\
|
||||
break
|
||||
|
||||
*hard
|
||||
break*
|
||||
|
||||
*hard\
|
||||
break*
|
||||
|
||||
`code
|
||||
span`
|
||||
|
||||
`code\
|
||||
span`
|
||||
|
||||
not\
|
||||
|
||||
not
|
||||
|
||||
## not\
|
||||
|
||||
### not
|
||||
|
||||
- Item
|
||||
|
||||
- Item
|
||||
|
||||
- Item
|
||||
|
||||
- Item
|
||||
|
||||
Text text
|
||||
text `code
|
||||
span code
|
||||
span` text
|
||||
text
|
||||
|
||||
Text text
|
||||
text text
|
||||
text
|
||||
|
||||
{MD009:9}
|
||||
{MD009:24}
|
||||
{MD009:32}
|
||||
{MD009:36}
|
||||
{MD009:39}
|
||||
{MD009:41}
|
||||
{MD009:43}
|
||||
{MD009:48}
|
||||
{MD009:54}
|
|
@ -1553,6 +1553,7 @@ module.exports.doc = function doc(test) {
|
|||
ruleUsesParams = ruleUsesParams.map(function forUse(use) {
|
||||
return use.split(".").pop();
|
||||
});
|
||||
ruleUsesParams.sort();
|
||||
}
|
||||
} else if (/^Tags: /.test(token.content) && rule) {
|
||||
test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),
|
||||
|
@ -1571,6 +1572,7 @@ module.exports.doc = function doc(test) {
|
|||
inDetails = inDetails || (part[0] === "(");
|
||||
return !inDetails;
|
||||
});
|
||||
parameters.sort();
|
||||
test.deepEqual(parameters, ruleUsesParams,
|
||||
"Missing parameter for rule " + rule.names);
|
||||
ruleUsesParams = null;
|
||||
|
|
7
test/trailing-spaces-in-lists-allowed-strict.json
Normal file
7
test/trailing-spaces-in-lists-allowed-strict.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD009": {
|
||||
"list_item_empty_lines": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
52
test/trailing-spaces-in-lists-allowed-strict.md
Normal file
52
test/trailing-spaces-in-lists-allowed-strict.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Heading
|
||||
|
||||
1. text
|
||||
text
|
||||
1. text
|
||||
|
||||
text
|
||||
1. text
|
||||
|
||||
text
|
||||
1. text
|
||||
text
|
||||
|
||||
1. text
|
||||
text
|
||||
|
||||
1. text
|
||||
|
||||
{MD009:16}
|
||||
{MD009:18}
|
||||
|
||||
1. text
|
||||
text
|
||||
1. text
|
||||
|
||||
text
|
||||
1. text
|
||||
|
||||
text
|
||||
1. text
|
||||
text
|
||||
|
||||
1. text
|
||||
text
|
||||
|
||||
1. text
|
||||
|
||||
1. text
|
||||
- text
|
||||
|
||||
text
|
||||
- text
|
||||
|
||||
text
|
||||
- text
|
||||
text
|
||||
|
||||
- text
|
||||
text
|
||||
|
||||
{MD009:37}
|
||||
{MD009:50}
|
Loading…
Add table
Add a link
Reference in a new issue