Update MD009/MD010/MD012/MD028 to report fixInfo for violations.

This commit is contained in:
David Anson 2019-08-24 22:55:51 -07:00
parent 679c83e23b
commit 2cd27c58f2
8 changed files with 165 additions and 31 deletions

View file

@ -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");
}; };

View file

@ -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
});
} }
} }
}); });

View file

@ -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)
});
} }
} }
}); });

View file

@ -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
});
} }
}); });
} }

View file

@ -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;
}
}); });
} }
}; };

View file

@ -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)

View file

@ -2,6 +2,8 @@
Hard tab {MD010} Hard tab {MD010}
Hard tabs hard tabs {MD010}
<!-- Hard tab --> <!-- Hard tab -->
<!--Hard tab--> <!--Hard tab-->

View file

@ -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) => {