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