Reimplement MD037/no-space-in-emphasis using micromark tokens, report start/end separately for smaller edit spans, remove markdown-it-texmath (fixes #533, fixes #597).

This commit is contained in:
David Anson 2023-07-24 21:36:55 -07:00
parent e86fb7699d
commit 73b9704159
20 changed files with 1784 additions and 2502 deletions

View file

@ -60,7 +60,11 @@
micromarkHtml.mathHtml() micromarkHtml.mathHtml()
] ]
}; };
return micromarkHtml.compile(compileOptions)(events); try {
return micromarkHtml.compile(compileOptions)(events);
} catch (error) {
return `[Exception: "${error}"]`;
}
} }
return `[Unsupported renderer "${renderer}"]`; return `[Unsupported renderer "${renderer}"]`;
} }

View file

@ -47,9 +47,6 @@ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" =
var micromark = __webpack_require__(/*! ./micromark.cjs */ "../helpers/micromark.cjs"); var micromark = __webpack_require__(/*! ./micromark.cjs */ "../helpers/micromark.cjs");
var _require = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js"), var _require = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js"),
newLineRe = _require.newLineRe; newLineRe = _require.newLineRe;
// Regular expression for matching common newline characters
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
module.exports.newLineRe = newLineRe; module.exports.newLineRe = newLineRe;
// Regular expression for matching common front matter (YAML and TOML) // Regular expression for matching common front matter (YAML and TOML)
@ -71,9 +68,6 @@ module.exports.htmlElementRe = htmlElementRe;
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/; module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/; module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
// Regular expression for all instances of emphasis markers
var emphasisMarkersRe = /[_*]/g;
// Regular expression for blockquote prefixes // Regular expression for blockquote prefixes
var blockquotePrefixRe = /^[>\s]*/; var blockquotePrefixRe = /^[>\s]*/;
module.exports.blockquotePrefixRe = blockquotePrefixRe; module.exports.blockquotePrefixRe = blockquotePrefixRe;
@ -383,21 +377,10 @@ function filterTokens(params, type, handler) {
} }
module.exports.filterTokens = filterTokens; module.exports.filterTokens = filterTokens;
/**
* Returns whether a token is a math block (created by markdown-it-texmath).
*
* @param {Object} token MarkdownItToken instance.
* @returns {boolean} True iff token is a math block.
*/
function isMathBlock(token) {
return (token.tag === "$$" || token.tag === "math") && token.type.startsWith("math_block") && !token.type.endsWith("_end");
}
module.exports.isMathBlock = isMathBlock;
// Get line metadata array // Get line metadata array
module.exports.getLineMetadata = function getLineMetadata(params) { module.exports.getLineMetadata = function getLineMetadata(params) {
var lineMetadata = params.lines.map(function (line, index) { var lineMetadata = params.lines.map(function (line, index) {
return [line, index, false, 0, false, false, false, false]; return [line, index, false, 0, false, false, false];
}); });
filterTokens(params, "fence", function (token) { filterTokens(params, "fence", function (token) {
lineMetadata[token.map[0]][3] = 1; lineMetadata[token.map[0]][3] = 1;
@ -426,20 +409,6 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
filterTokens(params, "hr", function (token) { filterTokens(params, "hr", function (token) {
lineMetadata[token.map[0]][6] = true; lineMetadata[token.map[0]][6] = true;
}); });
var _iterator2 = _createForOfIteratorHelper(params.parsers.markdownit.tokens.filter(isMathBlock)),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var token = _step2.value;
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][7] = true;
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
return lineMetadata; return lineMetadata;
}; };
@ -448,21 +417,21 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
* *
* @param {Object} lineMetadata Line metadata object. * @param {Object} lineMetadata Line metadata object.
* @param {Function} handler Function taking (line, lineIndex, inCode, onFence, * @param {Function} handler Function taking (line, lineIndex, inCode, onFence,
* inTable, inItem, inBreak, inMath). * inTable, inItem, inBreak).
* @returns {void} * @returns {void}
*/ */
function forEachLine(lineMetadata, handler) { function forEachLine(lineMetadata, handler) {
var _iterator3 = _createForOfIteratorHelper(lineMetadata), var _iterator2 = _createForOfIteratorHelper(lineMetadata),
_step3; _step2;
try { try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var metadata = _step3.value; var metadata = _step2.value;
handler.apply(void 0, _toConsumableArray(metadata)); handler.apply(void 0, _toConsumableArray(metadata));
} }
} catch (err) { } catch (err) {
_iterator3.e(err); _iterator2.e(err);
} finally { } finally {
_iterator3.f(); _iterator2.f();
} }
} }
module.exports.forEachLine = forEachLine; module.exports.forEachLine = forEachLine;
@ -477,11 +446,11 @@ module.exports.flattenLists = function flattenLists(tokens) {
var lastWithMap = { var lastWithMap = {
"map": [0, 1] "map": [0, 1]
}; };
var _iterator4 = _createForOfIteratorHelper(tokens), var _iterator3 = _createForOfIteratorHelper(tokens),
_step4; _step3;
try { try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var token = _step4.value; var token = _step3.value;
if (token.type === "bullet_list_open" || token.type === "ordered_list_open") { if (token.type === "bullet_list_open" || token.type === "ordered_list_open") {
// Save current context and start a new one // Save current context and start a new one
stack.push(current); stack.push(current);
@ -519,9 +488,9 @@ module.exports.flattenLists = function flattenLists(tokens) {
} }
} }
} catch (err) { } catch (err) {
_iterator4.e(err); _iterator3.e(err);
} finally { } finally {
_iterator4.f(); _iterator3.f();
} }
return flattenedLists; return flattenedLists;
}; };
@ -536,19 +505,19 @@ module.exports.flattenLists = function flattenLists(tokens) {
*/ */
function forEachInlineChild(params, type, handler) { function forEachInlineChild(params, type, handler) {
filterTokens(params, "inline", function (token) { filterTokens(params, "inline", function (token) {
var _iterator5 = _createForOfIteratorHelper(token.children.filter(function (c) { var _iterator4 = _createForOfIteratorHelper(token.children.filter(function (c) {
return c.type === type; return c.type === type;
})), })),
_step5; _step4;
try { try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var child = _step5.value; var child = _step4.value;
handler(child, token); handler(child, token);
} }
} catch (err) { } catch (err) {
_iterator5.e(err); _iterator4.e(err);
} finally { } finally {
_iterator5.f(); _iterator4.f();
} }
}); });
} }
@ -557,11 +526,11 @@ module.exports.forEachInlineChild = forEachInlineChild;
// Calls the provided function for each heading's content // Calls the provided function for each heading's content
module.exports.forEachHeading = function forEachHeading(params, handler) { module.exports.forEachHeading = function forEachHeading(params, handler) {
var heading = null; var heading = null;
var _iterator6 = _createForOfIteratorHelper(params.parsers.markdownit.tokens), var _iterator5 = _createForOfIteratorHelper(params.parsers.markdownit.tokens),
_step6; _step5;
try { try {
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
var token = _step6.value; var token = _step5.value;
if (token.type === "heading_open") { if (token.type === "heading_open") {
heading = token; heading = token;
} else if (token.type === "heading_close") { } else if (token.type === "heading_close") {
@ -571,9 +540,9 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
} }
} }
} catch (err) { } catch (err) {
_iterator6.e(err); _iterator5.e(err);
} finally { } finally {
_iterator6.f(); _iterator5.f();
} }
}; };
@ -708,19 +677,19 @@ module.exports.codeBlockAndSpanRanges = function (params, lineMetadata) {
var tokenLines = params.lines.slice(token.map[0], token.map[1]); var tokenLines = params.lines.slice(token.map[0], token.map[1]);
forEachInlineCodeSpan(tokenLines.join("\n"), function (code, lineIndex, columnIndex) { forEachInlineCodeSpan(tokenLines.join("\n"), function (code, lineIndex, columnIndex) {
var codeLines = code.split(newLineRe); var codeLines = code.split(newLineRe);
var _iterator7 = _createForOfIteratorHelper(codeLines.entries()), var _iterator6 = _createForOfIteratorHelper(codeLines.entries()),
_step7; _step6;
try { try {
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
var _step7$value = _slicedToArray(_step7.value, 2), var _step6$value = _slicedToArray(_step6.value, 2),
i = _step7$value[0], i = _step6$value[0],
line = _step7$value[1]; line = _step6$value[1];
exclusions.push([token.lineNumber - 1 + lineIndex + i, i ? 0 : columnIndex, line.length]); exclusions.push([token.lineNumber - 1 + lineIndex + i, i ? 0 : columnIndex, line.length]);
} }
} catch (err) { } catch (err) {
_iterator7.e(err); _iterator6.e(err);
} finally { } finally {
_iterator7.f(); _iterator6.f();
} }
}); });
} }
@ -728,51 +697,6 @@ module.exports.codeBlockAndSpanRanges = function (params, lineMetadata) {
return exclusions; return exclusions;
}; };
/**
* Returns an array of HTML element ranges.
*
* @param {Object} params RuleParams instance.
* @param {Object} lineMetadata Line metadata object.
* @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
*/
module.exports.htmlElementRanges = function (params, lineMetadata) {
var exclusions = [];
// Match with htmlElementRe
forEachLine(lineMetadata, function (line, lineIndex, inCode) {
var match = null;
// eslint-disable-next-line no-unmodified-loop-condition
while (!inCode && (match = htmlElementRe.exec(line)) !== null) {
exclusions.push([lineIndex, match.index, match[0].length]);
}
});
// Match with html_inline
forEachInlineChild(params, "html_inline", function (token, parent) {
var parentContent = parent.content;
var tokenContent = token.content;
var parentIndex = parentContent.indexOf(tokenContent);
var deltaLines = 0;
var indent = 0;
for (var i = parentIndex - 1; i >= 0; i--) {
if (parentContent[i] === "\n") {
deltaLines++;
} else if (deltaLines === 0) {
indent++;
}
}
var lineIndex = token.lineNumber - 1 + deltaLines;
do {
var index = tokenContent.indexOf("\n");
var length = index === -1 ? tokenContent.length : index;
exclusions.push([lineIndex, indent, length]);
tokenContent = tokenContent.slice(length + 1);
lineIndex++;
indent = 0;
} while (tokenContent.length > 0);
});
// Return results
return exclusions;
};
/** /**
* Determines whether the specified range is within another range. * Determines whether the specified range is within another range.
* *
@ -810,155 +734,6 @@ module.exports.frontMatterHasTitle = function frontMatterHasTitle(frontMatterLin
}); });
}; };
/**
* Calls the provided function for each link.
*
* @param {string} line Line of Markdown input.
* @param {Function} handler Function taking (index, link, text, destination).
* @returns {void}
*/
function forEachLink(line, handler) {
// Helper to find matching close symbol for link text/destination
var findClosingSymbol = function findClosingSymbol(index) {
var begin = line[index];
var end = begin === "[" ? "]" : ")";
var nesting = 0;
var escaping = false;
var pointy = false;
for (var i = index + 1; i < line.length; i++) {
var current = line[i];
if (current === "\\") {
escaping = !escaping;
} else if (!escaping && current === begin) {
nesting++;
} else if (!escaping && current === end) {
if (nesting > 0) {
nesting--;
} else if (!pointy) {
// Return index after matching close symbol
return i + 1;
}
} else if (i === index + 1 && begin === "(" && current === "<") {
pointy = true;
} else if (!escaping && pointy && current === ">") {
pointy = false;
nesting = 0;
} else {
escaping = false;
}
}
// No match found
return -1;
};
// Scan line for unescaped "[" character
var escaping = false;
for (var i = 0; i < line.length; i++) {
var current = line[i];
if (current === "\\") {
escaping = !escaping;
} else if (!escaping && current === "[") {
// Scan for matching close "]" of link text
var textEnd = findClosingSymbol(i);
if (textEnd !== -1) {
if (line[textEnd] === "(" || line[textEnd] === "[") {
// Scan for matching close ")" or "]" of link destination
var destEnd = findClosingSymbol(textEnd);
if (destEnd !== -1) {
// Call handler with link text and destination
var link = line.slice(i, destEnd);
var text = line.slice(i, textEnd);
var dest = line.slice(textEnd, destEnd);
handler(i, link, text, dest);
i = destEnd;
}
}
if (i < textEnd) {
// Call handler with link text only
var _text = line.slice(i, textEnd);
handler(i, _text, _text);
i = textEnd;
}
}
} else {
escaping = false;
}
}
}
module.exports.forEachLink = forEachLink;
/**
* Returns a list of emphasis markers in code spans and links.
*
* @param {Object} params RuleParams instance.
* @returns {number[][]} List of markers.
*/
function emphasisMarkersInContent(params) {
var lines = params.lines;
var byLine = new Array(lines.length);
// Search links
var _iterator8 = _createForOfIteratorHelper(lines.entries()),
_step8;
try {
var _loop = function _loop() {
var _step8$value = _slicedToArray(_step8.value, 2),
tokenLineIndex = _step8$value[0],
tokenLine = _step8$value[1];
var inLine = [];
forEachLink(tokenLine, function (index, match) {
var markerMatch = null;
while (markerMatch = emphasisMarkersRe.exec(match)) {
inLine.push(index + markerMatch.index);
}
});
byLine[tokenLineIndex] = inLine;
};
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
_loop();
}
// Search code spans
} catch (err) {
_iterator8.e(err);
} finally {
_iterator8.f();
}
filterTokens(params, "inline", function (token) {
var children = token.children,
lineNumber = token.lineNumber,
map = token.map;
if (children.some(function (child) {
return child.type === "code_inline";
})) {
var tokenLines = lines.slice(map[0], map[1]);
forEachInlineCodeSpan(tokenLines.join("\n"), function (code, lineIndex, column, tickCount) {
var codeLines = code.split(newLineRe);
var _iterator9 = _createForOfIteratorHelper(codeLines.entries()),
_step9;
try {
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
var _step9$value = _slicedToArray(_step9.value, 2),
codeLineIndex = _step9$value[0],
codeLine = _step9$value[1];
var byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
var inLine = byLine[byLineIndex];
var codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
var match = null;
while (match = emphasisMarkersRe.exec(codeLine)) {
inLine.push(codeLineOffset + match.index);
}
byLine[byLineIndex] = inLine;
}
} catch (err) {
_iterator9.e(err);
} finally {
_iterator9.f();
}
});
}
});
return byLine;
}
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
/** /**
* Returns an object with information about reference links and images. * Returns an object with information about reference links and images.
* *
@ -981,11 +756,11 @@ function getReferenceLinkImageData(params) {
"definitionLabelString", "gfmFootnoteDefinitionLabelString", "definitionLabelString", "gfmFootnoteDefinitionLabelString",
// references and shortcuts // references and shortcuts
"gfmFootnoteCall", "image", "link"]); "gfmFootnoteCall", "image", "link"]);
var _iterator10 = _createForOfIteratorHelper(filteredTokens), var _iterator7 = _createForOfIteratorHelper(filteredTokens),
_step10; _step7;
try { try {
for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) { for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
var token = _step10.value; var token = _step7.value;
var labelPrefix = ""; var labelPrefix = "";
// eslint-disable-next-line default-case // eslint-disable-next-line default-case
switch (token.type) { switch (token.type) {
@ -1052,9 +827,9 @@ function getReferenceLinkImageData(params) {
} }
} }
} catch (err) { } catch (err) {
_iterator10.e(err); _iterator7.e(err);
} finally { } finally {
_iterator10.f(); _iterator7.f();
} }
return { return {
references: references, references: references,
@ -1078,11 +853,11 @@ function getPreferredLineEnding(input, os) {
var lf = 0; var lf = 0;
var crlf = 0; var crlf = 0;
var endings = input.match(newLineRe) || []; var endings = input.match(newLineRe) || [];
var _iterator11 = _createForOfIteratorHelper(endings), var _iterator8 = _createForOfIteratorHelper(endings),
_step11; _step8;
try { try {
for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) { for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
var ending = _step11.value; var ending = _step8.value;
// eslint-disable-next-line default-case // eslint-disable-next-line default-case
switch (ending) { switch (ending) {
case "\r": case "\r":
@ -1097,9 +872,9 @@ function getPreferredLineEnding(input, os) {
} }
} }
} catch (err) { } catch (err) {
_iterator11.e(err); _iterator8.e(err);
} finally { } finally {
_iterator11.f(); _iterator8.f();
} }
var preferredLineEnding = null; var preferredLineEnding = null;
if (!cr && !lf && !crlf) { if (!cr && !lf && !crlf) {
@ -1182,11 +957,11 @@ function applyFixes(input, errors) {
lastFixInfo = { lastFixInfo = {
"lineNumber": -1 "lineNumber": -1
}; };
var _iterator12 = _createForOfIteratorHelper(fixInfos), var _iterator9 = _createForOfIteratorHelper(fixInfos),
_step12; _step9;
try { try {
for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) { for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
var fixInfo = _step12.value; var fixInfo = _step9.value;
if (fixInfo.lineNumber === lastFixInfo.lineNumber && fixInfo.editColumn === lastFixInfo.editColumn && !fixInfo.insertText && fixInfo.deleteCount > 0 && lastFixInfo.insertText && !lastFixInfo.deleteCount) { if (fixInfo.lineNumber === lastFixInfo.lineNumber && fixInfo.editColumn === lastFixInfo.editColumn && !fixInfo.insertText && fixInfo.deleteCount > 0 && lastFixInfo.insertText && !lastFixInfo.deleteCount) {
fixInfo.insertText = lastFixInfo.insertText; fixInfo.insertText = lastFixInfo.insertText;
lastFixInfo.lineNumber = 0; lastFixInfo.lineNumber = 0;
@ -1194,9 +969,9 @@ function applyFixes(input, errors) {
lastFixInfo = fixInfo; lastFixInfo = fixInfo;
} }
} catch (err) { } catch (err) {
_iterator12.e(err); _iterator9.e(err);
} finally { } finally {
_iterator12.f(); _iterator9.f();
} }
fixInfos = fixInfos.filter(function (fixInfo) { fixInfos = fixInfos.filter(function (fixInfo) {
return fixInfo.lineNumber; return fixInfo.lineNumber;
@ -1204,11 +979,11 @@ function applyFixes(input, errors) {
// Apply all (remaining/updated) fixes // Apply all (remaining/updated) fixes
var lastLineIndex = -1; var lastLineIndex = -1;
var lastEditIndex = -1; var lastEditIndex = -1;
var _iterator13 = _createForOfIteratorHelper(fixInfos), var _iterator10 = _createForOfIteratorHelper(fixInfos),
_step13; _step10;
try { try {
for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) { for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
var _fixInfo = _step13.value; var _fixInfo = _step10.value;
var lineNumber = _fixInfo.lineNumber, var lineNumber = _fixInfo.lineNumber,
editColumn = _fixInfo.editColumn, editColumn = _fixInfo.editColumn,
deleteCount = _fixInfo.deleteCount; deleteCount = _fixInfo.deleteCount;
@ -1223,9 +998,9 @@ function applyFixes(input, errors) {
} }
// Return corrected input // Return corrected input
} catch (err) { } catch (err) {
_iterator13.e(err); _iterator10.e(err);
} finally { } finally {
_iterator13.f(); _iterator10.f();
} }
return lines.filter(function (line) { return lines.filter(function (line) {
return line !== null; return line !== null;
@ -1710,9 +1485,6 @@ module.exports.codeBlockAndSpanRanges = function () {
module.exports.flattenedLists = function () { module.exports.flattenedLists = function () {
return map.get("flattenedLists"); return map.get("flattenedLists");
}; };
module.exports.htmlElementRanges = function () {
return map.get("htmlElementRanges");
};
module.exports.lineMetadata = function () { module.exports.lineMetadata = function () {
return map.get("lineMetadata"); return map.get("lineMetadata");
}; };
@ -2477,12 +2249,10 @@ function lintContent(ruleList, aliasToRuleNames, name, content, md, config, conf
var lineMetadata = helpers.getLineMetadata(paramsBase); var lineMetadata = helpers.getLineMetadata(paramsBase);
var codeBlockAndSpanRanges = helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata); var codeBlockAndSpanRanges = helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
var flattenedLists = helpers.flattenLists(paramsBase.parsers.markdownit.tokens); var flattenedLists = helpers.flattenLists(paramsBase.parsers.markdownit.tokens);
var htmlElementRanges = helpers.htmlElementRanges(paramsBase, lineMetadata);
var referenceLinkImageData = helpers.getReferenceLinkImageData(paramsBase); var referenceLinkImageData = helpers.getReferenceLinkImageData(paramsBase);
cache.set({ cache.set({
codeBlockAndSpanRanges: codeBlockAndSpanRanges, codeBlockAndSpanRanges: codeBlockAndSpanRanges,
flattenedLists: flattenedLists, flattenedLists: flattenedLists,
htmlElementRanges: htmlElementRanges,
lineMetadata: lineMetadata, lineMetadata: lineMetadata,
referenceLinkImageData: referenceLinkImageData referenceLinkImageData: referenceLinkImageData
}); });
@ -5346,6 +5116,11 @@ module.exports = {
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
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(_e2) { throw _e2; }, 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(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
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 _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); }
@ -5353,154 +5128,113 @@ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symb
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
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; } 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"), var _require = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"),
addErrorContext = _require.addErrorContext, addError = _require.addError;
emphasisMarkersInContent = _require.emphasisMarkersInContent, var emphasisStartTextRe = /^(\S{1,3})(\s+)\S/;
forEachLine = _require.forEachLine, var emphasisEndTextRe = /\S(\s+)(\S{1,3})$/;
isBlankLine = _require.isBlankLine,
withinAnyRange = _require.withinAnyRange;
var _require2 = __webpack_require__(/*! ./cache */ "../lib/cache.js"),
htmlElementRanges = _require2.htmlElementRanges,
lineMetadata = _require2.lineMetadata;
var emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g;
var embeddedUnderscoreRe = /([A-Za-z\d])(_([A-Za-z\d]))+/g;
var asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
var leftSpaceRe = /^\s+/;
var rightSpaceRe = /\s+$/;
var tablePipeRe = /\|/;
var allUnderscoresRe = /_/g;
module.exports = { module.exports = {
"names": ["MD037", "no-space-in-emphasis"], "names": ["MD037", "no-space-in-emphasis"],
"description": "Spaces inside emphasis markers", "description": "Spaces inside emphasis markers",
"tags": ["whitespace", "emphasis"], "tags": ["whitespace", "emphasis"],
"function": function MD037(params, onError) { "function": function MD037(params, onError) {
var exclusions = htmlElementRanges(); // Initialize variables
// eslint-disable-next-line init-declarations var lines = params.lines,
var effectiveEmphasisLength, parsers = params.parsers;
emphasisIndex, var emphasisTokensByMarker = new Map();
emphasisKind, for (var _i = 0, _arr = ["_", "__", "___", "*", "**", "***"]; _i < _arr.length; _i++) {
emphasisLength, var marker = _arr[_i];
pendingError = null; emphasisTokensByMarker.set(marker, []);
// eslint-disable-next-line jsdoc/require-jsdoc
function resetRunTracking() {
emphasisIndex = -1;
emphasisLength = 0;
emphasisKind = "";
effectiveEmphasisLength = 0;
pendingError = null;
} }
// eslint-disable-next-line jsdoc/require-jsdoc var pending = _toConsumableArray(parsers.micromark.tokens);
function handleRunEnd(line, lineIndex, contextLength, match, matchIndex, inTable) { var token = null;
// Close current run while (token = pending.shift()) {
var content = line.substring(emphasisIndex, matchIndex); // Use reparsed children of htmlFlow tokens
if (!emphasisLength) { if (token.type === "htmlFlow") {
content = content.trimStart(); pending.unshift.apply(pending, _toConsumableArray(token.htmlFlowChildren));
continue;
} }
if (!match) { pending.push.apply(pending, _toConsumableArray(token.children));
content = content.trimEnd();
} // Build lists of bare tokens for each emphasis marker type
var leftSpace = leftSpaceRe.test(content); var _iterator = _createForOfIteratorHelper(emphasisTokensByMarker.values()),
var rightSpace = rightSpaceRe.test(content); _step;
if ((leftSpace || rightSpace) && (!inTable || !tablePipeRe.test(content))) { try {
// Report the violation for (_iterator.s(); !(_step = _iterator.n()).done;) {
var contextStart = emphasisIndex - emphasisLength; var emphasisTokens = _step.value;
var contextEnd = matchIndex + contextLength; emphasisTokens.length = 0;
var column = contextStart + 1;
var length = contextEnd - contextStart;
if (!withinAnyRange(exclusions, lineIndex, column, length)) {
var context = line.substring(contextStart, contextEnd);
var leftMarker = line.substring(contextStart, emphasisIndex);
var rightMarker = match ? match[2] || match[3] : "";
var fixedText = "".concat(leftMarker).concat(content.trim()).concat(rightMarker);
return [onError, lineIndex + 1, context, leftSpace, rightSpace, [column, length], {
"editColumn": column,
"deleteCount": length,
"insertText": fixedText
}];
} }
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var _iterator2 = _createForOfIteratorHelper(token.children),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var child = _step2.value;
var text = child.text,
type = child.type;
if (type === "data" && text.length <= 3) {
var _emphasisTokens = emphasisTokensByMarker.get(text);
if (_emphasisTokens) {
_emphasisTokens.push(child);
}
}
}
// Process bare tokens for each emphasis marker type
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var _iterator3 = _createForOfIteratorHelper(emphasisTokensByMarker.values()),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var _emphasisTokens2 = _step3.value;
for (var i = 0; i + 1 < _emphasisTokens2.length; i += 2) {
// Process start token of start/end pair
var startToken = _emphasisTokens2[i];
var startText = lines[startToken.startLine - 1].slice(startToken.startColumn - 1);
var startMatch = startText.match(emphasisStartTextRe);
if (startMatch) {
var _startMatch = _slicedToArray(startMatch, 3),
startContext = _startMatch[0],
startMarker = _startMatch[1],
startSpaces = _startMatch[2];
if (startMarker === startToken.text && startSpaces.length > 0) {
addError(onError, startToken.startLine, undefined, startContext, [startToken.startColumn, startContext.length], {
"editColumn": startToken.endColumn,
"deleteCount": startSpaces.length
});
}
}
// Process end token of start/end pair
var endToken = _emphasisTokens2[i + 1];
var endText = lines[endToken.startLine - 1].slice(0, endToken.endColumn - 1);
var endMatch = endText.match(emphasisEndTextRe);
if (endMatch) {
var _endMatch = _slicedToArray(endMatch, 3),
endContext = _endMatch[0],
endSpace = _endMatch[1],
endMarker = _endMatch[2];
if (endMarker === endToken.text && endSpace.length > 0) {
addError(onError, endToken.startLine, undefined, endContext, [endToken.endColumn - endContext.length, endContext.length], {
"editColumn": endToken.startColumn - endSpace.length,
"deleteCount": endSpace.length
});
}
}
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
} }
return null;
} }
// Initialize
var ignoreMarkersByLine = emphasisMarkersInContent(params);
resetRunTracking();
forEachLine(lineMetadata(), function (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) {
var onItemStart = inItem === 1;
if (inCode || onFence || inTable || onBreak || onItemStart || isBlankLine(line)) {
// Emphasis resets when leaving a block
resetRunTracking();
}
if (inCode || onFence || onBreak || inMath) {
// Emphasis has no meaning here
return;
}
var patchedLine = line.replace(embeddedUnderscoreRe, function (match) {
return match.replace(allUnderscoresRe, " ");
});
if (onItemStart) {
// Trim overlapping '*' list item marker
patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
}
var match = null;
// Match all emphasis-looking runs in the line...
while (match = emphasisRe.exec(patchedLine)) {
var ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
var matchIndex = match.index + match[1].length;
if (ignoreMarkersForLine.includes(matchIndex)) {
// Ignore emphasis markers inside code spans and links
continue;
}
var matchLength = match[0].length - match[1].length;
var matchKind = (match[2] || match[3])[0];
if (emphasisIndex === -1) {
// New run
emphasisIndex = matchIndex + matchLength;
emphasisLength = matchLength;
emphasisKind = matchKind;
effectiveEmphasisLength = matchLength;
} else if (matchKind === emphasisKind) {
// Matching emphasis markers
if (matchLength === effectiveEmphasisLength) {
// Ending an existing run, report any pending error
if (pendingError) {
// @ts-ignore
addErrorContext.apply(void 0, _toConsumableArray(pendingError));
pendingError = null;
}
var error = handleRunEnd(line, lineIndex, effectiveEmphasisLength, match, matchIndex, inTable);
if (error) {
// @ts-ignore
addErrorContext.apply(void 0, _toConsumableArray(error));
}
// Reset
resetRunTracking();
} else if (matchLength === 3) {
// Swap internal run length (1->2 or 2->1)
effectiveEmphasisLength = matchLength - effectiveEmphasisLength;
} else if (effectiveEmphasisLength === 3) {
// Downgrade internal run (3->1 or 3->2)
effectiveEmphasisLength -= matchLength;
} else {
// Upgrade to internal run (1->3 or 2->3)
effectiveEmphasisLength += matchLength;
}
// Back up one character so RegExp has a chance to match the
// next marker (ex: "**star**_underscore_")
if (emphasisRe.lastIndex > 1) {
emphasisRe.lastIndex--;
}
} else if (emphasisRe.lastIndex > 1) {
// Back up one character so RegExp has a chance to match the
// mis-matched marker (ex: "*text_*")
emphasisRe.lastIndex--;
}
}
if (emphasisIndex !== -1) {
pendingError = pendingError || handleRunEnd(line, lineIndex, 0, null, line.length, inTable);
// Adjust for pending run on new line
emphasisIndex = 0;
emphasisLength = 0;
}
});
} }
}; };

