Update rules MD049/emphasis-style and MD050/strong-style to include range and fixInfo when reporting issues (i.e., to be automatically fixable).

This commit is contained in:
David Anson 2021-11-28 23:18:57 -08:00
parent a508824b0f
commit 291597edb9
13 changed files with 263 additions and 41 deletions

View file

@ -783,6 +783,57 @@ module.exports.applyFixes = function applyFixes(input, errors) {
// Return corrected input // Return corrected input
return lines.filter(function (line) { return line !== null; }).join(lineEnding); return lines.filter(function (line) { return line !== null; }).join(lineEnding);
}; };
/**
* Gets the range and fixInfo values for reporting an error if the expected
* text is found on the specified line.
*
* @param {string[]} lines Lines of Markdown content.
* @param {number} lineIndex Line index to check.
* @param {string} search Text to search for.
* @param {string} replace Text to replace with.
* @returns {Object} Range and fixInfo wrapper.
*/
function getRangeAndFixInfoIfFound(lines, lineIndex, search, replace) {
var range = null;
var fixInfo = null;
var searchIndex = lines[lineIndex].indexOf(search);
if (searchIndex !== -1) {
var column = searchIndex + 1;
var length = search.length;
range = [column, length];
fixInfo = {
"editColumn": column,
"deleteCount": length,
"insertText": replace
};
}
return {
range: range,
fixInfo: fixInfo
};
}
module.exports.getRangeAndFixInfoIfFound = getRangeAndFixInfoIfFound;
/**
* Gets the next (subsequent) child token if it is of the expected type.
*
* @param {Object} parentToken Parent token.
* @param {Object} childToken Child token basis.
* @param {string} nextType Token type of next token.
* @param {string} nextNextType Token type of next-next token.
* @returns {Object} Next token.
*/
function getNextChildToken(parentToken, childToken, nextType, nextNextType) {
var children = parentToken.children;
var index = children.indexOf(childToken);
if ((index !== -1) &&
(children.length > index + 2) &&
(children[index + 1].type === nextType) &&
(children[index + 2].type === nextNextType)) {
return children[index + 1];
}
return null;
}
module.exports.getNextChildToken = getNextChildToken;
/***/ }), /***/ }),
@ -4083,20 +4134,31 @@ module.exports = {
"use strict"; "use strict";
// @ts-check // @ts-check
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, emphasisOrStrongStyleFor = _a.emphasisOrStrongStyleFor, forEachInlineChild = _a.forEachInlineChild; var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addError = _a.addError, emphasisOrStrongStyleFor = _a.emphasisOrStrongStyleFor, forEachInlineChild = _a.forEachInlineChild, getNextChildToken = _a.getNextChildToken, getRangeAndFixInfoIfFound = _a.getRangeAndFixInfoIfFound;
module.exports = { module.exports = {
"names": ["MD049", "emphasis-style"], "names": ["MD049", "emphasis-style"],
"description": "Emphasis style should be consistent", "description": "Emphasis style should be consistent",
"tags": ["emphasis"], "tags": ["emphasis"],
"function": function MD049(params, onError) { "function": function MD049(params, onError) {
var expectedStyle = String(params.config.style || "consistent"); var expectedStyle = String(params.config.style || "consistent");
forEachInlineChild(params, "em_open", function (token) { forEachInlineChild(params, "em_open", function (token, parent) {
var lineNumber = token.lineNumber, markup = token.markup; var lineNumber = token.lineNumber, markup = token.markup;
var markupStyle = emphasisOrStrongStyleFor(markup); var markupStyle = emphasisOrStrongStyleFor(markup);
if (expectedStyle === "consistent") { if (expectedStyle === "consistent") {
expectedStyle = markupStyle; expectedStyle = markupStyle;
} }
addErrorDetailIf(onError, lineNumber, expectedStyle, markupStyle); if (expectedStyle !== markupStyle) {
var rangeAndFixInfo = {};
var contentToken = getNextChildToken(parent, token, "text", "em_close");
if (contentToken) {
var content = contentToken.content;
var actual = "" + markup + content + markup;
var expectedMarkup = (expectedStyle === "asterisk") ? "*" : "_";
var expected = "" + expectedMarkup + content + expectedMarkup;
rangeAndFixInfo = getRangeAndFixInfoIfFound(params.lines, lineNumber - 1, actual, expected);
}
addError(onError, lineNumber, "Expected: " + expectedStyle + "; Actual: " + markupStyle, null, rangeAndFixInfo.range, rangeAndFixInfo.fixInfo);
}
}); });
} }
}; };
@ -4113,20 +4175,31 @@ module.exports = {
"use strict"; "use strict";
// @ts-check // @ts-check
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, emphasisOrStrongStyleFor = _a.emphasisOrStrongStyleFor, forEachInlineChild = _a.forEachInlineChild; var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addError = _a.addError, emphasisOrStrongStyleFor = _a.emphasisOrStrongStyleFor, forEachInlineChild = _a.forEachInlineChild, getNextChildToken = _a.getNextChildToken, getRangeAndFixInfoIfFound = _a.getRangeAndFixInfoIfFound;
module.exports = { module.exports = {
"names": ["MD050", "strong-style"], "names": ["MD050", "strong-style"],
"description": "Strong style should be consistent", "description": "Strong style should be consistent",
"tags": ["emphasis"], "tags": ["emphasis"],
"function": function MD050(params, onError) { "function": function MD050(params, onError) {
var expectedStyle = String(params.config.style || "consistent"); var expectedStyle = String(params.config.style || "consistent");
forEachInlineChild(params, "strong_open", function (token) { forEachInlineChild(params, "strong_open", function (token, parent) {
var lineNumber = token.lineNumber, markup = token.markup; var lineNumber = token.lineNumber, markup = token.markup;
var markupStyle = emphasisOrStrongStyleFor(markup); var markupStyle = emphasisOrStrongStyleFor(markup);
if (expectedStyle === "consistent") { if (expectedStyle === "consistent") {
expectedStyle = markupStyle; expectedStyle = markupStyle;
} }
addErrorDetailIf(onError, lineNumber, expectedStyle, markupStyle); if (expectedStyle !== markupStyle) {
var rangeAndFixInfo = {};
var contentToken = getNextChildToken(parent, token, "text", "strong_close");
if (contentToken) {
var content = contentToken.content;
var actual = "" + markup + content + markup;
var expectedMarkup = (expectedStyle === "asterisk") ? "**" : "__";
var expected = "" + expectedMarkup + content + expectedMarkup;
rangeAndFixInfo = getRangeAndFixInfoIfFound(params.lines, lineNumber - 1, actual, expected);
}
addError(onError, lineNumber, "Expected: " + expectedStyle + "; Actual: " + markupStyle, null, rangeAndFixInfo.range, rangeAndFixInfo.fixInfo);
}
}); });
} }
}; };

View file

@ -1923,6 +1923,8 @@ Aliases: emphasis-style
Parameters: style ("consistent", "asterisk", "underscore"; default "consistent") Parameters: style ("consistent", "asterisk", "underscore"; default "consistent")
Fixable: Most violations can be fixed by tooling
This rule is triggered when the symbols used in the document for emphasis do not This rule is triggered when the symbols used in the document for emphasis do not
match the configured emphasis style: match the configured emphasis style:
@ -1953,6 +1955,8 @@ Aliases: strong-style
Parameters: style ("consistent", "asterisk", "underscore"; default "consistent") Parameters: style ("consistent", "asterisk", "underscore"; default "consistent")
Fixable: Most violations can be fixed by tooling
This rule is triggered when the symbols used in the document for strong do not This rule is triggered when the symbols used in the document for strong do not
match the configured strong style: match the configured strong style:

View file

@ -813,3 +813,58 @@ module.exports.applyFixes = function applyFixes(input, errors) {
// Return corrected input // Return corrected input
return lines.filter((line) => line !== null).join(lineEnding); return lines.filter((line) => line !== null).join(lineEnding);
}; };
/**
* Gets the range and fixInfo values for reporting an error if the expected
* text is found on the specified line.
*
* @param {string[]} lines Lines of Markdown content.
* @param {number} lineIndex Line index to check.
* @param {string} search Text to search for.
* @param {string} replace Text to replace with.
* @returns {Object} Range and fixInfo wrapper.
*/
function getRangeAndFixInfoIfFound(lines, lineIndex, search, replace) {
let range = null;
let fixInfo = null;
const searchIndex = lines[lineIndex].indexOf(search);
if (searchIndex !== -1) {
const column = searchIndex + 1;
const length = search.length;
range = [ column, length ];
fixInfo = {
"editColumn": column,
"deleteCount": length,
"insertText": replace
};
}
return {
range,
fixInfo
};
}
module.exports.getRangeAndFixInfoIfFound = getRangeAndFixInfoIfFound;
/**
* Gets the next (subsequent) child token if it is of the expected type.
*
* @param {Object} parentToken Parent token.
* @param {Object} childToken Child token basis.
* @param {string} nextType Token type of next token.
* @param {string} nextNextType Token type of next-next token.
* @returns {Object} Next token.
*/
function getNextChildToken(parentToken, childToken, nextType, nextNextType) {
const { children } = parentToken;
const index = children.indexOf(childToken);
if (
(index !== -1) &&
(children.length > index + 2) &&
(children[index + 1].type === nextType) &&
(children[index + 2].type === nextNextType)
) {
return children[index + 1];
}
return null;
}
module.exports.getNextChildToken = getNextChildToken;

View file

@ -2,8 +2,8 @@
"use strict"; "use strict";
const { addErrorDetailIf, emphasisOrStrongStyleFor, forEachInlineChild } = const { addError, emphasisOrStrongStyleFor, forEachInlineChild,
require("../helpers"); getNextChildToken, getRangeAndFixInfoIfFound } = require("../helpers");
module.exports = { module.exports = {
"names": [ "MD049", "emphasis-style" ], "names": [ "MD049", "emphasis-style" ],
@ -11,18 +11,35 @@ module.exports = {
"tags": [ "emphasis" ], "tags": [ "emphasis" ],
"function": function MD049(params, onError) { "function": function MD049(params, onError) {
let expectedStyle = String(params.config.style || "consistent"); let expectedStyle = String(params.config.style || "consistent");
forEachInlineChild(params, "em_open", (token) => { forEachInlineChild(params, "em_open", (token, parent) => {
const { lineNumber, markup } = token; const { lineNumber, markup } = token;
const markupStyle = emphasisOrStrongStyleFor(markup); const markupStyle = emphasisOrStrongStyleFor(markup);
if (expectedStyle === "consistent") { if (expectedStyle === "consistent") {
expectedStyle = markupStyle; expectedStyle = markupStyle;
} }
addErrorDetailIf( if (expectedStyle !== markupStyle) {
let rangeAndFixInfo = {};
const contentToken = getNextChildToken(
parent, token, "text", "em_close"
);
if (contentToken) {
const { content } = contentToken;
const actual = `${markup}${content}${markup}`;
const expectedMarkup = (expectedStyle === "asterisk") ? "*" : "_";
const expected = `${expectedMarkup}${content}${expectedMarkup}`;
rangeAndFixInfo = getRangeAndFixInfoIfFound(
params.lines, lineNumber - 1, actual, expected
);
}
addError(
onError, onError,
lineNumber, lineNumber,
expectedStyle, `Expected: ${expectedStyle}; Actual: ${markupStyle}`,
markupStyle null,
rangeAndFixInfo.range,
rangeAndFixInfo.fixInfo
); );
}
}); });
} }
}; };

View file

@ -2,8 +2,8 @@
"use strict"; "use strict";
const { addErrorDetailIf, emphasisOrStrongStyleFor, forEachInlineChild } = const { addError, emphasisOrStrongStyleFor, forEachInlineChild,
require("../helpers"); getNextChildToken, getRangeAndFixInfoIfFound } = require("../helpers");
module.exports = { module.exports = {
"names": [ "MD050", "strong-style" ], "names": [ "MD050", "strong-style" ],
@ -11,18 +11,35 @@ module.exports = {
"tags": [ "emphasis" ], "tags": [ "emphasis" ],
"function": function MD050(params, onError) { "function": function MD050(params, onError) {
let expectedStyle = String(params.config.style || "consistent"); let expectedStyle = String(params.config.style || "consistent");
forEachInlineChild(params, "strong_open", (token) => { forEachInlineChild(params, "strong_open", (token, parent) => {
const { lineNumber, markup } = token; const { lineNumber, markup } = token;
const markupStyle = emphasisOrStrongStyleFor(markup); const markupStyle = emphasisOrStrongStyleFor(markup);
if (expectedStyle === "consistent") { if (expectedStyle === "consistent") {
expectedStyle = markupStyle; expectedStyle = markupStyle;
} }
addErrorDetailIf( if (expectedStyle !== markupStyle) {
let rangeAndFixInfo = {};
const contentToken = getNextChildToken(
parent, token, "text", "strong_close"
);
if (contentToken) {
const { content } = contentToken;
const actual = `${markup}${content}${markup}`;
const expectedMarkup = (expectedStyle === "asterisk") ? "**" : "__";
const expected = `${expectedMarkup}${content}${expectedMarkup}`;
rangeAndFixInfo = getRangeAndFixInfoIfFound(
params.lines, lineNumber - 1, actual, expected
);
}
addError(
onError, onError,
lineNumber, lineNumber,
expectedStyle, `Expected: ${expectedStyle}; Actual: ${markupStyle}`,
markupStyle null,
rangeAndFixInfo.range,
rangeAndFixInfo.fixInfo
); );
}
}); });
} }
}; };

View file

@ -64,7 +64,7 @@ https://example.com/page {MD034}
_Section {MD036} Heading_ _Section {MD036} Heading_
Emphasis *with * space {MD037} Emphasis _with _ space {MD037}
Code `with ` space {MD038} Code `with ` space {MD038}

View file

@ -28,9 +28,9 @@ Fenced code
Fenced code Fenced code
~~~ ~~~
Mixed *emphasis* on _this_ line Mixed *emphasis* on _this_ line *with* multiple _issues_
Mixed __strong emphasis__ on **this** line Mixed __strong emphasis__ on **this** line __with__ multiple **issues**
Inconsistent Inconsistent
emphasis _text emphasis _text

View file

@ -28,9 +28,9 @@ Fenced code
Fenced code Fenced code
~~~ ~~~
Mixed *emphasis* on _this_ line Mixed *emphasis* on *this* line *with* multiple *issues*
Mixed __strong emphasis__ on **this** line Mixed __strong emphasis__ on __this__ line __with__ multiple __issues__
Inconsistent Inconsistent
emphasis _text emphasis _text

View file

@ -212,8 +212,35 @@
{ {
"errorContext": null, "errorContext": null,
"errorDetail": "Expected: asterisk; Actual: underscore", "errorDetail": "Expected: asterisk; Actual: underscore",
"errorRange": null, "errorRange": [
"fixInfo": null, 21,
6
],
"fixInfo": {
"deleteCount": 6,
"editColumn": 21,
"insertText": "*this*"
},
"lineNumber": 31,
"ruleDescription": "Emphasis style should be consistent",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md049",
"ruleNames": [
"MD049",
"emphasis-style"
]
},
{
"errorContext": null,
"errorDetail": "Expected: asterisk; Actual: underscore",
"errorRange": [
49,
8
],
"fixInfo": {
"deleteCount": 8,
"editColumn": 49,
"insertText": "*issues*"
},
"lineNumber": 31, "lineNumber": 31,
"ruleDescription": "Emphasis style should be consistent", "ruleDescription": "Emphasis style should be consistent",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md049", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md049",
@ -238,8 +265,35 @@
{ {
"errorContext": null, "errorContext": null,
"errorDetail": "Expected: underscore; Actual: asterisk", "errorDetail": "Expected: underscore; Actual: asterisk",
"errorRange": null, "errorRange": [
"fixInfo": null, 30,
8
],
"fixInfo": {
"deleteCount": 8,
"editColumn": 30,
"insertText": "__this__"
},
"lineNumber": 33,
"ruleDescription": "Strong style should be consistent",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md050",
"ruleNames": [
"MD050",
"strong-style"
]
},
{
"errorContext": null,
"errorDetail": "Expected: underscore; Actual: asterisk",
"errorRange": [
62,
10
],
"fixInfo": {
"deleteCount": 10,
"editColumn": 62,
"insertText": "__issues__"
},
"lineNumber": 33, "lineNumber": 33,
"ruleDescription": "Strong style should be consistent", "ruleDescription": "Strong style should be consistent",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md050", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md050",

View file

@ -2,9 +2,11 @@
[test _test_ test](www.test.com) [test _test_ test](www.test.com)
[test `test` test](www.test.com) [test `test` test](www.test.com)
[test *test* test](www.test.com) {MD049} [test *test* test](www.test.com)
[test *test* *test* test](www.test.com) {MD049} [test *test* *test* test](www.test.com)
[test *test* *test* *test* test](www.test.com) {MD049} [test *test* *test* *test* test](www.test.com)
[test **test** test](www.test.com) [test **test** test](www.test.com)
[test __test__ test](www.test.com) {MD050} [test __test__ test](www.test.com)
[this should not raise](www.shouldnotraise.com) [this should not raise](www.shouldnotraise.com)
<!-- markdownlint-disable-file MD049 MD050 -->

View file

@ -10,7 +10,7 @@
[This link has `code` and right space ](link) {MD039} [This link has `code` and right space ](link) {MD039}
[ This link has _emphasis_ and left space](link) {MD039} {MD049} [ This link has *emphasis* and left space](link) {MD039}
[This](link) line has [multiple](link) links. [This](link) line has [multiple](link) links.

View file

@ -15,3 +15,5 @@ This paragraph _nests both *kinds* of emphasis_ marker. {MD049}
This paragraph _nests both **kinds** of emphasis_ marker. {MD049} {MD050} This paragraph _nests both **kinds** of emphasis_ marker. {MD049} {MD050}
This paragraph __nests both **kinds** of emphasis__ marker. {MD050} This paragraph __nests both **kinds** of emphasis__ marker. {MD050}
<!-- markdownlint-disable-file MD037 -->

View file

@ -6,8 +6,6 @@ Quoted "Markdownlint" {MD044}
Emphasized *Markdownlint* {MD044} Emphasized *Markdownlint* {MD044}
Emphasized _Markdownlint_ {MD044} {MD049}
JavaScript is a language JavaScript is a language
JavaScript is not Java JavaScript is not Java
@ -52,7 +50,7 @@ HTML <u>javascript</u> {MD033} {MD044}
node.js is runtime {MD044} node.js is runtime {MD044}
```js ```js
javascript is code {MD044} {MD046:54} javascript is code {MD044} {MD046:52}
node.js is runtime {MD044} node.js is runtime {MD044}
``` ```