Merge pull request #2258 from Microsoft/formatLine

TS Server format line fixes
This commit is contained in:
Steve Lucco 2015-03-09 12:52:14 -07:00
commit 5556683227
4 changed files with 140 additions and 18 deletions

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