Merge the comment writer back into the emitter to reduce stack size.

This commit is contained in:
Ron Buckton
2017-02-09 17:36:25 -08:00
parent cd22d81c67
commit 22b9c69873
9 changed files with 431 additions and 575 deletions
+1 -64
View File
@@ -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",
-429
View File
@@ -1,429 +0,0 @@
/// <reference path="sourcemap.ts" />
/* @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:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// 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;
}
}
}
+419 -76
View File
@@ -2,7 +2,6 @@
/// <reference path="transformer.ts" />
/// <reference path="declarationEmitter.ts" />
/// <reference path="sourcemap.ts" />
/// <reference path="comments.ts" />
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<string>; // 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<string>();
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<Node>, 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 = (<ParenthesizedExpression>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:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// 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
+8 -1
View File
@@ -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);
}
-1
View File
@@ -31,7 +31,6 @@
"transformers/module/system.ts",
"transformers/module/es2015.ts",
"transformer.ts",
"comments.ts",
"sourcemap.ts",
"declarationEmitter.ts",
"emitter.ts",
+1
View File
@@ -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.
+1 -1
View File
@@ -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);
}
/**
+1 -2
View File
@@ -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",
-1
View File
@@ -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",