Fixes #10823: Improve error reporting in tsserver.

This commit is contained in:
Dirk Baeumer 2016-08-23 10:56:57 +02:00
parent a58be75c43
commit 1897d17bce
12 changed files with 93 additions and 20 deletions

View file

@ -10,7 +10,7 @@ import { CompletionItem, TextDocument, Position, CompletionItemKind, CompletionI
import { ITypescriptServiceClient } from '../typescriptService';
import * as PConst from '../protocol.const';
import { CompletionEntry, CompletionsRequestArgs, CompletionsResponse, CompletionDetailsRequestArgs, CompletionDetailsResponse, CompletionEntryDetails } from '../protocol';
import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails } from '../protocol';
import * as Previewer from './previewer';
class MyCompletionItem extends CompletionItem {
@ -125,8 +125,8 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
}
return completionItems;
}, (err: CompletionsResponse) => {
}, (err) => {
this.client.error(`'completions' request failed with error.`, err);
return [];
});
}
@ -168,7 +168,8 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
return item;
}, (err: CompletionDetailsResponse) => {
}, (err) => {
this.client.error(`'completionEntryDetails' request failed with error.`, err);
return item;
});

View file

@ -42,7 +42,8 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider
return new Location(resource, new Range(location.start.line - 1, location.start.offset - 1, location.end.line - 1, location.end.offset - 1));
}
});
}, () => {
}, (error) => {
this.client.error(`'definition' request failed with error.`, error);
return null;
});
}

View file

@ -37,6 +37,7 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh
});
}
}, (err) => {
this.client.error(`'occurrences' request failed with error.`, err);
return [];
});
}

View file

@ -69,6 +69,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
return result;
}
}, (err) => {
this.client.error(`'navbar' request failed with error.`, err);
return [];
});
}

View file

@ -102,6 +102,7 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
return this.client.execute('format', args, token).then((response): TextEdit[] => {
return response.body.map(this.codeEdit2SingleEditOperation);
}, (err: any) => {
this.client.error(`'format' request failed with error.`, err);
return [];
});
});
@ -149,6 +150,7 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
}
return result;
}, (err: any) => {
this.client.error(`'formatonkey' request failed with error.`, err);
return [];
});
});

View file

@ -35,6 +35,7 @@ export default class TypeScriptHoverProvider implements HoverProvider {
new Range(data.start.line - 1, data.start.offset - 1, data.end.line - 1, data.end.offset - 1));
}
}, (err) => {
this.client.error(`'quickinfo' request failed with error.`, err);
return null;
});
}

View file

@ -46,7 +46,8 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider {
result.push(location);
}
return result;
}, () => {
}, (err) => {
this.client.error(`'references' request failed with error.`, err);
return [];
});
}

View file

@ -54,6 +54,7 @@ export default class TypeScriptRenameProvider implements RenameProvider {
});
return result;
}, (err) => {
this.client.error(`'rename' request failed with error.`, err);
return null;
});
}

View file

@ -70,6 +70,7 @@ export default class TypeScriptSignatureHelpProvider implements SignatureHelpPro
return result;
}, (err: any) => {
this.client.error(`'signatureHelp' request failed with error.`, err);
return null;
});
}

View file

@ -74,6 +74,7 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
}
}, (err) => {
this.client.error(`'navto' request failed with error.`, err);
return [];
});
}

View file

