// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. // See LICENSE.txt in the project root for complete license information. /// module TypeScript.Services.Breakpoints { function createBreakpointSpanInfo(parentElement: TypeScript.ISyntaxElement, ...childElements: TypeScript.ISyntaxElement[]): TextSpan { if (!parentElement) { return null; } if (childElements.length == 0) { return TextSpan.fromBounds(TypeScript.start(parentElement), TypeScript.end(parentElement)); } var start: number; var end: number; for (var i = 0; i < childElements.length; i++) { var element = childElements[i]; if (element && !isShared(element)) { if (start == undefined) { start = TypeScript.start(element); } end = TypeScript.end(element); } } return TextSpan.fromBounds(start, end); } function createBreakpointSpanInfoWithLimChar(startElement: TypeScript.ISyntaxElement, limChar: number): TextSpan { return TextSpan.fromBounds(start(startElement), limChar); } class BreakpointResolver { constructor(private posLine: number, private lineMap: TypeScript.LineMap) { } private breakpointSpanOfToken(positionedToken: TypeScript.ISyntaxToken): TextSpan { switch (positionedToken.kind()) { case TypeScript.SyntaxKind.OpenBraceToken: return this.breakpointSpanOfOpenBrace(positionedToken); case TypeScript.SyntaxKind.CloseBraceToken: return this.breakpointSpanOfCloseBrace(positionedToken); case TypeScript.SyntaxKind.CommaToken: return this.breakpointSpanOfComma(positionedToken); case TypeScript.SyntaxKind.SemicolonToken: case TypeScript.SyntaxKind.EndOfFileToken: return this.breakpointSpanIfStartsOnSameLine(previousToken(positionedToken)); case TypeScript.SyntaxKind.CloseParenToken: return this.breakpointSpanOfCloseParen(positionedToken); case TypeScript.SyntaxKind.DoKeyword: var parentElement = positionedToken.parent; if (parentElement && parentElement.kind() == TypeScript.SyntaxKind.DoStatement) { return this.breakpointSpanIfStartsOnSameLine(nextToken(positionedToken)); } break; } return this.breakpointSpanOfContainingNode(positionedToken); } private breakpointSpanOfOpenBrace(openBraceToken: TypeScript.ISyntaxToken): TextSpan { var container = Syntax.containingNode(openBraceToken); if (container) { var originalContainer = container; if (container && container.kind() == TypeScript.SyntaxKind.Block) { // We have to check the parent and decide what to do with the breakpoint container = Syntax.containingNode(container); if (!container) { container = originalContainer; } } switch (container.kind()) { case TypeScript.SyntaxKind.Block: if (!this.canHaveBreakpointInBlock(container)) { return null; } return this.breakpointSpanOfFirstStatementInBlock(container); break; case TypeScript.SyntaxKind.ModuleDeclaration: case TypeScript.SyntaxKind.ClassDeclaration: case TypeScript.SyntaxKind.FunctionDeclaration: case TypeScript.SyntaxKind.ConstructorDeclaration: case TypeScript.SyntaxKind.MemberFunctionDeclaration: case TypeScript.SyntaxKind.GetAccessor: case TypeScript.SyntaxKind.SetAccessor: case TypeScript.SyntaxKind.FunctionExpression: case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: if (!this.canHaveBreakpointInDeclaration(container)) { return null; } if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { return this.breakpointSpanOfFirstChildOfSyntaxList(this.getSyntaxListOfDeclarationWithElements(container)); } else { return this.breakpointSpanOf(container); } case TypeScript.SyntaxKind.EnumDeclaration: if (!this.canHaveBreakpointInDeclaration(container)) { return null; } if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { return this.breakpointSpanOfFirstEnumElement(container); } else { return this.breakpointSpanOf(container); } case TypeScript.SyntaxKind.IfStatement: case TypeScript.SyntaxKind.ForInStatement: case TypeScript.SyntaxKind.WhileStatement: case TypeScript.SyntaxKind.CatchClause: if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { return this.breakpointSpanOfFirstStatementInBlock(originalContainer); } else { return this.breakpointSpanOf(container); } case TypeScript.SyntaxKind.DoStatement: return this.breakpointSpanOfFirstStatementInBlock(originalContainer); case TypeScript.SyntaxKind.ForStatement: if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { return this.breakpointSpanOfFirstStatementInBlock(originalContainer); } else { return this.breakpointSpanOf(previousToken(openBraceToken)); } case TypeScript.SyntaxKind.ElseClause: case TypeScript.SyntaxKind.CaseSwitchClause: case TypeScript.SyntaxKind.DefaultSwitchClause: case TypeScript.SyntaxKind.WithStatement: case TypeScript.SyntaxKind.TryStatement: case TypeScript.SyntaxKind.FinallyClause: return this.breakpointSpanOfFirstStatementInBlock(originalContainer); case TypeScript.SyntaxKind.SwitchStatement: if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { return this.breakpointSpanOfFirstStatementOfFirstCaseClause(container); } else { return this.breakpointSpanOf(container); } } } return null; } private breakpointSpanOfCloseBrace(closeBraceToken: TypeScript.ISyntaxToken): TextSpan { var container = Syntax.containingNode(closeBraceToken); if (container) { var originalContainer = container; if (container.kind() == TypeScript.SyntaxKind.Block) { // We have to check the parent and decide what to do with the breakpoint container = Syntax.containingNode(container); if (!container) { container = originalContainer; } } switch (container.kind()) { case TypeScript.SyntaxKind.Block: if (!this.canHaveBreakpointInBlock(container)) { return null; } return this.breakpointSpanOfLastStatementInBlock(container); break; case TypeScript.SyntaxKind.ModuleDeclaration: if (!this.canHaveBreakpointInDeclaration(container)) { return null; } var moduleSyntax = container; if (moduleSyntax.moduleElements && moduleSyntax.moduleElements.length > 0) { return createBreakpointSpanInfo(closeBraceToken); } else { return null; } case TypeScript.SyntaxKind.ClassDeclaration: case TypeScript.SyntaxKind.FunctionDeclaration: case TypeScript.SyntaxKind.ConstructorDeclaration: case TypeScript.SyntaxKind.MemberFunctionDeclaration: case TypeScript.SyntaxKind.GetAccessor: case TypeScript.SyntaxKind.SetAccessor: case TypeScript.SyntaxKind.FunctionExpression: if (!this.canHaveBreakpointInDeclaration(container)) { return null; } return createBreakpointSpanInfo(closeBraceToken); case TypeScript.SyntaxKind.EnumDeclaration: if (!this.canHaveBreakpointInDeclaration(container)) { return null; } return createBreakpointSpanInfo(closeBraceToken); case TypeScript.SyntaxKind.IfStatement: case TypeScript.SyntaxKind.ElseClause: case TypeScript.SyntaxKind.ForInStatement: case TypeScript.SyntaxKind.ForStatement: case TypeScript.SyntaxKind.WhileStatement: case TypeScript.SyntaxKind.DoStatement: case TypeScript.SyntaxKind.CaseSwitchClause: case TypeScript.SyntaxKind.DefaultSwitchClause: case TypeScript.SyntaxKind.WithStatement: case TypeScript.SyntaxKind.TryStatement: case TypeScript.SyntaxKind.CatchClause: case TypeScript.SyntaxKind.FinallyClause: case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: return this.breakpointSpanOfLastStatementInBlock(originalContainer); case TypeScript.SyntaxKind.SwitchStatement: return this.breakpointSpanOfLastStatementOfLastCaseClause(container); } } return null; } private breakpointSpanOfComma(commaToken: TypeScript.ISyntaxToken): TextSpan { var commaParent = commaToken.parent; if (isSeparatedList(commaParent)) { var grandParent = commaParent.parent; if (grandParent) { switch (grandParent.kind()) { case TypeScript.SyntaxKind.VariableDeclaration: case TypeScript.SyntaxKind.EnumDeclaration: case TypeScript.SyntaxKind.ParameterList: var index = Syntax.childIndex(commaParent, commaToken); // Use the previous child if (index > 0) { var child = childAt(commaParent, index - 1); return this.breakpointSpanOf(child); } // If we cant set breakpoint on enum element, just dont set breakpoint if (grandParent.kind() == TypeScript.SyntaxKind.EnumDeclaration) { return null; } break; } } } return this.breakpointSpanOfContainingNode(commaToken); } private breakpointSpanOfCloseParen(closeParenToken: TypeScript.ISyntaxToken): TextSpan { var closeParenParent = closeParenToken.parent; if (closeParenParent) { switch (closeParenParent.kind()) { case TypeScript.SyntaxKind.ForStatement: case TypeScript.SyntaxKind.ParameterList: return this.breakpointSpanOf(previousToken(closeParenToken)); } } return this.breakpointSpanOfContainingNode(closeParenToken); } private canHaveBreakpointInBlock(blockNode: TypeScript.ISyntaxNode) { if (!blockNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(blockNode)) { return false; } var blockSyntax = blockNode; return blockSyntax.statements && blockSyntax.statements.length != 0; } private breakpointSpanOfFirstStatementInBlock(blockNode: TypeScript.ISyntaxNode): TextSpan { if (!blockNode) { return null; } var blockSyntax = blockNode; var statementsNode = blockSyntax.statements; if (!statementsNode || statementsNode.length == 0) { return null; } var firstStatement = childAt(statementsNode, 0); if (firstStatement && firstStatement.kind() == TypeScript.SyntaxKind.Block) { if (this.canHaveBreakpointInBlock(firstStatement)) { return this.breakpointSpanOfFirstStatementInBlock(firstStatement); } return null; } else { return this.breakpointSpanOf(firstStatement); } } private breakpointSpanOfLastStatementInBlock(blockNode: TypeScript.ISyntaxNode): TextSpan { if (!blockNode) { return null; } var blockSyntax = blockNode; var statementsNode = blockSyntax.statements; if (!statementsNode || statementsNode.length == 0) { return null; } var lastStatement = childAt(statementsNode, statementsNode.length - 1); if (lastStatement && lastStatement.kind() == TypeScript.SyntaxKind.Block) { if (this.canHaveBreakpointInBlock(lastStatement)) { return this.breakpointSpanOfLastStatementInBlock(lastStatement); } return null; } else { return this.breakpointSpanOf(lastStatement); } } private breakpointSpanOfFirstChildOfSyntaxList(positionedList: TypeScript.ISyntaxNodeOrToken[]): TextSpan { if (!positionedList) { return null; } // Find the first syntax element var listSyntax = positionedList; if (listSyntax.length == 0) { return null; } var firstStatement = childAt(positionedList, 0); if (firstStatement && firstStatement.kind() == TypeScript.SyntaxKind.Block) { if (this.canHaveBreakpointInBlock(firstStatement)) { return this.breakpointSpanOfFirstStatementInBlock(firstStatement); } return null; } else { return this.breakpointSpanOf(firstStatement); } } private breakpointSpanOfLastChildOfSyntaxList(positionedList: TypeScript.ISyntaxNodeOrToken[]): TextSpan { if (!positionedList) { return null; } // Find the first syntax element var listSyntax = positionedList; if (listSyntax.length == 0) { return null; } var lastStatement = childAt(positionedList, 0); if (lastStatement && lastStatement.kind() == TypeScript.SyntaxKind.Block) { if (this.canHaveBreakpointInBlock(lastStatement)) { return this.breakpointSpanOfLastStatementInBlock(lastStatement); } return null; } else { return this.breakpointSpanOf(lastStatement); } } private breakpointSpanOfNode(positionedNode: ISyntaxNode): TextSpan { var node = positionedNode; switch (node.kind()) { // Declarations with elements case TypeScript.SyntaxKind.ModuleDeclaration: case TypeScript.SyntaxKind.ClassDeclaration: case TypeScript.SyntaxKind.FunctionDeclaration: case TypeScript.SyntaxKind.ConstructorDeclaration: case TypeScript.SyntaxKind.MemberFunctionDeclaration: case TypeScript.SyntaxKind.GetAccessor: case TypeScript.SyntaxKind.SetAccessor: case TypeScript.SyntaxKind.FunctionExpression: return this.breakpointSpanOfDeclarationWithElements(positionedNode); // Var, parameter and member variable declaration syntax case TypeScript.SyntaxKind.VariableDeclarator: return this.breakpointSpanOfVariableDeclarator(positionedNode); case TypeScript.SyntaxKind.VariableDeclaration: return this.breakpointSpanOfVariableDeclaration(positionedNode); case TypeScript.SyntaxKind.VariableStatement: return this.breakpointSpanOfVariableStatement(positionedNode); case TypeScript.SyntaxKind.Parameter: return this.breakpointSpanOfParameter(positionedNode); case TypeScript.SyntaxKind.MemberVariableDeclaration: return this.breakpointSpanOfMemberVariableDeclaration(positionedNode); case TypeScript.SyntaxKind.ImportDeclaration: return this.breakpointSpanOfImportDeclaration(positionedNode); case TypeScript.SyntaxKind.EnumDeclaration: return this.breakpointSpanOfEnumDeclaration(positionedNode); case TypeScript.SyntaxKind.EnumElement: return this.breakpointSpanOfEnumElement(positionedNode); // Statements case TypeScript.SyntaxKind.IfStatement: return this.breakpointSpanOfIfStatement(positionedNode); case TypeScript.SyntaxKind.ElseClause: return this.breakpointSpanOfElseClause(positionedNode); case TypeScript.SyntaxKind.ForInStatement: return this.breakpointSpanOfForInStatement(positionedNode); case TypeScript.SyntaxKind.ForStatement: return this.breakpointSpanOfForStatement(positionedNode); case TypeScript.SyntaxKind.WhileStatement: return this.breakpointSpanOfWhileStatement(positionedNode); case TypeScript.SyntaxKind.DoStatement: return this.breakpointSpanOfDoStatement(positionedNode); case TypeScript.SyntaxKind.SwitchStatement: return this.breakpointSpanOfSwitchStatement(positionedNode); case TypeScript.SyntaxKind.CaseSwitchClause: return this.breakpointSpanOfCaseSwitchClause(positionedNode); case TypeScript.SyntaxKind.DefaultSwitchClause: return this.breakpointSpanOfDefaultSwitchClause(positionedNode); case TypeScript.SyntaxKind.WithStatement: return this.breakpointSpanOfWithStatement(positionedNode); case TypeScript.SyntaxKind.TryStatement: return this.breakpointSpanOfTryStatement(positionedNode); case TypeScript.SyntaxKind.CatchClause: return this.breakpointSpanOfCatchClause(positionedNode); case TypeScript.SyntaxKind.FinallyClause: return this.breakpointSpanOfFinallyClause(positionedNode); // Arrow expressions case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: return this.breakpointSpanOfParenthesizedArrowFunctionExpression(positionedNode); case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: return this.breakpointSpanOfSimpleArrowFunctionExpression(positionedNode); // Expressions or statements default: if (SyntaxUtilities.isStatement(node)) { return this.breakpointSpanOfStatement(positionedNode); } else { return this.breakpointOfExpression(positionedNode); } } } private isExpressionOfArrowExpressions(expression: ISyntaxElement): boolean { if (!expression) { return false; } var expressionParent = expression.parent; if (expressionParent) { if (expressionParent.kind() == TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression) { var parenthesizedArrowExpression = expressionParent; var expressionOfParenthesizedArrowExpression = parenthesizedArrowExpression.expression; return expressionOfParenthesizedArrowExpression == expression; } else if (expressionParent.kind() == TypeScript.SyntaxKind.SimpleArrowFunctionExpression) { var simpleArrowExpression = expressionParent; var expressionOfSimpleArrowExpression = simpleArrowExpression.expression; return expressionOfSimpleArrowExpression == expression; } else if (expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.isExpressionOfArrowExpressions(expressionParent); } } return false; } private isInitializerOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { if (!expressionNode) { return false; } var expressionParent = expressionNode.parent; if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { var expression = expressionNode; var forStatement = expressionParent; var initializer = forStatement.initializer; return initializer === expression; } else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.isInitializerOfForStatement(expressionParent); } return false; } private isConditionOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { if (!expressionNode) { return false; } var expressionParent = expressionNode.parent; if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { var expression = expressionNode; var forStatement = expressionParent; var condition = forStatement.condition; return condition === expression; } else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.isConditionOfForStatement(expressionParent); } return false; } private isIncrememtorOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { if (!expressionNode) { return false; } var expressionParent = expressionNode.parent; if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { var expression = expressionNode; var forStatement = expressionParent; var incrementor = forStatement.incrementor; return incrementor === expression; } else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.isIncrememtorOfForStatement(expressionParent); } return false; } private breakpointOfLeftOfCommaExpression(commaExpressionNode: TypeScript.ISyntaxNode): TextSpan { var commaExpression = commaExpressionNode; return this.breakpointSpanOf(commaExpression.left); } private breakpointOfExpression(expressionNode: TypeScript.ISyntaxNode): TextSpan { if (this.isInitializerOfForStatement(expressionNode) || this.isConditionOfForStatement(expressionNode) || this.isIncrememtorOfForStatement(expressionNode)) { if (expressionNode.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.breakpointOfLeftOfCommaExpression(expressionNode); } return createBreakpointSpanInfo(expressionNode); } if (this.isExpressionOfArrowExpressions(expressionNode)) { if (expressionNode.kind() == TypeScript.SyntaxKind.CommaExpression) { return this.breakpointOfLeftOfCommaExpression(expressionNode); } return createBreakpointSpanInfo(expressionNode); } if (expressionNode.kind() == TypeScript.SyntaxKind.ExportAssignment) { var exportAssignmentSyntax = expressionNode; return createBreakpointSpanInfo(expressionNode, exportAssignmentSyntax.exportKeyword, exportAssignmentSyntax.equalsToken, exportAssignmentSyntax.identifier); } return this.breakpointSpanOfContainingNode(expressionNode); } private breakpointSpanOfStatement(statementNode: TypeScript.ISyntaxNode): TextSpan { var statement = statementNode; if (statement.kind() == TypeScript.SyntaxKind.EmptyStatement) { return null; } var containingNode = Syntax.containingNode(statementNode); if (SyntaxUtilities.isStatement(containingNode)) { // Check if not the declarations and the compound statements var useNodeForBreakpoint = false; switch (containingNode.kind()) { // Declarations case TypeScript.SyntaxKind.ModuleDeclaration: case TypeScript.SyntaxKind.ClassDeclaration: case TypeScript.SyntaxKind.FunctionDeclaration: case TypeScript.SyntaxKind.ConstructorDeclaration: case TypeScript.SyntaxKind.MemberFunctionDeclaration: case TypeScript.SyntaxKind.GetAccessor: case TypeScript.SyntaxKind.SetAccessor: case TypeScript.SyntaxKind.Block: // Compound Statements case TypeScript.SyntaxKind.IfStatement: case TypeScript.SyntaxKind.ElseClause: case TypeScript.SyntaxKind.ForInStatement: case TypeScript.SyntaxKind.ForStatement: case TypeScript.SyntaxKind.WhileStatement: case TypeScript.SyntaxKind.DoStatement: case TypeScript.SyntaxKind.SwitchStatement: case TypeScript.SyntaxKind.CaseSwitchClause: case TypeScript.SyntaxKind.DefaultSwitchClause: case TypeScript.SyntaxKind.WithStatement: case TypeScript.SyntaxKind.TryStatement: case TypeScript.SyntaxKind.CatchClause: case TypeScript.SyntaxKind.FinallyClause: case TypeScript.SyntaxKind.Block: useNodeForBreakpoint = true; } if (!useNodeForBreakpoint) { return this.breakpointSpanOfContainingNode(statementNode); } } switch (statement.kind()) { case TypeScript.SyntaxKind.ExpressionStatement: var expressionSyntax = statement; return createBreakpointSpanInfo(expressionSyntax.expression); case TypeScript.SyntaxKind.ReturnStatement: var returnStatementSyntax = statement; return createBreakpointSpanInfo(statementNode, returnStatementSyntax.returnKeyword, returnStatementSyntax.expression); case TypeScript.SyntaxKind.ThrowStatement: var throwStatementSyntax = statement; return createBreakpointSpanInfo(statementNode, throwStatementSyntax.throwKeyword, throwStatementSyntax.expression); case TypeScript.SyntaxKind.BreakStatement: var breakStatementSyntax = statement; return createBreakpointSpanInfo(statementNode, breakStatementSyntax.breakKeyword, breakStatementSyntax.identifier); case TypeScript.SyntaxKind.ContinueStatement: var continueStatementSyntax = statement; return createBreakpointSpanInfo(statementNode, continueStatementSyntax.continueKeyword, continueStatementSyntax.identifier); case TypeScript.SyntaxKind.DebuggerStatement: var debuggerStatementSyntax = statement; return createBreakpointSpanInfo(debuggerStatementSyntax.debuggerKeyword); case TypeScript.SyntaxKind.LabeledStatement: var labeledStatementSyntax = statement; return this.breakpointSpanOf(labeledStatementSyntax.statement); } return null; } private getSyntaxListOfDeclarationWithElements(positionedNode: TypeScript.ISyntaxNode) { var node = positionedNode; var elementsList: TypeScript.ISyntaxNodeOrToken[]; var block: TypeScript.BlockSyntax; switch (node.kind()) { case TypeScript.SyntaxKind.ModuleDeclaration: elementsList = (node).moduleElements; break; case TypeScript.SyntaxKind.ClassDeclaration: elementsList = (node).classElements; break; case TypeScript.SyntaxKind.FunctionDeclaration: block = (node).block; break; case TypeScript.SyntaxKind.ConstructorDeclaration: block = (node).block; break; case TypeScript.SyntaxKind.MemberFunctionDeclaration: block = (node).block; break; case TypeScript.SyntaxKind.GetAccessor: block = (node).block; break; case TypeScript.SyntaxKind.SetAccessor: block = (node).block; break; case TypeScript.SyntaxKind.FunctionExpression: block = (node).block; break; case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: block = (node).block; break; case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: block = (node).block; break; default: throw TypeScript.Errors.argument('positionNode', 'unknown node kind in getSyntaxListOfDeclarationWithElements'); } var parentElement: TypeScript.ISyntaxElement = positionedNode; if (block) { parentElement = block; elementsList = block.statements; } return elementsList; } private canHaveBreakpointInDeclaration(positionedNode: TypeScript.ISyntaxNode) { return positionedNode && !TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(positionedNode); } private breakpointSpanOfDeclarationWithElements(positionedNode: TypeScript.ISyntaxNode): TextSpan { if (!this.canHaveBreakpointInDeclaration(positionedNode)) { return null; } // If inside another module the whole declaration is debuggable var node = positionedNode; var moduleSyntax = positionedNode; if ((SyntaxUtilities.isModuleElement(node) && Syntax.containingNode(positionedNode).kind() != TypeScript.SyntaxKind.SourceUnit) || SyntaxUtilities.isClassElement(node) || (moduleSyntax.kind() == TypeScript.SyntaxKind.ModuleDeclaration && moduleSyntax.name && moduleSyntax.name.kind() == TypeScript.SyntaxKind.QualifiedName)) { return createBreakpointSpanInfo(positionedNode); } else { // Try to get the breakpoint in first element declaration return this.breakpointSpanOfFirstChildOfSyntaxList(this.getSyntaxListOfDeclarationWithElements(positionedNode)); } } private canHaveBreakpointInVariableDeclarator(varDeclaratorNode: TypeScript.ISyntaxNode) { if (!varDeclaratorNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varDeclaratorNode)) { return false; } var varDeclaratorSyntax = varDeclaratorNode; return !!varDeclaratorSyntax.equalsValueClause; } private breakpointSpanOfVariableDeclarator(varDeclaratorNode: TypeScript.ISyntaxNode): TextSpan { if (!this.canHaveBreakpointInVariableDeclarator(varDeclaratorNode)) { return null; } var container = Syntax.containingNode(varDeclaratorNode); if (container && container.kind() == TypeScript.SyntaxKind.VariableDeclaration) { var parentDeclaratorsList = varDeclaratorNode.parent; // If this is the first declarator in the list use the declaration instead if (parentDeclaratorsList && childAt(parentDeclaratorsList, 0) == varDeclaratorNode) { return this.breakpointSpanOfVariableDeclaration(container); } // Create breakpoint on this var declarator if (this.canHaveBreakpointInVariableDeclarator(varDeclaratorNode)) { return createBreakpointSpanInfo(varDeclaratorNode); } else { return null; } } else if (container) { // Member Variable syntax return this.breakpointSpanOfMemberVariableDeclaration(container); } return null; } private canHaveBreakpointInVariableDeclaration(varDeclarationNode: TypeScript.ISyntaxNode) { if (!varDeclarationNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varDeclarationNode)) { return false; } var varDeclarationSyntax = varDeclarationNode; var containerChildren = varDeclarationSyntax.variableDeclarators; if (!containerChildren || childCount(containerChildren) == 0) { return false; } var child = childAt(containerChildren, 0); if (isNode(child)) { return this.canHaveBreakpointInVariableDeclarator(child); } return false; } private breakpointSpanOfVariableDeclaration(varDeclarationNode: TypeScript.ISyntaxNode): TextSpan { if (!this.canHaveBreakpointInDeclaration(varDeclarationNode)) { return null; } var container = Syntax.containingNode(varDeclarationNode); var varDeclarationSyntax = varDeclarationNode; var varDeclarators = varDeclarationSyntax.variableDeclarators; if (container && container.kind() == TypeScript.SyntaxKind.VariableStatement) { return this.breakpointSpanOfVariableStatement(container); } if (this.canHaveBreakpointInVariableDeclaration(varDeclarationNode)) { return createBreakpointSpanInfoWithLimChar(varDeclarationNode, end(childAt(varDeclarators, 0))); } else { return null; } } private canHaveBreakpointInVariableStatement(varStatementNode: TypeScript.ISyntaxNode) { if (!varStatementNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varStatementNode)) { return false; } var variableStatement = varStatementNode; return this.canHaveBreakpointInVariableDeclaration(variableStatement.variableDeclaration); } private breakpointSpanOfVariableStatement(varStatementNode: TypeScript.ISyntaxNode): TextSpan { if (!this.canHaveBreakpointInVariableStatement(varStatementNode)) { return null; } var variableStatement = varStatementNode; var variableDeclaration = variableStatement.variableDeclaration; var varDeclarationSyntax = variableDeclaration; var varDeclarators = varDeclarationSyntax.variableDeclarators; return createBreakpointSpanInfoWithLimChar(varStatementNode, end(childAt(varDeclarators, 0))); } private breakpointSpanOfParameter(parameterNode: TypeScript.ISyntaxNode): TextSpan { if (parameterNode.parent.kind() === SyntaxKind.SimpleArrowFunctionExpression) { return this.breakpointSpanOfNode(parameterNode.parent); } if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(parameterNode)) { return null; } var parameterSyntax = parameterNode; if (parameterSyntax.dotDotDotToken || parameterSyntax.equalsValueClause || parameterSyntax.modifiers.length > 0) { return createBreakpointSpanInfo(parameterNode); } else { return null; } } private breakpointSpanOfMemberVariableDeclaration(memberVarDeclarationNode: TypeScript.ISyntaxNode): TextSpan { if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(memberVarDeclarationNode)) { return null; } var memberVariableDeclaration = memberVarDeclarationNode; if (this.canHaveBreakpointInVariableDeclarator(memberVariableDeclaration.variableDeclarator)) { return createBreakpointSpanInfo(memberVarDeclarationNode, memberVariableDeclaration.modifiers, memberVariableDeclaration.variableDeclarator); } else { return null; } } private breakpointSpanOfImportDeclaration(importDeclarationNode: TypeScript.ISyntaxNode): TextSpan { if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(importDeclarationNode)) { return null; } var importSyntax = importDeclarationNode; return createBreakpointSpanInfo(importDeclarationNode, importSyntax.modifiers, importSyntax.importKeyword, importSyntax.identifier, importSyntax.equalsToken, importSyntax.moduleReference); } private breakpointSpanOfEnumDeclaration(enumDeclarationNode: TypeScript.ISyntaxNode): TextSpan { if (!this.canHaveBreakpointInDeclaration(enumDeclarationNode)) { return null; } return createBreakpointSpanInfo(enumDeclarationNode); } private breakpointSpanOfFirstEnumElement(enumDeclarationNode: TypeScript.ISyntaxNode): TextSpan { var enumDeclarationSyntax = enumDeclarationNode; var enumElements = enumDeclarationSyntax.enumElements; if (enumElements && childCount(enumElements)) { return this.breakpointSpanOf(childAt(enumElements, 0)); } return null; } private breakpointSpanOfEnumElement(enumElementNode: TypeScript.ISyntaxNode): TextSpan { if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(enumElementNode)) { return null; } return createBreakpointSpanInfo(enumElementNode); } private breakpointSpanOfIfStatement(ifStatementNode: TypeScript.ISyntaxNode): TextSpan { var ifStatement = ifStatementNode; return createBreakpointSpanInfo(ifStatementNode, ifStatement.ifKeyword, ifStatement.openParenToken, ifStatement.condition, ifStatement.closeParenToken); } private breakpointSpanOfElseClause(elseClauseNode: TypeScript.ISyntaxNode): TextSpan { var elseClause = elseClauseNode; return this.breakpointSpanOf(elseClause.statement); } private breakpointSpanOfForInStatement(forInStatementNode: TypeScript.ISyntaxNode): TextSpan { var forInStatement = forInStatementNode; return createBreakpointSpanInfo(forInStatementNode, forInStatement.forKeyword, forInStatement.openParenToken, forInStatement.variableDeclaration, forInStatement.left, forInStatement.inKeyword, forInStatement.expression, forInStatement.closeParenToken); } private breakpointSpanOfForStatement(forStatementNode: TypeScript.ISyntaxNode): TextSpan { var forStatement = forStatementNode; return this.breakpointSpanOf(forStatement.variableDeclaration ? forStatement.variableDeclaration : forStatement.initializer); } private breakpointSpanOfWhileStatement(whileStatementNode: TypeScript.ISyntaxNode): TextSpan { var whileStatement = whileStatementNode; return createBreakpointSpanInfo(whileStatementNode, whileStatement.whileKeyword, whileStatement.openParenToken, whileStatement.condition, whileStatement.closeParenToken); } private breakpointSpanOfDoStatement(doStatementNode: TypeScript.ISyntaxNode): TextSpan { var doStatement = doStatementNode; return createBreakpointSpanInfo(doStatementNode, doStatement.whileKeyword, doStatement.openParenToken, doStatement.condition, doStatement.closeParenToken); } private breakpointSpanOfSwitchStatement(switchStatementNode: TypeScript.ISyntaxNode): TextSpan { var switchStatement = switchStatementNode; return createBreakpointSpanInfo(switchStatementNode, switchStatement.switchKeyword, switchStatement.openParenToken, switchStatement.expression, switchStatement.closeParenToken); } private breakpointSpanOfFirstStatementOfFirstCaseClause(switchStatementNode: TypeScript.ISyntaxNode): TextSpan { var switchStatement = switchStatementNode; if (switchStatement.switchClauses && switchStatement.switchClauses.length == 0) { return null; } var switchClauses = switchStatement.switchClauses; if (switchClauses.length == 0) { return null; } var firstCaseClause = switchClauses[0]; var statements = firstCaseClause.statements; return this.breakpointSpanOfFirstChildOfSyntaxList(statements); } private breakpointSpanOfLastStatementOfLastCaseClause(switchStatementNode: TypeScript.ISyntaxNode): TextSpan { var switchStatement = switchStatementNode; if (switchStatement.switchClauses && switchStatement.switchClauses.length == 0) { return null; } var switchClauses = switchStatement.switchClauses; if (switchClauses.length == 0) { return null; } var lastClauseNode = switchClauses[switchClauses.length - 1]; var statements = lastClauseNode.statements; return this.breakpointSpanOfLastChildOfSyntaxList(statements); } private breakpointSpanOfCaseSwitchClause(caseClauseNode: TypeScript.ISyntaxNode): TextSpan { var caseSwitchClause = caseClauseNode; return this.breakpointSpanOfFirstChildOfSyntaxList(caseSwitchClause.statements); } private breakpointSpanOfDefaultSwitchClause(defaultSwithClauseNode: TypeScript.ISyntaxNode): TextSpan { var defaultSwitchClause = defaultSwithClauseNode; return this.breakpointSpanOfFirstChildOfSyntaxList(defaultSwitchClause.statements); } private breakpointSpanOfWithStatement(withStatementNode: TypeScript.ISyntaxNode): TextSpan { var withStatement = withStatementNode; return this.breakpointSpanOf(withStatement.statement); } private breakpointSpanOfTryStatement(tryStatementNode: TypeScript.ISyntaxNode): TextSpan { var tryStatement = tryStatementNode; return this.breakpointSpanOfFirstStatementInBlock(tryStatement.block); } private breakpointSpanOfCatchClause(catchClauseNode: TypeScript.ISyntaxNode): TextSpan { var catchClause = catchClauseNode; return createBreakpointSpanInfo(catchClauseNode, catchClause.catchKeyword, catchClause.openParenToken, catchClause.identifier, catchClause.typeAnnotation, catchClause.closeParenToken); } private breakpointSpanOfFinallyClause(finallyClauseNode: TypeScript.ISyntaxNode): TextSpan { var finallyClause = finallyClauseNode; return this.breakpointSpanOfFirstStatementInBlock(finallyClause.block); } private breakpointSpanOfParenthesizedArrowFunctionExpression(arrowFunctionExpression: ParenthesizedArrowFunctionExpressionSyntax): TextSpan { if (arrowFunctionExpression.block) { return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block); } else { return this.breakpointSpanOf(arrowFunctionExpression.expression); } } private breakpointSpanOfSimpleArrowFunctionExpression(arrowFunctionExpression: SimpleArrowFunctionExpressionSyntax): TextSpan { if (arrowFunctionExpression.block) { return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block); } else { return this.breakpointSpanOf(arrowFunctionExpression.expression); } } private breakpointSpanOfContainingNode(positionedElement: ISyntaxElement): TextSpan { var current = positionedElement.parent; while (!isNode(current)) { current = current.parent; } return this.breakpointSpanOf(current); } private breakpointSpanIfStartsOnSameLine(positionedElement: TypeScript.ISyntaxElement): TextSpan { if (positionedElement && this.posLine == this.lineMap.getLineNumberFromPosition(start(positionedElement))) { return this.breakpointSpanOf(positionedElement); } return null; } public breakpointSpanOf(positionedElement: TypeScript.ISyntaxElement): TextSpan { if (!positionedElement) { return null; } for (var containingNode = Syntax.containingNode(positionedElement); containingNode != null; containingNode = Syntax.containingNode(containingNode)) { if (containingNode.kind() == TypeScript.SyntaxKind.TypeAnnotation) { return this.breakpointSpanIfStartsOnSameLine(containingNode); } } var element = positionedElement; // Syntax node if (isNode(element)) { return this.breakpointSpanOfNode(positionedElement); } // Token if (isToken(element)) { return this.breakpointSpanOfToken(positionedElement); } // List // Separated List return this.breakpointSpanOfContainingNode(positionedElement); } } export function getBreakpointLocation(syntaxTree: TypeScript.SyntaxTree, askedPos: number): TextSpan { // Cannot set breakpoint in dts file if (TypeScript.isDTSFile(syntaxTree.fileName())) { return null; } var sourceUnit = syntaxTree.sourceUnit(); var positionedToken = TypeScript.findToken(sourceUnit, askedPos); var lineMap = syntaxTree.lineMap(); var posLine = lineMap.getLineNumberFromPosition(askedPos); var tokenStartLine = lineMap.getLineNumberFromPosition(start(positionedToken)); if (posLine < tokenStartLine) { return null; } var breakpointResolver = new BreakpointResolver(posLine, lineMap); return breakpointResolver.breakpointSpanOf(positionedToken); } }