diff --git a/helpers/helpers.js b/helpers/helpers.js index f407e7ef..d5f0c0b9 100644 --- a/helpers/helpers.js +++ b/helpers/helpers.js @@ -404,7 +404,7 @@ module.exports.frontMatterHasTitle = module.exports.fixErrors = function fixErrors(input, errors) { const lines = input.split(newLineRe); // Normalize fixInfo objects - const fixInfos = errors.filter((error) => !!error.fixInfo).map((error) => { + let fixInfos = errors.filter((error) => !!error.fixInfo).map((error) => { const { fixInfo } = error; return { "lineNumber": fixInfo.lineNumber || error.lineNumber, @@ -413,14 +413,46 @@ module.exports.fixErrors = function fixErrors(input, errors) { "insertText": fixInfo.insertText || "" }; }); - // Sort bottom-to-top, deletes last, right-to-left, long-to-short - fixInfos.sort((a, b) => ( - (b.lineNumber - a.lineNumber) || - ((a.deleteCount === -1) ? 1 : ((b.deleteCount === -1) ? -1 : 0)) || - (b.editColumn - a.editColumn) || - (b.insertText.length - a.insertText.length) - )); - // Apply all fixes + // Sort bottom-to-top, line-deletes last, right-to-left, long-to-short + fixInfos.sort((a, b) => { + const aDeletingLine = (a.deleteCount === -1); + const bDeletingLine = (b.deleteCount === -1); + return ( + (b.lineNumber - a.lineNumber) || + (aDeletingLine ? 1 : (bDeletingLine ? -1 : 0)) || + (b.editColumn - a.editColumn) || + (b.insertText.length - a.insertText.length) + ); + }); + // Remove duplicate entries (needed for following collapse step) + let lastFixInfo = {}; + fixInfos = fixInfos.filter((fixInfo) => { + const unique = ( + (fixInfo.lineNumber !== lastFixInfo.lineNumber) || + (fixInfo.editColumn !== lastFixInfo.editColumn) || + (fixInfo.deleteCount !== lastFixInfo.deleteCount) || + (fixInfo.insertText !== lastFixInfo.insertText) + ); + lastFixInfo = fixInfo; + return unique; + }); + // Collapse insert/no-delete and no-insert/delete for same line/column + lastFixInfo = {}; + fixInfos.forEach((fixInfo) => { + if ( + (fixInfo.lineNumber === lastFixInfo.lineNumber) && + (fixInfo.editColumn === lastFixInfo.editColumn) && + !fixInfo.insertText && + (fixInfo.deleteCount > 0) && + lastFixInfo.insertText && + !lastFixInfo.deleteCount) { + fixInfo.insertText = lastFixInfo.insertText; + lastFixInfo.lineNumber = 0; + } + lastFixInfo = fixInfo; + }); + fixInfos = fixInfos.filter((fixInfo) => fixInfo.lineNumber); + // Apply all (remaining/updated) fixes let lastLineIndex = -1; let lastEditIndex = -1; fixInfos.forEach((fixInfo) => { diff --git a/lib/md006.js b/lib/md006.js index 2117ffaa..673bb1f0 100644 --- a/lib/md006.js +++ b/lib/md006.js @@ -13,10 +13,19 @@ module.exports = { "tags": [ "bullet", "ul", "indentation" ], "function": function MD006(params, onError) { flattenedLists().forEach((list) => { - if (list.unordered && !list.nesting) { - addErrorDetailIf(onError, list.open.lineNumber, - 0, list.indent, null, null, - rangeFromRegExp(list.open.line, listItemMarkerRe)); + if (list.unordered && !list.nesting && (list.indent !== 0)) { + const { lineNumber, line } = list.open; + addErrorDetailIf( + onError, + lineNumber, + 0, + list.indent, + null, + null, + rangeFromRegExp(line, listItemMarkerRe), + { + "deleteCount": line.length - line.trimLeft().length + }); } }); } diff --git a/lib/md023.js b/lib/md023.js index bf29d513..92d4c00f 100644 --- a/lib/md023.js +++ b/lib/md023.js @@ -2,8 +2,7 @@ "use strict"; -const { addErrorContext, filterTokens, rangeFromRegExp } = - require("../helpers"); +const { addErrorContext, filterTokens } = require("../helpers"); const spaceBeforeHeadingRe = /^((?:\s+)|(?:[>\s]+\s\s))[^>\s]/; @@ -13,9 +12,26 @@ module.exports = { "tags": [ "headings", "headers", "spaces" ], "function": function MD023(params, onError) { filterTokens(params, "heading_open", function forToken(token) { - if (spaceBeforeHeadingRe.test(token.line)) { - addErrorContext(onError, token.lineNumber, token.line, null, - null, rangeFromRegExp(token.line, spaceBeforeHeadingRe)); + const { lineNumber, line } = token; + const match = line.match(spaceBeforeHeadingRe); + if (match) { + const [ prefixAndFirstChar, prefix ] = match; + let deleteCount = prefix.length; + const prefixLengthNoSpace = prefix.trimRight().length; + if (prefixLengthNoSpace) { + deleteCount -= prefixLengthNoSpace - 1; + } + addErrorContext( + onError, + lineNumber, + line, + null, + null, + [ 1, prefixAndFirstChar.length ], + { + "editColumn": prefixLengthNoSpace + 1, + "deleteCount": deleteCount + }); } }); } diff --git a/test/lists-with-nesting.md b/test/lists-with-nesting.md index defdf813..5c89a8c9 100644 --- a/test/lists-with-nesting.md +++ b/test/lists-with-nesting.md @@ -16,13 +16,11 @@ - one {MD032} 1. two {MD032} - 1. three {MD032} -- four {MD032} +- three {MD032} 1. one {MD032} - two {MD006} {MD032} - - three {MD032} -1. four {MD032} +1. three {MD032} ## Correct nesting, same type diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 537d5c7a..437e5a6a 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -1898,7 +1898,7 @@ module.exports.forEachInlineCodeSpan = function forEachInlineCodeSpan(test) { }; module.exports.fixErrors = function fixErrors(test) { - test.expect(23); + test.expect(24); const testCases = [ [ "Hello world.", @@ -2221,6 +2221,20 @@ module.exports.fixErrors = function fixErrors(test) { ], "Hello wld" ], + [ + "Hello world", + [ + { + "fixInfo": { + "lineNumber": 1, + "editColumn": 7, + "deleteCount": 1, + "insertText": "z" + } + } + ], + "Hello zorld" + ], [ "Hello world", [ @@ -2239,7 +2253,7 @@ module.exports.fixErrors = function fixErrors(test) { } } ], - "Hello zworld" + "Hello zorld" ], [ "Hello world", @@ -2259,7 +2273,7 @@ module.exports.fixErrors = function fixErrors(test) { } } ], - "Hello zworld" + "Hello zorld" ] ]; testCases.forEach((testCase) => {