Skip to content

Commit 6add1d9

Browse files
committed
Fix: More reliable comment attachment (fixes #76)
Comment attachment was sensitive to whitespace around the code block and preceding comments. In some cases, the parser would place comments as descendants of code blocks' preceding sibling nodes. However, a depth-first traversal of the tree will still encounter the comments in linear order, which is sufficient for our purposes.
1 parent 610cffd commit 6add1d9

File tree

2 files changed

+56
-19
lines changed

2 files changed

+56
-19
lines changed

lib/processor.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@ let blocks = [];
2222
* Performs a depth-first traversal of the Markdown AST.
2323
* @param {ASTNode} node A Markdown AST node.
2424
* @param {Object} callbacks A map of node types to callbacks.
25-
* @param {Object} [parent] The node's parent AST node.
2625
* @returns {void}
2726
*/
28-
function traverse(node, callbacks, parent) {
27+
function traverse(node, callbacks) {
2928
if (callbacks[node.type]) {
30-
callbacks[node.type](node, parent);
29+
callbacks[node.type](node);
30+
} else {
31+
callbacks["*"]();
3132
}
3233

3334
if (typeof node.children !== "undefined") {
3435
for (let i = 0; i < node.children.length; i++) {
35-
traverse(node.children[i], callbacks, node);
36+
traverse(node.children[i], callbacks);
3637
}
3738
}
3839
}
@@ -215,39 +216,44 @@ function getBlockRangeMap(text, node, comments) {
215216
*/
216217
function preprocess(text) {
217218
const ast = markdown.parse(text);
219+
let htmlComments = [];
218220

219221
blocks = [];
220222
traverse(ast, {
221-
code(node, parent) {
222-
const comments = [];
223-
223+
"*"() {
224+
htmlComments = [];
225+
},
226+
code(node) {
224227
if (node.lang) {
225-
let index = parent.children.indexOf(node) - 1;
226-
let previousNode = parent.children[index];
227-
228-
while (previousNode && previousNode.type === "html") {
229-
const comment = getComment(previousNode.value);
230-
231-
if (!comment) {
232-
break;
233-
}
228+
const comments = [];
234229

230+
for (const comment of htmlComments) {
235231
if (comment.trim() === "eslint-skip") {
232+
htmlComments = [];
236233
return;
237234
}
238235

239-
comments.unshift(`/*${comment}*/`);
240-
index--;
241-
previousNode = parent.children[index];
236+
comments.push(`/*${comment}*/`);
242237
}
243238

239+
htmlComments = [];
240+
244241
blocks.push({
245242
...node,
246243
baseIndentText: getIndentText(text, node),
247244
comments,
248245
rangeMap: getBlockRangeMap(text, node, comments)
249246
});
250247
}
248+
},
249+
html(node) {
250+
const comment = getComment(node.value);
251+
252+
if (comment) {
253+
htmlComments.push(comment);
254+
} else {
255+
htmlComments = [];
256+
}
251257
}
252258
});
253259

tests/lib/processor.js

+31
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,37 @@ describe("processor", () => {
454454
].join("\n"));
455455
});
456456

457+
// https://github.com/eslint/eslint-plugin-markdown/issues/76
458+
it("should insert comments inside list items", () => {
459+
const code = [
460+
"* List item followed by a blank line",
461+
"",
462+
"<!-- eslint-disable no-console -->",
463+
"```js",
464+
"console.log(\"Blank line\");",
465+
"```",
466+
"",
467+
"* List item without a blank line",
468+
"<!-- eslint-disable no-console -->",
469+
"```js",
470+
"console.log(\"No blank line\");",
471+
"```"
472+
].join("\n");
473+
const blocks = processor.preprocess(code);
474+
475+
assert.strictEqual(blocks.length, 2);
476+
assert.strictEqual(blocks[0].text, [
477+
"/* eslint-disable no-console */",
478+
"console.log(\"Blank line\");",
479+
""
480+
].join("\n"));
481+
assert.strictEqual(blocks[1].text, [
482+
"/* eslint-disable no-console */",
483+
"console.log(\"No blank line\");",
484+
""
485+
].join("\n"));
486+
});
487+
457488
it("should ignore non-eslint comments", () => {
458489
const code = [
459490
"<!-- eslint-env browser -->",

0 commit comments

Comments
 (0)