mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 05:50:13 +01:00
Reimplement MD049/emphasis-style, MD050/strong-style to use micromark parser (with smaller ranges and handling of multi-line fixes).
This commit is contained in:
parent
e1233aad4b
commit
7005a8a438
9 changed files with 1211 additions and 348 deletions
|
|
@ -1165,61 +1165,6 @@ function applyFixes(input, errors) {
|
|||
}
|
||||
module.exports.applyFixes = applyFixes;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {number} [instance] Instance on the line (1-based).
|
||||
* @returns {Object} Range and fixInfo wrapper.
|
||||
*/
|
||||
module.exports.getRangeAndFixInfoIfFound = function (lines, lineIndex, search, replace) {
|
||||
var instance = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
|
||||
var range = null;
|
||||
var fixInfo = null;
|
||||
var searchIndex = -1;
|
||||
while (instance > 0) {
|
||||
searchIndex = lines[lineIndex].indexOf(search, searchIndex + 1);
|
||||
instance--;
|
||||
}
|
||||
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
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Expands a path with a tilde to an absolute path.
|
||||
*
|
||||
|
|
@ -1569,6 +1514,17 @@ function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified token iff it is of the desired type.
|
||||
*
|
||||
* @param {Token} token Micromark token candidate.
|
||||
* @param {string} type Desired type.
|
||||
* @returns {Token | null} Token instance.
|
||||
*/
|
||||
function tokenIfType(token, type) {
|
||||
return token && token.type === type ? token : null;
|
||||
}
|
||||
module.exports = {
|
||||
"parse": micromarkParse,
|
||||
filterByPredicate: filterByPredicate,
|
||||
|
|
@ -1576,7 +1532,8 @@ module.exports = {
|
|||
getHtmlTagInfo: getHtmlTagInfo,
|
||||
getMicromarkEvents: getMicromarkEvents,
|
||||
getTokenTextByType: getTokenTextByType,
|
||||
matchAndGetTokensByType: matchAndGetTokensByType
|
||||
matchAndGetTokensByType: matchAndGetTokensByType,
|
||||
tokenIfType: tokenIfType
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
|
|
@ -5387,7 +5344,8 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
|
|||
var _require = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"),
|
||||
addErrorContext = _require.addErrorContext;
|
||||
var _require2 = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"),
|
||||
filterByTypes = _require2.filterByTypes;
|
||||
filterByTypes = _require2.filterByTypes,
|
||||
tokenIfType = _require2.tokenIfType;
|
||||
var leftSpaceRe = /^\s(?:[^`]|$)/;
|
||||
var rightSpaceRe = /[^`]\s$/;
|
||||
var trimCodeText = function trimCodeText(text, start, end) {
|
||||
|
|
@ -5400,9 +5358,6 @@ var trimCodeText = function trimCodeText(text, start, end) {
|
|||
}
|
||||
return text;
|
||||
};
|
||||
var tokenIfType = function tokenIfType(token, type) {
|
||||
return token && token.type === type && token;
|
||||
};
|
||||
module.exports = {
|
||||
"names": ["MD038", "no-space-in-code"],
|
||||
"description": "Spaces inside code span elements",
|
||||
|
|
@ -6086,49 +6041,56 @@ module.exports = {
|
|||
|
||||
|
||||
|
||||
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
||||
var _require = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"),
|
||||
addError = _require.addError,
|
||||
emphasisOrStrongStyleFor = _require.emphasisOrStrongStyleFor,
|
||||
forEachInlineChild = _require.forEachInlineChild,
|
||||
getNextChildToken = _require.getNextChildToken,
|
||||
getRangeAndFixInfoIfFound = _require.getRangeAndFixInfoIfFound;
|
||||
var impl = function impl(params, onError, tagPrefix, asterisk, underline) {
|
||||
emphasisOrStrongStyleFor = _require.emphasisOrStrongStyleFor;
|
||||
var _require2 = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"),
|
||||
filterByTypes = _require2.filterByTypes,
|
||||
tokenIfType = _require2.tokenIfType;
|
||||
var impl = function impl(params, onError, type, asterisk, underline) {
|
||||
var style = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "consistent";
|
||||
var lastLineNumber = -1;
|
||||
var instances = new Map();
|
||||
forEachInlineChild(params, "".concat(tagPrefix, "_open"), function (token, parent) {
|
||||
var lineNumber = token.lineNumber,
|
||||
markup = token.markup;
|
||||
var markupStyle = emphasisOrStrongStyleFor(markup);
|
||||
if (style === "consistent") {
|
||||
style = markupStyle;
|
||||
}
|
||||
if (style !== markupStyle) {
|
||||
var rangeAndFixInfo = {};
|
||||
var contentToken = getNextChildToken(parent, token, "text", "".concat(tagPrefix, "_close"));
|
||||
if (contentToken) {
|
||||
var content = contentToken.content;
|
||||
var actual = "".concat(markup).concat(content).concat(markup);
|
||||
var expectedMarkup = style === "asterisk" ? asterisk : underline;
|
||||
var expected = "".concat(expectedMarkup).concat(content).concat(expectedMarkup);
|
||||
if (lastLineNumber !== lineNumber) {
|
||||
lastLineNumber = lineNumber;
|
||||
instances.clear();
|
||||
var emphasisTokens = filterByTypes(params.parsers.micromark.tokens, [type]);
|
||||
var _iterator = _createForOfIteratorHelper(emphasisTokens),
|
||||
_step;
|
||||
try {
|
||||
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
||||
var token = _step.value;
|
||||
var children = token.children;
|
||||
var childType = "".concat(type, "Sequence");
|
||||
var startSequence = tokenIfType(children[0], childType);
|
||||
var endSequence = tokenIfType(children[children.length - 1], childType);
|
||||
if (startSequence && endSequence) {
|
||||
var markupStyle = emphasisOrStrongStyleFor(startSequence.text);
|
||||
if (style === "consistent") {
|
||||
style = markupStyle;
|
||||
}
|
||||
if (style !== markupStyle) {
|
||||
for (var _i = 0, _arr = [startSequence, endSequence]; _i < _arr.length; _i++) {
|
||||
var sequence = _arr[_i];
|
||||
addError(onError, sequence.startLine, "Expected: ".concat(style, "; Actual: ").concat(markupStyle), undefined, [sequence.startColumn, sequence.text.length], {
|
||||
"editColumn": sequence.startColumn,
|
||||
"deleteCount": sequence.text.length,
|
||||
"insertText": style === "asterisk" ? asterisk : underline
|
||||
});
|
||||
}
|
||||
}
|
||||
var instance = (instances.get(expected) || 0) + 1;
|
||||
instances.set(expected, instance);
|
||||
rangeAndFixInfo = getRangeAndFixInfoIfFound(params.lines, lineNumber - 1, actual, expected, instance);
|
||||
}
|
||||
addError(onError, lineNumber, "Expected: ".concat(style, "; Actual: ").concat(markupStyle), undefined, rangeAndFixInfo.range, rangeAndFixInfo.fixInfo);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
_iterator.e(err);
|
||||
} finally {
|
||||
_iterator.f();
|
||||
}
|
||||
};
|
||||
module.exports = [{
|
||||
"names": ["MD049", "emphasis-style"],
|
||||
"description": "Emphasis style should be consistent",
|
||||
"tags": ["emphasis"],
|
||||
"function": function MD049(params, onError) {
|
||||
return impl(params, onError, "em", "*", "_", params.config.style || undefined);
|
||||
return impl(params, onError, "emphasis", "*", "_", params.config.style || undefined);
|
||||
}
|
||||
}, {
|
||||
"names": ["MD050", "strong-style"],
|
||||
|
|
|
|||
|
|
@ -1080,66 +1080,6 @@ function applyFixes(input, errors) {
|
|||
}
|
||||
module.exports.applyFixes = applyFixes;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {number} [instance] Instance on the line (1-based).
|
||||
* @returns {Object} Range and fixInfo wrapper.
|
||||
*/
|
||||
module.exports.getRangeAndFixInfoIfFound =
|
||||
(lines, lineIndex, search, replace, instance = 1) => {
|
||||
let range = null;
|
||||
let fixInfo = null;
|
||||
let searchIndex = -1;
|
||||
while (instance > 0) {
|
||||
searchIndex = lines[lineIndex].indexOf(search, searchIndex + 1);
|
||||
instance--;
|
||||
}
|
||||
if (searchIndex !== -1) {
|
||||
const column = searchIndex + 1;
|
||||
const length = search.length;
|
||||
range = [ column, length ];
|
||||
fixInfo = {
|
||||
"editColumn": column,
|
||||
"deleteCount": length,
|
||||
"insertText": replace
|
||||
};
|
||||
}
|
||||
return {
|
||||
range,
|
||||
fixInfo
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Expands a path with a tilde to an absolute path.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -203,6 +203,17 @@ function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified token iff it is of the desired type.
|
||||
*
|
||||
* @param {Token} token Micromark token candidate.
|
||||
* @param {string} type Desired type.
|
||||
* @returns {Token | null} Token instance.
|
||||
*/
|
||||
function tokenIfType(token, type) {
|
||||
return (token && (token.type === type)) ? token : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
"parse": micromarkParse,
|
||||
filterByPredicate,
|
||||
|
|
@ -210,5 +221,6 @@ module.exports = {
|
|||
getHtmlTagInfo,
|
||||
getMicromarkEvents,
|
||||
getTokenTextByType,
|
||||
matchAndGetTokensByType
|
||||
matchAndGetTokensByType,
|
||||
tokenIfType
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorContext } = require("../helpers");
|
||||
const { filterByTypes } = require("../helpers/micromark.cjs");
|
||||
const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
|
||||
|
||||
const leftSpaceRe = /^\s(?:[^`]|$)/;
|
||||
const rightSpaceRe = /[^`]\s$/;
|
||||
|
|
@ -17,7 +17,6 @@ const trimCodeText = (text, start, end) => {
|
|||
}
|
||||
return text;
|
||||
};
|
||||
const tokenIfType = (token, type) => token && (token.type === type) && token;
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD038", "no-space-in-code" ],
|
||||
|
|
|
|||
|
|
@ -2,54 +2,41 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addError, emphasisOrStrongStyleFor, forEachInlineChild,
|
||||
getNextChildToken, getRangeAndFixInfoIfFound } = require("../helpers");
|
||||
const { addError, emphasisOrStrongStyleFor } = require("../helpers");
|
||||
const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
|
||||
|
||||
const impl =
|
||||
(params, onError, tagPrefix, asterisk, underline, style = "consistent") => {
|
||||
let lastLineNumber = -1;
|
||||
const instances = new Map();
|
||||
forEachInlineChild(params, `${tagPrefix}_open`, (token, parent) => {
|
||||
const { lineNumber, markup } = token;
|
||||
const markupStyle = emphasisOrStrongStyleFor(markup);
|
||||
if (style === "consistent") {
|
||||
style = markupStyle;
|
||||
}
|
||||
if (style !== markupStyle) {
|
||||
let rangeAndFixInfo = {};
|
||||
const contentToken = getNextChildToken(
|
||||
parent, token, "text", `${tagPrefix}_close`
|
||||
);
|
||||
if (contentToken) {
|
||||
const { content } = contentToken;
|
||||
const actual = `${markup}${content}${markup}`;
|
||||
const expectedMarkup =
|
||||
(style === "asterisk") ? asterisk : underline;
|
||||
const expected = `${expectedMarkup}${content}${expectedMarkup}`;
|
||||
if (lastLineNumber !== lineNumber) {
|
||||
lastLineNumber = lineNumber;
|
||||
instances.clear();
|
||||
}
|
||||
const instance = (instances.get(expected) || 0) + 1;
|
||||
instances.set(expected, instance);
|
||||
rangeAndFixInfo = getRangeAndFixInfoIfFound(
|
||||
params.lines,
|
||||
lineNumber - 1,
|
||||
actual,
|
||||
expected,
|
||||
instance
|
||||
);
|
||||
(params, onError, type, asterisk, underline, style = "consistent") => {
|
||||
const emphasisTokens =
|
||||
filterByTypes(params.parsers.micromark.tokens, [ type ]);
|
||||
for (const token of emphasisTokens) {
|
||||
const { children } = token;
|
||||
const childType = `${type}Sequence`;
|
||||
const startSequence = tokenIfType(children[0], childType);
|
||||
const endSequence = tokenIfType(children[children.length - 1], childType);
|
||||
if (startSequence && endSequence) {
|
||||
const markupStyle = emphasisOrStrongStyleFor(startSequence.text);
|
||||
if (style === "consistent") {
|
||||
style = markupStyle;
|
||||
}
|
||||
if (style !== markupStyle) {
|
||||
for (const sequence of [ startSequence, endSequence ]) {
|
||||
addError(
|
||||
onError,
|
||||
sequence.startLine,
|
||||
`Expected: ${style}; Actual: ${markupStyle}`,
|
||||
undefined,
|
||||
[ sequence.startColumn, sequence.text.length ],
|
||||
{
|
||||
"editColumn": sequence.startColumn,
|
||||
"deleteCount": sequence.text.length,
|
||||
"insertText": (style === "asterisk") ? asterisk : underline
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
addError(
|
||||
onError,
|
||||
lineNumber,
|
||||
`Expected: ${style}; Actual: ${markupStyle}`,
|
||||
undefined,
|
||||
rangeAndFixInfo.range,
|
||||
rangeAndFixInfo.fixInfo
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
|
|
@ -61,7 +48,7 @@ module.exports = [
|
|||
return impl(
|
||||
params,
|
||||
onError,
|
||||
"em",
|
||||
"emphasis",
|
||||
"*",
|
||||
"_",
|
||||
params.config.style || undefined
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ Mixed __strong emphasis__ on **this** line __with__ multiple **issues** {MD050}
|
|||
|
||||
Inconsistent
|
||||
emphasis _text {MD049}
|
||||
spanning_ many
|
||||
spanning_ many {MD049}
|
||||
lines
|
||||
|
||||
Inconsistent
|
||||
strong **emphasis {MD050}
|
||||
spanning** many
|
||||
spanning** many {MD050}
|
||||
lines
|
||||
|
||||
Inconsistent _double_ text _interleaved_ text _double_ _interleaved_ emphasis. {MD049}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -68,8 +68,8 @@ text text
|
|||
* Item item item
|
||||
item * emphasis * item {MD037}
|
||||
|
||||
Text _ emphasis {MD037}
|
||||
emphasis _ text {MD037}
|
||||
Text * emphasis {MD037}
|
||||
emphasis * text {MD037}
|
||||
|
||||
Text ** bold {MD037}
|
||||
bold ** text {MD037}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue