• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    ArrayLiteralExpression, BinaryExpression, BindingPattern, Block, BreakOrContinueStatement, canHaveDecorators,
3    CaseBlock, CaseOrDefaultClause, CatchClause, ClassDeclaration, createTextSpanFromBounds, Debug,
4    DestructuringPattern, DoStatement, EnumDeclaration, ExportAssignment, ExportDeclaration, Expression,
5    ExpressionStatement, findLast, findNextToken, findPrecedingToken, forEach, ForInStatement, ForOfStatement,
6    ForStatement, FunctionLikeDeclaration, getModuleInstanceState, getTokenAtPosition, HasDecorators,
7    hasOnlyExpressionInitializer, hasSyntacticModifier, IfStatement, ImportDeclaration, ImportEqualsDeclaration,
8    isArrayLiteralOrObjectLiteralDestructuringPattern, isAssignmentOperator, isBindingPattern, isDecorator,
9    isExpressionNode, isFunctionBlock, isFunctionLike, isVariableDeclarationList, LabeledStatement, lastOrUndefined,
10    ModifierFlags, ModuleDeclaration, ModuleInstanceState, Node, NodeArray, NodeFlags, ObjectLiteralElement,
11    ObjectLiteralExpression, ParameterDeclaration, PropertyAssignment, PropertyDeclaration, PropertySignature,
12    ReturnStatement, skipTrivia, SourceFile, SwitchStatement, SyntaxKind, TextSpan, ThrowStatement, TryStatement,
13    TypeAssertion, VariableDeclaration, VariableDeclarationList, VariableStatement, WhileStatement, WithStatement,
14} from "./_namespaces/ts";
15
16/**
17 * Get the breakpoint span in given sourceFile
18 *
19 * @internal
20 */
21export function spanInSourceFileAtLocation(sourceFile: SourceFile, position: number) {
22    // Cannot set breakpoint in dts file
23    if (sourceFile.isDeclarationFile) {
24        return undefined;
25    }
26
27    let tokenAtLocation = getTokenAtPosition(sourceFile, position);
28    const lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
29    if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
30        // Get previous token if the token is returned starts on new line
31        // eg: let x =10; |--- cursor is here
32        //     let y = 10;
33        // token at position will return let keyword on second line as the token but we would like to use
34        // token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
35        const preceding = findPrecedingToken(tokenAtLocation.pos, sourceFile);
36
37        // It's a blank line
38        if (!preceding || sourceFile.getLineAndCharacterOfPosition(preceding.getEnd()).line !== lineOfPosition) {
39            return undefined;
40        }
41        tokenAtLocation = preceding;
42    }
43
44    // Cannot set breakpoint in ambient declarations
45    if (tokenAtLocation.flags & NodeFlags.Ambient) {
46        return undefined;
47    }
48
49    // Get the span in the node based on its syntax
50    return spanInNode(tokenAtLocation);
51
52    function textSpan(startNode: Node, endNode?: Node) {
53        const lastDecorator = canHaveDecorators(startNode) ? findLast(startNode.modifiers, isDecorator) : undefined;
54        const start = lastDecorator ?
55            skipTrivia(sourceFile.text, lastDecorator.end) :
56            startNode.getStart(sourceFile);
57        return createTextSpanFromBounds(start, (endNode || startNode).getEnd());
58    }
59
60    function textSpanEndingAtNextToken(startNode: Node, previousTokenToFindNextEndToken: Node): TextSpan {
61        return textSpan(startNode, findNextToken(previousTokenToFindNextEndToken, previousTokenToFindNextEndToken.parent, sourceFile));
62    }
63
64    function spanInNodeIfStartsOnSameLine(node: Node | undefined, otherwiseOnNode?: Node): TextSpan | undefined {
65        if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line) {
66            return spanInNode(node);
67        }
68        return spanInNode(otherwiseOnNode);
69    }
70
71    function spanInNodeArray<T extends Node>(nodeArray: NodeArray<T> | undefined, node: T, match: (value: Node) => boolean) {
72        if (nodeArray) {
73            const index = nodeArray.indexOf(node);
74            if (index >= 0) {
75                let start = index;
76                let end = index + 1;
77                while (start > 0 && match(nodeArray[start - 1])) start--;
78                while (end < nodeArray.length && match(nodeArray[end])) end++;
79                return createTextSpanFromBounds(skipTrivia(sourceFile.text, nodeArray[start].pos), nodeArray[end - 1].end);
80            }
81        }
82        return textSpan(node);
83    }
84
85    function spanInPreviousNode(node: Node): TextSpan | undefined {
86        return spanInNode(findPrecedingToken(node.pos, sourceFile));
87    }
88
89    function spanInNextNode(node: Node): TextSpan | undefined {
90        return spanInNode(findNextToken(node, node.parent, sourceFile));
91    }
92
93    function spanInNode(node: Node | undefined): TextSpan | undefined {
94        if (node) {
95            const { parent } = node;
96            switch (node.kind) {
97                case SyntaxKind.VariableStatement:
98                    // Span on first variable declaration
99                    return spanInVariableDeclaration((node as VariableStatement).declarationList.declarations[0]);
100
101                case SyntaxKind.VariableDeclaration:
102                case SyntaxKind.PropertyDeclaration:
103                case SyntaxKind.PropertySignature:
104                    return spanInVariableDeclaration(node as VariableDeclaration | PropertyDeclaration | PropertySignature);
105
106                case SyntaxKind.Parameter:
107                    return spanInParameterDeclaration(node as ParameterDeclaration);
108
109                case SyntaxKind.FunctionDeclaration:
110                case SyntaxKind.MethodDeclaration:
111                case SyntaxKind.MethodSignature:
112                case SyntaxKind.GetAccessor:
113                case SyntaxKind.SetAccessor:
114                case SyntaxKind.Constructor:
115                case SyntaxKind.FunctionExpression:
116                case SyntaxKind.ArrowFunction:
117                    return spanInFunctionDeclaration(node as FunctionLikeDeclaration);
118
119                case SyntaxKind.Block:
120                    if (isFunctionBlock(node)) {
121                        return spanInFunctionBlock(node as Block);
122                    }
123                    // falls through
124                case SyntaxKind.ModuleBlock:
125                    return spanInBlock(node as Block);
126
127                case SyntaxKind.CatchClause:
128                    return spanInBlock((node as CatchClause).block);
129
130                case SyntaxKind.ExpressionStatement:
131                    // span on the expression
132                    return textSpan((node as ExpressionStatement).expression);
133
134                case SyntaxKind.ReturnStatement:
135                    // span on return keyword and expression if present
136                    return textSpan(node.getChildAt(0), (node as ReturnStatement).expression);
137
138                case SyntaxKind.WhileStatement:
139                    // Span on while(...)
140                    return textSpanEndingAtNextToken(node, (node as WhileStatement).expression);
141
142                case SyntaxKind.DoStatement:
143                    // span in statement of the do statement
144                    return spanInNode((node as DoStatement).statement);
145
146                case SyntaxKind.DebuggerStatement:
147                    // span on debugger keyword
148                    return textSpan(node.getChildAt(0));
149
150                case SyntaxKind.IfStatement:
151                    // set on if(..) span
152                    return textSpanEndingAtNextToken(node, (node as IfStatement).expression);
153
154                case SyntaxKind.LabeledStatement:
155                    // span in statement
156                    return spanInNode((node as LabeledStatement).statement);
157
158                case SyntaxKind.BreakStatement:
159                case SyntaxKind.ContinueStatement:
160                    // On break or continue keyword and label if present
161                    return textSpan(node.getChildAt(0), (node as BreakOrContinueStatement).label);
162
163                case SyntaxKind.ForStatement:
164                    return spanInForStatement(node as ForStatement);
165
166                case SyntaxKind.ForInStatement:
167                    // span of for (a in ...)
168                    return textSpanEndingAtNextToken(node, (node as ForInStatement).expression);
169
170                case SyntaxKind.ForOfStatement:
171                    // span in initializer
172                    return spanInInitializerOfForLike(node as ForOfStatement);
173
174                case SyntaxKind.SwitchStatement:
175                    // span on switch(...)
176                    return textSpanEndingAtNextToken(node, (node as SwitchStatement).expression);
177
178                case SyntaxKind.CaseClause:
179                case SyntaxKind.DefaultClause:
180                    // span in first statement of the clause
181                    return spanInNode((node as CaseOrDefaultClause).statements[0]);
182
183                case SyntaxKind.TryStatement:
184                    // span in try block
185                    return spanInBlock((node as TryStatement).tryBlock);
186
187                case SyntaxKind.ThrowStatement:
188                    // span in throw ...
189                    return textSpan(node, (node as ThrowStatement).expression);
190
191                case SyntaxKind.ExportAssignment:
192                    // span on export = id
193                    return textSpan(node, (node as ExportAssignment).expression);
194
195                case SyntaxKind.ImportEqualsDeclaration:
196                    // import statement without including semicolon
197                    return textSpan(node, (node as ImportEqualsDeclaration).moduleReference);
198
199                case SyntaxKind.ImportDeclaration:
200                    // import statement without including semicolon
201                    return textSpan(node, (node as ImportDeclaration).moduleSpecifier);
202
203                case SyntaxKind.ExportDeclaration:
204                    // import statement without including semicolon
205                    return textSpan(node, (node as ExportDeclaration).moduleSpecifier);
206
207                case SyntaxKind.ModuleDeclaration:
208                    // span on complete module if it is instantiated
209                    if (getModuleInstanceState(node as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
210                        return undefined;
211                    }
212                    // falls through
213
214                case SyntaxKind.ClassDeclaration:
215                case SyntaxKind.EnumDeclaration:
216                case SyntaxKind.EnumMember:
217                case SyntaxKind.BindingElement:
218                    // span on complete node
219                    return textSpan(node);
220
221                case SyntaxKind.WithStatement:
222                    // span in statement
223                    return spanInNode((node as WithStatement).statement);
224
225                case SyntaxKind.Decorator:
226                    return spanInNodeArray((parent as HasDecorators).modifiers, node, isDecorator);
227
228                case SyntaxKind.ObjectBindingPattern:
229                case SyntaxKind.ArrayBindingPattern:
230                    return spanInBindingPattern(node as BindingPattern);
231
232                // No breakpoint in interface, type alias
233                case SyntaxKind.InterfaceDeclaration:
234                case SyntaxKind.TypeAliasDeclaration:
235                    return undefined;
236
237                // Tokens:
238                case SyntaxKind.SemicolonToken:
239                case SyntaxKind.EndOfFileToken:
240                    return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile));
241
242                case SyntaxKind.CommaToken:
243                    return spanInPreviousNode(node);
244
245                case SyntaxKind.OpenBraceToken:
246                    return spanInOpenBraceToken(node);
247
248                case SyntaxKind.CloseBraceToken:
249                    return spanInCloseBraceToken(node);
250
251                case SyntaxKind.CloseBracketToken:
252                    return spanInCloseBracketToken(node);
253
254                case SyntaxKind.OpenParenToken:
255                    return spanInOpenParenToken(node);
256
257                case SyntaxKind.CloseParenToken:
258                    return spanInCloseParenToken(node);
259
260                case SyntaxKind.ColonToken:
261                    return spanInColonToken(node);
262
263                case SyntaxKind.GreaterThanToken:
264                case SyntaxKind.LessThanToken:
265                    return spanInGreaterThanOrLessThanToken(node);
266
267                // Keywords:
268                case SyntaxKind.WhileKeyword:
269                    return spanInWhileKeyword(node);
270
271                case SyntaxKind.ElseKeyword:
272                case SyntaxKind.CatchKeyword:
273                case SyntaxKind.FinallyKeyword:
274                    return spanInNextNode(node);
275
276                case SyntaxKind.OfKeyword:
277                    return spanInOfKeyword(node);
278
279                default:
280                    // Destructuring pattern in destructuring assignment
281                    // [a, b, c] of
282                    // [a, b, c] = expression
283                    if (isArrayLiteralOrObjectLiteralDestructuringPattern(node)) {
284                        return spanInArrayLiteralOrObjectLiteralDestructuringPattern(node as DestructuringPattern);
285                    }
286
287                    // Set breakpoint on identifier element of destructuring pattern
288                    // `a` or `...c` or `d: x` from
289                    // `[a, b, ...c]` or `{ a, b }` or `{ d: x }` from destructuring pattern
290                    if ((node.kind === SyntaxKind.Identifier ||
291                        node.kind === SyntaxKind.SpreadElement ||
292                        node.kind === SyntaxKind.PropertyAssignment ||
293                        node.kind === SyntaxKind.ShorthandPropertyAssignment) &&
294                        isArrayLiteralOrObjectLiteralDestructuringPattern(parent)) {
295                        return textSpan(node);
296                    }
297
298                    if (node.kind === SyntaxKind.BinaryExpression) {
299                        const { left, operatorToken } = node as BinaryExpression;
300                        // Set breakpoint in destructuring pattern if its destructuring assignment
301                        // [a, b, c] or {a, b, c} of
302                        // [a, b, c] = expression or
303                        // {a, b, c} = expression
304                        if (isArrayLiteralOrObjectLiteralDestructuringPattern(left)) {
305                            return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
306                                left as ArrayLiteralExpression | ObjectLiteralExpression);
307                        }
308
309                        if (operatorToken.kind === SyntaxKind.EqualsToken && isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
310                            // Set breakpoint on assignment expression element of destructuring pattern
311                            // a = expression of
312                            // [a = expression, b, c] = someExpression or
313                            // { a = expression, b, c } = someExpression
314                            return textSpan(node);
315                        }
316
317                        if (operatorToken.kind === SyntaxKind.CommaToken) {
318                            return spanInNode(left);
319                        }
320                    }
321
322                    if (isExpressionNode(node)) {
323                        switch (parent.kind) {
324                            case SyntaxKind.DoStatement:
325                                // Set span as if on while keyword
326                                return spanInPreviousNode(node);
327
328                            case SyntaxKind.Decorator:
329                                // Set breakpoint on the decorator emit
330                                return spanInNode(node.parent);
331
332                            case SyntaxKind.ForStatement:
333                            case SyntaxKind.ForOfStatement:
334                                return textSpan(node);
335
336                            case SyntaxKind.BinaryExpression:
337                                if ((node.parent as BinaryExpression).operatorToken.kind === SyntaxKind.CommaToken) {
338                                    // If this is a comma expression, the breakpoint is possible in this expression
339                                    return textSpan(node);
340                                }
341                                break;
342
343                            case SyntaxKind.ArrowFunction:
344                                if ((node.parent as FunctionLikeDeclaration).body === node) {
345                                    // If this is body of arrow function, it is allowed to have the breakpoint
346                                    return textSpan(node);
347                                }
348                                break;
349                        }
350                    }
351
352                    switch (node.parent.kind) {
353                        case SyntaxKind.PropertyAssignment:
354                            // If this is name of property assignment, set breakpoint in the initializer
355                            if ((node.parent as PropertyAssignment).name === node &&
356                                !isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.parent)) {
357                                return spanInNode((node.parent as PropertyAssignment).initializer);
358                            }
359                            break;
360                        case SyntaxKind.TypeAssertionExpression:
361                            // Breakpoint in type assertion goes to its operand
362                            if ((node.parent as TypeAssertion).type === node) {
363                                return spanInNextNode((node.parent as TypeAssertion).type);
364                            }
365                            break;
366                        case SyntaxKind.VariableDeclaration:
367                        case SyntaxKind.Parameter: {
368                            // initializer of variable/parameter declaration go to previous node
369                            const { initializer, type } = node.parent as VariableDeclaration | ParameterDeclaration;
370                            if (initializer === node || type === node || isAssignmentOperator(node.kind)) {
371                                return spanInPreviousNode(node);
372                            }
373                            break;
374                        }
375                        case SyntaxKind.BinaryExpression: {
376                            const { left } = node.parent as BinaryExpression;
377                            if (isArrayLiteralOrObjectLiteralDestructuringPattern(left) && node !== left) {
378                                // If initializer of destructuring assignment move to previous token
379                                return spanInPreviousNode(node);
380                            }
381                            break;
382                        }
383                        default:
384                            // return type of function go to previous token
385                            if (isFunctionLike(node.parent) && node.parent.type === node) {
386                                return spanInPreviousNode(node);
387                            }
388                    }
389
390                    // Default go to parent to set the breakpoint
391                    return spanInNode(node.parent);
392            }
393        }
394
395        function textSpanFromVariableDeclaration(variableDeclaration: VariableDeclaration | PropertyDeclaration | PropertySignature): TextSpan {
396            if (isVariableDeclarationList(variableDeclaration.parent) && variableDeclaration.parent.declarations[0] === variableDeclaration) {
397                // First declaration - include let keyword
398                return textSpan(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent)!, variableDeclaration);
399            }
400            else {
401                // Span only on this declaration
402                return textSpan(variableDeclaration);
403            }
404        }
405
406        function spanInVariableDeclaration(variableDeclaration: VariableDeclaration | PropertyDeclaration | PropertySignature): TextSpan | undefined {
407            // If declaration of for in statement, just set the span in parent
408            if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
409                return spanInNode(variableDeclaration.parent.parent);
410            }
411
412            const parent = variableDeclaration.parent;
413            // If this is a destructuring pattern, set breakpoint in binding pattern
414            if (isBindingPattern(variableDeclaration.name)) {
415                return spanInBindingPattern(variableDeclaration.name);
416            }
417
418            // Breakpoint is possible in variableDeclaration only if there is initialization
419            // or its declaration from 'for of'
420            if ((hasOnlyExpressionInitializer(variableDeclaration) && variableDeclaration.initializer) ||
421                hasSyntacticModifier(variableDeclaration, ModifierFlags.Export) ||
422                parent.parent.kind === SyntaxKind.ForOfStatement) {
423                return textSpanFromVariableDeclaration(variableDeclaration);
424            }
425
426            if (isVariableDeclarationList(variableDeclaration.parent) &&
427                variableDeclaration.parent.declarations[0] !== variableDeclaration) {
428                // If we cannot set breakpoint on this declaration, set it on previous one
429                // Because the variable declaration may be binding pattern and
430                // we would like to set breakpoint in last binding element if that's the case,
431                // use preceding token instead
432                return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
433            }
434        }
435
436        function canHaveSpanInParameterDeclaration(parameter: ParameterDeclaration): boolean {
437            // Breakpoint is possible on parameter only if it has initializer, is a rest parameter, or has public or private modifier
438            return !!parameter.initializer || parameter.dotDotDotToken !== undefined ||
439                hasSyntacticModifier(parameter, ModifierFlags.Public | ModifierFlags.Private);
440        }
441
442        function spanInParameterDeclaration(parameter: ParameterDeclaration): TextSpan | undefined {
443            if (isBindingPattern(parameter.name)) {
444                // Set breakpoint in binding pattern
445                return spanInBindingPattern(parameter.name);
446            }
447            else if (canHaveSpanInParameterDeclaration(parameter)) {
448                return textSpan(parameter);
449            }
450            else {
451                const functionDeclaration = parameter.parent as FunctionLikeDeclaration;
452                const indexOfParameter = functionDeclaration.parameters.indexOf(parameter);
453                Debug.assert(indexOfParameter !== -1);
454                if (indexOfParameter !== 0) {
455                    // Not a first parameter, go to previous parameter
456                    return spanInParameterDeclaration(functionDeclaration.parameters[indexOfParameter - 1]);
457                }
458                else {
459                    // Set breakpoint in the function declaration body
460                    return spanInNode(functionDeclaration.body);
461                }
462            }
463        }
464
465        function canFunctionHaveSpanInWholeDeclaration(functionDeclaration: FunctionLikeDeclaration) {
466            return hasSyntacticModifier(functionDeclaration, ModifierFlags.Export) ||
467                (functionDeclaration.parent.kind === SyntaxKind.ClassDeclaration && functionDeclaration.kind !== SyntaxKind.Constructor);
468        }
469
470        function spanInFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration): TextSpan | undefined {
471            // No breakpoints in the function signature
472            if (!functionDeclaration.body) {
473                return undefined;
474            }
475
476            if (canFunctionHaveSpanInWholeDeclaration(functionDeclaration)) {
477                // Set the span on whole function declaration
478                return textSpan(functionDeclaration);
479            }
480
481            // Set span in function body
482            return spanInNode(functionDeclaration.body);
483        }
484
485        function spanInFunctionBlock(block: Block): TextSpan | undefined {
486            const nodeForSpanInBlock = block.statements.length ? block.statements[0] : block.getLastToken();
487            if (canFunctionHaveSpanInWholeDeclaration(block.parent as FunctionLikeDeclaration)) {
488                return spanInNodeIfStartsOnSameLine(block.parent, nodeForSpanInBlock);
489            }
490
491            return spanInNode(nodeForSpanInBlock);
492        }
493
494        function spanInBlock(block: Block): TextSpan | undefined {
495            switch (block.parent.kind) {
496                case SyntaxKind.ModuleDeclaration:
497                    if (getModuleInstanceState(block.parent as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
498                        return undefined;
499                    }
500
501                // Set on parent if on same line otherwise on first statement
502                // falls through
503                case SyntaxKind.WhileStatement:
504                case SyntaxKind.IfStatement:
505                case SyntaxKind.ForInStatement:
506                    return spanInNodeIfStartsOnSameLine(block.parent, block.statements[0]);
507
508                // Set span on previous token if it starts on same line otherwise on the first statement of the block
509                case SyntaxKind.ForStatement:
510                case SyntaxKind.ForOfStatement:
511                    return spanInNodeIfStartsOnSameLine(findPrecedingToken(block.pos, sourceFile, block.parent), block.statements[0]);
512            }
513
514            // Default action is to set on first statement
515            return spanInNode(block.statements[0]);
516        }
517
518        function spanInInitializerOfForLike(forLikeStatement: ForStatement | ForOfStatement | ForInStatement): TextSpan | undefined {
519            if (forLikeStatement.initializer!.kind === SyntaxKind.VariableDeclarationList) {
520                // Declaration list - set breakpoint in first declaration
521                const variableDeclarationList = forLikeStatement.initializer as VariableDeclarationList;
522                if (variableDeclarationList.declarations.length > 0) {
523                    return spanInNode(variableDeclarationList.declarations[0]);
524                }
525            }
526            else {
527                // Expression - set breakpoint in it
528                return spanInNode(forLikeStatement.initializer);
529            }
530        }
531
532        function spanInForStatement(forStatement: ForStatement): TextSpan | undefined {
533            if (forStatement.initializer) {
534                return spanInInitializerOfForLike(forStatement);
535            }
536
537            if (forStatement.condition) {
538                return textSpan(forStatement.condition);
539            }
540            if (forStatement.incrementor) {
541                return textSpan(forStatement.incrementor);
542            }
543        }
544
545        function spanInBindingPattern(bindingPattern: BindingPattern): TextSpan | undefined {
546            // Set breakpoint in first binding element
547            const firstBindingElement = forEach(bindingPattern.elements,
548                element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
549
550            if (firstBindingElement) {
551                return spanInNode(firstBindingElement);
552            }
553
554            // Empty binding pattern of binding element, set breakpoint on binding element
555            if (bindingPattern.parent.kind === SyntaxKind.BindingElement) {
556                return textSpan(bindingPattern.parent);
557            }
558
559            // Variable declaration is used as the span
560            return textSpanFromVariableDeclaration(bindingPattern.parent as VariableDeclaration);
561        }
562
563        function spanInArrayLiteralOrObjectLiteralDestructuringPattern(node: DestructuringPattern): TextSpan | undefined {
564            Debug.assert(node.kind !== SyntaxKind.ArrayBindingPattern && node.kind !== SyntaxKind.ObjectBindingPattern);
565            const elements: NodeArray<Expression | ObjectLiteralElement> = node.kind === SyntaxKind.ArrayLiteralExpression ? node.elements : node.properties;
566
567            const firstBindingElement = forEach(elements,
568                element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
569
570            if (firstBindingElement) {
571                return spanInNode(firstBindingElement);
572            }
573
574            // Could be ArrayLiteral from destructuring assignment or
575            // just nested element in another destructuring assignment
576            // set breakpoint on assignment when parent is destructuring assignment
577            // Otherwise set breakpoint for this element
578            return textSpan(node.parent.kind === SyntaxKind.BinaryExpression ? node.parent : node);
579        }
580
581        // Tokens:
582        function spanInOpenBraceToken(node: Node): TextSpan | undefined {
583            switch (node.parent.kind) {
584                case SyntaxKind.EnumDeclaration:
585                    const enumDeclaration = node.parent as EnumDeclaration;
586                    return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), enumDeclaration.members.length ? enumDeclaration.members[0] : enumDeclaration.getLastToken(sourceFile));
587
588                case SyntaxKind.ClassDeclaration:
589                    const classDeclaration = node.parent as ClassDeclaration;
590                    return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), classDeclaration.members.length ? classDeclaration.members[0] : classDeclaration.getLastToken(sourceFile));
591
592                case SyntaxKind.CaseBlock:
593                    return spanInNodeIfStartsOnSameLine(node.parent.parent, (node.parent as CaseBlock).clauses[0]);
594            }
595
596            // Default to parent node
597            return spanInNode(node.parent);
598        }
599
600        function spanInCloseBraceToken(node: Node): TextSpan | undefined {
601            switch (node.parent.kind) {
602                case SyntaxKind.ModuleBlock:
603                    // If this is not an instantiated module block, no bp span
604                    if (getModuleInstanceState(node.parent.parent as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
605                        return undefined;
606                    }
607                    // falls through
608
609                case SyntaxKind.EnumDeclaration:
610                case SyntaxKind.ClassDeclaration:
611                    // Span on close brace token
612                    return textSpan(node);
613
614                case SyntaxKind.Block:
615                    if (isFunctionBlock(node.parent)) {
616                        // Span on close brace token
617                        return textSpan(node);
618                    }
619                    // falls through
620
621                case SyntaxKind.CatchClause:
622                    return spanInNode(lastOrUndefined((node.parent as Block).statements));
623
624                case SyntaxKind.CaseBlock:
625                    // breakpoint in last statement of the last clause
626                    const caseBlock = node.parent as CaseBlock;
627                    const lastClause = lastOrUndefined(caseBlock.clauses);
628                    if (lastClause) {
629                        return spanInNode(lastOrUndefined(lastClause.statements));
630                    }
631                    return undefined;
632
633                case SyntaxKind.ObjectBindingPattern:
634                    // Breakpoint in last binding element or binding pattern if it contains no elements
635                    const bindingPattern = node.parent as BindingPattern;
636                    return spanInNode(lastOrUndefined(bindingPattern.elements) || bindingPattern);
637
638                // Default to parent node
639                default:
640                    if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
641                        // Breakpoint in last binding element or binding pattern if it contains no elements
642                        const objectLiteral = node.parent as ObjectLiteralExpression;
643                        return textSpan(lastOrUndefined(objectLiteral.properties) || objectLiteral);
644                    }
645                    return spanInNode(node.parent);
646            }
647        }
648
649        function spanInCloseBracketToken(node: Node): TextSpan | undefined {
650            switch (node.parent.kind) {
651                case SyntaxKind.ArrayBindingPattern:
652                    // Breakpoint in last binding element or binding pattern if it contains no elements
653                    const bindingPattern = node.parent as BindingPattern;
654                    return textSpan(lastOrUndefined(bindingPattern.elements) || bindingPattern);
655
656                default:
657                    if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
658                        // Breakpoint in last binding element or binding pattern if it contains no elements
659                        const arrayLiteral = node.parent as ArrayLiteralExpression;
660                        return textSpan(lastOrUndefined(arrayLiteral.elements) || arrayLiteral);
661                    }
662
663                    // Default to parent node
664                    return spanInNode(node.parent);
665            }
666        }
667
668        function spanInOpenParenToken(node: Node): TextSpan | undefined {
669            if (node.parent.kind === SyntaxKind.DoStatement || // Go to while keyword and do action instead
670                node.parent.kind === SyntaxKind.CallExpression ||
671                node.parent.kind === SyntaxKind.NewExpression) {
672                return spanInPreviousNode(node);
673            }
674
675            if (node.parent.kind === SyntaxKind.ParenthesizedExpression) {
676                return spanInNextNode(node);
677            }
678
679            // Default to parent node
680            return spanInNode(node.parent);
681        }
682
683        function spanInCloseParenToken(node: Node): TextSpan | undefined {
684            // Is this close paren token of parameter list, set span in previous token
685            switch (node.parent.kind) {
686                case SyntaxKind.FunctionExpression:
687                case SyntaxKind.FunctionDeclaration:
688                case SyntaxKind.ArrowFunction:
689                case SyntaxKind.MethodDeclaration:
690                case SyntaxKind.MethodSignature:
691                case SyntaxKind.GetAccessor:
692                case SyntaxKind.SetAccessor:
693                case SyntaxKind.Constructor:
694                case SyntaxKind.WhileStatement:
695                case SyntaxKind.DoStatement:
696                case SyntaxKind.ForStatement:
697                case SyntaxKind.ForOfStatement:
698                case SyntaxKind.CallExpression:
699                case SyntaxKind.NewExpression:
700                case SyntaxKind.ParenthesizedExpression:
701                    return spanInPreviousNode(node);
702
703                // Default to parent node
704                default:
705                    return spanInNode(node.parent);
706            }
707        }
708
709        function spanInColonToken(node: Node): TextSpan | undefined {
710            // Is this : specifying return annotation of the function declaration
711            if (isFunctionLike(node.parent) ||
712                node.parent.kind === SyntaxKind.PropertyAssignment ||
713                node.parent.kind === SyntaxKind.Parameter) {
714                return spanInPreviousNode(node);
715            }
716
717            return spanInNode(node.parent);
718        }
719
720        function spanInGreaterThanOrLessThanToken(node: Node): TextSpan | undefined {
721            if (node.parent.kind === SyntaxKind.TypeAssertionExpression) {
722                return spanInNextNode(node);
723            }
724
725            return spanInNode(node.parent);
726        }
727
728        function spanInWhileKeyword(node: Node): TextSpan | undefined {
729            if (node.parent.kind === SyntaxKind.DoStatement) {
730                // Set span on while expression
731                return textSpanEndingAtNextToken(node, (node.parent as DoStatement).expression);
732            }
733
734            // Default to parent node
735            return spanInNode(node.parent);
736        }
737
738        function spanInOfKeyword(node: Node): TextSpan | undefined {
739            if (node.parent.kind === SyntaxKind.ForOfStatement) {
740                // Set using next token
741                return spanInNextNode(node);
742            }
743
744            // Default to parent node
745            return spanInNode(node.parent);
746        }
747    }
748}
749