3618bb3bf088239dd14054cd1fe2b95cc9ae7438
[platform/framework/web/crosswalk-tizen.git] /
1 /**
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>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
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.
15  *
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.
26  */
27
28 "use strict";
29
30 //------------------------------------------------------------------------------
31 // Requirements
32 //------------------------------------------------------------------------------
33
34 var astNodeTypes = require("./ast-node-types");
35
36 //------------------------------------------------------------------------------
37 // Private
38 //------------------------------------------------------------------------------
39
40 var extra = {
41         trailingComments: [],
42         leadingComments: [],
43         bottomRightStack: []
44     };
45
46 //------------------------------------------------------------------------------
47 // Public
48 //------------------------------------------------------------------------------
49
50 module.exports = {
51
52     reset: function() {
53         extra.trailingComments = [];
54         extra.leadingComments = [];
55         extra.bottomRightStack = [];
56     },
57
58     addComment: function(comment) {
59         extra.trailingComments.push(comment);
60         extra.leadingComments.push(comment);
61     },
62
63     processComment: function(node) {
64         var lastChild,
65             trailingComments,
66             i;
67
68         if (node.type === astNodeTypes.Program) {
69             if (node.body.length > 0) {
70                 return;
71             }
72         }
73
74         if (extra.trailingComments.length > 0) {
75
76             /*
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
80              * trailingComments.
81              */
82             if (extra.trailingComments[0].range[0] >= node.range[1]) {
83                 trailingComments = extra.trailingComments;
84                 extra.trailingComments = [];
85             } else {
86
87                 /*
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
93                  * later.
94                  */
95                 extra.trailingComments.length = 0;
96             }
97         } else {
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;
103             }
104         }
105
106         // Eating the stack.
107         while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) {
108             lastChild = extra.bottomRightStack.pop();
109         }
110
111         if (lastChild) {
112             if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) {
113                 node.leadingComments = lastChild.leadingComments;
114                 delete lastChild.leadingComments;
115             }
116         } else if (extra.leadingComments.length > 0) {
117
118             if (extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) {
119                 node.leadingComments = extra.leadingComments;
120                 extra.leadingComments = [];
121             } else {
122
123                 // https://github.com/eslint/espree/issues/2
124
125                 /*
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.
131                  *
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.
135                  */
136                 for (i = 0; i < extra.leadingComments.length; i++) {
137                     if (extra.leadingComments[i].range[1] > node.range[0]) {
138                         break;
139                     }
140                 }
141
142                 /*
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
146                  * deleted.
147                  */
148                 node.leadingComments = extra.leadingComments.slice(0, i);
149                 if (node.leadingComments.length === 0) {
150                     delete node.leadingComments;
151                 }
152
153                 /*
154                  * Similarly, trailing comments are attached later. The variable
155                  * must be reset to null if there are no trailing comments.
156                  */
157                 trailingComments = extra.leadingComments.slice(i);
158                 if (trailingComments.length === 0) {
159                     trailingComments = null;
160                 }
161             }
162         }
163
164         if (trailingComments) {
165             node.trailingComments = trailingComments;
166         }
167
168         extra.bottomRightStack.push(node);
169     }
170
171 };