View file

@ -3,10 +3,8 @@
"use strict"; "use strict";
const micromark = require("./micromark.cjs"); const micromark = require("./micromark.cjs");
const { newLineRe } = require("./shared.js");
// Regular expression for matching common newline characters const { newLineRe } = require("./shared.js");
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
module.exports.newLineRe = newLineRe; module.exports.newLineRe = newLineRe;
// Regular expression for matching common front matter (YAML and TOML) // Regular expression for matching common front matter (YAML and TOML)
@ -28,9 +26,6 @@ module.exports.htmlElementRe = htmlElementRe;
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/; module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/; module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
// Regular expression for all instances of emphasis markers
const emphasisMarkersRe = /[_*]/g;
// Regular expression for blockquote prefixes // Regular expression for blockquote prefixes
const blockquotePrefixRe = /^[>\s]*/; const blockquotePrefixRe = /^[>\s]*/;
module.exports.blockquotePrefixRe = blockquotePrefixRe; module.exports.blockquotePrefixRe = blockquotePrefixRe;
@ -348,25 +343,10 @@ function filterTokens(params, type, handler) {
} }
module.exports.filterTokens = filterTokens; module.exports.filterTokens = filterTokens;
/**
* Returns whether a token is a math block (created by markdown-it-texmath).
*
* @param {Object} token MarkdownItToken instance.
* @returns {boolean} True iff token is a math block.
*/
function isMathBlock(token) {
return (
((token.tag === "$$") || (token.tag === "math")) &&
token.type.startsWith("math_block") &&
!token.type.endsWith("_end")
);
}
module.exports.isMathBlock = isMathBlock;
// Get line metadata array // Get line metadata array
module.exports.getLineMetadata = function getLineMetadata(params) { module.exports.getLineMetadata = function getLineMetadata(params) {
const lineMetadata = params.lines.map( const lineMetadata = params.lines.map(
(line, index) => [ line, index, false, 0, false, false, false, false ] (line, index) => [ line, index, false, 0, false, false, false ]
); );
filterTokens(params, "fence", (token) => { filterTokens(params, "fence", (token) => {
lineMetadata[token.map[0]][3] = 1; lineMetadata[token.map[0]][3] = 1;
@ -395,11 +375,6 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
filterTokens(params, "hr", (token) => { filterTokens(params, "hr", (token) => {
lineMetadata[token.map[0]][6] = true; lineMetadata[token.map[0]][6] = true;
}); });
for (const token of params.parsers.markdownit.tokens.filter(isMathBlock)) {
for (let i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][7] = true;
}
}
return lineMetadata; return lineMetadata;
}; };
@ -408,7 +383,7 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
* *
* @param {Object} lineMetadata Line metadata object. * @param {Object} lineMetadata Line metadata object.
* @param {Function} handler Function taking (line, lineIndex, inCode, onFence, * @param {Function} handler Function taking (line, lineIndex, inCode, onFence,
* inTable, inItem, inBreak, inMath). * inTable, inItem, inBreak).
* @returns {void} * @returns {void}
*/ */
function forEachLine(lineMetadata, handler) { function forEachLine(lineMetadata, handler) {
@ -655,51 +630,6 @@ module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
return exclusions; return exclusions;
}; };
/**
* Returns an array of HTML element ranges.
*
* @param {Object} params RuleParams instance.
* @param {Object} lineMetadata Line metadata object.
* @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
*/
module.exports.htmlElementRanges = (params, lineMetadata) => {
const exclusions = [];
// Match with htmlElementRe
forEachLine(lineMetadata, (line, lineIndex, inCode) => {
let match = null;
// eslint-disable-next-line no-unmodified-loop-condition
while (!inCode && ((match = htmlElementRe.exec(line)) !== null)) {
exclusions.push([ lineIndex, match.index, match[0].length ]);
}
});
// Match with html_inline
forEachInlineChild(params, "html_inline", (token, parent) => {
const parentContent = parent.content;
let tokenContent = token.content;
const parentIndex = parentContent.indexOf(tokenContent);
let deltaLines = 0;
let indent = 0;
for (let i = parentIndex - 1; i >= 0; i--) {
if (parentContent[i] === "\n") {
deltaLines++;
} else if (deltaLines === 0) {
indent++;
}
}
let lineIndex = token.lineNumber - 1 + deltaLines;
do {
const index = tokenContent.indexOf("\n");
const length = (index === -1) ? tokenContent.length : index;
exclusions.push([ lineIndex, indent, length ]);
tokenContent = tokenContent.slice(length + 1);
lineIndex++;
indent = 0;
} while (tokenContent.length > 0);
});
// Return results
return exclusions;
};
/** /**
* Determines whether the specified range is within another range. * Determines whether the specified range is within another range.
* *
@ -744,129 +674,6 @@ module.exports.frontMatterHasTitle =
frontMatterLines.some((line) => frontMatterTitleRe.test(line)); frontMatterLines.some((line) => frontMatterTitleRe.test(line));
}; };
/**
* Calls the provided function for each link.
*
* @param {string} line Line of Markdown input.
* @param {Function} handler Function taking (index, link, text, destination).
* @returns {void}
*/
function forEachLink(line, handler) {
// Helper to find matching close symbol for link text/destination
const findClosingSymbol = (index) => {
const begin = line[index];
const end = (begin === "[") ? "]" : ")";
let nesting = 0;
let escaping = false;
let pointy = false;
for (let i = index + 1; i < line.length; i++) {
const current = line[i];
if (current === "\\") {
escaping = !escaping;
} else if (!escaping && (current === begin)) {
nesting++;
} else if (!escaping && (current === end)) {
if (nesting > 0) {
nesting--;
} else if (!pointy) {
// Return index after matching close symbol
return i + 1;
}
} else if ((i === index + 1) && (begin === "(") && (current === "<")) {
pointy = true;
} else if (!escaping && pointy && current === ">") {
pointy = false;
nesting = 0;
} else {
escaping = false;
}
}
// No match found
return -1;
};
// Scan line for unescaped "[" character
let escaping = false;
for (let i = 0; i < line.length; i++) {
const current = line[i];
if (current === "\\") {
escaping = !escaping;
} else if (!escaping && (current === "[")) {
// Scan for matching close "]" of link text
const textEnd = findClosingSymbol(i);
if (textEnd !== -1) {
if ((line[textEnd] === "(") || (line[textEnd] === "[")) {
// Scan for matching close ")" or "]" of link destination
const destEnd = findClosingSymbol(textEnd);
if (destEnd !== -1) {
// Call handler with link text and destination
const link = line.slice(i, destEnd);
const text = line.slice(i, textEnd);
const dest = line.slice(textEnd, destEnd);
handler(i, link, text, dest);
i = destEnd;
}
}
if (i < textEnd) {
// Call handler with link text only
const text = line.slice(i, textEnd);
handler(i, text, text);
i = textEnd;
}
}
} else {
escaping = false;
}
}
}
module.exports.forEachLink = forEachLink;
/**
* Returns a list of emphasis markers in code spans and links.
*
* @param {Object} params RuleParams instance.
* @returns {number[][]} List of markers.
*/
function emphasisMarkersInContent(params) {
const { lines } = params;
const byLine = new Array(lines.length);
// Search links
for (const [ tokenLineIndex, tokenLine ] of lines.entries()) {
const inLine = [];
forEachLink(tokenLine, (index, match) => {
let markerMatch = null;
while ((markerMatch = emphasisMarkersRe.exec(match))) {
inLine.push(index + markerMatch.index);
}
});
byLine[tokenLineIndex] = inLine;
}
// Search code spans
filterTokens(params, "inline", (token) => {
const { children, lineNumber, map } = token;
if (children.some((child) => child.type === "code_inline")) {
const tokenLines = lines.slice(map[0], map[1]);
forEachInlineCodeSpan(
tokenLines.join("\n"),
(code, lineIndex, column, tickCount) => {
const codeLines = code.split(newLineRe);
for (const [ codeLineIndex, codeLine ] of codeLines.entries()) {
const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
const inLine = byLine[byLineIndex];
const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
let match = null;
while ((match = emphasisMarkersRe.exec(codeLine))) {
inLine.push(codeLineOffset + match.index);
}
byLine[byLineIndex] = inLine;
}
}
);
}
});
return byLine;
}
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
/** /**
* Returns an object with information about reference links and images. * Returns an object with information about reference links and images.
* *

View file

@ -15,8 +15,6 @@ module.exports.codeBlockAndSpanRanges =
() => map.get("codeBlockAndSpanRanges"); () => map.get("codeBlockAndSpanRanges");
module.exports.flattenedLists = module.exports.flattenedLists =
() => map.get("flattenedLists"); () => map.get("flattenedLists");
module.exports.htmlElementRanges =
() => map.get("htmlElementRanges");
module.exports.lineMetadata = module.exports.lineMetadata =
() => map.get("lineMetadata"); () => map.get("lineMetadata");
module.exports.referenceLinkImageData = module.exports.referenceLinkImageData =

View file

@ -583,14 +583,11 @@ function lintContent(
helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata); helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
const flattenedLists = const flattenedLists =
helpers.flattenLists(paramsBase.parsers.markdownit.tokens); helpers.flattenLists(paramsBase.parsers.markdownit.tokens);
const htmlElementRanges =
helpers.htmlElementRanges(paramsBase, lineMetadata);
const referenceLinkImageData = const referenceLinkImageData =
helpers.getReferenceLinkImageData(paramsBase); helpers.getReferenceLinkImageData(paramsBase);
cache.set({ cache.set({
codeBlockAndSpanRanges, codeBlockAndSpanRanges,
flattenedLists, flattenedLists,
htmlElementRanges,
lineMetadata, lineMetadata,
referenceLinkImageData referenceLinkImageData
}); });

View file

@ -2,184 +2,97 @@
"use strict"; "use strict";
const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine, const { addError } = require("../helpers");
withinAnyRange } = require("../helpers");
const { htmlElementRanges, lineMetadata } = require("./cache");
const emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g; const emphasisStartTextRe = /^(\S{1,3})(\s+)\S/;
const embeddedUnderscoreRe = /([A-Za-z\d])(_([A-Za-z\d]))+/g; const emphasisEndTextRe = /\S(\s+)(\S{1,3})$/;
const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
const leftSpaceRe = /^\s+/;
const rightSpaceRe = /\s+$/;
const tablePipeRe = /\|/;
const allUnderscoresRe = /_/g;
module.exports = { module.exports = {
"names": [ "MD037", "no-space-in-emphasis" ], "names": [ "MD037", "no-space-in-emphasis" ],
"description": "Spaces inside emphasis markers", "description": "Spaces inside emphasis markers",
"tags": [ "whitespace", "emphasis" ], "tags": [ "whitespace", "emphasis" ],
"function": function MD037(params, onError) { "function": function MD037(params, onError) {
const exclusions = htmlElementRanges();
// eslint-disable-next-line init-declarations // Initialize variables
let effectiveEmphasisLength, emphasisIndex, emphasisKind, emphasisLength, const { lines, parsers } = params;
pendingError = null; const emphasisTokensByMarker = new Map();
// eslint-disable-next-line jsdoc/require-jsdoc for (const marker of [ "_", "__", "___", "*", "**", "***" ]) {
function resetRunTracking() { emphasisTokensByMarker.set(marker, []);
emphasisIndex = -1;
emphasisLength = 0;
emphasisKind = "";
effectiveEmphasisLength = 0;
pendingError = null;
} }
// eslint-disable-next-line jsdoc/require-jsdoc const pending = [ ...parsers.micromark.tokens ];
function handleRunEnd( let token = null;
line, lineIndex, contextLength, match, matchIndex, inTable while ((token = pending.shift())) {
) {
// Close current run // Use reparsed children of htmlFlow tokens
let content = line.substring(emphasisIndex, matchIndex); if (token.type === "htmlFlow") {
if (!emphasisLength) { pending.unshift(...token.htmlFlowChildren);
content = content.trimStart(); continue;
} }
if (!match) { pending.push(...token.children);
content = content.trimEnd();
// Build lists of bare tokens for each emphasis marker type
for (const emphasisTokens of emphasisTokensByMarker.values()) {
emphasisTokens.length = 0;
} }
const leftSpace = leftSpaceRe.test(content); for (const child of token.children) {
const rightSpace = rightSpaceRe.test(content); const { text, type } = child;
if ( if ((type === "data") && (text.length <= 3)) {
(leftSpace || rightSpace) && const emphasisTokens = emphasisTokensByMarker.get(text);
(!inTable || !tablePipeRe.test(content)) if (emphasisTokens) {
) { emphasisTokens.push(child);
// Report the violation
const contextStart = emphasisIndex - emphasisLength;
const contextEnd = matchIndex + contextLength;
const column = contextStart + 1;
const length = contextEnd - contextStart;
if (!withinAnyRange(exclusions, lineIndex, column, length)) {
const context = line.substring(contextStart, contextEnd);
const leftMarker = line.substring(contextStart, emphasisIndex);
const rightMarker = match ? (match[2] || match[3]) : "";
const fixedText = `${leftMarker}${content.trim()}${rightMarker}`;
return [
onError,
lineIndex + 1,
context,
leftSpace,
rightSpace,
[ column, length ],
{
"editColumn": column,
"deleteCount": length,
"insertText": fixedText
}
];
}
}
return null;
}
// Initialize
const ignoreMarkersByLine = emphasisMarkersInContent(params);
resetRunTracking();
forEachLine(
lineMetadata(),
(line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
const onItemStart = (inItem === 1);
if (
inCode ||
onFence ||
inTable ||
onBreak ||
onItemStart ||
isBlankLine(line)
) {
// Emphasis resets when leaving a block
resetRunTracking();
}
if (
inCode ||
onFence ||
onBreak ||
inMath
) {
// Emphasis has no meaning here
return;
}
let patchedLine = line.replace(
embeddedUnderscoreRe,
(match) => match.replace(allUnderscoresRe, " ")
);
if (onItemStart) {
// Trim overlapping '*' list item marker
patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
}
let match = null;
// Match all emphasis-looking runs in the line...
while ((match = emphasisRe.exec(patchedLine))) {
const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
const matchIndex = match.index + match[1].length;
if (ignoreMarkersForLine.includes(matchIndex)) {
// Ignore emphasis markers inside code spans and links
continue;
} }
const matchLength = match[0].length - match[1].length; }
const matchKind = (match[2] || match[3])[0]; }
if (emphasisIndex === -1) {
// New run // Process bare tokens for each emphasis marker type
emphasisIndex = matchIndex + matchLength; for (const emphasisTokens of emphasisTokensByMarker.values()) {
emphasisLength = matchLength; for (let i = 0; i + 1 < emphasisTokens.length; i += 2) {
emphasisKind = matchKind;
effectiveEmphasisLength = matchLength; // Process start token of start/end pair
} else if (matchKind === emphasisKind) { const startToken = emphasisTokens[i];
// Matching emphasis markers const startText =
if (matchLength === effectiveEmphasisLength) { lines[startToken.startLine - 1].slice(startToken.startColumn - 1);
// Ending an existing run, report any pending error const startMatch = startText.match(emphasisStartTextRe);
if (pendingError) { if (startMatch) {
// @ts-ignore const [ startContext, startMarker, startSpaces ] = startMatch;
addErrorContext(...pendingError); if ((startMarker === startToken.text) && (startSpaces.length > 0)) {
pendingError = null; addError(
} onError,
const error = handleRunEnd( startToken.startLine,
line, undefined,
lineIndex, startContext,
effectiveEmphasisLength, [ startToken.startColumn, startContext.length ],
match, {
matchIndex, "editColumn": startToken.endColumn,
inTable "deleteCount": startSpaces.length
}
); );
if (error) {
// @ts-ignore
addErrorContext(...error);
}
// Reset
resetRunTracking();
} else if (matchLength === 3) {
// Swap internal run length (1->2 or 2->1)
effectiveEmphasisLength = matchLength - effectiveEmphasisLength;
} else if (effectiveEmphasisLength === 3) {
// Downgrade internal run (3->1 or 3->2)
effectiveEmphasisLength -= matchLength;
} else {
// Upgrade to internal run (1->3 or 2->3)
effectiveEmphasisLength += matchLength;
} }
// Back up one character so RegExp has a chance to match the }
// next marker (ex: "**star**_underscore_")
if (emphasisRe.lastIndex > 1) { // Process end token of start/end pair
emphasisRe.lastIndex--; const endToken = emphasisTokens[i + 1];
const endText =
lines[endToken.startLine - 1].slice(0, endToken.endColumn - 1);
const endMatch = endText.match(emphasisEndTextRe);
if (endMatch) {
const [ endContext, endSpace, endMarker ] = endMatch;
if ((endMarker === endToken.text) && (endSpace.length > 0)) {
addError(
onError,
endToken.startLine,
undefined,
endContext,
[ endToken.endColumn - endContext.length, endContext.length ],
{
"editColumn": endToken.startColumn - endSpace.length,
"deleteCount": endSpace.length
}
);
} }
} else if (emphasisRe.lastIndex > 1) {
// Back up one character so RegExp has a chance to match the
// mis-matched marker (ex: "*text_*")
emphasisRe.lastIndex--;
} }
} }
if (emphasisIndex !== -1) {
pendingError = pendingError ||
handleRunEnd(line, lineIndex, 0, null, line.length, inTable);
// Adjust for pending run on new line
emphasisIndex = 0;
emphasisLength = 0;
}
} }
); }
} }
}; };

View file

@ -86,7 +86,6 @@
"markdown-it-for-inline": "0.1.1", "markdown-it-for-inline": "0.1.1",
"markdown-it-sub": "1.0.0", "markdown-it-sub": "1.0.0",
"markdown-it-sup": "1.0.0", "markdown-it-sup": "1.0.0",
"markdown-it-texmath": "1.0.0",
"markdownlint-rule-helpers": "0.20.0", "markdownlint-rule-helpers": "0.20.0",
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"strip-json-comments": "5.0.1", "strip-json-comments": "5.0.1",

View file

@ -52,14 +52,8 @@ t<!--> *{MD037} * -->
t<!---> *{MD037} * --> t<!---> *{MD037} * -->
t<!-- *{MD037} * ---> t<!-- *comment * --->
t<!-- -- *{MD037} * --> t<!-- -- *comment * -->
t<!-- *{MD037} * -- --> t<!-- *comment * -- -->
## Notes
It's important that the rule used above is one that calls
`helpers.forEachLine` so `markdown-it` doesn't ignore any
incorrectly-remaining comment blocks.

View file

@ -895,333 +895,6 @@ test("applyFixes", (t) => {
} }
}); });
test("forEachLink", (t) => {
t.plan(291);
const testCases = [
[
"",
[]
],
[
"Text",
[]
],
[
"Text [text] (text) text",
[ [ 5, "[text]", "[text]", undefined ] ]
],
[
"Text [text] text (text) text",
[ [ 5, "[text]", "[text]", undefined ] ]
],
[
"Text [text] [text] text",
[
[ 5, "[text]", "[text]", undefined ],
[ 12, "[text]", "[text]", undefined ]
]
],
[
"Text [text] text [text] text",
[
[ 5, "[text]", "[text]", undefined ],
[ 17, "[text]", "[text]", undefined ]
]
],
[
"Text [link](destination) text",
[ [ 5, "[link](destination)", "[link]", "(destination)" ] ]
],
[
"Text [link0](destination0) text [link1](destination1) text",
[
[ 5, "[link0](destination0)", "[link0]", "(destination0)" ],
[ 32, "[link1](destination1)", "[link1]", "(destination1)" ]
]
],
[
"Text [link0] text [link1](destination1) text [link2] text",
[
[ 5, "[link0]", "[link0]", undefined ],
[ 18, "[link1](destination1)", "[link1]", "(destination1)" ],
[ 45, "[link2]", "[link2]", undefined ]
]
],
[
"Text [link0](destination0) text [link1] text [link2](destination2) text",
[
[ 5, "[link0](destination0)", "[link0]", "(destination0)" ],
[ 32, "[link1]", "[link1]", undefined ],
[ 45, "[link2](destination2)", "[link2]", "(destination2)" ]
]
],
[
"Text [link0][destination0] text [link1] text [link2](destination2) text",
[
[ 5, "[link0][destination0]", "[link0]", "[destination0]" ],
[ 32, "[link1]", "[link1]", undefined ],
[ 45, "[link2](destination2)", "[link2]", "(destination2)" ]
]
],
[
"Text [link0](destination0) text [link1] text [link2][destination2] text",
[
[ 5, "[link0](destination0)", "[link0]", "(destination0)" ],
[ 32, "[link1]", "[link1]", undefined ],
[ 45, "[link2][destination2]", "[link2]", "[destination2]" ]
]
],
[
"Text [link](destination \"title\") text",
[
[
5,
"[link](destination \"title\")",
"[link]",
"(destination \"title\")"
]
]
],
[
"Text [link](<destination> \"title\") text",
[
[
5,
"[link](<destination> \"title\")",
"[link]",
"(<destination> \"title\")"
]
]
],
[
"Text [link](destination \"ti\\\"tle\") text",
[
[
5,
"[link](destination \"ti\\\"tle\")",
"[link]",
"(destination \"ti\\\"tle\")"
]
]
],
[
"Text [link](destination 'title') text",
[
[
5,
"[link](destination 'title')",
"[link]",
"(destination 'title')"
]
]
],
[
"Text [link](destination (title)) text",
[
[
5,
"[link](destination (title))",
"[link]",
"(destination (title))"
]
]
],
[
"Text [link](\"title\") text",
[ [ 5, "[link](\"title\")", "[link]", "(\"title\")" ] ]
],
[
"[]()",
[ [ 0, "[]()", "[]", "()" ] ]
],
[
"[l](d)",
[ [ 0, "[l](d)", "[l]", "(d)" ] ]
],
[
"Text [li[nk](dest) text",
[ [ 8, "[nk](dest)", "[nk]", "(dest)" ] ]
],
[
"Text [li\\[nk](dest) text",
[ [ 5, "[li\\[nk](dest)", "[li\\[nk]", "(dest)" ] ]
],
[
"Text [li]nk](dest) text",
[ [ 5, "[li]", "[li]", undefined ] ]
],
[
"Text [li\\]nk](dest) text",
[ [ 5, "[li\\]nk](dest)", "[li\\]nk]", "(dest)" ] ]
],
[
"Text [l[in]k](dest) text",
[ [ 5, "[l[in]k](dest)", "[l[in]k]", "(dest)" ] ]
],
[
"Text [li(nk](dest) text",
[ [ 5, "[li(nk](dest)", "[li(nk]", "(dest)" ] ]
],
[
"Text [li)nk](dest) text",
[ [ 5, "[li)nk](dest)", "[li)nk]", "(dest)" ] ]
],
[
"Text [l(in)k](dest) text",
[ [ 5, "[l(in)k](dest)", "[l(in)k]", "(dest)" ] ]
],
[
"Text [link](de(st) text",
[ [ 5, "[link]", "[link]", undefined ] ]
],
[
"Text [link](de\\(st) text",
[ [ 5, "[link](de\\(st)", "[link]", "(de\\(st)" ] ]
],
[
"Text [link](de)st) text",
[ [ 5, "[link](de)", "[link]", "(de)" ] ]
],
[
"Text [link](de\\)st) text",
[ [ 5, "[link](de\\)st)", "[link]", "(de\\)st)" ] ]
],
[
"Text [link](d(es)t) text",
[ [ 5, "[link](d(es)t)", "[link]", "(d(es)t)" ] ]
],
[
"Text [link]() text",
[ [ 5, "[link]()", "[link]", "()" ] ]
],
[
"Text [link](#) text",
[ [ 5, "[link](#)", "[link]", "(#)" ] ]
],
[
"Text [link](<de) text",
[ [ 5, "[link]", "[link]", undefined ] ]
],
[
"Text [link](<de)st> text",
[ [ 5, "[link]", "[link]", undefined ] ]
],
[
"Text [link](<>) text",
[ [ 5, "[link](<>)", "[link]", "(<>)" ] ]
],
[
"Text [link](<dest>) text",
[ [ 5, "[link](<dest>)", "[link]", "(<dest>)" ] ]
],
[
"Text [link](<de st>) text",
[ [ 5, "[link](<de st>)", "[link]", "(<de st>)" ] ]
],
[
"Text [link](<de)st>) text",
[ [ 5, "[link](<de)st>)", "[link]", "(<de)st>)" ] ]
],
[
"Text [<link](dest) text",
[ [ 5, "[<link](dest)", "[<link]", "(dest)" ] ]
],
[
"Text [<link>](dest) text",
[ [ 5, "[<link>](dest)", "[<link>]", "(dest)" ] ]
],
[
"Text [<link>](<dest) text",
[ [ 5, "[<link>]", "[<link>]", undefined ] ]
],
[
"Text [<link>](<dest>) text",
[ [ 5, "[<link>](<dest>)", "[<link>]", "(<dest>)" ] ]
],
[
"Text [[[[l[i]n[k]](dest) text",
[ [ 8, "[l[i]n[k]](dest)", "[l[i]n[k]]", "(dest)" ] ]
],
[
"Text [link](d(e(st))) text",
[ [ 5, "[link](d(e(st)))", "[link]", "(d(e(st)))" ] ]
],
[
"Text [link](d(e(st)) text",
[ [ 5, "[link]", "[link]", undefined ] ]
],
[
"Text [link](<d(e(st)>) text",
[ [ 5, "[link](<d(e(st)>)", "[link]", "(<d(e(st)>)" ] ]
],
[
"Text [link][reference] text",
[ [ 5, "[link][reference]", "[link]", "[reference]" ] ]
],
[
"Text [link][refer]ence] text",
[ [ 5, "[link][refer]", "[link]", "[refer]" ] ]
]
];
for (const testCase of testCases) {
const [ markdown, matches ] = testCase;
helpers.forEachLink(String(markdown), (idx, lnk, txt, des) => {
// @ts-ignore
const match = matches.shift();
const [ index, link, text, destination ] = match;
t.is(idx, index, String(markdown));
t.is(lnk, link, String(markdown));
t.is(txt, text, String(markdown));
t.is(des, destination, String(markdown));
});
t.is(matches.length, 0, "Missing match");
}
});
test("htmlElementRanges", (t) => {
t.plan(1);
const params = {
"lines": [
"# Heading",
"",
"Text text text",
"text <a id='id'/> text",
"text text text",
"",
"<p>",
"Text <em>text</em> text",
"</p>",
"",
"```",
"<br/>",
"```",
"",
"Text `<br/>` text",
"text <br/> text"
],
"parsers": {
"markdownit": {
"tokens": [
{
"type": "code_block",
"map": [ 10, 12 ]
}
]
}
}
};
const expected = [
[ 3, 5, 12 ],
[ 6, 0, 3 ],
[ 7, 5, 4 ],
[ 14, 6, 5 ],
[ 15, 5, 5 ]
];
const lineMetadata = helpers.getLineMetadata(params);
const actual = helpers.htmlElementRanges(params, lineMetadata);
t.deepEqual(actual, expected);
});
test("expandTildePath", (t) => { test("expandTildePath", (t) => {
t.plan(17); t.plan(17);
const homedir = os.homedir(); const homedir = os.homedir();

View file

@ -406,12 +406,24 @@ test("resultFormattingV3", (t) => new Promise((resolve) => {
"ruleDescription": "Spaces inside emphasis markers", "ruleDescription": "Spaces inside emphasis markers",
"ruleInformation": `${homepage}/blob/v${version}/doc/md037.md`, "ruleInformation": `${homepage}/blob/v${version}/doc/md037.md`,
"errorDetail": null, "errorDetail": null,
"errorContext": "* emphasis *", "errorContext": "* e",
"errorRange": [ 6, 12 ], "errorRange": [ 6, 3 ],
"fixInfo": { "fixInfo": {
"editColumn": 6, "editColumn": 7,
"deleteCount": 12, "deleteCount": 1
"insertText": "*emphasis*" }
},
{
"lineNumber": 4,
"ruleNames": [ "MD037", "no-space-in-emphasis" ],
"ruleDescription": "Spaces inside emphasis markers",
"ruleInformation": `${homepage}/blob/v${version}/doc/md037.md`,
"errorDetail": null,
"errorContext": "s *",
"errorRange": [ 15, 3 ],
"fixInfo": {
"editColumn": 16,
"deleteCount": 1
} }
}, },
{ {
@ -439,7 +451,9 @@ test("resultFormattingV3", (t) => new Promise((resolve) => {
"input: 3: MD010/no-hard-tabs" + "input: 3: MD010/no-hard-tabs" +
" Hard tabs [Column: 10]\n" + " Hard tabs [Column: 10]\n" +
"input: 4: MD037/no-space-in-emphasis" + "input: 4: MD037/no-space-in-emphasis" +
" Spaces inside emphasis markers [Context: \"* emphasis *\"]\n" + " Spaces inside emphasis markers [Context: \"* e\"]\n" +
"input: 4: MD037/no-space-in-emphasis" +
" Spaces inside emphasis markers [Context: \"s *\"]\n" +
"input: 4: MD047/single-trailing-newline" + "input: 4: MD047/single-trailing-newline" +
" Files should end with a single newline character"; " Files should end with a single newline character";
t.is(actualMessage, expectedMessage, "Incorrect message."); t.is(actualMessage, expectedMessage, "Incorrect message.");

View file

@ -9,7 +9,6 @@ const md = require("markdown-it")();
const pluginInline = require("markdown-it-for-inline"); const pluginInline = require("markdown-it-for-inline");
const pluginSub = require("markdown-it-sub"); const pluginSub = require("markdown-it-sub");
const pluginSup = require("markdown-it-sup"); const pluginSup = require("markdown-it-sup");
const pluginTexMath = require("markdown-it-texmath");
const test = require("ava").default; const test = require("ava").default;
const tv4 = require("tv4"); const tv4 = require("tv4");
const { "exports": packageExports, homepage, version } = const { "exports": packageExports, homepage, version } =
@ -20,11 +19,6 @@ const rules = require("../lib/rules");
const customRules = require("./rules/rules.js"); const customRules = require("./rules/rules.js");
const configSchema = require("../schema/markdownlint-config-schema.json"); const configSchema = require("../schema/markdownlint-config-schema.json");
const pluginTexMathOptions = {
"engine": {
"renderToString": () => ""
}
};
const deprecatedRuleNames = new Set(constants.deprecatedRuleNames); const deprecatedRuleNames = new Set(constants.deprecatedRuleNames);
const configSchemaStrict = { const configSchemaStrict = {
...configSchema, ...configSchema,
@ -1104,77 +1098,6 @@ test("markdownItPluginsMultiple", (t) => new Promise((resolve) => {
}); });
})); }));
test("markdownItPluginsMathjax", (t) => new Promise((resolve) => {
t.plan(2);
markdownlint({
"strings": {
"string":
"# Heading\n" +
"\n" +
"$1 *2* 3$\n" +
"\n" +
"$$1 *2* 3$$\n" +
"\n" +
"$$1\n" +
"+ 2\n" +
"+ 3$$\n"
},
"markdownItPlugins": [ [ pluginTexMath, pluginTexMathOptions ] ]
}, function callback(err, actual) {
t.falsy(err);
const expected = { "string": [] };
t.deepEqual(actual, expected, "Unexpected issues.");
resolve();
});
}));
test("markdownItPluginsMathjaxIssue166", (t) => new Promise((resolve) => {
t.plan(2);
markdownlint({
"strings": {
"string":
`## Heading
$$
1
$$$$
2
$$\n`
},
"markdownItPlugins": [ [ pluginTexMath, pluginTexMathOptions ] ],
"resultVersion": 0
}, function callback(err, actual) {
t.falsy(err);
const expected = {
"string": {
"MD041": [ 1 ]
}
};
// @ts-ignore
t.deepEqual(actual, expected, "Unexpected issues.");
resolve();
});
}));
test("texmath test files with texmath plugin", (t) => new Promise((resolve) => {
t.plan(2);
markdownlint({
"files": [
"./test/texmath-content-in-lists.md",
"./test/texmath-content-violating-md037.md"
],
"markdownItPlugins": [ [ pluginTexMath, pluginTexMathOptions ] ]
}, function callback(err, actual) {
t.falsy(err);
const expected = {
"./test/texmath-content-in-lists.md": [],
"./test/texmath-content-violating-md037.md": []
};
t.deepEqual(actual, expected, "Unexpected issues.");
resolve();
});
}));
test("Pandoc footnote", (t) => new Promise((resolve) => { test("Pandoc footnote", (t) => new Promise((resolve) => {
t.plan(2); t.plan(2);
markdownlint({ markdownlint({

15
test/mathjax-scenarios.md Normal file
View file

@ -0,0 +1,15 @@
# Mathjax Scenarios
$1 * 2 * 3$
$$1 * 2 * 3$$
$$1
+ 2
+ 3$$
$$
1
$$$$
2
$$

View file

@ -17,7 +17,9 @@ Generated by [AVA](https://avajs.dev).
`test-repos/dotnet-docs/docs/azure/sdk/includes/assign-local-dev-group-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊ `test-repos/dotnet-docs/docs/azure/sdk/includes/assign-local-dev-group-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊
test-repos/dotnet-docs/docs/azure/sdk/includes/assign-managed-identity-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊ test-repos/dotnet-docs/docs/azure/sdk/includes/assign-managed-identity-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊
test-repos/dotnet-docs/docs/azure/sdk/includes/assign-service-principal-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊ test-repos/dotnet-docs/docs/azure/sdk/includes/assign-service-principal-to-role-azure-portal-4.md: 5: MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "<br>"]␊
test-repos/dotnet-docs/docs/core/compatibility/core-libraries/5.0/code-access-security-apis-obsolete.md: 431: MD033/no-inline-html Inline HTML [Element: StrongName]` test-repos/dotnet-docs/docs/core/compatibility/core-libraries/5.0/code-access-security-apis-obsolete.md: 431: MD033/no-inline-html Inline HTML [Element: StrongName]␊
test-repos/dotnet-docs/docs/core/extensions/windows-service.md: 177: MD037/no-space-in-emphasis Spaces inside emphasis markers [Context: "n *"]␊
test-repos/dotnet-docs/docs/devops/github-actions-overview.md: 32: MD037/no-space-in-emphasis Spaces inside emphasis markers [Context: "r *"]`
## https://github.com/electron/electron ## https://github.com/electron/electron

File diff suppressed because it is too large Load diff

View file

@ -42,8 +42,8 @@ emphasis * text {MD037}
Text * emphasis {MD037} Text * emphasis {MD037}
emphasis * text {MD037} emphasis * text {MD037}
Text *emphasis * *emphasis {MD037} Text *emphasis * *emphasis
emphasis* * emphasis* text {MD037} emphasis* * emphasis* text
Text *emphasis* * emphasis {MD037} Text *emphasis* * emphasis {MD037}
emphasis * *emphasis* text {MD037} emphasis * *emphasis* text {MD037}

View file

@ -125,32 +125,51 @@ Uncommon scenarios from the CommonMark specification (and some variations):
*in emph **strong*** *in emph **strong***
*** strong emph*** {MD037} *** strong emph*** {MD037}
*** strong** in emph* {MD037}
*** emph* in strong** {MD037} *** strong** in emph* {possible MD037}
*** emph* in strong** {possible MD037}
** in strong *emph*** {MD037} ** in strong *emph*** {MD037}
***strong emph *** {MD037} ***strong emph *** {MD037}
***strong** in emph * {MD037} ***strong** in emph * {MD037}
***emph* in strong ** {MD037} ***emph* in strong ** {MD037}
**in strong *emph *** {MD037}
*in emph **strong *** {MD037} **in strong *emph *** {possible MD037}
*in emph **strong *** {possible MD037}
** *strong emph*** {MD037} ** *strong emph*** {MD037}
** *strong** in emph* {MD037} ** *strong** in emph* {MD037}
** *emph* in strong** {MD037} ** *emph* in strong** {MD037}
**in strong * emph*** (internal spaces are not detected) **in strong * emph*** (internal spaces are not detected)
*in emph ** strong*** (internal spaces are not detected) *in emph ** strong*** (internal spaces are not detected)
***strong emph* ** {MD037} ***strong emph* ** {MD037}
***strong ** in emph* (internal spaces are not detected) ***strong ** in emph* (internal spaces are not detected)
***emph * in strong** (internal spaces are not detected) ***emph * in strong** (internal spaces are not detected)
**in strong *emph* ** {MD037} **in strong *emph* ** {MD037}
*in emph **strong* ** {MD037} *in emph **strong* ** {MD037}
Text *emph***strong** text Text *emph***strong** text
Text * emph***strong** text {MD037} Text * emph***strong** text {MD037}
Text *emph ***strong** text (internal spaces are not detected)
Text *emph*** strong** text (internal spaces are not detected) Text *emph ***strong** text {MD037}
Text *emph*** strong** text {MD037}
Text *emph***strong ** text {MD037} Text *emph***strong ** text {MD037}
```markdown ```markdown

View file

@ -1,19 +0,0 @@
# texmath-content-violating-md037
## Inline (not handled)
text `$ x * y * z $` text
text `$$ x * y * z $$` text
## Block (handled when used with markdown-it-texmath)
$$
x * y * z {MD037}
$$
text
$$
x * y = x * y {MD037}
$$

19
test/texmath-content.md Normal file
View file

@ -0,0 +1,19 @@
# texmath-content
## Inline
text $ x * y * z $ text
text $$ x * y * z $$ text
## Block
$$
x * y * z
$$
text
$$
x * y = x * y
$$