176 lines
6.7 KiB
JavaScript
176 lines
6.7 KiB
JavaScript
/**
|
|
* @fileoverview Attaches comments to the AST.
|
|
* @author Nicholas C. Zakas
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Requirements
|
|
//------------------------------------------------------------------------------
|
|
|
|
var astNodeTypes = require("./ast-node-types");
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private
|
|
//------------------------------------------------------------------------------
|
|
|
|
var extra = {
|
|
trailingComments: [],
|
|
leadingComments: [],
|
|
bottomRightStack: [],
|
|
previousNode: null
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public
|
|
//------------------------------------------------------------------------------
|
|
|
|
module.exports = {
|
|
|
|
reset: function() {
|
|
extra.trailingComments = [];
|
|
extra.leadingComments = [];
|
|
extra.bottomRightStack = [];
|
|
extra.previousNode = null;
|
|
},
|
|
|
|
addComment: function(comment) {
|
|
extra.trailingComments.push(comment);
|
|
extra.leadingComments.push(comment);
|
|
},
|
|
|
|
processComment: function(node) {
|
|
var lastChild,
|
|
trailingComments,
|
|
i,
|
|
j;
|
|
|
|
if (node.type === astNodeTypes.Program) {
|
|
if (node.body.length > 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (extra.trailingComments.length > 0) {
|
|
|
|
/*
|
|
* If the first comment in trailingComments comes after the
|
|
* current node, then we're good - all comments in the array will
|
|
* come after the node and so it's safe to add then as official
|
|
* trailingComments.
|
|
*/
|
|
if (extra.trailingComments[0].range[0] >= node.range[1]) {
|
|
trailingComments = extra.trailingComments;
|
|
extra.trailingComments = [];
|
|
} else {
|
|
|
|
/*
|
|
* Otherwise, if the first comment doesn't come after the
|
|
* current node, that means we have a mix of leading and trailing
|
|
* comments in the array and that leadingComments contains the
|
|
* same items as trailingComments. Reset trailingComments to
|
|
* zero items and we'll handle this by evaluating leadingComments
|
|
* later.
|
|
*/
|
|
extra.trailingComments.length = 0;
|
|
}
|
|
} else {
|
|
if (extra.bottomRightStack.length > 0 &&
|
|
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments &&
|
|
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) {
|
|
trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
|
|
delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
|
|
}
|
|
}
|
|
|
|
// Eating the stack.
|
|
while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) {
|
|
lastChild = extra.bottomRightStack.pop();
|
|
}
|
|
|
|
if (lastChild) {
|
|
if (lastChild.leadingComments) {
|
|
if (lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) {
|
|
node.leadingComments = lastChild.leadingComments;
|
|
delete lastChild.leadingComments;
|
|
} else {
|
|
// A leading comment for an anonymous class had been stolen by its first MethodDefinition,
|
|
// so this takes back the leading comment.
|
|
// See Also: https://github.com/eslint/espree/issues/158
|
|
for (i = lastChild.leadingComments.length - 2; i >= 0; --i) {
|
|
if (lastChild.leadingComments[i].range[1] <= node.range[0]) {
|
|
node.leadingComments = lastChild.leadingComments.splice(0, i + 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (extra.leadingComments.length > 0) {
|
|
if (extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) {
|
|
if (extra.previousNode) {
|
|
for (j = 0; j < extra.leadingComments.length; j++) {
|
|
if (extra.leadingComments[j].end < extra.previousNode.end) {
|
|
extra.leadingComments.splice(j, 1);
|
|
j--;
|
|
}
|
|
}
|
|
}
|
|
if (extra.leadingComments.length > 0) {
|
|
node.leadingComments = extra.leadingComments;
|
|
extra.leadingComments = [];
|
|
}
|
|
} else {
|
|
|
|
// https://github.com/eslint/espree/issues/2
|
|
|
|
/*
|
|
* In special cases, such as return (without a value) and
|
|
* debugger, all comments will end up as leadingComments and
|
|
* will otherwise be eliminated. This extra step runs when the
|
|
* bottomRightStack is empty and there are comments left
|
|
* in leadingComments.
|
|
*
|
|
* This loop figures out the stopping point between the actual
|
|
* leading and trailing comments by finding the location of the
|
|
* first comment that comes after the given node.
|
|
*/
|
|
for (i = 0; i < extra.leadingComments.length; i++) {
|
|
if (extra.leadingComments[i].range[1] > node.range[0]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Split the array based on the location of the first comment
|
|
* that comes after the node. Keep in mind that this could
|
|
* result in an empty array, and if so, the array must be
|
|
* deleted.
|
|
*/
|
|
node.leadingComments = extra.leadingComments.slice(0, i);
|
|
if (node.leadingComments.length === 0) {
|
|
delete node.leadingComments;
|
|
}
|
|
|
|
/*
|
|
* Similarly, trailing comments are attached later. The variable
|
|
* must be reset to null if there are no trailing comments.
|
|
*/
|
|
trailingComments = extra.leadingComments.slice(i);
|
|
if (trailingComments.length === 0) {
|
|
trailingComments = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
extra.previousNode = node;
|
|
|
|
if (trailingComments) {
|
|
node.trailingComments = trailingComments;
|
|
}
|
|
|
|
extra.bottomRightStack.push(node);
|
|
}
|
|
|
|
};
|