Merge branch 'master' of https://github.com/Microsoft/TypeScript into for-ofES5

This commit is contained in:
Jason Freeman 2015-03-09 19:45:37 -07:00
commit 1349a196a0
7 changed files with 226 additions and 128 deletions

View File

@ -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 emit = emitNode;
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) { };
@ -2071,25 +2071,34 @@ module ts {
sourceMapDir = getDirectoryPath(normalizePath(jsFilePath));
}
function emitNodeWithMap(node: Node) {
function emitNodeWithSourceMap(node: Node) {
if (node) {
if (nodeIsSynthesized(node)) {
return emitNode(node);
}
if (node.kind != SyntaxKind.SourceFile) {
recordEmitNodeStartSpan(node);
emitNode(node);
emitNodeWithoutSourceMap(node);
recordEmitNodeEndSpan(node);
}
else {
recordNewSourceFileStart(<SourceFile>node);
emitNode(node);
emitNodeWithoutSourceMap(node);
}
}
}
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;
@ -3058,8 +3067,10 @@ module ts {
return false;
}
function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node) {
// Use a newline for existing code if the original had one, and we're preserving formatting.
// 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.
@ -3070,8 +3081,12 @@ module ts {
writeLine();
return true;
}
return false;
else {
if (valueToWriteWhenNotIndenting) {
write(valueToWriteWhenNotIndenting);
}
return false;
}
}
function emitPropertyAccess(node: PropertyAccessExpression) {
@ -3080,18 +3095,11 @@ module ts {
}
emit(node.expression);
var indented = indentIfOnDifferentLines(node, node.expression, node.dotToken);
var indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken);
write(".");
indented = indented || indentIfOnDifferentLines(node, node.dotToken, node.name);
var indentedAfterDot = indentIfOnDifferentLines(node, node.dotToken, node.name);
emit(node.name);
if (indented) {
decreaseIndent();
}
decreaseIndentIf(indentedBeforeDot, indentedAfterDot);
}
function emitQualifiedName(node: QualifiedName) {
@ -3324,33 +3332,11 @@ 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 indentedBeforeOperator = 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 indentedAfterOperator = 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(indentedBeforeOperator, indentedAfterOperator);
}
}
@ -3360,43 +3346,27 @@ module ts {
function emitConditionalExpression(node: ConditionalExpression) {
emit(node.condition);
var indent1 = indentIfOnDifferentLines(node, node.condition, node.questionToken);
if (!indent1) {
write(" ");
}
var indentedBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " ");
write("?");
if (!indent1) {
var indent2 = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue);
}
if (!indent2) {
write(" ");
}
var indentedAfterQuestion = indentIfOnDifferentLines(node, node.questionToken, node.whenTrue, " ");
emit(node.whenTrue);
decreaseIndentIf(indentedBeforeQuestion, indentedAfterQuestion);
var indentedBeforeColon = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken, " ");
write(":");
var indentedAfterColon = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse, " ");
emit(node.whenFalse);
decreaseIndentIf(indentedBeforeColon, indentedAfterColon);
}
if (indent1 || indent2) {
// 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();
}
var indent3 = indentIfOnDifferentLines(node, node.whenTrue, node.colonToken);
if (!indent3) {
write(" ");
}
write(":");
if (!indent3) {
var indent4 = indentIfOnDifferentLines(node, node.colonToken, node.whenFalse);
}
if (!indent4) {
write(" ");
}
emit(node.whenFalse);
if (indent3 || indent4) {
if (value2) {
decreaseIndent();
}
}
@ -3839,7 +3809,7 @@ module ts {
emitContainingModuleName(node);
write(".");
}
emitNode(node.name);
emitNodeWithoutSourceMap(node.name);
emitEnd(node.name);
}
@ -3858,10 +3828,10 @@ module ts {
emitStart(specifier.name);
emitContainingModuleName(specifier);
write(".");
emitNode(specifier.name);
emitNodeWithoutSourceMap(specifier.name);
emitEnd(specifier.name);
write(" = ");
emitNode(name);
emitNodeWithoutSourceMap(name);
write(";");
});
}
@ -4267,14 +4237,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("; }");
}
@ -4291,7 +4261,7 @@ module ts {
emitLeadingComments(restParam);
emitStart(restParam);
write("var ");
emitNode(restParam.name);
emitNodeWithoutSourceMap(restParam.name);
write(" = [];");
emitEnd(restParam);
emitTrailingComments(restParam);
@ -4312,7 +4282,7 @@ module ts {
increaseIndent();
writeLine();
emitStart(restParam);
emitNode(restParam.name);
emitNodeWithoutSourceMap(restParam.name);
write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];");
emitEnd(restParam);
decreaseIndent();
@ -4333,7 +4303,7 @@ module ts {
function emitDeclarationName(node: Declaration) {
if (node.name) {
emitNode(node.name);
emitNodeWithoutSourceMap(node.name);
}
else {
write(resolver.getGeneratedNameForNode(node));
@ -4494,7 +4464,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);
emitWithoutComments(body);
emitEnd(body);
write(";");
emitTempDeclarations(/*newLine*/ false);
@ -4505,7 +4475,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);
@ -4583,7 +4556,7 @@ module ts {
emitStart(param);
emitStart(param.name);
write("this.");
emitNode(param.name);
emitNodeWithoutSourceMap(param.name);
emitEnd(param.name);
write(" = ");
emit(param.name);
@ -4597,7 +4570,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) {
@ -4605,7 +4578,7 @@ module ts {
}
else {
write(".");
emitNode(memberName);
emitNodeWithoutSourceMap(memberName);
}
}
@ -5098,11 +5071,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);
});
@ -5366,7 +5339,7 @@ module ts {
emitLeadingComments(node.endOfFileToken);
}
function emitNode(node: Node, disableComments?:boolean): void {
function emitNodeWithoutSourceMapWithComments(node: Node): void {
if (!node) {
return;
}
@ -5375,7 +5348,7 @@ module ts {
return emitPinnedOrTripleSlashComments(node);
}
var emitComments = !disableComments && shouldEmitLeadingAndTrailingComments(node);
var emitComments = shouldEmitLeadingAndTrailingComments(node);
if (emitComments) {
emitLeadingComments(node);
}
@ -5387,6 +5360,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
@ -5681,7 +5666,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) {

View File

@ -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;
return getErrorBaseline(toBeCompiled, otherFiles, result);
});
}

