From 46cbcfa55e6aee7818b1130e8e574286a0d70048 Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 22 Dec 2016 13:35:58 -0800 Subject: [PATCH 01/12] Rename test case files to allow for additional rules. --- ...results-MD041-MD043.json => detailed-results-MD041-MD050.json} | 0 ...led-results-MD041-MD043.md => detailed-results-MD041-MD050.md} | 0 ...043.results.json => detailed-results-MD041-MD050.results.json} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/{detailed-results-MD041-MD043.json => detailed-results-MD041-MD050.json} (100%) rename test/{detailed-results-MD041-MD043.md => detailed-results-MD041-MD050.md} (100%) rename test/{detailed-results-MD041-MD043.results.json => detailed-results-MD041-MD050.results.json} (100%) diff --git a/test/detailed-results-MD041-MD043.json b/test/detailed-results-MD041-MD050.json similarity index 100% rename from test/detailed-results-MD041-MD043.json rename to test/detailed-results-MD041-MD050.json diff --git a/test/detailed-results-MD041-MD043.md b/test/detailed-results-MD041-MD050.md similarity index 100% rename from test/detailed-results-MD041-MD043.md rename to test/detailed-results-MD041-MD050.md diff --git a/test/detailed-results-MD041-MD043.results.json b/test/detailed-results-MD041-MD050.results.json similarity index 100% rename from test/detailed-results-MD041-MD043.results.json rename to test/detailed-results-MD041-MD050.results.json From d8975282dce06ea75460f7fbc79df2e9b11a734e Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 22 Dec 2016 13:34:18 -0800 Subject: [PATCH 02/12] Add MD044 proper-names "Proper names should have the correct capitalization" (fixes #39). --- README.md | 2 + doc/Rules.md | 20 +++++++++ lib/rules.js | 22 +++++++++ schema/build-config-schema.js | 12 +++++ schema/markdownlint-config-schema.json | 43 ++++++++++++++++++ test/break-all-the-rules.json | 5 +++ test/break-all-the-rules.md | 2 + test/detailed-results-MD041-MD050.json | 5 +++ test/detailed-results-MD041-MD050.md | 2 + .../detailed-results-MD041-MD050.results.json | 11 ++++- test/markdownlint-test.js | 8 ++-- test/proper-names.json | 13 ++++++ test/proper-names.md | 45 +++++++++++++++++++ 13 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 test/proper-names.json create mode 100644 test/proper-names.md diff --git a/README.md b/README.md index 99b5f0bc..47e83cf2 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ playground for learning and exploring. * **MD041** *first-line-h1* - First line in file should be a top level header * **MD042** *no-empty-links* - No empty links * **MD043** *required-headers* - Required header structure +* **MD044** *proper-names* - Proper names should have the correct capitalization See [Rules.md](doc/Rules.md) for more details. @@ -107,6 +108,7 @@ See [Rules.md](doc/Rules.md) for more details. * **links** - MD011, MD034, MD039, MD042 * **ol** - MD029, MD030, MD032 * **spaces** - MD018, MD019, MD020, MD021, MD023 +* **spelling** - MD044 * **ul** - MD004, MD005, MD006, MD007, MD030, MD032 * **url** - MD034 * **whitespace** - MD009, MD010, MD012, MD027, MD028, MD030, MD037, MD038, MD039 diff --git a/doc/Rules.md b/doc/Rules.md index 5f7792f9..debd4c3d 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -1119,3 +1119,23 @@ problematic header (otherwise, it outputs the last line number of the file). Note that while the `headers` parameter uses the "## Text" ATX header style for simplicity, a file may use any supported header style. + +## MD044 - Proper names should have the correct capitalization + +Tags: spelling + +Aliases: proper-names + +Parameters: names (array of string; default `null` for disabled) + +This rule is triggered when any of the strings in the `names` array do not have +the specified capitalization. It can be used to enforce a standard letter case +for the names of projects and products. + +For example, the language "JavaScript" is usually written with both the 'J' and +'S' capitalized - though sometimes the 's' or 'j' appear in lower-case. To enforce +the proper capitalization, specify the desired letter case in the `names` array: + + [ + "JavaScript" + ] diff --git a/lib/rules.js b/lib/rules.js index a2d000f9..6b5497bc 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -1090,5 +1090,27 @@ module.exports = [ } } } + }, + + { + "name": "MD044", + "desc": "Proper names should have the correct capitalization", + "tags": [ "spelling" ], + "aliases": [ "proper-names" ], + "regexp": null, + "func": function MD044(params, errors) { + var names = params.options.names || []; + names.forEach(function forName(name) { + var escapedName = escapeForRegExp(name); + var namePattern = "\\b" + escapedName + "\\b"; + var anyNameRe = new RegExp(namePattern, "gi"); + forEachLine(params, function forLine(line, lineIndex) { + var matches = line.match(anyNameRe) || []; + matches.forEach(function forMatch(match) { + errors.addDetailIf(lineIndex + 1, name, match); + }); + }); + }); + } } ]; diff --git a/schema/build-config-schema.js b/schema/build-config-schema.js index 873c3acd..50a9bd7d 100644 --- a/schema/build-config-schema.js +++ b/schema/build-config-schema.js @@ -212,6 +212,18 @@ rules.forEach(function forRule(rule) { } }; break; + case "MD044": + scheme.properties = { + "names": { + "description": "List of proper names", + "type": "array", + "items": { + "type": "string" + }, + "default": null + } + }; + break; default: custom = false; break; diff --git a/schema/markdownlint-config-schema.json b/schema/markdownlint-config-schema.json index 87859dfb..fae3c959 100644 --- a/schema/markdownlint-config-schema.json +++ b/schema/markdownlint-config-schema.json @@ -871,6 +871,44 @@ }, "additionalProperties": false }, + "MD044": { + "description": "MD044/proper-names - Proper names should have the correct capitalization", + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "names": { + "description": "List of proper names", + "type": "array", + "items": { + "type": "string" + }, + "default": null + } + }, + "additionalProperties": false + }, + "proper-names": { + "description": "MD044/proper-names - Proper names should have the correct capitalization", + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "names": { + "description": "List of proper names", + "type": "array", + "items": { + "type": "string" + }, + "default": null + } + }, + "additionalProperties": false + }, "headers": { "description": "headers - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043", "type": "boolean", @@ -970,6 +1008,11 @@ "description": "language - MD040", "type": "boolean", "default": true + }, + "spelling": { + "description": "spelling - MD044", + "type": "boolean", + "default": true } }, "additionalProperties": false diff --git a/test/break-all-the-rules.json b/test/break-all-the-rules.json index cd5d749d..9f200f6b 100644 --- a/test/break-all-the-rules.json +++ b/test/break-all-the-rules.json @@ -7,5 +7,10 @@ "#### Header 2 {MD001}", "# Broken" ] + }, + "MD044": { + "names": [ + "markdownlint" + ] } } diff --git a/test/break-all-the-rules.md b/test/break-all-the-rules.md index b832885e..ae037f08 100644 --- a/test/break-all-the-rules.md +++ b/test/break-all-the-rules.md @@ -75,3 +75,5 @@ code fence without language {MD040:73} ``` [empty link]() {MD042} + +markdownLint {MD044} diff --git a/test/detailed-results-MD041-MD050.json b/test/detailed-results-MD041-MD050.json index 7b669757..e6ba034d 100644 --- a/test/detailed-results-MD041-MD050.json +++ b/test/detailed-results-MD041-MD050.json @@ -5,5 +5,10 @@ "headers": [ "# Header" ] + }, + "MD044": { + "names": [ + "markdownlint" + ] } } diff --git a/test/detailed-results-MD041-MD050.md b/test/detailed-results-MD041-MD050.md index 985d782d..6756a337 100644 --- a/test/detailed-results-MD041-MD050.md +++ b/test/detailed-results-MD041-MD050.md @@ -1,3 +1,5 @@ Not a header An [empty]() link + +This is a test file for the MARKDOWNLINT package. diff --git a/test/detailed-results-MD041-MD050.results.json b/test/detailed-results-MD041-MD050.results.json index 1b90f8d3..ba99ab55 100644 --- a/test/detailed-results-MD041-MD050.results.json +++ b/test/detailed-results-MD041-MD050.results.json @@ -18,12 +18,21 @@ "errorRange": [4, 7] }, { - "lineNumber": 4, + "lineNumber": 6, "ruleName": "MD043", "ruleAlias": "required-headers", "ruleDescription": "Required header structure", "errorDetail": null, "errorContext": "# Header", "errorRange": null + }, + { + "lineNumber": 5, + "ruleName": "MD044", + "ruleAlias": "proper-names", + "ruleDescription": "Proper names should have the correct capitalization", + "errorDetail": "Expected: markdownlint; Actual: MARKDOWNLINT", + "errorContext": null, + "errorRange": null } ] \ No newline at end of file diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 2b00e173..1e61314a 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -905,7 +905,7 @@ module.exports.missingStringValue = function missingStringValue(test) { }; module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) { - test.expect(39); + test.expect(40); rules.forEach(function forRule(rule) { test.equal(rule.name, rule.name.toUpperCase(), "Rule name not upper-case."); }); @@ -913,7 +913,7 @@ module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) { }; module.exports.uniqueAliases = function uniqueAliases(test) { - test.expect(78); + test.expect(80); var tags = []; rules.forEach(function forRule(rule) { Array.prototype.push.apply(tags, rule.tags); @@ -930,7 +930,7 @@ module.exports.uniqueAliases = function uniqueAliases(test) { }; module.exports.readme = function readme(test) { - test.expect(101); + test.expect(104); var tagToRules = {}; rules.forEach(function forRule(rule) { rule.tags.forEach(function forTag(tag) { @@ -991,7 +991,7 @@ module.exports.readme = function readme(test) { }; module.exports.doc = function doc(test) { - test.expect(295); + test.expect(303); fs.readFile("doc/Rules.md", shared.utf8Encoding, function readFile(err, contents) { test.ifError(err); diff --git a/test/proper-names.json b/test/proper-names.json new file mode 100644 index 00000000..1d8c37b7 --- /dev/null +++ b/test/proper-names.json @@ -0,0 +1,13 @@ +{ + "default": true, + "MD044": { + "names": [ + "markdownlint", + "JavaScript", + "Node.js", + "GitHub", + "npm", + "Internet Explorer" + ] + } +} diff --git a/test/proper-names.md b/test/proper-names.md new file mode 100644 index 00000000..675197cb --- /dev/null +++ b/test/proper-names.md @@ -0,0 +1,45 @@ +# markdownlint test file + +Markdownlint is a tool {MD044} + +JavaScript is a language + +JavaScript is not Java + +Nor is it Javascript. {MD044} + +markdownlint runs on Node.js via npm + +Node is an environment + +Install into it with NPM {MD044} + +Node.JSX is not a real thing + +Nor is nodesjs or NPMI + +npm can npm stand npm for npm many npm things + +Writing npm is right, but NPM is wrong {MD044} + +Get excited about Github! {MD044} + +Share code on GitHub via Git + +Internet Explorer is a web browser + +OTOH, "internet explorer" is a job {MD044} + +## node.js instructions {MD044} + +Code in `javascript` {MD044} + +Execute `via the node.js engine` {MD044} + +* Use NPM {MD044} + +> Run Markdownlint on your README {MD044} + +Upload the code (to github) {MD044} + +Link to [github](https://github.com/). {MD044} From 5fa065a7b0f8a0ed1da3445a1cd3140268ebd4ec Mon Sep 17 00:00:00 2001 From: David Anson Date: Sun, 15 Jan 2017 14:41:58 -0800 Subject: [PATCH 03/12] Update MD032/blanks-around-lists to ignore non-interrupting prefixes (fixes #34). --- lib/rules.js | 8 +++++- test/list-syntax-in-paragraph-text.md | 37 +++++++++++++++++++++++++++ test/lists_without_blank_lines.md | 4 +-- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 test/list-syntax-in-paragraph-text.md diff --git a/lib/rules.js b/lib/rules.js index 6b5497bc..8d3f2d87 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -11,6 +11,7 @@ var dollarCommandRe = /^(\s*)(\$\s)/; var emptyLinkRe = /\[[^\]]*](?=(?:\((?:#?|(?:<>))\))|(?:\[[^\]]*]))/; var htmlRe = /<[^>]*>/; var listItemMarkerRe = /^[\s>]*(?:[*+-]|\d+\.)\s+/; +var listItemMarkerInterruptsRe = /^[\s>]*(?:[*+-]|1\.)\s+/; var reversedLinkRe = /\([^)]+\)\[[^\]^][^\]]*]/; var spaceAfterBlockQuote = />\s+\S/; var spaceBeforeHeaderRe = /^\s+\S/; @@ -774,7 +775,12 @@ module.exports = [ var lineTrim = line.trim(); var listMarker = listItemMarkerRe.test(lineTrim); if (listMarker && !inList && !blankOrListRe.test(prevLine)) { - errors.addContext(lineIndex + 1, lineTrim); + // Check whether this list prefix can interrupt a paragraph + if (listItemMarkerInterruptsRe.test(lineTrim)) { + errors.addContext(lineIndex + 1, lineTrim); + } else { + listMarker = false; + } } else if (!listMarker && inList && !blankOrListRe.test(line)) { errors.addContext(lineIndex, lineTrim); } diff --git a/test/list-syntax-in-paragraph-text.md b/test/list-syntax-in-paragraph-text.md new file mode 100644 index 00000000..0c9a685f --- /dev/null +++ b/test/list-syntax-in-paragraph-text.md @@ -0,0 +1,37 @@ +# Heading + +This paragraph has the number +5. More text. + +This paragraph has the number +11. More text. + +This non-paragraph has the number +1. This is a list. {MD032} + +This non-paragraph has the number +1. This is a list. It also has the number {MD032} +5. This is a list. {MD029} + +This non-paragraph has spaces and the number +1. This is a list. {MD030} {MD032} + +This non-paragraph has a dash +- in its list. {MD032} + +This non-paragraph has a dash +- in its list. It also has a plus {MD032} ++ in its list. {MD004} + +This non-paragraph has spaces and a dash +- This is a list. {MD030} {MD032} + +This is a mixed paragraph that has +2. followed by text followed by +1. which creates a list {MD032} +1. with a couple of items + +Another mixed paragraph +2. with more text +in the middle of things +1. before the list {MD032} diff --git a/test/lists_without_blank_lines.md b/test/lists_without_blank_lines.md index b1571966..4de1221b 100644 --- a/test/lists_without_blank_lines.md +++ b/test/lists_without_blank_lines.md @@ -19,8 +19,8 @@ text text text -10. list {MD032} -20. list +1. list {MD032} +2. list text From 16dc230d542fffd152dfdededb582d1059d6889b Mon Sep 17 00:00:00 2001 From: David Anson Date: Sun, 15 Jan 2017 15:14:41 -0800 Subject: [PATCH 04/12] Tweak range of MD042/no-empty-links to include link destination. --- lib/rules.js | 2 +- test/detailed-results-MD041-MD050.md | 2 ++ test/detailed-results-MD041-MD050.results.json | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/rules.js b/lib/rules.js index 8d3f2d87..695a352f 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -8,7 +8,7 @@ var atxClosedHeaderSpaceRe = /(?:^#+\s\s+?\S)|(?:\S\s\s+?#+\s*$)/; var atxHeaderSpaceRe = /^#+\s*\S/; var bareUrlRe = /(?:http|ftp)s?:\/\/[^\s]*/; var dollarCommandRe = /^(\s*)(\$\s)/; -var emptyLinkRe = /\[[^\]]*](?=(?:\((?:#?|(?:<>))\))|(?:\[[^\]]*]))/; +var emptyLinkRe = /\[[^\]]*](?:\((?:#?|(?:<>))\))|(?:\[[^\]]*])/; var htmlRe = /<[^>]*>/; var listItemMarkerRe = /^[\s>]*(?:[*+-]|\d+\.)\s+/; var listItemMarkerInterruptsRe = /^[\s>]*(?:[*+-]|1\.)\s+/; diff --git a/test/detailed-results-MD041-MD050.md b/test/detailed-results-MD041-MD050.md index 6756a337..b564c98e 100644 --- a/test/detailed-results-MD041-MD050.md +++ b/test/detailed-results-MD041-MD050.md @@ -2,4 +2,6 @@ Not a header An [empty]() link +An [empty](#) link with fragment + This is a test file for the MARKDOWNLINT package. diff --git a/test/detailed-results-MD041-MD050.results.json b/test/detailed-results-MD041-MD050.results.json index ba99ab55..1f7f19fd 100644 --- a/test/detailed-results-MD041-MD050.results.json +++ b/test/detailed-results-MD041-MD050.results.json @@ -15,10 +15,19 @@ "ruleDescription": "No empty links", "errorDetail": null, "errorContext": "[empty]", - "errorRange": [4, 7] + "errorRange": [4, 9] }, { - "lineNumber": 6, + "lineNumber": 5, + "ruleName": "MD042", + "ruleAlias": "no-empty-links", + "ruleDescription": "No empty links", + "errorDetail": null, + "errorContext": "[empty]", + "errorRange": [4, 10] + }, + { + "lineNumber": 8, "ruleName": "MD043", "ruleAlias": "required-headers", "ruleDescription": "Required header structure", @@ -27,7 +36,7 @@ "errorRange": null }, { - "lineNumber": 5, + "lineNumber": 7, "ruleName": "MD044", "ruleAlias": "proper-names", "ruleDescription": "Proper names should have the correct capitalization", From 00171da5658fa06bce1544001bfe6ec4ac07754a Mon Sep 17 00:00:00 2001 From: David Anson Date: Sat, 11 Feb 2017 16:20:24 -0800 Subject: [PATCH 05/12] Add headers parameter to MD013/line-length (fixes #35). --- doc/Rules.md | 6 +++--- lib/rules.js | 12 +++++++++++- schema/build-config-schema.js | 5 +++++ schema/markdownlint-config-schema.json | 10 ++++++++++ test/long-header-exceptions.json | 6 ++++++ test/long-header-exceptions.md | 21 +++++++++++++++++++++ 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 test/long-header-exceptions.json create mode 100644 test/long-header-exceptions.md diff --git a/doc/Rules.md b/doc/Rules.md index debd4c3d..52ae87b7 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -334,7 +334,7 @@ Tags: line_length Aliases: line-length -Parameters: line_length, code_blocks, tables (number; default 80, boolean; default true) +Parameters: line_length, code_blocks, tables, headers (number; default 80, boolean; default true) 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 @@ -344,8 +344,8 @@ 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 being forced to break them in the middle. -You also have the option to exclude this rule for code blocks and tables. To -do this, set the `code_blocks` and/or `tables` parameters to false. +You have the option to exclude this rule for code blocks, tables, or headers. +To do so, set the `code_blocks`, `tables`, or `headers` parameter(s) to false. Code blocks are included in this rule by default since it is often a requirement for document readability, and tentatively compatible with code diff --git a/lib/rules.js b/lib/rules.js index 695a352f..355dce71 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -427,13 +427,23 @@ module.exports = [ var includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks; var tables = params.options.tables; var includeTables = (tables === undefined) ? true : !!tables; + var headers = params.options.headers; + var includeHeaders = (headers === undefined) ? true : !!headers; + var headerLineNumbers = []; + if (!includeHeaders) { + forEachHeading(params, function forHeading(heading) { + headerLineNumbers.push(heading.lineNumber); + }); + } var re = longLineReFunc(params.options); forEachLine(params, function forLine(line, lineIndex, inCode, onFence, inTable) { + var lineNumber = lineIndex + 1; if ((includeCodeBlocks || !inCode) && (includeTables || !inTable) && + (includeHeaders || (headerLineNumbers.indexOf(lineNumber)) < 0) && re.test(line)) { - errors.addDetailIf(lineIndex + 1, lineLength, line.length); + errors.addDetailIf(lineNumber, lineLength, line.length); } }); } diff --git a/schema/build-config-schema.js b/schema/build-config-schema.js index 50a9bd7d..9d0c05f1 100644 --- a/schema/build-config-schema.js +++ b/schema/build-config-schema.js @@ -129,6 +129,11 @@ rules.forEach(function forRule(rule) { "description": "Include tables", "type": "boolean", "default": true + }, + "headers": { + "description": "Include headers", + "type": "boolean", + "default": true } }; break; diff --git a/schema/markdownlint-config-schema.json b/schema/markdownlint-config-schema.json index fae3c959..a83ee102 100644 --- a/schema/markdownlint-config-schema.json +++ b/schema/markdownlint-config-schema.json @@ -323,6 +323,11 @@ "description": "Include tables", "type": "boolean", "default": true + }, + "headers": { + "description": "Include headers", + "type": "boolean", + "default": true } }, "additionalProperties": false @@ -349,6 +354,11 @@ "description": "Include tables", "type": "boolean", "default": true + }, + "headers": { + "description": "Include headers", + "type": "boolean", + "default": true } }, "additionalProperties": false diff --git a/test/long-header-exceptions.json b/test/long-header-exceptions.json new file mode 100644 index 00000000..6af9f257 --- /dev/null +++ b/test/long-header-exceptions.json @@ -0,0 +1,6 @@ +{ + "default": true, + "MD013": { + "headers": false + } +} diff --git a/test/long-header-exceptions.md b/test/long-header-exceptions.md new file mode 100644 index 00000000..ce5dba99 --- /dev/null +++ b/test/long-header-exceptions.md @@ -0,0 +1,21 @@ +# Header + +Text + +## Header header header header header header header header header header header header header header header header + +Text + +Text text text text text text text text text text text text text text text text text text text text text text text text {MD013} + +## Header header + +Text + +Text text text text text text text text text text text text text text text text text text text text text text text text {MD013} + +### Header header header header header header header header header header header header header header header header header + +Text + +Text text text text text text text text text text text text text text text text text text text text text text text text {MD013} From 31206f9387a6e5120079967350d20b1be2cbe0c5 Mon Sep 17 00:00:00 2001 From: David Anson Date: Sat, 18 Feb 2017 22:56:06 -0800 Subject: [PATCH 06/12] Reimplement MD038/no-space-in-code to handle multi-backtick strings (fixes #46). --- doc/Rules.md | 5 ++++ lib/rules.js | 25 +++++++++--------- test/spaces_inside_codespan_elements.md | 34 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/doc/Rules.md b/doc/Rules.md index 52ae87b7..abb1db06 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -986,6 +986,11 @@ To fix this, remove the spaces inside the codespan markers: `some text` +Note: A single leading or trailing space is allowed if used to separate codespan +markers from an embedded backtick: + + `` ` embedded backtick`` + ## MD039 - Spaces inside link text Tags: whitespace, links diff --git a/lib/rules.js b/lib/rules.js index 355dce71..9450fb94 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -947,20 +947,19 @@ module.exports = [ "aliases": [ "no-space-in-code" ], "regexp": spaceInsideCodeRe, "func": function MD038(params, errors) { + var inlineCodeSpansRe = /(`+)((?:.*?[^`])|)\1(?!`)/g; forEachInlineChild(params, "code_inline", - function forToken(token, inline) { - var backtickPairs = "^(?:[^`]*`[^`]*`)*[^`]*"; - var escapedContent = escapeForRegExp(token.content); - var left = (new RegExp( - backtickPairs + "(`\\s+" + escapedContent + "\\s*`)")) - .exec(inline.content); - var right = (new RegExp( - backtickPairs + "(`\\s*" + escapedContent + "\\s+`)")) - .exec(inline.content); - if (left) { - errors.addContext(token.lineNumber, left[1]); - } else if (right) { - errors.addContext(token.lineNumber, right[1], false, true); + function forToken(token) { + var line = params.lines[token.lineNumber - 1]; + var match = null; + while ((match = inlineCodeSpansRe.exec(line)) !== null) { + var inlineCodeSpan = match[0]; + var content = match[2]; + if (/^\s([^`]|$)/.test(content)) { + errors.addContext(token.lineNumber, inlineCodeSpan); + } else if (/[^`]\s$/.test(content)) { + errors.addContext(token.lineNumber, inlineCodeSpan, false, true); + } } }); } diff --git a/test/spaces_inside_codespan_elements.md b/test/spaces_inside_codespan_elements.md index f0e08952..5c60b40f 100644 --- a/test/spaces_inside_codespan_elements.md +++ b/test/spaces_inside_codespan_elements.md @@ -6,6 +6,10 @@ ` codespan element with spaces inside ` {MD038} +empty `` codespan element + +single space ` ` codespan element {MD038} + `,`, `.` `,`, `code` @@ -21,3 +25,33 @@ text `code` text `anything` code `end` text `anything` code `code` text `end` text `anything` text `anything` code `anything` `code` + +text ``code`` text ``code`` text + +text `` code`` text {MD038} + +text ``code `` text {MD038} + +text ```code``` text ```code``` text + +text ```code``` text `` code`` text {MD038} + +text ```code``` text ``code `` text {MD038} + +``embedded ` backtick`` text `code` + +`backslash does not escape \` backtick` + +`` ` `` text `code` + +` `` ` text `code` + +``` ` leading space allowed for backtick``` text `code` + +``` ` multiple leading spaces not allowed``` text `code` {MD038} + +``trailing space allowed for backtick ` `` text `code` + +``multiple trailing spaces not allowed ` `` text `code` {MD038} + +`` ` leading and trailing space allowed for backtick ` `` text `code` From 15b37d51fff027e4b937c839f159bf4004ec93e0 Mon Sep 17 00:00:00 2001 From: David Anson Date: Sat, 18 Feb 2017 23:21:46 -0800 Subject: [PATCH 07/12] Clarify MD043/required-headers reporting of extra header. --- lib/rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules.js b/lib/rules.js index 9450fb94..7059308c 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -1088,7 +1088,7 @@ module.exports = [ forEachHeading(params, function forHeading(heading, content) { if (!errors.length) { var actual = levels[heading.tag] + " " + content; - var expected = requiredHeaders[i++] || ""; + var expected = requiredHeaders[i++] || "[None]"; if (expected === "*") { optional = true; } else if (expected.toLowerCase() === actual.toLowerCase()) { From acd36d5802a79b99525da2d7d3b10e8dc7ad9792 Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 23 Feb 2017 22:08:54 -0800 Subject: [PATCH 08/12] Update MD041/first-line-h1 to ignore leading HTML comments (fixes #37). --- doc/Rules.md | 4 ---- lib/rules.js | 20 +++++++++---------- ...d041-ignore-leading-comments-combined.json | 4 ++++ .../md041-ignore-leading-comments-combined.md | 11 ++++++++++ ...041-ignore-leading-comments-violation.json | 4 ++++ ...md041-ignore-leading-comments-violation.md | 11 ++++++++++ test/md041-ignore-leading-comments.json | 4 ++++ test/md041-ignore-leading-comments.md | 13 ++++++++++++ 8 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 test/md041-ignore-leading-comments-combined.json create mode 100644 test/md041-ignore-leading-comments-combined.md create mode 100644 test/md041-ignore-leading-comments-violation.json create mode 100644 test/md041-ignore-leading-comments-violation.md create mode 100644 test/md041-ignore-leading-comments.json create mode 100644 test/md041-ignore-leading-comments.md diff --git a/doc/Rules.md b/doc/Rules.md index abb1db06..201606e1 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -1037,17 +1037,13 @@ Parameters: level (number; default 1) This rule is triggered when the first line in the file isn't a top level (h1) header: - ``` This is a file without a header - ``` To fix this, add a header to the top of your file: - ``` # File with header This is a file with a top level header - ``` Note: The `level` parameter can be used to change the top level (ex: to h2) in cases where an h1 is added externally. diff --git a/lib/rules.js b/lib/rules.js index 7059308c..053daf8b 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -1019,21 +1019,19 @@ module.exports = [ "func": function MD041(params, errors) { var level = params.options.level || 1; var tag = "h" + level; - var firstHeader = null; - params.tokens.every(function forToken(token) { + params.tokens.every(function forToken(token, index) { if (token.type === "heading_open") { - firstHeader = token; - return false; - } else if (token.lineNumber > 1) { + if (!((token.lineNumber === 1) || (index > 0)) || + (token.tag !== tag)) { + errors.addContext(token.lineNumber, token.line); + } return false; + } else if (token.type === "html_block") { + return true; } - return true; + errors.addContext(token.lineNumber, token.line); + return false; }); - if (!firstHeader || - (firstHeader.lineNumber !== 1) || - (firstHeader.tag !== tag)) { - errors.addContext(1, params.lines[0]); - } } }, diff --git a/test/md041-ignore-leading-comments-combined.json b/test/md041-ignore-leading-comments-combined.json new file mode 100644 index 00000000..2be9298b --- /dev/null +++ b/test/md041-ignore-leading-comments-combined.json @@ -0,0 +1,4 @@ +{ + "default": true, + "first-line-h1": true +} diff --git a/test/md041-ignore-leading-comments-combined.md b/test/md041-ignore-leading-comments-combined.md new file mode 100644 index 00000000..e3dc0814 --- /dev/null +++ b/test/md041-ignore-leading-comments-combined.md @@ -0,0 +1,11 @@ +# Heading # + +Text text text + +Embedded tab + +Text text text + +Trailing space {MD009} + +Text text text diff --git a/test/md041-ignore-leading-comments-violation.json b/test/md041-ignore-leading-comments-violation.json new file mode 100644 index 00000000..2be9298b --- /dev/null +++ b/test/md041-ignore-leading-comments-violation.json @@ -0,0 +1,4 @@ +{ + "default": true, + "first-line-h1": true +} diff --git a/test/md041-ignore-leading-comments-violation.md b/test/md041-ignore-leading-comments-violation.md new file mode 100644 index 00000000..58ca7025 --- /dev/null +++ b/test/md041-ignore-leading-comments-violation.md @@ -0,0 +1,11 @@ + + +Text text text {MD041} + +Embedded tab + +Text text text + +Trailing space {MD009} + +Text text text diff --git a/test/md041-ignore-leading-comments.json b/test/md041-ignore-leading-comments.json new file mode 100644 index 00000000..2be9298b --- /dev/null +++ b/test/md041-ignore-leading-comments.json @@ -0,0 +1,4 @@ +{ + "default": true, + "first-line-h1": true +} diff --git a/test/md041-ignore-leading-comments.md b/test/md041-ignore-leading-comments.md new file mode 100644 index 00000000..3c61047d --- /dev/null +++ b/test/md041-ignore-leading-comments.md @@ -0,0 +1,13 @@ + + +# Heading + +Text text text + +Embedded tab + +Text text text + +Trailing space {MD009} + +Text text text From 0c1f40323dfc3f92b7a7f1a91db93d05ba524ff4 Mon Sep 17 00:00:00 2001 From: David Anson Date: Wed, 1 Mar 2017 21:43:04 -0800 Subject: [PATCH 09/12] Update MD013/line-length not to report long link-only lines (fixes #36). --- lib/rules.js | 17 ++++++++++++++--- test/long_lines.md | 22 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/rules.js b/lib/rules.js index 053daf8b..f77e0b8e 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -56,7 +56,7 @@ function unorderedListStyleFor(token) { return "dash"; case "+": return "plus"; - case "*": + // case "*": default: return "asterisk"; } @@ -435,14 +435,25 @@ module.exports = [ headerLineNumbers.push(heading.lineNumber); }); } - var re = longLineReFunc(params.options); + var linkOnlyLineNumbers = []; + filterTokens(params, "inline", function forToken(token) { + if (((token.children.length === 2) || (token.children.length === 3)) && + (token.children[0].type === "link_open") && + (token.children[token.children.length - 1].type === "link_close")) { + linkOnlyLineNumbers.push(token.lineNumber); + } + }); + var longLineRe = longLineReFunc(params.options); + var labelRe = /^\s*\[.*[^\\]]:/; forEachLine(params, function forLine(line, lineIndex, inCode, onFence, inTable) { var lineNumber = lineIndex + 1; if ((includeCodeBlocks || !inCode) && (includeTables || !inTable) && (includeHeaders || (headerLineNumbers.indexOf(lineNumber)) < 0) && - re.test(line)) { + (linkOnlyLineNumbers.indexOf(lineNumber) < 0) && + longLineRe.test(line) && + !labelRe.test(line)) { errors.addDetailIf(lineNumber, lineLength, line.length); } }); diff --git a/test/long_lines.md b/test/long_lines.md index 875c8c69..94ae56af 100644 --- a/test/long_lines.md +++ b/test/long_lines.md @@ -1,3 +1,23 @@ This is a very very very very very very very very very very very very very very long line {MD013} -This line however, while very long, doesn't have whitespace after the 80th columnwhichallowsforURLsandotherlongthings. \ No newline at end of file +This line however, while very long, doesn't have whitespace after the 80th columnwhichallowsforURLsandotherlongthings. + +[This long line is comprised entirely of a link](http://example.com "This is the long link's title") + +> [This long line is comprised entirely of a link](http://example.com "This is the long link's title") + + [This long line is comprised entirely of a link](http://example.com "But is inside a code block") {MD013} + +This [long line is comprised mostly of a link](http://example.com "This is the long link's title") {MD013} + +[This long line is comprised mostly of a link](http://example.com "This is the long link's title") text {MD013} + +This long line includes a simple [reference][label] link and is long enough to violate the rule. {MD013} + +[This long line is comprised entirely of a reference link and is long enough to violate the rule][label] + +[label]: http://example.org "Title for a link reference that is itself long enough to violate the rule" + +[Link to broken label][notlabel] + +[notlabel\]: notlink "Invalid syntax for a link label because the right bracket is backslash-escaped {MD013}" From f39f3b2e45fa57edb0b36190c943294c7c2aafd1 Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 2 Mar 2017 21:32:43 -0800 Subject: [PATCH 10/12] Update copyright year to 2017. --- LICENSE | 2 +- demo/default.htm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 576a5e0f..c6a0b942 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2016 David Anson +Copyright (c) 2015-2017 David Anson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/demo/default.htm b/demo/default.htm index 14548fe3..114073aa 100644 --- a/demo/default.htm +++ b/demo/default.htm @@ -28,7 +28,7 @@
- +
From 7a669bc50f80e2423e024657d5688d28cb0d7384 Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 2 Mar 2017 21:54:06 -0800 Subject: [PATCH 11/12] Update dependencies: browserify to 14.1.0, eslint to 3.16.1, nodeunit to 0.11.0, rimraf to 2.6.1, uglify-js to 2.8.5. --- .eslintrc.json | 137 ++++++++++++++++++++++++++----------------------- package.json | 10 ++-- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index eeb60e8d..48d362a9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,8 +5,69 @@ "node": true }, "rules": { + "accessor-pairs": "error", + "array-bracket-spacing": ["error", "always"], + "array-callback-return": "error", + "arrow-body-style": "error", + "arrow-parens": "error", + "arrow-spacing": "error", + "block-scoped-var": "error", + "block-spacing": "error", + "brace-style": "error", + "callback-return": "off", + "camelcase": "error", + "capitalized-comments": "off", + "class-methods-use-this": "error", + "comma-dangle": "error", + "comma-spacing": "error", + "comma-style": "error", + "complexity": "error", + "computed-property-spacing": "error", + "consistent-return": "off", + "consistent-this": "error", + "constructor-super": "error", + "curly": "error", + "default-case": "error", + "dot-location": ["error", "property"], + "dot-notation": "error", + "eol-last": "error", + "eqeqeq": "error", + "func-call-spacing": "error", + "func-name-matching": "off", + "func-names": "error", + "func-style": ["error", "declaration"], + "generator-star-spacing": "error", + "global-require": "off", + "guard-for-in": "error", + "handle-callback-err": "error", + "id-blacklist": "error", + "id-length": "off", + "id-match": "error", + "indent": ["error", 2, { "SwitchCase": 1 }], + "init-declarations": "error", + "jsx-quotes": "error", + "key-spacing": "error", + "keyword-spacing": "error", + "line-comment-position": "error", + "linebreak-style": "off", + "lines-around-comment": "off", + "lines-around-directive": "error", + "max-depth": "error", + "max-len": "error", + "max-lines": "off", + "max-nested-callbacks": "error", + "max-params": ["error", 6], + "max-statements": ["error", 28], + "max-statements-per-line": "error", + "multiline-ternary": "off", + "new-cap": "error", + "new-parens": "error", + "newline-after-var": "off", + "newline-before-return": "off", + "newline-per-chained-call": "off", "no-alert": "error", "no-array-constructor": "error", + "no-await-in-loop": "error", "no-bitwise": "off", "no-caller": "error", "no-case-declarations": "error", @@ -63,6 +124,7 @@ "no-mixed-operators": "error", "no-mixed-requires": "error", "no-mixed-spaces-and-tabs": "error", + "no-multi-assign": "off", "no-multi-spaces": "error", "no-multi-str": "error", "no-multiple-empty-lines": "error", @@ -101,20 +163,20 @@ "no-sequences": "error", "no-shadow": "error", "no-shadow-restricted-names": "error", - "no-whitespace-before-property": "error", "no-spaced-func": "error", "no-sparse-arrays": "error", "no-sync": "off", "no-tabs": "error", + "no-template-curly-in-string": "error", "no-ternary": "off", - "no-trailing-spaces": "error", "no-this-before-super": "error", "no-throw-literal": "error", + "no-trailing-spaces": "error", "no-undef": "error", "no-undef-init": "error", "no-undefined": "off", - "no-unexpected-multiline": "error", "no-underscore-dangle": "error", + "no-unexpected-multiline": "error", "no-unmodified-loop-condition": "error", "no-unneeded-ternary": "error", "no-unreachable": "error", @@ -131,69 +193,11 @@ "no-useless-escape": "error", "no-useless-rename": "error", "no-useless-return": "error", - "no-void": "error", "no-var": "off", + "no-void": "error", "no-warning-comments": "error", + "no-whitespace-before-property": "error", "no-with": "error", - "array-bracket-spacing": ["error", "always"], - "array-callback-return": "error", - "arrow-body-style": "error", - "arrow-parens": "error", - "arrow-spacing": "error", - "accessor-pairs": "error", - "block-scoped-var": "error", - "block-spacing": "error", - "brace-style": "error", - "callback-return": "off", - "camelcase": "error", - "class-methods-use-this": "error", - "comma-dangle": "error", - "comma-spacing": "error", - "comma-style": "error", - "complexity": "error", - "computed-property-spacing": "error", - "consistent-return": "off", - "consistent-this": "error", - "constructor-super": "error", - "curly": "error", - "default-case": "error", - "dot-location": ["error", "property"], - "dot-notation": "error", - "eol-last": "error", - "eqeqeq": "error", - "func-call-spacing": "error", - "func-names": "error", - "func-name-matching": "off", - "func-style": ["error", "declaration"], - "generator-star-spacing": "error", - "global-require": "off", - "guard-for-in": "error", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "indent": ["error", 2, { "SwitchCase": 1 }], - "init-declarations": "error", - "jsx-quotes": "error", - "key-spacing": "error", - "keyword-spacing": "error", - "linebreak-style": "off", - "line-comment-position": "error", - "lines-around-comment": "off", - "lines-around-directive": "error", - "max-depth": "error", - "max-len": "error", - "max-lines": "off", - "max-nested-callbacks": "error", - "max-params": ["error", 6], - "max-statements": ["error", 28], - "max-statements-per-line": "error", - "multiline-ternary": "off", - "new-cap": "error", - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", "object-curly-newline": "off", "object-curly-spacing": ["error", "always"], "object-property-newline": "error", @@ -205,7 +209,9 @@ "padded-blocks": "off", "prefer-arrow-callback": "off", "prefer-const": "off", + "prefer-destructuring": "off", "prefer-numeric-literals": "error", + "prefer-promise-reject-errors": "error", "prefer-reflect": "off", "prefer-rest-params": "off", "prefer-spread": "off", @@ -213,13 +219,14 @@ "quote-props": "error", "quotes": "error", "radix": "error", + "require-await": "error", "require-jsdoc": "off", "require-yield": "error", "rest-spread-spacing": "error", "semi": "error", "semi-spacing": "error", - "sort-keys": "off", "sort-imports": "error", + "sort-keys": "off", "sort-vars": "error", "space-before-blocks": "error", "space-before-function-paren": ["error", "never"], @@ -230,6 +237,7 @@ "strict": "error", "symbol-description": "error", "template-curly-spacing": "error", + "template-tag-spacing": "error", "unicode-bom": "error", "use-isnan": "error", "valid-jsdoc": "error", @@ -237,7 +245,6 @@ "vars-on-top": "off", "wrap-iife": "error", "wrap-regex": "off", - "no-template-curly-in-string": "error", "yield-star-spacing": "error", "yoda": "error" } diff --git a/package.json b/package.json index 0de21663..66878ba7 100644 --- a/package.json +++ b/package.json @@ -26,16 +26,16 @@ "markdown-it": "^8.0.1" }, "devDependencies": { - "browserify": "^13.1.1", + "browserify": "^14.1.0", "cpy-cli": "^1.0.1", - "eslint": "^3.10.0", + "eslint": "^3.16.1", "glob": "^7.1.1", "istanbul": "^0.4.5", - "nodeunit": "^0.10.2", + "nodeunit": "^0.11.0", "q": "^1.4.1", - "rimraf": "^2.5.4", + "rimraf": "^2.6.1", "tv4": "^1.2.7", - "uglify-js": "^2.7.4" + "uglify-js": "^2.8.5" }, "keywords": [ "markdown", From 5cfb41f6946ea78f9e41b3812cd0d5b796fc53d2 Mon Sep 17 00:00:00 2001 From: David Anson Date: Fri, 3 Mar 2017 22:48:05 -0800 Subject: [PATCH 12/12] Update dependency: markdown-it to 8.3.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66878ba7..306164ab 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "example": "cd example && node standalone.js && grunt markdownlint --force && gulp markdownlint" }, "dependencies": { - "markdown-it": "^8.0.1" + "markdown-it": "^8.3.0" }, "devDependencies": { "browserify": "^14.1.0",