177713ef45
* Switch Debug.assertX functions to use asserts conditions * Replace assert functions with ts.noop when assertion level too low
724 lines
37 KiB
TypeScript
724 lines
37 KiB
TypeScript
/* @internal */
|
|
namespace ts.BreakpointResolver {
|
|
/**
|
|
* Get the breakpoint span in given sourceFile
|
|
*/
|
|
export function spanInSourceFileAtLocation(sourceFile: SourceFile, position: number) {
|
|
// Cannot set breakpoint in dts file
|
|
if (sourceFile.isDeclarationFile) {
|
|
return undefined;
|
|
}
|
|
|
|
let tokenAtLocation = getTokenAtPosition(sourceFile, position);
|
|
const lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
|
|
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
|
|
// Get previous token if the token is returned starts on new line
|
|
// eg: let x =10; |--- cursor is here
|
|
// let y = 10;
|
|
// token at position will return let keyword on second line as the token but we would like to use
|
|
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
|
|
const preceding = findPrecedingToken(tokenAtLocation.pos, sourceFile);
|
|
|
|
// It's a blank line
|
|
if (!preceding || sourceFile.getLineAndCharacterOfPosition(preceding.getEnd()).line !== lineOfPosition) {
|
|
return undefined;
|
|
}
|
|
tokenAtLocation = preceding;
|
|
}
|
|
|
|
// Cannot set breakpoint in ambient declarations
|
|
if (tokenAtLocation.flags & NodeFlags.Ambient) {
|
|
return undefined;
|
|
}
|
|
|
|
// Get the span in the node based on its syntax
|
|
return spanInNode(tokenAtLocation);
|
|
|
|
function textSpan(startNode: Node, endNode?: Node) {
|
|
const start = startNode.decorators ?
|
|
skipTrivia(sourceFile.text, startNode.decorators.end) :
|
|
startNode.getStart(sourceFile);
|
|
return createTextSpanFromBounds(start, (endNode || startNode).getEnd());
|
|
}
|
|
|
|
function textSpanEndingAtNextToken(startNode: Node, previousTokenToFindNextEndToken: Node): TextSpan {
|
|
return textSpan(startNode, findNextToken(previousTokenToFindNextEndToken, previousTokenToFindNextEndToken.parent, sourceFile));
|
|
}
|
|
|
|
function spanInNodeIfStartsOnSameLine(node: Node | undefined, otherwiseOnNode?: Node): TextSpan | undefined {
|
|
if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line) {
|
|
return spanInNode(node);
|
|
}
|
|
return spanInNode(otherwiseOnNode);
|
|
}
|
|
|
|
function spanInNodeArray<T extends Node>(nodeArray: NodeArray<T>) {
|
|
return createTextSpanFromBounds(skipTrivia(sourceFile.text, nodeArray.pos), nodeArray.end);
|
|
}
|
|
|
|
function spanInPreviousNode(node: Node): TextSpan | undefined {
|
|
return spanInNode(findPrecedingToken(node.pos, sourceFile));
|
|
}
|
|
|
|
function spanInNextNode(node: Node): TextSpan | undefined {
|
|
return spanInNode(findNextToken(node, node.parent, sourceFile));
|
|
}
|
|
|
|
function spanInNode(node: Node | undefined): TextSpan | undefined {
|
|
if (node) {
|
|
const { parent } = node;
|
|
switch (node.kind) {
|
|
case SyntaxKind.VariableStatement:
|
|
// Span on first variable declaration
|
|
return spanInVariableDeclaration((<VariableStatement>node).declarationList.declarations[0]);
|
|
|
|
case SyntaxKind.VariableDeclaration:
|
|
case SyntaxKind.PropertyDeclaration:
|
|
case SyntaxKind.PropertySignature:
|
|
return spanInVariableDeclaration(<VariableDeclaration | PropertyDeclaration | PropertySignature>node);
|
|
|
|
case SyntaxKind.Parameter:
|
|
return spanInParameterDeclaration(<ParameterDeclaration>node);
|
|
|
|
case SyntaxKind.FunctionDeclaration:
|
|
case SyntaxKind.MethodDeclaration:
|
|
case SyntaxKind.MethodSignature:
|
|
case SyntaxKind.GetAccessor:
|
|
case SyntaxKind.SetAccessor:
|
|
case SyntaxKind.Constructor:
|
|
case SyntaxKind.FunctionExpression:
|
|
case SyntaxKind.ArrowFunction:
|
|
return spanInFunctionDeclaration(<FunctionLikeDeclaration>node);
|
|
|
|
case SyntaxKind.Block:
|
|
if (isFunctionBlock(node)) {
|
|
return spanInFunctionBlock(<Block>node);
|
|
}
|
|
// falls through
|
|
case SyntaxKind.ModuleBlock:
|
|
return spanInBlock(<Block>node);
|
|
|
|
case SyntaxKind.CatchClause:
|
|
return spanInBlock((<CatchClause>node).block);
|
|
|
|
case SyntaxKind.ExpressionStatement:
|
|
// span on the expression
|
|
return textSpan((<ExpressionStatement>node).expression);
|
|
|
|
case SyntaxKind.ReturnStatement:
|
|
// span on return keyword and expression if present
|
|
return textSpan(node.getChildAt(0), (<ReturnStatement>node).expression);
|
|
|
|
case SyntaxKind.WhileStatement:
|
|
// Span on while(...)
|
|
return textSpanEndingAtNextToken(node, (<WhileStatement>node).expression);
|
|
|
|
case SyntaxKind.DoStatement:
|
|
// span in statement of the do statement
|
|
return spanInNode((<DoStatement>node).statement);
|
|
|
|
case SyntaxKind.DebuggerStatement:
|
|
// span on debugger keyword
|
|
return textSpan(node.getChildAt(0));
|
|
|
|
case SyntaxKind.IfStatement:
|
|
// set on if(..) span
|
|
return textSpanEndingAtNextToken(node, (<IfStatement>node).expression);
|
|
|
|
case SyntaxKind.LabeledStatement:
|
|
// span in statement
|
|
return spanInNode((<LabeledStatement>node).statement);
|
|
|
|
case SyntaxKind.BreakStatement:
|
|
case SyntaxKind.ContinueStatement:
|
|
// On break or continue keyword and label if present
|
|
return textSpan(node.getChildAt(0), (<BreakOrContinueStatement>node).label);
|
|
|
|
case SyntaxKind.ForStatement:
|
|
return spanInForStatement(<ForStatement>node);
|
|
|
|
case SyntaxKind.ForInStatement:
|
|
// span of for (a in ...)
|
|
return textSpanEndingAtNextToken(node, (<ForInStatement>node).expression);
|
|
|
|
case SyntaxKind.ForOfStatement:
|
|
// span in initializer
|
|
return spanInInitializerOfForLike(<ForOfStatement>node);
|
|
|
|
case SyntaxKind.SwitchStatement:
|
|
// span on switch(...)
|
|
return textSpanEndingAtNextToken(node, (<SwitchStatement>node).expression);
|
|
|
|
case SyntaxKind.CaseClause:
|
|
case SyntaxKind.DefaultClause:
|
|
// span in first statement of the clause
|
|
return spanInNode((<CaseOrDefaultClause>node).statements[0]);
|
|
|
|
case SyntaxKind.TryStatement:
|
|
// span in try block
|
|
return spanInBlock((<TryStatement>node).tryBlock);
|
|
|
|
case SyntaxKind.ThrowStatement:
|
|
// span in throw ...
|
|
return textSpan(node, (<ThrowStatement>node).expression);
|
|
|
|
case SyntaxKind.ExportAssignment:
|
|
// span on export = id
|
|
return textSpan(node, (<ExportAssignment>node).expression);
|
|
|
|
case SyntaxKind.ImportEqualsDeclaration:
|
|
// import statement without including semicolon
|
|
return textSpan(node, (<ImportEqualsDeclaration>node).moduleReference);
|
|
|
|
case SyntaxKind.ImportDeclaration:
|
|
// import statement without including semicolon
|
|
return textSpan(node, (<ImportDeclaration>node).moduleSpecifier);
|
|
|
|
case SyntaxKind.ExportDeclaration:
|
|
// import statement without including semicolon
|
|
return textSpan(node, (<ExportDeclaration>node).moduleSpecifier);
|
|
|
|
case SyntaxKind.ModuleDeclaration:
|
|
// span on complete module if it is instantiated
|
|
if (getModuleInstanceState(node as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
|
|
return undefined;
|
|
}
|
|
// falls through
|
|
|
|
case SyntaxKind.ClassDeclaration:
|
|
case SyntaxKind.EnumDeclaration:
|
|
case SyntaxKind.EnumMember:
|
|
case SyntaxKind.BindingElement:
|
|
// span on complete node
|
|
return textSpan(node);
|
|
|
|
case SyntaxKind.WithStatement:
|
|
// span in statement
|
|
return spanInNode((<WithStatement>node).statement);
|
|
|
|
case SyntaxKind.Decorator:
|
|
return spanInNodeArray(parent.decorators!);
|
|
|
|
case SyntaxKind.ObjectBindingPattern:
|
|
case SyntaxKind.ArrayBindingPattern:
|
|
return spanInBindingPattern(<BindingPattern>node);
|
|
|
|
// No breakpoint in interface, type alias
|
|
case SyntaxKind.InterfaceDeclaration:
|
|
case SyntaxKind.TypeAliasDeclaration:
|
|
return undefined;
|
|
|
|
// Tokens:
|
|
case SyntaxKind.SemicolonToken:
|
|
case SyntaxKind.EndOfFileToken:
|
|
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile));
|
|
|
|
case SyntaxKind.CommaToken:
|
|
return spanInPreviousNode(node);
|
|
|
|
case SyntaxKind.OpenBraceToken:
|
|
return spanInOpenBraceToken(node);
|
|
|
|
case SyntaxKind.CloseBraceToken:
|
|
return spanInCloseBraceToken(node);
|
|
|
|
case SyntaxKind.CloseBracketToken:
|
|
return spanInCloseBracketToken(node);
|
|
|
|
case SyntaxKind.OpenParenToken:
|
|
return spanInOpenParenToken(node);
|
|
|
|
case SyntaxKind.CloseParenToken:
|
|
return spanInCloseParenToken(node);
|
|
|
|
case SyntaxKind.ColonToken:
|
|
return spanInColonToken(node);
|
|
|
|
case SyntaxKind.GreaterThanToken:
|
|
case SyntaxKind.LessThanToken:
|
|
return spanInGreaterThanOrLessThanToken(node);
|
|
|
|
// Keywords:
|
|
case SyntaxKind.WhileKeyword:
|
|
return spanInWhileKeyword(node);
|
|
|
|
case SyntaxKind.ElseKeyword:
|
|
case SyntaxKind.CatchKeyword:
|
|
case SyntaxKind.FinallyKeyword:
|
|
return spanInNextNode(node);
|
|
|
|
case SyntaxKind.OfKeyword:
|
|
return spanInOfKeyword(node);
|
|
|
|
default:
|
|
// Destructuring pattern in destructuring assignment
|
|
// [a, b, c] of
|
|
// [a, b, c] = expression
|
|
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node)) {
|
|
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(<DestructuringPattern>node);
|
|
}
|
|
|
|
// Set breakpoint on identifier element of destructuring pattern
|
|
// `a` or `...c` or `d: x` from
|
|
// `[a, b, ...c]` or `{ a, b }` or `{ d: x }` from destructuring pattern
|
|
if ((node.kind === SyntaxKind.Identifier ||
|
|
node.kind === SyntaxKind.SpreadElement ||
|
|
node.kind === SyntaxKind.PropertyAssignment ||
|
|
node.kind === SyntaxKind.ShorthandPropertyAssignment) &&
|
|
isArrayLiteralOrObjectLiteralDestructuringPattern(parent)) {
|
|
return textSpan(node);
|
|
}
|
|
|
|
if (node.kind === SyntaxKind.BinaryExpression) {
|
|
const { left, operatorToken } = <BinaryExpression>node;
|
|
// Set breakpoint in destructuring pattern if its destructuring assignment
|
|
// [a, b, c] or {a, b, c} of
|
|
// [a, b, c] = expression or
|
|
// {a, b, c} = expression
|
|
if (isArrayLiteralOrObjectLiteralDestructuringPattern(left)) {
|
|
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
|
|
<ArrayLiteralExpression | ObjectLiteralExpression>left);
|
|
}
|
|
|
|
if (operatorToken.kind === SyntaxKind.EqualsToken && isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
|
// Set breakpoint on assignment expression element of destructuring pattern
|
|
// a = expression of
|
|
// [a = expression, b, c] = someExpression or
|
|
// { a = expression, b, c } = someExpression
|
|
return textSpan(node);
|
|
}
|
|
|
|
if (operatorToken.kind === SyntaxKind.CommaToken) {
|
|
return spanInNode(left);
|
|
}
|
|
}
|
|
|
|
if (isExpressionNode(node)) {
|
|
switch (parent.kind) {
|
|
case SyntaxKind.DoStatement:
|
|
// Set span as if on while keyword
|
|
return spanInPreviousNode(node);
|
|
|
|
case SyntaxKind.Decorator:
|
|
// Set breakpoint on the decorator emit
|
|
return spanInNode(node.parent);
|
|
|
|
case SyntaxKind.ForStatement:
|
|
case SyntaxKind.ForOfStatement:
|
|
return textSpan(node);
|
|
|
|
case SyntaxKind.BinaryExpression:
|
|
if ((<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.CommaToken) {
|
|
// If this is a comma expression, the breakpoint is possible in this expression
|
|
return textSpan(node);
|
|
}
|
|
break;
|
|
|
|
case SyntaxKind.ArrowFunction:
|
|
if ((<FunctionLikeDeclaration>node.parent).body === node) {
|
|
// If this is body of arrow function, it is allowed to have the breakpoint
|
|
return textSpan(node);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (node.parent.kind) {
|
|
case SyntaxKind.PropertyAssignment:
|
|
// If this is name of property assignment, set breakpoint in the initializer
|
|
if ((<PropertyAssignment>node.parent).name === node &&
|
|
!isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.parent)) {
|
|
return spanInNode((<PropertyAssignment>node.parent).initializer);
|
|
}
|
|
break;
|
|
case SyntaxKind.TypeAssertionExpression:
|
|
// Breakpoint in type assertion goes to its operand
|
|
if ((<TypeAssertion>node.parent).type === node) {
|
|
return spanInNextNode((<TypeAssertion>node.parent).type);
|
|
}
|
|
break;
|
|
case SyntaxKind.VariableDeclaration:
|
|
case SyntaxKind.Parameter: {
|
|
// initializer of variable/parameter declaration go to previous node
|
|
const { initializer, type } = <VariableDeclaration | ParameterDeclaration>node.parent;
|
|
if (initializer === node || type === node || isAssignmentOperator(node.kind)) {
|
|
return spanInPreviousNode(node);
|
|
}
|
|
break;
|
|
}
|
|
case SyntaxKind.BinaryExpression: {
|
|
const { left } = <BinaryExpression>node.parent;
|
|
if (isArrayLiteralOrObjectLiteralDestructuringPattern(left) && node !== left) {
|
|
// If initializer of destructuring assignment move to previous token
|
|
return spanInPreviousNode(node);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// return type of function go to previous token
|
|
if (isFunctionLike(node.parent) && node.parent.type === node) {
|
|
return spanInPreviousNode(node);
|
|
}
|
|
}
|
|
|
|
// Default go to parent to set the breakpoint
|
|
return spanInNode(node.parent);
|
|
}
|
|
}
|
|
|
|
function textSpanFromVariableDeclaration(variableDeclaration: VariableDeclaration | PropertyDeclaration | PropertySignature): TextSpan {
|
|
if (isVariableDeclarationList(variableDeclaration.parent) && variableDeclaration.parent.declarations[0] === variableDeclaration) {
|
|
// First declaration - include let keyword
|
|
return textSpan(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent)!, variableDeclaration);
|
|
}
|
|
else {
|
|
// Span only on this declaration
|
|
return textSpan(variableDeclaration);
|
|
}
|
|
}
|
|
|
|
function spanInVariableDeclaration(variableDeclaration: VariableDeclaration | PropertyDeclaration | PropertySignature): TextSpan | undefined {
|
|
// If declaration of for in statement, just set the span in parent
|
|
if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
|
|
return spanInNode(variableDeclaration.parent.parent);
|
|
}
|
|
|
|
const parent = variableDeclaration.parent;
|
|
// If this is a destructuring pattern, set breakpoint in binding pattern
|
|
if (isBindingPattern(variableDeclaration.name)) {
|
|
return spanInBindingPattern(variableDeclaration.name);
|
|
}
|
|
|
|
// Breakpoint is possible in variableDeclaration only if there is initialization
|
|
// or its declaration from 'for of'
|
|
if (variableDeclaration.initializer ||
|
|
hasModifier(variableDeclaration, ModifierFlags.Export) ||
|
|
parent.parent.kind === SyntaxKind.ForOfStatement) {
|
|
return textSpanFromVariableDeclaration(variableDeclaration);
|
|
}
|
|
|
|
if (isVariableDeclarationList(variableDeclaration.parent) &&
|
|
variableDeclaration.parent.declarations[0] !== variableDeclaration) {
|
|
// If we cannot set breakpoint on this declaration, set it on previous one
|
|
// Because the variable declaration may be binding pattern and
|
|
// we would like to set breakpoint in last binding element if that's the case,
|
|
// use preceding token instead
|
|
return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
|
|
}
|
|
}
|
|
|
|
function canHaveSpanInParameterDeclaration(parameter: ParameterDeclaration): boolean {
|
|
// Breakpoint is possible on parameter only if it has initializer, is a rest parameter, or has public or private modifier
|
|
return !!parameter.initializer || parameter.dotDotDotToken !== undefined ||
|
|
hasModifier(parameter, ModifierFlags.Public | ModifierFlags.Private);
|
|
}
|
|
|
|
function spanInParameterDeclaration(parameter: ParameterDeclaration): TextSpan | undefined {
|
|
if (isBindingPattern(parameter.name)) {
|
|
// Set breakpoint in binding pattern
|
|
return spanInBindingPattern(parameter.name);
|
|
}
|
|
else if (canHaveSpanInParameterDeclaration(parameter)) {
|
|
return textSpan(parameter);
|
|
}
|
|
else {
|
|
const functionDeclaration = <FunctionLikeDeclaration>parameter.parent;
|
|
const indexOfParameter = functionDeclaration.parameters.indexOf(parameter);
|
|
Debug.assert(indexOfParameter !== -1);
|
|
if (indexOfParameter !== 0) {
|
|
// Not a first parameter, go to previous parameter
|
|
return spanInParameterDeclaration(functionDeclaration.parameters[indexOfParameter - 1]);
|
|
}
|
|
else {
|
|
// Set breakpoint in the function declaration body
|
|
return spanInNode(functionDeclaration.body);
|
|
}
|
|
}
|
|
}
|
|
|
|
function canFunctionHaveSpanInWholeDeclaration(functionDeclaration: FunctionLikeDeclaration) {
|
|
return hasModifier(functionDeclaration, ModifierFlags.Export) ||
|
|
(functionDeclaration.parent.kind === SyntaxKind.ClassDeclaration && functionDeclaration.kind !== SyntaxKind.Constructor);
|
|
}
|
|
|
|
function spanInFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration): TextSpan | undefined {
|
|
// No breakpoints in the function signature
|
|
if (!functionDeclaration.body) {
|
|
return undefined;
|
|
}
|
|
|
|
if (canFunctionHaveSpanInWholeDeclaration(functionDeclaration)) {
|
|
// Set the span on whole function declaration
|
|
return textSpan(functionDeclaration);
|
|
}
|
|
|
|
// Set span in function body
|
|
return spanInNode(functionDeclaration.body);
|
|
}
|
|
|
|
function spanInFunctionBlock(block: Block): TextSpan | undefined {
|
|
const nodeForSpanInBlock = block.statements.length ? block.statements[0] : block.getLastToken();
|
|
if (canFunctionHaveSpanInWholeDeclaration(<FunctionLikeDeclaration>block.parent)) {
|
|
return spanInNodeIfStartsOnSameLine(block.parent, nodeForSpanInBlock);
|
|
}
|
|
|
|
return spanInNode(nodeForSpanInBlock);
|
|
}
|
|
|
|
function spanInBlock(block: Block): TextSpan | undefined {
|
|
switch (block.parent.kind) {
|
|
case SyntaxKind.ModuleDeclaration:
|
|
if (getModuleInstanceState(block.parent as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
|
|
return undefined;
|
|
}
|
|
|
|
// Set on parent if on same line otherwise on first statement
|
|
// falls through
|
|
case SyntaxKind.WhileStatement:
|
|
case SyntaxKind.IfStatement:
|
|
case SyntaxKind.ForInStatement:
|
|
return spanInNodeIfStartsOnSameLine(block.parent, block.statements[0]);
|
|
|
|
// Set span on previous token if it starts on same line otherwise on the first statement of the block
|
|
case SyntaxKind.ForStatement:
|
|
case SyntaxKind.ForOfStatement:
|
|
return spanInNodeIfStartsOnSameLine(findPrecedingToken(block.pos, sourceFile, block.parent), block.statements[0]);
|
|
}
|
|
|
|
// Default action is to set on first statement
|
|
return spanInNode(block.statements[0]);
|
|
}
|
|
|
|
function spanInInitializerOfForLike(forLikeStatement: ForStatement | ForOfStatement | ForInStatement): TextSpan | undefined {
|
|
if (forLikeStatement.initializer!.kind === SyntaxKind.VariableDeclarationList) {
|
|
// Declaration list - set breakpoint in first declaration
|
|
const variableDeclarationList = <VariableDeclarationList>forLikeStatement.initializer;
|
|
if (variableDeclarationList.declarations.length > 0) {
|
|
return spanInNode(variableDeclarationList.declarations[0]);
|
|
}
|
|
}
|
|
else {
|
|
// Expression - set breakpoint in it
|
|
return spanInNode(forLikeStatement.initializer);
|
|
}
|
|
}
|
|
|
|
function spanInForStatement(forStatement: ForStatement): TextSpan | undefined {
|
|
if (forStatement.initializer) {
|
|
return spanInInitializerOfForLike(forStatement);
|
|
}
|
|
|
|
if (forStatement.condition) {
|
|
return textSpan(forStatement.condition);
|
|
}
|
|
if (forStatement.incrementor) {
|
|
return textSpan(forStatement.incrementor);
|
|
}
|
|
}
|
|
|
|
function spanInBindingPattern(bindingPattern: BindingPattern): TextSpan | undefined {
|
|
// Set breakpoint in first binding element
|
|
const firstBindingElement = forEach(bindingPattern.elements,
|
|
element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
|
|
|
|
if (firstBindingElement) {
|
|
return spanInNode(firstBindingElement);
|
|
}
|
|
|
|
// Empty binding pattern of binding element, set breakpoint on binding element
|
|
if (bindingPattern.parent.kind === SyntaxKind.BindingElement) {
|
|
return textSpan(bindingPattern.parent);
|
|
}
|
|
|
|
// Variable declaration is used as the span
|
|
return textSpanFromVariableDeclaration(<VariableDeclaration>bindingPattern.parent);
|
|
}
|
|
|
|
function spanInArrayLiteralOrObjectLiteralDestructuringPattern(node: DestructuringPattern): TextSpan | undefined {
|
|
Debug.assert(node.kind !== SyntaxKind.ArrayBindingPattern && node.kind !== SyntaxKind.ObjectBindingPattern);
|
|
const elements: NodeArray<Expression | ObjectLiteralElement> = node.kind === SyntaxKind.ArrayLiteralExpression ? node.elements : node.properties;
|
|
|
|
const firstBindingElement = forEach(elements,
|
|
element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
|
|
|
|
if (firstBindingElement) {
|
|
return spanInNode(firstBindingElement);
|
|
}
|
|
|
|
// Could be ArrayLiteral from destructuring assignment or
|
|
// just nested element in another destructuring assignment
|
|
// set breakpoint on assignment when parent is destructuring assignment
|
|
// Otherwise set breakpoint for this element
|
|
return textSpan(node.parent.kind === SyntaxKind.BinaryExpression ? node.parent : node);
|
|
}
|
|
|
|
// Tokens:
|
|
function spanInOpenBraceToken(node: Node): TextSpan | undefined {
|
|
switch (node.parent.kind) {
|
|
case SyntaxKind.EnumDeclaration:
|
|
const enumDeclaration = <EnumDeclaration>node.parent;
|
|
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), enumDeclaration.members.length ? enumDeclaration.members[0] : enumDeclaration.getLastToken(sourceFile));
|
|
|
|
case SyntaxKind.ClassDeclaration:
|
|
const classDeclaration = <ClassDeclaration>node.parent;
|
|
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile, node.parent), classDeclaration.members.length ? classDeclaration.members[0] : classDeclaration.getLastToken(sourceFile));
|
|
|
|
case SyntaxKind.CaseBlock:
|
|
return spanInNodeIfStartsOnSameLine(node.parent.parent, (<CaseBlock>node.parent).clauses[0]);
|
|
}
|
|
|
|
// Default to parent node
|
|
return spanInNode(node.parent);
|
|
}
|
|
|
|
function spanInCloseBraceToken(node: Node): TextSpan | undefined {
|
|
switch (node.parent.kind) {
|
|
case SyntaxKind.ModuleBlock:
|
|
// If this is not an instantiated module block, no bp span
|
|
if (getModuleInstanceState(node.parent.parent as ModuleDeclaration) !== ModuleInstanceState.Instantiated) {
|
|
return undefined;
|
|
}
|
|
// falls through
|
|
|
|
case SyntaxKind.EnumDeclaration:
|
|
case SyntaxKind.ClassDeclaration:
|
|
// Span on close brace token
|
|
return textSpan(node);
|
|
|
|
case SyntaxKind.Block:
|
|
if (isFunctionBlock(node.parent)) {
|
|
// Span on close brace token
|
|
return textSpan(node);
|
|
}
|
|
// falls through
|
|
|
|
case SyntaxKind.CatchClause:
|
|
return spanInNode(lastOrUndefined((<Block>node.parent).statements));
|
|
|
|
case SyntaxKind.CaseBlock:
|
|
// breakpoint in last statement of the last clause
|
|
const caseBlock = <CaseBlock>node.parent;
|
|
const lastClause = lastOrUndefined(caseBlock.clauses);
|
|
if (lastClause) {
|
|
return spanInNode(lastOrUndefined(lastClause.statements));
|
|
}
|
|
return undefined;
|
|
|
|
case SyntaxKind.ObjectBindingPattern:
|
|
// Breakpoint in last binding element or binding pattern if it contains no elements
|
|
const bindingPattern = <BindingPattern>node.parent;
|
|
return spanInNode(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
|
|
|
// Default to parent node
|
|
default:
|
|
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
|
// Breakpoint in last binding element or binding pattern if it contains no elements
|
|
const objectLiteral = <ObjectLiteralExpression>node.parent;
|
|
return textSpan(lastOrUndefined(objectLiteral.properties) || objectLiteral);
|
|
}
|
|
return spanInNode(node.parent);
|
|
}
|
|
}
|
|
|
|
function spanInCloseBracketToken(node: Node): TextSpan | undefined {
|
|
switch (node.parent.kind) {
|
|
case SyntaxKind.ArrayBindingPattern:
|
|
// Breakpoint in last binding element or binding pattern if it contains no elements
|
|
const bindingPattern = <BindingPattern>node.parent;
|
|
return textSpan(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
|
|
|
default:
|
|
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
|
// Breakpoint in last binding element or binding pattern if it contains no elements
|
|
const arrayLiteral = <ArrayLiteralExpression>node.parent;
|
|
return textSpan(lastOrUndefined(arrayLiteral.elements) || arrayLiteral);
|
|
}
|
|
|
|
// Default to parent node
|
|
return spanInNode(node.parent);
|
|
}
|
|
}
|
|
|
|
function spanInOpenParenToken(node: Node): TextSpan | undefined {
|
|
if (node.parent.kind === SyntaxKind.DoStatement || // Go to while keyword and do action instead
|
|
node.parent.kind === SyntaxKind.CallExpression ||
|
|
node.parent.kind === SyntaxKind.NewExpression) {
|
|
return spanInPreviousNode(node);
|
|
}
|
|
|
|
if (node.parent.kind === SyntaxKind.ParenthesizedExpression) {
|
|
return spanInNextNode(node);
|
|
}
|
|
|
|
// Default to parent node
|
|
return spanInNode(node.parent);
|
|
}
|
|
|
|
function spanInCloseParenToken(node: Node): TextSpan | undefined {
|
|
// Is this close paren token of parameter list, set span in previous token
|
|
switch (node.parent.kind) {
|
|
case SyntaxKind.FunctionExpression:
|
|
case SyntaxKind.FunctionDeclaration:
|
|
case SyntaxKind.ArrowFunction:
|
|
case SyntaxKind.MethodDeclaration:
|
|
case SyntaxKind.MethodSignature:
|
|
case SyntaxKind.GetAccessor:
|
|
case SyntaxKind.SetAccessor:
|
|
case SyntaxKind.Constructor:
|
|
case SyntaxKind.WhileStatement:
|
|
case SyntaxKind.DoStatement:
|
|
case SyntaxKind.ForStatement:
|
|
case SyntaxKind.ForOfStatement:
|
|
case SyntaxKind.CallExpression:
|
|
case SyntaxKind.NewExpression:
|
|
case SyntaxKind.ParenthesizedExpression:
|
|
return spanInPreviousNode(node);
|
|
|
|
// Default to parent node
|
|
default:
|
|
return spanInNode(node.parent);
|
|
}
|
|
}
|
|
|
|
function spanInColonToken(node: Node): TextSpan | undefined {
|
|
// Is this : specifying return annotation of the function declaration
|
|
if (isFunctionLike(node.parent) ||
|
|
node.parent.kind === SyntaxKind.PropertyAssignment ||
|
|
node.parent.kind === SyntaxKind.Parameter) {
|
|
return spanInPreviousNode(node);
|
|
}
|
|
|
|
return spanInNode(node.parent);
|
|
}
|
|
|
|
function spanInGreaterThanOrLessThanToken(node: Node): TextSpan | undefined {
|
|
if (node.parent.kind === SyntaxKind.TypeAssertionExpression) {
|
|
return spanInNextNode(node);
|
|
}
|
|
|
|
return spanInNode(node.parent);
|
|
}
|
|
|
|
function spanInWhileKeyword(node: Node): TextSpan | undefined {
|
|
if (node.parent.kind === SyntaxKind.DoStatement) {
|
|
// Set span on while expression
|
|
return textSpanEndingAtNextToken(node, (<DoStatement>node.parent).expression);
|
|
}
|
|
|
|
// Default to parent node
|
|
return spanInNode(node.parent);
|
|
}
|
|
|
|
function spanInOfKeyword(node: Node): TextSpan | undefined {
|
|
if (node.parent.kind === SyntaxKind.ForOfStatement) {
|
|
// Set using next token
|
|
return spanInNextNode(node);
|
|
}
|
|
|
|
// Default to parent node
|
|
return spanInNode(node.parent);
|
|
}
|
|
}
|
|
}
|
|
}
|