Update MD037/no-space-in-emphasis to ignore content of math blocks when used with markdown-it-texmath (fixes #357).

This commit is contained in:
David Anson 2021-01-24 17:50:39 -08:00
parent d6cd840e7d
commit df4aa9f4e8
5 changed files with 89 additions and 37 deletions

View file

@ -228,44 +228,59 @@ 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 === "math") &&
token.type.startsWith("math_block") &&
!token.type.endsWith("_end"));
}
// 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 mapLine(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]; filterTokens(params, "fence", function (token) {
});
filterTokens(params, "fence", function forToken(token) {
lineMetadata[token.map[0]][3] = 1; lineMetadata[token.map[0]][3] = 1;
lineMetadata[token.map[1] - 1][3] = -1; lineMetadata[token.map[1] - 1][3] = -1;
for (var i = token.map[0] + 1; i < token.map[1] - 1; i++) { for (var i = token.map[0] + 1; i < token.map[1] - 1; i++) {
lineMetadata[i][2] = true; lineMetadata[i][2] = true;
} }
}); });
filterTokens(params, "code_block", function forToken(token) { filterTokens(params, "code_block", function (token) {
for (var i = token.map[0]; i < token.map[1]; i++) { for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][2] = true; lineMetadata[i][2] = true;
} }
}); });
filterTokens(params, "table_open", function forToken(token) { filterTokens(params, "table_open", function (token) {
for (var i = token.map[0]; i < token.map[1]; i++) { for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][4] = true; lineMetadata[i][4] = true;
} }
}); });
filterTokens(params, "list_item_open", function forToken(token) { filterTokens(params, "list_item_open", function (token) {
var count = 1; var count = 1;
for (var i = token.map[0]; i < token.map[1]; i++) { for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][5] = count; lineMetadata[i][5] = count;
count++; count++;
} }
}); });
filterTokens(params, "hr", function forToken(token) { filterTokens(params, "hr", function (token) {
lineMetadata[token.map[0]][6] = true; lineMetadata[token.map[0]][6] = true;
}); });
params.tokens.filter(isMathBlock).forEach(function (token) {
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][7] = true;
}
});
return lineMetadata; return lineMetadata;
}; };
// Calls the provided function for each line (with context) // Calls the provided function for each line (with context)
module.exports.forEachLine = function forEachLine(lineMetadata, handler) { module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
lineMetadata.forEach(function forMetadata(metadata) { lineMetadata.forEach(function forMetadata(metadata) {
// Parameters: line, lineIndex, inCode, onFence, inTable, inItem, inBreak // Parameters:
// line, lineIndex, inCode, onFence, inTable, inItem, inBreak, inMath
handler.apply(void 0, metadata); handler.apply(void 0, metadata);
}); });
}; };
@ -277,11 +292,9 @@ module.exports.flattenLists = function flattenLists(params) {
var nesting = 0; var nesting = 0;
var nestingStack = []; var nestingStack = [];
var lastWithMap = { "map": [0, 1] }; var lastWithMap = { "map": [0, 1] };
params.tokens.forEach(function forToken(token) { params.tokens.forEach(function (token) {
if ((token.type === "math_block") && if (isMathBlock(token) && token.map[1]) {
(token.tag === "math") && // markdown-it-texmath plugin does not account for math_block_end
token.map[1]) {
// markdown-it-texmath package does not account for math_block_end
token.map[1]++; token.map[1]++;
} }
if ((token.type === "bullet_list_open") || if ((token.type === "bullet_list_open") ||
@ -3269,13 +3282,13 @@ module.exports = {
// Initialize // Initialize
var ignoreMarkersByLine = emphasisMarkersInContent(params); var ignoreMarkersByLine = emphasisMarkersInContent(params);
resetRunTracking(); resetRunTracking();
forEachLine(lineMetadata(), function (line, lineIndex, inCode, onFence, inTable, inItem, onBreak) { forEachLine(lineMetadata(), function (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) {
var onItemStart = (inItem === 1); var onItemStart = (inItem === 1);
if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) { if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) {
// Emphasis resets when leaving a block // Emphasis resets when leaving a block
resetRunTracking(); resetRunTracking();
} }
if (inCode || onBreak) { if (inCode || onBreak || inMath) {
// Emphasis has no meaning here // Emphasis has no meaning here
return; return;
} }

View file

@ -223,45 +223,65 @@ 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 === "math") &&
token.type.startsWith("math_block") &&
!token.type.endsWith("_end")
);
}
// 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(function mapLine(line, index) { const lineMetadata = params.lines.map(
return [ line, index, false, 0, false, false ]; (line, index) => [ line, index, false, 0, false, false, false, false ]
}); );
filterTokens(params, "fence", function forToken(token) { filterTokens(params, "fence", (token) => {
lineMetadata[token.map[0]][3] = 1; lineMetadata[token.map[0]][3] = 1;
lineMetadata[token.map[1] - 1][3] = -1; lineMetadata[token.map[1] - 1][3] = -1;
for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) { for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
lineMetadata[i][2] = true; lineMetadata[i][2] = true;
} }
}); });
filterTokens(params, "code_block", function forToken(token) { filterTokens(params, "code_block", (token) => {
for (let i = token.map[0]; i < token.map[1]; i++) { for (let i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][2] = true; lineMetadata[i][2] = true;
} }
}); });
filterTokens(params, "table_open", function forToken(token) { filterTokens(params, "table_open", (token) => {
for (let i = token.map[0]; i < token.map[1]; i++) { for (let i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][4] = true; lineMetadata[i][4] = true;
} }
}); });
filterTokens(params, "list_item_open", function forToken(token) { filterTokens(params, "list_item_open", (token) => {
let count = 1; let count = 1;
for (let i = token.map[0]; i < token.map[1]; i++) { for (let i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][5] = count; lineMetadata[i][5] = count;
count++; count++;
} }
}); });
filterTokens(params, "hr", function forToken(token) { filterTokens(params, "hr", (token) => {
lineMetadata[token.map[0]][6] = true; lineMetadata[token.map[0]][6] = true;
}); });
params.tokens.filter(isMathBlock).forEach((token) => {
for (let i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i][7] = true;
}
});
return lineMetadata; return lineMetadata;
}; };
// Calls the provided function for each line (with context) // Calls the provided function for each line (with context)
module.exports.forEachLine = function forEachLine(lineMetadata, handler) { module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
lineMetadata.forEach(function forMetadata(metadata) { lineMetadata.forEach(function forMetadata(metadata) {
// Parameters: line, lineIndex, inCode, onFence, inTable, inItem, inBreak // Parameters:
// line, lineIndex, inCode, onFence, inTable, inItem, inBreak, inMath
handler(...metadata); handler(...metadata);
}); });
}; };
@ -274,13 +294,9 @@ module.exports.flattenLists = function flattenLists(params) {
let nesting = 0; let nesting = 0;
const nestingStack = []; const nestingStack = [];
let lastWithMap = { "map": [ 0, 1 ] }; let lastWithMap = { "map": [ 0, 1 ] };
params.tokens.forEach(function forToken(token) { params.tokens.forEach((token) => {
if ( if (isMathBlock(token) && token.map[1]) {
(token.type === "math_block") && // markdown-it-texmath plugin does not account for math_block_end
(token.tag === "math") &&
token.map[1]
) {
// markdown-it-texmath package does not account for math_block_end
token.map[1]++; token.map[1]++;
} }
if ((token.type === "bullet_list_open") || if ((token.type === "bullet_list_open") ||

View file

@ -76,13 +76,13 @@ module.exports = {
resetRunTracking(); resetRunTracking();
forEachLine( forEachLine(
lineMetadata(), lineMetadata(),
(line, lineIndex, inCode, onFence, inTable, inItem, onBreak) => { (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
const onItemStart = (inItem === 1); const onItemStart = (inItem === 1);
if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) { if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) {
// Emphasis resets when leaving a block // Emphasis resets when leaving a block
resetRunTracking(); resetRunTracking();
} }
if (inCode || onBreak) { if (inCode || onBreak || inMath) {
// Emphasis has no meaning here // Emphasis has no meaning here
return; return;
} }

View file

@ -1508,15 +1508,19 @@ $$\n`
}); });
}); });
test.cb("texmath-content-in-lists with texmath plugin", (t) => { test.cb("texmath test files with texmath plugin", (t) => {
t.plan(2); t.plan(2);
markdownlint({ markdownlint({
"files": [ "./test/texmath-content-in-lists.md" ], "files": [
"./test/texmath-content-in-lists.md",
"./test/texmath-content-violating-md037.md"
],
"markdownItPlugins": [ [ pluginTexMath, pluginTexMathOptions ] ] "markdownItPlugins": [ [ pluginTexMath, pluginTexMathOptions ] ]
}, function callback(err, actual) { }, function callback(err, actual) {
t.falsy(err); t.falsy(err);
const expected = { const expected = {
"./test/texmath-content-in-lists.md": [] "./test/texmath-content-in-lists.md": [],
"./test/texmath-content-violating-md037.md": []
}; };
t.deepEqual(actual, expected, "Unexpected issues."); t.deepEqual(actual, expected, "Unexpected issues.");
t.end(); t.end();

View file

@ -0,0 +1,19 @@
# 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}
$$