Update MD013/line-length with heading_line_length parameter (fixes #170).

This commit is contained in:
David Anson 2019-03-26 22:34:19 -07:00
parent fa04d29485
commit d7c0d195d7
9 changed files with 110 additions and 40 deletions

View file

@ -443,13 +443,14 @@ Tags: line_length
Aliases: line-length Aliases: line-length
Parameters: line_length, code_blocks, tables, headings, headers (number; default 80, boolean; default true) Parameters: line_length, heading_line_length, code_blocks, tables, headings, headers (number; default 80, boolean; default true)
> If `headings` is not provided, `headers` (deprecated) will be used. > If `headings` is not provided, `headers` (deprecated) will be used.
This rule is triggered when there are lines that are longer than the This rule is triggered when there are lines that are longer than the
configured line length (default: 80 characters). To fix this, split the line configured `line_length` (default: 80 characters). To fix this, split the line
up into multiple lines. up into multiple lines. To set a different maximum length for headings, use
`heading_line_length`.
This rule has an exception where there is no whitespace beyond the configured This rule has an exception where there is no whitespace beyond the configured
line length. This allows you to still include items such as long URLs without line length. This allows you to still include items such as long URLs without

View file

@ -3,8 +3,23 @@
"use strict"; "use strict";
const shared = require("./shared"); const shared = require("./shared");
const {
addErrorDetailIf, filterTokens, forEachHeading, forEachLine, rangeFromRegExp
} = shared;
const longLineRePrefix = "^(.{";
const longLineRePostfix = "})(.*\\s.*)$";
const labelRe = /^\s*\[.*[^\\]]:/; const labelRe = /^\s*\[.*[^\\]]:/;
const linkOnlyLineRe = /^[es]*lT?L[ES]*$/;
const tokenTypeMap = {
"em_open": "e",
"em_close": "E",
"link_open": "l",
"link_close": "L",
"strong_open": "s",
"strong_close": "S",
"text": "T"
};
module.exports = { module.exports = {
"names": [ "MD013", "line-length" ], "names": [ "MD013", "line-length" ],
@ -12,6 +27,11 @@ module.exports = {
"tags": [ "line_length" ], "tags": [ "line_length" ],
"function": function MD013(params, onError) { "function": function MD013(params, onError) {
const lineLength = params.config.line_length || 80; const lineLength = params.config.line_length || 80;
const headingLineLength = params.config.heading_line_length || lineLength;
const longLineRe =
new RegExp(longLineRePrefix + lineLength + longLineRePostfix);
const longHeadingLineRe =
new RegExp(longLineRePrefix + headingLineLength + longLineRePostfix);
const codeBlocks = params.config.code_blocks; const codeBlocks = params.config.code_blocks;
const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks; const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
const tables = params.config.tables; const tables = params.config.tables;
@ -22,45 +42,35 @@ module.exports = {
} }
const includeHeadings = (headings === undefined) ? true : !!headings; const includeHeadings = (headings === undefined) ? true : !!headings;
const headingLineNumbers = []; const headingLineNumbers = [];
if (!includeHeadings) { forEachHeading(params, (heading) => {
shared.forEachHeading(params, function forHeading(heading) { headingLineNumbers.push(heading.lineNumber);
headingLineNumbers.push(heading.lineNumber); });
});
}
const tokenTypeMap = {
"em_open": "e",
"em_close": "E",
"link_open": "l",
"link_close": "L",
"strong_open": "s",
"strong_close": "S",
"text": "T"
};
const linkOnlyLineNumbers = []; const linkOnlyLineNumbers = [];
shared.filterTokens(params, "inline", function forToken(token) { filterTokens(params, "inline", (token) => {
let childTokenTypes = ""; let childTokenTypes = "";
token.children.forEach(function forChild(child) { token.children.forEach((child) => {
if (child.type !== "text" || child.content !== "") { if (child.type !== "text" || child.content !== "") {
childTokenTypes += tokenTypeMap[child.type] || "x"; childTokenTypes += tokenTypeMap[child.type] || "x";
} }
}); });
if (/^[es]*lT?L[ES]*$/.test(childTokenTypes)) { if (linkOnlyLineRe.test(childTokenTypes)) {
linkOnlyLineNumbers.push(token.lineNumber); linkOnlyLineNumbers.push(token.lineNumber);
} }
}); });
const longLineRe = new RegExp("^(.{" + lineLength + "})(.*\\s.*)$"); forEachLine((line, lineIndex, inCode, onFence, inTable) => {
shared.forEachLine( const lineNumber = lineIndex + 1;
function forLine(line, lineIndex, inCode, onFence, inTable) { const isHeading = headingLineNumbers.indexOf(lineNumber) >= 0;
const lineNumber = lineIndex + 1; const length = isHeading ? headingLineLength : lineLength;
if ((includeCodeBlocks || !inCode) && const lengthRe = isHeading ? longHeadingLineRe : longLineRe;
(includeTables || !inTable) && if ((includeCodeBlocks || !inCode) &&
(includeHeadings || (headingLineNumbers.indexOf(lineNumber)) < 0) && (includeTables || !inTable) &&
(linkOnlyLineNumbers.indexOf(lineNumber) < 0) && (includeHeadings || !isHeading) &&
longLineRe.test(line) && (linkOnlyLineNumbers.indexOf(lineNumber) < 0) &&
!labelRe.test(line)) { lengthRe.test(line) &&
shared.addErrorDetailIf(onError, lineNumber, lineLength, !labelRe.test(line)) {
line.length, null, null, shared.rangeFromRegExp(line, longLineRe)); addErrorDetailIf(onError, lineNumber, length, line.length,
} null, null, rangeFromRegExp(line, lengthRe));
}); }
});
} }
}; };

View file

@ -129,6 +129,11 @@ rules.forEach(function forRule(rule) {
"type": "integer", "type": "integer",
"default": 80 "default": 80
}, },
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": { "code_blocks": {
"description": "Include code blocks", "description": "Include code blocks",
"type": "boolean", "type": "boolean",

View file

@ -374,6 +374,11 @@
"type": "integer", "type": "integer",
"default": 80 "default": 80
}, },
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": { "code_blocks": {
"description": "Include code blocks", "description": "Include code blocks",
"type": "boolean", "type": "boolean",
@ -410,6 +415,11 @@
"type": "integer", "type": "integer",
"default": 80 "default": 80
}, },
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": { "code_blocks": {
"description": "Include code blocks", "description": "Include code blocks",
"type": "boolean", "type": "boolean",

