Update MD018/MD019/MD020/MD021 to report fixInfo for violations.

This commit is contained in:
David Anson 2019-09-08 16:51:00 -07:00
parent c8a74bd72c
commit 316bfeadaa
8 changed files with 167 additions and 50 deletions

View file

@ -19,7 +19,6 @@ const inlineCommentRe =
module.exports.inlineCommentRe = inlineCommentRe; module.exports.inlineCommentRe = inlineCommentRe;
// Regular expressions for range matching // Regular expressions for range matching
module.exports.atxHeadingSpaceRe = /^#+\s*\S/;
module.exports.bareUrlRe = /(?:http|ftp)s?:\/\/[^\s]*/i; module.exports.bareUrlRe = /(?:http|ftp)s?:\/\/[^\s]*/i;
module.exports.listItemMarkerRe = /^[\s>]*(?:[*+-]|\d+[.)])\s+/; module.exports.listItemMarkerRe = /^[\s>]*(?:[*+-]|\d+[.)])\s+/;
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/; module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;

View file

@ -2,8 +2,7 @@
"use strict"; "use strict";
const { addErrorContext, atxHeadingSpaceRe, forEachLine, const { addErrorContext, forEachLine } = require("../helpers");
rangeFromRegExp } = require("../helpers");
const { lineMetadata } = require("./cache"); const { lineMetadata } = require("./cache");
module.exports = { module.exports = {
@ -12,9 +11,20 @@ module.exports = {
"tags": [ "headings", "headers", "atx", "spaces" ], "tags": [ "headings", "headers", "atx", "spaces" ],
"function": function MD018(params, onError) { "function": function MD018(params, onError) {
forEachLine(lineMetadata(), (line, lineIndex, inCode) => { forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
if (!inCode && /^#+[^#\s]/.test(line) && !/#$/.test(line)) { if (!inCode && /^#+[^#\s]/.test(line) && !/#\s*$/.test(line)) {
addErrorContext(onError, lineIndex + 1, line.trim(), null, const hashCount = /^#+/.exec(line)[0].length;
null, rangeFromRegExp(line, atxHeadingSpaceRe)); addErrorContext(
onError,
lineIndex + 1,
line.trim(),
null,
null,
[ 1, hashCount + 1 ],
{
"editColumn": hashCount + 1,
"insertText": " "
}
);
} }
}); });
} }

View file

@ -2,20 +2,37 @@
"use strict"; "use strict";
const { addErrorContext, atxHeadingSpaceRe, filterTokens, headingStyleFor, const { addErrorContext, filterTokens, headingStyleFor } =
rangeFromRegExp } = require("../helpers"); require("../helpers");
module.exports = { module.exports = {
"names": [ "MD019", "no-multiple-space-atx" ], "names": [ "MD019", "no-multiple-space-atx" ],
"description": "Multiple spaces after hash on atx style heading", "description": "Multiple spaces after hash on atx style heading",
"tags": [ "headings", "headers", "atx", "spaces" ], "tags": [ "headings", "headers", "atx", "spaces" ],
"function": function MD019(params, onError) { "function": function MD019(params, onError) {
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", (token) => {
if ((headingStyleFor(token) === "atx") && if (headingStyleFor(token) === "atx") {
/^#+\s\s/.test(token.line)) { const { line, lineNumber } = token;
addErrorContext(onError, token.lineNumber, token.line.trim(), const match = /^(#+)(\s{2,})/.exec(line);
null, null, if (match) {
rangeFromRegExp(token.line, atxHeadingSpaceRe)); const [
,
{ "length": hashLength },
{ "length": spacesLength }
] = match;
addErrorContext(
onError,
lineNumber,
line.trim(),
null,
null,
[ 1, hashLength + spacesLength + 1 ],
{
"editColumn": hashLength + 1,
"deleteCount": spacesLength - 1
}
);
}
} }
}); });
} }

View file

