diff --git a/doc/Rules.md b/doc/Rules.md index fedfaaf0..42d0ad1c 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -190,6 +190,31 @@ for the list to fix it: * Nested Item 3 ``` +Sequentially-ordered list markers are usually left-aligned such that all items +have the same starting column: + +```markdown +... +8. Item +9. Item +10. Item +11. Item +... +``` + +This rule also supports right-alignment of list markers such that all items have +the same ending column: + +```markdown +... + 8. Item + 9. Item +10. Item +11. Item +... +``` + + ## MD006 - Consider starting bulleted lists at the beginning of the line @@ -850,6 +875,17 @@ Example invalid list for all styles: 3. Done. ``` +This rule supports 0-prefixing ordered list items for uniform indentation: + +```markdown +... +08. Item +09. Item +10. Item +11. Item +... +``` + ## MD030 - Spaces after list markers diff --git a/lib/md005.js b/lib/md005.js index 0d1c9cf7..5a220bda 100644 --- a/lib/md005.js +++ b/lib/md005.js @@ -10,10 +10,32 @@ module.exports = { "tags": [ "bullet", "ul", "indentation" ], "function": function MD005(params, onError) { shared.flattenLists().forEach(function forList(list) { + const expectedIndent = list.indent; + let expectedEnd = 0; + let actualEnd = -1; + let endMatching = false; list.items.forEach(function forItem(item) { - shared.addErrorDetailIf(onError, item.lineNumber, list.indent, - shared.indentFor(item), null, - shared.rangeFromRegExp(item.line, shared.listItemMarkerRe)); + const actualIndent = shared.indentFor(item); + if (list.unordered) { + shared.addErrorDetailIf(onError, item.lineNumber, + expectedIndent, actualIndent, null, + shared.rangeFromRegExp(item.line, shared.listItemMarkerRe)); + } else { + const match = shared.orderedListItemMarkerRe.exec(item.line); + actualEnd = match && match[0].length; + expectedEnd = expectedEnd || actualEnd; + if ((expectedIndent !== actualIndent) || endMatching) { + if (expectedEnd === actualEnd) { + endMatching = true; + } else { + const detail = endMatching ? + `Expected: (${expectedEnd}); Actual: (${actualEnd})` : + `Expected: ${expectedIndent}; Actual: ${actualIndent}`; + shared.addError(onError, item.lineNumber, detail, null, + shared.rangeFromRegExp(item.line, shared.listItemMarkerRe)); + } + } + } }); }); } diff --git a/lib/md029.js b/lib/md029.js index 2015177b..d0f2d2f9 100644 --- a/lib/md029.js +++ b/lib/md029.js @@ -4,8 +4,6 @@ const shared = require("./shared"); -const numberRe = /^[\s>]*([^.)]*)[.)]/; - module.exports = { "names": [ "MD029", "ol-prefix" ], "description": "Ordered list item prefix", @@ -17,12 +15,12 @@ module.exports = { let listStyle = style; if (listStyle === "one_or_ordered") { const second = (list.items.length > 1) && - numberRe.exec(list.items[1].line); + shared.orderedListItemMarkerRe.exec(list.items[1].line); listStyle = (second && (second[1] !== "1")) ? "ordered" : "one"; } let number = 1; list.items.forEach(function forItem(item) { - const match = numberRe.exec(item.line); + const match = shared.orderedListItemMarkerRe.exec(item.line); shared.addErrorDetailIf(onError, item.lineNumber, String(number), !match || match[1], "Style: " + (listStyle === "one" ? "1/1/1" : "1/2/3"), diff --git a/lib/shared.js b/lib/shared.js index 6af653ad..cbd5e434 100644 --- a/lib/shared.js +++ b/lib/shared.js @@ -17,7 +17,8 @@ module.exports.inlineCommentRe = inlineCommentRe; // Regular expressions for range matching module.exports.atxHeadingSpaceRe = /^#+\s*\S/; 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+)[.)]/; // readFile options for reading with the UTF-8 encoding module.exports.utf8Encoding = { "encoding": "utf8" }; diff --git a/test/detailed-results-MD001-MD010.md b/test/detailed-results-MD001-MD010.md index 57781ff7..34bafbdb 100644 --- a/test/detailed-results-MD001-MD010.md +++ b/test/detailed-results-MD001-MD010.md @@ -15,3 +15,16 @@ Text Text text + + 1. One + 2. Two + 3. Three + 4. Four +5. Five + 6. Six + 7. Seven + 8. Eight + 9. Nine +10. Ten + 11. Eleven +12. Twelve diff --git a/test/detailed-results-MD001-MD010.results.json b/test/detailed-results-MD001-MD010.results.json index 9c6918e4..832fd993 100644 --- a/test/detailed-results-MD001-MD010.results.json +++ b/test/detailed-results-MD001-MD010.results.json @@ -39,6 +39,22 @@ "errorContext": null, "errorRange": [1, 3] }, + { + "lineNumber": 23, + "ruleNames": [ "MD005", "list-indent" ], + "ruleDescription": "Inconsistent indentation for list items at the same level", + "errorDetail": "Expected: 1; Actual: 0", + "errorContext": null, + "errorRange": [1, 3] + }, + { + "lineNumber": 29, + "ruleNames": [ "MD005", "list-indent" ], + "ruleDescription": "Inconsistent indentation for list items at the same level", + "errorDetail": "Expected: (3); Actual: (4)", + "errorContext": null, + "errorRange": [1, 5] + }, { "lineNumber": 12, "ruleNames": [ "MD006", "ul-start-left" ], diff --git a/test/list-item-prefix-alignment.md b/test/list-item-prefix-alignment.md new file mode 100644 index 00000000..fc6677e8 --- /dev/null +++ b/test/list-item-prefix-alignment.md @@ -0,0 +1,96 @@ +# List Alignment + +## Leading Spaces + + 1. One + 2. Two + 3. Three + 4. Four + 5. Five + 6. Six + 7. Seven + 8. Eight + 9. Nine +10. Ten +11. Eleven +12. Twelve + +## Leading Zeroes + +01. One +02. Two +03. Three +04. Four +05. Five +06. Six +07. Seven +08. Eight +09. Nine +10. Ten +11. Eleven +12. Twelve + +## Double Leading Zeroes + +001. One +002. Two +003. Three + +## Nested Lists + +- Item +- Item + 01. One + 02. Two + 03. Three + 04. Four + 05. Five + 06. Six + 07. Seven + 08. Eight + 09. Nine + 10. Ten + 11. Eleven + 12. Twelve +- Item +- Item + 1. One + 2. Two + 3. Three + 4. Four + 5. Five + 6. Six + 7. Seven + 8. Eight + 9. Nine + 10. Ten + 11. Eleven + 12. Twelve +- Item +- Item + +## Another Nested List + +01. One +02. Two + 01. One + 02. Two +03. Three +04. Four + +## Leading Spaces Errors + + 1. One + 2. Two + 3. Three + 4. Four + 5. Five {MD005} + 6. Six + 7. Seven + 8. Eight + 9. Nine + 10. Ten + 11. Eleven {MD005} + 12. Twelve +13. Thirteen {MD005} + 14. Fourteen