1 | /**
|
2 | * @fileoverview Object to handle access and retrieval of tokens.
|
3 | * @author Brandon Mills
|
4 | */
|
5 | ;
|
6 |
|
7 | //------------------------------------------------------------------------------
|
8 | // Requirements
|
9 | //------------------------------------------------------------------------------
|
10 |
|
11 | const assert = require("assert");
|
12 | const cursors = require("./cursors");
|
13 | const ForwardTokenCursor = require("./forward-token-cursor");
|
14 | const PaddedTokenCursor = require("./padded-token-cursor");
|
15 | const utils = require("./utils");
|
16 | const astUtils = require("../ast-utils");
|
17 |
|
18 | //------------------------------------------------------------------------------
|
19 | // Helpers
|
20 | //------------------------------------------------------------------------------
|
21 |
|
22 | const TOKENS = Symbol("tokens");
|
23 | const COMMENTS = Symbol("comments");
|
24 | const INDEX_MAP = Symbol("indexMap");
|
25 |
|
26 | /**
|
27 | * Creates the map from locations to indices in `tokens`.
|
28 | *
|
29 | * The first/last location of tokens is mapped to the index of the token.
|
30 | * The first/last location of comments is mapped to the index of the next token of each comment.
|
31 | *
|
32 | * @param {Token[]} tokens - The array of tokens.
|
33 | * @param {Comment[]} comments - The array of comments.
|
34 | * @returns {Object} The map from locations to indices in `tokens`.
|
35 | * @private
|
36 | */
|
37 | function createIndexMap(tokens, comments) {
|
38 | const map = Object.create(null);
|
39 | let tokenIndex = 0;
|
40 | let commentIndex = 0;
|
41 | let nextStart = 0;
|
42 | let range = null;
|
43 |
|
44 | while (tokenIndex < tokens.length || commentIndex < comments.length) {
|
45 | nextStart = (commentIndex < comments.length) ? comments[commentIndex].range[0] : Number.MAX_SAFE_INTEGER;
|
46 | while (tokenIndex < tokens.length && (range = tokens[tokenIndex].range)[0] < nextStart) {
|
47 | map[range[0]] = tokenIndex;
|
48 | map[range[1] - 1] = tokenIndex;
|
49 | tokenIndex += 1;
|
50 | }
|
51 |
|
52 | nextStart = (tokenIndex < tokens.length) ? tokens[tokenIndex].range[0] : Number.MAX_SAFE_INTEGER;
|
53 | while (commentIndex < comments.length && (range = comments[commentIndex].range)[0] < nextStart) {
|
54 | map[range[0]] = tokenIndex;
|
55 | map[range[1] - 1] = tokenIndex;
|
56 | commentIndex += 1;
|
57 | }
|
58 | }
|
59 |
|
60 | return map;
|
61 | }
|
62 |
|
63 | /**
|
64 | * Creates the cursor iterates tokens with options.
|
65 | *
|
66 | * @param {CursorFactory} factory - The cursor factory to initialize cursor.
|
67 | * @param {Token[]} tokens - The array of tokens.
|
68 | * @param {Comment[]} comments - The array of comments.
|
69 | * @param {Object} indexMap - The map from locations to indices in `tokens`.
|
70 | * @param {number} startLoc - The start location of the iteration range.
|
71 | * @param {number} endLoc - The end location of the iteration range.
|
72 | * @param {number|Function|Object} [opts=0] - The option object. If this is a number then it's `opts.skip`. If this is a function then it's `opts.filter`.
|
73 | * @param {boolean} [opts.includeComments=false] - The flag to iterate comments as well.
|
74 | * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
|
75 | * @param {number} [opts.skip=0] - The count of tokens the cursor skips.
|
76 | * @returns {Cursor} The created cursor.
|
77 | * @private
|
78 | */
|
79 | function createCursorWithSkip(factory, tokens, comments, indexMap, startLoc, endLoc, opts) {
|
80 | let includeComments = false;
|
81 | let skip = 0;
|
82 | let filter = null;
|
83 |
|
84 | if (typeof opts === "number") {
|
85 | skip = opts | 0;
|
86 | } else if (typeof opts === "function") {
|
87 | filter = opts;
|
88 | } else if (opts) {
|
89 | includeComments = !!opts.includeComments;
|
90 | skip = opts.skip | 0;
|
91 | filter = opts.filter || null;
|
92 | }
|
93 | assert(skip >= 0, "options.skip should be zero or a positive integer.");
|
94 | assert(!filter || typeof filter === "function", "options.filter should be a function.");
|
95 |
|
96 | return factory.createCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments, filter, skip, -1);
|
97 | }
|
98 |
|
99 | /**
|
100 | * Creates the cursor iterates tokens with options.
|
101 | *
|
102 | * @param {CursorFactory} factory - The cursor factory to initialize cursor.
|
103 | * @param {Token[]} tokens - The array of tokens.
|
104 | * @param {Comment[]} comments - The array of comments.
|
105 | * @param {Object} indexMap - The map from locations to indices in `tokens`.
|
106 | * @param {number} startLoc - The start location of the iteration range.
|
107 | * @param {number} endLoc - The end location of the iteration range.
|
108 | * @param {number|Function|Object} [opts=0] - The option object. If this is a number then it's `opts.count`. If this is a function then it's `opts.filter`.
|
109 | * @param {boolean} [opts.includeComments] - The flag to iterate comments as well.
|
110 | * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
|
111 | * @param {number} [opts.count=0] - The maximum count of tokens the cursor iterates. Zero is no iteration for backward compatibility.
|
112 | * @returns {Cursor} The created cursor.
|
113 | * @private
|
114 | */
|
115 | function createCursorWithCount(factory, tokens, comments, indexMap, startLoc, endLoc, opts) {
|
116 | let includeComments = false;
|
117 | let count = 0;
|
118 | let countExists = false;
|
119 | let filter = null;
|
120 |
|
121 | if (typeof opts === "number") {
|
122 | count = opts | 0;
|
123 | countExists = true;
|
124 | } else if (typeof opts === "function") {
|
125 | filter = opts;
|
126 | } else if (opts) {
|
127 | includeComments = !!opts.includeComments;
|
128 | count = opts.count | 0;
|
129 | countExists = typeof opts.count === "number";
|
130 | filter = opts.filter || null;
|
131 | }
|
132 | assert(count >= 0, "options.count should be zero or a positive integer.");
|
133 | assert(!filter || typeof filter === "function", "options.filter should be a function.");
|
134 |
|
135 | return factory.createCursor(tokens, comments, indexMap, startLoc, endLoc, includeComments, filter, 0, countExists ? count : -1);
|
136 | }
|
137 |
|
138 | /**
|
139 | * Creates the cursor iterates tokens with options.
|
140 | * This is overload function of the below.
|
141 | *
|
142 | * @param {Token[]} tokens - The array of tokens.
|
143 | * @param {Comment[]} comments - The array of comments.
|
144 | * @param {Object} indexMap - The map from locations to indices in `tokens`.
|
145 | * @param {number} startLoc - The start location of the iteration range.
|
146 | * @param {number} endLoc - The end location of the iteration range.
|
147 | * @param {Function|Object} opts - The option object. If this is a function then it's `opts.filter`.
|
148 | * @param {boolean} [opts.includeComments] - The flag to iterate comments as well.
|
149 | * @param {Function|null} [opts.filter=null] - The predicate function to choose tokens.
|
150 | * @param {number} [opts.count=0] - The maximum count of tokens the cursor iterates. Zero is no iteration for backward compatibility.
|
151 | * @returns {Cursor} The created cursor.
|
152 | * @private
|
153 | */
|
154 | /**
|
155 | * Creates the cursor iterates tokens with options.
|
156 | *
|
157 | * @param {Token[]} tokens - The array of tokens.
|
158 | * @param {Comment[]} comments - The array of comments.
|
159 | * @param {Object} indexMap - The map from locations to indices in `tokens`.
|
160 | * @param {number} startLoc - The start location of the iteration range.
|
161 | * @param {number} endLoc - The end location of the iteration range.
|
162 | * @param {number} [beforeCount=0] - The number of tokens before the node to retrieve.
|
163 | * @param {boolean} [afterCount=0] - The number of tokens after the node to retrieve.
|
164 | * @returns {Cursor} The created cursor.
|
165 | * @private
|
166 | */
|
167 | function createCursorWithPadding(tokens, comments, indexMap, startLoc, endLoc, beforeCount, afterCount) {
|
168 | if (typeof beforeCount === "undefined" && typeof afterCount === "undefined") {
|
169 | return new ForwardTokenCursor(tokens, comments, indexMap, startLoc, endLoc);
|
170 | }
|
171 | if (typeof beforeCount === "number" || typeof beforeCount === "undefined") {
|
172 | return new PaddedTokenCursor(tokens, comments, indexMap, startLoc, endLoc, beforeCount | 0, afterCount | 0);
|
173 | }
|
174 | return createCursorWithCount(cursors.forward, tokens, comments, indexMap, startLoc, endLoc, beforeCount);
|
175 | }
|
176 |
|
177 | /**
|
178 | * Gets comment tokens that are adjacent to the current cursor position.
|
179 | * @param {Cursor} cursor - A cursor instance.
|
180 | * @returns {Array} An array of comment tokens adjacent to the current cursor position.
|
181 | * @private
|
182 | */
|
183 | function getAdjacentCommentTokensFromCursor(cursor) {
|
184 | const tokens = [];
|
185 | let currentToken = cursor.getOneToken();
|
186 |
|
187 | while (currentToken && astUtils.isCommentToken(currentToken)) {
|
188 | tokens.push(currentToken);
|
189 | currentToken = cursor.getOneToken();
|
190 | }
|
191 |
|
192 | return tokens;
|
193 | }
|
194 |
|
195 | //------------------------------------------------------------------------------
|
196 | // Exports
|
197 | //------------------------------------------------------------------------------
|
198 |
|
199 | /**
|
200 | * The token store.
|
201 | *
|
202 | * This class provides methods to get tokens by locations as fast as possible.
|
203 | * The methods are a part of public API, so we should be careful if it changes this class.
|
204 | *
|
205 | * People can get tokens in O(1) by the hash map which is mapping from the location of tokens/comments to tokens.
|
206 | * Also people can get a mix of tokens and comments in O(log k), the k is the number of comments.
|
207 | * Assuming that comments to be much fewer than tokens, this does not make hash map from token's locations to comments to reduce memory cost.
|
208 | * This uses binary-searching instead for comments.
|
209 | */
|
210 | module.exports = class TokenStore {
|
211 |
|
212 | /**
|
213 | * Initializes this token store.
|
214 | * @param {Token[]} tokens - The array of tokens.
|
215 | * @param {Comment[]} comments - The array of comments.
|
216 | */
|
217 | constructor(tokens, comments) {
|
218 | this[TOKENS] = tokens;
|
219 | this[COMMENTS] = comments;
|
220 | this[INDEX_MAP] = createIndexMap(tokens, comments);
|
221 | }
|
222 |
|
223 | //--------------------------------------------------------------------------
|
224 | // Gets single token.
|
225 | //--------------------------------------------------------------------------
|
226 |
|
227 | /**
|
228 | * Gets the token starting at the specified index.
|
229 | * @param {number} offset - Index of the start of the token's range.
|
230 | * @param {Object} [options=0] - The option object.
|
231 | * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
|
232 | * @returns {Token|null} The token starting at index, or null if no such token.
|
233 | */
|
234 | getTokenByRangeStart(offset, options) {
|
235 | const includeComments = options && options.includeComments;
|
236 | const token = cursors.forward.createBaseCursor(
|
237 | this[TOKENS],
|
238 | this[COMMENTS],
|
239 | this[INDEX_MAP],
|
240 | offset,
|
241 | -1,
|
242 | includeComments
|
243 | ).getOneToken();
|
244 |
|
245 | if (token && token.range[0] === offset) {
|
246 | return token;
|
247 | }
|
248 | return null;
|
249 | }
|
250 |
|
251 | /**
|
252 | * Gets the first token of the given node.
|
253 | * @param {ASTNode} node - The AST node.
|
254 | * @param {number|Function|Object} [options=0] - The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`.
|
255 | * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
|
256 | * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
|
257 | * @param {number} [options.skip=0] - The count of tokens the cursor skips.
|
258 | * @returns {Token|null} An object representing the token.
|
259 | */
|
260 | getFirstToken(node, options) {
|
261 | return createCursorWithSkip(
|
262 | cursors.forward,
|
263 | this[TOKENS],
|
264 | this[COMMENTS],
|
265 | this[INDEX_MAP],
|
266 | node.range[0],
|
267 | node.range[1],
|
268 | options
|
269 | ).getOneToken();
|
270 | }
|
271 |
|
272 | /**
|
273 | * Gets the last token of the given node.
|
274 | * @param {ASTNode} node - The AST node.
|
275 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
|
276 | * @returns {Token|null} An object representing the token.
|
277 | */
|
278 | getLastToken(node, options) {
|
279 | return createCursorWithSkip(
|
280 | cursors.backward,
|
281 | this[TOKENS],
|
282 | this[COMMENTS],
|
283 | this[INDEX_MAP],
|
284 | node.range[0],
|
285 | node.range[1],
|
286 | options
|
287 | ).getOneToken();
|
288 | }
|
289 |
|
290 | /**
|
291 | * Gets the token that precedes a given node or token.
|
292 | * @param {ASTNode|Token|Comment} node - The AST node or token.
|
293 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
|
294 | * @returns {Token|null} An object representing the token.
|
295 | */
|
296 | getTokenBefore(node, options) {
|
297 | return createCursorWithSkip(
|
298 | cursors.backward,
|
299 | this[TOKENS],
|
300 | this[COMMENTS],
|
301 | this[INDEX_MAP],
|
302 | -1,
|
303 | node.range[0],
|
304 | options
|
305 | ).getOneToken();
|
306 | }
|
307 |
|
308 | /**
|
309 | * Gets the token that follows a given node or token.
|
310 | * @param {ASTNode|Token|Comment} node - The AST node or token.
|
311 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
|
312 | * @returns {Token|null} An object representing the token.
|
313 | */
|
314 | getTokenAfter(node, options) {
|
315 | return createCursorWithSkip(
|
316 | cursors.forward,
|
317 | this[TOKENS],
|
318 | this[COMMENTS],
|
319 | this[INDEX_MAP],
|
320 | node.range[1],
|
321 | -1,
|
322 | options
|
323 | ).getOneToken();
|
324 | }
|
325 |
|
326 | /**
|
327 | * Gets the first token between two non-overlapping nodes.
|
328 | * @param {ASTNode|Token|Comment} left - Node before the desired token range.
|
329 | * @param {ASTNode|Token|Comment} right - Node after the desired token range.
|
330 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
|
331 | * @returns {Token|null} An object representing the token.
|
332 | */
|
333 | getFirstTokenBetween(left, right, options) {
|
334 | return createCursorWithSkip(
|
335 | cursors.forward,
|
336 | this[TOKENS],
|
337 | this[COMMENTS],
|
338 | this[INDEX_MAP],
|
339 | left.range[1],
|
340 | right.range[0],
|
341 | options
|
342 | ).getOneToken();
|
343 | }
|
344 |
|
345 | /**
|
346 | * Gets the last token between two non-overlapping nodes.
|
347 | * @param {ASTNode|Token|Comment} left Node before the desired token range.
|
348 | * @param {ASTNode|Token|Comment} right Node after the desired token range.
|
349 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstToken()
|
350 | * @returns {Token|null} An object representing the token.
|
351 | */
|
352 | getLastTokenBetween(left, right, options) {
|
353 | return createCursorWithSkip(
|
354 | cursors.backward,
|
355 | this[TOKENS],
|
356 | this[COMMENTS],
|
357 | this[INDEX_MAP],
|
358 | left.range[1],
|
359 | right.range[0],
|
360 | options
|
361 | ).getOneToken();
|
362 | }
|
363 |
|
364 | /**
|
365 | * Gets the token that precedes a given node or token in the token stream.
|
366 | * This is defined for backward compatibility. Use `includeComments` option instead.
|
367 | * TODO: We have a plan to remove this in a future major version.
|
368 | * @param {ASTNode|Token|Comment} node The AST node or token.
|
369 | * @param {number} [skip=0] A number of tokens to skip.
|
370 | * @returns {Token|null} An object representing the token.
|
371 | * @deprecated
|
372 | */
|
373 | getTokenOrCommentBefore(node, skip) {
|
374 | return this.getTokenBefore(node, { includeComments: true, skip });
|
375 | }
|
376 |
|
377 | /**
|
378 | * Gets the token that follows a given node or token in the token stream.
|
379 | * This is defined for backward compatibility. Use `includeComments` option instead.
|
380 | * TODO: We have a plan to remove this in a future major version.
|
381 | * @param {ASTNode|Token|Comment} node The AST node or token.
|
382 | * @param {number} [skip=0] A number of tokens to skip.
|
383 | * @returns {Token|null} An object representing the token.
|
384 | * @deprecated
|
385 | */
|
386 | getTokenOrCommentAfter(node, skip) {
|
387 | return this.getTokenAfter(node, { includeComments: true, skip });
|
388 | }
|
389 |
|
390 | //--------------------------------------------------------------------------
|
391 | // Gets multiple tokens.
|
392 | //--------------------------------------------------------------------------
|
393 |
|
394 | /**
|
395 | * Gets the first `count` tokens of the given node.
|
396 | * @param {ASTNode} node - The AST node.
|
397 | * @param {number|Function|Object} [options=0] - The option object. If this is a number then it's `options.count`. If this is a function then it's `options.filter`.
|
398 | * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
|
399 | * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
|
400 | * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
|
401 | * @returns {Token[]} Tokens.
|
402 | */
|
403 | getFirstTokens(node, options) {
|
404 | return createCursorWithCount(
|
405 | cursors.forward,
|
406 | this[TOKENS],
|
407 | this[COMMENTS],
|
408 | this[INDEX_MAP],
|
409 | node.range[0],
|
410 | node.range[1],
|
411 | options
|
412 | ).getAllTokens();
|
413 | }
|
414 |
|
415 | /**
|
416 | * Gets the last `count` tokens of the given node.
|
417 | * @param {ASTNode} node - The AST node.
|
418 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
|
419 | * @returns {Token[]} Tokens.
|
420 | */
|
421 | getLastTokens(node, options) {
|
422 | return createCursorWithCount(
|
423 | cursors.backward,
|
424 | this[TOKENS],
|
425 | this[COMMENTS],
|
426 | this[INDEX_MAP],
|
427 | node.range[0],
|
428 | node.range[1],
|
429 | options
|
430 | ).getAllTokens().reverse();
|
431 | }
|
432 |
|
433 | /**
|
434 | * Gets the `count` tokens that precedes a given node or token.
|
435 | * @param {ASTNode|Token|Comment} node - The AST node or token.
|
436 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
|
437 | * @returns {Token[]} Tokens.
|
438 | */
|
439 | getTokensBefore(node, options) {
|
440 | return createCursorWithCount(
|
441 | cursors.backward,
|
442 | this[TOKENS],
|
443 | this[COMMENTS],
|
444 | this[INDEX_MAP],
|
445 | -1,
|
446 | node.range[0],
|
447 | options
|
448 | ).getAllTokens().reverse();
|
449 | }
|
450 |
|
451 | /**
|
452 | * Gets the `count` tokens that follows a given node or token.
|
453 | * @param {ASTNode|Token|Comment} node - The AST node or token.
|
454 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
|
455 | * @returns {Token[]} Tokens.
|
456 | */
|
457 | getTokensAfter(node, options) {
|
458 | return createCursorWithCount(
|
459 | cursors.forward,
|
460 | this[TOKENS],
|
461 | this[COMMENTS],
|
462 | this[INDEX_MAP],
|
463 | node.range[1],
|
464 | -1,
|
465 | options
|
466 | ).getAllTokens();
|
467 | }
|
468 |
|
469 | /**
|
470 | * Gets the first `count` tokens between two non-overlapping nodes.
|
471 | * @param {ASTNode|Token|Comment} left - Node before the desired token range.
|
472 | * @param {ASTNode|Token|Comment} right - Node after the desired token range.
|
473 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
|
474 | * @returns {Token[]} Tokens between left and right.
|
475 | */
|
476 | getFirstTokensBetween(left, right, options) {
|
477 | return createCursorWithCount(
|
478 | cursors.forward,
|
479 | this[TOKENS],
|
480 | this[COMMENTS],
|
481 | this[INDEX_MAP],
|
482 | left.range[1],
|
483 | right.range[0],
|
484 | options
|
485 | ).getAllTokens();
|
486 | }
|
487 |
|
488 | /**
|
489 | * Gets the last `count` tokens between two non-overlapping nodes.
|
490 | * @param {ASTNode|Token|Comment} left Node before the desired token range.
|
491 | * @param {ASTNode|Token|Comment} right Node after the desired token range.
|
492 | * @param {number|Function|Object} [options=0] - The option object. Same options as getFirstTokens()
|
493 | * @returns {Token[]} Tokens between left and right.
|
494 | */
|
495 | getLastTokensBetween(left, right, options) {
|
496 | return createCursorWithCount(
|
497 | cursors.backward,
|
498 | this[TOKENS],
|
499 | this[COMMENTS],
|
500 | this[INDEX_MAP],
|
501 | left.range[1],
|
502 | right.range[0],
|
503 | options
|
504 | ).getAllTokens().reverse();
|
505 | }
|
506 |
|
507 | /**
|
508 | * Gets all tokens that are related to the given node.
|
509 | * @param {ASTNode} node - The AST node.
|
510 | * @param {Function|Object} options The option object. If this is a function then it's `options.filter`.
|
511 | * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
|
512 | * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
|
513 | * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
|
514 | * @returns {Token[]} Array of objects representing tokens.
|
515 | */
|
516 | /**
|
517 | * Gets all tokens that are related to the given node.
|
518 | * @param {ASTNode} node - The AST node.
|
519 | * @param {int} [beforeCount=0] - The number of tokens before the node to retrieve.
|
520 | * @param {int} [afterCount=0] - The number of tokens after the node to retrieve.
|
521 | * @returns {Token[]} Array of objects representing tokens.
|
522 | */
|
523 | getTokens(node, beforeCount, afterCount) {
|
524 | return createCursorWithPadding(
|
525 | this[TOKENS],
|
526 | this[COMMENTS],
|
527 | this[INDEX_MAP],
|
528 | node.range[0],
|
529 | node.range[1],
|
530 | beforeCount,
|
531 | afterCount
|
532 | ).getAllTokens();
|
533 | }
|
534 |
|
535 | /**
|
536 | * Gets all of the tokens between two non-overlapping nodes.
|
537 | * @param {ASTNode|Token|Comment} left Node before the desired token range.
|
538 | * @param {ASTNode|Token|Comment} right Node after the desired token range.
|
539 | * @param {Function|Object} options The option object. If this is a function then it's `options.filter`.
|
540 | * @param {boolean} [options.includeComments=false] - The flag to iterate comments as well.
|
541 | * @param {Function|null} [options.filter=null] - The predicate function to choose tokens.
|
542 | * @param {number} [options.count=0] - The maximum count of tokens the cursor iterates.
|
543 | * @returns {Token[]} Tokens between left and right.
|
544 | */
|
545 | /**
|
546 | * Gets all of the tokens between two non-overlapping nodes.
|
547 | * @param {ASTNode|Token|Comment} left Node before the desired token range.
|
548 | * @param {ASTNode|Token|Comment} right Node after the desired token range.
|
549 | * @param {int} [padding=0] Number of extra tokens on either side of center.
|
550 | * @returns {Token[]} Tokens between left and right.
|
551 | */
|
552 | getTokensBetween(left, right, padding) {
|
553 | return createCursorWithPadding(
|
554 | this[TOKENS],
|
555 | this[COMMENTS],
|
556 | this[INDEX_MAP],
|
557 | left.range[1],
|
558 | right.range[0],
|
559 | padding,
|
560 | padding
|
561 | ).getAllTokens();
|
562 | }
|
563 |
|
564 | //--------------------------------------------------------------------------
|
565 | // Others.
|
566 | //--------------------------------------------------------------------------
|
567 |
|
568 | /**
|
569 | * Checks whether any comments exist or not between the given 2 nodes.
|
570 | *
|
571 | * @param {ASTNode} left - The node to check.
|
572 | * @param {ASTNode} right - The node to check.
|
573 | * @returns {boolean} `true` if one or more comments exist.
|
574 | */
|
575 | commentsExistBetween(left, right) {
|
576 | const index = utils.search(this[COMMENTS], left.range[1]);
|
577 |
|
578 | return (
|
579 | index < this[COMMENTS].length &&
|
580 | this[COMMENTS][index].range[1] <= right.range[0]
|
581 | );
|
582 | }
|
583 |
|
584 | /**
|
585 | * Gets all comment tokens directly before the given node or token.
|
586 | * @param {ASTNode|token} nodeOrToken The AST node or token to check for adjacent comment tokens.
|
587 | * @returns {Array} An array of comments in occurrence order.
|
588 | */
|
589 | getCommentsBefore(nodeOrToken) {
|
590 | const cursor = createCursorWithCount(
|
591 | cursors.backward,
|
592 | this[TOKENS],
|
593 | this[COMMENTS],
|
594 | this[INDEX_MAP],
|
595 | -1,
|
596 | nodeOrToken.range[0],
|
597 | { includeComments: true }
|
598 | );
|
599 |
|
600 | return getAdjacentCommentTokensFromCursor(cursor).reverse();
|
601 | }
|
602 |
|
603 | /**
|
604 | * Gets all comment tokens directly after the given node or token.
|
605 | * @param {ASTNode|token} nodeOrToken The AST node or token to check for adjacent comment tokens.
|
606 | * @returns {Array} An array of comments in occurrence order.
|
607 | */
|
608 | getCommentsAfter(nodeOrToken) {
|
609 | const cursor = createCursorWithCount(
|
610 | cursors.forward,
|
611 | this[TOKENS],
|
612 | this[COMMENTS],
|
613 | this[INDEX_MAP],
|
614 | nodeOrToken.range[1],
|
615 | -1,
|
616 | { includeComments: true }
|
617 | );
|
618 |
|
619 | return getAdjacentCommentTokensFromCursor(cursor);
|
620 | }
|
621 |
|
622 | /**
|
623 | * Gets all comment tokens inside the given node.
|
624 | * @param {ASTNode} node The AST node to get the comments for.
|
625 | * @returns {Array} An array of comments in occurrence order.
|
626 | */
|
627 | getCommentsInside(node) {
|
628 | return this.getTokens(node, {
|
629 | includeComments: true,
|
630 | filter: astUtils.isCommentToken
|
631 | });
|
632 | }
|
633 | };
|