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