View File

@ -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);

View File

@ -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);
}
loggingEnabled() {
return true;
}
isVerbose() {
return false;
}
endGroup(): void {
}

View File

@ -5,6 +5,8 @@
module ts.server {
export interface Logger {
close(): void;
isVerbose(): boolean;
loggingEnabled(): boolean;
perftrc(s: string): void;
info(s: string): void;
startGroup(): void;
@ -1071,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) {
@ -1131,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;
}

View File

@ -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;
}
loggingEnabled() {
return !!this.logFilename;
}
isVerbose() {
return this.loggingEnabled() && (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,61 @@ module ts.server {
});
rl.on('close',() => {
this.projectService.closeLog();
this.projectService.log("Exiting...");
this.projectService.closeLog();
process.exit(0);
});
}
}
interface LogOptions {
file?: string;
detailLevel?: string;
}
function parseLoggingEnvironmentString(logEnvStr: string): LogOptions {
var logEnv: LogOptions = {};
var args = logEnvStr.split(' ');
for (var i = 0, len = args.length; i < (len - 1); i += 2) {
var option = args[i];
var value = args[i + 1];
if (option && value) {
switch (option) {
case "-file":
logEnv.file = value;
break;
case "-level":
logEnv.detailLevel = value;
break;
}
}
}
return logEnv;
}
// TSS_LOG "{ level: "normal | verbose | terse", file?: string}"
function createLoggerFromEnv() {
var fileName: string = undefined;
var detailLevel = "normal";
var logEnvStr = process.env["TSS_LOG"];
if (logEnvStr) {
var logEnv = parseLoggingEnvironmentString(logEnvStr);
if (logEnv.file) {
fileName = logEnv.file;
}
else {
fileName = __dirname + "/.log" + process.pid.toString();
}
if (logEnv.detailLevel) {
detailLevel = logEnv.detailLevel;
}
}
return new Logger(fileName, detailLevel);
}
// 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

View File

@ -145,6 +145,9 @@ module ts.server {
send(msg: NodeJS._debugger.Message) {
var json = JSON.stringify(msg);
if (this.logger.isVerbose()) {
this.logger.info(msg.type + ": " + json);
}
this.sendLineToClient('Content-Length: ' + (1 + Buffer.byteLength(json, 'utf8')) +
'\r\n\r\n' + json);
}
@ -461,19 +464,49 @@ 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))) {
// 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 +524,7 @@ module ts.server {
};
});
}
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
if (!prefix) {
prefix = "";
@ -693,6 +726,10 @@ module ts.server {
}
onMessage(message: string) {
if (this.logger.isVerbose()) {
this.logger.info("request: " + message);
var start = process.hrtime();
}
try {
var request = <protocol.Request>JSON.parse(message);
var response: any;
@ -798,13 +835,23 @@ module ts.server {
}
}
if (this.logger.isVerbose()) {
var elapsed = process.hrtime(start);
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");
}
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