2 * @fileoverview Attaches comments to the AST.
3 * @author Nicholas C. Zakas
4 * @copyright 2015 Nicholas C. Zakas. All rights reserved.
5 * @copyright 2011-2013 Ariya Hidayat <ariya.hidayat@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 //------------------------------------------------------------------------------
32 //------------------------------------------------------------------------------
34 var astNodeTypes = require("./ast-node-types");
36 //------------------------------------------------------------------------------
38 //------------------------------------------------------------------------------
46 //------------------------------------------------------------------------------
48 //------------------------------------------------------------------------------
53 extra.trailingComments = [];
54 extra.leadingComments = [];
55 extra.bottomRightStack = [];
58 addComment: function(comment) {
59 extra.trailingComments.push(comment);
60 extra.leadingComments.push(comment);
63 processComment: function(node) {
68 if (node.type === astNodeTypes.Program) {
69 if (node.body.length > 0) {
74 if (extra.trailingComments.length > 0) {
77 * If the first comment in trailingComments comes after the
78 * current node, then we're good - all comments in the array will
79 * come after the node and so it's safe to add then as official
82 if (extra.trailingComments[0].range[0] >= node.range[1]) {
83 trailingComments = extra.trailingComments;
84 extra.trailingComments = [];
88 * Otherwise, if the first comment doesn't come after the
89 * current node, that means we have a mix of leading and trailing
90 * comments in the array and that leadingComments contains the
91 * same items as trailingComments. Reset trailingComments to
92 * zero items and we'll handle this by evaluating leadingComments
95 extra.trailingComments.length = 0;
98 if (extra.bottomRightStack.length > 0 &&
99 extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments &&
100 extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) {
101 trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
102 delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
107 while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) {
108 lastChild = extra.bottomRightStack.pop();
112 if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) {
113 node.leadingComments = lastChild.leadingComments;
114 delete lastChild.leadingComments;
116 } else if (extra.leadingComments.length > 0) {
118 if (extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) {
119 node.leadingComments = extra.leadingComments;
120 extra.leadingComments = [];
123 // https://github.com/eslint/espree/issues/2
126 * In special cases, such as return (without a value) and
127 * debugger, all comments will end up as leadingComments and
128 * will otherwise be eliminated. This extra step runs when the
129 * bottomRightStack is empty and there are comments left
130 * in leadingComments.
132 * This loop figures out the stopping point between the actual
133 * leading and trailing comments by finding the location of the
134 * first comment that comes after the given node.
136 for (i = 0; i < extra.leadingComments.length; i++) {
137 if (extra.leadingComments[i].range[1] > node.range[0]) {
143 * Split the array based on the location of the first comment
144 * that comes after the node. Keep in mind that this could
145 * result in an empty array, and if so, the array must be
148 node.leadingComments = extra.leadingComments.slice(0, i);
149 if (node.leadingComments.length === 0) {
150 delete node.leadingComments;
154 * Similarly, trailing comments are attached later. The variable
155 * must be reset to null if there are no trailing comments.
157 trailingComments = extra.leadingComments.slice(i);
158 if (trailingComments.length === 0) {
159 trailingComments = null;
164 if (trailingComments) {
165 node.trailingComments = trailingComments;
168 extra.bottomRightStack.push(node);