feat(unstable): support comments in lint plugin (#29189)

This PR adds support for comments in the AST for lint plugins.

- The `Program` node has a `comments` field pointing to an array of all
comments.
- `SourceCode.getAllComments()`: Returns all comments (same as
`program.comments`)
- `SourceCode.getCommentsBefore(node)`: Get all comments before this
Node
- `SourceCode.getCommentsAfter(node)`: Get all comments after this Node
- `SourceCode.getCommentsInside(node)`: Get all comments inside this
Node

ESLint docs:
-
https://eslint.org/docs/latest/extend/custom-rules#accessing-the-source-code
- https://eslint.org/docs/latest/extend/custom-rules#accessing-comments
This commit is contained in:
Marvin Hagemeister 2025-05-08 21:59:36 +02:00 committed by GitHub
parent e1e67a703c
commit c015b8affd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 352 additions and 7 deletions

View file

@ -263,6 +263,79 @@ export class SourceCode {
return ancestors;
}
/**
* @returns {Array<Deno.lint.LineComment | Deno.lint.BlockComment>}
*/
getAllComments() {
materializeComments(this.#ctx);
return this.#ctx.comments;
}
/**
* @param {Deno.lint.Node} node
* @returns {Array<Deno.lint.LineComment | Deno.lint.BlockComment>}
*/
getCommentsBefore(node) {
materializeComments(this.#ctx);
/** @type {Array<Deno.lint.LineComment | Deno.lint.BlockComment>} */
const before = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (comment.range[0] <= node.range[0]) {
before.push(comment);
}
}
return before;
}
/**
* @param {Deno.lint.Node} node
* @returns {Array<Deno.lint.LineComment | Deno.lint.BlockComment>}
*/
getCommentsAfter(node) {
materializeComments(this.#ctx);
/** @type {Array<Deno.lint.LineComment | Deno.lint.BlockComment>} */
const after = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (comment.range[0] >= node.range[1]) {
after.push(comment);
}
}
return after;
}
/**
* @param {Deno.lint.Node} node
* @returns {Array<Deno.lint.LineComment | Deno.lint.BlockComment>}
*/
getCommentsInside(node) {
materializeComments(this.#ctx);
/** @type {Array<Deno.lint.LineComment | Deno.lint.BlockComment>} */
const inside = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (
comment.range[0] >= node.range[0] && comment.range[1] <= node.range[1]
) {
inside.push(comment);
}
}
return inside;
}
/**
* @returns {string}
*/
@ -345,6 +418,34 @@ export class Context {
}
}
/**
* @param {AstContext} ctx
*/
function materializeComments(ctx) {
const { buf, commentsOffset, comments, strTable } = ctx;
let offset = commentsOffset;
const count = readU32(buf, offset);
offset += 4;
if (comments.length === count) return;
while (offset < buf.length && comments.length < count) {
const kind = buf[offset];
offset++;
const spanId = readU32(buf, offset);
offset += 4;
const strId = readU32(buf, offset);
offset += 4;
comments.push({
type: kind === 0 ? "Line" : "Block",
range: readSpan(ctx, spanId),
value: getString(strTable, strId),
});
}
}
/**
* @param {Deno.lint.Plugin[]} plugins
* @param {string[]} exclude
@ -489,6 +590,7 @@ class FacadeNode {
/** @type {Set<number>} */
const appliedGetters = new Set();
let hasCommenstGetter = false;
/**
* Add getters for all potential properties found in the message.
@ -515,6 +617,16 @@ function setNodeGetters(ctx) {
},
});
}
if (!hasCommenstGetter) {
hasCommenstGetter = true;
Object.defineProperty(FacadeNode.prototype, "comments", {
get() {
materializeComments(ctx);
return ctx.comments;
},
});
}
}
/**
@ -994,6 +1106,7 @@ function createAstContext(buf, token) {
// The buffer has a few offsets at the end which allows us to easily
// jump to the relevant sections of the message.
const commentsOffset = readU32(buf, buf.length - 28);
const propsOffset = readU32(buf, buf.length - 24);
const spansOffset = readU32(buf, buf.length - 20);
const typeMapOffset = readU32(buf, buf.length - 16);
@ -1060,7 +1173,9 @@ function createAstContext(buf, token) {
rootOffset,
spansOffset,
propsOffset,
commentsOffset,
nodes: new Map(),
comments: [],
strTableOffset,
strByProp,
strByType,