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