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