@ -2,23 +2,59 @@
"use strict"; "use strict";
const { addErrorContext, forEachLine, rangeFromRegExp } = require("../helpers"); const { addErrorContext, forEachLine } = require("../helpers");
const { lineMetadata } = require("./cache"); const { lineMetadata } = require("./cache");
const atxClosedHeadingNoSpaceRe = /(?:^#+[^#\s])|(?:[^#\s]#+\s*$)/;
module.exports = { module.exports = {
"names": [ "MD020", "no-missing-space-closed-atx" ], "names": [ "MD020", "no-missing-space-closed-atx" ],
"description": "No space inside hashes on closed atx style heading", "description": "No space inside hashes on closed atx style heading",
"tags": [ "headings", "headers", "atx_closed", "spaces" ], "tags": [ "headings", "headers", "atx_closed", "spaces" ],
"function": function MD020(params, onError) { "function": function MD020(params, onError) {
forEachLine(lineMetadata(), (line, lineIndex, inCode) => { forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
if (!inCode && /^#+[^#]*[^\\]#+$/.test(line)) { if (!inCode) {
const left = /^#+[^#\s]/.test(line); const match = /^(#+)(\s*)([^#]+?)(\s*)(\\?)(#+)(\s*)$/.exec(line);
const right = /[^#\s]#+$/.test(line); if (match) {
const [
,
leftHash,
{ "length": leftSpaceLength },
content,
{ "length": rightSpaceLength },
rightEscape,
rightHash,
{ "length": trailSpaceLength }
] = match;
const leftHashLength = leftHash.length;
const rightHashLength = rightHash.length;
const left = !leftSpaceLength;
const right =
(!rightSpaceLength && (!rightEscape || (rightHashLength > 1))) ||
(rightEscape && (rightHashLength > 1));
if (left || right) { if (left || right) {
addErrorContext(onError, lineIndex + 1, line.trim(), left, const range = left ?
right, rangeFromRegExp(line, atxClosedHeadingNoSpaceRe)); [
1,
leftHashLength + 1
] :
[
line.length - trailSpaceLength - rightHashLength,
rightHashLength + 1
];
addErrorContext(
onError,
lineIndex + 1,
line.trim(),
left,
right,
range,
{
"editColumn": 1,
"deleteLength": line.length,
"insertText":
`${leftHash} ${content} ${rightEscape}${rightHash}`
}
);
}
} }
} }
}); });

View file

@ -2,24 +2,57 @@
"use strict"; "use strict";
const { addErrorContext, filterTokens, headingStyleFor, rangeFromRegExp } = const { addErrorContext, filterTokens, headingStyleFor } =
require("../helpers"); require("../helpers");
const atxClosedHeadingSpaceRe = /(?:^#+\s\s+?\S)|(?:\S\s\s+?#+\s*$)/;
module.exports = { module.exports = {
"names": [ "MD021", "no-multiple-space-closed-atx" ], "names": [ "MD021", "no-multiple-space-closed-atx" ],
"description": "Multiple spaces inside hashes on closed atx style heading", "description": "Multiple spaces inside hashes on closed atx style heading",
"tags": [ "headings", "headers", "atx_closed", "spaces" ], "tags": [ "headings", "headers", "atx_closed", "spaces" ],
"function": function MD021(params, onError) { "function": function MD021(params, onError) {
filterTokens(params, "heading_open", function forToken(token) { filterTokens(params, "heading_open", (token) => {
if (headingStyleFor(token) === "atx_closed") { if (headingStyleFor(token) === "atx_closed") {
const left = /^#+\s\s/.test(token.line); const { line, lineNumber } = token;
const right = /\s\s#+$/.test(token.line); const match = /^(#+)(\s+)([^#]+?)(\s+)(#+)(\s*)$/.exec(line);
if (match) {
const [
,
leftHash,
{ "length": leftSpaceLength },
content,
{ "length": rightSpaceLength },
rightHash,
{ "length": trailSpaceLength }
] = match;
const left = leftSpaceLength > 1;
const right = rightSpaceLength > 1;
if (left || right) { if (left || right) {
addErrorContext(onError, token.lineNumber, token.line.trim(), const length = line.length;
left, right, const leftHashLength = leftHash.length;
rangeFromRegExp(token.line, atxClosedHeadingSpaceRe)); const rightHashLength = rightHash.length;
const range = left ?
[
1,
leftHashLength + leftSpaceLength + 1
] :
[
length - trailSpaceLength - rightHashLength - rightSpaceLength,
rightSpaceLength + rightHashLength + 1
];
addErrorContext(
onError,
lineNumber,
line.trim(),
left,
right,
range,
{
"editColumn": 1,
"deleteLength": length,
"insertText": `${leftHash} ${content} ${rightHash}`
}
);
}
} }
} }
}); });

View file

@ -0,0 +1,19 @@
# atx-heading-spacing-trailing-spaces
<!-- markdownlint-disable heading-style -->
##Heading 1 {MD018}
## Heading 2 {MD019}
##Heading 3 {MD020} ##
## Heading 4 {MD020}##
##Heading 5 {MD020}##
## Heading 5 {MD021} ##
## Heading 6 {MD021} ##
## Heading 7 {MD021} ##

View file

@ -27,17 +27,17 @@ long line long line long line long line long line long line long line long line
# Heading 5 {MD019} # Heading 5 {MD019}
#Heading 6 {MD020} # #Heading 6 {MD020} #
# Heading 7 {MD021} {MD022} {MD023} {MD003} #
# Heading 7 {MD021} {MD003} #
# Heading 8 # Heading 8
# Heading 8 # Heading 8
{MD024:34} {MD024:35}
Note: Can not break MD025 and MD002 in the same file Note: Can not break MD025 and MD002 in the same file
# Heading 9 {MD026}. # Heading 9 {MD023} {MD026}.
> {MD027} > {MD027}
@ -78,4 +78,7 @@ code fence without language {MD040:73} {MD046:73}
markdownLint {MD044} markdownLint {MD044}
![](image.jpg) {MD045} {MD047} ![](image.jpg) {MD045}
## Heading 10 {MD022}
EOF {MD047}

View file

@ -1038,7 +1038,7 @@ module.exports.styleAll = function styleAll(test) {
const expectedResult = { const expectedResult = {
"./test/break-all-the-rules.md": { "./test/break-all-the-rules.md": {
"MD001": [ 3 ], "MD001": [ 3 ],
"MD003": [ 5, 30 ], "MD003": [ 5, 31 ],
"MD004": [ 8 ], "MD004": [ 8 ],
"MD005": [ 12 ], "MD005": [ 12 ],
"MD006": [ 8 ], "MD006": [ 8 ],
@ -1052,10 +1052,10 @@ module.exports.styleAll = function styleAll(test) {
"MD018": [ 25 ], "MD018": [ 25 ],
"MD019": [ 27 ], "MD019": [ 27 ],
"MD020": [ 29 ], "MD020": [ 29 ],
"MD021": [ 30 ], "MD021": [ 31 ],
"MD022": [ 30 ], "MD022": [ 82 ],
"MD023": [ 30 ], "MD023": [ 40 ],
"MD024": [ 34 ], "MD024": [ 35 ],
"MD026": [ 40 ], "MD026": [ 40 ],
"MD027": [ 42 ], "MD027": [ 42 ],
"MD028": [ 43 ], "MD028": [ 43 ],
@ -1075,7 +1075,7 @@ module.exports.styleAll = function styleAll(test) {
"MD042": [ 77 ], "MD042": [ 77 ],
"MD045": [ 81 ], "MD045": [ 81 ],
"MD046": [ 49, 73 ], "MD046": [ 49, 73 ],
"MD047": [ 81 ] "MD047": [ 84 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -1095,7 +1095,7 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
const expectedResult = { const expectedResult = {
"./test/break-all-the-rules.md": { "./test/break-all-the-rules.md": {
"MD001": [ 3 ], "MD001": [ 3 ],
"MD003": [ 5, 30 ], "MD003": [ 5, 31 ],
"MD004": [ 8 ], "MD004": [ 8 ],
"MD005": [ 12 ], "MD005": [ 12 ],
"MD011": [ 16 ], "MD011": [ 16 ],
@ -1103,10 +1103,10 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD018": [ 25 ], "MD018": [ 25 ],
"MD019": [ 27 ], "MD019": [ 27 ],
"MD020": [ 29 ], "MD020": [ 29 ],
"MD021": [ 30 ], "MD021": [ 31 ],
"MD022": [ 30 ], "MD022": [ 82 ],
"MD023": [ 30 ], "MD023": [ 40 ],
"MD024": [ 34 ], "MD024": [ 35 ],
"MD026": [ 40 ], "MD026": [ 40 ],
"MD029": [ 47 ], "MD029": [ 47 ],
"MD031": [ 50 ], "MD031": [ 50 ],
@ -1116,7 +1116,7 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD042": [ 77 ], "MD042": [ 77 ],
"MD045": [ 81 ], "MD045": [ 81 ],
"MD046": [ 49, 73 ], "MD046": [ 49, 73 ],
"MD047": [ 81 ] "MD047": [ 84 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");