mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Update MD009/MD010/MD012/MD028 to report fixInfo for violations.
This commit is contained in:
parent
679c83e23b
commit
2cd27c58f2
8 changed files with 165 additions and 31 deletions
|
@ -345,7 +345,7 @@ module.exports.addError = addError;
|
||||||
|
|
||||||
// Adds an error object with details conditionally via the onError callback
|
// Adds an error object with details conditionally via the onError callback
|
||||||
module.exports.addErrorDetailIf = function addErrorDetailIf(
|
module.exports.addErrorDetailIf = function addErrorDetailIf(
|
||||||
onError, lineNumber, expected, actual, detail, context, range) {
|
onError, lineNumber, expected, actual, detail, context, range, fixInfo) {
|
||||||
if (expected !== actual) {
|
if (expected !== actual) {
|
||||||
addError(
|
addError(
|
||||||
onError,
|
onError,
|
||||||
|
@ -353,7 +353,8 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(
|
||||||
"Expected: " + expected + "; Actual: " + actual +
|
"Expected: " + expected + "; Actual: " + actual +
|
||||||
(detail ? "; " + detail : ""),
|
(detail ? "; " + detail : ""),
|
||||||
context,
|
context,
|
||||||
range);
|
range,
|
||||||
|
fixInfo);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -411,9 +412,11 @@ module.exports.fixErrors = function fixErrors(input, errors) {
|
||||||
const editIndex = editColumn - 1;
|
const editIndex = editColumn - 1;
|
||||||
const line = lines[lineIndex];
|
const line = lines[lineIndex];
|
||||||
lines[lineIndex] =
|
lines[lineIndex] =
|
||||||
line.slice(0, editIndex) +
|
(deleteCount === -1) ?
|
||||||
insertText +
|
null :
|
||||||
line.slice(editIndex + deleteCount);
|
line.slice(0, editIndex) +
|
||||||
|
insertText +
|
||||||
|
line.slice(editIndex + deleteCount);
|
||||||
});
|
});
|
||||||
return lines.join("\n");
|
return lines.filter((line) => line !== null).join("\n");
|
||||||
};
|
};
|
||||||
|
|
24
lib/md009.js
24
lib/md009.js
|
@ -2,12 +2,10 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { addError, filterTokens, forEachLine, includesSorted, rangeFromRegExp } =
|
const { addError, filterTokens, forEachLine, includesSorted } =
|
||||||
require("../helpers");
|
require("../helpers");
|
||||||
const { lineMetadata } = require("./cache");
|
const { lineMetadata } = require("./cache");
|
||||||
|
|
||||||
const trailingSpaceRe = /\s+$/;
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": [ "MD009", "no-trailing-spaces" ],
|
"names": [ "MD009", "no-trailing-spaces" ],
|
||||||
"description": "Trailing spaces",
|
"description": "Trailing spaces",
|
||||||
|
@ -34,14 +32,22 @@ module.exports = {
|
||||||
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
|
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
|
||||||
inFencedCode += onFence;
|
inFencedCode += onFence;
|
||||||
const lineNumber = lineIndex + 1;
|
const lineNumber = lineIndex + 1;
|
||||||
if ((!inCode || inFencedCode) && trailingSpaceRe.test(line) &&
|
const trailingSpaces = line.length - line.trimRight().length;
|
||||||
|
if ((!inCode || inFencedCode) && trailingSpaces &&
|
||||||
!includesSorted(listItemLineNumbers, lineNumber)) {
|
!includesSorted(listItemLineNumbers, lineNumber)) {
|
||||||
const actual = line.length - line.trimRight().length;
|
if (expected !== trailingSpaces) {
|
||||||
if (expected !== actual) {
|
const column = line.length - trailingSpaces + 1;
|
||||||
addError(onError, lineNumber,
|
addError(
|
||||||
|
onError,
|
||||||
|
lineNumber,
|
||||||
"Expected: " + (expected === 0 ? "" : "0 or ") +
|
"Expected: " + (expected === 0 ? "" : "0 or ") +
|
||||||
expected + "; Actual: " + actual,
|
expected + "; Actual: " + trailingSpaces,
|
||||||
null, rangeFromRegExp(line, trailingSpaceRe));
|
null,
|
||||||
|
[ column, trailingSpaces ],
|
||||||
|
{
|
||||||
|
"editColumn": column,
|
||||||
|
"deleteCount": trailingSpaces
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,12 +19,18 @@ module.exports = {
|
||||||
let match = null;
|
let match = null;
|
||||||
while ((match = tabRe.exec(line)) !== null) {
|
while ((match = tabRe.exec(line)) !== null) {
|
||||||
const column = match.index + 1;
|
const column = match.index + 1;
|
||||||
|
const length = match[0].length;
|
||||||
addError(
|
addError(
|
||||||
onError,
|
onError,
|
||||||
lineIndex + 1,
|
lineIndex + 1,
|
||||||
"Column: " + column,
|
"Column: " + column,
|
||||||
null,
|
null,
|
||||||
[ column, match[0].length ]);
|
[ column, length ],
|
||||||
|
{
|
||||||
|
"editColumn": column,
|
||||||
|
"deleteCount": length,
|
||||||
|
"insertText": "".padEnd(length)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
12
lib/md012.js
12
lib/md012.js
|
@ -15,7 +15,17 @@ module.exports = {
|
||||||
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
||||||
count = (inCode || line.trim().length) ? 0 : count + 1;
|
count = (inCode || line.trim().length) ? 0 : count + 1;
|
||||||
if (maximum < count) {
|
if (maximum < count) {
|
||||||
addErrorDetailIf(onError, lineIndex + 1, maximum, count);
|
addErrorDetailIf(
|
||||||
|
onError,
|
||||||
|
lineIndex + 1,
|
||||||
|
maximum,
|
||||||
|
count,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"deleteCount": -1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
19
lib/md028.js
19
lib/md028.js
|
@ -10,12 +10,29 @@ module.exports = {
|
||||||
"tags": [ "blockquote", "whitespace" ],
|
"tags": [ "blockquote", "whitespace" ],
|
||||||
"function": function MD028(params, onError) {
|
"function": function MD028(params, onError) {
|
||||||
let prevToken = {};
|
let prevToken = {};
|
||||||
|
let prevLineNumber = null;
|
||||||
params.tokens.forEach(function forToken(token) {
|
params.tokens.forEach(function forToken(token) {
|
||||||
if ((token.type === "blockquote_open") &&
|
if ((token.type === "blockquote_open") &&
|
||||||
(prevToken.type === "blockquote_close")) {
|
(prevToken.type === "blockquote_close")) {
|
||||||
addError(onError, token.lineNumber - 1);
|
for (
|
||||||
|
let lineNumber = prevLineNumber;
|
||||||
|
lineNumber < token.lineNumber;
|
||||||
|
lineNumber++) {
|
||||||
|
addError(
|
||||||
|
onError,
|
||||||
|
lineNumber,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"deleteCount": -1
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prevToken = token;
|
prevToken = token;
|
||||||
|
if (token.type === "blockquote_open") {
|
||||||
|
prevLineNumber = token.map[1] + 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,6 @@ Some text
|
||||||
|
|
||||||
Expected errors:
|
Expected errors:
|
||||||
|
|
||||||
{MD028:5} {MD028:8} {MD028:10} {MD028:17}
|
{MD028:5} {MD028:7} {MD028:8} {MD028:10} {MD028:17}
|
||||||
{MD009:10} (trailing space is intentional)
|
{MD009:10} (trailing space is intentional)
|
||||||
{MD012:8} (multiple blank lines are intentional)
|
{MD012:8} (multiple blank lines are intentional)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
Hard tab {MD010}
|
Hard tab {MD010}
|
||||||
|
|
||||||
|
Hard tabs hard tabs {MD010}
|
||||||
|
|
||||||
<!-- Hard tab -->
|
<!-- Hard tab -->
|
||||||
|
|
||||||
<!--Hard tab-->
|
<!--Hard tab-->
|
||||||
|
|
|
@ -492,7 +492,10 @@ module.exports.resultFormattingV3 = function resultFormattingV3(test) {
|
||||||
test.expect(3);
|
test.expect(3);
|
||||||
const options = {
|
const options = {
|
||||||
"strings": {
|
"strings": {
|
||||||
"input": "# Heading"
|
"input":
|
||||||
|
"# Heading \n" +
|
||||||
|
"\n" +
|
||||||
|
"Text\ttext\t\ttext"
|
||||||
},
|
},
|
||||||
"resultVersion": 3
|
"resultVersion": 3
|
||||||
};
|
};
|
||||||
|
@ -502,6 +505,47 @@ module.exports.resultFormattingV3 = function resultFormattingV3(test) {
|
||||||
"input": [
|
"input": [
|
||||||
{
|
{
|
||||||
"lineNumber": 1,
|
"lineNumber": 1,
|
||||||
|
"ruleNames": [ "MD009", "no-trailing-spaces" ],
|
||||||
|
"ruleDescription": "Trailing spaces",
|
||||||
|
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md009`,
|
||||||
|
"errorDetail": "Expected: 0 or 2; Actual: 3",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 10, 3 ],
|
||||||
|
"fixInfo": {
|
||||||
|
"editColumn": 10,
|
||||||
|
"deleteCount": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 3,
|
||||||
|
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
||||||
|
"ruleDescription": "Hard tabs",
|
||||||
|
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
||||||
|
"errorDetail": "Column: 5",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 5, 1 ],
|
||||||
|
"fixInfo": {
|
||||||
|
"editColumn": 5,
|
||||||
|
"deleteCount": 1,
|
||||||
|
"insertText": " "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 3,
|
||||||
|
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
||||||
|
"ruleDescription": "Hard tabs",
|
||||||
|
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
||||||
|
"errorDetail": "Column: 10",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 10, 2 ],
|
||||||
|
"fixInfo": {
|
||||||
|
"editColumn": 10,
|
||||||
|
"deleteCount": 2,
|
||||||
|
"insertText": " "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 3,
|
||||||
"ruleNames": [ "MD047", "single-trailing-newline" ],
|
"ruleNames": [ "MD047", "single-trailing-newline" ],
|
||||||
"ruleDescription": "Files should end with a single newline character",
|
"ruleDescription": "Files should end with a single newline character",
|
||||||
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md047`,
|
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md047`,
|
||||||
|
@ -510,7 +554,7 @@ module.exports.resultFormattingV3 = function resultFormattingV3(test) {
|
||||||
"errorRange": null,
|
"errorRange": null,
|
||||||
"fixInfo": {
|
"fixInfo": {
|
||||||
"insertText": "\n",
|
"insertText": "\n",
|
||||||
"editColumn": 10
|
"editColumn": 16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -518,7 +562,13 @@ module.exports.resultFormattingV3 = function resultFormattingV3(test) {
|
||||||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
const actualMessage = actualResult.toString();
|
const actualMessage = actualResult.toString();
|
||||||
const expectedMessage =
|
const expectedMessage =
|
||||||
"input: 1: MD047/single-trailing-newline" +
|
"input: 1: MD009/no-trailing-spaces" +
|
||||||
|
" Trailing spaces [Expected: 0 or 2; Actual: 3]\n" +
|
||||||
|
"input: 3: MD010/no-hard-tabs" +
|
||||||
|
" Hard tabs [Column: 5]\n" +
|
||||||
|
"input: 3: MD010/no-hard-tabs" +
|
||||||
|
" Hard tabs [Column: 10]\n" +
|
||||||
|
"input: 3: MD047/single-trailing-newline" +
|
||||||
" Files should end with a single newline character";
|
" Files should end with a single newline character";
|
||||||
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
test.equal(actualMessage, expectedMessage, "Incorrect message.");
|
||||||
test.done();
|
test.done();
|
||||||
|
@ -559,7 +609,8 @@ module.exports.onePerLineResultVersion1 =
|
||||||
test.ifError(err);
|
test.ifError(err);
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
"input": [
|
"input": [
|
||||||
{ "lineNumber": 1,
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
"ruleName": "MD010",
|
"ruleName": "MD010",
|
||||||
"ruleAlias": "no-hard-tabs",
|
"ruleAlias": "no-hard-tabs",
|
||||||
"ruleDescription": "Hard tabs",
|
"ruleDescription": "Hard tabs",
|
||||||
|
@ -567,7 +618,8 @@ module.exports.onePerLineResultVersion1 =
|
||||||
`${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
`${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
||||||
"errorDetail": "Column: 10",
|
"errorDetail": "Column: 10",
|
||||||
"errorContext": null,
|
"errorContext": null,
|
||||||
"errorRange": [ 10, 1 ] }
|
"errorRange": [ 10, 1 ]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
@ -588,14 +640,16 @@ module.exports.onePerLineResultVersion2 =
|
||||||
test.ifError(err);
|
test.ifError(err);
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
"input": [
|
"input": [
|
||||||
{ "lineNumber": 1,
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
||||||
"ruleDescription": "Hard tabs",
|
"ruleDescription": "Hard tabs",
|
||||||
"ruleInformation":
|
"ruleInformation":
|
||||||
`${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
`${homepage}/blob/v${version}/doc/Rules.md#md010`,
|
||||||
"errorDetail": "Column: 10",
|
"errorDetail": "Column: 10",
|
||||||
"errorContext": null,
|
"errorContext": null,
|
||||||
"errorRange": [ 10, 1 ] }
|
"errorRange": [ 10, 1 ]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
@ -616,7 +670,8 @@ module.exports.manyPerLineResultVersion3 =
|
||||||
test.ifError(err);
|
test.ifError(err);
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
"input": [
|
"input": [
|
||||||
{ "lineNumber": 1,
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
||||||
"ruleDescription": "Hard tabs",
|
"ruleDescription": "Hard tabs",
|
||||||
"ruleInformation":
|
"ruleInformation":
|
||||||
|
@ -624,8 +679,14 @@ module.exports.manyPerLineResultVersion3 =
|
||||||
"errorDetail": "Column: 10",
|
"errorDetail": "Column: 10",
|
||||||
"errorContext": null,
|
"errorContext": null,
|
||||||
"errorRange": [ 10, 1 ],
|
"errorRange": [ 10, 1 ],
|
||||||
"fixInfo": null },
|
"fixInfo": {
|
||||||
{ "lineNumber": 1,
|
"editColumn": 10,
|
||||||
|
"deleteCount": 1,
|
||||||
|
"insertText": " "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
"ruleNames": [ "MD010", "no-hard-tabs" ],
|
||||||
"ruleDescription": "Hard tabs",
|
"ruleDescription": "Hard tabs",
|
||||||
"ruleInformation":
|
"ruleInformation":
|
||||||
|
@ -633,7 +694,12 @@ module.exports.manyPerLineResultVersion3 =
|
||||||
"errorDetail": "Column: 18",
|
"errorDetail": "Column: 18",
|
||||||
"errorContext": null,
|
"errorContext": null,
|
||||||
"errorRange": [ 18, 2 ],
|
"errorRange": [ 18, 2 ],
|
||||||
"fixInfo": null }
|
"fixInfo": {
|
||||||
|
"editColumn": 18,
|
||||||
|
"deleteCount": 2,
|
||||||
|
"insertText": " "
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
|
||||||
|
@ -1832,7 +1898,7 @@ module.exports.forEachInlineCodeSpan = function forEachInlineCodeSpan(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.fixErrors = function fixErrors(test) {
|
module.exports.fixErrors = function fixErrors(test) {
|
||||||
test.expect(8);
|
test.expect(10);
|
||||||
const testCases = [
|
const testCases = [
|
||||||
[
|
[
|
||||||
"Hello world.",
|
"Hello world.",
|
||||||
|
@ -1932,6 +1998,30 @@ module.exports.fixErrors = function fixErrors(test) {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Hello world. Hi."
|
"Hello world. Hi."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hello\nworld",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lineNumber": 1,
|
||||||
|
"fixInfo": {
|
||||||
|
"deleteCount": -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"world"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hello\nworld",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lineNumber": 2,
|
||||||
|
"fixInfo": {
|
||||||
|
"deleteCount": -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Hello"
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
testCases.forEach((testCase) => {
|
testCases.forEach((testCase) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue