Update MD037/no-space-in-emphasis to ignore emphasis markers in link text/destination (refs #286).

This commit is contained in:
David Anson 2020-05-08 16:01:42 -07:00
parent 0cea489e18
commit 37f1d6b64b
3 changed files with 38 additions and 8 deletions

View file

@ -28,6 +28,9 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
// Regular expression for emphasis markers // Regular expression for emphasis markers
const emphasisMarkersRe = /[_*]+/g; const emphasisMarkersRe = /[_*]+/g;
// Regular expression for links
const linkRe = /\[(?:[^\]]|\[[^\]]*\])*\]\(\S*\)/g;
// readFile options for reading with the UTF-8 encoding // readFile options for reading with the UTF-8 encoding
module.exports.utf8Encoding = { "encoding": "utf8" }; module.exports.utf8Encoding = { "encoding": "utf8" };
@ -494,14 +497,15 @@ module.exports.frontMatterHasTitle =
}; };
/** /**
* Returns a list of emphasis markers in code spans. * Returns a list of emphasis markers in code spans and links.
* *
* @param {Object} params RuleParams instance. * @param {Object} params RuleParams instance.
* @returns {number[][]} List of markers. * @returns {number[][]} List of markers.
*/ */
function emphasisMarkersInCodeSpans(params) { function emphasisMarkersInContent(params) {
const { lines } = params; const { lines } = params;
const byLine = new Array(lines.length); const byLine = new Array(lines.length);
// Search code spans
filterTokens(params, "inline", (token) => { filterTokens(params, "inline", (token) => {
const { children, lineNumber, map } = token; const { children, lineNumber, map } = token;
if (children.some((child) => child.type === "code_inline")) { if (children.some((child) => child.type === "code_inline")) {
@ -524,9 +528,21 @@ function emphasisMarkersInCodeSpans(params) {
); );
} }
}); });
// Search links
lines.forEach((tokenLine, tokenLineIndex) => {
let linkMatch = null;
while ((linkMatch = linkRe.exec(tokenLine))) {
let markerMatch = null;
while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
const inLine = byLine[tokenLineIndex] || [];
inLine.push(linkMatch.index + markerMatch.index);
byLine[tokenLineIndex] = inLine;
}
}
});
return byLine; return byLine;
} }
module.exports.emphasisMarkersInCodeSpans = emphasisMarkersInCodeSpans; module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
/** /**
* Gets the most common line ending, falling back to the platform default. * Gets the most common line ending, falling back to the platform default.

View file

@ -2,8 +2,8 @@
"use strict"; "use strict";
const { addErrorContext, emphasisMarkersInCodeSpans, forEachLine, const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine } =
includesSorted, isBlankLine } = require("../helpers"); require("../helpers");
const { lineMetadata } = require("./cache"); const { lineMetadata } = require("./cache");
const emphasisRe = /(^|[^\\])(?:(\*\*?\*?)|(__?_?))/g; const emphasisRe = /(^|[^\\])(?:(\*\*?\*?)|(__?_?))/g;
@ -66,7 +66,7 @@ module.exports = {
return null; return null;
} }
// Initialize // Initialize
const ignoreMarkersByLine = emphasisMarkersInCodeSpans(params); const ignoreMarkersByLine = emphasisMarkersInContent(params);
resetRunTracking(); resetRunTracking();
forEachLine( forEachLine(
lineMetadata(), lineMetadata(),
@ -89,8 +89,8 @@ module.exports = {
while ((match = emphasisRe.exec(line))) { while ((match = emphasisRe.exec(line))) {
const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex] || []; const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex] || [];
const matchIndex = match.index + match[1].length; const matchIndex = match.index + match[1].length;
if (includesSorted(ignoreMarkersForLine, matchIndex)) { if (ignoreMarkersForLine.includes(matchIndex)) {
// Ignore emphasis markers inside code spans // Ignore emphasis markers inside code spans and links
continue; continue;
} }
const matchLength = match[0].length - match[1].length; const matchLength = match[0].length - match[1].length;

View file

@ -174,3 +174,17 @@ Mixed `code*span` scenarios are *also* okay.
Mixed `code*span` scenarios are _also_ okay. Mixed `code*span` scenarios are _also_ okay.
Mixed `code_span` scenarios are *also* okay. Mixed `code_span` scenarios are *also* okay.
[Link](under_score) followed by _underscore_
[Link](un_der_score) followed by _underscore_
[Link](un_der_sco_re) followed by _underscore_
[Link](star*star) followed by *star*
* [Link](star*star) followed by *star*
Text [Link](under_score) text _underscore_ text [Link](st*ar) text *star* text
[Link [link] link](under_score) followed by _underscore_