View file

@ -0,0 +1,6 @@
{
"default": true,
"MD013": {
"heading_line_length": 40
}
}

View file

@ -7,6 +7,8 @@ A (reversed)[link] example.
123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789
## 123456789 123456789 123456789 123456789 123456789 123456789
$ command with no output $ command with no output
##No space A ##No space A

View file

@ -28,6 +28,15 @@
}, },
{ {
"lineNumber": 10, "lineNumber": 10,
"ruleNames": [ "MD013", "line-length" ],
"ruleDescription": "Line length",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md013",
"errorDetail": "Expected: 40; Actual: 62",
"errorContext": null,
"errorRange": [41, 22]
},
{
"lineNumber": 12,
"ruleNames": [ "MD014", "commands-show-output" ], "ruleNames": [ "MD014", "commands-show-output" ],
"ruleDescription": "Dollar signs used before commands without showing output", "ruleDescription": "Dollar signs used before commands without showing output",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md014", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md014",
@ -36,7 +45,7 @@
"errorRange": [5, 2] "errorRange": [5, 2]
}, },
{ {
"lineNumber": 12, "lineNumber": 14,
"ruleNames": [ "MD018", "no-missing-space-atx" ], "ruleNames": [ "MD018", "no-missing-space-atx" ],
"ruleDescription": "No space after hash on atx style heading", "ruleDescription": "No space after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md018", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md018",
@ -45,7 +54,7 @@
"errorRange": [1, 3] "errorRange": [1, 3]
}, },
{ {
"lineNumber": 14, "lineNumber": 16,
"ruleNames": [ "MD019", "no-multiple-space-atx" ], "ruleNames": [ "MD019", "no-multiple-space-atx" ],
"ruleDescription": "Multiple spaces after hash on atx style heading", "ruleDescription": "Multiple spaces after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md019", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md019",
@ -54,7 +63,7 @@
"errorRange": [1, 5] "errorRange": [1, 5]
}, },
{ {
"lineNumber": 16, "lineNumber": 18,
"ruleNames": [ "MD020", "no-missing-space-closed-atx" ], "ruleNames": [ "MD020", "no-missing-space-closed-atx" ],
"ruleDescription": "No space inside hashes on closed atx style heading", "ruleDescription": "No space inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020",
@ -63,7 +72,7 @@
"errorRange": [1, 3] "errorRange": [1, 3]
}, },
{ {
"lineNumber": 18, "lineNumber": 20,
"ruleNames": [ "MD020", "no-missing-space-closed-atx" ], "ruleNames": [ "MD020", "no-missing-space-closed-atx" ],
"ruleDescription": "No space inside hashes on closed atx style heading", "ruleDescription": "No space inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020",
@ -72,7 +81,7 @@
"errorRange": [13, 3] "errorRange": [13, 3]
}, },
{ {
"lineNumber": 20, "lineNumber": 22,
"ruleNames": [ "MD021", "no-multiple-space-closed-atx" ], "ruleNames": [ "MD021", "no-multiple-space-closed-atx" ],
"ruleDescription": "Multiple spaces inside hashes on closed atx style heading", "ruleDescription": "Multiple spaces inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md021", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md021",
@ -81,7 +90,7 @@
"errorRange": [1, 5] "errorRange": [1, 5]
}, },
{ {
"lineNumber": 22, "lineNumber": 24,
"ruleNames": [ "MD021", "no-multiple-space-closed-atx" ], "ruleNames": [ "MD021", "no-multiple-space-closed-atx" ],
"ruleDescription": "Multiple spaces inside hashes on closed atx style heading", "ruleDescription": "Multiple spaces inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md021", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md021",

View file

@ -0,0 +1,7 @@
{
"default": true,
"MD003": false,
"MD013": {
"heading_line_length": 30
}
}

View file

@ -0,0 +1,20 @@
# Long Lines, Short Headings
Text text text text text text text text text text text text text text text text text {MD013}
## Short heading text text text
Text
## Long heading text text text {MD013}
Text
## Long heading text text {MD013} ##
Text
Long heading of text text text text text text {MD013}
-----------------------------------------------------
Text