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