@ -46,6 +46,10 @@ export interface ITypescriptServiceClient {
asAbsolutePath(resource: Uri): string;
asUrl(filepath: string): Uri;
info(message: string, data?: any): void;
warn(message: string, data?: any): void;
error(message: string, data?: any): void;
logTelemetry(eventName: string, properties?: { [prop: string]: string });
experimentalAutoBuild: boolean;

View file

@ -17,6 +17,7 @@ import * as Proto from './protocol';
import { ITypescriptServiceClient, ITypescriptServiceClientHost, APIVersion } from './typescriptService';
import * as VersionStatus from './utils/versionStatus';
import * as is from './utils/is';
import TelemetryReporter from 'vscode-extension-telemetry';
@ -75,7 +76,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private tsdk: string;
private _experimentalAutoBuild: boolean;
private trace: Trace;
private output: OutputChannel;
private _output: OutputChannel;
private servicePromise: Promise<cp.ChildProcess>;
private lastError: Error;
private reader: Reader<Proto.Response>;
@ -132,14 +133,18 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.startService();
}
private get output(): OutputChannel {
if (!this._output) {
this._output = window.createOutputChannel(localize('channelName', 'TypeScript'));
}
return this._output;
}
private readTrace(): Trace {
let result: Trace = Trace.fromString(workspace.getConfiguration().get<string>('typescript.tsserver.trace', 'off'));
if (result === Trace.Off && !!process.env.TSS_TRACE) {
result = Trace.Messages;
}
if (result !== Trace.Off && !this.output) {
this.output = window.createOutputChannel(localize('channelName', 'TypeScript'));
}
return result;
}
@ -155,6 +160,52 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return this._onReady.promise;
}
private data2String(data: any): string {
if (data instanceof Error) {
if (is.string(data.stack)) {
return data.stack;
}
return (data as Error).message;
}
if (is.boolean(data.success) && data.success && is.string(data.message)) {
return data.message;
}
if (is.string(data)) {
return data;
}
return data.toString();
}
public info(message: string, data?: any): void {
this.output.appendLine(`[Info - ${(new Date().toLocaleTimeString())}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}
public warn(message: string, data?: any): void {
this.output.appendLine(`[Warn - ${(new Date().toLocaleTimeString())}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}
public error(message: string, data?: any): void {
this.output.appendLine(`[Error - ${(new Date().toLocaleTimeString())}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
this.output.show();
}
private logTrace(message: string, data?: any): void {
this.output.appendLine(`[Trace - ${(new Date().toLocaleTimeString())}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
this.output.show();
}
private get packageInfo(): IPackageInfo {
if (this._packageInfo !== undefined) {
@ -202,7 +253,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
modulePath = path.join(workspace.rootPath, this.tsdk, 'tsserver.js');
}
}
this.info(`Using tsserver from location: ${modulePath}`);
if (!fs.existsSync(modulePath)) {
window.showErrorMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. TypeScript language features will be disabled.', path.dirname(modulePath)));
return;
@ -229,12 +280,14 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (value) {
let port = parseInt(value);
if (!isNaN(port)) {
this.info(`TSServer started in debug mode using port ${port}`);
options.execArgv = [`--debug=${port}`];
}
}
electron.fork(modulePath, [], options, (err: any, childProcess: cp.ChildProcess) => {
if (err) {
this.lastError = err;
this.error('Starting TSServer failed with error.', err);
window.showErrorMessage(localize('serverCouldNotBeStarted', 'TypeScript language server couldn\'t be started. Error message is: {0}', err.message || err));
this.logTelemetry('error', {message: err.message});
return;
@ -242,9 +295,11 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.lastStart = Date.now();
childProcess.on('error', (err: Error) => {
this.lastError = err;
this.error('TSServer errored with error.', err);
this.serviceExited(false);
});
childProcess.on('exit', (err: Error) => {
childProcess.on('exit', (code: any) => {
this.error(`TSServer exited with code: ${code ? code : 'unknown'}`);
this.serviceExited(true);
});
this.reader = new Reader<Proto.Response>(childProcess.stdout, (msg) => {
@ -408,13 +463,13 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (this.requestQueue[i].request.seq === seq) {
this.requestQueue.splice(i, 1);
if (this.trace !== Trace.Off) {
this.output.append(`TypeScript Service: canceled request with sequence number ${seq}\n`);
this.logTrace(`TypeScript Service: canceled request with sequence number ${seq}`);
}
return true;
}
}
if (this.trace !== Trace.Off) {
this.output.append(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.\n`);
this.logTrace(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.`);
}
return false;
}
@ -461,29 +516,32 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (this.trace === Trace.Off) {
return;
}
this.output.append(`Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${this.requestQueue.length}\n`);
let data: string = undefined;
if (this.trace === Trace.Verbose && request.arguments) {
this.output.append(`Arguments: ${JSON.stringify(request.arguments, null, 4)}\n\n`);
data = `Arguments: ${JSON.stringify(request.arguments, null, 4)}`;
}
this.logTrace(`Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${this.requestQueue.length}`, data);
}
private traceResponse(response: Proto.Response, startTime: number): void {
if (this.trace === Trace.Off) {
return;
}
this.output.append(`Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}\n`);
let data: string = undefined;
if (this.trace === Trace.Verbose && response.body) {
this.output.append(`Result: ${JSON.stringify(response.body, null, 4)}\n\n`);
data = `Result: ${JSON.stringify(response.body, null, 4)}`;
}
this.logTrace(`Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data);
}
private traceEvent(event: Proto.Event): void {
if (this.trace === Trace.Off) {
return;
}
this.output.append(`Event received: ${event.event} (${event.seq}).\n`);
let data: string = undefined;
if (this.trace === Trace.Verbose && event.body) {
this.output.append(`Data: ${JSON.stringify(event.body, null, 4)}\n\n`);
data = `Data: ${JSON.stringify(event.body, null, 4)}`;
}
this.logTrace(`Event received: ${event.event} (${event.seq}).`, data);
}
}