diff --git a/src/services/braceMatcher.ts b/src/services/braceMatcher.ts
new file mode 100644
index 00000000000..0cb336d4dc2
--- /dev/null
+++ b/src/services/braceMatcher.ts
@@ -0,0 +1,116 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript.Services {
+ export class BraceMatcher {
+
+ // Given a script name and position in the script, return a pair of text range if the
+ // position corresponds to a "brace matchin" characters (e.g. "{" or "(", etc.)
+ // If the position is not on any range, return an empty set.
+ public static getMatchSpans(syntaxTree: TypeScript.SyntaxTree, position: number): TypeScript.TextSpan[] {
+ var result: TypeScript.TextSpan[] = [];
+
+ var currentToken = findToken(syntaxTree.sourceUnit(), position);
+
+ BraceMatcher.getMatchingCloseBrace(currentToken, position, result);
+ BraceMatcher.getMatchingOpenBrace(currentToken, position, result);
+
+ return result;
+ }
+
+ private static getMatchingCloseBrace(currentToken: TypeScript.ISyntaxToken, position: number, result: TypeScript.TextSpan[]) {
+ if (start(currentToken) === position) {
+ var closingBraceKind = BraceMatcher.getMatchingCloseBraceTokenKind(currentToken);
+ if (closingBraceKind !== null) {
+ var parentElement = currentToken.parent
+ var currentPosition = fullStart(currentToken.parent);
+ for (var i = 0, n = childCount(parentElement); i < n; i++) {
+ var element = childAt(parentElement, i);
+ if (element !== null && fullWidth(element) > 0) {
+ if (element.kind() === closingBraceKind) {
+ var range1 = new TypeScript.TextSpan(position, width(currentToken));
+ var range2 = new TypeScript.TextSpan(currentPosition + leadingTriviaWidth(element), width(element));
+ result.push(range1, range2);
+ break;
+ }
+
+ currentPosition += fullWidth(element);
+ }
+ }
+ }
+ }
+ }
+
+ private static getMatchingOpenBrace(currentToken: TypeScript.ISyntaxToken, position: number, result: TypeScript.TextSpan[]) {
+ // Check if the current token to the left is a close brace
+ if (currentToken.fullStart() === position) {
+ currentToken = previousToken(currentToken);
+ }
+
+ if (currentToken !== null && start(currentToken) === (position - 1)) {
+ var openBraceKind = BraceMatcher.getMatchingOpenBraceTokenKind(currentToken);
+ if (openBraceKind !== null) {
+ var parentElement = currentToken.parent;
+ var currentPosition = fullStart(currentToken.parent) + fullWidth(parentElement);
+ for (var i = childCount(parentElement) - 1 ; i >= 0; i--) {
+ var element = childAt(parentElement, i);
+ if (element !== null && fullWidth(element) > 0) {
+ if (element.kind() === openBraceKind) {
+ var range1 = new TypeScript.TextSpan(position - 1, width(currentToken));
+ var range2 = new TypeScript.TextSpan(currentPosition - lastToken(element).trailingTriviaWidth() - width(element), width(element));
+ result.push(range1, range2);
+ break;
+ }
+
+ currentPosition -= fullWidth(element);
+ }
+ }
+ }
+ }
+ }
+
+ private static getMatchingCloseBraceTokenKind(positionedElement: TypeScript.ISyntaxElement): TypeScript.SyntaxKind {
+ var element = positionedElement !== null && positionedElement;
+ switch (element.kind()) {
+ case TypeScript.SyntaxKind.OpenBraceToken:
+ return TypeScript.SyntaxKind.CloseBraceToken
+ case TypeScript.SyntaxKind.OpenParenToken:
+ return TypeScript.SyntaxKind.CloseParenToken;
+ case TypeScript.SyntaxKind.OpenBracketToken:
+ return TypeScript.SyntaxKind.CloseBracketToken;
+ case TypeScript.SyntaxKind.LessThanToken:
+ return TypeScript.SyntaxUtilities.isAngleBracket(positionedElement) ? TypeScript.SyntaxKind.GreaterThanToken : null;
+ }
+ return null;
+ }
+
+ private static getMatchingOpenBraceTokenKind(positionedElement: TypeScript.ISyntaxElement): TypeScript.SyntaxKind {
+ var element = positionedElement !== null && positionedElement;
+ switch (element.kind()) {
+ case TypeScript.SyntaxKind.CloseBraceToken:
+ return TypeScript.SyntaxKind.OpenBraceToken
+ case TypeScript.SyntaxKind.CloseParenToken:
+ return TypeScript.SyntaxKind.OpenParenToken;
+ case TypeScript.SyntaxKind.CloseBracketToken:
+ return TypeScript.SyntaxKind.OpenBracketToken;
+ case TypeScript.SyntaxKind.GreaterThanToken:
+ return TypeScript.SyntaxUtilities.isAngleBracket(positionedElement) ? TypeScript.SyntaxKind.LessThanToken : null;
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts
new file mode 100644
index 00000000000..c1e3535d432
--- /dev/null
+++ b/src/services/breakpoints.ts
@@ -0,0 +1,1088 @@
+// 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[]): SpanInfo {
+ if (!parentElement) {
+ return null;
+ }
+
+ if (childElements.length == 0) {
+ return new SpanInfo(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 new SpanInfo(start, end);
+ }
+
+ function createBreakpointSpanInfoWithLimChar(startElement: TypeScript.ISyntaxElement, limChar: number): SpanInfo {
+ return new SpanInfo(start(startElement), limChar);
+ }
+
+ class BreakpointResolver {
+ constructor(private posLine: number, private lineMap: TypeScript.LineMap) {
+ }
+
+ private breakpointSpanOfToken(positionedToken: TypeScript.ISyntaxToken): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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[]): SpanInfo {
+ 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[]): SpanInfo {
+ 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: TypeScript.ISyntaxNode): SpanInfo {
+ 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): SpanInfo {
+ var commaExpression = commaExpressionNode;
+ return this.breakpointSpanOf(commaExpression.left);
+ }
+
+ private breakpointOfExpression(expressionNode: TypeScript.ISyntaxNode): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ if (!this.canHaveBreakpointInDeclaration(varDeclarationNode)) {
+ return null;
+ }
+
+ var container = Syntax.containingNode(varDeclarationNode);
+ var varDeclarationSyntax = varDeclarationNode;
+ var varDeclarators = varDeclarationSyntax.variableDeclarators;
+ var varDeclaratorsCount = childCount(varDeclarators); // varDeclarators has to be non null because its checked in canHaveBreakpoint
+
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ if (!this.canHaveBreakpointInDeclaration(enumDeclarationNode)) {
+ return null;
+ }
+
+ return createBreakpointSpanInfo(enumDeclarationNode);
+ }
+
+ private breakpointSpanOfFirstEnumElement(enumDeclarationNode: TypeScript.ISyntaxNode): SpanInfo {
+ var enumDeclarationSyntax = enumDeclarationNode;
+ var enumElements = enumDeclarationSyntax.enumElements;
+ if (enumElements && childCount(enumElements)) {
+ return this.breakpointSpanOf(childAt(enumElements, 0));
+ }
+
+ return null;
+ }
+
+ private breakpointSpanOfEnumElement(enumElementNode: TypeScript.ISyntaxNode): SpanInfo {
+ if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(enumElementNode)) {
+ return null;
+ }
+
+ return createBreakpointSpanInfo(enumElementNode);
+ }
+
+ private breakpointSpanOfIfStatement(ifStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var ifStatement = ifStatementNode;
+ return createBreakpointSpanInfo(ifStatementNode, ifStatement.ifKeyword, ifStatement.openParenToken, ifStatement.condition, ifStatement.closeParenToken);
+ }
+
+ private breakpointSpanOfElseClause(elseClauseNode: TypeScript.ISyntaxNode): SpanInfo {
+ var elseClause = elseClauseNode;
+ return this.breakpointSpanOf(elseClause.statement);
+ }
+
+ private breakpointSpanOfForInStatement(forInStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ 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): SpanInfo {
+ var forStatement = forStatementNode;
+ return this.breakpointSpanOf(forStatement.variableDeclaration
+ ? forStatement.variableDeclaration
+ : forStatement.initializer);
+ }
+
+ private breakpointSpanOfWhileStatement(whileStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var whileStatement = whileStatementNode;
+ return createBreakpointSpanInfo(whileStatementNode, whileStatement.whileKeyword, whileStatement.openParenToken, whileStatement.condition, whileStatement.closeParenToken);
+ }
+
+ private breakpointSpanOfDoStatement(doStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var doStatement = doStatementNode;
+ return createBreakpointSpanInfo(doStatementNode, doStatement.whileKeyword, doStatement.openParenToken, doStatement.condition, doStatement.closeParenToken);
+ }
+
+ private breakpointSpanOfSwitchStatement(switchStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var switchStatement = switchStatementNode;
+ return createBreakpointSpanInfo(switchStatementNode, switchStatement.switchKeyword, switchStatement.openParenToken, switchStatement.expression, switchStatement.closeParenToken);
+ }
+
+ private breakpointSpanOfFirstStatementOfFirstCaseClause(switchStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ 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): SpanInfo {
+ 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): SpanInfo {
+ var caseSwitchClause = caseClauseNode;
+ return this.breakpointSpanOfFirstChildOfSyntaxList(caseSwitchClause.statements);
+ }
+
+ private breakpointSpanOfDefaultSwitchClause(defaultSwithClauseNode: TypeScript.ISyntaxNode): SpanInfo {
+ var defaultSwitchClause = defaultSwithClauseNode;
+ return this.breakpointSpanOfFirstChildOfSyntaxList(defaultSwitchClause.statements);
+ }
+
+ private breakpointSpanOfWithStatement(withStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var withStatement = withStatementNode;
+ return this.breakpointSpanOf(withStatement.statement);
+ }
+
+ private breakpointSpanOfTryStatement(tryStatementNode: TypeScript.ISyntaxNode): SpanInfo {
+ var tryStatement = tryStatementNode;
+ return this.breakpointSpanOfFirstStatementInBlock(tryStatement.block);
+ }
+
+ private breakpointSpanOfCatchClause(catchClauseNode: TypeScript.ISyntaxNode): SpanInfo {
+ var catchClause = catchClauseNode;
+ return createBreakpointSpanInfo(catchClauseNode, catchClause.catchKeyword, catchClause.openParenToken, catchClause.identifier, catchClause.typeAnnotation, catchClause.closeParenToken);
+ }
+
+ private breakpointSpanOfFinallyClause(finallyClauseNode: TypeScript.ISyntaxNode): SpanInfo {
+ var finallyClause = finallyClauseNode;
+ return this.breakpointSpanOfFirstStatementInBlock(finallyClause.block);
+ }
+
+ private breakpointSpanOfParenthesizedArrowFunctionExpression(arrowFunctionExpression: ParenthesizedArrowFunctionExpressionSyntax): SpanInfo {
+ if (arrowFunctionExpression.block) {
+ return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block);
+ }
+ else {
+ return this.breakpointSpanOf(arrowFunctionExpression.expression);
+ }
+ }
+
+ private breakpointSpanOfSimpleArrowFunctionExpression(arrowFunctionExpression: SimpleArrowFunctionExpressionSyntax): SpanInfo {
+ if (arrowFunctionExpression.block) {
+ return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block);
+ }
+ else {
+ return this.breakpointSpanOf(arrowFunctionExpression.expression);
+ }
+ }
+
+ private breakpointSpanOfContainingNode(positionedElement: ISyntaxElement): SpanInfo {
+ var current = positionedElement.parent;
+ while (!isNode(current)) {
+ current = current.parent;
+ }
+
+ return this.breakpointSpanOf(current);
+ }
+
+ private breakpointSpanIfStartsOnSameLine(positionedElement: TypeScript.ISyntaxElement): SpanInfo {
+ if (positionedElement && this.posLine == this.lineMap.getLineNumberFromPosition(start(positionedElement))) {
+ return this.breakpointSpanOf(positionedElement);
+ }
+
+ return null;
+ }
+
+ public breakpointSpanOf(positionedElement: TypeScript.ISyntaxElement): SpanInfo {
+ 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): SpanInfo {
+ // 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);
+ }
+}
\ No newline at end of file
diff --git a/src/services/classifier.ts b/src/services/classifier.ts
new file mode 100644
index 00000000000..e5e68f06296
--- /dev/null
+++ b/src/services/classifier.ts
@@ -0,0 +1,194 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript.Services {
+ export enum EndOfLineState {
+ Start,
+ InMultiLineCommentTrivia,
+ InSingleQuoteStringLiteral,
+ InDoubleQuoteStringLiteral,
+ }
+
+ export enum TokenClass {
+ Punctuation,
+ Keyword,
+ Operator,
+ Comment,
+ Whitespace,
+ Identifier,
+ NumberLiteral,
+ StringLiteral,
+ RegExpLiteral,
+ }
+
+ var noRegexTable: boolean[] = [];
+ noRegexTable[TypeScript.SyntaxKind.IdentifierName] = true;
+ noRegexTable[TypeScript.SyntaxKind.StringLiteral] = true;
+ noRegexTable[TypeScript.SyntaxKind.NumericLiteral] = true;
+ noRegexTable[TypeScript.SyntaxKind.RegularExpressionLiteral] = true;
+ noRegexTable[TypeScript.SyntaxKind.ThisKeyword] = true;
+ noRegexTable[TypeScript.SyntaxKind.PlusPlusToken] = true;
+ noRegexTable[TypeScript.SyntaxKind.MinusMinusToken] = true;
+ noRegexTable[TypeScript.SyntaxKind.CloseParenToken] = true;
+ noRegexTable[TypeScript.SyntaxKind.CloseBracketToken] = true;
+ noRegexTable[TypeScript.SyntaxKind.CloseBraceToken] = true;
+ noRegexTable[TypeScript.SyntaxKind.TrueKeyword] = true;
+ noRegexTable[TypeScript.SyntaxKind.FalseKeyword] = true;
+
+ export class Classifier {
+ private scanner: TypeScript.Scanner.IScanner;
+ private lastDiagnosticKey: string = null;
+ private reportDiagnostic = (position: number, fullWidth: number, key: string, args: any[]) => {
+ this.lastDiagnosticKey = key;
+ };
+
+ constructor(public host: IClassifierHost) {
+ }
+
+ /// COLORIZATION
+ public getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
+ var offset = 0;
+ if (lexState !== EndOfLineState.Start) {
+ // If we're in a string literal, then prepend: "\
+ // (and a newline). That way when we lex we'll think we're still in a string literal.
+ //
+ // If we're in a multiline comment, then prepend: /*
+ // (and a newline). That way when we lex we'll think we're still in a multiline comment.
+ if (lexState === EndOfLineState.InDoubleQuoteStringLiteral) {
+ text = '"\\\n' + text;
+ }
+ else if (lexState === EndOfLineState.InSingleQuoteStringLiteral) {
+ text = "'\\\n" + text;
+ }
+ else if (lexState === EndOfLineState.InMultiLineCommentTrivia) {
+ text = "/*\n" + text;
+ }
+
+ offset = 3;
+ }
+
+ var result = new ClassificationResult();
+ var simpleText = TypeScript.SimpleText.fromString(text);
+ this.scanner = Scanner.createScanner(ts.ScriptTarget.ES5, simpleText, this.reportDiagnostic);
+
+ var lastTokenKind = TypeScript.SyntaxKind.None;
+ var token: ISyntaxToken = null;
+ do {
+ this.lastDiagnosticKey = null;
+
+ token = this.scanner.scan(!noRegexTable[lastTokenKind]);
+ lastTokenKind = token.kind();
+
+ this.processToken(text, simpleText, offset, token, result);
+ }
+ while (token.kind() !== SyntaxKind.EndOfFileToken);
+
+ this.lastDiagnosticKey = null;
+ return result;
+ }
+
+ private processToken(text: string, simpleText: ISimpleText, offset: number, token: TypeScript.ISyntaxToken, result: ClassificationResult): void {
+ this.processTriviaList(text, offset, token.leadingTrivia(simpleText), result);
+ this.addResult(text, offset, result, width(token), token.kind());
+ this.processTriviaList(text, offset, token.trailingTrivia(simpleText), result);
+
+ if (fullEnd(token) >= text.length) {
+ // We're at the end.
+ if (this.lastDiagnosticKey === TypeScript.DiagnosticCode.AsteriskSlash_expected) {
+ result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
+ return;
+ }
+
+ if (token.kind() === TypeScript.SyntaxKind.StringLiteral) {
+ var tokenText = token.text();
+ if (tokenText.length > 0 && tokenText.charCodeAt(tokenText.length - 1) === TypeScript.CharacterCodes.backslash) {
+ var quoteChar = tokenText.charCodeAt(0);
+ result.finalLexState = quoteChar === TypeScript.CharacterCodes.doubleQuote
+ ? EndOfLineState.InDoubleQuoteStringLiteral
+ : EndOfLineState.InSingleQuoteStringLiteral;
+ return;
+ }
+ }
+ }
+ }
+
+ private processTriviaList(text: string, offset: number, triviaList: TypeScript.ISyntaxTriviaList, result: ClassificationResult): void {
+ for (var i = 0, n = triviaList.count(); i < n; i++) {
+ var trivia = triviaList.syntaxTriviaAt(i);
+ this.addResult(text, offset, result, trivia.fullWidth(), trivia.kind());
+ }
+ }
+
+ private addResult(text: string, offset: number, result: ClassificationResult, length: number, kind: TypeScript.SyntaxKind): void {
+ if (length > 0) {
+ // If this is the first classification we're adding to the list, then remove any
+ // offset we have if we were continuing a construct from the previous line.
+ if (result.entries.length === 0) {
+ length -= offset;
+ }
+
+ result.entries.push(new ClassificationInfo(length, this.classFromKind(kind)));
+ }
+ }
+
+ private classFromKind(kind: TypeScript.SyntaxKind) {
+ if (TypeScript.SyntaxFacts.isAnyKeyword(kind)) {
+ return TokenClass.Keyword;
+ }
+ else if (TypeScript.SyntaxFacts.isBinaryExpressionOperatorToken(kind) ||
+ TypeScript.SyntaxFacts.isPrefixUnaryExpressionOperatorToken(kind)) {
+ return TokenClass.Operator;
+ }
+ else if (TypeScript.SyntaxFacts.isAnyPunctuation(kind)) {
+ return TokenClass.Punctuation;
+ }
+
+ switch (kind) {
+ case TypeScript.SyntaxKind.WhitespaceTrivia:
+ return TokenClass.Whitespace;
+ case TypeScript.SyntaxKind.MultiLineCommentTrivia:
+ case TypeScript.SyntaxKind.SingleLineCommentTrivia:
+ return TokenClass.Comment;
+ case TypeScript.SyntaxKind.NumericLiteral:
+ return TokenClass.NumberLiteral;
+ case TypeScript.SyntaxKind.StringLiteral:
+ return TokenClass.StringLiteral;
+ case TypeScript.SyntaxKind.RegularExpressionLiteral:
+ return TokenClass.RegExpLiteral;
+ case TypeScript.SyntaxKind.IdentifierName:
+ default:
+ return TokenClass.Identifier;
+ }
+ }
+ }
+
+ export interface IClassifierHost extends TypeScript.ILogger {
+ }
+
+ export class ClassificationResult {
+ public finalLexState: EndOfLineState = EndOfLineState.Start;
+ public entries: ClassificationInfo[] = [];
+
+ constructor() {
+ }
+ }
+
+ export class ClassificationInfo {
+ constructor(public length: number, public classification: TokenClass) {
+ }
+ }
+}
diff --git a/src/services/compiler/ast.ts b/src/services/compiler/ast.ts
new file mode 100644
index 00000000000..76b4903768d
--- /dev/null
+++ b/src/services/compiler/ast.ts
@@ -0,0 +1,53 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript {
+ export class Comment {
+ constructor(private _trivia: ISyntaxTrivia,
+ public endsLine: boolean,
+ public _start: number,
+ public _end: number) {
+ }
+
+ public start(): number {
+ return this._start;
+ }
+
+ public end(): number {
+ return this._end;
+ }
+
+ public fullText(): string {
+ return this._trivia.fullText();
+ }
+
+ public kind(): SyntaxKind {
+ return this._trivia.kind();
+ }
+
+ public structuralEquals(ast: Comment, includingPosition: boolean): boolean {
+ if (includingPosition) {
+ if (this.start() !== ast.start() || this.end() !== ast.end()) {
+ return false;
+ }
+ }
+
+ return this._trivia.fullText() === ast._trivia.fullText() &&
+ this.endsLine === ast.endsLine;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/astHelpers.ts b/src/services/compiler/astHelpers.ts
new file mode 100644
index 00000000000..589e4fad503
--- /dev/null
+++ b/src/services/compiler/astHelpers.ts
@@ -0,0 +1,755 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript.ASTHelpers {
+ //export function scriptIsElided(sourceUnit: SourceUnitSyntax): boolean {
+ // return isDTSFile(sourceUnit.syntaxTree.fileName()) || moduleMembersAreElided(sourceUnit.moduleElements);
+ //}
+
+ //export function moduleIsElided(declaration: ModuleDeclarationSyntax): boolean {
+ // return hasModifier(declaration.modifiers, PullElementFlags.Ambient) || moduleMembersAreElided(declaration.moduleElements);
+ //}
+
+ //function moduleMembersAreElided(members: IModuleElementSyntax[]): boolean {
+ // for (var i = 0, n = members.length; i < n; i++) {
+ // var member = members[i];
+
+ // // We should emit *this* module if it contains any non-interface types.
+ // // Caveat: if we have contain a module, then we should be emitted *if we want to
+ // // emit that inner module as well.
+ // if (member.kind() === SyntaxKind.ModuleDeclaration) {
+ // if (!moduleIsElided(member)) {
+ // return false;
+ // }
+ // }
+ // else if (member.kind() !== SyntaxKind.InterfaceDeclaration) {
+ // return false;
+ // }
+ // }
+
+ // return true;
+ //}
+
+ //export function enumIsElided(declaration: EnumDeclarationSyntax): boolean {
+ // if (hasModifier(declaration.modifiers, PullElementFlags.Ambient)) {
+ // return true;
+ // }
+
+ // return false;
+ //}
+
+ export function isValidAstNode(ast: ISyntaxElement): boolean {
+ return ast && !isShared(ast) && start(ast) !== -1 && end(ast) !== -1;
+ }
+
+ export function isValidSpan(ast: ISpan): boolean {
+ if (!ast)
+ return false;
+
+ if (ast.start() === -1 || ast.end() === -1)
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Return the ISyntaxElement containing "position"
+ ///
+ export function getAstAtPosition(script: ISyntaxElement, pos: number, useTrailingTriviaAsLimChar: boolean = true, forceInclusive: boolean = false): ISyntaxElement {
+ var top: ISyntaxElement = null;
+
+ var pre = function (cur: ISyntaxElement, walker: IAstWalker) {
+ if (!isShared(cur) && isValidAstNode(cur)) {
+ var isInvalid1 = cur.kind() === SyntaxKind.ExpressionStatement && width(cur) === 0;
+
+ if (isInvalid1) {
+ walker.options.goChildren = false;
+ }
+ else {
+ // Add "cur" to the stack if it contains our position
+ // For "identifier" nodes, we need a special case: A position equal to "limChar" is
+ // valid, since the position corresponds to a caret position (in between characters)
+ // For example:
+ // bar
+ // 0123
+ // If "position === 3", the caret is at the "right" of the "r" character, which should be considered valid
+ var inclusive =
+ forceInclusive ||
+ cur.kind() === SyntaxKind.IdentifierName ||
+ cur.kind() === SyntaxKind.MemberAccessExpression ||
+ cur.kind() === SyntaxKind.QualifiedName ||
+ //cur.kind() === SyntaxKind.TypeRef ||
+ cur.kind() === SyntaxKind.VariableDeclaration ||
+ cur.kind() === SyntaxKind.VariableDeclarator ||
+ cur.kind() === SyntaxKind.InvocationExpression ||
+ pos === end(script) + lastToken(script).trailingTriviaWidth(); // Special "EOF" case
+
+ var minChar = start(cur);
+ var limChar = end(cur) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(cur) : 0) + (inclusive ? 1 : 0);
+ if (pos >= minChar && pos < limChar) {
+
+ // Ignore empty lists
+ if ((cur.kind() !== SyntaxKind.List && cur.kind() !== SyntaxKind.SeparatedList) || end(cur) > start(cur)) {
+ // TODO: Since ISyntaxElement is sometimes not correct wrt to position, only add "cur" if it's better
+ // than top of the stack.
+ if (top === null) {
+ top = cur;
+ }
+ else if (start(cur) >= start(top) &&
+ (end(cur) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(cur) : 0)) <= (end(top) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(top) : 0))) {
+ // this new node appears to be better than the one we're
+ // storing. Make this the new node.
+
+ // However, If the current top is a missing identifier, we
+ // don't want to replace it with another missing identifier.
+ // We want to return the first missing identifier found in a
+ // depth first walk of the tree.
+ if (width(top) !== 0 || width(cur) !== 0) {
+ top = cur;
+ }
+ }
+ }
+ }
+
+ // Don't go further down the tree if pos is outside of [minChar, limChar]
+ walker.options.goChildren = (minChar <= pos && pos <= limChar);
+ }
+ }
+ };
+
+ getAstWalkerFactory().walk(script, pre);
+ return top;
+ }
+
+ export function getExtendsHeritageClause(clauses: HeritageClauseSyntax[]): HeritageClauseSyntax {
+ return getHeritageClause(clauses, SyntaxKind.ExtendsHeritageClause);
+ }
+
+ export function getImplementsHeritageClause(clauses: HeritageClauseSyntax[]): HeritageClauseSyntax {
+ return getHeritageClause(clauses, SyntaxKind.ImplementsHeritageClause);
+ }
+
+ function getHeritageClause(clauses: HeritageClauseSyntax[], kind: SyntaxKind): HeritageClauseSyntax {
+ if (clauses) {
+ for (var i = 0, n = clauses.length; i < n; i++) {
+ var child = clauses[i];
+
+ if (child.typeNames.length > 0 && child.kind() === kind) {
+ return child;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ export function isCallExpression(ast: ISyntaxElement): boolean {
+ return (ast && ast.kind() === SyntaxKind.InvocationExpression) ||
+ (ast && ast.kind() === SyntaxKind.ObjectCreationExpression);
+ }
+
+ export function isCallExpressionTarget(ast: ISyntaxElement): boolean {
+ return !!getCallExpressionTarget(ast);
+ }
+
+ export function getCallExpressionTarget(ast: ISyntaxElement): ISyntaxElement {
+ if (!ast) {
+ return null;
+ }
+
+ var current = ast;
+
+ while (current && current.parent) {
+ if (current.parent.kind() === SyntaxKind.MemberAccessExpression &&
+ (current.parent).name === current) {
+ current = current.parent;
+ continue;
+ }
+
+ break;
+ }
+
+ if (current && current.parent) {
+ if (current.parent.kind() === SyntaxKind.InvocationExpression || current.parent.kind() === SyntaxKind.ObjectCreationExpression) {
+ return current === (current.parent).expression ? current : null;
+ }
+ }
+ return null;
+ }
+
+ function isNameOfSomeDeclaration(ast: ISyntaxElement) {
+ if (ast === null || ast.parent === null) {
+ return false;
+ }
+ if (ast.kind() !== SyntaxKind.IdentifierName) {
+ return false;
+ }
+
+ switch (ast.parent.kind()) {
+ case SyntaxKind.ClassDeclaration:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.InterfaceDeclaration:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.EnumDeclaration:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.ModuleDeclaration:
+ return (ast.parent).name === ast || (ast.parent).stringLiteral === ast;
+ case SyntaxKind.VariableDeclarator:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.FunctionDeclaration:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.MemberFunctionDeclaration:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.Parameter:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.TypeParameter:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.SimplePropertyAssignment:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.FunctionPropertyAssignment:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.EnumElement:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.ImportDeclaration:
+ return (ast.parent).identifier === ast;
+ case SyntaxKind.MethodSignature:
+ return (ast.parent).propertyName === ast;
+ case SyntaxKind.PropertySignature:
+ return (ast.parent).propertyName === ast;
+ }
+
+ return false;
+ }
+
+ export function isDeclarationASTOrDeclarationNameAST(ast: ISyntaxElement) {
+ return isNameOfSomeDeclaration(ast) || ASTHelpers.isDeclarationAST(ast);
+ }
+
+ export function getEnclosingParameterForInitializer(ast: ISyntaxElement): ParameterSyntax {
+ var current = ast;
+ while (current) {
+ switch (current.kind()) {
+ case SyntaxKind.EqualsValueClause:
+ if (current.parent && current.parent.kind() === SyntaxKind.Parameter) {
+ return current.parent;
+ }
+ break;
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ // exit early
+ return null;
+ }
+
+ current = current.parent;
+ }
+ return null;
+ }
+
+ export function getEnclosingMemberDeclaration(ast: ISyntaxElement): ISyntaxElement {
+ var current = ast;
+
+ while (current) {
+ switch (current.kind()) {
+ case SyntaxKind.MemberVariableDeclaration:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.MemberFunctionDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ return current;
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ // exit early
+ return null;
+ }
+ current = current.parent;
+ }
+
+ return null;
+ }
+
+ export function isNameOfFunction(ast: ISyntaxElement) {
+ return ast
+ && ast.parent
+ && ast.kind() === SyntaxKind.IdentifierName
+ && ast.parent.kind() === SyntaxKind.FunctionDeclaration
+ && (ast.parent).identifier === ast;
+ }
+
+ export function isNameOfMemberFunction(ast: ISyntaxElement) {
+ return ast
+ && ast.parent
+ && ast.kind() === SyntaxKind.IdentifierName
+ && ast.parent.kind() === SyntaxKind.MemberFunctionDeclaration
+ && (ast.parent).propertyName === ast;
+ }
+
+ export function isNameOfMemberAccessExpression(ast: ISyntaxElement) {
+ if (ast &&
+ ast.parent &&
+ ast.parent.kind() === SyntaxKind.MemberAccessExpression &&
+ (ast.parent).name === ast) {
+
+ return true;
+ }
+
+ return false;
+ }
+
+ export function isRightSideOfQualifiedName(ast: ISyntaxElement) {
+ if (ast &&
+ ast.parent &&
+ ast.parent.kind() === SyntaxKind.QualifiedName &&
+ (ast.parent).right === ast) {
+
+ return true;
+ }
+
+ return false;
+ }
+
+ export function parentIsModuleDeclaration(ast: ISyntaxElement) {
+ return ast.parent && ast.parent.kind() === SyntaxKind.ModuleDeclaration;
+ }
+
+ export function isDeclarationAST(ast: ISyntaxElement): boolean {
+ switch (ast.kind()) {
+ case SyntaxKind.VariableDeclarator:
+ return getVariableStatement(ast) !== null;
+
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.Parameter:
+ case SyntaxKind.SimpleArrowFunctionExpression:
+ case SyntaxKind.ParenthesizedArrowFunctionExpression:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.ArrayType:
+ case SyntaxKind.ObjectType:
+ case SyntaxKind.TypeParameter:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.MemberFunctionDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.MemberVariableDeclaration:
+ case SyntaxKind.IndexMemberDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.EnumElement:
+ case SyntaxKind.SimplePropertyAssignment:
+ case SyntaxKind.FunctionPropertyAssignment:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.PropertySignature:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ export function preComments(element: ISyntaxElement, text: ISimpleText): Comment[]{
+ if (element) {
+ switch (element.kind()) {
+ case SyntaxKind.VariableStatement:
+ case SyntaxKind.ExpressionStatement:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.IfStatement:
+ case SyntaxKind.SimplePropertyAssignment:
+ case SyntaxKind.MemberFunctionDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.ReturnStatement:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.MemberVariableDeclaration:
+ case SyntaxKind.EnumElement:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.PropertySignature:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.FunctionPropertyAssignment:
+ case SyntaxKind.Parameter:
+ return convertNodeLeadingComments(element, text);
+ }
+ }
+
+ return null;
+ }
+
+ export function postComments(element: ISyntaxElement, text: ISimpleText): Comment[] {
+ if (element) {
+ switch (element.kind()) {
+ case SyntaxKind.ExpressionStatement:
+ return convertNodeTrailingComments(element, text, /*allowWithNewLine:*/ true);
+ case SyntaxKind.VariableStatement:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.IfStatement:
+ case SyntaxKind.SimplePropertyAssignment:
+ case SyntaxKind.MemberFunctionDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.ReturnStatement:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.MemberVariableDeclaration:
+ case SyntaxKind.EnumElement:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.PropertySignature:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.FunctionPropertyAssignment:
+ case SyntaxKind.Parameter:
+ return convertNodeTrailingComments(element, text);
+ }
+ }
+
+ return null;
+ }
+
+ function convertNodeTrailingComments(node: ISyntaxElement, text: ISimpleText, allowWithNewLine = false): Comment[]{
+ // Bail out quickly before doing any expensive math computation.
+ var _lastToken = lastToken(node);
+ if (_lastToken === null || !_lastToken.hasTrailingTrivia()) {
+ return null;
+ }
+
+ if (!allowWithNewLine && SyntaxUtilities.isLastTokenOnLine(_lastToken, text)) {
+ return null;
+ }
+
+ return convertComments(_lastToken.trailingTrivia(text), fullStart(node) + fullWidth(node) - _lastToken.trailingTriviaWidth(text));
+ }
+
+ function convertNodeLeadingComments(element: ISyntaxElement, text: ISimpleText): Comment[]{
+ if (element) {
+ return convertTokenLeadingComments(firstToken(element), text);
+ }
+
+ return null;
+ }
+
+ export function convertTokenLeadingComments(token: ISyntaxToken, text: ISimpleText): Comment[]{
+ if (token === null) {
+ return null;
+ }
+
+ return token.hasLeadingTrivia()
+ ? convertComments(token.leadingTrivia(text), token.fullStart())
+ : null;
+ }
+
+ export function convertTokenTrailingComments(token: ISyntaxToken, text: ISimpleText): Comment[] {
+ if (token === null) {
+ return null;
+ }
+
+ return token.hasTrailingTrivia()
+ ? convertComments(token.trailingTrivia(text), fullEnd(token) - token.trailingTriviaWidth(text))
+ : null;
+ }
+
+ function convertComments(triviaList: ISyntaxTriviaList, commentStartPosition: number): Comment[]{
+ var result: Comment[] = null;
+
+ for (var i = 0, n = triviaList.count(); i < n; i++) {
+ var trivia = triviaList.syntaxTriviaAt(i);
+
+ if (trivia.isComment()) {
+ var hasTrailingNewLine = ((i + 1) < n) && triviaList.syntaxTriviaAt(i + 1).isNewLine();
+ result = result || [];
+ result.push(convertComment(trivia, commentStartPosition, hasTrailingNewLine));
+ }
+
+ commentStartPosition += trivia.fullWidth();
+ }
+
+ return result;
+ }
+
+ function convertComment(trivia: ISyntaxTrivia, commentStartPosition: number, hasTrailingNewLine: boolean): Comment {
+ var comment = new Comment(trivia, hasTrailingNewLine, commentStartPosition, commentStartPosition + trivia.fullWidth());
+
+ return comment;
+ }
+
+ export function docComments(ast: ISyntaxElement, text: ISimpleText): Comment[] {
+ if (isDeclarationAST(ast)) {
+ var comments: Comment[] = null;
+
+ if (ast.kind() === SyntaxKind.VariableDeclarator) {
+ // Get the doc comments for a variable off of the variable statement. That's what
+ // they'll be attached to in the tree.
+ comments = TypeScript.ASTHelpers.preComments(getVariableStatement(ast), text);
+ }
+ else if (ast.kind() === SyntaxKind.Parameter) {
+ // First check if the parameter was written like so:
+ // (
+ // /** blah */ a,
+ // /** blah */ b);
+ comments = TypeScript.ASTHelpers.preComments(ast, text);
+ if (!comments) {
+ // Now check if it was written like so:
+ // (/** blah */ a, /** blah */ b);
+ // In this case, the comment will belong to the preceding token.
+ var previousToken = findToken(syntaxTree(ast).sourceUnit(), firstToken(ast).fullStart() - 1);
+ if (previousToken && (previousToken.kind() === SyntaxKind.OpenParenToken || previousToken.kind() === SyntaxKind.CommaToken)) {
+ comments = convertTokenTrailingComments(previousToken, text);
+ }
+ }
+ }
+ else {
+ comments = TypeScript.ASTHelpers.preComments(ast, text);
+ }
+
+ if (comments && comments.length > 0) {
+ return comments.filter(c => isDocComment(c));
+ }
+ }
+
+ return sentinelEmptyArray;
+ }
+
+ export function isDocComment(comment: Comment) {
+ if (comment.kind() === SyntaxKind.MultiLineCommentTrivia) {
+ var fullText = comment.fullText();
+ return fullText.charAt(2) === "*" && fullText.charAt(3) !== "/";
+ }
+
+ return false;
+ }
+
+ export function getParameterList(ast: ISyntaxElement): ParameterListSyntax {
+ if (ast) {
+ switch (ast.kind()) {
+ case SyntaxKind.ConstructorDeclaration:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.FunctionDeclaration:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.ParenthesizedArrowFunctionExpression:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.ConstructSignature:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.MemberFunctionDeclaration:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.FunctionPropertyAssignment:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.FunctionExpression:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.MethodSignature:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.ConstructorType:
+ return (ast).parameterList;
+ case SyntaxKind.FunctionType:
+ return (ast).parameterList;
+ case SyntaxKind.CallSignature:
+ return (ast).parameterList;
+ case SyntaxKind.GetAccessor:
+ return getParameterList((ast).callSignature);
+ case SyntaxKind.SetAccessor:
+ return getParameterList((ast).callSignature);
+ }
+ }
+
+ return null;
+ }
+
+ export function getType(ast: ISyntaxElement): ITypeSyntax {
+ if (ast) {
+ switch (ast.kind()) {
+ case SyntaxKind.FunctionDeclaration:
+ return getType((ast).callSignature);
+ case SyntaxKind.ParenthesizedArrowFunctionExpression:
+ return getType((ast).callSignature);
+ case SyntaxKind.ConstructSignature:
+ return getType((ast).callSignature);
+ case SyntaxKind.MemberFunctionDeclaration:
+ return getType((ast).callSignature);
+ case SyntaxKind.FunctionPropertyAssignment:
+ return getType((ast).callSignature);
+ case SyntaxKind.FunctionExpression:
+ return getType((ast).callSignature);
+ case SyntaxKind.MethodSignature:
+ return getType((ast).callSignature);
+ case SyntaxKind.CallSignature:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.IndexSignature:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.PropertySignature:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.GetAccessor:
+ return getType((ast).callSignature);
+ case SyntaxKind.Parameter:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.MemberVariableDeclaration:
+ return getType((ast).variableDeclarator);
+ case SyntaxKind.VariableDeclarator:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.CatchClause:
+ return getType((ast).typeAnnotation);
+ case SyntaxKind.ConstructorType:
+ return (ast).type;
+ case SyntaxKind.FunctionType:
+ return (ast).type;
+ case SyntaxKind.TypeAnnotation:
+ return (ast).type;
+ }
+ }
+
+ return null;
+ }
+
+ function getVariableStatement(variableDeclarator: VariableDeclaratorSyntax): VariableStatementSyntax {
+ if (variableDeclarator && variableDeclarator.parent && variableDeclarator.parent.parent && variableDeclarator.parent.parent.parent &&
+ variableDeclarator.parent.kind() === SyntaxKind.SeparatedList &&
+ variableDeclarator.parent.parent.kind() === SyntaxKind.VariableDeclaration &&
+ variableDeclarator.parent.parent.parent.kind() === SyntaxKind.VariableStatement) {
+
+ return variableDeclarator.parent.parent.parent;
+ }
+
+ return null;
+ }
+
+ export function getVariableDeclaratorModifiers(variableDeclarator: VariableDeclaratorSyntax): ISyntaxToken[] {
+ var variableStatement = getVariableStatement(variableDeclarator);
+ return variableStatement ? variableStatement.modifiers : Syntax.emptyList();
+ }
+
+ export function isIntegerLiteralAST(expression: ISyntaxElement): boolean {
+ if (expression) {
+ switch (expression.kind()) {
+ case SyntaxKind.PlusExpression:
+ case SyntaxKind.NegateExpression:
+ // Note: if there is a + or - sign, we can only allow a normal integer following
+ // (and not a hex integer). i.e. -0xA is a legal expression, but it is not a
+ // *literal*.
+ expression = (expression).operand;
+ return expression.kind() === SyntaxKind.NumericLiteral && IntegerUtilities.isInteger((expression).text());
+
+ case SyntaxKind.NumericLiteral:
+ // If it doesn't have a + or -, then either an integer literal or a hex literal
+ // is acceptable.
+ var text = (expression).text();
+ return IntegerUtilities.isInteger(text) || IntegerUtilities.isHexInteger(text);
+ }
+ }
+
+ return false;
+ }
+
+ export function getEnclosingModuleDeclaration(ast: ISyntaxElement): ModuleDeclarationSyntax {
+ while (ast) {
+ if (ast.kind() === SyntaxKind.ModuleDeclaration) {
+ return ast;
+ }
+
+ ast = ast.parent;
+ }
+
+ return null;
+ }
+
+ function isEntireNameOfModuleDeclaration(nameAST: ISyntaxElement) {
+ return parentIsModuleDeclaration(nameAST) && (nameAST.parent).name === nameAST;
+ }
+
+ export function getModuleDeclarationFromNameAST(ast: ISyntaxElement): ModuleDeclarationSyntax {
+ if (ast) {
+ switch (ast.kind()) {
+ case SyntaxKind.StringLiteral:
+ if (parentIsModuleDeclaration(ast) && (ast.parent).stringLiteral === ast) {
+ return ast.parent;
+ }
+ return null;
+
+ case SyntaxKind.IdentifierName:
+ case SyntaxKind.QualifiedName:
+ if (isEntireNameOfModuleDeclaration(ast)) {
+ return ast.parent;
+ }
+ break;
+
+ default:
+ return null;
+ }
+
+ // Only qualified names can be name of module declaration if they didnt satisfy above conditions
+ for (ast = ast.parent; ast && ast.kind() === SyntaxKind.QualifiedName; ast = ast.parent) {
+ if (isEntireNameOfModuleDeclaration(ast)) {
+ return ast.parent;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ export function isLastNameOfModule(ast: ModuleDeclarationSyntax, astName: ISyntaxElement): boolean {
+ if (ast) {
+ if (ast.stringLiteral) {
+ return astName === ast.stringLiteral;
+ }
+ else if (ast.name.kind() === SyntaxKind.QualifiedName) {
+ return astName === (ast.name).right;
+ }
+ else {
+ return astName === ast.name;
+ }
+ }
+
+ return false;
+ }
+
+ export function getNameOfIdenfierOrQualifiedName(name: ISyntaxElement): string {
+ if (name.kind() === SyntaxKind.IdentifierName) {
+ return (name).text();
+ }
+ else {
+ Debug.assert(name.kind() == SyntaxKind.QualifiedName);
+ var dotExpr = name;
+ return getNameOfIdenfierOrQualifiedName(dotExpr.left) + "." + getNameOfIdenfierOrQualifiedName(dotExpr.right);
+ }
+ }
+
+ export function getModuleNames(name: ISyntaxElement, result?: ISyntaxToken[]): ISyntaxToken[] {
+ result = result || [];
+
+ if (name.kind() === SyntaxKind.QualifiedName) {
+ getModuleNames((name).left, result);
+ result.push((name).right);
+ }
+ else {
+ result.push(name);
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/astWalker.ts b/src/services/compiler/astWalker.ts
new file mode 100644
index 00000000000..53d22142cbb
--- /dev/null
+++ b/src/services/compiler/astWalker.ts
@@ -0,0 +1,716 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript {
+ function walkListChildren(preAst: ISyntaxNodeOrToken[], walker: AstWalker): void {
+ for (var i = 0, n = preAst.length; i < n; i++) {
+ walker.walk(preAst[i]);
+ }
+ }
+
+ function walkThrowStatementChildren(preAst: ThrowStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkPrefixUnaryExpressionChildren(preAst: PrefixUnaryExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.operand);
+ }
+
+ function walkPostfixUnaryExpressionChildren(preAst: PostfixUnaryExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.operand);
+ }
+
+ function walkDeleteExpressionChildren(preAst: DeleteExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkTypeArgumentListChildren(preAst: TypeArgumentListSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeArguments);
+ }
+
+ function walkTypeOfExpressionChildren(preAst: TypeOfExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkVoidExpressionChildren(preAst: VoidExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkArgumentListChildren(preAst: ArgumentListSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeArgumentList);
+ walker.walk(preAst.arguments);
+ }
+
+ function walkArrayLiteralExpressionChildren(preAst: ArrayLiteralExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expressions);
+ }
+
+ function walkSimplePropertyAssignmentChildren(preAst: SimplePropertyAssignmentSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.expression);
+ }
+
+ function walkFunctionPropertyAssignmentChildren(preAst: FunctionPropertyAssignmentSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkGetAccessorChildren(preAst: GetAccessorSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkSeparatedListChildren(preAst: ISyntaxNodeOrToken[], walker: AstWalker): void {
+ for (var i = 0, n = preAst.length; i < n; i++) {
+ walker.walk(preAst[i]);
+ }
+ }
+
+ function walkSetAccessorChildren(preAst: SetAccessorSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkObjectLiteralExpressionChildren(preAst: ObjectLiteralExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyAssignments);
+ }
+
+ function walkCastExpressionChildren(preAst: CastExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.type);
+ walker.walk(preAst.expression);
+ }
+
+ function walkParenthesizedExpressionChildren(preAst: ParenthesizedExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkElementAccessExpressionChildren(preAst: ElementAccessExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.argumentExpression);
+ }
+
+ function walkMemberAccessExpressionChildren(preAst: MemberAccessExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.name);
+ }
+
+ function walkQualifiedNameChildren(preAst: QualifiedNameSyntax, walker: AstWalker): void {
+ walker.walk(preAst.left);
+ walker.walk(preAst.right);
+ }
+
+ function walkBinaryExpressionChildren(preAst: BinaryExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.left);
+ walker.walk(preAst.right);
+ }
+
+ function walkEqualsValueClauseChildren(preAst: EqualsValueClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.value);
+ }
+
+ function walkTypeParameterChildren(preAst: TypeParameterSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.constraint);
+ }
+
+ function walkTypeParameterListChildren(preAst: TypeParameterListSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeParameters);
+ }
+
+ function walkGenericTypeChildren(preAst: GenericTypeSyntax, walker: AstWalker): void {
+ walker.walk(preAst.name);
+ walker.walk(preAst.typeArgumentList);
+ }
+
+ function walkTypeAnnotationChildren(preAst: TypeAnnotationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.type);
+ }
+
+ function walkTypeQueryChildren(preAst: TypeQuerySyntax, walker: AstWalker): void {
+ walker.walk(preAst.name);
+ }
+
+ function walkInvocationExpressionChildren(preAst: InvocationExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.argumentList);
+ }
+
+ function walkObjectCreationExpressionChildren(preAst: ObjectCreationExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.argumentList);
+ }
+
+ function walkTrinaryExpressionChildren(preAst: ConditionalExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.condition);
+ walker.walk(preAst.whenTrue);
+ walker.walk(preAst.whenFalse);
+ }
+
+ function walkFunctionExpressionChildren(preAst: FunctionExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkFunctionTypeChildren(preAst: FunctionTypeSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeParameterList);
+ walker.walk(preAst.parameterList);
+ walker.walk(preAst.type);
+ }
+
+ function walkParenthesizedArrowFunctionExpressionChildren(preAst: ParenthesizedArrowFunctionExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ walker.walk(preAst.expression);
+ }
+
+ function walkSimpleArrowFunctionExpressionChildren(preAst: SimpleArrowFunctionExpressionSyntax, walker: AstWalker): void {
+ walker.walk(preAst.parameter);
+ walker.walk(preAst.block);
+ walker.walk(preAst.expression);
+ }
+
+ function walkMemberFunctionDeclarationChildren(preAst: MemberFunctionDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkFuncDeclChildren(preAst: FunctionDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkIndexMemberDeclarationChildren(preAst: IndexMemberDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.indexSignature);
+ }
+
+ function walkIndexSignatureChildren(preAst: IndexSignatureSyntax, walker: AstWalker): void {
+ walker.walk(preAst.parameters);
+ walker.walk(preAst.typeAnnotation);
+ }
+
+ function walkCallSignatureChildren(preAst: CallSignatureSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeParameterList);
+ walker.walk(preAst.parameterList);
+ walker.walk(preAst.typeAnnotation);
+ }
+
+ function walkConstraintChildren(preAst: ConstraintSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeOrExpression);
+ }
+
+ function walkConstructorDeclarationChildren(preAst: ConstructorDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.callSignature);
+ walker.walk(preAst.block);
+ }
+
+ function walkConstructorTypeChildren(preAst: FunctionTypeSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeParameterList);
+ walker.walk(preAst.parameterList);
+ walker.walk(preAst.type);
+ }
+
+ function walkConstructSignatureChildren(preAst: ConstructSignatureSyntax, walker: AstWalker): void {
+ walker.walk(preAst.callSignature);
+ }
+
+ function walkParameterChildren(preAst: ParameterSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.typeAnnotation);
+ walker.walk(preAst.equalsValueClause);
+ }
+
+ function walkParameterListChildren(preAst: ParameterListSyntax, walker: AstWalker): void {
+ walker.walk(preAst.parameters);
+ }
+
+ function walkPropertySignatureChildren(preAst: PropertySignatureSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.typeAnnotation);
+ }
+
+ function walkVariableDeclaratorChildren(preAst: VariableDeclaratorSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.typeAnnotation);
+ walker.walk(preAst.equalsValueClause);
+ }
+
+ function walkMemberVariableDeclarationChildren(preAst: MemberVariableDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.variableDeclarator);
+ }
+
+ function walkMethodSignatureChildren(preAst: MethodSignatureSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.callSignature);
+ }
+
+ function walkReturnStatementChildren(preAst: ReturnStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkForStatementChildren(preAst: ForStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.variableDeclaration);
+ walker.walk(preAst.initializer);
+ walker.walk(preAst.condition);
+ walker.walk(preAst.incrementor);
+ walker.walk(preAst.statement);
+ }
+
+ function walkForInStatementChildren(preAst: ForInStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.variableDeclaration);
+ walker.walk(preAst.left);
+ walker.walk(preAst.expression);
+ walker.walk(preAst.statement);
+ }
+
+ function walkIfStatementChildren(preAst: IfStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.condition);
+ walker.walk(preAst.statement);
+ walker.walk(preAst.elseClause);
+ }
+
+ function walkElseClauseChildren(preAst: ElseClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.statement);
+ }
+
+ function walkWhileStatementChildren(preAst: WhileStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.condition);
+ walker.walk(preAst.statement);
+ }
+
+ function walkDoStatementChildren(preAst: DoStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.condition);
+ walker.walk(preAst.statement);
+ }
+
+ function walkBlockChildren(preAst: BlockSyntax, walker: AstWalker): void {
+ walker.walk(preAst.statements);
+ }
+
+ function walkVariableDeclarationChildren(preAst: VariableDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.variableDeclarators);
+ }
+
+ function walkCaseSwitchClauseChildren(preAst: CaseSwitchClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.statements);
+ }
+
+ function walkDefaultSwitchClauseChildren(preAst: DefaultSwitchClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.statements);
+ }
+
+ function walkSwitchStatementChildren(preAst: SwitchStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ walker.walk(preAst.switchClauses);
+ }
+
+ function walkTryStatementChildren(preAst: TryStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.block);
+ walker.walk(preAst.catchClause);
+ walker.walk(preAst.finallyClause);
+ }
+
+ function walkCatchClauseChildren(preAst: CatchClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.typeAnnotation);
+ walker.walk(preAst.block);
+ }
+
+ function walkExternalModuleReferenceChildren(preAst: ExternalModuleReferenceSyntax, walker: AstWalker): void {
+ walker.walk(preAst.stringLiteral);
+ }
+
+ function walkFinallyClauseChildren(preAst: FinallyClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.block);
+ }
+
+ function walkClassDeclChildren(preAst: ClassDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.typeParameterList);
+ walker.walk(preAst.heritageClauses);
+ walker.walk(preAst.classElements);
+ }
+
+ function walkScriptChildren(preAst: SourceUnitSyntax, walker: AstWalker): void {
+ walker.walk(preAst.moduleElements);
+ }
+
+ function walkHeritageClauseChildren(preAst: HeritageClauseSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeNames);
+ }
+
+ function walkInterfaceDeclerationChildren(preAst: InterfaceDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.typeParameterList);
+ walker.walk(preAst.heritageClauses);
+ walker.walk(preAst.body);
+ }
+
+ function walkObjectTypeChildren(preAst: ObjectTypeSyntax, walker: AstWalker): void {
+ walker.walk(preAst.typeMembers);
+ }
+
+ function walkArrayTypeChildren(preAst: ArrayTypeSyntax, walker: AstWalker): void {
+ walker.walk(preAst.type);
+ }
+
+ function walkModuleDeclarationChildren(preAst: ModuleDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.name);
+ walker.walk(preAst.stringLiteral);
+ walker.walk(preAst.moduleElements);
+ }
+
+ function walkModuleNameModuleReferenceChildren(preAst: ModuleNameModuleReferenceSyntax, walker: AstWalker): void {
+ walker.walk(preAst.moduleName);
+ }
+
+ function walkEnumDeclarationChildren(preAst: EnumDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.enumElements);
+ }
+
+ function walkEnumElementChildren(preAst: EnumElementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.propertyName);
+ walker.walk(preAst.equalsValueClause);
+ }
+
+ function walkImportDeclarationChildren(preAst: ImportDeclarationSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.moduleReference);
+ }
+
+ function walkExportAssignmentChildren(preAst: ExportAssignmentSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ }
+
+ function walkWithStatementChildren(preAst: WithStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.condition);
+ walker.walk(preAst.statement);
+ }
+
+ function walkExpressionStatementChildren(preAst: ExpressionStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.expression);
+ }
+
+ function walkLabeledStatementChildren(preAst: LabeledStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.identifier);
+ walker.walk(preAst.statement);
+ }
+
+ function walkVariableStatementChildren(preAst: VariableStatementSyntax, walker: AstWalker): void {
+ walker.walk(preAst.variableDeclaration);
+ }
+
+ var childrenWalkers: IAstWalkChildren[] = new Array(SyntaxKind.LastNode + 1);
+
+ // Tokens/trivia can't ever be walked into.
+ for (var i = SyntaxKind.FirstToken, n = SyntaxKind.LastToken; i <= n; i++) {
+ childrenWalkers[i] = null;
+ }
+ for (var i = SyntaxKind.FirstTrivia, n = SyntaxKind.LastTrivia; i <= n; i++) {
+ childrenWalkers[i] = null;
+ }
+
+ childrenWalkers[SyntaxKind.AddAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.AddExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.AndAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.AnyKeyword] = null;
+ childrenWalkers[SyntaxKind.ArgumentList] = walkArgumentListChildren;
+ childrenWalkers[SyntaxKind.ArrayLiteralExpression] = walkArrayLiteralExpressionChildren;
+ childrenWalkers[SyntaxKind.ArrayType] = walkArrayTypeChildren;
+ childrenWalkers[SyntaxKind.SimpleArrowFunctionExpression] = walkSimpleArrowFunctionExpressionChildren;
+ childrenWalkers[SyntaxKind.ParenthesizedArrowFunctionExpression] = walkParenthesizedArrowFunctionExpressionChildren;
+ childrenWalkers[SyntaxKind.AssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.BitwiseAndExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.BitwiseExclusiveOrExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.BitwiseNotExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.BitwiseOrExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.Block] = walkBlockChildren;
+ childrenWalkers[SyntaxKind.BooleanKeyword] = null;
+ childrenWalkers[SyntaxKind.BreakStatement] = null;
+ childrenWalkers[SyntaxKind.CallSignature] = walkCallSignatureChildren;
+ childrenWalkers[SyntaxKind.CaseSwitchClause] = walkCaseSwitchClauseChildren;
+ childrenWalkers[SyntaxKind.CastExpression] = walkCastExpressionChildren;
+ childrenWalkers[SyntaxKind.CatchClause] = walkCatchClauseChildren;
+ childrenWalkers[SyntaxKind.ClassDeclaration] = walkClassDeclChildren;
+ childrenWalkers[SyntaxKind.CommaExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.ConditionalExpression] = walkTrinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.Constraint] = walkConstraintChildren;
+ childrenWalkers[SyntaxKind.ConstructorDeclaration] = walkConstructorDeclarationChildren;
+ childrenWalkers[SyntaxKind.ConstructSignature] = walkConstructSignatureChildren;
+ childrenWalkers[SyntaxKind.ContinueStatement] = null;
+ childrenWalkers[SyntaxKind.ConstructorType] = walkConstructorTypeChildren;
+ childrenWalkers[SyntaxKind.DebuggerStatement] = null;
+ childrenWalkers[SyntaxKind.DefaultSwitchClause] = walkDefaultSwitchClauseChildren;
+ childrenWalkers[SyntaxKind.DeleteExpression] = walkDeleteExpressionChildren;
+ childrenWalkers[SyntaxKind.DivideAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.DivideExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.DoStatement] = walkDoStatementChildren;
+ childrenWalkers[SyntaxKind.ElementAccessExpression] = walkElementAccessExpressionChildren;
+ childrenWalkers[SyntaxKind.ElseClause] = walkElseClauseChildren;
+ childrenWalkers[SyntaxKind.EmptyStatement] = null;
+ childrenWalkers[SyntaxKind.EnumDeclaration] = walkEnumDeclarationChildren;
+ childrenWalkers[SyntaxKind.EnumElement] = walkEnumElementChildren;
+ childrenWalkers[SyntaxKind.EqualsExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.EqualsValueClause] = walkEqualsValueClauseChildren;
+ childrenWalkers[SyntaxKind.EqualsWithTypeConversionExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.ExclusiveOrAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.ExportAssignment] = walkExportAssignmentChildren;
+ childrenWalkers[SyntaxKind.ExpressionStatement] = walkExpressionStatementChildren;
+ childrenWalkers[SyntaxKind.ExtendsHeritageClause] = walkHeritageClauseChildren;
+ childrenWalkers[SyntaxKind.ExternalModuleReference] = walkExternalModuleReferenceChildren;
+ childrenWalkers[SyntaxKind.FalseKeyword] = null;
+ childrenWalkers[SyntaxKind.FinallyClause] = walkFinallyClauseChildren;
+ childrenWalkers[SyntaxKind.ForInStatement] = walkForInStatementChildren;
+ childrenWalkers[SyntaxKind.ForStatement] = walkForStatementChildren;
+ childrenWalkers[SyntaxKind.FunctionDeclaration] = walkFuncDeclChildren;
+ childrenWalkers[SyntaxKind.FunctionExpression] = walkFunctionExpressionChildren;
+ childrenWalkers[SyntaxKind.FunctionPropertyAssignment] = walkFunctionPropertyAssignmentChildren;
+ childrenWalkers[SyntaxKind.FunctionType] = walkFunctionTypeChildren;
+ childrenWalkers[SyntaxKind.GenericType] = walkGenericTypeChildren;
+ childrenWalkers[SyntaxKind.GetAccessor] = walkGetAccessorChildren;
+ childrenWalkers[SyntaxKind.GreaterThanExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.GreaterThanOrEqualExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.IfStatement] = walkIfStatementChildren;
+ childrenWalkers[SyntaxKind.ImplementsHeritageClause] = walkHeritageClauseChildren;
+ childrenWalkers[SyntaxKind.ImportDeclaration] = walkImportDeclarationChildren;
+ childrenWalkers[SyntaxKind.IndexMemberDeclaration] = walkIndexMemberDeclarationChildren;
+ childrenWalkers[SyntaxKind.IndexSignature] = walkIndexSignatureChildren;
+ childrenWalkers[SyntaxKind.InExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.InstanceOfExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.InterfaceDeclaration] = walkInterfaceDeclerationChildren;
+ childrenWalkers[SyntaxKind.InvocationExpression] = walkInvocationExpressionChildren;
+ childrenWalkers[SyntaxKind.LabeledStatement] = walkLabeledStatementChildren;
+ childrenWalkers[SyntaxKind.LeftShiftAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.LeftShiftExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.LessThanExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.LessThanOrEqualExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.List] = walkListChildren;
+ childrenWalkers[SyntaxKind.LogicalAndExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.LogicalNotExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.LogicalOrExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.MemberAccessExpression] = walkMemberAccessExpressionChildren;
+ childrenWalkers[SyntaxKind.MemberFunctionDeclaration] = walkMemberFunctionDeclarationChildren;
+ childrenWalkers[SyntaxKind.MemberVariableDeclaration] = walkMemberVariableDeclarationChildren;
+ childrenWalkers[SyntaxKind.MethodSignature] = walkMethodSignatureChildren;
+ childrenWalkers[SyntaxKind.ModuleDeclaration] = walkModuleDeclarationChildren;
+ childrenWalkers[SyntaxKind.ModuleNameModuleReference] = walkModuleNameModuleReferenceChildren;
+ childrenWalkers[SyntaxKind.ModuloAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.ModuloExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.MultiplyAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.MultiplyExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.IdentifierName] = null;
+ childrenWalkers[SyntaxKind.NegateExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.None] = null;
+ childrenWalkers[SyntaxKind.NotEqualsExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.NotEqualsWithTypeConversionExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.NullKeyword] = null;
+ childrenWalkers[SyntaxKind.NumberKeyword] = null;
+ childrenWalkers[SyntaxKind.NumericLiteral] = null;
+ childrenWalkers[SyntaxKind.ObjectCreationExpression] = walkObjectCreationExpressionChildren;
+ childrenWalkers[SyntaxKind.ObjectLiteralExpression] = walkObjectLiteralExpressionChildren;
+ childrenWalkers[SyntaxKind.ObjectType] = walkObjectTypeChildren;
+ childrenWalkers[SyntaxKind.OmittedExpression] = null;
+ childrenWalkers[SyntaxKind.OrAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.Parameter] = walkParameterChildren;
+ childrenWalkers[SyntaxKind.ParameterList] = walkParameterListChildren;
+ childrenWalkers[SyntaxKind.ParenthesizedExpression] = walkParenthesizedExpressionChildren;
+ childrenWalkers[SyntaxKind.PlusExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.PostDecrementExpression] = walkPostfixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.PostIncrementExpression] = walkPostfixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.PreDecrementExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.PreIncrementExpression] = walkPrefixUnaryExpressionChildren;
+ childrenWalkers[SyntaxKind.PropertySignature] = walkPropertySignatureChildren;
+ childrenWalkers[SyntaxKind.QualifiedName] = walkQualifiedNameChildren;
+ childrenWalkers[SyntaxKind.RegularExpressionLiteral] = null;
+ childrenWalkers[SyntaxKind.ReturnStatement] = walkReturnStatementChildren;
+ childrenWalkers[SyntaxKind.SourceUnit] = walkScriptChildren;
+ childrenWalkers[SyntaxKind.SeparatedList] = walkSeparatedListChildren;
+ childrenWalkers[SyntaxKind.SetAccessor] = walkSetAccessorChildren;
+ childrenWalkers[SyntaxKind.SignedRightShiftAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.SignedRightShiftExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.SimplePropertyAssignment] = walkSimplePropertyAssignmentChildren;
+ childrenWalkers[SyntaxKind.StringLiteral] = null;
+ childrenWalkers[SyntaxKind.StringKeyword] = null;
+ childrenWalkers[SyntaxKind.SubtractAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.SubtractExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.SuperKeyword] = null;
+ childrenWalkers[SyntaxKind.SwitchStatement] = walkSwitchStatementChildren;
+ childrenWalkers[SyntaxKind.ThisKeyword] = null;
+ childrenWalkers[SyntaxKind.ThrowStatement] = walkThrowStatementChildren;
+ childrenWalkers[SyntaxKind.TriviaList] = null;
+ childrenWalkers[SyntaxKind.TrueKeyword] = null;
+ childrenWalkers[SyntaxKind.TryStatement] = walkTryStatementChildren;
+ childrenWalkers[SyntaxKind.TypeAnnotation] = walkTypeAnnotationChildren;
+ childrenWalkers[SyntaxKind.TypeArgumentList] = walkTypeArgumentListChildren;
+ childrenWalkers[SyntaxKind.TypeOfExpression] = walkTypeOfExpressionChildren;
+ childrenWalkers[SyntaxKind.TypeParameter] = walkTypeParameterChildren;
+ childrenWalkers[SyntaxKind.TypeParameterList] = walkTypeParameterListChildren;
+ childrenWalkers[SyntaxKind.TypeQuery] = walkTypeQueryChildren;
+ childrenWalkers[SyntaxKind.UnsignedRightShiftAssignmentExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.UnsignedRightShiftExpression] = walkBinaryExpressionChildren;
+ childrenWalkers[SyntaxKind.VariableDeclaration] = walkVariableDeclarationChildren;
+ childrenWalkers[SyntaxKind.VariableDeclarator] = walkVariableDeclaratorChildren;
+ childrenWalkers[SyntaxKind.VariableStatement] = walkVariableStatementChildren;
+ childrenWalkers[SyntaxKind.VoidExpression] = walkVoidExpressionChildren;
+ childrenWalkers[SyntaxKind.VoidKeyword] = null;
+ childrenWalkers[SyntaxKind.WhileStatement] = walkWhileStatementChildren;
+ childrenWalkers[SyntaxKind.WithStatement] = walkWithStatementChildren;
+
+ // Verify the code is up to date with the enum
+ for (var e in SyntaxKind) {
+ if (SyntaxKind.hasOwnProperty(e) && StringUtilities.isString(SyntaxKind[e])) {
+ TypeScript.Debug.assert(childrenWalkers[e] !== undefined, "Fix initWalkers: " + SyntaxKind[e]);
+ }
+ }
+
+ export class AstWalkOptions {
+ public goChildren = true;
+ public stopWalking = false;
+ }
+
+ interface IAstWalkChildren {
+ (preAst: ISyntaxElement, walker: AstWalker): void;
+ }
+
+ export interface IAstWalker {
+ options: AstWalkOptions;
+ state: any
+ }
+
+ interface AstWalker {
+ walk(ast: ISyntaxElement): void;
+ }
+
+ class SimplePreAstWalker implements AstWalker {
+ public options: AstWalkOptions = new AstWalkOptions();
+
+ constructor(
+ private pre: (ast: ISyntaxElement, state: any) => void,
+ public state: any) {
+ }
+
+ public walk(ast: ISyntaxElement): void {
+ if (!ast) {
+ return;
+ }
+
+ this.pre(ast, this.state);
+
+ var walker = childrenWalkers[ast.kind()];
+ if (walker) {
+ walker(ast, this);
+ }
+ }
+ }
+
+ class SimplePrePostAstWalker implements AstWalker {
+ public options: AstWalkOptions = new AstWalkOptions();
+
+ constructor(
+ private pre: (ast: ISyntaxElement, state: any) => void,
+ private post: (ast: ISyntaxElement, state: any) => void,
+ public state: any) {
+ }
+
+ public walk(ast: ISyntaxElement): void {
+ if (!ast) {
+ return;
+ }
+
+ this.pre(ast, this.state);
+
+ var walker = childrenWalkers[ast.kind()];
+ if (walker) {
+ walker(ast, this);
+ }
+
+ this.post(ast, this.state);
+ }
+ }
+
+ class NormalAstWalker implements AstWalker {
+ public options: AstWalkOptions = new AstWalkOptions();
+
+ constructor(
+ private pre: (ast: ISyntaxElement, walker: IAstWalker) => void,
+ private post: (ast: ISyntaxElement, walker: IAstWalker) => void,
+ public state: any) {
+ }
+
+ public walk(ast: ISyntaxElement): void {
+ if (!ast) {
+ return;
+ }
+
+ // If we're stopping, then bail out immediately.
+ if (this.options.stopWalking) {
+ return;
+ }
+
+ this.pre(ast, this);
+
+ // If we were asked to stop, then stop.
+ if (this.options.stopWalking) {
+ return;
+ }
+
+ if (this.options.goChildren) {
+ // Call the "walkChildren" function corresponding to "nodeType".
+ var walker = childrenWalkers[ast.kind()];
+ if (walker) {
+ walker(ast, this);
+ }
+ }
+ else {
+ // no go only applies to children of node issuing it
+ this.options.goChildren = true;
+ }
+
+ if (this.post) {
+ this.post(ast, this);
+ }
+ }
+ }
+
+ export class AstWalkerFactory {
+ public walk(ast: ISyntaxElement, pre: (ast: ISyntaxElement, walker: IAstWalker) => void, post?: (ast: ISyntaxElement, walker: IAstWalker) => void, state?: any): void {
+ new NormalAstWalker(pre, post, state).walk(ast);
+ }
+
+ public simpleWalk(ast: ISyntaxElement, pre: (ast: ISyntaxElement, state: any) => void, post?: (ast: ISyntaxElement, state: any) => void, state?: any): void {
+ if (post) {
+ new SimplePrePostAstWalker(pre, post, state).walk(ast);
+ }
+ else {
+ new SimplePreAstWalker(pre, state).walk(ast);
+ }
+ }
+ }
+
+ var globalAstWalkerFactory = new AstWalkerFactory();
+
+ export function getAstWalkerFactory(): AstWalkerFactory {
+ return globalAstWalkerFactory;
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/base64.ts b/src/services/compiler/base64.ts
new file mode 100644
index 00000000000..06eac4f4b74
--- /dev/null
+++ b/src/services/compiler/base64.ts
@@ -0,0 +1,96 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+module TypeScript {
+ class Base64Format {
+ static encodedValues = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ static encode(inValue: number) {
+ if (inValue < 64) {
+ return Base64Format.encodedValues.charAt(inValue);
+ }
+ throw TypeError(inValue + ": not a 64 based value");
+ }
+
+ static decodeChar(inChar: string) {
+ if (inChar.length === 1) {
+ return Base64Format.encodedValues.indexOf(inChar);
+ }
+ else {
+ throw TypeError('"' + inChar + '" must have length 1');
+ }
+ }
+ }
+
+ export class Base64VLQFormat {
+ static encode(inValue: number) {
+ // Add a new least significant bit that has the sign of the value.
+ // if negative number the least significant bit that gets added to the number has value 1
+ // else least significant bit value that gets added is 0
+ // eg. -1 changes to binary : 01 [1] => 3
+ // +1 changes to binary : 01 [0] => 2
+ if (inValue < 0) {
+ inValue = ((-inValue) << 1) + 1;
+ }
+ else {
+ inValue = inValue << 1;
+ }
+
+ // Encode 5 bits at a time starting from least significant bits
+ var encodedStr = "";
+ do {
+ var currentDigit = inValue & 31; // 11111
+ inValue = inValue >> 5;
+ if (inValue > 0) {
+ // There are still more digits to decode, set the msb (6th bit)
+ currentDigit = currentDigit | 32;
+ }
+ encodedStr = encodedStr + Base64Format.encode(currentDigit);
+ } while (inValue > 0);
+
+ return encodedStr;
+ }
+
+ static decode(inString: string) {
+ var result = 0;
+ var negative = false;
+
+ var shift = 0;
+ for (var i = 0; i < inString.length; i++) {
+ var byte = Base64Format.decodeChar(inString[i]);
+ if (i === 0) {
+ // Sign bit appears in the LSBit of the first value
+ if ((byte & 1) === 1) {
+ negative = true;
+ }
+ result = (byte >> 1) & 15; // 1111x
+ }
+ else {
+ result = result | ((byte & 31) << shift); // 11111
+ }
+
+ shift += (i === 0) ? 4 : 5;
+
+ if ((byte & 32) === 32) {
+ // Continue
+ }
+ else {
+ return { value: negative ? -(result) : result, rest: inString.substr(i + 1) };
+ }
+ }
+
+ throw new Error(getDiagnosticMessage(DiagnosticCode.Base64_value_0_finished_with_a_continuation_bit, [inString]));
+ }
+ }
+}
diff --git a/src/services/compiler/bloomFilter.ts b/src/services/compiler/bloomFilter.ts
new file mode 100644
index 00000000000..fe841b6f7a3
--- /dev/null
+++ b/src/services/compiler/bloomFilter.ts
@@ -0,0 +1,131 @@
+///
+
+module TypeScript {
+
+ export class BloomFilter {
+ private bitArray: boolean[];
+ private hashFunctionCount: number;
+
+ public static falsePositiveProbability: number = 0.0001;
+
+ /*
+ * From the bloom filter calculator here: http://hur.st/bloomfilter?n=4&p=1.0E-20
+ *
+ * 1) n = Number of items in the filter
+ *
+ * 2) p = Probability of false positives, (a double between 0 and 1).
+ *
+ * 3) m = Number of bits in the filter
+ *
+ * 4) k = Number of hash functions
+ *
+ * m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0)))))
+ *
+ * k = round(log(2.0) * m / n)
+ *
+ */
+ constructor(expectedCount: number) {
+ var m: number = Math.max(1, BloomFilter.computeM(expectedCount));
+ var k: number = Math.max(1, BloomFilter.computeK(expectedCount));;
+
+ // We must have size in even bytes, so that when we deserialize from bytes we get a bit array with the same count.
+ // The count is used by the hash functions.
+ var sizeInEvenBytes = (m + 7) & ~7;
+
+ this.bitArray = [];
+ for (var i = 0, len = sizeInEvenBytes; i < len; i++) {
+ this.bitArray[i] = false;
+ }
+ this.hashFunctionCount = k;
+ }
+
+ // m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0)))))
+ static computeM(expectedCount: number): number {
+ var p: number = BloomFilter.falsePositiveProbability;
+ var n: number = expectedCount;
+
+ var numerator = n * Math.log(p);
+ var denominator = Math.log(1.0 / Math.pow(2.0, Math.log(2.0)));
+ return Math.ceil(numerator / denominator);
+ }
+
+ // k = round(log(2.0) * m / n)
+ static computeK(expectedCount: number): number {
+ var n: number = expectedCount;
+ var m: number = BloomFilter.computeM(expectedCount);
+
+ var temp = Math.log(2.0) * m / n;
+ return Math.round(temp);
+ }
+
+ /** Modification of the murmurhash2 algorithm. Code is simpler because it operates over
+ * strings instead of byte arrays. Because each string character is two bytes, it is known
+ * that the input will be an even number of bytes (though not necessarily a multiple of 4).
+ *
+ * This is needed over the normal 'string.GetHashCode()' because we need to be able to generate
+ * 'k' different well distributed hashes for any given string s. Also, we want to be able to
+ * generate these hashes without allocating any memory. My ideal solution would be to use an
+ * MD5 hash. However, there appears to be no way to do MD5 in .Net where you can:
+ *
+ * a) feed it individual values instead of a byte[]
+ *
+ * b) have the hash computed into a byte[] you provide instead of a newly allocated one
+ *
+ * Generating 'k' pieces of garbage on each insert and lookup seems very wasteful. So,
+ * instead, we use murmur hash since it provides well distributed values, allows for a
+ * seed, and allocates no memory.
+ *
+ * Murmur hash is public domain. Actual code is included below as reference.
+ */
+ private computeHash(key: string, seed: number): number {
+ return Hash.computeMurmur2StringHashCode(key, seed);
+ }
+
+ public addKeys(keys: IIndexable) {
+ for (var name in keys) {
+ if (keys[name]) {
+ this.add(name);
+ }
+ }
+ }
+
+ public add(value: string) {
+ for (var i = 0; i < this.hashFunctionCount; i++) {
+ var hash = this.computeHash(value, i);
+ hash = hash % this.bitArray.length;
+ this.bitArray[Math.abs(hash)] = true;
+ }
+ }
+
+ public probablyContains(value: string): boolean {
+ for (var i = 0; i < this.hashFunctionCount; i++) {
+ var hash = this.computeHash(value, i);
+ hash = hash % this.bitArray.length;
+ if (!this.bitArray[Math.abs(hash)]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public isEquivalent(filter: BloomFilter): boolean {
+ return BloomFilter.isEquivalent(this.bitArray, filter.bitArray)
+ && this.hashFunctionCount === filter.hashFunctionCount;
+ }
+
+ static isEquivalent(array1: boolean[], array2: boolean[]): boolean {
+ if (array1.length !== array2.length) {
+ return false;
+ }
+
+ for (var i = 0; i < array1.length; i++) {
+ if (array1[i] !== array2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/services/compiler/declarationEmitter.ts b/src/services/compiler/declarationEmitter.ts
new file mode 100644
index 00000000000..d3052389cb0
--- /dev/null
+++ b/src/services/compiler/declarationEmitter.ts
@@ -0,0 +1,1138 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript {
+ export class TextWriter {
+ private contents = "";
+ public onNewLine = true;
+ constructor(private name: string, private writeByteOrderMark: boolean, private outputFileType: OutputFileType) {
+ }
+
+ public Write(s: string) {
+ this.contents += s;
+ this.onNewLine = false;
+ }
+
+ public WriteLine(s: string) {
+ this.contents += s;
+ this.contents += TypeScript.newLine();
+ this.onNewLine = true;
+ }
+
+ public Close(): void {
+ }
+
+ public getOutputFile(): OutputFile {
+ return new OutputFile(this.name, this.writeByteOrderMark, this.contents, this.outputFileType);
+ }
+ }
+
+ export class DeclarationEmitter {
+ private declFile: TextWriter = null;
+ private indenter = new Indenter();
+ private emittedReferencePaths = false;
+
+ constructor(private emittingFileName: string,
+ public document: Document,
+ private compiler: TypeScriptCompiler,
+ private emitOptions: EmitOptions,
+ private semanticInfoChain: SemanticInfoChain) {
+ this.declFile = new TextWriter(emittingFileName, this.document.byteOrderMark !== ByteOrderMark.None, OutputFileType.Declaration);
+ }
+
+ public getOutputFile(): OutputFile {
+ return this.declFile.getOutputFile();
+ }
+
+ public emitDeclarations(sourceUnit: SourceUnitSyntax) {
+ this.emitDeclarationsForSourceUnit(sourceUnit);
+ }
+
+ private emitDeclarationsForList(list: ISyntaxNodeOrToken[]) {
+ for (var i = 0, n = list.length; i < n; i++) {
+ this.emitDeclarationsForAST(list[i]);
+ }
+ }
+
+ private emitSeparatedList(list: ISyntaxNodeOrToken[]) {
+ for (var i = 0, n = list.length; i < n; i++) {
+ this.emitDeclarationsForAST(list[i]);
+ }
+ }
+
+ private emitDeclarationsForAST(ast: ISyntaxElement) {
+ switch (ast.kind()) {
+ case SyntaxKind.VariableStatement:
+ return this.emitDeclarationsForVariableStatement(ast);
+ case SyntaxKind.PropertySignature:
+ return this.emitPropertySignature(ast);
+ case SyntaxKind.VariableDeclarator:
+ return this.emitVariableDeclarator(ast, true, true);
+ case SyntaxKind.MemberVariableDeclaration:
+ return this.emitDeclarationsForMemberVariableDeclaration(ast);
+ case SyntaxKind.ConstructorDeclaration:
+ return this.emitDeclarationsForConstructorDeclaration(ast);
+ case SyntaxKind.GetAccessor:
+ return this.emitDeclarationsForGetAccessor(ast);
+ case SyntaxKind.SetAccessor:
+ return this.emitDeclarationsForSetAccessor(ast);
+ case SyntaxKind.IndexMemberDeclaration:
+ return this.emitIndexMemberDeclaration(ast);
+ case SyntaxKind.IndexSignature:
+ return this.emitIndexSignature(ast);
+ case SyntaxKind.CallSignature:
+ return this.emitCallSignature(ast);
+ case SyntaxKind.ConstructSignature:
+ return this.emitConstructSignature(ast);
+ case SyntaxKind.MethodSignature:
+ return this.emitMethodSignature(ast);
+ case SyntaxKind.FunctionDeclaration:
+ return this.emitDeclarationsForFunctionDeclaration(ast);
+ case SyntaxKind.MemberFunctionDeclaration:
+ return this.emitMemberFunctionDeclaration(ast);
+ case SyntaxKind.ClassDeclaration:
+ return this.emitDeclarationsForClassDeclaration(ast);
+ case SyntaxKind.InterfaceDeclaration:
+ return this.emitDeclarationsForInterfaceDeclaration(ast);
+ case SyntaxKind.ImportDeclaration:
+ return this.emitDeclarationsForImportDeclaration(ast);
+ case SyntaxKind.ModuleDeclaration:
+ return this.emitDeclarationsForModuleDeclaration(ast);
+ case SyntaxKind.EnumDeclaration:
+ return this.emitDeclarationsForEnumDeclaration(ast);
+ case SyntaxKind.ExportAssignment:
+ return this.emitDeclarationsForExportAssignment(ast);
+ }
+ }
+
+ private getIndentString(declIndent = false) {
+ return this.indenter.getIndent();
+ }
+
+ private emitIndent() {
+ this.declFile.Write(this.getIndentString());
+ }
+
+ private canEmitDeclarations(declAST: ISyntaxElement): boolean {
+ var container = DeclarationEmitter.getEnclosingContainer(declAST);
+ if (container.kind() === SyntaxKind.ModuleDeclaration || container.kind() === SyntaxKind.SourceUnit) {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(declAST);
+ if (!hasFlag(pullDecl.flags, PullElementFlags.Exported)) {
+ var start = new Date().getTime();
+ var declSymbol = this.semanticInfoChain.getSymbolForAST(declAST);
+ var result = declSymbol && declSymbol.isExternallyVisible();
+ TypeScript.declarationEmitIsExternallyVisibleTime += new Date().getTime() - start;
+
+ return result;
+ }
+ }
+
+ return true;
+ }
+
+ private getDeclFlagsString(pullDecl: PullDecl, typeString: string) {
+ var result = this.getIndentString();
+ var pullFlags = pullDecl.flags;
+
+ // Static/public/private/global declare
+ if (hasFlag(pullFlags, PullElementFlags.Static)) {
+ if (hasFlag(pullFlags, PullElementFlags.Private)) {
+ result += "private ";
+ }
+ result += "static ";
+ }
+ else {
+ if (hasFlag(pullFlags, PullElementFlags.Private)) {
+ result += "private ";
+ }
+ else if (hasFlag(pullFlags, PullElementFlags.Public)) {
+ result += "public ";
+ }
+ else {
+ var emitDeclare = !hasFlag(pullFlags, PullElementFlags.Exported);
+
+ var declAST = this.semanticInfoChain.getASTForDecl(pullDecl);
+ var container = DeclarationEmitter.getEnclosingContainer(declAST);
+
+ var isExternalModule = container.kind() === SyntaxKind.SourceUnit && this.document.syntaxTree().isExternalModule();
+
+ // Emit export only for global export statements.
+ // The container for this would be dynamic module which is whole file
+ if (isExternalModule && hasFlag(pullFlags, PullElementFlags.Exported)) {
+ result += "export ";
+ emitDeclare = true;
+ }
+
+ // Emit declare only in global context
+ if (isExternalModule || container.kind() === SyntaxKind.SourceUnit) {
+ // Emit declare if not interface declaration or import declaration && is not from module
+ if (emitDeclare && typeString !== "interface" && typeString !== "import") {
+ result += "declare ";
+ }
+ }
+
+ result += typeString + " ";
+ }
+ }
+
+ return result;
+ }
+
+ private emitDeclFlags(declarationAST: ISyntaxElement, typeString: string) {
+ this.declFile.Write(this.getDeclFlagsString(this.semanticInfoChain.getDeclForAST(declarationAST), typeString));
+ }
+
+ private emitTypeNamesMember(memberName: MemberName, emitIndent: boolean = false) {
+ if (memberName.prefix === "{ ") {
+ if (emitIndent) {
+ this.emitIndent();
+ }
+
+ this.declFile.WriteLine("{");
+ this.indenter.increaseIndent();
+ emitIndent = true;
+ }
+ else if (memberName.prefix !== "") {
+ if (emitIndent) {
+ this.emitIndent();
+ }
+
+ this.declFile.Write(memberName.prefix);
+ emitIndent = false;
+ }
+
+ if (memberName.isString()) {
+ if (emitIndent) {
+ this.emitIndent();
+ }
+
+ this.declFile.Write((memberName).text);
+ }
+ else if (memberName.isArray()) {
+ var ar = memberName;
+ for (var index = 0; index < ar.entries.length; index++) {
+ this.emitTypeNamesMember(ar.entries[index], emitIndent);
+ if (ar.delim === "; ") {
+ this.declFile.WriteLine(";");
+ }
+ }
+ }
+
+ if (memberName.suffix === "}") {
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.declFile.Write(memberName.suffix);
+ }
+ else {
+ this.declFile.Write(memberName.suffix);
+ }
+ }
+
+ private emitTypeSignature(ast: ISyntaxElement, type: PullTypeSymbol) {
+ var declarationContainerAst = DeclarationEmitter.getEnclosingContainer(ast);
+
+ var start = new Date().getTime();
+ var declarationContainerDecl = this.semanticInfoChain.getDeclForAST(declarationContainerAst);
+
+ var declarationPullSymbol = declarationContainerDecl.getSymbol(this.semanticInfoChain);
+ TypeScript.declarationEmitTypeSignatureTime += new Date().getTime() - start;
+
+ var isNotAGenericType = ast.kind() !== SyntaxKind.GenericType;
+
+ var typeNameMembers = type.getScopedNameEx(
+ declarationPullSymbol,
+ /*skipTypeParametersInName?*/ false,
+ /*useConstraintInName?*/ false,
+ /*getPrettyTypeName?*/ false,
+ /*getTypeParamMarkerInfo?*/ false,
+ /*skipInternalAliasName?*/ false,
+ /*shouldAllowArrayType:*/ isNotAGenericType);
+ this.emitTypeNamesMember(typeNameMembers);
+ }
+
+ private emitComment(comment: Comment) {
+ var text = getTrimmedTextLines(comment);
+ if (this.declFile.onNewLine) {
+ this.emitIndent();
+ }
+ else if (comment.kind() !== SyntaxKind.MultiLineCommentTrivia) {
+ this.declFile.WriteLine("");
+ this.emitIndent();
+ }
+
+ this.declFile.Write(text[0]);
+
+ for (var i = 1; i < text.length; i++) {
+ this.declFile.WriteLine("");
+ this.emitIndent();
+ this.declFile.Write(text[i]);
+ }
+
+ if (comment.endsLine || comment.kind() !== SyntaxKind.MultiLineCommentTrivia) {
+ this.declFile.WriteLine("");
+ }
+ else {
+ this.declFile.Write(" ");
+ }
+ }
+
+ private text() {
+ return this.document.syntaxTree().text;
+ }
+
+ private emitDeclarationComments(ast: ISyntaxElement, endLine?: boolean): void;
+ private emitDeclarationComments(astOrSymbol: any, endLine = true) {
+ if (this.emitOptions.compilationSettings().removeComments()) {
+ return;
+ }
+
+ var declComments: Comment[] = astOrSymbol.docComments ? astOrSymbol.docComments() : ASTHelpers.docComments(astOrSymbol, this.text());
+ this.writeDeclarationComments(declComments, endLine);
+ }
+
+ private writeDeclarationComments(declComments: Comment[], endLine = true) {
+ if (declComments) {
+ var wroteComment = false;
+ for (var i = 0; i < declComments.length; i++) {
+ if (ASTHelpers.isDocComment(declComments[i])) {
+ this.emitComment(declComments[i]);
+ wroteComment = true;
+ }
+ }
+
+ if (wroteComment) {
+ if (endLine) {
+ if (!this.declFile.onNewLine) {
+ this.declFile.WriteLine("");
+ }
+ }
+ else {
+ if (this.declFile.onNewLine) {
+ this.emitIndent();
+ }
+ }
+ }
+ }
+ }
+
+ private emitTypeOfVariableDeclaratorOrParameter(boundDecl: ISyntaxElement) {
+ var start = new Date().getTime();
+ var decl = this.semanticInfoChain.getDeclForAST(boundDecl);
+ var pullSymbol = decl.getSymbol(this.semanticInfoChain);
+ TypeScript.declarationEmitGetBoundDeclTypeTime += new Date().getTime() - start;
+
+ var type = pullSymbol.type;
+ Debug.assert(type);
+
+ this.declFile.Write(": ");
+ this.emitTypeSignature(boundDecl, type);
+ }
+
+ private emitPropertySignature(varDecl: PropertySignatureSyntax): void {
+ this.emitDeclarationComments(varDecl);
+ this.emitIndent();
+ this.declFile.Write(varDecl.propertyName.text());
+ if (varDecl.questionToken) {
+ this.declFile.Write("?");
+ }
+
+ this.emitTypeOfVariableDeclaratorOrParameter(varDecl);
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitVariableDeclarator(varDecl: VariableDeclaratorSyntax, isFirstVarInList: boolean, isLastVarInList: boolean) {
+ if (this.canEmitDeclarations(varDecl)) {
+ this.emitDeclarationComments(varDecl);
+ // If it is var list of form var a, b, c = emit it only if count > 0 - which will be when emitting first var
+ // If it is var list of form var a = varList count will be 0
+ if (isFirstVarInList) {
+ this.emitDeclFlags(varDecl, "var");
+ }
+
+ this.declFile.Write(varDecl.propertyName.text());
+
+ if (!hasModifier(ASTHelpers.getVariableDeclaratorModifiers(varDecl), PullElementFlags.Private)) {
+ this.emitTypeOfVariableDeclaratorOrParameter(varDecl);
+ }
+
+ // Write ; or ,
+ if (isLastVarInList) {
+ this.declFile.WriteLine(";");
+ }
+ else {
+ this.declFile.Write(", ");
+ }
+ }
+ }
+
+ private emitClassElementModifiers(modifiers: ISyntaxToken[]): void {
+ if (hasModifier(modifiers, PullElementFlags.Static)) {
+ if (hasModifier(modifiers, PullElementFlags.Private)) {
+ this.declFile.Write("private ");
+ }
+ this.declFile.Write("static ");
+ }
+ else {
+ if (hasModifier(modifiers, PullElementFlags.Private)) {
+ this.declFile.Write("private ");
+ }
+ else {
+ this.declFile.Write("public ");
+ }
+ }
+ }
+
+ private emitDeclarationsForMemberVariableDeclaration(varDecl: MemberVariableDeclarationSyntax) {
+ if (this.canEmitDeclarations(varDecl)) {
+ this.emitDeclarationComments(varDecl);
+
+ this.declFile.Write(this.getIndentString());
+ this.emitClassElementModifiers(varDecl.modifiers);;
+
+ this.declFile.Write(varDecl.variableDeclarator.propertyName.text());
+
+ if (!hasModifier(varDecl.modifiers, PullElementFlags.Private)) {
+ this.emitTypeOfVariableDeclaratorOrParameter(varDecl);
+ }
+
+ this.declFile.WriteLine(";");
+ }
+ }
+
+ private emitDeclarationsForVariableStatement(variableStatement: VariableStatementSyntax) {
+ this.emitDeclarationsForVariableDeclaration(variableStatement.variableDeclaration);
+ }
+
+ private emitDeclarationsForVariableDeclaration(variableDeclaration: VariableDeclarationSyntax) {
+ var varListCount = variableDeclaration.variableDeclarators.length;
+ for (var i = 0; i < varListCount; i++) {
+ this.emitVariableDeclarator(variableDeclaration.variableDeclarators[i], i === 0, i === varListCount - 1);
+ }
+ }
+
+ private parameterIsOptional(parameter: ParameterSyntax) {
+ return parameter.questionToken !== null || parameter.equalsValueClause !== null;
+ }
+
+ private emitParameter(argDecl: ParameterSyntax, isPrivate: boolean) {
+ this.indenter.increaseIndent();
+
+ this.emitDeclarationComments(argDecl, false);
+ this.declFile.Write(argDecl.identifier.text());
+ if (this.parameterIsOptional(argDecl)) {
+ this.declFile.Write("?");
+ }
+
+ this.indenter.decreaseIndent();
+
+ if (!isPrivate) {
+ this.emitTypeOfVariableDeclaratorOrParameter(argDecl);
+ }
+ }
+
+ private isOverloadedCallSignature(funcDecl: ISyntaxElement) {
+ var start = new Date().getTime();
+ var functionDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ var funcSymbol = functionDecl.getSymbol(this.semanticInfoChain);
+ TypeScript.declarationEmitIsOverloadedCallSignatureTime += new Date().getTime() - start;
+
+ var funcTypeSymbol = funcSymbol.type;
+ var signatures = funcTypeSymbol.getCallSignatures();
+ var result = signatures && signatures.length > 1;
+
+ return result;
+ }
+
+ private emitDeclarationsForConstructorDeclaration(funcDecl: ConstructorDeclarationSyntax) {
+ var start = new Date().getTime();
+ var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl);
+
+ TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start;
+
+ var funcTypeSymbol = funcSymbol.type;
+ if (funcDecl.block) {
+ var constructSignatures = funcTypeSymbol.getConstructSignatures();
+ if (constructSignatures && constructSignatures.length > 1) {
+ return;
+ }
+ //else if (this.isOverloadedCallSignature(funcDecl)) {
+ // // This means its implementation of overload signature. do not emit
+ // return;
+ //}
+ }
+
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitDeclarationComments(funcDecl);
+
+ this.emitIndent();
+ this.declFile.Write("constructor");
+
+ this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList);
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitParameterList(isPrivate: boolean, parameterList: ParameterListSyntax): void {
+ this.declFile.Write("(");
+ this.emitParameters(isPrivate, parameterList.parameters);
+ this.declFile.Write(")");
+ }
+
+ private emitParameters(isPrivate: boolean, parameterList: ParameterSyntax[]): void {
+ var hasLastParameterRestParameter = lastParameterIsRest(parameterList);
+ var argsLen = parameterList.length;
+ if (hasLastParameterRestParameter) {
+ argsLen--;
+ }
+
+ for (var i = 0; i < argsLen; i++) {
+ this.emitParameter(parameterList[i], isPrivate);
+ if (i < (argsLen - 1)) {
+ this.declFile.Write(", ");
+ }
+ }
+
+ if (hasLastParameterRestParameter) {
+ if (parameterList.length > 1) {
+ this.declFile.Write(", ...");
+ }
+ else {
+ this.declFile.Write("...");
+ }
+
+ var index = parameterList.length - 1;
+ this.emitParameter(parameterList[index], isPrivate);
+ }
+ }
+
+ private emitMemberFunctionDeclaration(funcDecl: MemberFunctionDeclarationSyntax) {
+ var start = new Date().getTime();
+ var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl);
+
+ TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start;
+
+ var funcTypeSymbol = funcSymbol.type;
+ if (funcDecl.block) {
+ var constructSignatures = funcTypeSymbol.getConstructSignatures();
+ if (constructSignatures && constructSignatures.length > 1) {
+ return;
+ }
+ else if (this.isOverloadedCallSignature(funcDecl)) {
+ // This means its implementation of overload signature. do not emit
+ return;
+ }
+ }
+ else if (hasModifier(funcDecl.modifiers, PullElementFlags.Private) && this.isOverloadedCallSignature(funcDecl)) {
+ // Print only first overload of private function
+ var callSignatures = funcTypeSymbol.getCallSignatures();
+ Debug.assert(callSignatures && callSignatures.length > 1);
+ var firstSignature = callSignatures[0].isDefinition() ? callSignatures[1] : callSignatures[0];
+ var firstSignatureDecl = firstSignature.getDeclarations()[0];
+ var firstFuncDecl = this.semanticInfoChain.getASTForDecl(firstSignatureDecl);
+ if (firstFuncDecl !== funcDecl) {
+ return;
+ }
+ }
+
+ if (!this.canEmitDeclarations(funcDecl)) {
+ return;
+ }
+
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitDeclarationComments(funcDecl);
+
+ this.emitDeclFlags(funcDecl, "function");
+ var id = funcDecl.propertyName.text();
+ this.declFile.Write(id);
+ this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature);
+
+ var isPrivate = hasModifier(funcDecl.modifiers, PullElementFlags.Private);
+
+ this.emitParameterList(isPrivate, funcDecl.callSignature.parameterList);
+
+ if (!isPrivate) {
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ this.emitTypeSignature(funcDecl, returnType);
+ }
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitCallSignature(funcDecl: CallSignatureSyntax): void {
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+
+ this.emitDeclarationComments(funcDecl);
+
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitTypeParameters(funcDecl.typeParameterList, funcSignature);
+
+ this.emitIndent();
+
+ this.emitParameterList(/*isPrivate:*/ false, funcDecl.parameterList);
+
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ if (returnType) {
+ this.emitTypeSignature(funcDecl, returnType);
+ }
+ else {
+ this.declFile.Write("any");
+ }
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitConstructSignature(funcDecl: ConstructSignatureSyntax) {
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+
+ var start = new Date().getTime();
+ var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl);
+
+ TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start;
+
+ this.emitDeclarationComments(funcDecl);
+
+ this.emitIndent();
+ this.declFile.Write("new");
+
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature);
+
+ this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList);
+
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ if (returnType) {
+ this.emitTypeSignature(funcDecl, returnType);
+ }
+ else {
+ this.declFile.Write("any");
+ }
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitMethodSignature(funcDecl: MethodSignatureSyntax) {
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+
+ var start = new Date().getTime();
+ var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl);
+
+ TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start;
+
+ this.emitDeclarationComments(funcDecl);
+
+ this.emitIndent();
+ this.declFile.Write(funcDecl.propertyName.text());
+ if (funcDecl.questionToken) {
+ this.declFile.Write("? ");
+ }
+
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature);
+
+ this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList);
+
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ if (returnType) {
+ this.emitTypeSignature(funcDecl, returnType);
+ }
+ else {
+ this.declFile.Write("any");
+ }
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitDeclarationsForFunctionDeclaration(funcDecl: FunctionDeclarationSyntax) {
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+
+ var start = new Date().getTime();
+ var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl);
+
+ TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start;
+
+ if (funcDecl.block) {
+ var funcTypeSymbol = funcSymbol.type;
+ var constructSignatures = funcTypeSymbol.getConstructSignatures();
+ if (constructSignatures && constructSignatures.length > 1) {
+ return;
+ }
+ else if (this.isOverloadedCallSignature(funcDecl)) {
+ // This means its implementation of overload signature. do not emit
+ return;
+ }
+ }
+
+ if (!this.canEmitDeclarations(funcDecl)) {
+ return;
+ }
+
+ this.emitDeclarationComments(funcDecl);
+
+ var id = funcDecl.identifier.text();
+ this.emitDeclFlags(funcDecl, "function");
+ if (id !== "" || !funcDecl.identifier || funcDecl.identifier.text().length > 0) {
+ this.declFile.Write(id);
+ }
+ else if (funcPullDecl.kind === PullElementKind.ConstructSignature) {
+ this.declFile.Write("new");
+ }
+
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature);
+
+ this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList);
+
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ if (returnType) {
+ this.emitTypeSignature(funcDecl, returnType);
+ }
+ else {
+ this.declFile.Write("any");
+ }
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitIndexMemberDeclaration(funcDecl: IndexMemberDeclarationSyntax) {
+ this.emitDeclarationsForAST(funcDecl.indexSignature);
+ }
+
+ private emitIndexSignature(funcDecl: IndexSignatureSyntax) {
+ if (!this.canEmitDeclarations(funcDecl)) {
+ return;
+ }
+
+ this.emitDeclarationComments(funcDecl);
+
+ this.emitIndent();
+ this.declFile.Write("[");
+ this.emitParameters(/*isPrivate:*/ false, funcDecl.parameters);
+ this.declFile.Write("]");
+
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain);
+ var returnType = funcSignature.returnType;
+ this.declFile.Write(": ");
+ this.emitTypeSignature(funcDecl, returnType);
+
+ this.declFile.WriteLine(";");
+ }
+
+ private emitBaseList(bases: INameSyntax[], useExtendsList: boolean) {
+ if (bases && (bases.length > 0)) {
+ var qual = useExtendsList ? "extends" : "implements";
+ this.declFile.Write(" " + qual + " ");
+ var basesLen = bases.length;
+ for (var i = 0; i < basesLen; i++) {
+ if (i > 0) {
+ this.declFile.Write(", ");
+ }
+ var base = bases[i];
+ var baseType = this.semanticInfoChain.getSymbolForAST(base);
+ this.emitTypeSignature(base, baseType);
+ }
+ }
+ }
+
+ private emitAccessorDeclarationComments(funcDecl: ISyntaxElement) {
+ if (this.emitOptions.compilationSettings().removeComments()) {
+ return;
+ }
+
+ var start = new Date().getTime();
+ var accessors = PullHelpers.getGetterAndSetterFunction(funcDecl, this.semanticInfoChain);
+ TypeScript.declarationEmitGetAccessorFunctionTime += new Date().getTime();
+
+ var comments: Comment[] = [];
+ if (accessors.getter) {
+ comments = comments.concat(ASTHelpers.docComments(accessors.getter, this.text()));
+ }
+ if (accessors.setter) {
+ comments = comments.concat(ASTHelpers.docComments(accessors.setter, this.text()));
+ }
+
+ this.writeDeclarationComments(comments);
+ }
+
+ private emitDeclarationsForGetAccessor(funcDecl: GetAccessorSyntax): void {
+ this.emitMemberAccessorDeclaration(funcDecl, funcDecl.modifiers, funcDecl.propertyName);
+ }
+
+ private emitDeclarationsForSetAccessor(funcDecl: SetAccessorSyntax): void {
+ this.emitMemberAccessorDeclaration(funcDecl, funcDecl.modifiers, funcDecl.propertyName);
+ }
+
+ private emitMemberAccessorDeclaration(funcDecl: ISyntaxElement, modifiers: ISyntaxToken[], name: ISyntaxToken) {
+ var start = new Date().getTime();
+ var accessorSymbol = PullHelpers.getAccessorSymbol(funcDecl, this.semanticInfoChain);
+ TypeScript.declarationEmitGetAccessorFunctionTime += new Date().getTime();
+
+ if (funcDecl.kind() === SyntaxKind.SetAccessor && accessorSymbol.getGetter()) {
+ // Setter is being used to emit the type info.
+ return;
+ }
+
+ var isPrivate = hasModifier(modifiers, PullElementFlags.Private);
+ this.emitAccessorDeclarationComments(funcDecl);
+ this.declFile.Write(this.getIndentString());
+ this.emitClassElementModifiers(modifiers);
+ this.declFile.Write(name.text());
+ if (!isPrivate) {
+ this.declFile.Write(" : ");
+ var type = accessorSymbol.type;
+ this.emitTypeSignature(funcDecl, type);
+ }
+ this.declFile.WriteLine(";");
+ }
+
+ private emitClassMembersFromConstructorDefinition(funcDecl: ConstructorDeclarationSyntax) {
+ var argsLen = funcDecl.callSignature.parameterList.parameters.length;
+ if (lastParameterIsRest(funcDecl.callSignature.parameterList.parameters)) {
+ argsLen--;
+ }
+
+ for (var i = 0; i < argsLen; i++) {
+ var parameter = funcDecl.callSignature.parameterList.parameters[i];
+ var parameterDecl = this.semanticInfoChain.getDeclForAST(parameter);
+ if (hasFlag(parameterDecl.flags, PullElementFlags.PropertyParameter)) {
+ var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.emitDeclarationComments(parameter);
+ this.declFile.Write(this.getIndentString());
+ this.emitClassElementModifiers(parameter.modifiers);
+ this.declFile.Write(parameter.identifier.text());
+
+ if (!hasModifier(parameter.modifiers, PullElementFlags.Private)) {
+ this.emitTypeOfVariableDeclaratorOrParameter(parameter);
+ }
+ this.declFile.WriteLine(";");
+ }
+ }
+ }
+
+ private emitDeclarationsForClassDeclaration(classDecl: ClassDeclarationSyntax) {
+ if (!this.canEmitDeclarations(classDecl)) {
+ return;
+ }
+
+ var className = classDecl.identifier.text();
+ this.emitDeclarationComments(classDecl);
+ var classPullDecl = this.semanticInfoChain.getDeclForAST(classDecl);
+ this.emitDeclFlags(classDecl, "class");
+ this.declFile.Write(className);
+
+ this.emitTypeParameters(classDecl.typeParameterList);
+ this.emitHeritageClauses(classDecl.heritageClauses);
+ this.declFile.WriteLine(" {");
+
+ this.indenter.increaseIndent();
+ var constructorDecl = getLastConstructor(classDecl);
+ if (constructorDecl) {
+ this.emitClassMembersFromConstructorDefinition(constructorDecl);
+ }
+
+ this.emitDeclarationsForList(classDecl.classElements);
+
+ this.indenter.decreaseIndent();
+
+ this.emitIndent();
+ this.declFile.WriteLine("}");
+ }
+
+ private emitHeritageClauses(clauses: HeritageClauseSyntax[]): void {
+ if (clauses) {
+ for (var i = 0, n = clauses.length; i < n; i++) {
+ this.emitHeritageClause(clauses[i]);
+ }
+ }
+ }
+
+ private emitHeritageClause(clause: HeritageClauseSyntax) {
+ this.emitBaseList(clause.typeNames, clause.kind() === SyntaxKind.ExtendsHeritageClause);
+ }
+
+ static getEnclosingContainer(ast: ISyntaxElement): ISyntaxElement {
+ // If the passed in as is the 'name' portion of an module declaration.
+ // If so, we want the actual container of *that* module declaration.
+ var enclosingModule = ASTHelpers.getModuleDeclarationFromNameAST(ast);
+ ast = enclosingModule || ast;
+
+ ast = ast.parent;
+ while (ast) {
+ if (ast.kind() === SyntaxKind.ClassDeclaration ||
+ ast.kind() === SyntaxKind.InterfaceDeclaration ||
+ ast.kind() === SyntaxKind.ModuleDeclaration ||
+ ast.kind() === SyntaxKind.SourceUnit) {
+
+ return ast;
+ }
+
+ ast = ast.parent;
+ }
+
+ return null;
+ }
+
+ private emitTypeParameters(typeParams: TypeParameterListSyntax, funcSignature?: PullSignatureSymbol) {
+ if (!typeParams || !typeParams.typeParameters.length) {
+ return;
+ }
+
+ this.declFile.Write("<");
+ var containerAst = DeclarationEmitter.getEnclosingContainer(typeParams);
+
+ var start = new Date().getTime();
+ var containerDecl = this.semanticInfoChain.getDeclForAST(containerAst);
+ var containerSymbol = containerDecl.getSymbol(this.semanticInfoChain);
+ TypeScript.declarationEmitGetTypeParameterSymbolTime += new Date().getTime() - start;
+
+ var typars: PullTypeSymbol[];
+ if (funcSignature) {
+ typars = funcSignature.getTypeParameters();
+ }
+ else {
+ typars = containerSymbol.getTypeArgumentsOrTypeParameters();
+ }
+
+ for (var i = 0; i < typars.length; i++) {
+ if (i) {
+ this.declFile.Write(", ");
+ }
+
+ var memberName = typars[i].getScopedNameEx(containerSymbol, /*skipTypeParametersInName*/ false, /*useConstraintInName:*/ true);
+ this.emitTypeNamesMember(memberName);
+ }
+
+ this.declFile.Write(">");
+ }
+
+ private emitDeclarationsForInterfaceDeclaration(interfaceDecl: InterfaceDeclarationSyntax) {
+ if (!this.canEmitDeclarations(interfaceDecl)) {
+ return;
+ }
+
+ var interfaceName = interfaceDecl.identifier.text();
+ this.emitDeclarationComments(interfaceDecl);
+ var interfacePullDecl = this.semanticInfoChain.getDeclForAST(interfaceDecl);
+ this.emitDeclFlags(interfaceDecl, "interface");
+ this.declFile.Write(interfaceName);
+
+ this.emitTypeParameters(interfaceDecl.typeParameterList);
+ this.emitHeritageClauses(interfaceDecl.heritageClauses);
+ this.declFile.WriteLine(" {");
+
+ this.indenter.increaseIndent();
+
+ this.emitSeparatedList(interfaceDecl.body.typeMembers);
+
+ this.indenter.decreaseIndent();
+
+ this.emitIndent();
+ this.declFile.WriteLine("}");
+ }
+
+ private emitDeclarationsForImportDeclaration(importDeclAST: ImportDeclarationSyntax) {
+ var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST);
+ var importSymbol = importDecl.getSymbol(this.semanticInfoChain);
+ var isExportedImportDecl = hasModifier(importDeclAST.modifiers, PullElementFlags.Exported);
+
+ if (isExportedImportDecl || importSymbol.typeUsedExternally() || PullContainerSymbol.usedAsSymbol(importSymbol.getContainer(), importSymbol)) {
+ this.emitDeclarationComments(importDeclAST);
+ this.emitIndent();
+ if (isExportedImportDecl) {
+ this.declFile.Write("export ");
+ }
+ this.declFile.Write("import ");
+ this.declFile.Write(importDeclAST.identifier.text() + " = ");
+ if (importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference) {
+ this.declFile.WriteLine("require(" + (importDeclAST.moduleReference).stringLiteral.text() + ");");
+ }
+ else {
+ this.declFile.WriteLine(ASTHelpers.getNameOfIdenfierOrQualifiedName((importDeclAST.moduleReference).moduleName) + ";");
+ }
+ }
+ }
+
+ private emitDeclarationsForEnumDeclaration(moduleDecl: EnumDeclarationSyntax): void {
+ if (!this.canEmitDeclarations(moduleDecl)) {
+ return;
+ }
+
+ this.emitDeclarationComments(moduleDecl);
+ var modulePullDecl = this.semanticInfoChain.getDeclForAST(moduleDecl);
+ this.emitDeclFlags(moduleDecl, "enum");
+ this.declFile.WriteLine(moduleDecl.identifier.text() + " {");
+
+ this.indenter.increaseIndent();
+ var membersLen = moduleDecl.enumElements.length;
+ for (var j = 0; j < membersLen; j++) {
+ var enumElement = moduleDecl.enumElements[j];
+ var enumElementDecl = this.semanticInfoChain.getDeclForAST(enumElement);
+ this.emitDeclarationComments(enumElement);
+ this.emitIndent();
+ this.declFile.Write(enumElement.propertyName.text());
+ if (enumElementDecl.constantValue !== null) {
+ this.declFile.Write(" = " + enumElementDecl.constantValue);
+ }
+ this.declFile.WriteLine(",");
+ }
+ this.indenter.decreaseIndent();
+
+ this.emitIndent();
+ this.declFile.WriteLine("}");
+ }
+
+ private emitDeclarationsForModuleDeclaration(moduleDecl: ModuleDeclarationSyntax) {
+ // If module is defined as A.B.C
+ // The whole moduleDecl will have the pullDecl corresponding to innermost
+ // Which would always be exported and hence would need to be emitted
+ // But we really want to check if module A needs to be emitted and hence use
+ // the leftmost name to determine if this needs to be emitted
+ // Since the module name will have the correct decl, it is always used to determine
+ // if this ast needs to be emitted or not
+ var name: ISyntaxElement = moduleDecl.stringLiteral || ArrayUtilities.first(ASTHelpers.getModuleNames(moduleDecl.name));
+ if (!this.canEmitDeclarations(name)) {
+ return;
+ }
+
+ this.emitDeclarationComments(moduleDecl);
+ this.emitDeclFlags(name, "module");
+
+ if (moduleDecl.stringLiteral) {
+ this.declFile.Write(moduleDecl.stringLiteral.text());
+ }
+ else {
+ this.declFile.Write(ASTHelpers.getNameOfIdenfierOrQualifiedName(moduleDecl.name));
+ }
+
+ this.declFile.WriteLine(" {");
+ this.indenter.increaseIndent();
+
+ this.emitDeclarationsForList(moduleDecl.moduleElements);
+
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.declFile.WriteLine("}");
+ }
+
+ private emitDeclarationsForExportAssignment(ast: ExportAssignmentSyntax) {
+ this.emitIndent();
+ this.declFile.Write("export = ");
+ this.declFile.Write(ast.identifier.text());
+ this.declFile.WriteLine(";");
+ }
+
+ private resolveScriptReference(document: Document, reference: string) {
+ if (!this.emitOptions.compilationSettings().noResolve() || isRooted(reference)) {
+ return reference;
+ }
+
+ var documentDir = convertToDirectoryPath(switchToForwardSlashes(getRootFilePath(document.fileName)));
+ var resolvedReferencePath = this.emitOptions.resolvePath(documentDir + reference);
+ return resolvedReferencePath;
+ }
+
+ private emitReferencePaths(sourceUnit: SourceUnitSyntax) {
+ // In case of shared handler we collect all the references and emit them
+ if (this.emittedReferencePaths) {
+ return;
+ }
+
+ // Collect all the documents that need to be emitted as reference
+ var documents: Document[] = [];
+ if (this.document.emitToOwnOutputFile()) {
+ // Emit only from this file
+ var scriptReferences = this.document.referencedFiles;
+ var addedGlobalDocument = false;
+ for (var j = 0; j < scriptReferences.length; j++) {
+ var currentReference = this.resolveScriptReference(this.document, scriptReferences[j]);
+ var document = this.compiler.getDocument(currentReference);
+ // All the references that are not going to be part of same file
+
+ if (document &&
+ (document.emitToOwnOutputFile() || document.isDeclareFile() || !addedGlobalDocument)) {
+
+ documents = documents.concat(document);
+
+ if (!document.isDeclareFile() && document.syntaxTree().isExternalModule()) {
+ addedGlobalDocument = true;
+ }
+ }
+ }
+ }
+ else {
+ // Collect from all the references and emit
+ var fileNames = this.compiler.fileNames();
+ for (var i = 0; i < fileNames.length; i++) {
+ var doc = this.compiler.getDocument(fileNames[i]);
+ if (!doc.isDeclareFile() && !doc.syntaxTree().isExternalModule()) {
+ // Check what references need to be added
+ var scriptReferences = doc.referencedFiles;
+ for (var j = 0; j < scriptReferences.length; j++) {
+ var currentReference = this.resolveScriptReference(doc, scriptReferences[j]);
+ var document = this.compiler.getDocument(currentReference);
+ // All the references that are not going to be part of same file
+ if (document &&
+ (document.isDeclareFile() || document.syntaxTree().isExternalModule())) {
+ for (var k = 0; k < documents.length; k++) {
+ if (documents[k] === document) {
+ break;
+ }
+ }
+
+ if (k === documents.length) {
+ documents = documents.concat(document);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Emit the references
+ var emittingFilePath = documents.length ? getRootFilePath(this.emittingFileName) : null;
+ for (var i = 0; i < documents.length; i++) {
+ var document = documents[i];
+ var declFileName: string;
+ if (document.isDeclareFile()) {
+ declFileName = document.fileName;
+ }
+ else {
+ declFileName = this.compiler.mapOutputFileName(document, this.emitOptions, TypeScriptCompiler.mapToDTSFileName);
+ }
+
+ // Get the relative path
+ declFileName = getRelativePathToFixedPath(emittingFilePath, declFileName, false);
+ this.declFile.WriteLine('/// ');
+ }
+
+ this.emittedReferencePaths = true;
+ }
+
+ private emitDeclarationsForSourceUnit(sourceUnit: SourceUnitSyntax) {
+ this.emitReferencePaths(sourceUnit);
+ this.emitDeclarationsForList(sourceUnit.moduleElements);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/diagnostics.ts b/src/services/compiler/diagnostics.ts
new file mode 100644
index 00000000000..75d7bfd5a78
--- /dev/null
+++ b/src/services/compiler/diagnostics.ts
@@ -0,0 +1,47 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript {
+ export interface ILogger {
+ information(): boolean;
+ debug(): boolean;
+ warning(): boolean;
+ error(): boolean;
+ fatal(): boolean;
+ log(s: string): void;
+ }
+
+ export class NullLogger implements ILogger {
+ public information(): boolean { return false; }
+ public debug(): boolean { return false; }
+ public warning(): boolean { return false; }
+ public error(): boolean { return false; }
+ public fatal(): boolean { return false; }
+ public log(s: string): void {
+ }
+ }
+
+ export function timeFunction(logger: ILogger, funcDescription: string, func: () => any): any {
+ var start = (new Date()).getTime();
+ var result = func();
+ var end = (new Date()).getTime();
+ if (logger.information()) {
+ logger.log(funcDescription + " completed in " + (end - start) + " msec");
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/document.ts b/src/services/compiler/document.ts
new file mode 100644
index 00000000000..e8a2f89df68
--- /dev/null
+++ b/src/services/compiler/document.ts
@@ -0,0 +1,151 @@
+///
+
+module TypeScript {
+ export interface IncrementalParse {
+ (oldSyntaxTree: SyntaxTree, textChangeRange: TextChangeRange, newText: ISimpleText): SyntaxTree
+ }
+
+
+ export class Document {
+ private _bloomFilter: BloomFilter = null;
+
+ // By default, our Document class doesn't support incremental update of its contents.
+ // However, we enable other layers (like teh services layer) to inject the capability
+ // into us by setting this function.
+ public static incrementalParse: IncrementalParse = null;
+
+ constructor(private compilationSettings: ts.CompilerOptions,
+ public filename: string,
+ public referencedFiles: string[],
+ private _scriptSnapshot: IScriptSnapshot,
+ public byteOrderMark: ByteOrderMark,
+ public version: number,
+ public isOpen: boolean,
+ private _syntaxTree: SyntaxTree,
+ private _soruceFile: ts.SourceFile) {
+ }
+
+ public isDeclareFile(): boolean {
+ return isDTSFile(this.filename);
+ }
+
+ public sourceUnit(): SourceUnitSyntax {
+ // If we don't have a script, create one from our parse tree.
+ return this.syntaxTree().sourceUnit();
+ }
+
+ public diagnostics(): Diagnostic[] {
+ return this.syntaxTree().diagnostics();
+ }
+
+ public lineMap(): LineMap {
+ return this.syntaxTree().lineMap();
+ }
+
+ public syntaxTree(): SyntaxTree {
+ if (!this._syntaxTree) {
+ var start = new Date().getTime();
+
+ this._syntaxTree = Parser.parse(
+ this.filename, SimpleText.fromScriptSnapshot(this._scriptSnapshot), this.compilationSettings.target, this.isDeclareFile());
+
+ var time = new Date().getTime() - start;
+
+ //TypeScript.syntaxTreeParseTime += time;
+ }
+
+ return this._syntaxTree;
+ }
+
+ public sourceFile(): ts.SourceFile {
+ if (!this._soruceFile) {
+ var start = new Date().getTime();
+
+ this._soruceFile = ts.createSourceFile(this.filename, this._scriptSnapshot.getText(0, this._scriptSnapshot.getLength()), this.compilationSettings.target);
+
+ var time = new Date().getTime() - start;
+
+ //TypeScript.astParseTime += time;
+ }
+
+ return this._soruceFile;
+ }
+
+ public bloomFilter(): BloomFilter {
+ if (!this._bloomFilter) {
+ var identifiers = createIntrinsicsObject();
+ var pre = function (cur: TypeScript.ISyntaxElement) {
+ if (ASTHelpers.isValidAstNode(cur)) {
+ if (cur.kind() === SyntaxKind.IdentifierName) {
+ var nodeText = tokenValueText((cur));
+
+ identifiers[nodeText] = true;
+ }
+ }
+ };
+
+ TypeScript.getAstWalkerFactory().simpleWalk(this.sourceUnit(), pre, null, identifiers);
+
+ var identifierCount = 0;
+ for (var name in identifiers) {
+ if (identifiers[name]) {
+ identifierCount++;
+ }
+ }
+
+ this._bloomFilter = new BloomFilter(identifierCount);
+ this._bloomFilter.addKeys(identifiers);
+ }
+ return this._bloomFilter;
+ }
+
+ // Returns true if this file should get emitted into its own unique output file.
+ // Otherwise, it should be written into a single output file along with the rest of hte
+ // documents in the compilation.
+ public emitToOwnOutputFile(): boolean {
+ // If we haven't specified an output file in our settings, then we're definitely
+ // emitting to our own file. Also, if we're an external module, then we're
+ // definitely emitting to our own file.
+ return !this.compilationSettings.out || this.syntaxTree().isExternalModule();
+ }
+
+ public update(scriptSnapshot: IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TextChangeRange): Document {
+ // See if we are currently holding onto a syntax tree. We may not be because we're
+ // either a closed file, or we've just been lazy and haven't had to create the syntax
+ // tree yet. Access the field instead of the method so we don't accidently realize
+ // the old syntax tree.
+ var oldSyntaxTree = this._syntaxTree;
+
+ if (textChangeRange !== null && Debug.shouldAssert(AssertionLevel.Normal)) {
+ var oldText = this._scriptSnapshot;
+ var newText = scriptSnapshot;
+
+ TypeScript.Debug.assert((oldText.getLength() - textChangeRange.span().length() + textChangeRange.newLength()) === newText.getLength());
+
+ if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
+ var oldTextPrefix = oldText.getText(0, textChangeRange.span().start());
+ var newTextPrefix = newText.getText(0, textChangeRange.span().start());
+ TypeScript.Debug.assert(oldTextPrefix === newTextPrefix);
+
+ var oldTextSuffix = oldText.getText(textChangeRange.span().end(), oldText.getLength());
+ var newTextSuffix = newText.getText(textChangeRange.newSpan().end(), newText.getLength());
+ TypeScript.Debug.assert(oldTextSuffix === newTextSuffix);
+ }
+ }
+
+ var text = SimpleText.fromScriptSnapshot(scriptSnapshot);
+
+ // If we don't have a text change, or we don't have an old syntax tree, then do a full
+ // parse. Otherwise, do an incremental parse.
+ var newSyntaxTree = textChangeRange === null || oldSyntaxTree === null || Document.incrementalParse === null
+ ? TypeScript.Parser.parse(this.filename, text, this.compilationSettings.target, TypeScript.isDTSFile(this.filename))
+ : Document.incrementalParse(oldSyntaxTree, textChangeRange, text);
+
+ return new Document(this.compilationSettings, this.filename, this.referencedFiles, scriptSnapshot, this.byteOrderMark, version, isOpen, newSyntaxTree, /*soruceFile*/ null);
+ }
+
+ public static create(compilationSettings: ts.CompilerOptions, fileName: string, scriptSnapshot: IScriptSnapshot, byteOrderMark: ByteOrderMark, version: number, isOpen: boolean, referencedFiles: string[]): Document {
+ return new Document(compilationSettings, fileName, referencedFiles, scriptSnapshot, byteOrderMark, version, isOpen, /*syntaxTree:*/ null, /*soruceFile*/ null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/services/compiler/emitter.ts b/src/services/compiler/emitter.ts
new file mode 100644
index 00000000000..8de5e64dbba
--- /dev/null
+++ b/src/services/compiler/emitter.ts
@@ -0,0 +1,3797 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+///
+
+module TypeScript {
+ export enum EmitContainer {
+ Prog,
+ Module,
+ DynamicModule,
+ Class,
+ Constructor,
+ Function,
+ Args,
+ Interface,
+ }
+
+ export class EmitState {
+ public column: number;
+ public line: number;
+ public container: EmitContainer;
+
+ constructor() {
+ this.column = 0;
+ this.line = 0;
+ this.container = EmitContainer.Prog;
+ }
+ }
+
+ export class EmitOptions {
+ private _diagnostic: Diagnostic = null;
+
+ private _settings: ImmutableCompilationSettings = null;
+ private _commonDirectoryPath = "";
+ private _sharedOutputFile = "";
+ private _sourceRootDirectory = "";
+ private _sourceMapRootDirectory = "";
+ private _outputDirectory = "";
+
+ public diagnostic(): Diagnostic { return this._diagnostic; }
+
+ public commonDirectoryPath() { return this._commonDirectoryPath; }
+ public sharedOutputFile() { return this._sharedOutputFile; }
+ public sourceRootDirectory() { return this._sourceRootDirectory; }
+ public sourceMapRootDirectory() { return this._sourceMapRootDirectory; }
+ public outputDirectory() { return this._outputDirectory; }
+
+ public compilationSettings() { return this._settings; }
+
+ constructor(compiler: TypeScriptCompiler, public resolvePath: (path: string) => string) {
+ var settings = compiler.compilationSettings();
+ this._settings = settings;
+
+ // If the document is an external module, then report if the the user has not
+ // provided the right command line option.
+ if (settings.moduleGenTarget() === ModuleGenTarget.Unspecified) {
+ var fileNames = compiler.fileNames();
+ for (var i = 0, n = fileNames.length; i < n; i++) {
+ var document = compiler.getDocument(fileNames[i]);
+ if (!document.isDeclareFile() && document.syntaxTree().isExternalModule()) {
+ var errorSpan = externalModuleIndicatorSpan(document.syntaxTree());
+ this._diagnostic = new Diagnostic(document.fileName, document.lineMap(), errorSpan.start(), errorSpan.length(),
+ DiagnosticCode.Cannot_compile_external_modules_unless_the_module_flag_is_provided);
+
+ return;
+ }
+ }
+ }
+
+ if (!settings.mapSourceFiles()) {
+ // Error to specify --mapRoot or --sourceRoot without mapSourceFiles
+ if (settings.mapRoot()) {
+ if (settings.sourceRoot()) {
+ this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Options_mapRoot_and_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option, null);
+ return;
+ }
+ else {
+ this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option, null);
+ return;
+ }
+ }
+ else if (settings.sourceRoot()) {
+ this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option, null);
+ return;
+ }
+ }
+
+ this._sourceMapRootDirectory = convertToDirectoryPath(switchToForwardSlashes(settings.mapRoot()));
+ this._sourceRootDirectory = convertToDirectoryPath(switchToForwardSlashes(settings.sourceRoot()));
+
+ if (settings.outFileOption() ||
+ settings.outDirOption() ||
+ settings.mapRoot() ||
+ settings.sourceRoot()) {
+
+ if (settings.outFileOption()) {
+ this._sharedOutputFile = switchToForwardSlashes(resolvePath(settings.outFileOption()));
+ }
+
+ if (settings.outDirOption()) {
+ this._outputDirectory = convertToDirectoryPath(switchToForwardSlashes(resolvePath(settings.outDirOption())));
+ }
+
+ // Parse the directory structure
+ if (this._outputDirectory || this._sourceMapRootDirectory || this.sourceRootDirectory) {
+ this.determineCommonDirectoryPath(compiler);
+ }
+ }
+ }
+
+ private determineCommonDirectoryPath(compiler: TypeScriptCompiler): void {
+ var commonComponents: string[] = [];
+ var commonComponentsLength = -1;
+
+ var fileNames = compiler.fileNames();
+ for (var i = 0, len = fileNames.length; i < len; i++) {
+ var fileName = fileNames[i];
+ var document = compiler.getDocument(fileNames[i]);
+ var sourceUnit = document.sourceUnit();
+
+ if (!document.isDeclareFile()) {
+ var fileComponents = filePathComponents(fileName);
+ if (commonComponentsLength === -1) {
+ // First time at finding common path
+ // So common path = directory of file
+ commonComponents = fileComponents;
+ commonComponentsLength = commonComponents.length;
+ }
+ else {
+ var updatedPath = false;
+ for (var j = 0; j < commonComponentsLength && j < fileComponents.length; j++) {
+ if (commonComponents[j] !== fileComponents[j]) {
+ // The new components = 0 ... j -1
+ commonComponentsLength = j;
+ updatedPath = true;
+
+ if (j === 0) {
+ var isDynamicModuleCompilation = ArrayUtilities.any(fileNames, fileName => {
+ document = compiler.getDocument(fileName);
+ return !document.isDeclareFile() && document.syntaxTree().isExternalModule();
+ });
+
+ if (this._outputDirectory || // there is --outDir specified
+ this._sourceRootDirectory || // there is --sourceRoot specified
+ (this._sourceMapRootDirectory && // there is --map Specified and there would be multiple js files generated
+ (!this._sharedOutputFile || isDynamicModuleCompilation))) {
+ // Its error to not have common path
+ this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Cannot_find_the_common_subdirectory_path_for_the_input_files, null);
+ return;
+ }
+
+ return;
+ }
+
+ break;
+ }
+ }
+
+ // If the fileComponent path completely matched and less than already found update the length
+ if (!updatedPath && fileComponents.length < commonComponentsLength) {
+ commonComponentsLength = fileComponents.length;
+ }
+ }
+ }
+ }
+
+ this._commonDirectoryPath = commonComponents.slice(0, commonComponentsLength).join("/") + "/";
+ }
+ }
+
+ export class Indenter {
+ static indentStep: number = 4;
+ static indentStepString: string = " ";
+ static indentStrings: string[] = [];
+ public indentAmt: number = 0;
+
+ public increaseIndent() {
+ this.indentAmt += Indenter.indentStep;
+ }
+
+ public decreaseIndent() {
+ this.indentAmt -= Indenter.indentStep;
+ }
+
+ public getIndent() {
+ var indentString = Indenter.indentStrings[this.indentAmt];
+ if (indentString === undefined) {
+ indentString = "";
+ for (var i = 0; i < this.indentAmt; i = i + Indenter.indentStep) {
+ indentString += Indenter.indentStepString;
+ }
+ Indenter.indentStrings[this.indentAmt] = indentString;
+ }
+ return indentString;
+ }
+ }
+
+ export function lastParameterIsRest(parameters: ParameterSyntax[]): boolean {
+ return parameters.length > 0 && (parameters[parameters.length - 1]).dotDotDotToken !== null;
+ }
+
+ export class Emitter {
+ public globalThisCapturePrologueEmitted = false;
+ public extendsPrologueEmitted = false;
+ public thisClassNode: ClassDeclarationSyntax = null;
+ public inArrowFunction: boolean = false;
+ public moduleName = "";
+ public emitState = new EmitState();
+ public indenter = new Indenter();
+ public sourceMapper: SourceMapper = null;
+ public captureThisStmtString = "var _this = this;";
+ private currentVariableDeclaration: VariableDeclarationSyntax;
+ private declStack: PullDecl[] = [];
+ private exportAssignment: ExportAssignmentSyntax = null;
+ private inWithBlock = false;
+
+ public document: Document = null;
+
+ // If we choose to detach comments from an element (for example, the Copyright comments),
+ // then keep track of that element so that we don't emit all on the comments on it when
+ // we visit it.
+ private detachedCommentsElement: ISyntaxElement = null;
+
+ constructor(public emittingFileName: string,
+ public outfile: TextWriter,
+ public emitOptions: EmitOptions,
+ private semanticInfoChain: SemanticInfoChain) {
+ }
+
+ private pushDecl(decl: PullDecl) {
+ if (decl) {
+ this.declStack[this.declStack.length] = decl;
+ }
+ }
+
+ private popDecl(decl: PullDecl) {
+ if (decl) {
+ this.declStack.length--;
+ }
+ }
+
+ private getEnclosingDecl() {
+ var declStackLen = this.declStack.length;
+ var enclosingDecl = declStackLen > 0 ? this.declStack[declStackLen - 1] : null;
+ return enclosingDecl;
+ }
+
+ public setExportAssignment(exportAssignment: ExportAssignmentSyntax) {
+ this.exportAssignment = exportAssignment;
+ }
+
+ public getExportAssignment() {
+ return this.exportAssignment;
+ }
+
+ public setDocument(document: Document) {
+ this.document = document;
+ }
+
+ public shouldEmitImportDeclaration(importDeclAST: ImportDeclarationSyntax) {
+ var isExternalModuleReference = importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference;
+ var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST);
+ var isExported = hasFlag(importDecl.flags, PullElementFlags.Exported);
+ var isAmdCodeGen = this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous;
+
+ // 1) Any internal reference needs to check if the emit can happen
+ // 2) External module reference with export modifier always needs to be emitted
+ // 3) commonjs needs the var declaration for the import declaration
+ if (isExternalModuleReference && !isExported && isAmdCodeGen) {
+ return false;
+ }
+
+ var importSymbol = importDecl.getSymbol(this.semanticInfoChain);
+ if (importSymbol.isUsedAsValue()) {
+ return true;
+ }
+
+ if (importDeclAST.moduleReference.kind() !== SyntaxKind.ExternalModuleReference) {
+ var canBeUsedExternally = isExported || importSymbol.typeUsedExternally() || importSymbol.isUsedInExportedAlias();
+ if (!canBeUsedExternally && !this.document.syntaxTree().isExternalModule()) {
+ // top level import in non-external module are visible across the whole global module
+ canBeUsedExternally = hasFlag(importDecl.getParentDecl().kind, PullElementKind.Script | PullElementKind.DynamicModule);
+ }
+
+ if (canBeUsedExternally) {
+ if (importSymbol.getExportAssignedValueSymbol()) {
+ return true;
+ }
+
+ var containerSymbol = importSymbol.getExportAssignedContainerSymbol();
+ if (containerSymbol && containerSymbol.getInstanceSymbol()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public emitImportDeclaration(importDeclAST: ImportDeclarationSyntax) {
+ var isExternalModuleReference = importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference;
+ var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST);
+ var isExported = hasFlag(importDecl.flags, PullElementFlags.Exported);
+ var isAmdCodeGen = this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous;
+
+ this.emitComments(importDeclAST, true);
+
+ var importSymbol = importDecl.getSymbol(this.semanticInfoChain);
+
+ var parentSymbol = importSymbol.getContainer();
+ var parentKind = parentSymbol ? parentSymbol.kind : PullElementKind.None;
+ var associatedParentSymbol = parentSymbol ? parentSymbol.getAssociatedContainerType() : null;
+ var associatedParentSymbolKind = associatedParentSymbol ? associatedParentSymbol.kind : PullElementKind.None;
+
+ var needsPropertyAssignment = false;
+ var usePropertyAssignmentInsteadOfVarDecl = false;
+ var moduleNamePrefix: string;
+
+ if (isExported &&
+ (parentKind === PullElementKind.Container ||
+ parentKind === PullElementKind.DynamicModule ||
+ associatedParentSymbolKind === PullElementKind.Container ||
+ associatedParentSymbolKind === PullElementKind.DynamicModule)) {
+ if (importSymbol.getExportAssignedTypeSymbol() || importSymbol.getExportAssignedContainerSymbol()) {
+ // Type or container assignment that is exported
+ needsPropertyAssignment = true;
+ }
+ else {
+ var valueSymbol = importSymbol.getExportAssignedValueSymbol();
+ if (valueSymbol &&
+ (valueSymbol.kind === PullElementKind.Method || valueSymbol.kind === PullElementKind.Function)) {
+ needsPropertyAssignment = true;
+ }
+ else {
+ usePropertyAssignmentInsteadOfVarDecl = true;
+ }
+ }
+
+ // Calculate what name prefix to use
+ if (this.emitState.container === EmitContainer.DynamicModule) {
+ moduleNamePrefix = "exports."
+ }
+ else {
+ moduleNamePrefix = this.moduleName + ".";
+ }
+ }
+
+ if (isAmdCodeGen && isExternalModuleReference) {
+ // For amdCode gen of exported external module reference, do not emit var declaration
+ // Emit the property assignment since it is exported
+ needsPropertyAssignment = true;
+ }
+ else {
+ this.recordSourceMappingStart(importDeclAST);
+ if (usePropertyAssignmentInsteadOfVarDecl) {
+ this.writeToOutput(moduleNamePrefix);
+ }
+ else {
+ this.writeToOutput("var ");
+ }
+ this.writeToOutput(importDeclAST.identifier.text() + " = ");
+ var aliasAST = importDeclAST.moduleReference;
+
+ if (isExternalModuleReference) {
+ this.writeToOutput("require(" + (aliasAST).stringLiteral.text() + ")");
+ }
+ else {
+ this.emitJavascript((aliasAST).moduleName, false);
+ }
+
+ this.recordSourceMappingEnd(importDeclAST);
+ this.writeToOutput(";");
+
+ if (needsPropertyAssignment) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ }
+ }
+
+ if (needsPropertyAssignment) {
+ this.writeToOutputWithSourceMapRecord(moduleNamePrefix + importDeclAST.identifier.text() + " = " + importDeclAST.identifier.text(), importDeclAST);
+ this.writeToOutput(";");
+ }
+ this.emitComments(importDeclAST, false);
+ }
+
+ public createSourceMapper(document: Document, jsFileName: string, jsFile: TextWriter, sourceMapOut: TextWriter, resolvePath: (path: string) => string) {
+ this.sourceMapper = new SourceMapper(jsFile, sourceMapOut, document, jsFileName, this.emitOptions, resolvePath);
+ }
+
+ public setSourceMapperNewSourceFile(document: Document) {
+ this.sourceMapper.setNewSourceFile(document, this.emitOptions);
+ }
+
+ private updateLineAndColumn(s: string) {
+ var lineNumbers = TextUtilities.parseLineStarts(s);
+ if (lineNumbers.length > 1) {
+ // There are new lines in the string, update the line and column number accordingly
+ this.emitState.line += lineNumbers.length - 1;
+ this.emitState.column = s.length - lineNumbers[lineNumbers.length - 1];
+ }
+ else {
+ // No new lines in the string
+ this.emitState.column += s.length;
+ }
+ }
+
+ public writeToOutputWithSourceMapRecord(s: string, astSpan: ISyntaxElement) {
+ if (astSpan) {
+ this.recordSourceMappingStart(astSpan);
+ }
+
+ this.writeToOutput(s);
+
+ if (astSpan) {
+ this.recordSourceMappingEnd(astSpan);
+ }
+ }
+
+ public writeToOutput(s: string) {
+ this.outfile.Write(s);
+ this.updateLineAndColumn(s);
+ }
+
+ public writeLineToOutput(s: string, force = false) {
+ // No need to print a newline if we're already at the start of the line.
+ if (!force && s === "" && this.emitState.column === 0) {
+ return;
+ }
+
+ this.outfile.WriteLine(s);
+ this.updateLineAndColumn(s);
+ this.emitState.column = 0;
+ this.emitState.line++;
+ }
+
+ public writeCaptureThisStatement(ast: ISyntaxElement) {
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord(this.captureThisStmtString, ast);
+ this.writeLineToOutput("");
+ }
+
+ public setContainer(c: number): number {
+ var temp = this.emitState.container;
+ this.emitState.container = c;
+ return temp;
+ }
+
+ private getIndentString() {
+ return this.indenter.getIndent();
+ }
+
+ public emitIndent() {
+ this.writeToOutput(this.getIndentString());
+ }
+
+ public emitComment(comment: Comment, trailing: boolean, first: boolean, noLeadingSpace = false) {
+ if (this.emitOptions.compilationSettings().removeComments()) {
+ return;
+ }
+
+ var text = getTrimmedTextLines(comment);
+ var emitColumn = this.emitState.column;
+
+ if (emitColumn === 0) {
+ this.emitIndent();
+ }
+ else if (trailing && first && !noLeadingSpace) {
+ this.writeToOutput(" ");
+ }
+
+ if (comment.kind() === SyntaxKind.MultiLineCommentTrivia) {
+ this.recordSourceMappingCommentStart(comment);
+ this.writeToOutput(text[0]);
+
+ if (text.length > 1 || comment.endsLine) {
+ for (var i = 1; i < text.length; i++) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ this.writeToOutput(text[i]);
+ }
+ this.recordSourceMappingCommentEnd(comment);
+ this.writeLineToOutput("");
+ // Fall through
+ }
+ else {
+ this.recordSourceMappingCommentEnd(comment);
+ this.writeToOutput(" ");
+ return;
+ }
+ }
+ else {
+ this.recordSourceMappingCommentStart(comment);
+ this.writeToOutput(text[0]);
+ this.recordSourceMappingCommentEnd(comment);
+ this.writeLineToOutput("");
+ // Fall through
+ }
+
+ if (!trailing && emitColumn !== 0) {
+ // If we were indented before, stay indented after.
+ this.emitIndent();
+ }
+ }
+
+ private text(): ISimpleText {
+ return this.document.syntaxTree().text;
+ }
+
+ public emitComments(ast: ISyntaxElement, pre: boolean, onlyPinnedOrTripleSlashComments: boolean = false) {
+ // Emitting the comments for the exprssion inside an arrow function is handled specially
+ // in emitFunctionBodyStatements. We don't want to emit those comments a second time.
+ if (ast && !isShared(ast) && ast.kind() !== SyntaxKind.Block) {
+ if (ast.parent.kind() === SyntaxKind.SimpleArrowFunctionExpression || ast.parent.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression) {
+ return;
+ }
+ }
+
+ if (pre) {
+ var preComments = TypeScript.ASTHelpers.preComments(ast, this.text());
+
+ if (preComments && ast === this.detachedCommentsElement) {
+ // We're emitting the comments for the first script element. Skip any
+ // copyright comments, as we'll already have emitted those.
+ var detachedComments = this.getDetachedComments(ast);
+ preComments = preComments.slice(detachedComments.length);
+ this.detachedCommentsElement = null;
+ }
+
+ // We're emitting comments on an elided element. Only keep the comment if it is
+ // a triple slash or pinned comment.
+ if (preComments && onlyPinnedOrTripleSlashComments) {
+ preComments = ArrayUtilities.where(preComments, c => this.isPinnedOrTripleSlash(c));
+ }
+
+ this.emitCommentsArray(preComments, /*trailing:*/ false);
+ }
+ else {
+ this.emitCommentsArray(ASTHelpers.postComments(ast, this.text()), /*trailing:*/ true);
+ }
+ }
+
+ private isPinnedOrTripleSlash(comment: Comment): boolean {
+ var fullText = comment.fullText();
+ if (fullText.match(tripleSlashReferenceRegExp)) {
+ return true;
+ }
+ else {
+ return fullText.indexOf("/*!") === 0;
+ }
+ }
+
+ private emitCommentsArray(comments: Comment[], trailing: boolean, noLeadingSpace = false): void {
+ if (!this.emitOptions.compilationSettings().removeComments() && comments) {
+ for (var i = 0, n = comments.length; i < n; i++) {
+ this.emitComment(comments[i], trailing, /*first:*/ i === 0, noLeadingSpace);
+ }
+ }
+ }
+
+ public emitObjectLiteralExpression(objectLiteral: ObjectLiteralExpressionSyntax) {
+ this.recordSourceMappingStart(objectLiteral);
+
+ // Try to preserve the newlines between elements that the user had.
+ this.writeToken(objectLiteral.openBraceToken);
+ this.emitCommaSeparatedList(objectLiteral, objectLiteral.propertyAssignments, /*buffer:*/ " ", /*preserveNewLines:*/ true);
+ this.writeToken(objectLiteral.closeBraceToken);
+
+ this.recordSourceMappingEnd(objectLiteral);
+ }
+
+ public emitArrayLiteralExpression(arrayLiteral: ArrayLiteralExpressionSyntax) {
+ this.recordSourceMappingStart(arrayLiteral);
+
+ // Try to preserve the newlines between elements that the user had.
+ this.writeToken(arrayLiteral.openBracketToken);
+ this.emitCommaSeparatedList(arrayLiteral, arrayLiteral.expressions, /*buffer:*/ "", /*preserveNewLines:*/ true);
+ this.writeToken(arrayLiteral.closeBracketToken);
+
+ this.recordSourceMappingEnd(arrayLiteral);
+ }
+
+ public emitObjectCreationExpression(objectCreationExpression: ObjectCreationExpressionSyntax) {
+ this.recordSourceMappingStart(objectCreationExpression);
+ this.writeToken(objectCreationExpression.newKeyword);
+ this.writeToOutput(" ");
+ var target = objectCreationExpression.expression;
+
+ this.emit(target);
+ if (objectCreationExpression.argumentList) {
+ this.recordSourceMappingStart(objectCreationExpression.argumentList);
+ this.writeToken(objectCreationExpression.argumentList.openParenToken);
+ this.emitCommaSeparatedList(objectCreationExpression.argumentList, objectCreationExpression.argumentList.arguments, /*buffer:*/ "", /*preserveNewLines:*/ false);
+ this.writeToken(objectCreationExpression.argumentList.closeParenToken);
+ this.recordSourceMappingEnd(objectCreationExpression.argumentList);
+ }
+
+ this.recordSourceMappingEnd(objectCreationExpression);
+ }
+
+ public getConstantDecl(dotExpr: MemberAccessExpressionSyntax): PullEnumElementDecl {
+ var pullSymbol = this.semanticInfoChain.getSymbolForAST(dotExpr);
+ if (pullSymbol && pullSymbol.kind === PullElementKind.EnumMember) {
+ var pullDecls = pullSymbol.getDeclarations();
+ if (pullDecls.length === 1) {
+ var pullDecl = pullDecls[0];
+ if (pullDecl.kind === PullElementKind.EnumMember) {
+ return pullDecl;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public tryEmitConstant(dotExpr: MemberAccessExpressionSyntax) {
+ var propertyName = dotExpr.name;
+ var boundDecl = this.getConstantDecl(dotExpr);
+ if (boundDecl) {
+ var value = boundDecl.constantValue;
+ if (value !== null) {
+ this.recordSourceMappingStart(dotExpr);
+ this.writeToOutput(value.toString());
+ var comment = " /* ";
+ comment += propertyName.text();
+ comment += " */";
+ this.writeToOutput(comment);
+ this.recordSourceMappingEnd(dotExpr);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public emitInvocationExpression(callNode: InvocationExpressionSyntax) {
+ this.recordSourceMappingStart(callNode);
+ var target = callNode.expression;
+ var args = callNode.argumentList.arguments;
+
+ if (target.kind() === SyntaxKind.MemberAccessExpression && (target).expression.kind() === SyntaxKind.SuperKeyword) {
+ this.emit(target);
+ this.writeToOutput(".call");
+ this.recordSourceMappingStart(args);
+ this.writeToken(callNode.argumentList.openParenToken);
+ this.emitThis();
+ if (args && args.length > 0) {
+ this.writeToOutput(", ");
+ this.emitCommaSeparatedList(callNode.argumentList, args, /*buffer:*/ "", /*preserveNewLines:*/ false);
+ }
+ }
+ else {
+ if (callNode.expression.kind() === SyntaxKind.SuperKeyword && this.emitState.container === EmitContainer.Constructor) {
+ this.writeToOutput("_super.call");
+ }
+ else {
+ this.emitJavascript(target, false);
+ }
+ this.recordSourceMappingStart(args);
+ this.writeToken(callNode.argumentList.openParenToken);
+ if (callNode.expression.kind() === SyntaxKind.SuperKeyword && this.emitState.container === EmitContainer.Constructor) {
+ this.writeToOutput("this");
+ if (args && args.length > 0) {
+ this.writeToOutput(", ");
+ }
+ }
+ this.emitCommaSeparatedList(callNode.argumentList, args, /*buffer:*/ "", /*preserveNewLines:*/ false);
+ }
+
+ this.writeToken(callNode.argumentList.closeParenToken);
+ this.recordSourceMappingEnd(args);
+ this.recordSourceMappingEnd(callNode);
+ }
+
+ private emitParameterList(list: ParameterListSyntax): void {
+ this.writeToken(list.openParenToken);
+ this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(list.openParenToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true);
+ this.emitFunctionParameters(list.parameters, list.parameters);
+ this.writeToken(list.closeParenToken);
+ }
+
+ private emitFunctionParameters(ast: ISyntaxElement, parameters: ParameterSyntax[]): void {
+ var argsLen = 0;
+
+ if (parameters) {
+ this.emitComments(ast, true);
+
+ var tempContainer = this.setContainer(EmitContainer.Args);
+ argsLen = parameters.length;
+ var printLen = argsLen;
+ if (lastParameterIsRest(parameters)) {
+ printLen--;
+ }
+ for (var i = 0; i < printLen; i++) {
+ var arg = parameters[i];
+ this.emit(arg);
+
+ if (i < (printLen - 1)) {
+ this.writeToOutput(", ");
+ if (parameters) {
+ this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(parameters.separatorAt(i), this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true);
+ }
+ }
+ }
+ this.setContainer(tempContainer);
+
+ this.emitComments(ast, false);
+ }
+ }
+
+ private emitFunctionBodyStatements(name: string, funcDecl: ISyntaxElement, parameters: ParameterSyntax[], block: BlockSyntax, bodyExpression: ISyntaxElement): void {
+ this.writeLineToOutput(" {");
+ if (name) {
+ this.recordSourceMappingNameStart(name);
+ }
+
+ this.indenter.increaseIndent();
+
+ if (block) {
+ // We want any detached statements at the start of hte block to stay at the start.
+ // This is important for features like VSDoc which place their comments inside a
+ // block, but can't have them preceded by things like "var _this = this" when we
+ // emit.
+
+ this.emitDetachedComments(block.statements);
+ }
+
+ // Parameter list parameters with defaults could capture this
+ if (this.shouldCaptureThis(funcDecl)) {
+ this.writeCaptureThisStatement(funcDecl);
+ }
+
+ if (parameters) {
+ this.emitDefaultValueAssignments(parameters);
+ this.emitRestParameterInitializer(parameters);
+ }
+
+ if (block) {
+ this.emitList(block.statements);
+ this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(block.closeBraceToken, this.text()), /*trailing:*/ false);
+ }
+ else {
+ // Copy any comments before the body of the arrow function to the return statement.
+ // This is necessary for emitting correctness so we don't emit something like this:
+ //
+ // return
+ // // foo
+ // this.foo();
+ //
+ // Because of ASI, this gets parsed as "return;" which is *not* what we want for
+ // proper semantics.
+ //var preComments = bodyExpression.preComments();
+ //var postComments = bodyExpression.postComments();
+
+ //bodyExpression.setPreComments(null);
+ //bodyExpression.setPostComments(null);
+
+ this.emitIndent();
+ this.emitCommentsArray(ASTHelpers.preComments(bodyExpression, this.text()), /*trailing:*/ false);
+ this.writeToOutput("return ");
+ this.emit(bodyExpression);
+ this.writeLineToOutput(";");
+ this.emitCommentsArray(ASTHelpers.preComments(bodyExpression, this.text()), /*trailing:*/ true);
+
+ //bodyExpression.setPreComments(preComments);
+ //bodyExpression.setPostComments(postComments);
+ }
+
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+
+ if (block) {
+ this.writeToken(block.closeBraceToken);
+ }
+ else {
+ this.writeToOutputWithSourceMapRecord("}", bodyExpression);
+ }
+
+ if (name) {
+ this.recordSourceMappingNameEnd();
+ }
+ }
+
+ private emitDefaultValueAssignments(parameters: ParameterSyntax[]): void {
+ var n = parameters.length;
+ if (lastParameterIsRest(parameters)) {
+ n--;
+ }
+
+ for (var i = 0; i < n; i++) {
+ var arg = parameters[i];
+ var id = arg.identifier;
+ var equalsValueClause = arg.equalsValueClause;
+ if (equalsValueClause) {
+ this.emitIndent();
+ this.recordSourceMappingStart(arg);
+ this.writeToOutput("if (typeof " + id.text() + " === \"undefined\") { ");//
+ this.writeToken(id);
+ this.emitJavascript(equalsValueClause, false);
+ this.writeLineToOutput("; }");
+ this.recordSourceMappingEnd(arg);
+ }
+ }
+ }
+
+ private emitRestParameterInitializer(parameters: ParameterSyntax[]): void {
+ if (lastParameterIsRest(parameters)) {
+ var n = parameters.length;
+ var lastArg = parameters[n - 1];
+ var id = lastArg.identifier;
+ this.emitIndent();
+ this.recordSourceMappingStart(lastArg);
+ this.writeToOutput("var ");
+ this.writeToken(id);
+ this.writeLineToOutput(" = [];");
+ this.recordSourceMappingEnd(lastArg);
+ this.emitIndent();
+ this.writeToOutput("for (");
+ this.writeToOutputWithSourceMapRecord("var _i = 0;", lastArg);
+ this.writeToOutput(" ");
+ this.writeToOutputWithSourceMapRecord("_i < (arguments.length - " + (n - 1) + ")", lastArg);
+ this.writeToOutput("; ");
+ this.writeToOutputWithSourceMapRecord("_i++", lastArg);
+ this.writeLineToOutput(") {");
+ this.indenter.increaseIndent();
+ this.emitIndent();
+
+ this.writeToOutputWithSourceMapRecord(id.text() + "[_i] = arguments[_i + " + (n - 1) + "];", lastArg);
+ this.writeLineToOutput("");
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeLineToOutput("}");
+ }
+ }
+
+ private getImportDecls(fileName: string): PullDecl[] {
+ var topLevelDecl = this.semanticInfoChain.topLevelDecl(this.document.fileName);
+ var result: PullDecl[] = [];
+
+ var dynamicModuleDecl = topLevelDecl.getChildDecls()[0]; // Dynamic module declaration has to be present
+ var queue: PullDecl[] = dynamicModuleDecl.getChildDecls();
+
+ for (var i = 0, n = queue.length; i < n; i++) {
+ var decl = queue[i];
+
+ if (decl.kind & PullElementKind.TypeAlias) {
+ var importStatementAST = this.semanticInfoChain.getASTForDecl(decl);
+ if (importStatementAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference) { // external module
+ var symbol = decl.getSymbol(this.semanticInfoChain);
+ var typeSymbol = symbol && symbol.type;
+ if (typeSymbol && typeSymbol !== this.semanticInfoChain.anyTypeSymbol && !typeSymbol.isError()) {
+ result.push(decl);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public getModuleImportAndDependencyList(sourceUnit: SourceUnitSyntax) {
+ var importList = "";
+ var dependencyList = "";
+
+ var importDecls = this.getImportDecls(this.document.fileName);
+
+ // all dependencies are quoted
+ if (importDecls.length) {
+ for (var i = 0; i < importDecls.length; i++) {
+ var importStatementDecl = importDecls[i];
+ var importStatementSymbol = importStatementDecl.getSymbol(this.semanticInfoChain);
+ var importStatementAST = this.semanticInfoChain.getASTForDecl(importStatementDecl);
+
+ if (importStatementSymbol.isUsedAsValue()) {
+ if (i <= importDecls.length - 1) {
+ dependencyList += ", ";
+ importList += ", ";
+ }
+
+ importList += importStatementDecl.name;
+ dependencyList += (importStatementAST.moduleReference).stringLiteral.text();
+ }
+ }
+ }
+
+ // emit any potential amd dependencies
+ var amdDependencies = this.document.syntaxTree().amdDependencies();
+ for (var i = 0; i < amdDependencies.length; i++) {
+ dependencyList += ", \"" + amdDependencies[i] + "\"";
+ }
+
+ return {
+ importList: importList,
+ dependencyList: dependencyList
+ };
+ }
+
+ public shouldCaptureThis(ast: ISyntaxElement) {
+ if (ast.kind() === SyntaxKind.SourceUnit) {
+ var scriptDecl = this.semanticInfoChain.topLevelDecl(this.document.fileName);
+ return hasFlag(scriptDecl.flags, PullElementFlags.MustCaptureThis);
+ }
+
+ var decl = this.semanticInfoChain.getDeclForAST(ast);
+ if (decl) {
+ return hasFlag(decl.flags, PullElementFlags.MustCaptureThis);
+ }
+
+ return false;
+ }
+
+ public emitEnum(moduleDecl: EnumDeclarationSyntax) {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(moduleDecl);
+ this.pushDecl(pullDecl);
+
+ var svModuleName = this.moduleName;
+ this.moduleName = moduleDecl.identifier.text();
+
+ var temp = this.setContainer(EmitContainer.Module);
+ var isExported = hasFlag(pullDecl.flags, PullElementFlags.Exported);
+
+ if (!isExported) {
+ this.recordSourceMappingStart(moduleDecl);
+ this.writeToOutput("var ");
+ this.writeToOutputWithSourceMapRecord(this.moduleName, moduleDecl.identifier);
+ this.writeLineToOutput(";");
+ this.recordSourceMappingEnd(moduleDecl);
+ this.emitIndent();
+ }
+
+ this.writeToOutput("(");
+ this.recordSourceMappingStart(moduleDecl);
+ this.writeToOutput("function (");
+ this.writeToOutputWithSourceMapRecord(this.moduleName, moduleDecl.identifier);
+ this.writeLineToOutput(") {");
+
+ this.recordSourceMappingNameStart(this.moduleName);
+
+ this.indenter.increaseIndent();
+
+ if (this.shouldCaptureThis(moduleDecl)) {
+ this.writeCaptureThisStatement(moduleDecl);
+ }
+
+ this.emitSeparatedList(moduleDecl.enumElements);
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+
+ var parentIsDynamic = temp === EmitContainer.DynamicModule;
+ if (temp === EmitContainer.Prog && isExported) {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.writeToOutput(")(this." + this.moduleName + " || (this." + this.moduleName + " = {}));");
+ }
+ else if (isExported || temp === EmitContainer.Prog) {
+ var dotMod = svModuleName !== "" ? (parentIsDynamic ? "exports" : svModuleName) + "." : svModuleName;
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.writeToOutput(")(" + dotMod + this.moduleName + " || (" + dotMod + this.moduleName + " = {}));");
+ }
+ else if (!isExported && temp !== EmitContainer.Prog) {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.writeToOutput(")(" + this.moduleName + " || (" + this.moduleName + " = {}));");
+ }
+ else {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.writeToOutput(")();");
+ }
+
+ this.recordSourceMappingEnd(moduleDecl);
+ if (temp !== EmitContainer.Prog && isExported) {
+ this.recordSourceMappingStart(moduleDecl);
+ if (parentIsDynamic) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ this.writeToOutput("var " + this.moduleName + " = exports." + this.moduleName + ";");
+ }
+ else {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ this.writeToOutput("var " + this.moduleName + " = " + svModuleName + "." + this.moduleName + ";");
+ }
+ this.recordSourceMappingEnd(moduleDecl);
+ }
+
+ this.setContainer(temp);
+ this.moduleName = svModuleName;
+
+ this.popDecl(pullDecl);
+ }
+
+ private getModuleDeclToVerifyChildNameCollision(moduleDecl: PullDecl, changeNameIfAnyDeclarationInContext: boolean) {
+ if (ArrayUtilities.contains(this.declStack, moduleDecl)) {
+ // Given decl is in the scope, we would need to check for child name collision
+ return moduleDecl;
+ }
+ else if (changeNameIfAnyDeclarationInContext) {
+ // Check if any other declaration of the given symbol is in scope
+ // (eg. when emitting expression of type defined from different declaration in reopened module)
+ var symbol = moduleDecl.getSymbol(this.semanticInfoChain);
+ if (symbol) {
+ var otherDecls = symbol.getDeclarations();
+ for (var i = 0; i < otherDecls.length; i++) {
+ // If the other decl is in the scope, use this decl to determine which name to display
+ if (ArrayUtilities.contains(this.declStack, otherDecls[i])) {
+ return otherDecls[i];
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private hasChildNameCollision(moduleName: string, parentDecl: PullDecl) {
+ var childDecls = parentDecl.getChildDecls();
+ return ArrayUtilities.any(childDecls, (childDecl: PullDecl) => {
+ var childAST = this.semanticInfoChain.getASTForDecl(childDecl);
+ // Enum member it can never conflict with module name as it is property of the enum
+ // Only if this child would be emitted we need to look further in
+ if (childDecl.kind != PullElementKind.EnumMember && this.shouldEmit(childAST)) {
+ // same name child
+ if (childDecl.name === moduleName) {
+ // collision if the parent was not class
+ if (parentDecl.kind != PullElementKind.Class) {
+ return true;
+ }
+
+ // If the parent was class, we would find name collision if this was not a property/method/accessor
+ if (!(childDecl.kind == PullElementKind.Method ||
+ childDecl.kind == PullElementKind.Property ||
+ childDecl.kind == PullElementKind.SetAccessor ||
+ childDecl.kind == PullElementKind.GetAccessor)) {
+ return true;
+ }
+ }
+
+ // Check if the name collision exists in any of the children
+ if (this.hasChildNameCollision(moduleName, childDecl)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ // Get the moduleName to write in js file
+ // If changeNameIfAnyDeclarationInContext is true, verify if any of the declarations for the symbol would need rename.
+ private getModuleName(moduleDecl: PullDecl, changeNameIfAnyDeclarationInContext?: boolean) {
+ var moduleName = moduleDecl.name;
+ var moduleDisplayName = moduleDecl.getDisplayName();
+
+ // If the decl is in stack it may need name change in the js file
+ moduleDecl = this.getModuleDeclToVerifyChildNameCollision(moduleDecl, changeNameIfAnyDeclarationInContext);
+ if (moduleDecl && moduleDecl.kind != PullElementKind.Enum) {
+ // If there is any child that would be emitted with same name as module, js files would need to use rename for the module
+ while (this.hasChildNameCollision(moduleName, moduleDecl)) {
+ // there was name collision with member which could result in faulty codegen, try rename with prepend of '_'
+ moduleName = "_" + moduleName;
+ moduleDisplayName = "_" + moduleDisplayName;
+ }
+ }
+
+ return moduleDisplayName;
+ }
+
+ private emitModuleDeclarationWorker(moduleDecl: ModuleDeclarationSyntax) {
+ if (moduleDecl.stringLiteral) {
+ this.emitSingleModuleDeclaration(moduleDecl, moduleDecl.stringLiteral);
+ }
+ else {
+ var moduleNames = ASTHelpers.getModuleNames(moduleDecl.name);
+ this.emitSingleModuleDeclaration(moduleDecl, moduleNames[0]);
+ }
+ }
+
+ private writeToken(token: ISyntaxToken) {
+ if (token) {
+ this.writeToOutputWithSourceMapRecord(token.text(), token);
+ }
+ }
+
+ public emitSingleModuleDeclaration(moduleDecl: ModuleDeclarationSyntax, moduleName: ISyntaxToken) {
+ var isLastName = ASTHelpers.isLastNameOfModule(moduleDecl, moduleName);
+
+ if (isLastName) {
+ // Doc Comments on the ast belong to the innermost module being emitted.
+ this.emitComments(moduleDecl, true);
+ }
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(moduleName);
+ this.pushDecl(pullDecl);
+
+ var svModuleName = this.moduleName;
+
+ if (moduleDecl.stringLiteral) {
+ this.moduleName = tokenValueText(moduleDecl.stringLiteral);
+ if (isTSFile(this.moduleName)) {
+ this.moduleName = this.moduleName.substring(0, this.moduleName.length - ".ts".length);
+ }
+ }
+ else {
+ this.moduleName = moduleName.text();
+ }
+
+ var temp = this.setContainer(EmitContainer.Module);
+ var isExported = hasFlag(pullDecl.flags, PullElementFlags.Exported);
+
+ // prologue
+
+ if (!isExported) {
+ this.recordSourceMappingStart(moduleDecl);
+ this.writeToOutput("var ");
+ this.writeToOutputWithSourceMapRecord(this.moduleName, moduleName);
+ this.writeLineToOutput(";");
+ this.recordSourceMappingEnd(moduleDecl);
+ this.emitIndent();
+ }
+
+ this.writeToOutput("(");
+ this.recordSourceMappingStart(moduleDecl);
+ this.writeToOutput("function (");
+ // Use the name that doesnt conflict with its members,
+ // this.moduleName needs to be updated to make sure that export member declaration is emitted correctly
+ this.moduleName = this.getModuleName(pullDecl);
+ this.writeToOutputWithSourceMapRecord(this.moduleName, moduleName);
+ this.writeLineToOutput(") {");
+
+ this.recordSourceMappingNameStart(moduleName.text());
+
+ this.indenter.increaseIndent();
+
+ if (this.shouldCaptureThis(moduleDecl)) {
+ this.writeCaptureThisStatement(moduleDecl);
+ }
+
+ if (moduleName === moduleDecl.stringLiteral) {
+ this.emitList(moduleDecl.moduleElements);
+ }
+ else {
+ var moduleNames = ASTHelpers.getModuleNames(moduleDecl.name);
+ var nameIndex = moduleNames.indexOf(moduleName);
+
+ Debug.assert(nameIndex >= 0);
+
+ if (isLastName) {
+ // If we're on the innermost module, we can emit the module elements.
+ this.emitList(moduleDecl.moduleElements);
+ }
+ else {
+ // otherwise, just recurse and emit the next module in the A.B.C module name.
+ this.emitIndent();
+ this.emitSingleModuleDeclaration(moduleDecl, moduleNames[nameIndex + 1]);
+ this.writeLineToOutput("");
+ }
+ }
+
+ this.moduleName = moduleName.text();
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+
+ // epilogue
+ var parentIsDynamic = temp === EmitContainer.DynamicModule;
+ this.recordSourceMappingStart(moduleDecl.closeBraceToken);
+ if (temp === EmitContainer.Prog && isExported) {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(moduleDecl.closeBraceToken);
+ this.writeToOutput(")(this." + this.moduleName + " || (this." + this.moduleName + " = {}));");
+ }
+ else if (isExported || temp === EmitContainer.Prog) {
+ var dotMod = svModuleName !== "" ? (parentIsDynamic ? "exports" : svModuleName) + "." : svModuleName;
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(moduleDecl.closeBraceToken);
+ this.writeToOutput(")(" + dotMod + this.moduleName + " || (" + dotMod + this.moduleName + " = {}));");
+ }
+ else if (!isExported && temp !== EmitContainer.Prog) {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(moduleDecl.closeBraceToken);
+ this.writeToOutput(")(" + this.moduleName + " || (" + this.moduleName + " = {}));");
+ }
+ else {
+ this.writeToOutput("}");
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(moduleDecl.closeBraceToken);
+ this.writeToOutput(")();");
+ }
+
+ this.recordSourceMappingEnd(moduleDecl);
+ if (temp !== EmitContainer.Prog && isExported) {
+ this.recordSourceMappingStart(moduleDecl);
+ if (parentIsDynamic) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ this.writeToOutput("var " + this.moduleName + " = exports." + this.moduleName + ";");
+ }
+ else {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ this.writeToOutput("var " + this.moduleName + " = " + svModuleName + "." + this.moduleName + ";");
+ }
+ this.recordSourceMappingEnd(moduleDecl);
+ }
+
+ this.setContainer(temp);
+ this.moduleName = svModuleName;
+
+ this.popDecl(pullDecl);
+
+ if (isLastName) {
+ // Comments on the module ast belong to the innermost module being emitted.
+ this.emitComments(moduleDecl, false);
+ }
+ }
+
+ public emitEnumElement(varDecl: EnumElementSyntax): void {
+ // [[""] = ] = "";
+ var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl);
+ Debug.assert(pullDecl && pullDecl.kind === PullElementKind.EnumMember);
+
+ this.emitComments(varDecl, true);
+ this.recordSourceMappingStart(varDecl);
+
+ var representation = (varDecl.propertyName.kind() === SyntaxKind.StringLiteral)
+ ? varDecl.propertyName.text()
+ : ('"' + tokenValueText(varDecl.propertyName) + '"');
+
+ this.writeToOutput(this.moduleName);
+ this.writeToOutput('[');
+ this.writeToOutput(this.moduleName);
+ this.writeToOutput('[');
+ this.writeToOutput(representation);
+ this.writeToOutput(']');
+
+ if (varDecl.equalsValueClause) {
+ this.emit(varDecl.equalsValueClause);
+ }
+ else if (pullDecl.constantValue !== null) {
+ this.writeToOutput(' = ');
+ this.writeToOutput(pullDecl.constantValue.toString());
+ }
+ else {
+ this.writeToOutput(' = null');
+ }
+
+ this.writeToOutput('] = ');
+ this.writeToOutput(representation);
+ this.recordSourceMappingEnd(varDecl);
+ this.emitComments(varDecl, false);
+ this.writeToOutput(';');
+ }
+
+ public emitElementAccessExpression(expression: ElementAccessExpressionSyntax) {
+ this.recordSourceMappingStart(expression);
+ this.emit(expression.expression);
+ this.writeToken(expression.openBracketToken);
+ this.emit(expression.argumentExpression);
+ this.writeToken(expression.closeBracketToken);
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitSimpleArrowFunctionExpression(arrowFunction: SimpleArrowFunctionExpressionSyntax): void {
+ this.emitAnyArrowFunctionExpression(arrowFunction, arrowFunction.block, arrowFunction.expression);
+ }
+
+ public emitParenthesizedArrowFunctionExpression(arrowFunction: ParenthesizedArrowFunctionExpressionSyntax): void {
+ this.emitAnyArrowFunctionExpression(arrowFunction, arrowFunction.block, arrowFunction.expression);
+ }
+
+ private emitAnyArrowFunctionExpression(arrowFunction: ISyntaxElement, block: BlockSyntax, expression: ISyntaxElement): void {
+ var savedInArrowFunction = this.inArrowFunction;
+ this.inArrowFunction = true;
+
+ var temp = this.setContainer(EmitContainer.Function);
+
+ this.recordSourceMappingStart(arrowFunction);
+
+ // Start
+ var pullDecl = this.semanticInfoChain.getDeclForAST(arrowFunction);
+ this.pushDecl(pullDecl);
+
+ this.emitComments(arrowFunction, true);
+
+ this.recordSourceMappingStart(arrowFunction);
+ this.writeToOutput("function ");
+
+ var parameters: ParameterSyntax[] = null;
+ if (arrowFunction.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression) {
+ var parenthesizedArrowFunction = arrowFunction;
+
+ parameters = parenthesizedArrowFunction.callSignature.parameterList.parameters;
+ this.emitParameterList(parenthesizedArrowFunction.callSignature.parameterList);
+ }
+ else {
+ var parameter = (arrowFunction).parameter;
+ parameters = [parameter];
+ this.writeToOutput("(");
+ this.emitFunctionParameters(parameter, parameters);
+ this.writeToOutput(")");
+ }
+
+ this.emitFunctionBodyStatements(/*funcName:*/ null, arrowFunction, parameters, block, expression);
+
+ this.recordSourceMappingEnd(arrowFunction);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(arrowFunction);
+
+ this.emitComments(arrowFunction, false);
+
+ this.popDecl(pullDecl);
+ this.setContainer(temp);
+ this.inArrowFunction = savedInArrowFunction;
+ }
+
+ public emitConstructor(funcDecl: ConstructorDeclarationSyntax) {
+ if (!funcDecl.block) {
+ return;
+ }
+ var temp = this.setContainer(EmitContainer.Constructor);
+
+ this.recordSourceMappingStart(funcDecl);
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.pushDecl(pullDecl);
+
+ this.emitComments(funcDecl, true);
+
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToOutput("function ");
+ this.writeToOutput(this.thisClassNode.identifier.text());
+
+ this.emitParameterList(funcDecl.callSignature.parameterList);
+ this.writeLineToOutput(" {");
+
+ this.recordSourceMappingNameStart("constructor");
+ this.indenter.increaseIndent();
+
+ var parameters = funcDecl.callSignature.parameterList.parameters;
+ this.emitDefaultValueAssignments(parameters);
+ this.emitRestParameterInitializer(parameters);
+
+ if (this.shouldCaptureThis(funcDecl)) {
+ this.writeCaptureThisStatement(funcDecl);
+ }
+
+ this.emitConstructorStatements(funcDecl);
+ this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(funcDecl.block.closeBraceToken, this.text()), /*trailing:*/ false);
+
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeToken(funcDecl.block.closeBraceToken);
+
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(funcDecl);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(funcDecl);
+
+ this.emitComments(funcDecl, false);
+
+ this.popDecl(pullDecl);
+ this.setContainer(temp);
+ }
+
+ public emitGetAccessor(accessor: GetAccessorSyntax): void {
+ this.recordSourceMappingStart(accessor);
+ this.writeToOutput("get ");
+
+ var temp = this.setContainer(EmitContainer.Function);
+
+ this.recordSourceMappingStart(accessor);
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(accessor);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(accessor);
+
+ var accessorSymbol = PullHelpers.getAccessorSymbol(accessor, this.semanticInfoChain);
+ var container = accessorSymbol.getContainer();
+ var containerKind = container.kind;
+
+ this.recordSourceMappingNameStart(accessor.propertyName.text());
+ this.writeToOutput(accessor.propertyName.text());
+ this.emitParameterList(accessor.callSignature.parameterList);
+
+ this.emitFunctionBodyStatements(null, accessor, accessor.callSignature.parameterList.parameters, accessor.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(accessor);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(accessor);
+
+ this.popDecl(pullDecl);
+ this.setContainer(temp);
+ this.recordSourceMappingEnd(accessor);
+ }
+
+ public emitSetAccessor(accessor: SetAccessorSyntax): void {
+ this.recordSourceMappingStart(accessor);
+ this.writeToOutput("set ");
+
+ var temp = this.setContainer(EmitContainer.Function);
+
+ this.recordSourceMappingStart(accessor);
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(accessor);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(accessor);
+
+ var accessorSymbol = PullHelpers.getAccessorSymbol(accessor, this.semanticInfoChain);
+ var container = accessorSymbol.getContainer();
+ var containerKind = container.kind;
+
+ this.recordSourceMappingNameStart(accessor.propertyName.text());
+ this.writeToOutput(accessor.propertyName.text());
+
+ this.emitParameterList(accessor.callSignature.parameterList);
+
+ this.emitFunctionBodyStatements(null, accessor, accessor.callSignature.parameterList.parameters, accessor.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(accessor);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(accessor);
+
+ this.popDecl(pullDecl);
+ this.setContainer(temp);
+ this.recordSourceMappingEnd(accessor);
+ }
+
+ public emitFunctionExpression(funcDecl: FunctionExpressionSyntax): void {
+ var savedInArrowFunction = this.inArrowFunction;
+ this.inArrowFunction = false;
+
+ var temp = this.setContainer(EmitContainer.Function);
+
+ var funcName = funcDecl.identifier ? funcDecl.identifier.text() : null;//.getNameText();
+
+ this.recordSourceMappingStart(funcDecl);
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToken(funcDecl.functionKeyword);
+ this.writeToOutput(" ");
+
+ //var id = funcDecl.getNameText();
+ if (funcDecl.identifier) {
+ this.writeToOutputWithSourceMapRecord(funcDecl.identifier.text(), funcDecl.identifier);
+ }
+
+ this.emitParameterList(funcDecl.callSignature.parameterList);
+ this.emitFunctionBodyStatements(funcName, funcDecl, funcDecl.callSignature.parameterList.parameters, funcDecl.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(funcDecl);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(funcDecl);
+
+ this.emitComments(funcDecl, false);
+
+ this.popDecl(pullDecl);
+
+ this.setContainer(temp);
+ this.inArrowFunction = savedInArrowFunction;
+ }
+
+ public emitFunction(funcDecl: FunctionDeclarationSyntax) {
+ if (funcDecl.block === null) {
+ return;
+ }
+ var savedInArrowFunction = this.inArrowFunction;
+ this.inArrowFunction = false;
+
+ var temp = this.setContainer(EmitContainer.Function);
+
+ var funcName = funcDecl.identifier.text();
+
+ this.recordSourceMappingStart(funcDecl);
+
+ var printName = funcDecl.identifier !== null
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.pushDecl(pullDecl);
+
+ this.emitComments(funcDecl, true);
+
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToken(funcDecl.functionKeyword);
+ this.writeToOutput(" ");
+
+ if (printName) {
+ var id = funcDecl.identifier.text();
+ if (id) {
+ if (funcDecl.identifier) {
+ this.recordSourceMappingStart(funcDecl.identifier);
+ }
+ this.writeToOutput(id);
+ if (funcDecl.identifier) {
+ this.recordSourceMappingEnd(funcDecl.identifier);
+ }
+ }
+ }
+
+ this.emitParameterList(funcDecl.callSignature.parameterList);
+
+ var parameters = funcDecl.callSignature.parameterList.parameters;
+ this.emitFunctionBodyStatements(funcDecl.identifier.text(), funcDecl, parameters, funcDecl.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(funcDecl);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(funcDecl);
+
+ this.emitComments(funcDecl, false);
+
+ this.popDecl(pullDecl);
+
+ this.setContainer(temp);
+ this.inArrowFunction = savedInArrowFunction;
+
+ if (funcDecl.block) {
+ var pullFunctionDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ if ((this.emitState.container === EmitContainer.Module || this.emitState.container === EmitContainer.DynamicModule) && pullFunctionDecl && hasFlag(pullFunctionDecl.flags, PullElementFlags.Exported)) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ var modName = this.emitState.container === EmitContainer.Module ? this.moduleName : "exports";
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToOutput(modName + "." + funcName + " = " + funcName + ";");
+ this.recordSourceMappingEnd(funcDecl);
+ }
+ }
+ }
+
+ public emitAmbientVarDecl(varDecl: VariableDeclaratorSyntax) {
+ this.recordSourceMappingStart(this.currentVariableDeclaration);
+ if (varDecl.equalsValueClause) {
+ this.emitComments(varDecl, true);
+ this.recordSourceMappingStart(varDecl);
+ this.writeToOutputWithSourceMapRecord(varDecl.propertyName.text(), varDecl.propertyName);
+ this.emitJavascript(varDecl.equalsValueClause, false);
+ this.recordSourceMappingEnd(varDecl);
+ this.emitComments(varDecl, false);
+ }
+ }
+
+ // Emits "var " if it is allowed
+ public emitVarDeclVar() {
+ if (this.currentVariableDeclaration) {
+ this.writeToOutput("var ");
+ }
+ }
+
+ public emitVariableDeclaration(declaration: VariableDeclarationSyntax) {
+ var varDecl = declaration.variableDeclarators[0];
+
+ var symbol = this.semanticInfoChain.getSymbolForAST(varDecl);
+
+ var parentSymbol = symbol ? symbol.getContainer() : null;
+ var parentKind = parentSymbol ? parentSymbol.kind : PullElementKind.None;
+
+ this.emitComments(declaration, true);
+
+ var pullVarDecl = this.semanticInfoChain.getDeclForAST(varDecl);
+ var isAmbientWithoutInit = pullVarDecl && hasFlag(pullVarDecl.flags, PullElementFlags.Ambient) && varDecl.equalsValueClause === null;
+ if (!isAmbientWithoutInit) {
+ var prevVariableDeclaration = this.currentVariableDeclaration;
+ this.currentVariableDeclaration = declaration;
+
+ for (var i = 0, n = declaration.variableDeclarators.length; i < n; i++) {
+ var declarator = declaration.variableDeclarators[i];
+
+ if (i > 0) {
+ this.writeToOutput(", ");
+ }
+
+ this.emit(declarator);
+ }
+ this.currentVariableDeclaration = prevVariableDeclaration;
+
+ // Declarator emit would take care of emitting start of the variable declaration start
+ this.recordSourceMappingEnd(declaration);
+ }
+
+ this.emitComments(declaration, false);
+ }
+
+ private emitMemberVariableDeclaration(varDecl: MemberVariableDeclarationSyntax) {
+ Debug.assert(!hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause);
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl);
+ this.pushDecl(pullDecl);
+
+ this.emitComments(varDecl, true);
+ this.recordSourceMappingStart(varDecl);
+
+ var varDeclName = varDecl.variableDeclarator.propertyName.text();
+ var quotedOrNumber = isQuoted(varDeclName) || varDecl.variableDeclarator.propertyName.kind() !== SyntaxKind.IdentifierName;
+
+ var symbol = this.semanticInfoChain.getSymbolForAST(varDecl);
+ var parentSymbol = symbol ? symbol.getContainer() : null;
+ var parentDecl = pullDecl && pullDecl.getParentDecl();
+
+ if (quotedOrNumber) {
+ this.writeToOutput("this[");
+ }
+ else {
+ this.writeToOutput("this.");
+ }
+
+ this.writeToOutputWithSourceMapRecord(varDecl.variableDeclarator.propertyName.text(), varDecl.variableDeclarator.propertyName);
+
+ if (quotedOrNumber) {
+ this.writeToOutput("]");
+ }
+
+ if (varDecl.variableDeclarator.equalsValueClause) {
+ // Ensure we have a fresh var list count when recursing into the variable
+ // initializer. We don't want our current list of variables to affect how we
+ // emit nested variable lists.
+ var prevVariableDeclaration = this.currentVariableDeclaration;
+ this.emit(varDecl.variableDeclarator.equalsValueClause);
+ this.currentVariableDeclaration = prevVariableDeclaration;
+ }
+
+ // class
+ if (this.emitState.container !== EmitContainer.Args) {
+ this.writeToOutput(";");
+ }
+
+ this.recordSourceMappingEnd(varDecl);
+ this.emitComments(varDecl, false);
+
+ this.popDecl(pullDecl);
+ }
+
+ public emitVariableDeclarator(varDecl: VariableDeclaratorSyntax) {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl);
+ this.pushDecl(pullDecl);
+ if (pullDecl && (pullDecl.flags & PullElementFlags.Ambient) === PullElementFlags.Ambient) {
+ this.emitAmbientVarDecl(varDecl);
+ }
+ else {
+ this.emitComments(varDecl, true);
+ this.recordSourceMappingStart(this.currentVariableDeclaration);
+ this.recordSourceMappingStart(varDecl);
+
+ var varDeclName = varDecl.propertyName.text();
+
+ var symbol = this.semanticInfoChain.getSymbolForAST(varDecl);
+ var parentSymbol = symbol ? symbol.getContainer() : null;
+ var parentDecl = pullDecl && pullDecl.getParentDecl();
+ var parentIsModule = parentDecl && (parentDecl.flags & PullElementFlags.SomeInitializedModule);
+
+ if (parentIsModule) {
+ // module
+ if (!hasFlag(pullDecl.flags, PullElementFlags.Exported)/* && !varDecl.isProperty() */) {
+ this.emitVarDeclVar();
+ }
+ else {
+ if (this.emitState.container === EmitContainer.DynamicModule) {
+ this.writeToOutput("exports.");
+ }
+ else {
+ this.writeToOutput(this.moduleName + ".");
+ }
+ }
+ }
+ else {
+ this.emitVarDeclVar();
+ }
+
+ this.writeToOutputWithSourceMapRecord(varDecl.propertyName.text(), varDecl.propertyName);
+
+ if (varDecl.equalsValueClause) {
+ // Ensure we have a fresh var list count when recursing into the variable
+ // initializer. We don't want our current list of variables to affect how we
+ // emit nested variable lists.
+ var prevVariableDeclaration = this.currentVariableDeclaration;
+ this.emit(varDecl.equalsValueClause);
+ this.currentVariableDeclaration = prevVariableDeclaration;
+ }
+
+ this.recordSourceMappingEnd(varDecl);
+ this.emitComments(varDecl, false);
+ }
+ this.currentVariableDeclaration = undefined;
+ this.popDecl(pullDecl);
+ }
+
+ private symbolIsUsedInItsEnclosingContainer(symbol: PullSymbol, dynamic = false) {
+ var symDecls = symbol.getDeclarations();
+
+ if (symDecls.length) {
+ var enclosingDecl = this.getEnclosingDecl();
+ if (enclosingDecl) {
+ var parentDecl = symDecls[0].getParentDecl();
+ if (parentDecl) {
+ var symbolDeclarationEnclosingContainer = parentDecl;
+ var enclosingContainer = enclosingDecl;
+
+ // compute the closing container of the symbol's declaration
+ while (symbolDeclarationEnclosingContainer) {
+ if (symbolDeclarationEnclosingContainer.kind === (dynamic ? PullElementKind.DynamicModule : PullElementKind.Container)) {
+ break;
+ }
+ symbolDeclarationEnclosingContainer = symbolDeclarationEnclosingContainer.getParentDecl();
+ }
+
+ // if the symbol in question is not a global, compute the nearest
+ // enclosing declaration from the point of usage
+ if (symbolDeclarationEnclosingContainer) {
+ while (enclosingContainer) {
+ if (enclosingContainer.kind === (dynamic ? PullElementKind.DynamicModule : PullElementKind.Container)) {
+ break;
+ }
+
+ enclosingContainer = enclosingContainer.getParentDecl();
+ }
+ }
+
+ if (symbolDeclarationEnclosingContainer && enclosingContainer) {
+ var same = symbolDeclarationEnclosingContainer === enclosingContainer;
+
+ // initialized module object variables are bound to their parent's decls
+ if (!same && symbol.anyDeclHasFlag(PullElementFlags.InitializedModule)) {
+ same = symbolDeclarationEnclosingContainer === enclosingContainer.getParentDecl();
+ }
+
+ return same;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // In some cases, when emitting a name, the emitter needs to qualify a symbol
+ // name by first emitting its parent's name. This generally happens when the
+ // name referenced is in scope in TypeScript, but not in Javascript. This is true
+ // in any of the following 3 cases:
+ // - It is an enum member, even if accessed from within the enum declaration in which it is defined
+ // - It is an exported var, even if accessed from within the module declaration in which it is defined
+ // - It is an exported member of the current module, but is never defined in this particular module
+ // declaration (i.e. it is only defined in other components of the same merged module)
+ private shouldQualifySymbolNameWithParentName(symbol: PullSymbol): boolean {
+ var enclosingContextDeclPath = this.declStack;
+ var symbolDeclarations = symbol.getDeclarations();
+ for (var i = 0; i < symbolDeclarations.length; i++) {
+ var currentDecl = symbolDeclarations[i];
+ var declParent = currentDecl.getParentDecl();
+
+ if (currentDecl.kind === PullElementKind.EnumMember) {
+ return true;
+ }
+
+ // All decls of the same symbol must agree on whether they are exported
+ // If one decl is not exported, none of them are, and it is safe to
+ // assume it is just a local
+ if (!hasFlag(currentDecl.flags, PullElementFlags.Exported)) {
+ return false;
+ }
+
+ // Section 10.6:
+ // When a variable is exported, all references to the variable in the body of the
+ // module are replaced with
+ // .< VariableName>
+ if (currentDecl.kind === PullElementKind.Variable && !hasFlag(currentDecl.flags, PullElementFlags.ImplicitVariable)) {
+ return true;
+ }
+
+ if (ArrayUtilities.contains(this.declStack, declParent)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Get the symbol information to be used for emitting the ast
+ private getSymbolForEmit(ast: ISyntaxElement) {
+ var pullSymbol = this.semanticInfoChain.getSymbolForAST(ast);
+ var pullSymbolAlias = this.semanticInfoChain.getAliasSymbolForAST(ast);
+ if (pullSymbol && pullSymbolAlias) {
+ var symbolToCompare = isTypesOnlyLocation(ast) ?
+ pullSymbolAlias.getExportAssignedTypeSymbol() :
+ pullSymbolAlias.getExportAssignedValueSymbol();
+
+ if (pullSymbol === symbolToCompare) {
+ pullSymbol = pullSymbolAlias;
+ pullSymbolAlias = null;
+ }
+ }
+ return { symbol: pullSymbol, aliasSymbol: pullSymbolAlias };
+ }
+
+ public emitName(name: ISyntaxToken, addThis: boolean) {
+ this.emitComments(name, true);
+ this.recordSourceMappingStart(name);
+ if (name.text().length > 0) {
+ var symbolForEmit = this.getSymbolForEmit(name);
+ var pullSymbol = symbolForEmit.symbol;
+ if (!pullSymbol) {
+ pullSymbol = this.semanticInfoChain.anyTypeSymbol;
+ }
+ var pullSymbolAlias = symbolForEmit.aliasSymbol;
+ var pullSymbolKind = pullSymbol.kind;
+ var isLocalAlias = pullSymbolAlias && (pullSymbolAlias.getDeclarations()[0].getParentDecl() === this.getEnclosingDecl());
+ if (addThis && (this.emitState.container !== EmitContainer.Args) && pullSymbol) {
+ var pullSymbolContainer = pullSymbol.getContainer();
+
+ if (pullSymbolContainer) {
+ var pullSymbolContainerKind = pullSymbolContainer.kind;
+
+ if (PullHelpers.symbolIsModule(pullSymbolContainer) || pullSymbolContainerKind === PullElementKind.Enum ||
+ pullSymbolContainer.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum)) {
+ var needToEmitParentName = this.shouldQualifySymbolNameWithParentName(pullSymbol);
+ if (needToEmitParentName) {
+ var parentDecl = pullSymbol.getDeclarations()[0].getParentDecl();
+ Debug.assert(parentDecl && !parentDecl.isRootDecl());
+ this.writeToOutput(this.getModuleName(parentDecl, /* changeNameIfAnyDeclarationInContext */ true) + ".");
+ }
+ }
+ else if (pullSymbolContainerKind === PullElementKind.DynamicModule ||
+ pullSymbolContainer.anyDeclHasFlag(PullElementFlags.InitializedDynamicModule)) {
+ if (pullSymbolKind === PullElementKind.Property) {
+ // If dynamic module
+ this.writeToOutput("exports.");
+ }
+ else if (pullSymbol.anyDeclHasFlag(PullElementFlags.Exported) &&
+ !isLocalAlias &&
+ !pullSymbol.anyDeclHasFlag(PullElementFlags.ImplicitVariable) &&
+ pullSymbol.kind !== PullElementKind.ConstructorMethod &&
+ pullSymbol.kind !== PullElementKind.Class &&
+ pullSymbol.kind !== PullElementKind.Enum) {
+ this.writeToOutput("exports.");
+ }
+ }
+ }
+ }
+
+ this.writeToOutput(name.text());
+ }
+
+ this.recordSourceMappingEnd(name);
+ this.emitComments(name, false);
+ }
+
+ public recordSourceMappingNameStart(name: string) {
+ if (this.sourceMapper) {
+ var nameIndex = -1;
+ if (name) {
+ if (this.sourceMapper.currentNameIndex.length > 0) {
+ var parentNameIndex = this.sourceMapper.currentNameIndex[this.sourceMapper.currentNameIndex.length - 1];
+ if (parentNameIndex !== -1) {
+ name = this.sourceMapper.names[parentNameIndex] + "." + name;
+ }
+ }
+
+ // Look if there already exists name
+ var nameIndex = this.sourceMapper.names.length - 1;
+ for (nameIndex; nameIndex >= 0; nameIndex--) {
+ if (this.sourceMapper.names[nameIndex] === name) {
+ break;
+ }
+ }
+
+ if (nameIndex === -1) {
+ nameIndex = this.sourceMapper.names.length;
+ this.sourceMapper.names.push(name);
+ }
+ }
+ this.sourceMapper.currentNameIndex.push(nameIndex);
+ }
+ }
+
+ public recordSourceMappingNameEnd() {
+ if (this.sourceMapper) {
+ this.sourceMapper.currentNameIndex.pop();
+ }
+ }
+
+ private recordSourceMappingStart(ast: ISyntaxElement) {
+ if (this.sourceMapper && ASTHelpers.isValidAstNode(ast)) {
+ var text = this.text();
+ this.recordSourceMappingSpanStart(ast, start(ast, text), end(ast, text));
+ }
+ }
+
+ private recordSourceMappingCommentStart(comment: Comment) {
+ this.recordSourceMappingSpanStart(comment, comment.start(), comment.end());
+ }
+
+ private recordSourceMappingSpanStart(ast: any, start: number, end: number) {
+ if (this.sourceMapper && ast && start !== -1 && end !== -1) {
+ var lineCol = { line: -1, character: -1 };
+ var sourceMapping = new SourceMapping();
+ sourceMapping.start.emittedColumn = this.emitState.column;
+ sourceMapping.start.emittedLine = this.emitState.line;
+ // REVIEW: check time consumed by this binary search (about two per leaf statement)
+ var lineMap = this.document.lineMap();
+ lineMap.fillLineAndCharacterFromPosition(start, lineCol);
+ sourceMapping.start.sourceColumn = lineCol.character;
+ sourceMapping.start.sourceLine = lineCol.line + 1;
+ lineMap.fillLineAndCharacterFromPosition(end, lineCol);
+ sourceMapping.end.sourceColumn = lineCol.character;
+ sourceMapping.end.sourceLine = lineCol.line + 1;
+
+ Debug.assert(!isNaN(sourceMapping.start.emittedColumn));
+ Debug.assert(!isNaN(sourceMapping.start.emittedLine));
+ Debug.assert(!isNaN(sourceMapping.start.sourceColumn));
+ Debug.assert(!isNaN(sourceMapping.start.sourceLine));
+ Debug.assert(!isNaN(sourceMapping.end.sourceColumn));
+ Debug.assert(!isNaN(sourceMapping.end.sourceLine));
+
+ if (this.sourceMapper.currentNameIndex.length > 0) {
+ sourceMapping.nameIndex = this.sourceMapper.currentNameIndex[this.sourceMapper.currentNameIndex.length - 1];
+ }
+ // Set parent and child relationship
+ var siblings = this.sourceMapper.currentMappings[this.sourceMapper.currentMappings.length - 1];
+ siblings.push(sourceMapping);
+ this.sourceMapper.currentMappings.push(sourceMapping.childMappings);
+ this.sourceMapper.increaseMappingLevel(ast);
+ }
+ }
+
+ private recordSourceMappingEnd(ast: ISyntaxElement) {
+ if (this.sourceMapper && ASTHelpers.isValidAstNode(ast)) {
+ var text = this.text();
+ this.recordSourceMappingSpanEnd(ast, start(ast, text), end(ast, text));
+ }
+ }
+
+ private recordSourceMappingCommentEnd(ast: Comment) {
+ if (this.sourceMapper && ASTHelpers.isValidSpan(ast)) {
+ this.recordSourceMappingSpanEnd(ast, ast.start(), ast.end());
+ }
+ }
+
+ private recordSourceMappingSpanEnd(ast: any, start: number, end: number) {
+ if (this.sourceMapper && ast && start !== -1 && end !== -1) {
+ // Pop source mapping childs
+ this.sourceMapper.currentMappings.pop();
+
+ // Get the last source mapping from sibling list = which is the one we are recording end for
+ var siblings = this.sourceMapper.currentMappings[this.sourceMapper.currentMappings.length - 1];
+ var sourceMapping = siblings[siblings.length - 1];
+
+ sourceMapping.end.emittedColumn = this.emitState.column;
+ sourceMapping.end.emittedLine = this.emitState.line;
+
+ Debug.assert(!isNaN(sourceMapping.end.emittedColumn));
+ Debug.assert(!isNaN(sourceMapping.end.emittedLine));
+
+ this.sourceMapper.decreaseMappingLevel(ast);
+ }
+ }
+
+ // Note: may throw exception.
+ public getOutputFiles(): OutputFile[] {
+ // Output a source mapping. As long as we haven't gotten any errors yet.
+ var result: OutputFile[] = [];
+ if (this.sourceMapper !== null) {
+ this.sourceMapper.emitSourceMapping();
+ result.push(this.sourceMapper.getOutputFile());
+ }
+
+ result.push(this.outfile.getOutputFile());
+ return result;
+ }
+
+ private emitParameterPropertyAndMemberVariableAssignments(): void {
+ // emit any parameter properties first
+ var constructorDecl = getLastConstructor(this.thisClassNode);
+
+ if (constructorDecl) {
+ for (var i = 0, n = constructorDecl.callSignature.parameterList.parameters.length; i < n; i++) {
+ var parameter = constructorDecl.callSignature.parameterList.parameters[i];
+
+ var parameterDecl = this.semanticInfoChain.getDeclForAST(parameter);
+ if (hasFlag(parameterDecl.flags, PullElementFlags.PropertyParameter)) {
+ this.emitIndent();
+ this.recordSourceMappingStart(parameter);
+ this.writeToOutputWithSourceMapRecord("this." + parameter.identifier.text(), parameter.identifier);
+ this.writeToOutput(" = ");
+ this.writeToOutputWithSourceMapRecord(parameter.identifier.text(), parameter.identifier);
+ this.writeLineToOutput(";");
+ this.recordSourceMappingEnd(parameter);
+ }
+ }
+ }
+
+ for (var i = 0, n = this.thisClassNode.classElements.length; i < n; i++) {
+ if (this.thisClassNode.classElements[i].kind() === SyntaxKind.MemberVariableDeclaration) {
+ var varDecl = this.thisClassNode.classElements[i];
+ if (!hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause) {
+ this.emitIndent();
+ this.emitMemberVariableDeclaration(varDecl);
+ this.writeLineToOutput("");
+ }
+ }
+ }
+ }
+
+ private isOnSameLine(pos1: number, pos2: number): boolean {
+ if (pos1 < 0 || pos2 < 0) {
+ // Missing element. Assume it's on the same line as the other element.
+ return true;
+ }
+
+ var lineMap = this.document.lineMap();
+ return lineMap.getLineNumberFromPosition(pos1) === lineMap.getLineNumberFromPosition(pos2);
+ }
+
+ private emitCommaSeparatedList(parent: ISyntaxElement, list: T[], buffer: string, preserveNewLines: boolean): void {
+ if (list === null || list.length === 0) {
+ return;
+ }
+
+ // If the first element isn't on hte same line as the parent node, then we need to
+ // start with a newline.
+ var text = this.text();
+ var startLine = preserveNewLines && !this.isOnSameLine(end(parent, text), end(list[0], text));
+
+ if (preserveNewLines) {
+ // Any elements on a new line will have to be indented.
+ this.indenter.increaseIndent();
+ }
+
+ // If we're starting on a newline, then emit an actual newline. Otherwise write out
+ // the buffer character before hte first element.
+ if (startLine) {
+ this.writeLineToOutput("");
+ }
+ else {
+ this.writeToOutput(buffer);
+ }
+
+ for (var i = 0, n = list.length; i < n; i++) {
+ var emitNode = list[i];
+
+ // Write out the element, emitting an indent if we're on a new line.
+ this.emitJavascript(emitNode, startLine);
+
+ if (i < (n - 1)) {
+ // If the next element start on a different line than this element ended on,
+ // then we want to start on a newline. Emit the comma with a newline.
+ // Otherwise, emit the comma with the space.
+ startLine = preserveNewLines && !this.isOnSameLine(end(emitNode, text), start(list[i + 1], text));
+ if (startLine) {
+ this.writeLineToOutput(",");
+ }
+ else {
+ this.writeToOutput(", ");
+ }
+ }
+ }
+
+ if (preserveNewLines) {
+ // We're done with all the elements. Return the indent back to where it was.
+ this.indenter.decreaseIndent();
+ }
+
+ // If the last element isn't on the same line as the parent, then emit a newline
+ // after the last element and emit our indent so the list's terminator will be
+ // on the right line. Otherwise, emit the buffer string between the last value
+ // and the terminator.
+ if (preserveNewLines && !this.isOnSameLine(end(parent, text), end(list[list.length - 1], text))) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ }
+ else {
+ this.writeToOutput(buffer);
+ }
+ }
+
+ public emitList(list: T[], useNewLineSeparator = true, startInclusive = 0, endExclusive = list.length) {
+ if (list === null) {
+ return;
+ }
+
+ this.emitComments(list, true);
+ var lastEmittedNode: ISyntaxElement = null;
+
+ for (var i = startInclusive; i < endExclusive; i++) {
+ var node = list[i];
+
+ if (this.shouldEmit(node)) {
+ this.emitSpaceBetweenConstructs(lastEmittedNode, node);
+
+ this.emitJavascript(node, true);
+ if (useNewLineSeparator) {
+ this.writeLineToOutput("");
+ }
+
+ lastEmittedNode = node;
+ }
+ }
+
+ this.emitComments(list, false);
+ }
+
+ public emitSeparatedList(list: T[], useNewLineSeparator = true, startInclusive = 0, endExclusive = list.length) {
+ if (list === null) {
+ return;
+ }
+
+ this.emitComments(list, true);
+ var lastEmittedNode: ISyntaxElement = null;
+
+ for (var i = startInclusive; i < endExclusive; i++) {
+ var node = list[i];
+
+ if (this.shouldEmit(node)) {
+ this.emitSpaceBetweenConstructs(lastEmittedNode, node);
+
+ this.emitJavascript(node, true);
+ if (useNewLineSeparator) {
+ this.writeLineToOutput("");
+ }
+
+ lastEmittedNode = node;
+ }
+ }
+
+ this.emitComments(list, false);
+ }
+
+ private isDirectivePrologueElement(node: ISyntaxElement) {
+ if (node.kind() === SyntaxKind.ExpressionStatement) {
+ var exprStatement = node;
+ return exprStatement.expression.kind() === SyntaxKind.StringLiteral;
+ }
+
+ return false;
+ }
+
+ // If these two constructs had more than one line between them originally, then emit at
+ // least one blank line between them.
+ public emitSpaceBetweenConstructs(node1: ISyntaxElement, node2: ISyntaxElement): void {
+ if (node1 === null || node2 === null) {
+ return;
+ }
+
+ var text = this.text();
+ if (start(node1, text) === -1 || end(node1, text) === -1 || start(node2, text) === -1 || end(node2, text) === -1) {
+ return;
+ }
+
+ var lineMap = this.document.lineMap();
+ var node1EndLine = lineMap.getLineNumberFromPosition(end(node1, text));
+ var node2StartLine = lineMap.getLineNumberFromPosition(start(node2, text));
+
+ if ((node2StartLine - node1EndLine) > 1) {
+ this.writeLineToOutput("", /*force:*/ true);
+ }
+ }
+
+ // We consider a sequence of comments to be a detached from an ast if there are no blank lines
+ // between them, and there is a blank line after the last one and the node they're attached
+ // to.
+ private getDetachedComments(element: ISyntaxElement): Comment[] {
+ var text = this.text();
+ var preComments = TypeScript.ASTHelpers.preComments(element, text);
+ if (preComments) {
+ var lineMap = this.document.lineMap();
+
+ var detachedComments: Comment[] = [];
+ var lastComment: Comment = null;
+
+ for (var i = 0, n = preComments.length; i < n; i++) {
+ var comment = preComments[i];
+
+ if (lastComment) {
+ var lastCommentLine = lineMap.getLineNumberFromPosition(lastComment.end());
+ var commentLine = lineMap.getLineNumberFromPosition(comment.start());
+
+ if (commentLine >= lastCommentLine + 2) {
+ // There was a blank line between the last comment and this comment. This
+ // comment is not part of the copyright comments. Return what we have so
+ // far.
+ return detachedComments;
+ }
+ }
+
+ detachedComments.push(comment);
+ lastComment = comment;
+ }
+
+ // All comments look like they could have been part of the copyright header. Make
+ // sure there is at least one blank line between it and the node. If not, it's not
+ // a copyright header.
+ var lastCommentLine = lineMap.getLineNumberFromPosition(ArrayUtilities.last(detachedComments).end());
+ var astLine = lineMap.getLineNumberFromPosition(start(element, text));
+ if (astLine >= lastCommentLine + 2) {
+ return detachedComments;
+ }
+ }
+
+ // No usable copyright comments found.
+ return [];
+ }
+
+ private emitPossibleCopyrightHeaders(script: SourceUnitSyntax): void {
+ this.emitDetachedComments(script.moduleElements);
+ }
+
+ private emitDetachedComments(list: ISyntaxNodeOrToken[]): void {
+ if (list.length > 0) {
+ var firstElement = childAt(list, 0);
+
+ this.detachedCommentsElement = firstElement;
+ this.emitCommentsArray(this.getDetachedComments(this.detachedCommentsElement), /*trailing:*/ false);
+ }
+ }
+
+ public emitScriptElements(sourceUnit: SourceUnitSyntax) {
+ var list = sourceUnit.moduleElements;
+
+ this.emitPossibleCopyrightHeaders(sourceUnit);
+
+ // First, emit all the prologue elements.
+ for (var i = 0, n = list.length; i < n; i++) {
+ var node = list[i];
+
+ if (!this.isDirectivePrologueElement(node)) {
+ break;
+ }
+
+ this.emitJavascript(node, true);
+ this.writeLineToOutput("");
+ }
+
+ // Now emit __extends or a _this capture if necessary.
+ this.emitPrologue(sourceUnit);
+
+ var isExternalModule = this.document.syntaxTree().isExternalModule();
+ var isNonElidedExternalModule = isExternalModule && !ASTHelpers.scriptIsElided(sourceUnit);
+ if (isNonElidedExternalModule) {
+ this.recordSourceMappingStart(sourceUnit);
+
+ if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { // AMD
+ var dependencyList = "[\"require\", \"exports\"";
+ var importList = "require, exports";
+
+ var importAndDependencyList = this.getModuleImportAndDependencyList(sourceUnit);
+ importList += importAndDependencyList.importList;
+ dependencyList += importAndDependencyList.dependencyList + "]";
+
+ this.writeLineToOutput("define(" + dependencyList + "," + " function(" + importList + ") {");
+ }
+ }
+
+ if (isExternalModule) {
+ var temp = this.setContainer(EmitContainer.DynamicModule);
+
+ var svModuleName = this.moduleName;
+ this.moduleName = syntaxTree(sourceUnit).fileName();
+ if (TypeScript.isTSFile(this.moduleName)) {
+ this.moduleName = this.moduleName.substring(0, this.moduleName.length - ".ts".length);
+ }
+
+ // if the external module has an "export =" identifier, we'll
+ // set it in the ExportAssignment emit method
+ this.setExportAssignment(null);
+
+ if(this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) {
+ this.indenter.increaseIndent();
+ }
+
+ var externalModule = this.semanticInfoChain.getDeclForAST(this.document.sourceUnit());
+
+ if (hasFlag(externalModule.flags, PullElementFlags.MustCaptureThis)) {
+ this.writeCaptureThisStatement(sourceUnit);
+ }
+
+ this.pushDecl(externalModule);
+ }
+
+ this.emitList(list, /*useNewLineSeparator:*/ true, /*startInclusive:*/ i, /*endExclusive:*/ n);
+
+ if (isExternalModule) {
+ if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) {
+ this.indenter.decreaseIndent();
+ }
+
+ if (isNonElidedExternalModule) {
+ var exportAssignment = this.getExportAssignment();
+ var exportAssignmentIdentifierText = exportAssignment ? exportAssignment.identifier.text() : null;
+ var exportAssignmentValueSymbol = (externalModule.getSymbol(this.semanticInfoChain)).getExportAssignedValueSymbol();
+
+ if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { // AMD
+ if (exportAssignmentIdentifierText && exportAssignmentValueSymbol && !(exportAssignmentValueSymbol.kind & PullElementKind.SomeTypeReference)) {
+ // indent was decreased for AMD above
+ this.indenter.increaseIndent();
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord("return " + exportAssignmentIdentifierText, exportAssignment);
+ this.writeLineToOutput(";");
+ this.indenter.decreaseIndent();
+ }
+ this.writeToOutput("});");
+ }
+ else if (exportAssignmentIdentifierText && exportAssignmentValueSymbol && !(exportAssignmentValueSymbol.kind & PullElementKind.SomeTypeReference)) {
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord("module.exports = " + exportAssignmentIdentifierText, exportAssignment);
+ this.writeToOutput(";");
+ }
+
+ this.recordSourceMappingEnd(sourceUnit);
+ this.writeLineToOutput("");
+ }
+
+ this.setContainer(temp);
+ this.moduleName = svModuleName;
+ this.popDecl(externalModule);
+ }
+ }
+
+ public emitConstructorStatements(funcDecl: ConstructorDeclarationSyntax) {
+ var list = funcDecl.block.statements;
+
+ if (list === null) {
+ return;
+ }
+
+ this.emitComments(list, true);
+
+ var emitPropertyAssignmentsAfterSuperCall = ASTHelpers.getExtendsHeritageClause(this.thisClassNode.heritageClauses) !== null;
+ var propertyAssignmentIndex = emitPropertyAssignmentsAfterSuperCall ? 1 : 0;
+ var lastEmittedNode: ISyntaxElement = null;
+
+ for (var i = 0, n = list.length; i < n; i++) {
+ // In some circumstances, class property initializers must be emitted immediately after the 'super' constructor
+ // call which, in these cases, must be the first statement in the constructor body
+ if (i === propertyAssignmentIndex) {
+ this.emitParameterPropertyAndMemberVariableAssignments();
+ }
+
+ var node = list[i];
+
+ if (this.shouldEmit(node)) {
+ this.emitSpaceBetweenConstructs(lastEmittedNode, node);
+
+ this.emitJavascript(node, true);
+ this.writeLineToOutput("");
+
+ lastEmittedNode = node;
+ }
+ }
+
+ if (i === propertyAssignmentIndex) {
+ this.emitParameterPropertyAndMemberVariableAssignments();
+ }
+
+ this.emitComments(list, false);
+ }
+
+ // tokenId is the id the preceding token
+ public emitJavascript(ast: ISyntaxElement, startLine: boolean) {
+ if (ast === null) {
+ return;
+ }
+
+ if (startLine &&
+ this.indenter.indentAmt > 0) {
+
+ this.emitIndent();
+ }
+
+ this.emit(ast);
+ }
+
+ public emitAccessorMemberDeclaration(funcDecl: ISyntaxElement, name: ISyntaxToken, className: string, isProto: boolean) {
+ if (funcDecl.kind() !== SyntaxKind.GetAccessor) {
+ var accessorSymbol = PullHelpers.getAccessorSymbol(funcDecl, this.semanticInfoChain);
+ if (accessorSymbol.getGetter()) {
+ return;
+ }
+ }
+
+ this.emitIndent();
+ this.recordSourceMappingStart(funcDecl);
+
+ this.writeToOutput("Object.defineProperty(" + className);
+ if (isProto) {
+ this.writeToOutput(".prototype, ");
+ }
+ else {
+ this.writeToOutput(", ");
+ }
+
+ var functionName = name.text();
+ if (isQuoted(functionName)) {
+ this.writeToOutput(functionName);
+ }
+ else {
+ this.writeToOutput('"' + functionName + '"');
+ }
+
+ this.writeLineToOutput(", {");
+
+ this.indenter.increaseIndent();
+
+ var accessors = PullHelpers.getGetterAndSetterFunction(funcDecl, this.semanticInfoChain);
+ if (accessors.getter) {
+ this.emitIndent();
+ this.recordSourceMappingStart(accessors.getter);
+ this.emitComments(accessors.getter, true);
+ this.writeToOutput("get: ");
+ this.emitAccessorBody(accessors.getter, accessors.getter.callSignature.parameterList, accessors.getter.block);
+ this.writeLineToOutput(",");
+ }
+
+ if (accessors.setter) {
+ this.emitIndent();
+ this.recordSourceMappingStart(accessors.setter);
+ this.emitComments(accessors.setter, true);
+ this.writeToOutput("set: ");
+ this.emitAccessorBody(accessors.setter, accessors.setter.callSignature.parameterList, accessors.setter.block);
+ this.writeLineToOutput(",");
+ }
+
+ this.emitIndent();
+ this.writeLineToOutput("enumerable: true,");
+ this.emitIndent();
+ this.writeLineToOutput("configurable: true");
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeLineToOutput("});");
+ this.recordSourceMappingEnd(funcDecl);
+ }
+
+ private emitAccessorBody(funcDecl: ISyntaxElement, parameterList: ParameterListSyntax, block: BlockSyntax): void {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToOutput("function ");
+ this.emitParameterList(parameterList);
+
+ var parameters = parameterList.parameters;
+ this.emitFunctionBodyStatements(null, funcDecl, parameters, block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(funcDecl);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(funcDecl);
+ this.popDecl(pullDecl);
+ }
+
+ public emitClass(classDecl: ClassDeclarationSyntax) {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(classDecl);
+ this.pushDecl(pullDecl);
+
+ var svClassNode = this.thisClassNode;
+ this.thisClassNode = classDecl;
+ var className = classDecl.identifier.text();
+ this.emitComments(classDecl, true);
+ var temp = this.setContainer(EmitContainer.Class);
+
+ this.recordSourceMappingStart(classDecl);
+ this.writeToOutput("var " + className);
+
+ var hasBaseClass = ASTHelpers.getExtendsHeritageClause(classDecl.heritageClauses) !== null;
+ var baseTypeReference: ISyntaxElement = null;
+ var varDecl: VariableDeclaratorSyntax = null;
+
+ if (hasBaseClass) {
+ this.writeLineToOutput(" = (function (_super) {");
+ }
+ else {
+ this.writeLineToOutput(" = (function () {");
+ }
+
+ this.recordSourceMappingNameStart(className);
+ this.indenter.increaseIndent();
+
+ if (hasBaseClass) {
+ baseTypeReference = ASTHelpers.getExtendsHeritageClause(classDecl.heritageClauses).typeNames[0];
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord("__extends(" + className + ", _super)", baseTypeReference);
+ this.writeLineToOutput(";");
+ }
+
+ this.emitIndent();
+
+ var constrDecl = getLastConstructor(classDecl);
+
+ // output constructor
+ if (constrDecl) {
+ // declared constructor
+ this.emit(constrDecl);
+ this.writeLineToOutput("");
+ }
+ else {
+ this.recordSourceMappingStart(classDecl);
+ // default constructor
+ this.indenter.increaseIndent();
+ this.writeLineToOutput("function " + classDecl.identifier.text() + "() {");
+ this.recordSourceMappingNameStart("constructor");
+ if (hasBaseClass) {
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord("_super.apply(this, arguments)", baseTypeReference);
+ this.writeLineToOutput(";");
+ }
+
+ if (this.shouldCaptureThis(classDecl)) {
+ this.writeCaptureThisStatement(classDecl);
+ }
+
+ this.emitParameterPropertyAndMemberVariableAssignments();
+
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeToken(classDecl.closeBraceToken);
+ this.writeLineToOutput("");
+
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingEnd(classDecl);
+ }
+
+ this.emitClassMembers(classDecl);
+
+ this.emitIndent();
+ this.writeToOutputWithSourceMapRecord("return " + className + ";", classDecl.closeBraceToken);
+ this.writeLineToOutput("");
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeToken(classDecl.closeBraceToken);
+ this.recordSourceMappingNameEnd();
+ this.recordSourceMappingStart(classDecl);
+ this.writeToOutput(")(");
+ if (hasBaseClass) {
+ this.emitJavascript(baseTypeReference, /*startLine:*/ false);
+ }
+ this.writeToOutput(");");
+ this.recordSourceMappingEnd(classDecl);
+
+ if ((temp === EmitContainer.Module || temp === EmitContainer.DynamicModule) && hasFlag(pullDecl.flags, PullElementFlags.Exported)) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ var modName = temp === EmitContainer.Module ? this.moduleName : "exports";
+ this.writeToOutputWithSourceMapRecord(modName + "." + className + " = " + className + ";", classDecl);
+ }
+
+ this.recordSourceMappingEnd(classDecl);
+ this.emitComments(classDecl, false);
+ this.setContainer(temp);
+ this.thisClassNode = svClassNode;
+
+ this.popDecl(pullDecl);
+ }
+
+ private emitClassMembers(classDecl: ClassDeclarationSyntax): void {
+ // First, emit all the functions.
+ var lastEmittedMember: ISyntaxElement = null;
+
+ for (var i = 0, n = classDecl.classElements.length; i < n; i++) {
+ var memberDecl = classDecl.classElements[i];
+
+ if (memberDecl.kind() === SyntaxKind.GetAccessor) {
+ this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl);
+ var getter = memberDecl;
+ this.emitAccessorMemberDeclaration(getter, getter.propertyName, classDecl.identifier.text(),
+ !hasModifier(getter.modifiers, PullElementFlags.Static));
+ lastEmittedMember = memberDecl;
+ }
+ else if (memberDecl.kind() === SyntaxKind.SetAccessor) {
+ this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl);
+ var setter = memberDecl;
+ this.emitAccessorMemberDeclaration(setter, setter.propertyName, classDecl.identifier.text(),
+ !hasModifier(setter.modifiers, PullElementFlags.Static));
+ lastEmittedMember = memberDecl;
+ }
+ else if (memberDecl.kind() === SyntaxKind.MemberFunctionDeclaration) {
+
+ var memberFunction = memberDecl;
+
+ if (memberFunction.block) {
+ this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl);
+
+ this.emitClassMemberFunctionDeclaration(classDecl, memberFunction);
+ lastEmittedMember = memberDecl;
+ }
+ }
+ }
+
+ // Now emit all the statics.
+ for (var i = 0, n = classDecl.classElements.length; i < n; i++) {
+ var memberDecl = classDecl.classElements[i];
+
+ if (memberDecl.kind() === SyntaxKind.MemberVariableDeclaration) {
+ var varDecl = memberDecl;
+
+ if (hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause) {
+ this.emitSpaceBetweenConstructs(lastEmittedMember, varDecl);
+
+ this.emitIndent();
+ this.recordSourceMappingStart(varDecl);
+
+ this.emitComments(varDecl, true);
+ var varDeclName = varDecl.variableDeclarator.propertyName.text();
+ if (isQuoted(varDeclName) || varDecl.variableDeclarator.propertyName.kind() !== SyntaxKind.IdentifierName) {
+ this.writeToOutput(classDecl.identifier.text() + "[" + varDeclName + "]");
+ }
+ else {
+ this.writeToOutput(classDecl.identifier.text() + "." + varDeclName);
+ }
+
+ this.emit(varDecl.variableDeclarator.equalsValueClause);
+
+ this.recordSourceMappingEnd(varDecl);
+ this.writeLineToOutput(";");
+
+ lastEmittedMember = varDecl;
+ }
+ }
+ }
+ }
+
+ private emitClassMemberFunctionDeclaration(classDecl: ClassDeclarationSyntax, funcDecl: MemberFunctionDeclarationSyntax): void {
+ this.emitIndent();
+ this.recordSourceMappingStart(funcDecl);
+ this.emitComments(funcDecl, true);
+ var functionName = funcDecl.propertyName.text();
+
+ this.writeToOutput(classDecl.identifier.text());
+
+ if (!hasModifier(funcDecl.modifiers, PullElementFlags.Static)) {
+ this.writeToOutput(".prototype");
+ }
+
+ if (isQuoted(functionName) || funcDecl.propertyName.kind() !== SyntaxKind.IdentifierName) {
+ this.writeToOutput("[" + functionName + "] = ");
+ }
+ else {
+ this.writeToOutput("." + functionName + " = ");
+ }
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(funcDecl);
+ this.writeToOutput("function ");
+
+ this.emitParameterList(funcDecl.callSignature.parameterList);
+
+ var parameters = funcDecl.callSignature.parameterList.parameters;
+ this.emitFunctionBodyStatements(funcDecl.propertyName.text(), funcDecl, parameters, funcDecl.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(funcDecl);
+
+ this.emitComments(funcDecl, false);
+
+ this.recordSourceMappingEnd(funcDecl);
+ this.popDecl(pullDecl);
+
+ this.writeLineToOutput(";");
+ }
+
+ private requiresExtendsBlock(moduleElements: IModuleElementSyntax[]): boolean {
+ for (var i = 0, n = moduleElements.length; i < n; i++) {
+ var moduleElement = moduleElements[i];
+
+ if (moduleElement.kind() === SyntaxKind.ModuleDeclaration) {
+ var moduleAST = moduleElement;
+
+ if (!hasModifier(moduleAST.modifiers, PullElementFlags.Ambient) && this.requiresExtendsBlock(moduleAST.moduleElements)) {
+ return true;
+ }
+ }
+ else if (moduleElement.kind() === SyntaxKind.ClassDeclaration) {
+ var classDeclaration = moduleElement;
+
+ if (!hasModifier(classDeclaration.modifiers, PullElementFlags.Ambient) && ASTHelpers.getExtendsHeritageClause(classDeclaration.heritageClauses) !== null) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public emitPrologue(sourceUnit: SourceUnitSyntax) {
+ if (!this.extendsPrologueEmitted) {
+ if (this.requiresExtendsBlock(sourceUnit.moduleElements)) {
+ this.extendsPrologueEmitted = true;
+ this.writeLineToOutput("var __extends = this.__extends || function (d, b) {");
+ this.writeLineToOutput(" for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];");
+ this.writeLineToOutput(" function __() { this.constructor = d; }");
+ this.writeLineToOutput(" __.prototype = b.prototype;");
+ this.writeLineToOutput(" d.prototype = new __();");
+ this.writeLineToOutput("};");
+ }
+ }
+
+ if (!this.globalThisCapturePrologueEmitted) {
+ if (this.shouldCaptureThis(sourceUnit)) {
+ this.globalThisCapturePrologueEmitted = true;
+ this.writeLineToOutput(this.captureThisStmtString);
+ }
+ }
+ }
+
+ public emitThis() {
+ if (!this.inWithBlock && this.inArrowFunction) {
+ this.writeToOutput("_this");
+ }
+ else {
+ this.writeToOutput("this");
+ }
+ }
+
+ public emitBlockOrStatement(node: ISyntaxElement): void {
+ if (node.kind() === SyntaxKind.Block) {
+ this.emit(node);
+ }
+ else {
+ this.writeLineToOutput("");
+ this.indenter.increaseIndent();
+ this.emitJavascript(node, true);
+ this.indenter.decreaseIndent();
+ }
+ }
+
+ public emitLiteralExpression(expression: ISyntaxToken): void {
+ switch (expression.kind()) {
+ case SyntaxKind.NullKeyword:
+ this.writeToken(expression);
+ break;
+ case SyntaxKind.FalseKeyword:
+ this.writeToken(expression);
+ break;
+ case SyntaxKind.TrueKeyword:
+ this.writeToken(expression);
+ break;
+ default:
+ throw Errors.abstract();
+ }
+ }
+
+ public emitThisExpression(expression: ISyntaxToken): void {
+ if (!this.inWithBlock && this.inArrowFunction) {
+ this.writeToOutputWithSourceMapRecord("_this", expression);
+ }
+ else {
+ this.writeToken(expression);
+ }
+ }
+
+ public emitSuperExpression(expression: ISyntaxToken): void {
+ if (PullHelpers.isInStaticMemberContext(expression, this.semanticInfoChain)) {
+ this.writeToOutputWithSourceMapRecord("_super", expression);
+ }
+ else {
+ this.writeToOutputWithSourceMapRecord("_super.prototype", expression);
+ }
+ }
+
+ private hasTrailingComment(token: ISyntaxToken) {
+ return token.hasTrailingTrivia() && token.trailingTrivia().hasComment();
+ }
+
+ public emitParenthesizedExpression(parenthesizedExpression: ParenthesizedExpressionSyntax): void {
+ var omitParentheses = false;
+
+ if (parenthesizedExpression.expression.kind() === SyntaxKind.CastExpression && !this.hasTrailingComment(parenthesizedExpression.openParenToken)) {
+ var castedExpression = (parenthesizedExpression.expression).expression;
+
+ // Make sure we consider all nested cast expressions, e.g.:
+ // (-A).x;
+ while (castedExpression.kind() == SyntaxKind.CastExpression) {
+ castedExpression = (castedExpression).expression;
+ }
+
+ // We have an expression of the form: (SubExpr)
+ // Emitting this as (SubExpr) is really not desirable. Just emit the subexpr as is.
+ // We cannot generalize this rule however, as omitting the parentheses could cause change in the semantics of the generated
+ // code if the casted expression has a lower precedence than the rest of the expression, e.g.:
+ // (new A).foo should be emitted as (new A).foo and not new A.foo
+ // (typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString()
+ // (function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} ()
+ // Parenthesis can be safelly removed from:
+ // Literals
+ // MemberAccessExpressions
+ // ElementAccessExpressions
+ // InvocationExpression, only if they are not part of an object creation (new) expression; e.g.:
+ // new (A()) removing the parentheses would result in calling A as a constructor, instead of calling the
+ // result of the function invocation A() as a constructor
+ switch (castedExpression.kind()) {
+ case SyntaxKind.ParenthesizedExpression:
+ case SyntaxKind.IdentifierName:
+ case SyntaxKind.NullKeyword:
+ case SyntaxKind.ThisKeyword:
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.NumericLiteral:
+ case SyntaxKind.RegularExpressionLiteral:
+ case SyntaxKind.TrueKeyword:
+ case SyntaxKind.FalseKeyword:
+ case SyntaxKind.ArrayLiteralExpression:
+ case SyntaxKind.ObjectLiteralExpression:
+ case SyntaxKind.MemberAccessExpression:
+ case SyntaxKind.ElementAccessExpression:
+ omitParentheses = true;
+ break;
+
+ case SyntaxKind.InvocationExpression:
+ if (parenthesizedExpression.parent.kind() !== SyntaxKind.ObjectCreationExpression) {
+ omitParentheses = true;
+ }
+
+ break;
+ }
+ }
+
+ if (omitParentheses) {
+ this.emit(parenthesizedExpression.expression);
+ }
+ else {
+ this.recordSourceMappingStart(parenthesizedExpression);
+ this.writeToken(parenthesizedExpression.openParenToken);
+ this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(parenthesizedExpression.openParenToken, this.text()), /*trailing:*/ false);
+ this.emit(parenthesizedExpression.expression);
+ this.writeToken(parenthesizedExpression.closeParenToken);
+ this.recordSourceMappingEnd(parenthesizedExpression);
+ }
+ }
+
+ public emitCastExpression(expression: CastExpressionSyntax): void {
+ this.emit(expression.expression);
+ }
+
+ public emitPrefixUnaryExpression(expression: PrefixUnaryExpressionSyntax): void {
+ var nodeType = expression.kind();
+
+ this.recordSourceMappingStart(expression);
+ switch (nodeType) {
+ case SyntaxKind.LogicalNotExpression:
+ this.writeToken(expression.operatorToken);
+ this.emit(expression.operand);
+ break;
+ case SyntaxKind.BitwiseNotExpression:
+ this.writeToken(expression.operatorToken);
+ this.emit(expression.operand);
+ break;
+ case SyntaxKind.NegateExpression:
+ this.writeToken(expression.operatorToken);
+ if (expression.operand.kind() === SyntaxKind.NegateExpression || expression.operand.kind() === SyntaxKind.PreDecrementExpression) {
+ this.writeToOutput(" ");
+ }
+ this.emit(expression.operand);
+ break;
+ case SyntaxKind.PlusExpression:
+ this.writeToken(expression.operatorToken);
+ if (expression.operand.kind() === SyntaxKind.PlusExpression || expression.operand.kind() === SyntaxKind.PreIncrementExpression) {
+ this.writeToOutput(" ");
+ }
+ this.emit(expression.operand);
+ break;
+ case SyntaxKind.PreIncrementExpression:
+ this.writeToOutputWithSourceMapRecord("++", expression.operatorToken);
+ this.emit(expression.operand);
+ break;
+ case SyntaxKind.PreDecrementExpression:
+ this.writeToOutputWithSourceMapRecord("--", expression.operatorToken);
+ this.emit(expression.operand);
+ break;
+ default:
+ throw Errors.abstract();
+ }
+
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitPostfixUnaryExpression(expression: PostfixUnaryExpressionSyntax): void {
+ var nodeType = expression.kind();
+
+ this.recordSourceMappingStart(expression);
+ switch (nodeType) {
+ case SyntaxKind.PostIncrementExpression:
+ this.emit(expression.operand);
+ this.writeToOutputWithSourceMapRecord("++", expression.operatorToken);
+ break;
+ case SyntaxKind.PostDecrementExpression:
+ this.emit(expression.operand);
+ this.writeToOutputWithSourceMapRecord("--", expression.operatorToken);
+ break;
+ default:
+ throw Errors.abstract();
+ }
+
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitTypeOfExpression(expression: TypeOfExpressionSyntax): void {
+ this.recordSourceMappingStart(expression);
+ this.writeToken(expression.typeOfKeyword);
+ this.writeToOutput(" ");
+ this.emit(expression.expression);
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitDeleteExpression(expression: DeleteExpressionSyntax): void {
+ this.recordSourceMappingStart(expression);
+ this.writeToken(expression.deleteKeyword);
+ this.writeToOutput(" ");
+ this.emit(expression.expression);
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitVoidExpression(expression: VoidExpressionSyntax): void {
+ this.recordSourceMappingStart(expression);
+ this.writeToken(expression.voidKeyword);
+ this.writeToOutput(" ");
+ this.emit(expression.expression);
+ this.recordSourceMappingEnd(expression);
+ }
+
+ private canEmitDottedNameMemberAccessExpression(expression: MemberAccessExpressionSyntax) {
+ var memberExpressionNodeType = expression.expression.kind();
+
+ // If the memberAccess is of Name or another member access, we could potentially emit the symbol using the this memberAccessSymol
+ if (memberExpressionNodeType === SyntaxKind.IdentifierName || memberExpressionNodeType == SyntaxKind.MemberAccessExpression) {
+ var memberAccessSymbol = this.getSymbolForEmit(expression).symbol;
+ var memberAccessExpressionSymbol = this.getSymbolForEmit(expression.expression).symbol;
+ if (memberAccessSymbol && memberAccessExpressionSymbol // We have symbols resolved for this expression and access
+ && !this.semanticInfoChain.getAliasSymbolForAST(expression.expression) // The access is not off alias
+ && (PullHelpers.symbolIsModule(memberAccessExpressionSymbol) || memberAccessExpressionSymbol.kind === PullElementKind.Enum ||
+ memberAccessExpressionSymbol.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum))) { // container is module
+
+ // If the memberAccess is in the context of the container, we could use the symbol to emit this expression
+ var memberAccessSymbolKind = memberAccessSymbol.kind;
+ if (memberAccessSymbolKind === PullElementKind.Property
+ || memberAccessSymbolKind === PullElementKind.EnumMember
+ || (memberAccessSymbol.anyDeclHasFlag(PullElementFlags.Exported) && memberAccessSymbolKind === PullElementKind.Variable && !memberAccessSymbol.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum))
+ || ((memberAccessSymbol.anyDeclHasFlag(PullElementFlags.Exported) && !this.symbolIsUsedInItsEnclosingContainer(memberAccessSymbol)))) {
+
+ // If the expression is member access, we need to verify it as well
+ if (memberExpressionNodeType === SyntaxKind.MemberAccessExpression) {
+ return this.canEmitDottedNameMemberAccessExpression(expression.expression);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Emit the member access expression using the declPath
+ private emitDottedNameMemberAccessExpression(expression: MemberAccessExpressionSyntax) {
+ this.recordSourceMappingStart(expression);
+ if (expression.expression.kind() === SyntaxKind.MemberAccessExpression) {
+ // Emit the dotted name access expression
+ this.emitDottedNameMemberAccessExpressionRecurse(expression.expression);
+ }
+ else { // Name
+ this.emitName(expression.expression, /*addThis*/ true);
+ }
+
+ this.writeToken(expression.dotToken);
+ this.emitName(expression.name, /*addThis*/ false);
+
+ this.recordSourceMappingEnd(expression);
+ }
+
+ // Set the right indices for the recursive member access expression before emitting it using the decl path
+ private emitDottedNameMemberAccessExpressionRecurse(expression: MemberAccessExpressionSyntax) {
+ this.emitComments(expression, true);
+ this.emitDottedNameMemberAccessExpression(expression);
+ this.emitComments(expression, false);
+ }
+
+ public emitMemberAccessExpression(expression: MemberAccessExpressionSyntax): void {
+ if (!this.tryEmitConstant(expression)) {
+ // If the expression is dotted name of the modules, emit it using decl path so the name could be resolved correctly.
+ if (this.canEmitDottedNameMemberAccessExpression(expression)) {
+ this.emitDottedNameMemberAccessExpression(expression);
+ }
+ else {
+ this.recordSourceMappingStart(expression);
+ this.emit(expression.expression);
+ this.writeToken(expression.dotToken);
+ this.emitName(expression.name, false);
+ this.recordSourceMappingEnd(expression);
+ }
+ }
+ }
+
+ public emitQualifiedName(name: QualifiedNameSyntax): void {
+ this.recordSourceMappingStart(name);
+
+ this.emit(name.left);
+ this.writeToken(name.dotToken);
+ this.emitName(name.right, false);
+
+ this.recordSourceMappingEnd(name);
+ }
+
+ public emitBinaryExpression(expression: BinaryExpressionSyntax): void {
+ this.recordSourceMappingStart(expression);
+ switch (expression.kind()) {
+ case SyntaxKind.CommaExpression:
+ this.emit(expression.left);
+ this.writeToken(expression.operatorToken);
+ this.writeToOutput(" ");
+ this.emit(expression.right);
+ break;
+ default:
+ {
+ this.emit(expression.left);
+ var binOp = SyntaxFacts.getText(SyntaxFacts.getOperatorTokenFromBinaryExpression(expression.kind()));
+ this.writeToOutput(" ");
+ this.writeToOutputWithSourceMapRecord(binOp, expression.operatorToken);
+ this.writeToOutput(" ");
+ this.emit(expression.right);
+ }
+ }
+ this.recordSourceMappingEnd(expression);
+ }
+
+ public emitSimplePropertyAssignment(property: SimplePropertyAssignmentSyntax): void {
+ this.recordSourceMappingStart(property);
+ this.emit(property.propertyName);
+
+ this.writeToken(property.colonToken);
+ this.writeToOutput(" ");
+ this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(property.colonToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true);
+
+ this.emit(property.expression);
+ this.recordSourceMappingEnd(property);
+ }
+
+ public emitFunctionPropertyAssignment(funcProp: FunctionPropertyAssignmentSyntax): void {
+ this.recordSourceMappingStart(funcProp);
+
+ this.emit(funcProp.propertyName);
+ this.writeToOutput(": ");
+
+ var pullFunctionDecl = this.semanticInfoChain.getDeclForAST(funcProp);
+
+ var savedInArrowFunction = this.inArrowFunction;
+ this.inArrowFunction = false;
+
+ var temp = this.setContainer(EmitContainer.Function);
+ var funcName = funcProp.propertyName;
+
+ var pullDecl = this.semanticInfoChain.getDeclForAST(funcProp);
+ this.pushDecl(pullDecl);
+
+ this.recordSourceMappingStart(funcProp);
+ this.writeToOutput("function ");
+
+ //this.recordSourceMappingStart(funcProp.propertyName);
+ //this.writeToOutput(funcProp.propertyName.actualText);
+ //this.recordSourceMappingEnd(funcProp.propertyName);
+
+ this.emitParameterList(funcProp.callSignature.parameterList);
+
+ this.emitFunctionBodyStatements(funcProp.propertyName.text(), funcProp,
+ funcProp.callSignature.parameterList.parameters, funcProp.block, /*bodyExpression:*/ null);
+
+ this.recordSourceMappingEnd(funcProp);
+
+ // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it
+ this.recordSourceMappingEnd(funcProp);
+
+ this.emitComments(funcProp, false);
+
+ this.popDecl(pullDecl);
+
+ this.setContainer(temp);
+ this.inArrowFunction = savedInArrowFunction;
+ }
+
+ public emitConditionalExpression(expression: ConditionalExpressionSyntax): void {
+ this.emit(expression.condition);
+ this.writeToOutput(" ");
+ this.writeToken(expression.questionToken);
+ this.writeToOutput(" ");
+ this.emit(expression.whenTrue);
+ this.writeToOutput(" ");
+ this.writeToken(expression.colonToken);
+ this.writeToOutput(" ");
+ this.emit(expression.whenFalse);
+ }
+
+ public emitThrowStatement(statement: ThrowStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.throwKeyword);
+ this.writeToOutput(" ");
+ this.emit(statement.expression);
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitExpressionStatement(statement: ExpressionStatementSyntax): void {
+ var isArrowExpression = statement.expression.kind() === SyntaxKind.SimpleArrowFunctionExpression || statement.expression.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression;
+
+ this.recordSourceMappingStart(statement);
+ if (isArrowExpression) {
+ this.writeToOutput("(");
+ }
+
+ this.emit(statement.expression);
+
+ if (isArrowExpression) {
+ this.writeToOutput(")");
+ }
+
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitLabeledStatement(statement: LabeledStatementSyntax): void {
+ this.writeToOutputWithSourceMapRecord(statement.identifier.text(), statement.identifier);
+ this.writeToken(statement.colonToken);
+ this.writeLineToOutput("");
+ this.emitJavascript(statement.statement, /*startLine:*/ true);
+ }
+
+ public emitBlock(block: BlockSyntax): void {
+ this.recordSourceMappingStart(block);
+ this.writeLineToOutput(" {");
+ this.indenter.increaseIndent();
+ if (block.statements) {
+ this.emitList(block.statements);
+ }
+ this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(block.closeBraceToken, this.text()), /*trailing:*/ false);
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeToken(block.closeBraceToken);
+ this.recordSourceMappingEnd(block);
+ }
+
+ public emitBreakStatement(jump: BreakStatementSyntax): void {
+ this.recordSourceMappingStart(jump);
+ this.writeToken(jump.breakKeyword);
+
+ if (jump.identifier) {
+ this.writeToOutput(" ");
+ this.writeToOutputWithSourceMapRecord(jump.identifier.text(), jump.identifier);
+ }
+
+ this.writeToOutputWithSourceMapRecord(";", jump.semicolonToken);
+ this.recordSourceMappingEnd(jump);
+ }
+
+ public emitContinueStatement(jump: ContinueStatementSyntax): void {
+ this.recordSourceMappingStart(jump);
+ this.writeToken(jump.continueKeyword);
+
+ if (jump.identifier) {
+ this.writeToOutput(" ");
+ this.writeToOutputWithSourceMapRecord(jump.identifier.text(), jump.identifier);
+ }
+
+ this.writeToOutputWithSourceMapRecord(";", jump.semicolonToken);
+ this.recordSourceMappingEnd(jump);
+ }
+
+ public emitWhileStatement(statement: WhileStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.whileKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+ this.emit(statement.condition);
+ this.writeToken(statement.closeParenToken);
+ this.emitBlockOrStatement(statement.statement);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitDoStatement(statement: DoStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.doKeyword);
+ this.emitBlockOrStatement(statement.statement);
+ this.writeToOutput(" ");
+ this.writeToken(statement.whileKeyword);
+ this.writeToken(statement.openParenToken);
+ this.emit(statement.condition);
+ this.writeToken(statement.closeParenToken);
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitIfStatement(statement: IfStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.ifKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+ this.emit(statement.condition);
+ this.writeToken(statement.closeParenToken);
+
+ this.emitBlockOrStatement(statement.statement);
+
+ if (statement.elseClause) {
+ if (statement.statement.kind() !== SyntaxKind.Block) {
+ this.writeLineToOutput("");
+ this.emitIndent();
+ }
+ else {
+ this.writeToOutput(" ");
+ }
+
+ this.emit(statement.elseClause);
+ }
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitElseClause(elseClause: ElseClauseSyntax): void {
+ this.writeToken(elseClause.elseKeyword);
+ if (elseClause.statement.kind() === SyntaxKind.IfStatement) {
+ this.writeToOutput(" ");
+ this.emit(elseClause.statement);
+ }
+ else {
+ this.emitBlockOrStatement(elseClause.statement);
+ }
+ }
+
+ public emitReturnStatement(statement: ReturnStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.returnKeyword);
+ if (statement.expression) {
+ this.writeToOutput(" ");
+ this.emit(statement.expression);
+ }
+
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitForInStatement(statement: ForInStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.forKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+
+ if (statement.left) {
+ this.emit(statement.left);
+ }
+ else {
+ this.emit(statement.variableDeclaration);
+ }
+ this.writeToOutput(" ");
+ this.writeToken(statement.inKeyword);
+ this.writeToOutput(" ");
+ this.emit(statement.expression);
+ this.writeToken(statement.closeParenToken);
+ this.emitBlockOrStatement(statement.statement);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitForStatement(statement: ForStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.forKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+
+ if (statement.variableDeclaration) {
+ this.emit(statement.variableDeclaration);
+ }
+ else if (statement.initializer) {
+ this.emit(statement.initializer);
+ }
+
+ this.writeToken(statement.firstSemicolonToken);
+ this.writeToOutput(" ");
+ this.emitJavascript(statement.condition, false);
+ this.writeToken(statement.secondSemicolonToken);
+ if (statement.incrementor) {
+ this.writeToOutput(" ");
+ this.emitJavascript(statement.incrementor, false);
+ }
+ this.writeToken(statement.closeParenToken);
+ this.emitBlockOrStatement(statement.statement);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitWithStatement(statement: WithStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.withKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+ if (statement.condition) {
+ this.emit(statement.condition);
+ }
+
+ this.writeToken(statement.closeParenToken);
+ var prevInWithBlock = this.inWithBlock;
+ this.inWithBlock = true;
+ this.emitBlockOrStatement(statement.statement);
+ this.inWithBlock = prevInWithBlock;
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitSwitchStatement(statement: SwitchStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.switchKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(statement.openParenToken);
+ this.emit(statement.expression);
+ this.writeToken(statement.closeParenToken);
+ this.writeLineToOutput(" {");
+ this.indenter.increaseIndent();
+ this.emitList(statement.switchClauses, /*useNewLineSeparator:*/ false);
+ this.indenter.decreaseIndent();
+ this.emitIndent();
+ this.writeToken(statement.closeBraceToken);
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitCaseSwitchClause(clause: CaseSwitchClauseSyntax): void {
+ this.recordSourceMappingStart(clause);
+ this.writeToken(clause.caseKeyword);
+ this.writeToOutput(" ");
+ this.emit(clause.expression);
+ this.writeToken(clause.colonToken);
+
+ this.emitSwitchClauseBody(clause.colonToken, clause.statements);
+ this.recordSourceMappingEnd(clause);
+ }
+
+ private emitSwitchClauseBody(colonToken: ISyntaxToken, body: IStatementSyntax[]): void {
+ var text = this.text();
+ if (body.length === 1 && childAt(body, 0).kind() === SyntaxKind.Block) {
+ // The case statement was written with curly braces, so emit it with the appropriate formatting
+ this.emit(childAt(body, 0));
+ this.writeLineToOutput("");
+ }
+ else if (body.length === 1 && this.isOnSameLine(end(colonToken, text), start(body[0], text))) {
+ this.writeToOutput(" ");
+ this.emit(childAt(body, 0));
+ this.writeLineToOutput("");
+ }
+ else {
+ // No curly braces. Format in the expected way
+ this.writeLineToOutput("");
+ this.indenter.increaseIndent();
+ this.emit(body);
+ this.indenter.decreaseIndent();
+ }
+ }
+
+ public emitDefaultSwitchClause(clause: DefaultSwitchClauseSyntax): void {
+ this.recordSourceMappingStart(clause);
+ this.writeToken(clause.defaultKeyword);
+ this.writeToken(clause.colonToken);
+
+ this.emitSwitchClauseBody(clause.colonToken, clause.statements);
+ this.recordSourceMappingEnd(clause);
+ }
+
+ public emitTryStatement(statement: TryStatementSyntax): void {
+ this.recordSourceMappingStart(statement);
+ this.writeToken(statement.tryKeyword);
+ this.writeToOutput(" ");
+ this.emit(statement.block);
+ this.emitJavascript(statement.catchClause, false);
+
+ if (statement.finallyClause) {
+ this.emit(statement.finallyClause);
+ }
+ this.recordSourceMappingEnd(statement);
+ }
+
+ public emitCatchClause(clause: CatchClauseSyntax): void {
+ this.writeToOutput(" ");
+ this.recordSourceMappingStart(clause);
+ this.writeToken(clause.catchKeyword);
+ this.writeToOutput(" ");
+ this.writeToken(clause.openParenToken);
+ this.emit(clause.identifier);
+ this.writeToken(clause.closeParenToken);
+ this.emit(clause.block);
+ this.recordSourceMappingEnd(clause);
+ }
+
+ public emitFinallyClause(clause: FinallyClauseSyntax): void {
+ this.writeToOutput(" ");
+ this.writeToken(clause.finallyKeyword);
+ this.emit(clause.block);
+ }
+
+ public emitDebuggerStatement(statement: DebuggerStatementSyntax): void {
+ this.writeToken(statement.debuggerKeyword);
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ }
+
+ public emitNumericLiteral(literal: ISyntaxToken): void {
+ this.writeToOutputWithSourceMapRecord(literal.text(), literal);
+ }
+
+ public emitRegularExpressionLiteral(literal: ISyntaxToken): void {
+ this.writeToOutputWithSourceMapRecord(literal.text(), literal);
+ }
+
+ public emitStringLiteral(literal: ISyntaxToken): void {
+ this.writeToOutputWithSourceMapRecord(literal.text(), literal);
+ }
+
+ public emitEqualsValueClause(clause: EqualsValueClauseSyntax): void {
+ this.writeToOutput(" ");
+ this.writeToken(clause.equalsToken);
+ this.writeToOutput(" ");
+ this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(clause.equalsToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true);
+
+ this.emit(clause.value);
+ }
+
+ private emitParameter(parameter: ParameterSyntax): void {
+ this.writeToOutputWithSourceMapRecord(parameter.identifier.text(), parameter);
+ }
+
+ public emitConstructorDeclaration(declaration: ConstructorDeclarationSyntax): void {
+ if (declaration.block) {
+ this.emitConstructor(declaration);
+ }
+ else {
+ this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ public shouldEmitFunctionDeclaration(declaration: FunctionDeclarationSyntax): boolean {
+ return ASTHelpers.preComments(declaration, this.text()) !== null || (!hasModifier(declaration.modifiers, PullElementFlags.Ambient) && declaration.block !== null);
+ }
+
+ public emitFunctionDeclaration(declaration: FunctionDeclarationSyntax): void {
+ if (!hasModifier(declaration.modifiers, PullElementFlags.Ambient) && declaration.block !== null) {
+ this.emitFunction(declaration);
+ }
+ else {
+ this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ private emitSourceUnit(sourceUnit: SourceUnitSyntax): void {
+ if (!this.document.isDeclareFile()) {
+ var pullDecl = this.semanticInfoChain.getDeclForAST(sourceUnit);
+ this.pushDecl(pullDecl);
+ this.emitScriptElements(sourceUnit);
+ this.popDecl(pullDecl);
+
+ this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(sourceUnit.endOfFileToken, this.text()), /*trailing:*/ false);
+ }
+ }
+
+ public shouldEmitEnumDeclaration(declaration: EnumDeclarationSyntax): boolean {
+ return ASTHelpers.preComments(declaration, this.text()) !== null || !ASTHelpers.enumIsElided(declaration);
+ }
+
+ public emitEnumDeclaration(declaration: EnumDeclarationSyntax): void {
+ if (!ASTHelpers.enumIsElided(declaration)) {
+ this.emitComments(declaration, true);
+ this.emitEnum(declaration);
+ this.emitComments(declaration, false);
+ }
+ else {
+ this.emitComments(declaration, true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ public shouldEmitModuleDeclaration(declaration: ModuleDeclarationSyntax): boolean {
+ return ASTHelpers.preComments(declaration, this.text()) !== null || !ASTHelpers.moduleIsElided(declaration);
+ }
+
+ private emitModuleDeclaration(declaration: ModuleDeclarationSyntax): void {
+ if (!ASTHelpers.moduleIsElided(declaration)) {
+ this.emitModuleDeclarationWorker(declaration);
+ }
+ else {
+ this.emitComments(declaration, true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ public shouldEmitClassDeclaration(declaration: ClassDeclarationSyntax): boolean {
+ return ASTHelpers.preComments(declaration, this.text()) !== null || !hasModifier(declaration.modifiers, PullElementFlags.Ambient);
+ }
+
+ public emitClassDeclaration(declaration: ClassDeclarationSyntax): void {
+ if (!hasModifier(declaration.modifiers, PullElementFlags.Ambient)) {
+ this.emitClass(declaration);
+ }
+ else {
+ this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ public shouldEmitInterfaceDeclaration(declaration: InterfaceDeclarationSyntax): boolean {
+ return ASTHelpers.preComments(declaration, this.text()) !== null;
+ }
+
+ public emitInterfaceDeclaration(declaration: InterfaceDeclarationSyntax): void {
+ this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+
+ private firstVariableDeclarator(statement: VariableStatementSyntax): VariableDeclaratorSyntax {
+ return statement.variableDeclaration.variableDeclarators[0];
+ }
+
+ private isNotAmbientOrHasInitializer(variableStatement: VariableStatementSyntax): boolean {
+ return !hasModifier(variableStatement.modifiers, PullElementFlags.Ambient) || this.firstVariableDeclarator(variableStatement).equalsValueClause !== null;
+ }
+
+ public shouldEmitVariableStatement(statement: VariableStatementSyntax): boolean {
+ return ASTHelpers.preComments(statement, this.text()) !== null || this.isNotAmbientOrHasInitializer(statement);
+ }
+
+ public emitVariableStatement(statement: VariableStatementSyntax): void {
+ if (this.isNotAmbientOrHasInitializer(statement)) {
+ this.emitComments(statement, true);
+ this.emit(statement.variableDeclaration);
+ this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken);
+ this.emitComments(statement, false);
+ }
+ else {
+ this.emitComments(statement, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true);
+ }
+ }
+
+ public emitGenericType(type: GenericTypeSyntax): void {
+ this.emit(type.name);
+ }
+
+ private shouldEmit(ast: ISyntaxElement): boolean {
+ if (!ast) {
+ return false;
+ }
+
+ switch (ast.kind()) {
+ case SyntaxKind.ImportDeclaration:
+ return this.shouldEmitImportDeclaration(ast);
+ case SyntaxKind.ClassDeclaration:
+ return this.shouldEmitClassDeclaration(ast);
+ case SyntaxKind.InterfaceDeclaration:
+ return this.shouldEmitInterfaceDeclaration(ast);
+ case SyntaxKind.FunctionDeclaration:
+ return this.shouldEmitFunctionDeclaration(ast);
+ case SyntaxKind.ModuleDeclaration:
+ return this.shouldEmitModuleDeclaration(ast);
+ case SyntaxKind.VariableStatement:
+ return this.shouldEmitVariableStatement(ast);
+ case SyntaxKind.OmittedExpression:
+ return false;
+ case SyntaxKind.EnumDeclaration:
+ return this.shouldEmitEnumDeclaration(ast);
+ }
+
+ return true;
+ }
+
+ private emit(ast: ISyntaxElement): void {
+ if (!ast) {
+ return;
+ }
+
+ switch (ast.kind()) {
+ case SyntaxKind.SeparatedList:
+ return this.emitSeparatedList(ast);
+ case SyntaxKind.List:
+ return this.emitList(ast);
+ case SyntaxKind.SourceUnit:
+ return this.emitSourceUnit(ast);
+ case SyntaxKind.ImportDeclaration:
+ return this.emitImportDeclaration(ast);
+ case SyntaxKind.ExportAssignment:
+ return this.setExportAssignment(ast);
+ case SyntaxKind.ClassDeclaration:
+ return this.emitClassDeclaration(ast);
+ case SyntaxKind.InterfaceDeclaration:
+ return this.emitInterfaceDeclaration(ast);
+ case SyntaxKind.IdentifierName:
+ return this.emitName(ast, true);
+ case SyntaxKind.VariableDeclarator:
+ return this.emitVariableDeclarator(ast);
+ case SyntaxKind.SimpleArrowFunctionExpression:
+ return this.emitSimpleArrowFunctionExpression(ast);
+ case SyntaxKind.ParenthesizedArrowFunctionExpression:
+ return this.emitParenthesizedArrowFunctionExpression(ast);
+ case SyntaxKind.FunctionDeclaration:
+ return this.emitFunctionDeclaration(ast);
+ case SyntaxKind.ModuleDeclaration:
+ return this.emitModuleDeclaration(ast);
+ case SyntaxKind.VariableDeclaration:
+ return this.emitVariableDeclaration(ast);
+ case SyntaxKind.GenericType:
+ return this.emitGenericType(ast);
+ case SyntaxKind.ConstructorDeclaration:
+ return this.emitConstructorDeclaration(