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