Skip to content

Various fixes for sourcemap and breakpoints of decorators #5724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 30, 2015
56 changes: 26 additions & 30 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5307,10 +5307,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitDecoratorsOfConstructor(node: ClassLikeDeclaration) {
const decorators = node.decorators;
const constructor = getFirstConstructorWithBody(node);
const hasDecoratedParameters = constructor && forEach(constructor.parameters, nodeIsDecorated);
const firstParameterDecorator = constructor && forEach(constructor.parameters, parameter => parameter.decorators);

// skip decoration of the constructor if neither it nor its parameters are decorated
if (!decorators && !hasDecoratedParameters) {
if (!decorators && !firstParameterDecorator) {
return;
}

Expand All @@ -5326,28 +5326,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
//

writeLine();
emitStart(node);
emitStart(node.decorators || firstParameterDecorator);
emitDeclarationName(node);
write(" = __decorate([");
increaseIndent();
writeLine();

const decoratorCount = decorators ? decorators.length : 0;
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true, decorator => {
emitStart(decorator);
emit(decorator.expression);
emitEnd(decorator);
});

argumentsWritten += emitDecoratorsOfParameters(constructor, /*leadingComma*/ argumentsWritten > 0);
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true,
decorator => emit(decorator.expression));
if (firstParameterDecorator) {
argumentsWritten += emitDecoratorsOfParameters(constructor, /*leadingComma*/ argumentsWritten > 0);
}
emitSerializedTypeMetadata(node, /*leadingComma*/ argumentsWritten >= 0);

decreaseIndent();
writeLine();
write("], ");
emitDeclarationName(node);
write(");");
emitEnd(node);
write(")");
emitEnd(node.decorators || firstParameterDecorator);
write(";");
writeLine();
}

Expand All @@ -5363,11 +5362,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
continue;
}

// skip a member if it or any of its parameters are not decorated
if (!nodeOrChildIsDecorated(member)) {
continue;
}

// skip an accessor declaration if it is not the first accessor
let decorators: NodeArray<Decorator>;
let functionLikeMember: FunctionLikeDeclaration;
Expand All @@ -5394,6 +5388,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
functionLikeMember = <MethodDeclaration>member;
}
}
const firstParameterDecorator = functionLikeMember && forEach(functionLikeMember.parameters, parameter => parameter.decorators);

// skip a member if it or any of its parameters are not decorated
if (!decorators && !firstParameterDecorator) {
continue;
}

// Emit the call to __decorate. Given the following:
//
Expand Down Expand Up @@ -5427,29 +5427,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
//

writeLine();
emitStart(member);
emitStart(decorators || firstParameterDecorator);
write("__decorate([");
increaseIndent();
writeLine();

const decoratorCount = decorators ? decorators.length : 0;
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true, decorator => {
emitStart(decorator);
emit(decorator.expression);
emitEnd(decorator);
});
let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true,
decorator => emit(decorator.expression));

argumentsWritten += emitDecoratorsOfParameters(functionLikeMember, argumentsWritten > 0);
if (firstParameterDecorator) {
argumentsWritten += emitDecoratorsOfParameters(functionLikeMember, argumentsWritten > 0);
}
emitSerializedTypeMetadata(member, argumentsWritten > 0);

decreaseIndent();
writeLine();
write("], ");
emitStart(member.name);
emitClassMemberPrefix(node, member);
write(", ");
emitExpressionForPropertyName(member.name);
emitEnd(member.name);

if (languageVersion > ScriptTarget.ES3) {
if (member.kind !== SyntaxKind.PropertyDeclaration) {
Expand All @@ -5464,8 +5461,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}

write(");");
emitEnd(member);
write(")");
emitEnd(decorators || firstParameterDecorator);
write(";");
writeLine();
}
}
Expand All @@ -5478,11 +5476,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
if (nodeIsDecorated(parameter)) {
const decorators = parameter.decorators;
argumentsWritten += emitList(decorators, 0, decorators.length, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ leadingComma, /*noTrailingNewLine*/ true, decorator => {
emitStart(decorator);
write(`__param(${parameterIndex}, `);
emit(decorator.expression);
write(")");
emitEnd(decorator);
});
leadingComma = true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4902,7 +4902,7 @@ namespace ts {

if (!decorators) {
decorators = <NodeArray<Decorator>>[];
decorators.pos = scanner.getStartPos();
decorators.pos = decoratorStart;
}

const decorator = <Decorator>createNode(SyntaxKind.Decorator, decoratorStart);
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/sourcemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ namespace ts {
}

function emitStart(range: TextRange) {
emitPos(range.pos !== -1 ? skipTrivia(currentSourceFile.text, range.pos) : -1);
const rangeHasDecorators = !!(range as Node).decorators;
emitPos(range.pos !== -1 ? skipTrivia(currentSourceFile.text, rangeHasDecorators ? (range as Node).decorators.end : range.pos) : -1);
}

function emitEnd(range: TextRange) {
Expand Down
17 changes: 0 additions & 17 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -906,23 +906,6 @@ namespace ts {
return false;
}

export function childIsDecorated(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
return forEach((<ClassDeclaration>node).members, nodeOrChildIsDecorated);

case SyntaxKind.MethodDeclaration:
case SyntaxKind.SetAccessor:
return forEach((<FunctionLikeDeclaration>node).parameters, nodeIsDecorated);
}

return false;
}

export function nodeOrChildIsDecorated(node: Node): boolean {
return nodeIsDecorated(node) || childIsDecorated(node);
}

export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression {
return node.kind === SyntaxKind.PropertyAccessExpression;
}
Expand Down
21 changes: 18 additions & 3 deletions src/services/breakpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace ts.BreakpointResolver {

let tokenAtLocation = getTokenAtPosition(sourceFile, position);
let lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart()).line > lineOfPosition) {
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;
Expand All @@ -39,16 +39,23 @@ namespace ts.BreakpointResolver {
return spanInNode(tokenAtLocation);

function textSpan(startNode: Node, endNode?: Node) {
return createTextSpanFromBounds(startNode.getStart(), (endNode || startNode).getEnd());
const start = startNode.decorators ?
skipTrivia(sourceFile.text, startNode.decorators.end) :
startNode.getStart(sourceFile);
return createTextSpanFromBounds(start, (endNode || startNode).getEnd());
}

function spanInNodeIfStartsOnSameLine(node: Node, otherwiseOnNode?: Node): TextSpan {
if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart()).line) {
if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line) {
return spanInNode(node);
}
return spanInNode(otherwiseOnNode);
}

function spanInNodeArray<T>(nodeArray: NodeArray<T>) {
return createTextSpanFromBounds(skipTrivia(sourceFile.text, nodeArray.pos), nodeArray.end);
}

function spanInPreviousNode(node: Node): TextSpan {
return spanInNode(findPrecedingToken(node.pos, sourceFile));
}
Expand All @@ -65,6 +72,11 @@ namespace ts.BreakpointResolver {
return spanInPreviousNode(node);
}

if (node.parent.kind === SyntaxKind.Decorator) {
// Set breakpoint on the decorator emit
return spanInNode(node.parent);
}

if (node.parent.kind === SyntaxKind.ForStatement) {
// For now lets set the span on this expression, fix it later
return textSpan(node);
Expand Down Expand Up @@ -207,6 +219,9 @@ namespace ts.BreakpointResolver {
// span in statement
return spanInNode((<WithStatement>node).statement);

case SyntaxKind.Decorator:
return spanInNodeArray(node.parent.decorators);

// No breakpoint in interface, type alias
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
Expand Down
Loading