.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / source-code / token-store / index.js
1 /**
2  * @fileoverview Object to handle access and retrieval of tokens.
3  * @author Brandon Mills
4  */
5 "use strict";
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 };