diff --git a/Jakefile.js b/Jakefile.js
index 282471a09ef..26e9ef4ab01 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -82,70 +82,7 @@ var cancellationTokenSources = filesFromConfig(path.join(serverDirectory, "cance
var typingsInstallerSources = filesFromConfig(path.join(serverDirectory, "typingsInstaller/tsconfig.json"));
var serverSources = filesFromConfig(path.join(serverDirectory, "tsconfig.json"))
var languageServiceLibrarySources = filesFromConfig(path.join(serverDirectory, "tsconfig.library.json"));
-
-var harnessCoreSources = [
- "harness.ts",
- "virtualFileSystem.ts",
- "sourceMapRecorder.ts",
- "harnessLanguageService.ts",
- "fourslash.ts",
- "runnerbase.ts",
- "compilerRunner.ts",
- "typeWriter.ts",
- "fourslashRunner.ts",
- "projectsRunner.ts",
- "loggedIO.ts",
- "rwcRunner.ts",
- "test262Runner.ts",
- "runner.ts"
-].map(function (f) {
- return path.join(harnessDirectory, f);
-});
-
-var harnessSources = harnessCoreSources.concat([
- "incrementalParser.ts",
- "jsDocParsing.ts",
- "services/colorization.ts",
- "services/documentRegistry.ts",
- "services/preProcessFile.ts",
- "services/patternMatcher.ts",
- "session.ts",
- "versionCache.ts",
- "convertToBase64.ts",
- "transpile.ts",
- "reuseProgramStructure.ts",
- "textStorage.ts",
- "cachingInServerLSHost.ts",
- "moduleResolution.ts",
- "tsconfigParsing.ts",
- "commandLineParsing.ts",
- "configurationExtension.ts",
- "convertCompilerOptionsFromJson.ts",
- "convertTypeAcquisitionFromJson.ts",
- "tsserverProjectSystem.ts",
- "compileOnSave.ts",
- "typingsInstaller.ts",
- "projectErrors.ts",
- "matchFiles.ts",
- "initializeTSConfig.ts",
- "printer.ts",
- "transform.ts",
- "customTransforms.ts",
-].map(function (f) {
- return path.join(unittestsDirectory, f);
-})).concat([
- "protocol.ts",
- "utilities.ts",
- "scriptVersionCache.ts",
- "scriptInfo.ts",
- "lsHost.ts",
- "project.ts",
- "typingsCache.ts",
- "editorServices.ts",
- "session.ts",
-].map(function (f) {
- return path.join(serverDirectory, f);
-}));
+var harnessSources = filesFromConfig("./src/harness/tsconfig.json");
var es2015LibrarySources = [
"es2015.core.d.ts",
diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts
deleted file mode 100644
index 9e077ebfaa8..00000000000
--- a/src/compiler/comments.ts
+++ /dev/null
@@ -1,429 +0,0 @@
-///
-
-/* @internal */
-namespace ts {
- export interface CommentWriter {
- reset(): void;
- setSourceFile(sourceFile: SourceFile): void;
- setWriter(writer: EmitTextWriter): void;
- emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
- emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
- emitTrailingCommentsOfPosition(pos: number): void;
- emitLeadingCommentsOfPosition(pos: number): void;
- }
-
- export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter {
- const extendedDiagnostics = printerOptions.extendedDiagnostics;
- const newLine = getNewLineCharacter(printerOptions);
- let writer: EmitTextWriter;
- let containerPos = -1;
- let containerEnd = -1;
- let declarationListContainerEnd = -1;
- let currentSourceFile: SourceFile;
- let currentText: string;
- let currentLineMap: number[];
- let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
- let hasWrittenComment = false;
- let disabled: boolean = printerOptions.removeComments;
-
- return {
- reset,
- setWriter,
- setSourceFile,
- emitNodeWithComments,
- emitBodyWithDetachedComments,
- emitTrailingCommentsOfPosition,
- emitLeadingCommentsOfPosition,
- };
-
- function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
- if (disabled) {
- emitCallback(hint, node);
- return;
- }
-
- if (node) {
- hasWrittenComment = false;
-
- const emitNode = node.emitNode;
- const emitFlags = emitNode && emitNode.flags;
- const { pos, end } = emitNode && emitNode.commentRange || node;
- if ((pos < 0 && end < 0) || (pos === end)) {
- // Both pos and end are synthesized, so just emit the node without comments.
- emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
- }
- else {
- if (extendedDiagnostics) {
- performance.mark("preEmitNodeWithComment");
- }
-
- const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
- const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
- const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
-
- // Emit leading comments if the position is not synthesized and the node
- // has not opted out from emitting leading comments.
- if (!skipLeadingComments) {
- emitLeadingComments(pos, isEmittedNode);
- }
-
- // Save current container state on the stack.
- const savedContainerPos = containerPos;
- const savedContainerEnd = containerEnd;
- const savedDeclarationListContainerEnd = declarationListContainerEnd;
-
- if (!skipLeadingComments) {
- containerPos = pos;
- }
-
- if (!skipTrailingComments) {
- containerEnd = end;
-
- // To avoid invalid comment emit in a down-level binding pattern, we
- // keep track of the last declaration list container's end
- if (node.kind === SyntaxKind.VariableDeclarationList) {
- declarationListContainerEnd = end;
- }
- }
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "preEmitNodeWithComment");
- }
-
- emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
-
- if (extendedDiagnostics) {
- performance.mark("postEmitNodeWithComment");
- }
-
- // Restore previous container state.
- containerPos = savedContainerPos;
- containerEnd = savedContainerEnd;
- declarationListContainerEnd = savedDeclarationListContainerEnd;
-
- // Emit trailing comments if the position is not synthesized and the node
- // has not opted out from emitting leading comments and is an emitted node.
- if (!skipTrailingComments && isEmittedNode) {
- emitTrailingComments(end);
- }
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "postEmitNodeWithComment");
- }
- }
- }
- }
-
- function emitNodeWithSynthesizedComments(hint: EmitHint, node: Node, emitNode: EmitNode, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
- const leadingComments = emitNode && emitNode.leadingComments;
- if (some(leadingComments)) {
- if (extendedDiagnostics) {
- performance.mark("preEmitNodeWithSynthesizedComments");
- }
-
- forEach(leadingComments, emitLeadingSynthesizedComment);
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "preEmitNodeWithSynthesizedComments");
- }
- }
-
- emitNodeWithNestedComments(hint, node, emitFlags, emitCallback);
-
- const trailingComments = emitNode && emitNode.trailingComments;
- if (some(trailingComments)) {
- if (extendedDiagnostics) {
- performance.mark("postEmitNodeWithSynthesizedComments");
- }
-
- forEach(trailingComments, emitTrailingSynthesizedComment);
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "postEmitNodeWithSynthesizedComments");
- }
- }
- }
-
- function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
- if (comment.kind === SyntaxKind.SingleLineCommentTrivia) {
- writer.writeLine();
- }
- writeSynthesizedComment(comment);
- if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
- writer.writeLine();
- }
- else {
- writer.write(" ");
- }
- }
-
- function emitTrailingSynthesizedComment(comment: SynthesizedComment) {
- if (!writer.isAtStartOfLine()) {
- writer.write(" ");
- }
- writeSynthesizedComment(comment);
- if (comment.hasTrailingNewLine) {
- writer.writeLine();
- }
- }
-
- function writeSynthesizedComment(comment: SynthesizedComment) {
- const text = formatSynthesizedComment(comment);
- const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined;
- writeCommentRange(text, lineMap, writer, 0, text.length, newLine);
- }
-
- function formatSynthesizedComment(comment: SynthesizedComment) {
- return comment.kind === SyntaxKind.MultiLineCommentTrivia
- ? `/*${comment.text}*/`
- : `//${comment.text}`;
- }
-
- function emitNodeWithNestedComments(hint: EmitHint, node: Node, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
- if (emitFlags & EmitFlags.NoNestedComments) {
- disabled = true;
- emitCallback(hint, node);
- disabled = false;
- }
- else {
- emitCallback(hint, node);
- }
- }
-
- function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
- if (extendedDiagnostics) {
- performance.mark("preEmitBodyWithDetachedComments");
- }
-
- const { pos, end } = detachedRange;
- const emitFlags = getEmitFlags(node);
- const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
- const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
-
- if (!skipLeadingComments) {
- emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
- }
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "preEmitBodyWithDetachedComments");
- }
-
- if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
- disabled = true;
- emitCallback(node);
- disabled = false;
- }
- else {
- emitCallback(node);
- }
-
- if (extendedDiagnostics) {
- performance.mark("beginEmitBodyWithDetachedCommetns");
- }
-
- if (!skipTrailingComments) {
- emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
- if (hasWrittenComment && !writer.isAtStartOfLine()) {
- writer.writeLine();
- }
- }
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "beginEmitBodyWithDetachedCommetns");
- }
- }
-
- function emitLeadingComments(pos: number, isEmittedNode: boolean) {
- hasWrittenComment = false;
-
- if (isEmittedNode) {
- forEachLeadingCommentToEmit(pos, emitLeadingComment);
- }
- else if (pos === 0) {
- // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
- // unless it is a triple slash comment at the top of the file.
- // For Example:
- // ///
- // declare var x;
- // ///
- // interface F {}
- // The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
- forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
- }
- }
-
- function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
- if (isTripleSlashComment(commentPos, commentEnd)) {
- emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
- }
- }
-
- function emitLeadingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
- if (!hasWrittenComment) {
- emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
- hasWrittenComment = true;
- }
-
- // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
- if (emitPos) emitPos(commentPos);
- writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- if (emitPos) emitPos(commentEnd);
-
- if (hasTrailingNewLine) {
- writer.writeLine();
- }
- else {
- writer.write(" ");
- }
- }
-
- function emitLeadingCommentsOfPosition(pos: number) {
- if (disabled || pos === -1) {
- return;
- }
-
- emitLeadingComments(pos, /*isEmittedNode*/ true);
- }
-
- function emitTrailingComments(pos: number) {
- forEachTrailingCommentToEmit(pos, emitTrailingComment);
- }
-
- function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
- // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
- if (!writer.isAtStartOfLine()) {
- writer.write(" ");
- }
-
- if (emitPos) emitPos(commentPos);
- writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- if (emitPos) emitPos(commentEnd);
-
- if (hasTrailingNewLine) {
- writer.writeLine();
- }
- }
-
- function emitTrailingCommentsOfPosition(pos: number) {
- if (disabled) {
- return;
- }
-
- if (extendedDiagnostics) {
- performance.mark("beforeEmitTrailingCommentsOfPosition");
- }
-
- forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
-
- if (extendedDiagnostics) {
- performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition");
- }
- }
-
- function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
- // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
-
- if (emitPos) emitPos(commentPos);
- writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
- if (emitPos) emitPos(commentEnd);
-
- if (hasTrailingNewLine) {
- writer.writeLine();
- }
- else {
- writer.write(" ");
- }
- }
-
- function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
- // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
- if (containerPos === -1 || pos !== containerPos) {
- if (hasDetachedComments(pos)) {
- forEachLeadingCommentWithoutDetachedComments(cb);
- }
- else {
- forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
- }
- }
- }
-
- function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
- // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
- if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
- forEachTrailingCommentRange(currentText, end, cb);
- }
- }
-
- function reset() {
- currentSourceFile = undefined;
- currentText = undefined;
- currentLineMap = undefined;
- detachedCommentsInfo = undefined;
- }
-
- function setWriter(output: EmitTextWriter): void {
- writer = output;
- }
-
- function setSourceFile(sourceFile: SourceFile) {
- currentSourceFile = sourceFile;
- currentText = currentSourceFile.text;
- currentLineMap = getLineStarts(currentSourceFile);
- detachedCommentsInfo = undefined;
- }
-
- function hasDetachedComments(pos: number) {
- return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
- }
-
- function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
- // get the leading comments from detachedPos
- const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
- if (detachedCommentsInfo.length - 1) {
- detachedCommentsInfo.pop();
- }
- else {
- detachedCommentsInfo = undefined;
- }
-
- forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
- }
-
- function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
- const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
- if (currentDetachedCommentInfo) {
- if (detachedCommentsInfo) {
- detachedCommentsInfo.push(currentDetachedCommentInfo);
- }
- else {
- detachedCommentsInfo = [currentDetachedCommentInfo];
- }
- }
- }
-
- function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
- if (emitPos) emitPos(commentPos);
- writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
- if (emitPos) emitPos(commentEnd);
- }
-
- /**
- * Determine if the given comment is a triple-slash
- *
- * @return true if the comment is a triple-slash comment else false
- **/
- function isTripleSlashComment(commentPos: number, commentEnd: number) {
- // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
- // so that we don't end up computing comment string and doing match for all // comments
- if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
- commentPos + 2 < commentEnd &&
- currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
- const textSubStr = currentText.substring(commentPos, commentEnd);
- return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
- textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
- true : false;
- }
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index af8dbb07cbe..3add4c7903d 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -2,7 +2,6 @@
///
///
///
-///
namespace ts {
const delimiters = createDelimiterMap();
@@ -194,24 +193,19 @@ namespace ts {
hasGlobalName,
onEmitSourceMapOfNode,
onEmitSourceMapOfToken,
- onEmitSourceMapOfPosition,
+ onEmitSourceMapOfPosition: emitPos,
onEmitNode,
onEmitHelpers,
onSetSourceFile,
substituteNode,
} = handlers;
+ const extendedDiagnostics = printerOptions.extendedDiagnostics;
const newLine = getNewLineCharacter(printerOptions);
const languageVersion = getEmitScriptTarget(printerOptions);
- const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition);
- const {
- emitNodeWithComments,
- emitBodyWithDetachedComments,
- emitTrailingCommentsOfPosition,
- emitLeadingCommentsOfPosition,
- } = comments;
let currentSourceFile: SourceFile;
+ let currentText: string;
let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes.
let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables.
let generatedNames: Map; // Set of names generated by the NameGenerator.
@@ -219,6 +213,13 @@ namespace ts {
let tempFlags: TempFlags; // TempFlags for the current name generation scope.
let writer: EmitTextWriter;
let ownWriter: EmitTextWriter;
+ let containerPos = -1;
+ let containerEnd = -1;
+ let declarationListContainerEnd = -1;
+ let currentLineMap: number[];
+ let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
+ let hasWrittenComment = false;
+ let disabled: boolean = printerOptions.removeComments;
reset();
return {
@@ -302,12 +303,14 @@ namespace ts {
function print(hint: EmitHint, node: Node, sourceFile: SourceFile) {
setSourceFile(sourceFile);
- pipelineEmitWithNotification(hint, node);
+ pipelineEmit(hint, node);
}
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
- comments.setSourceFile(sourceFile);
+ currentText = currentSourceFile.text;
+ currentLineMap = getLineStarts(currentSourceFile);
+ detachedCommentsInfo = undefined;
if (onSetSourceFile) {
onSetSourceFile(sourceFile);
}
@@ -315,57 +318,73 @@ namespace ts {
function setWriter(output: EmitTextWriter | undefined) {
writer = output;
- comments.setWriter(output);
}
function reset() {
+ setWriter(/*output*/ undefined);
nodeIdToGeneratedName = [];
autoGeneratedIdToGeneratedName = [];
generatedNames = createMap();
tempFlagsStack = [];
tempFlags = TempFlags.Auto;
- comments.reset();
- setWriter(/*output*/ undefined);
+ currentSourceFile = undefined;
+ currentText = undefined;
+ currentLineMap = undefined;
+ detachedCommentsInfo = undefined;
}
function emit(node: Node) {
- pipelineEmitWithNotification(EmitHint.Unspecified, node);
+ pipelineEmit(EmitHint.Unspecified, node);
}
function emitIdentifierName(node: Identifier) {
- pipelineEmitWithNotification(EmitHint.IdentifierName, node);
+ pipelineEmit(EmitHint.IdentifierName, node);
}
function emitExpression(node: Expression) {
- pipelineEmitWithNotification(EmitHint.Expression, node);
+ pipelineEmit(EmitHint.Expression, node);
+ }
+
+ function pipelineEmit(hint: EmitHint, node: Node) {
+ getInitialPhase(hint)(hint, trySubstituteNode(hint, node));
+ }
+
+ function getInitialPhase(hint: EmitHint) {
+ return !shouldSkipNotificationPhase() ? pipelineEmitWithNotification : getNextPhaseFollowingNotification(hint);
+ }
+
+ function shouldSkipNotificationPhase() {
+ return onEmitNode === undefined;
+ }
+
+ function getNextPhaseFollowingNotification(hint: EmitHint) {
+ return !shouldSkipCommentPhase(hint) ? emitNodeConsideringComments : getNextPhaseFollowingComments(hint);
+ }
+
+ function shouldSkipCommentPhase(hint: EmitHint) {
+ return disabled || hint === EmitHint.SourceFile;
+ }
+
+ function getNextPhaseFollowingComments(hint: EmitHint) {
+ return !shouldSkipSourceMapPhase(hint) ? pipelineEmitWithSourceMap : getNextPhaseFollowingSourceMap();
+ }
+
+ function shouldSkipSourceMapPhase(hint: EmitHint) {
+ return onEmitSourceMapOfNode === undefined
+ || hint === EmitHint.SourceFile
+ || hint === EmitHint.IdentifierName;
+ }
+
+ function getNextPhaseFollowingSourceMap() {
+ return pipelineEmitWithHint;
}
function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
- if (onEmitNode) {
- onEmitNode(hint, node, pipelineEmitWithComments);
- }
- else {
- pipelineEmitWithComments(hint, node);
- }
- }
-
- function pipelineEmitWithComments(hint: EmitHint, node: Node) {
- node = trySubstituteNode(hint, node);
- if (emitNodeWithComments && hint !== EmitHint.SourceFile) {
- emitNodeWithComments(hint, node, pipelineEmitWithSourceMap);
- }
- else {
- pipelineEmitWithSourceMap(hint, node);
- }
+ onEmitNode(hint, node, getNextPhaseFollowingNotification(hint));
}
function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) {
- if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) {
- onEmitSourceMapOfNode(hint, node, pipelineEmitWithHint);
- }
- else {
- pipelineEmitWithHint(hint, node);
- }
+ onEmitSourceMapOfNode(hint, node, getNextPhaseFollowingSourceMap());
}
function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
@@ -728,15 +747,6 @@ namespace ts {
return node && substituteNode && substituteNode(hint, node) || node;
}
- function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void {
- if (emitBodyWithDetachedComments) {
- emitBodyWithDetachedComments(node, elements, emitCallback);
- }
- else {
- emitCallback(node);
- }
- }
-
function emitHelpersIndirect(node: Node) {
if (onEmitHelpers) {
onEmitHelpers(node, writeLines);
@@ -1635,26 +1645,30 @@ namespace ts {
write(" {");
increaseIndent();
- const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body)
- ? emitBlockFunctionBodyOnSingleLine
- : emitBlockFunctionBodyWorker;
+ const commentFlags = getEmitFlags(body);
+ emitLeadingDetachedComments(body.statements, commentFlags);
- emitBodyIndirect(body, body.statements, emitBlockFunctionBody);
+ if (!disabled && (commentFlags & EmitFlags.NoNestedComments)) {
+ disabled = true;
+ emitBlockFunctionBodyWorker(body);
+ disabled = false;
+ }
+ else {
+ emitBlockFunctionBodyWorker(body);
+ }
+
+ emitTrailingDetachedComments(body.statements, commentFlags);
decreaseIndent();
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
}
- function emitBlockFunctionBodyOnSingleLine(body: Block) {
- emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true);
- }
-
- function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) {
+ function emitBlockFunctionBodyWorker(body: Block) {
// Emit all the prologue directives (like "use strict").
- const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true);
const pos = writer.getTextPos();
+ const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true);
emitHelpersIndirect(body);
- if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) {
+ if (statementOffset === 0 && pos === writer.getTextPos() && shouldEmitBlockFunctionBodyOnSingleLine(body)) {
decreaseIndent();
emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements);
increaseIndent();
@@ -2040,16 +2054,17 @@ namespace ts {
function emitSourceFile(node: SourceFile) {
writeLine();
emitShebang();
- emitBodyIndirect(node, node.statements, emitSourceFileWorker);
- }
- function emitSourceFileWorker(node: SourceFile) {
- const statements = node.statements;
- const statementOffset = emitPrologueDirectives(statements);
+ const commentFlags = getEmitFlags(node);
+ emitLeadingDetachedComments(node.statements, commentFlags);
+
+ const statementOffset = emitPrologueDirectives(node.statements);
pushNameGenerationScope();
emitHelpersIndirect(node);
- emitList(node, statements, ListFormat.MultiLine, statementOffset);
+ emitList(node, node.statements, ListFormat.MultiLine, statementOffset);
popNameGenerationScope();
+
+ emitTrailingDetachedComments(node.statements, commentFlags);
}
// Transformation nodes
@@ -2493,9 +2508,9 @@ namespace ts {
}
function needsIndentation(parent: Node, node1: Node, node2: Node): boolean {
- parent = skipSynthesizedParentheses(parent);
- node1 = skipSynthesizedParentheses(node1);
- node2 = skipSynthesizedParentheses(node2);
+ parent = skipOuterExpressions(parent, OuterExpressionKinds.SynthesizedParentheses);
+ node1 = skipOuterExpressions(node1, OuterExpressionKinds.SynthesizedParentheses);
+ node2 = skipOuterExpressions(node2, OuterExpressionKinds.SynthesizedParentheses);
// Always use a newline for synthesized code if the synthesizer desires it.
if (node2.startsOnNewLine) {
@@ -2518,14 +2533,6 @@ namespace ts {
&& rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile);
}
- function skipSynthesizedParentheses(node: Node) {
- while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) {
- node = (node).expression;
- }
-
- return node;
- }
-
function getTextOfNode(node: Node, includeTrivia?: boolean): string {
if (isGeneratedIdentifier(node)) {
return generateName(node);
@@ -2557,6 +2564,10 @@ namespace ts {
return getLiteralText(node, currentSourceFile, languageVersion);
}
+ //
+ // Name generation
+ //
+
/**
* Push a new name generation scope.
*/
@@ -2779,6 +2790,297 @@ namespace ts {
// otherwise, return the original node for the source;
return node;
}
+
+ //
+ // Comments
+ //
+
+ function emitNodeConsideringComments(hint: EmitHint, node: Node) {
+ if (node === undefined) {
+ return;
+ }
+
+ const commentRange = getCommentRange(node);
+ const commentFlags = shouldEmitLeadingOrTrailingComments(node, commentRange);
+ if ((commentFlags & EmitFlags.NoComments) === EmitFlags.NoComments) {
+ // always emit any explicit leading or trailing comments.
+ emitLeadingSynthesizedCommentsOfNode(node);
+ emitNodeConsideringNestedComments(hint, node, commentFlags, commentRange);
+ emitTrailingSynthesizedCommentsOfNode(node);
+ }
+ else {
+ // Emit leading comments if the position is not synthesized and the node
+ // has not opted out from emitting leading comments.
+ if ((commentFlags & EmitFlags.NoLeadingComments) === 0) {
+ emitLeadingComments(commentRange.pos, !isNotEmittedStatement(node));
+ }
+
+ emitLeadingSynthesizedCommentsOfNode(node);
+ emitNodeConsideringNestedComments(hint, node, commentFlags, commentRange);
+ emitTrailingSynthesizedCommentsOfNode(node);
+
+ // Emit trailing comments if the position is not synthesized and the node
+ // has not opted out from emitting trailing comments.
+ if ((commentFlags & EmitFlags.NoTrailingComments) === 0 && !isNotEmittedStatement(node)) {
+ emitTrailingComments(commentRange.end);
+ }
+ }
+ }
+
+ function emitLeadingSynthesizedCommentsOfNode(node: Node) {
+ if (extendedDiagnostics) performance.mark("emitLeadingSynthesizedComments");
+ forEach(getSyntheticLeadingComments(node), emitLeadingSynthesizedComment);
+ if (extendedDiagnostics) performance.measure("commentTime", "emitLeadingSynthesizedComments");
+ }
+
+ function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
+ if (comment.kind === SyntaxKind.SingleLineCommentTrivia) {
+ writer.writeLine();
+ }
+ writeSynthesizedComment(comment);
+ if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
+ writer.writeLine();
+ }
+ else {
+ writer.write(" ");
+ }
+ }
+
+ function emitTrailingSynthesizedCommentsOfNode(node: Node) {
+ if (extendedDiagnostics) performance.mark("emitTrailingSynthesizedComments");
+ forEach(getSyntheticTrailingComments(node), emitTrailingSynthesizedComment);
+ if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingSynthesizedComments");
+ }
+
+ function emitTrailingSynthesizedComment(comment: SynthesizedComment) {
+ if (!writer.isAtStartOfLine()) {
+ writer.write(" ");
+ }
+ writeSynthesizedComment(comment);
+ if (comment.hasTrailingNewLine) {
+ writer.writeLine();
+ }
+ }
+
+ function writeSynthesizedComment(comment: SynthesizedComment) {
+ const text = formatSynthesizedComment(comment);
+ const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined;
+ writeCommentRange(text, lineMap, writer, 0, text.length, newLine);
+ }
+
+ function emitNodeConsideringNestedComments(hint: EmitHint, node: Node, commentFlags: EmitFlags, commentRange: TextRange) {
+ if (commentFlags & EmitFlags.NoNestedComments) {
+ disabled = true;
+ getNextPhaseFollowingComments(hint)(hint, node);
+ disabled = false;
+ }
+ else {
+ emitNodeWithNestedComments(hint, node, commentFlags, commentRange);
+ }
+ }
+
+ function emitNodeWithNestedComments(hint: EmitHint, node: Node, commentFlags: EmitFlags, commentRange: TextRange) {
+ // Save current container state on the stack.
+ const savedContainerPos = containerPos;
+ const savedContainerEnd = containerEnd;
+ const savedDeclarationListContainerEnd = declarationListContainerEnd;
+
+ if ((commentFlags & EmitFlags.NoLeadingComments) === 0) {
+ containerPos = commentRange.pos;
+ }
+
+ if ((commentFlags & EmitFlags.NoTrailingComments) === 0) {
+ containerEnd = commentRange.end;
+
+ // To avoid invalid comment emit in a down-level binding pattern, we
+ // keep track of the last declaration list container's end
+ if (node.kind === SyntaxKind.VariableDeclarationList) {
+ declarationListContainerEnd = commentRange.end;
+ }
+ }
+
+ getNextPhaseFollowingComments(hint)(hint, node);
+
+ // Restore previous container state.
+ containerPos = savedContainerPos;
+ containerEnd = savedContainerEnd;
+ declarationListContainerEnd = savedDeclarationListContainerEnd;
+ }
+
+ function emitLeadingDetachedComments(detachedRange: TextRange, commentFlags: EmitFlags) {
+ if (extendedDiagnostics) performance.mark("emitLeadingDetachedCommentsOfBody");
+ if (!positionIsSynthesized(detachedRange.pos) && (commentFlags & EmitFlags.NoLeadingComments) === 0) {
+ emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
+ }
+ if (extendedDiagnostics) performance.measure("commentTime", "emitLeadingDetachedCommentsOfBody");
+ }
+
+ function emitTrailingDetachedComments(detachedRange: TextRange, commentFlags: EmitFlags) {
+ if (disabled) {
+ return;
+ }
+ if (extendedDiagnostics) performance.mark("emitTrailingDetachedComments");
+ if (!positionIsSynthesized(detachedRange.end) && (commentFlags & EmitFlags.NoTrailingComments) === 0) {
+ emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
+ if (hasWrittenComment && !writer.isAtStartOfLine()) {
+ writer.writeLine();
+ }
+ }
+ if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingDetachedComments");
+ }
+
+ function emitLeadingCommentsOfPosition(pos: number) {
+ if (disabled || positionIsSynthesized(pos)) {
+ return;
+ }
+
+ emitLeadingComments(pos, /*isEmittedNode*/ true);
+ }
+
+ function emitLeadingComments(pos: number, isEmittedNode: boolean) {
+ if (extendedDiagnostics) performance.mark("emitLeadingComments");
+ hasWrittenComment = false;
+ if (isEmittedNode) {
+ forEachLeadingCommentToEmit(pos, emitLeadingComment);
+ }
+ else if (pos === 0) {
+ // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
+ // unless it is a triple slash comment at the top of the file.
+ // For Example:
+ // ///
+ // declare var x;
+ // ///
+ // interface F {}
+ // The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
+ forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
+ }
+ if (extendedDiagnostics) performance.measure("commentTime", "emitLeadingComments");
+ }
+
+ function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
+ if (isTripleSlashComment(currentText, commentPos, commentEnd)) {
+ emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
+ }
+ }
+
+ function emitLeadingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
+ if (!hasWrittenComment) {
+ emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
+ hasWrittenComment = true;
+ }
+
+ // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
+ if (emitPos) emitPos(commentPos);
+ writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
+ if (emitPos) emitPos(commentEnd);
+
+ if (hasTrailingNewLine) {
+ writer.writeLine();
+ }
+ else {
+ writer.write(" ");
+ }
+ }
+
+ function emitTrailingComments(pos: number) {
+ if (extendedDiagnostics) performance.mark("emitTrailingComments");
+ forEachTrailingCommentToEmit(pos, emitTrailingComment);
+ if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingComments");
+ }
+
+ function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
+ // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
+ if (!writer.isAtStartOfLine()) {
+ writer.write(" ");
+ }
+
+ if (emitPos) emitPos(commentPos);
+ writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
+ if (emitPos) emitPos(commentEnd);
+
+ if (hasTrailingNewLine) {
+ writer.writeLine();
+ }
+ }
+
+ function emitTrailingCommentsOfPosition(pos: number) {
+ if (disabled || positionIsSynthesized(pos)) {
+ return;
+ }
+
+ if (extendedDiagnostics) performance.mark("emitTrailingCommentsOfPosition");
+ forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
+ if (extendedDiagnostics) performance.measure("commentTime", "emitTrailingCommentsOfPosition");
+ }
+
+ function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
+ // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
+
+ if (emitPos) emitPos(commentPos);
+ writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
+ if (emitPos) emitPos(commentEnd);
+
+ if (hasTrailingNewLine) {
+ writer.writeLine();
+ }
+ else {
+ writer.write(" ");
+ }
+ }
+
+ function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
+ // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
+ if (containerPos === -1 || pos !== containerPos) {
+ if (hasDetachedComments(pos)) {
+ forEachLeadingCommentWithoutDetachedComments(cb);
+ }
+ else {
+ forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
+ }
+ }
+ }
+
+ function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
+ // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
+ if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
+ forEachTrailingCommentRange(currentText, end, cb);
+ }
+ }
+
+ function hasDetachedComments(pos: number) {
+ return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
+ }
+
+ function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
+ // get the leading comments from detachedPos
+ const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
+ if (detachedCommentsInfo.length - 1) {
+ detachedCommentsInfo.pop();
+ }
+ else {
+ detachedCommentsInfo = undefined;
+ }
+
+ forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
+ }
+
+ function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
+ const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
+ if (currentDetachedCommentInfo) {
+ if (detachedCommentsInfo) {
+ detachedCommentsInfo.push(currentDetachedCommentInfo);
+ }
+ else {
+ detachedCommentsInfo = [currentDetachedCommentInfo];
+ }
+ }
+ }
+
+ function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
+ if (emitPos) emitPos(commentPos);
+ writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
+ if (emitPos) emitPos(commentEnd);
+ }
}
function createDelimiterMap() {
@@ -2811,6 +3113,47 @@ namespace ts {
return brackets[format & ListFormat.BracketsMask][1];
}
+ function shouldEmitLeadingOrTrailingComments(node: Node, commentRange: TextRange) {
+ let commentFlags = getEmitFlags(node) & (EmitFlags.NoComments | EmitFlags.NoNestedComments);
+ if (isCollapsedRange(commentRange)) {
+ commentFlags |= EmitFlags.NoComments;
+ }
+ else {
+ if ((commentFlags & EmitFlags.NoLeadingComments) === 0 && positionIsSynthesized(commentRange.pos)) {
+ commentFlags |= EmitFlags.NoLeadingComments;
+ }
+ if ((commentFlags & EmitFlags.NoTrailingComments) === 0 && positionIsSynthesized(commentRange.end)) {
+ commentFlags |= EmitFlags.NoTrailingComments;
+ }
+ }
+ return commentFlags;
+ }
+
+ function formatSynthesizedComment(comment: SynthesizedComment) {
+ return comment.kind === SyntaxKind.MultiLineCommentTrivia
+ ? `/*${comment.text}*/`
+ : `//${comment.text}`;
+ }
+
+ /**
+ * Determine if the given comment is a triple-slash
+ *
+ * @return true if the comment is a triple-slash comment else false
+ **/
+ function isTripleSlashComment(text: string, commentPos: number, commentEnd: number) {
+ // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
+ // so that we don't end up computing comment string and doing match for all // comments
+ if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
+ commentPos + 2 < commentEnd &&
+ text.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
+ const textSubStr = text.substring(commentPos, commentEnd);
+ return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
+ textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
+ true : false;
+ }
+ return false;
+ }
+
// Flags enum to track count of temp variables and a few dedicated names
const enum TempFlags {
Auto = 0x00000000, // No preferred name
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index 95821ba10de..51ec900814a 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -3067,7 +3067,10 @@ namespace ts {
Assertions = 1 << 1,
PartiallyEmittedExpressions = 1 << 2,
- All = Parentheses | Assertions | PartiallyEmittedExpressions
+ All = Parentheses | Assertions | PartiallyEmittedExpressions,
+
+ OnlySynthetic = 1 << 3,
+ SynthesizedParentheses = Parentheses | OnlySynthetic,
}
export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression;
@@ -3076,6 +3079,10 @@ namespace ts {
let previousNode: Node;
do {
previousNode = node;
+ if (kinds & OuterExpressionKinds.OnlySynthetic && !nodeIsSynthesized(node)) {
+ break;
+ }
+
if (kinds & OuterExpressionKinds.Parentheses) {
node = skipParentheses(node);
}
diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json
index 52bb92ee1f0..e8ba6e0b525 100644
--- a/src/compiler/tsconfig.json
+++ b/src/compiler/tsconfig.json
@@ -31,7 +31,6 @@
"transformers/module/system.ts",
"transformers/module/es2015.ts",
"transformer.ts",
- "comments.ts",
"sourcemap.ts",
"declarationEmitter.ts",
"emitter.ts",
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 064e5330e9e..5de029f98dc 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -3749,6 +3749,7 @@
}
export const enum EmitFlags {
+ None = 0,
SingleLine = 1 << 0, // The contents of this node should be emitted on a single line.
AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node.
NoSubstitution = 1 << 2, // Disables further substitution of an expression.
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index a38578a7a81..d7673ccb3f1 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -3352,7 +3352,7 @@ namespace ts {
* @param range A TextRange.
*/
export function isCollapsedRange(range: TextRange) {
- return range.pos === range.end;
+ return getRangePos(range) === getRangeEnd(range);
}
/**
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index 32af0eb2601..c701369c2bb 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -34,7 +34,6 @@
"../compiler/transformers/module/system.ts",
"../compiler/transformers/module/es2015.ts",
"../compiler/transformer.ts",
- "../compiler/comments.ts",
"../compiler/sourcemap.ts",
"../compiler/declarationEmitter.ts",
"../compiler/emitter.ts",
@@ -91,7 +90,7 @@
"rwcRunner.ts",
"test262Runner.ts",
"runner.ts",
- "../server/protocol.d.ts",
+ "../server/protocol.ts",
"../server/session.ts",
"../server/client.ts",
"../server/editorServices.ts",
diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json
index 9bebdb6932e..8d51d84e1f2 100644
--- a/src/services/tsconfig.json
+++ b/src/services/tsconfig.json
@@ -31,7 +31,6 @@
"../compiler/transformers/module/system.ts",
"../compiler/transformers/module/es2015.ts",
"../compiler/transformer.ts",
- "../compiler/comments.ts",
"../compiler/sourcemap.ts",
"../compiler/declarationEmitter.ts",
"../compiler/emitter.ts",