From 8bef6d7ebc6a92aeabfe7b438f7a302ab3e89263 Mon Sep 17 00:00:00 2001 From: steveluc Date: Sat, 7 Mar 2015 15:44:21 -0800 Subject: [PATCH 01/13] Added additional cases for format on enter. These cases fix bugs in the orginal format on enter (which wasn't distinguishing whether there was existing whitespace to start some types of lines). --- src/server/session.ts | 48 ++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index 7e9c6972a1f..5a91bcc08a1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -463,17 +463,41 @@ module ts.server { var edits = compilerService.languageService.getFormattingEditsAfterKeystroke(file, position, key, compilerService.formatCodeOptions); if ((key == "\n") && ((!edits) || (edits.length == 0) || allEditsBeforePos(edits, position))) { - // TODO: get these options from host - var editorOptions: ts.EditorOptions = { - IndentSize: 4, - TabSize: 4, - NewLineCharacter: "\n", - ConvertTabsToSpaces: true, - }; - var indentPosition = compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); - var spaces = generateSpaces(indentPosition); - if (indentPosition > 0) { - edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces }); + var scriptInfo = compilerService.host.getScriptInfo(file); + if (scriptInfo) { + var lineInfo = scriptInfo.getLineInfo(line); + if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) { + var lineText = lineInfo.leaf.text; + if (lineText.search("\\S") < 0) { + // TODO: get these options from host + var editorOptions: ts.EditorOptions = { + IndentSize: 4, + TabSize: 4, + NewLineCharacter: "\n", + ConvertTabsToSpaces: true, + }; + var indentPosition = + compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); + for (var i = 0, len = lineText.length; i < len; i++) { + if (lineText.charAt(i) == " ") { + indentPosition--; + } + else { + break; + } + } + if (indentPosition > 0) { + var spaces = generateSpaces(indentPosition); + edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces }); + } + else if (indentPosition < 0) { + edits.push({ + span: ts.createTextSpanFromBounds(position, position - indentPosition), + newText: "" + }); + } + } + } } } @@ -491,7 +515,7 @@ module ts.server { }; }); } - + getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionEntry[] { if (!prefix) { prefix = ""; From 9a45160ab64da5d1121aa806a8fb49fe01e3eb05 Mon Sep 17 00:00:00 2001 From: steveluc Date: Sat, 7 Mar 2015 17:13:52 -0800 Subject: [PATCH 02/13] Changed TypeScript server logging to use an environment variable TSS_LOG as follows. If TSS_LOG is not set, no logging will occur. If TSS_LOG is set to any value, logging will occur as before this change (log file will be in directory of tsserver.js with name .logPID where PID is the process id of the server process; log will contain the pre-change messages). If TSS_LOG is set to a string that has the form "-file fileName", the log file will be in the tsserver.js directory with name 'fileName'. If TSS_LOG is set to a string that has the form "-level levelName", then the level of logging will be set to 'levelName'. Currently the two levels are 'normal' and 'verbose'. The TSS_LOG string can contain zero, one or both of its options as in "-file LOG -level verbose". At the verbose level, the server will log every request, response and event, and will also give elapsed time for message processing. --- src/server/editorServices.ts | 2 ++ src/server/server.ts | 64 +++++++++++++++++++++++++++++++++--- src/server/session.ts | 17 +++++++++- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 5ca4cb4d00f..3cb66fed297 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -5,6 +5,8 @@ module ts.server { export interface Logger { close(): void; + verbose(): boolean; + enabled(): boolean; perftrc(s: string): void; info(s: string): void; startGroup(): void; diff --git a/src/server/server.ts b/src/server/server.ts index c48ba951347..e5cc0fd398d 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -19,7 +19,7 @@ module ts.server { inGroup = false; firstInGroup = true; - constructor(public logFilename: string) { + constructor(public logFilename: string, public level: string) { } static padStringRight(str: string, padding: string) { @@ -51,9 +51,20 @@ module ts.server { this.firstInGroup = true; } + enabled() { + return !!this.logFilename; + } + + verbose() { + return this.enabled() && (this.level == "verbose"); + } + + msg(s: string, type = "Err") { if (this.fd < 0) { - this.fd = fs.openSync(this.logFilename, "w"); + if (this.logFilename) { + this.fd = fs.openSync(this.logFilename, "w"); + } } if (this.fd >= 0) { s = s + "\n"; @@ -173,17 +184,62 @@ module ts.server { }); rl.on('close',() => { - this.projectService.closeLog(); this.projectService.log("Exiting..."); + this.projectService.closeLog(); process.exit(0); }); } } + interface LogEnv { + file?: string; + level?: string; + } + + function parseLogEnv(logEnvStr: string): LogEnv { + var logEnv: LogEnv = {}; + var args = logEnvStr.split(' '); + for (var i = 0, len = args.length; i < len; i++) { + var option = args[i]; + var value = args[i + 1]; + if (option && value) { + switch (option) { + case "-file": + logEnv.file = value; + break; + case "-level": + logEnv.level = value; + break; + } + } + } + return logEnv; + } + + // TSS_LOG "{ level: "normal | verbose | terse", file?: string}" + function createLoggerFromEnv() { + var fileName: string = undefined; + var level = "normal"; + var logEnvStr = process.env["TSS_LOG"]; + if (logEnvStr) { + var logEnv = parseLogEnv(logEnvStr); + if (logEnv.file) { + fileName = logEnv.file; + } + else { + fileName = __dirname + "/.log" + process.pid.toString(); + } + if (logEnv.level) { + level = logEnv.level; + } + } + var logger = new Logger(fileName, level); + return logger; + } // This places log file in the directory containing editorServices.js // TODO: check that this location is writable - var logger = new Logger(__dirname + "/.log" + process.pid.toString()); + var logger = createLoggerFromEnv(); // REVIEW: for now this implementation uses polling. // The advantage of polling is that it works reliably diff --git a/src/server/session.ts b/src/server/session.ts index 5a91bcc08a1..59a049a18e9 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -145,6 +145,9 @@ module ts.server { send(msg: NodeJS._debugger.Message) { var json = JSON.stringify(msg); + if (this.logger.verbose()) { + this.logger.info(msg.type+": " + json); + } this.sendLineToClient('Content-Length: ' + (1 + Buffer.byteLength(json, 'utf8')) + '\r\n\r\n' + json); } @@ -717,6 +720,10 @@ module ts.server { } onMessage(message: string) { + if (this.logger.verbose()) { + this.logger.info("request: " + message); + var start = process.hrtime(); + } try { var request = JSON.parse(message); var response: any; @@ -822,13 +829,21 @@ module ts.server { } } + if (this.logger.verbose()) { + var elapsed = process.hrtime(start); + var elapsedms = ((1e9 * elapsed[0]) + elapsed[1])/1000000.0; + var leader = "Elapsed time (in milliseconds)"; + if (!responseRequired) { + leader = "Async elapsed time (in milliseconds)"; + } + this.logger.msg(leader+": " + elapsedms.toFixed(4).toString(), "Perf"); + } if (response) { this.output(response, request.command, request.seq); } else if (responseRequired) { this.output(undefined, request.command, request.seq, "No content available."); } - } catch (err) { if (err instanceof OperationCanceledException) { // Handle cancellation exceptions From 865802a63cf88bdea46938fc99fa4f13f2e0e61c Mon Sep 17 00:00:00 2001 From: steveluc Date: Sat, 7 Mar 2015 17:46:56 -0800 Subject: [PATCH 03/13] Added new logger methods to test harness. --- src/harness/harnessLanguageService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 4206f16bab3..202a167f089 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -469,6 +469,7 @@ module Harness.LanguageService { this.writeMessage(message); } + readFile(fileName: string): string { if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) { fileName = Harness.Compiler.defaultLibFileName; @@ -526,6 +527,15 @@ module Harness.LanguageService { msg(message: string) { return this.host.log(message); } + + enabled() { + return true; + } + + verbose() { + return false; + } + endGroup(): void { } From 360e47880ebc8c43060284aec99ddb87e20f9ca9 Mon Sep 17 00:00:00 2001 From: steveluc Date: Sat, 7 Mar 2015 23:19:58 -0800 Subject: [PATCH 04/13] Addressed comments on code style and organization. --- src/harness/harnessLanguageService.ts | 2 +- src/server/editorServices.ts | 2 +- src/server/server.ts | 23 +++++++++++------------ src/server/session.ts | 14 ++++++++------ 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 202a167f089..55b3464a384 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -532,7 +532,7 @@ module Harness.LanguageService { return true; } - verbose() { + isVerbose() { return false; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3cb66fed297..970286f29fa 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -5,7 +5,7 @@ module ts.server { export interface Logger { close(): void; - verbose(): boolean; + isVerbose(): boolean; enabled(): boolean; perftrc(s: string): void; info(s: string): void; diff --git a/src/server/server.ts b/src/server/server.ts index e5cc0fd398d..1735a2dddec 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -55,7 +55,7 @@ module ts.server { return !!this.logFilename; } - verbose() { + isVerbose() { return this.enabled() && (this.level == "verbose"); } @@ -191,15 +191,15 @@ module ts.server { } } - interface LogEnv { + interface LogOptions { file?: string; - level?: string; + detailLevel?: string; } - function parseLogEnv(logEnvStr: string): LogEnv { - var logEnv: LogEnv = {}; + function parseLogEnv(logEnvStr: string): LogOptions { + var logEnv: LogOptions = {}; var args = logEnvStr.split(' '); - for (var i = 0, len = args.length; i < len; i++) { + for (var i = 0, len = args.length; i < (len - 1); i += 2) { var option = args[i]; var value = args[i + 1]; if (option && value) { @@ -208,7 +208,7 @@ module ts.server { logEnv.file = value; break; case "-level": - logEnv.level = value; + logEnv.detailLevel = value; break; } } @@ -219,7 +219,7 @@ module ts.server { // TSS_LOG "{ level: "normal | verbose | terse", file?: string}" function createLoggerFromEnv() { var fileName: string = undefined; - var level = "normal"; + var detailLevel = "normal"; var logEnvStr = process.env["TSS_LOG"]; if (logEnvStr) { var logEnv = parseLogEnv(logEnvStr); @@ -229,12 +229,11 @@ module ts.server { else { fileName = __dirname + "/.log" + process.pid.toString(); } - if (logEnv.level) { - level = logEnv.level; + if (logEnv.detailLevel) { + detailLevel = logEnv.detailLevel; } } - var logger = new Logger(fileName, level); - return logger; + return new Logger(fileName, detailLevel); } // This places log file in the directory containing editorServices.js // TODO: check that this location is writable diff --git a/src/server/session.ts b/src/server/session.ts index 59a049a18e9..ee828642830 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -145,8 +145,8 @@ module ts.server { send(msg: NodeJS._debugger.Message) { var json = JSON.stringify(msg); - if (this.logger.verbose()) { - this.logger.info(msg.type+": " + json); + if (this.logger.isVerbose()) { + this.logger.info(msg.type + ": " + json); } this.sendLineToClient('Content-Length: ' + (1 + Buffer.byteLength(json, 'utf8')) + '\r\n\r\n' + json); @@ -720,7 +720,7 @@ module ts.server { } onMessage(message: string) { - if (this.logger.verbose()) { + if (this.logger.isVerbose()) { this.logger.info("request: " + message); var start = process.hrtime(); } @@ -829,14 +829,16 @@ module ts.server { } } - if (this.logger.verbose()) { + if (this.logger.isVerbose()) { var elapsed = process.hrtime(start); - var elapsedms = ((1e9 * elapsed[0]) + elapsed[1])/1000000.0; + var seconds = elapsed[0] + var nanoseconds = elapsed[1]; + var elapsedMs = ((1e9 * seconds) + nanoseconds)/1000000.0; var leader = "Elapsed time (in milliseconds)"; if (!responseRequired) { leader = "Async elapsed time (in milliseconds)"; } - this.logger.msg(leader+": " + elapsedms.toFixed(4).toString(), "Perf"); + this.logger.msg(leader + ": " + elapsedMs.toFixed(4).toString(), "Perf"); } if (response) { this.output(response, request.command, request.seq); From 6340531a16a1a98bfe7f3143eb9bf1f8fea5ea1a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 8 Mar 2015 23:07:03 -0700 Subject: [PATCH 05/13] Simplify indentation code in the emitter. --- src/compiler/emitter.ts | 91 ++++++++++++----------------------------- 1 file changed, 27 insertions(+), 64 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c7f132519ae..3b3c4e27eaf 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3033,7 +3033,7 @@ module ts { return false; } - function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node) { + function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, insertIfNoIndentValue?: string) { // Use a newline for existin code if the original had one, and we're preserving formatting. var realNodesAreOnDifferentLines = preserveNewLines && !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2); @@ -3045,8 +3045,12 @@ module ts { writeLine(); return true; } - - return false; + else { + if (insertIfNoIndentValue) { + write(insertIfNoIndentValue); + } + return false; + } } function emitPropertyAccess(node: PropertyAccessExpression) { @@ -3056,17 +3060,14 @@ module ts { emit(node.expression); - var indented = indentIfOnDifferentLines(node, node.expression, node.dotToken); - + var indentBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken); write("."); - indented = indented || indentIfOnDifferentLines(node, node.dotToken, node.name); - + var indentAfterDot = indentIfOnDifferentLines(node, node.dotToken, node.name); emit(node.name); - if (indented) { - decreaseIndent(); - } + decreaseIndentIf(indentBeforeDot); + decreaseIndentIf(indentAfterDot); } function emitQualifiedName(node: QualifiedName) { @@ -3300,32 +3301,14 @@ module ts { else { emit(node.left); - // If there was a newline between the left side of the binary expression and the - // operator, then try to preserve that. - var indented1 = indentIfOnDifferentLines(node, node.left, node.operatorToken); - - // Otherwise just emit the operator right afterwards. For everything but - // comma, emit a space before the operator. - if (!indented1 && node.operatorToken.kind !== SyntaxKind.CommaToken) { - write(" "); - } - + var indentBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined); write(tokenToString(node.operatorToken.kind)); - if (!indented1) { - var indented2 = indentIfOnDifferentLines(node, node.operatorToken, node.right); - } - - if (!indented2) { - write(" "); - } - + var indentAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " "); emit(node.right); - // If we indented the left or the right side, then dedent now. - if (indented1 || indented2) { - decreaseIndent(); - } + decreaseIndentIf(indentBeforeOperator); + decreaseIndentIf(indentAfterOperator); } } @@ -3335,43 +3318,23 @@ module ts { function emitConditionalExpression(node: ConditionalExpression) { emit(node.condition); - var indent1 = indentIfOnDifferentLines(node, node.condition, node.questionToken); - if (!indent1) { - write(" "); - } - + var indentBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " "); write("?"); - - if (!indent1) { - var indent2 = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue); - } - - if (!indent2) { - write(" "); - } - + var indentAfterQuestion = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue, " "); emit(node.whenTrue); + decreaseIndentIf(indentBeforeQuestion); + decreaseIndentIf(indentAfterQuestion); - if (indent1 || indent2) { - decreaseIndent(); - } - - var indent3 = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken); - if (!indent3) { - write(" "); - } - + var indentBeforeColon = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken, " "); write(":"); - if (!indent3) { - var indent4 = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse); - } - - if (!indent4) { - write(" "); - } - + var indentAfterColon = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse, " "); emit(node.whenFalse); - if (indent3 || indent4) { + decreaseIndentIf(indentBeforeColon); + decreaseIndentIf(indentAfterColon); + } + + function decreaseIndentIf(value: boolean) { + if (value) { decreaseIndent(); } } From 35b56aa69b18de3a023c68734372cbd84ca1f341 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 9 Mar 2015 01:07:02 -0700 Subject: [PATCH 06/13] Further simplification. --- src/compiler/emitter.ts | 48 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3b3c4e27eaf..6be76908131 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3033,7 +3033,7 @@ module ts { return false; } - function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, insertIfNoIndentValue?: string) { + function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, valueToWriteWhenNotIndenting?: string) { // Use a newline for existin code if the original had one, and we're preserving formatting. var realNodesAreOnDifferentLines = preserveNewLines && !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2); @@ -3046,8 +3046,8 @@ module ts { return true; } else { - if (insertIfNoIndentValue) { - write(insertIfNoIndentValue); + if (valueToWriteWhenNotIndenting) { + write(valueToWriteWhenNotIndenting); } return false; } @@ -3059,15 +3059,11 @@ module ts { } emit(node.expression); - - var indentBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken); + var indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken); write("."); - - var indentAfterDot = indentIfOnDifferentLines(node, node.dotToken, node.name); + var indentedAfterDot = indentIfOnDifferentLines(node, node.dotToken, node.name); emit(node.name); - - decreaseIndentIf(indentBeforeDot); - decreaseIndentIf(indentAfterDot); + decreaseIndentIf(indentedBeforeDot, indentedAfterDot); } function emitQualifiedName(node: QualifiedName) { @@ -3300,15 +3296,11 @@ module ts { } else { emit(node.left); - - var indentBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined); + var indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined); write(tokenToString(node.operatorToken.kind)); - - var indentAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " "); + var indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " "); emit(node.right); - - decreaseIndentIf(indentBeforeOperator); - decreaseIndentIf(indentAfterOperator); + decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); } } @@ -3318,23 +3310,23 @@ module ts { function emitConditionalExpression(node: ConditionalExpression) { emit(node.condition); - var indentBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " "); + var indentedBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " "); write("?"); - var indentAfterQuestion = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue, " "); + var indentedAfterQuestion = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue, " "); emit(node.whenTrue); - decreaseIndentIf(indentBeforeQuestion); - decreaseIndentIf(indentAfterQuestion); - - var indentBeforeColon = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken, " "); + decreaseIndentIf(indentedBeforeQuestion, indentedAfterQuestion); + var indentedBeforeColon = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken, " "); write(":"); - var indentAfterColon = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse, " "); + var indentedAfterColon = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse, " "); emit(node.whenFalse); - decreaseIndentIf(indentBeforeColon); - decreaseIndentIf(indentAfterColon); + decreaseIndentIf(indentedBeforeColon, indentedAfterColon); } - function decreaseIndentIf(value: boolean) { - if (value) { + function decreaseIndentIf(value1: boolean, value2?: boolean) { + if (value1) { + decreaseIndent(); + } + if (value2) { decreaseIndent(); } } From 04320e415e68841f3a517bb0a3616c78908750f2 Mon Sep 17 00:00:00 2001 From: steveluc Date: Mon, 9 Mar 2015 01:23:03 -0700 Subject: [PATCH 07/13] Renames and comments as requested by feedback. --- src/harness/harnessLanguageService.ts | 2 +- src/server/editorServices.ts | 2 +- src/server/server.ts | 8 ++++---- src/server/session.ts | 8 +++++++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 55b3464a384..68ac8d916fb 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -528,7 +528,7 @@ module Harness.LanguageService { return this.host.log(message); } - enabled() { + loggingEnabled() { return true; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 970286f29fa..dd5f74cfff2 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -6,7 +6,7 @@ module ts.server { export interface Logger { close(): void; isVerbose(): boolean; - enabled(): boolean; + loggingEnabled(): boolean; perftrc(s: string): void; info(s: string): void; startGroup(): void; diff --git a/src/server/server.ts b/src/server/server.ts index 1735a2dddec..4c13be80c4c 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -51,12 +51,12 @@ module ts.server { this.firstInGroup = true; } - enabled() { + loggingEnabled() { return !!this.logFilename; } isVerbose() { - return this.enabled() && (this.level == "verbose"); + return this.loggingEnabled() && (this.level == "verbose"); } @@ -196,7 +196,7 @@ module ts.server { detailLevel?: string; } - function parseLogEnv(logEnvStr: string): LogOptions { + function parseLoggingEnvironmentString(logEnvStr: string): LogOptions { var logEnv: LogOptions = {}; var args = logEnvStr.split(' '); for (var i = 0, len = args.length; i < (len - 1); i += 2) { @@ -222,7 +222,7 @@ module ts.server { var detailLevel = "normal"; var logEnvStr = process.env["TSS_LOG"]; if (logEnvStr) { - var logEnv = parseLogEnv(logEnvStr); + var logEnv = parseLoggingEnvironmentString(logEnvStr); if (logEnv.file) { fileName = logEnv.file; } diff --git a/src/server/session.ts b/src/server/session.ts index ee828642830..cc705d64d5e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -464,7 +464,13 @@ module ts.server { var compilerService = project.compilerService; var position = compilerService.host.lineColToPosition(file, line, col); var edits = compilerService.languageService.getFormattingEditsAfterKeystroke(file, position, key, - compilerService.formatCodeOptions); + compilerService.formatCodeOptions); + // Check whether we should auto-indent. This will be when + // the position is on a line containing only whitespace. + // This should leave the edits returned from + // getFormattingEditsAfterKeytroke either empty or pertaining + // only to the previous line. If all this is true, then + // add edits necessary to properly indent the current line. if ((key == "\n") && ((!edits) || (edits.length == 0) || allEditsBeforePos(edits, position))) { var scriptInfo = compilerService.host.getScriptInfo(file); if (scriptInfo) { From 37c6f69f015c3da3c709e2bae0786aa55a10a045 Mon Sep 17 00:00:00 2001 From: steveluc Date: Mon, 9 Mar 2015 11:57:50 -0700 Subject: [PATCH 08/13] Hold at most 4 snapshots per file in the server. This is about 1K per file. --- src/server/editorServices.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index dd5f74cfff2..7c9ccfa7854 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1073,6 +1073,7 @@ module ts.server { static changeNumberThreshold = 8; static changeLengthThreshold = 256; + static maxVersions = 8; // REVIEW: can optimize by coalescing simple edits edit(pos: number, deleteLen: number, insertedText?: string) { @@ -1133,6 +1134,13 @@ module ts.server { this.currentVersion = snap.version; this.versions[snap.version] = snap; this.changes = []; + if ((this.currentVersion - this.minVersion) >= ScriptVersionCache.maxVersions) { + var oldMin = this.minVersion; + this.minVersion = (this.currentVersion - ScriptVersionCache.maxVersions) + 1; + for (var j = oldMin; j < this.minVersion; j++) { + this.versions[j] = undefined; + } + } } return snap; } From bfbc18d6d7d69f51d3652c1c7d0e931dd9c085d9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 9 Mar 2015 15:01:02 -0700 Subject: [PATCH 09/13] rename emitNode to emitNodeWithoutSourceMap --- src/compiler/emitter.ts | 47 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index df6a68bae02..8b48b8ad611 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1609,7 +1609,7 @@ module ts { var writeComment = writeCommentRange; /** Emit a node */ - var emit = emitNode; + var emit = emitNodeWithoutSourceMap; /** Called just before starting emit of a node */ var emitStart = function (node: Node) { }; @@ -2065,12 +2065,12 @@ module ts { if (node) { if (node.kind != SyntaxKind.SourceFile) { recordEmitNodeStartSpan(node); - emitNode(node); + emitNodeWithoutSourceMap(node); recordEmitNodeEndSpan(node); } else { recordNewSourceFileStart(node); - emitNode(node); + emitNodeWithoutSourceMap(node); } } } @@ -3033,7 +3033,10 @@ module ts { return false; } - function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, valueToWriteWhenNotIndenting?: string) { + // Returns 'true' if the code was actually indented, false otherwise. + // If the code is not indented, an optional valueToWriteWhenNotIndenting will be + // emitted instead. + function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, valueToWriteWhenNotIndenting?: string): boolean { var realNodesAreOnDifferentLines = preserveNewLines && !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2); // Always use a newline for synthesized code if the synthesizer desires it. @@ -3321,6 +3324,10 @@ module ts { decreaseIndentIf(indentedBeforeColon, indentedAfterColon); } + // Helper function to decrease the indent if we previously indented. Allows multiple + // previous indent values to be considered at a time. This also allows caller to just + // call this once, passing in all their appropriate indent values, instead of needing + // to call this helper function multiple times. function decreaseIndentIf(value1: boolean, value2?: boolean) { if (value1) { decreaseIndent(); @@ -3624,7 +3631,7 @@ module ts { emitContainingModuleName(node); write("."); } - emitNode(node.name); + emitNodeWithoutSourceMap(node.name); emitEnd(node.name); } @@ -3643,10 +3650,10 @@ module ts { emitStart(specifier.name); emitContainingModuleName(specifier); write("."); - emitNode(specifier.name); + emitNodeWithoutSourceMap(specifier.name); emitEnd(specifier.name); write(" = "); - emitNode(name); + emitNodeWithoutSourceMap(name); write(";"); }); } @@ -4039,14 +4046,14 @@ module ts { writeLine(); emitStart(p); write("if ("); - emitNode(p.name); + emitNodeWithoutSourceMap(p.name); write(" === void 0)"); emitEnd(p); write(" { "); emitStart(p); - emitNode(p.name); + emitNodeWithoutSourceMap(p.name); write(" = "); - emitNode(p.initializer); + emitNodeWithoutSourceMap(p.initializer); emitEnd(p); write("; }"); } @@ -4063,7 +4070,7 @@ module ts { emitLeadingComments(restParam); emitStart(restParam); write("var "); - emitNode(restParam.name); + emitNodeWithoutSourceMap(restParam.name); write(" = [];"); emitEnd(restParam); emitTrailingComments(restParam); @@ -4084,7 +4091,7 @@ module ts { increaseIndent(); writeLine(); emitStart(restParam); - emitNode(restParam.name); + emitNodeWithoutSourceMap(restParam.name); write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];"); emitEnd(restParam); decreaseIndent(); @@ -4105,7 +4112,7 @@ module ts { function emitDeclarationName(node: Declaration) { if (node.name) { - emitNode(node.name); + emitNodeWithoutSourceMap(node.name); } else { write(resolver.getGeneratedNameForNode(node)); @@ -4266,7 +4273,7 @@ module ts { // Don't emit comments on this body. We'll have already taken care of it above // when we called emitDetachedComments. - emitNode(body, /*disableComments:*/ true); + emitNodeWithoutSourceMap(body, /*disableComments:*/ true); emitEnd(body); write(";"); emitTempDeclarations(/*newLine*/ false); @@ -4355,7 +4362,7 @@ module ts { emitStart(param); emitStart(param.name); write("this."); - emitNode(param.name); + emitNodeWithoutSourceMap(param.name); emitEnd(param.name); write(" = "); emit(param.name); @@ -4369,7 +4376,7 @@ module ts { // TODO: (jfreeman,drosen): comment on why this is emitNode instead of emit here. if (memberName.kind === SyntaxKind.StringLiteral || memberName.kind === SyntaxKind.NumericLiteral) { write("["); - emitNode(memberName); + emitNodeWithoutSourceMap(memberName); write("]"); } else if (memberName.kind === SyntaxKind.ComputedPropertyName) { @@ -4377,7 +4384,7 @@ module ts { } else { write("."); - emitNode(memberName); + emitNodeWithoutSourceMap(memberName); } } @@ -4870,11 +4877,11 @@ module ts { emitStart(specifier); emitContainingModuleName(specifier); write("."); - emitNode(specifier.name); + emitNodeWithoutSourceMap(specifier.name); write(" = "); write(generatedName); write("."); - emitNode(specifier.propertyName || specifier.name); + emitNodeWithoutSourceMap(specifier.propertyName || specifier.name); write(";"); emitEnd(specifier); }); @@ -5139,7 +5146,7 @@ module ts { emitLeadingComments(node.endOfFileToken); } - function emitNode(node: Node, disableComments?:boolean): void { + function emitNodeWithoutSourceMap(node: Node, disableComments?:boolean): void { if (!node) { return; } From 4c7891ce09a7e959bb461a8a9f0577ce886e9363 Mon Sep 17 00:00:00 2001 From: Yui T Date: Mon, 9 Mar 2015 15:10:47 -0700 Subject: [PATCH 10/13] Fix file path resolution in RWC --- src/harness/compilerRunner.ts | 2 +- src/harness/harness.ts | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index e4979f7a3c4..e920cfe3aca 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -152,7 +152,7 @@ class CompilerBaselineRunner extends RunnerBase { if (this.errors) { Harness.Baseline.runBaseline('Correct errors for ' + fileName, justName.replace(/\.ts$/, '.errors.txt'), (): string => { if (result.errors.length === 0) return null; - + debugger; return getErrorBaseline(toBeCompiled, otherFiles, result); }); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 8cbc48d9dce..90ffcef07ff 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -835,7 +835,7 @@ module Harness { // Register input files function register(file: { unitName: string; content: string; }) { if (file.content !== undefined) { - var fileName = ts.normalizeSlashes(file.unitName); + var fileName = ts.normalizePath(file.unitName); filemap[getCanonicalFileName(fileName)] = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget); } }; @@ -844,6 +844,7 @@ module Harness { return { getCurrentDirectory, getSourceFile: (fn, languageVersion) => { + fn = ts.normalizePath(fn); if (Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(fn))) { return filemap[getCanonicalFileName(fn)]; } @@ -1078,16 +1079,6 @@ module Harness { } }); - var filemap: { [name: string]: ts.SourceFile; } = {}; - var register = (file: { unitName: string; content: string; }) => { - if (file.content !== undefined) { - var fileName = ts.normalizeSlashes(file.unitName); - filemap[getCanonicalFileName(fileName)] = createSourceFileAndAssertInvariants(fileName, file.content, options.target, assertInvariants); - } - }; - inputFiles.forEach(register); - otherFiles.forEach(register); - var fileOutputs: GeneratedFile[] = []; var programFiles = inputFiles.map(file => file.unitName); From 725577e32863f45b56a9385c27e041125424e67e Mon Sep 17 00:00:00 2001 From: Yui T Date: Mon, 9 Mar 2015 15:14:08 -0700 Subject: [PATCH 11/13] Remove deubgger statement --- src/harness/compilerRunner.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index e920cfe3aca..d4dd8e31258 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -152,7 +152,6 @@ class CompilerBaselineRunner extends RunnerBase { if (this.errors) { Harness.Baseline.runBaseline('Correct errors for ' + fileName, justName.replace(/\.ts$/, '.errors.txt'), (): string => { if (result.errors.length === 0) return null; - debugger; return getErrorBaseline(toBeCompiled, otherFiles, result); }); } From 31cf443a284c7e3296bbec01a0a71929f1d8c08c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 9 Mar 2015 15:47:23 -0700 Subject: [PATCH 12/13] Add specialized codepath for emitting without comments. --- src/compiler/emitter.ts | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8b48b8ad611..8947314a6f6 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1600,16 +1600,16 @@ module ts { var emitLeadingCommentsOfPosition = compilerOptions.removeComments ? (pos: number) => { } : emitLeadingCommentsOfLocalPosition; var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number }[]; + /** Emit detached comments of the node */ var emitDetachedComments = compilerOptions.removeComments ? (node: TextRange) => { } : emitDetachedCommentsAtPosition; - /** Emits /// or pinned which is comment starting with /*! comments */ - var emitPinnedOrTripleSlashComments = compilerOptions.removeComments ? (node: Node) => { } : emitPinnedOrTripleSlashCommentsOfNode; - var writeComment = writeCommentRange; /** Emit a node */ + var emitNodeWithoutSourceMap = compilerOptions.removeComments ? emitNodeWithoutSourceMapWithoutComments : emitNodeWithoutSourceMapWithComments; var emit = emitNodeWithoutSourceMap; + var emitWithoutComments = emitNodeWithoutSourceMapWithoutComments; /** Called just before starting emit of a node */ var emitStart = function (node: Node) { }; @@ -2061,7 +2061,7 @@ module ts { sourceMapDir = getDirectoryPath(normalizePath(jsFilePath)); } - function emitNodeWithMap(node: Node) { + function emitNodeWithSourceMap(node: Node) { if (node) { if (node.kind != SyntaxKind.SourceFile) { recordEmitNodeStartSpan(node); @@ -2075,8 +2075,17 @@ module ts { } } + function emitNodeWithSourceMapWithoutComments(node: Node) { + if (node) { + recordEmitNodeStartSpan(node); + emitNodeWithoutSourceMapWithoutComments(node); + recordEmitNodeEndSpan(node); + } + } + writeEmittedFiles = writeJavaScriptAndSourceMapFile; - emit = emitNodeWithMap; + emit = emitNodeWithSourceMap; + emitWithoutComments = emitNodeWithSourceMapWithoutComments; emitStart = recordEmitNodeStartSpan; emitEnd = recordEmitNodeEndSpan; emitToken = writeTextWithSpanRecord; @@ -4273,7 +4282,7 @@ module ts { // Don't emit comments on this body. We'll have already taken care of it above // when we called emitDetachedComments. - emitNodeWithoutSourceMap(body, /*disableComments:*/ true); + emitWithoutComments(body); emitEnd(body); write(";"); emitTempDeclarations(/*newLine*/ false); @@ -4284,7 +4293,10 @@ module ts { writeLine(); emitLeadingComments(node.body); write("return "); - emit(node.body, /*disableComments:*/ true); + + // Don't emit comments on this body. We'll have already taken care of it above + // when we called emitDetachedComments. + emitWithoutComments(node.body); write(";"); emitTrailingComments(node.body); @@ -5146,7 +5158,7 @@ module ts { emitLeadingComments(node.endOfFileToken); } - function emitNodeWithoutSourceMap(node: Node, disableComments?:boolean): void { + function emitNodeWithoutSourceMapWithComments(node: Node): void { if (!node) { return; } @@ -5155,7 +5167,7 @@ module ts { return emitPinnedOrTripleSlashComments(node); } - var emitComments = !disableComments && shouldEmitLeadingAndTrailingComments(node); + var emitComments = shouldEmitLeadingAndTrailingComments(node); if (emitComments) { emitLeadingComments(node); } @@ -5167,6 +5179,18 @@ module ts { } } + function emitNodeWithoutSourceMapWithoutComments(node: Node): void { + if (!node) { + return; + } + + if (node.flags & NodeFlags.Ambient) { + return emitPinnedOrTripleSlashComments(node); + } + + emitJavaScriptWorker(node); + } + function shouldEmitLeadingAndTrailingComments(node: Node) { switch (node.kind) { // All of these entities are emitted in a specialized fashion. As such, we allow @@ -5461,7 +5485,8 @@ module ts { } } - function emitPinnedOrTripleSlashCommentsOfNode(node: Node) { + /** Emits /// or pinned which is comment starting with /*! comments */ + function emitPinnedOrTripleSlashComments(node: Node) { var pinnedComments = ts.filter(getLeadingCommentsToEmit(node), isPinnedOrTripleSlashComment); function isPinnedOrTripleSlashComment(comment: CommentRange) { From aa96475f73bcc619f63dea84e0c391ce0b6ceb43 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 9 Mar 2015 15:50:40 -0700 Subject: [PATCH 13/13] CR feedback. --- src/compiler/emitter.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index df6a68bae02..2b5eb62bef9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3033,6 +3033,9 @@ module ts { return false; } + // Returns 'true' if the code was actually indented, false otherwise. + // If the code is not indented, an optional valueToWriteWhenNotIndenting will be + // emitted instead. function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, valueToWriteWhenNotIndenting?: string) { var realNodesAreOnDifferentLines = preserveNewLines && !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2); @@ -3321,6 +3324,10 @@ module ts { decreaseIndentIf(indentedBeforeColon, indentedAfterColon); } + // Helper function to decrease the indent if we previously indented. Allows multiple + // previous indent values to be considered at a time. This also allows caller to just + // call this once, passing in all their appropriate indent values, instead of needing + // to call this helper function multiple times. function decreaseIndentIf(value1: boolean, value2?: boolean) { if (value1) { decreaseIndent();