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
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.
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
up into multiple lines.
configured `line_length` (default: 80 characters). To fix this, split the line
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
line length. This allows you to still include items such as long URLs without

View file

@ -3,8 +3,23 @@
"use strict";
const shared = require("./shared");
const {
addErrorDetailIf, filterTokens, forEachHeading, forEachLine, rangeFromRegExp
} = shared;
const longLineRePrefix = "^(.{";
const longLineRePostfix = "})(.*\\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 = {
"names": [ "MD013", "line-length" ],
@ -12,6 +27,11 @@ module.exports = {
"tags": [ "line_length" ],
"function": function MD013(params, onError) {
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 includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
const tables = params.config.tables;
@ -22,44 +42,34 @@ module.exports = {
}
const includeHeadings = (headings === undefined) ? true : !!headings;
const headingLineNumbers = [];
if (!includeHeadings) {
shared.forEachHeading(params, function forHeading(heading) {
forEachHeading(params, (heading) => {
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 = [];
shared.filterTokens(params, "inline", function forToken(token) {
filterTokens(params, "inline", (token) => {
let childTokenTypes = "";
token.children.forEach(function forChild(child) {
token.children.forEach((child) => {
if (child.type !== "text" || child.content !== "") {
childTokenTypes += tokenTypeMap[child.type] || "x";
}
});
if (/^[es]*lT?L[ES]*$/.test(childTokenTypes)) {
if (linkOnlyLineRe.test(childTokenTypes)) {
linkOnlyLineNumbers.push(token.lineNumber);
}
});
const longLineRe = new RegExp("^(.{" + lineLength + "})(.*\\s.*)$");
shared.forEachLine(
function forLine(line, lineIndex, inCode, onFence, inTable) {
forEachLine((line, lineIndex, inCode, onFence, inTable) => {
const lineNumber = lineIndex + 1;
const isHeading = headingLineNumbers.indexOf(lineNumber) >= 0;
const length = isHeading ? headingLineLength : lineLength;
const lengthRe = isHeading ? longHeadingLineRe : longLineRe;
if ((includeCodeBlocks || !inCode) &&
(includeTables || !inTable) &&
(includeHeadings || (headingLineNumbers.indexOf(lineNumber)) < 0) &&
(includeHeadings || !isHeading) &&
(linkOnlyLineNumbers.indexOf(lineNumber) < 0) &&
longLineRe.test(line) &&
lengthRe.test(line) &&
!labelRe.test(line)) {
shared.addErrorDetailIf(onError, lineNumber, lineLength,
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",
"default": 80
},
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": {
"description": "Include code blocks",
"type": "boolean",

View file

@ -374,6 +374,11 @@
"type": "integer",
"default": 80
},
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": {
"description": "Include code blocks",
"type": "boolean",
@ -410,6 +415,11 @@
"type": "integer",
"default": 80
},
"heading_line_length": {
"description": "Number of characters for headings",
"type": "integer",
"default": 80
},
"code_blocks": {
"description": "Include code blocks",
"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
$ command with no output
##No space A

View file

@ -28,6 +28,15 @@
},
{
"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" ],
"ruleDescription": "Dollar signs used before commands without showing output",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md014",
@ -36,7 +45,7 @@
"errorRange": [5, 2]
},
{
"lineNumber": 12,
"lineNumber": 14,
"ruleNames": [ "MD018", "no-missing-space-atx" ],
"ruleDescription": "No space after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md018",
@ -45,7 +54,7 @@
"errorRange": [1, 3]
},
{
"lineNumber": 14,
"lineNumber": 16,
"ruleNames": [ "MD019", "no-multiple-space-atx" ],
"ruleDescription": "Multiple spaces after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md019",
@ -54,7 +63,7 @@
"errorRange": [1, 5]
},
{
"lineNumber": 16,
"lineNumber": 18,
"ruleNames": [ "MD020", "no-missing-space-closed-atx" ],
"ruleDescription": "No space inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020",
@ -63,7 +72,7 @@
"errorRange": [1, 3]
},
{
"lineNumber": 18,
"lineNumber": 20,
"ruleNames": [ "MD020", "no-missing-space-closed-atx" ],
"ruleDescription": "No space inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md020",
@ -72,7 +81,7 @@
"errorRange": [13, 3]
},
{
"lineNumber": 20,
"lineNumber": 22,
"ruleNames": [ "MD021", "no-multiple-space-closed-atx" ],
"ruleDescription": "Multiple spaces inside hashes on closed atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md021",
@ -81,7 +90,7 @@
"errorRange": [1, 5]
},
{
"lineNumber": 22,
"lineNumber": 24,
"ruleNames": [ "MD021", "no-multiple-space-closed-atx" ],
"ruleDescription": "Multiple spaces inside hashes on closed atx style heading",
"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