提交 22deff95 编写于 作者: M Matt Bierner 提交者: GitHub

Use Strict Null Checks In TS Extension (#16244)

* Use Strict Null Checks In TS Extension

Updates the Ts extension to use strict null checks.

* Throw instead of returning undefined in some linkedmap cases

* fix small null check in buffersync

* Fix for request item null
上级 393c5a8e
......@@ -167,7 +167,10 @@ export default class BufferSyncSupport {
public dispose(): void {
while (this.disposables.length) {
this.disposables.pop().dispose();
const obj = this.disposables.pop();
if (obj) {
obj.dispose();
}
}
}
......@@ -191,7 +194,7 @@ export default class BufferSyncSupport {
}
private onDidCloseTextDocument(document: TextDocument): void {
let filepath: string = this.client.asAbsolutePath(document.uri);
let filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return;
}
......@@ -208,7 +211,7 @@ export default class BufferSyncSupport {
}
private onDidChangeTextDocument(e: TextDocumentChangeEvent): void {
let filepath: string = this.client.asAbsolutePath(e.document.uri);
let filepath = this.client.asAbsolutePath(e.document.uri);
if (!filepath) {
return;
}
......@@ -220,7 +223,7 @@ export default class BufferSyncSupport {
}
private onDidSaveTextDocument(document: TextDocument): void {
let filepath: string = this.client.asAbsolutePath(document.uri);
let filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return;
}
......@@ -312,7 +315,7 @@ export default class BufferSyncSupport {
return cp.exec(cmd + ' ' + url);
}
let tscVersion: string = undefined;
let tscVersion: string | undefined = undefined;
try {
let out = cp.execSync('tsc --version', { encoding: 'utf8' });
if (out) {
......
......@@ -92,6 +92,9 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> {
let filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve<CompletionItem[]>([]);
}
let args: CompletionsRequestArgs = {
file: filepath,
line: position.line + 1,
......@@ -120,14 +123,15 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
let completionItems: CompletionItem[] = [];
let body = msg.body;
for (let i = 0; i < body.length; i++) {
let element = body[i];
let item = new MyCompletionItem(element);
item.document = document;
item.position = position;
completionItems.push(item);
if (body) {
for (let i = 0; i < body.length; i++) {
let element = body[i];
let item = new MyCompletionItem(element);
item.document = document;
item.position = position;
completionItems.push(item);
}
}
return completionItems;
......@@ -139,16 +143,19 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public resolveCompletionItem(item: CompletionItem, token: CancellationToken): any | Thenable<any> {
if (item instanceof MyCompletionItem) {
const filepath = this.client.asAbsolutePath(item.document.uri);
if (!filepath) {
return null;
}
let args: CompletionDetailsRequestArgs = {
file: this.client.asAbsolutePath(item.document.uri),
file: filepath,
line: item.position.line + 1,
offset: item.position.character + 1,
entryNames: [item.label]
};
return this.client.execute('completionEntryDetails', args, token).then((response) => {
let details = response.body;
let detail: CompletionEntryDetails = null;
let detail: CompletionEntryDetails | null = null;
if (details && details.length > 0) {
detail = details[0];
item.documentation = Previewer.plain(detail.documentation);
......
......@@ -5,7 +5,7 @@
'use strict';
import { DefinitionProvider, TextDocument, Position, Range, CancellationToken, Location } from 'vscode';
import { DefinitionProvider, TextDocument, Position, Range, CancellationToken, Definition, Location } from 'vscode';
import * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService';
......@@ -20,19 +20,23 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider
this.client = client;
}
public provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise<Location> {
public provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise<Definition | null> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve(null);
}
let args: Proto.FileLocationRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1
};
if (!args.file) {
return Promise.resolve<Location>(null);
return Promise.resolve(null);
}
return this.client.execute('definition', args, token).then(response => {
let locations: Proto.FileSpan[] = response.body;
let locations: Proto.FileSpan[] = response.body || [];
if (!locations || locations.length === 0) {
return null;
return [] as Definition;
}
return locations.map(location => {
let resource = this.client.asUrl(location.file);
......@@ -41,10 +45,10 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider
} else {
return new Location(resource, new Range(location.start.line - 1, location.start.offset - 1, location.end.line - 1, location.end.offset - 1));
}
});
}).filter(x => x !== null) as Location[];
}, (error) => {
this.client.error(`'definition' request failed with error.`, error);
return null;
return [] as Definition;
});
}
}
\ No newline at end of file
......@@ -20,8 +20,12 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh
}
public provideDocumentHighlights(resource: TextDocument, position: Position, token: CancellationToken): Promise<DocumentHighlight[]> {
const filepath = this.client.asAbsolutePath(resource.uri);
if (!filepath) {
return Promise.resolve<DocumentHighlight[]>([]);
}
let args: Proto.FileLocationRequestArgs = {
file: this.client.asAbsolutePath(resource.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1
};
......@@ -36,6 +40,7 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh
item.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Read);
});
}
return [];
}, (err) => {
this.client.error(`'occurrences' request failed with error.`, err);
return [];
......
......@@ -40,8 +40,12 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
}
public provideDocumentSymbols(resource: TextDocument, token: CancellationToken): Promise<SymbolInformation[]> {
const filepath = this.client.asAbsolutePath(resource.uri);
if (!filepath) {
return Promise.resolve<SymbolInformation[]>([]);
}
let args: Proto.FileRequestArgs = {
file: this.client.asAbsolutePath(resource.uri)
file: filepath
};
if (!args.file) {
return Promise.resolve<SymbolInformation[]>([]);
......@@ -53,7 +57,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
if (realIndent !== 0 && !foldingMap[key]) {
let result = new SymbolInformation(item.text,
outlineTypeTable[item.kind] || SymbolKind.Variable,
containerLabel,
'' + containerLabel,
new Location(resource.uri, textSpan2Range(item.spans[0])));
foldingMap[key] = result;
bucket.push(result);
......@@ -68,7 +72,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
function convertNavTree(bucket: SymbolInformation[], item: Proto.NavigationTree, containerLabel?: string): void {
let result = new SymbolInformation(item.text,
outlineTypeTable[item.kind] || SymbolKind.Variable,
containerLabel,
'' + containerLabel,
new Location(resource.uri, textSpan2Range(item.spans[0]))
);
if (item.childItems && item.childItems.length > 0) {
......
......@@ -71,7 +71,7 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
private client: ITypescriptServiceClient;
private config: Configuration;
private formatOptions: { [key: string]: Proto.FormatCodeSettings; };
private formatOptions: { [key: string]: Proto.FormatCodeSettings | undefined; };
public constructor(client: ITypescriptServiceClient) {
this.client = client;
......@@ -106,8 +106,12 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
if (currentOptions && currentOptions.tabSize === options.tabSize && currentOptions.indentSize === options.tabSize && currentOptions.convertTabsToSpaces === options.insertSpaces) {
return Promise.resolve(currentOptions);
} else {
const absPath = this.client.asAbsolutePath(document.uri);
if (!absPath) {
return Promise.resolve(Object.create(null));
}
let args: Proto.ConfigureRequestArguments = {
file: this.client.asAbsolutePath(document.uri),
file: absPath,
formatOptions: this.getFormatOptions(options)
};
return this.client.execute('configure', args, token).then((response) => {
......@@ -120,7 +124,11 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
private doFormat(document: TextDocument, options: FormattingOptions, args: Proto.FormatRequestArgs, token: CancellationToken): Promise<TextEdit[]> {
return this.ensureFormatOptions(document, options, token).then(() => {
return this.client.execute('format', args, token).then((response): TextEdit[] => {
return response.body.map(this.codeEdit2SingleEditOperation);
if (response.body) {
return response.body.map(this.codeEdit2SingleEditOperation);
} else {
return [];
}
}, (err: any) => {
this.client.error(`'format' request failed with error.`, err);
return [];
......@@ -129,8 +137,12 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
}
public provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[]> {
const absPath = this.client.asAbsolutePath(document.uri);
if (!absPath) {
return Promise.resolve([]);
}
let args: Proto.FormatRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: absPath,
line: range.start.line + 1,
offset: range.start.character + 1,
endLine: range.end.line + 1,
......@@ -140,8 +152,12 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
}
public provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[]> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve([]);
}
let args: Proto.FormatOnKeyRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1,
key: ch
......@@ -151,6 +167,9 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat
return this.client.execute('formatonkey', args, token).then((response): TextEdit[] => {
let edits = response.body;
let result: TextEdit[] = [];
if (!edits) {
return result;
}
for (let edit of edits) {
let textEdit = this.codeEdit2SingleEditOperation(edit);
let range = textEdit.range;
......
......@@ -18,16 +18,20 @@ export default class TypeScriptHoverProvider implements HoverProvider {
this.client = client;
}
public provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover> {
public provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover | undefined | null> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve(null);
}
let args: Proto.FileLocationRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1
};
if (!args.file) {
return Promise.resolve<Hover>(null);
return Promise.resolve(null);
}
return this.client.execute('quickinfo', args, token).then((response): Hover => {
return this.client.execute('quickinfo', args, token).then((response): Hover | undefined => {
let data = response.body;
if (data) {
return new Hover(
......
......@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
interface Item<T> {
previous: Item<T>;
next: Item<T>;
previous: Item<T> | undefined;
next: Item<T> | undefined;
key: string;
value: T;
}
......@@ -13,8 +13,8 @@ interface Item<T> {
export default class LinkedMap<T> {
private map: Map<Item<T>>;
private head: Item<T>;
private tail: Item<T>;
private head: Item<T> | undefined;
private tail: Item<T> | undefined;
private _length: number;
constructor() {
......@@ -32,7 +32,7 @@ export default class LinkedMap<T> {
return this._length;
}
public get(key: string): T {
public get(key: string): T | undefined {
const item = this.map[key];
if (!item) {
return undefined;
......@@ -61,7 +61,7 @@ export default class LinkedMap<T> {
}
}
public remove(key: string): T {
public remove(key: string): T | undefined {
const item = this.map[key];
if (!item) {
return undefined;
......@@ -72,10 +72,13 @@ export default class LinkedMap<T> {
return item.value;
}
public shift(): T {
public shift(): T | undefined {
if (!this.head && !this.tail) {
return undefined;
}
if (!this.head || !this.tail) {
throw new Error('Invalid list');
}
const item = this.head;
delete this.map[item.key];
this.removeItem(item);
......@@ -87,8 +90,9 @@ export default class LinkedMap<T> {
// First time Insert
if (!this.head && !this.tail) {
this.tail = item;
}
else {
} else if (!this.head) {
throw new Error('Invalid list');
} else {
item.next = this.head;
this.head.previous = item;
}
......@@ -99,8 +103,9 @@ export default class LinkedMap<T> {
// First time Insert
if (!this.head && !this.tail) {
this.head = item;
}
else {
} else if (!this.tail) {
throw new Error('Invalid list');
} else {
item.previous = this.tail;
this.tail.next = item;
}
......@@ -121,6 +126,9 @@ export default class LinkedMap<T> {
else {
const next = item.next;
const previous = item.previous;
if (!next || !previous) {
throw new Error('Invalid list');
}
next.previous = previous;
previous.next = next;
}
......@@ -140,13 +148,20 @@ export default class LinkedMap<T> {
}
else {
// Both next and previous are not null since item was neither head nor tail.
next.previous = previous;
previous.next = next;
if (next) {
next.previous = previous;
}
if (previous) {
previous.next = next;
}
}
// Insert the node at head
item.previous = undefined;
item.next = this.head;
if (!this.head) {
throw new Error('Invalid list');
}
this.head.previous = item;
this.head = item;
}
......
......@@ -21,8 +21,12 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider {
}
public provideReferences(document: TextDocument, position: Position, options: { includeDeclaration: boolean }, token: CancellationToken): Promise<Location[]> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve<Location[]>([]);
}
let args: Proto.FileLocationRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1
};
......@@ -32,6 +36,9 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider {
const apiVersion = this.client.apiVersion;
return this.client.execute('references', args, token).then((msg) => {
let result: Location[] = [];
if (!msg.body) {
return result;
}
let refs = msg.body.refs;
for (let i = 0; i < refs.length; i++) {
let ref = refs[i];
......
......@@ -20,20 +20,27 @@ export default class TypeScriptRenameProvider implements RenameProvider {
this.client = client;
}
public provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): Promise<WorkspaceEdit> {
public provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): Promise<WorkspaceEdit | undefined | null> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve(null);
}
let args: Proto.RenameRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1,
findInStrings: false,
findInComments: false
};
if (!args.file) {
return Promise.resolve<WorkspaceEdit>(null);
return Promise.resolve(null);
}
return this.client.execute('rename', args, token).then((response) => {
let renameResponse = response.body;
if (!renameResponse) {
return Promise.resolve(null);
}
let renameInfo = renameResponse.info;
let result = new WorkspaceEdit();
......
......@@ -19,14 +19,18 @@ export default class TypeScriptSignatureHelpProvider implements SignatureHelpPro
this.client = client;
}
public provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Promise<SignatureHelp> {
public provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Promise<SignatureHelp | undefined | null> {
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve(null);
}
let args: Proto.SignatureHelpRequestArgs = {
file: this.client.asAbsolutePath(document.uri),
file: filepath,
line: position.line + 1,
offset: position.character + 1
};
if (!args.file) {
return Promise.resolve<SignatureHelp>(null);
return Promise.resolve(null);
}
return this.client.execute('signatureHelp', args, token).then((response) => {
let info = response.body;
......@@ -42,6 +46,9 @@ export default class TypeScriptSignatureHelpProvider implements SignatureHelpPro
}
info.items.forEach((item, i) => {
if (!info) {
return;
}
// keep active parameter in bounds
if (i === info.selectedItemIndex && item.isVariadic) {
......
......@@ -32,7 +32,7 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
// typescript wants to have a resource even when asking
// general questions so we check the active editor. If this
// doesn't match we take the first TS document.
let uri: Uri;
let uri: Uri | undefined = undefined;
let editor = window.activeTextEditor;
if (editor) {
let document = editor.document;
......@@ -54,8 +54,12 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
return Promise.resolve<SymbolInformation[]>([]);
}
const filepath = this.client.asAbsolutePath(uri);
if (!filepath) {
return Promise.resolve<SymbolInformation[]>([]);
}
let args: Proto.NavtoRequestArgs = {
file: this.client.asAbsolutePath(uri),
file: filepath,
searchValue: search
};
if (!args.file) {
......@@ -74,7 +78,7 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
if (item.kind === 'method' || item.kind === 'function') {
label += '()';
}
result.push(new SymbolInformation(label, _kindMapping[item.kind], item.containerName,
result.push(new SymbolInformation(label, _kindMapping[item.kind], '' + item.containerName,
new Location(this.client.asUrl(item.file), range)));
}
return result;
......
......@@ -103,7 +103,7 @@ class LanguageProvider {
private completionItemProvider: CompletionItemProvider;
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable;
private formattingProviderRegistration: Disposable | null;
private _validate: boolean;
......@@ -214,7 +214,7 @@ class LanguageProvider {
this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
this.formattingProviderRegistration.dispose();
this.formattingProviderRegistration = undefined;
this.formattingProviderRegistration = null;
} else if (this.formattingProvider.isEnabled() && !this.formattingProviderRegistration) {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
......@@ -228,7 +228,7 @@ class LanguageProvider {
return true;
}
let basename = path.basename(file);
return basename && basename === this.description.configFile;
return !!basename && basename === this.description.configFile;
}
public get id(): string {
......@@ -325,7 +325,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
return !!this.findLanguage(file);
}
private findLanguage(file: string): LanguageProvider {
private findLanguage(file: string): LanguageProvider | null {
for (let i = 0; i < this.languages.length; i++) {
let language = this.languages[i];
if (language.handles(file)) {
......@@ -348,7 +348,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
/* internal */ syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
let body = event.body;
if (body.diagnostics) {
if (body && body.diagnostics) {
let language = this.findLanguage(body.file);
if (language) {
language.syntaxDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
......@@ -358,7 +358,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
/* internal */ semanticDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
let body = event.body;
if (body.diagnostics) {
if (body && body.diagnostics) {
let language = this.findLanguage(body.file);
if (language) {
language.semanticDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
......
......@@ -56,7 +56,7 @@ export class API {
}
export interface ITypescriptServiceClient {
asAbsolutePath(resource: Uri): string;
asAbsolutePath(resource: Uri): string | null;
asUrl(filepath: string): Uri;
info(message: string, data?: any): void;
......
......@@ -36,8 +36,8 @@ interface CallbackMap {
interface RequestItem {
request: Proto.Request;
promise: Promise<any>;
callbacks: CallbackItem;
promise: Promise<any> | null;
callbacks: CallbackItem | null;
}
interface IPackageInfo {
......@@ -85,13 +85,13 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private pathSeparator: string;
private _onReady: { promise: Promise<void>; resolve: () => void; reject: () => void; };
private tsdk: string;
private tsdk: string | null;
private _checkGlobalTSCVersion: boolean;
private _experimentalAutoBuild: boolean;
private trace: Trace;
private _output: OutputChannel;
private servicePromise: Promise<cp.ChildProcess>;
private lastError: Error;
private servicePromise: Promise<cp.ChildProcess> | null;
private lastError: Error | null;
private reader: Reader<Proto.Response>;
private sequenceNumber: number;
private exitRequested: boolean;
......@@ -103,7 +103,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private pendingResponses: number;
private callbacks: CallbackMap;
private _packageInfo: IPackageInfo;
private _packageInfo: IPackageInfo | null;
private _apiVersion: API;
private telemetryReporter: TelemetryReporter;
......@@ -114,7 +114,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.pathSeparator = path.sep;
let p = new Promise<void>((resolve, reject) => {
this._onReady = { promise: null, resolve, reject };
this._onReady = { promise: Promise.reject<void>(null), resolve, reject };
});
this._onReady.promise = p;
......@@ -129,7 +129,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.pendingResponses = 0;
this.callbacks = Object.create(null);
const configuration = workspace.getConfiguration();
this.tsdk = configuration.get<string>('typescript.tsdk', null);
this.tsdk = configuration.get<string | null>('typescript.tsdk', null);
this._experimentalAutoBuild = false; // configuration.get<boolean>('typescript.tsserver.experimentalAutoBuild', false);
this._apiVersion = new API('1.0.0');
this._checkGlobalTSCVersion = true;
......@@ -137,7 +137,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
workspace.onDidChangeConfiguration(() => {
this.trace = this.readTrace();
let oldTsdk = this.tsdk;
this.tsdk = workspace.getConfiguration().get<string>('typescript.tsdk', null);
this.tsdk = workspace.getConfiguration().get<string | null>('typescript.tsdk', null);
if (this.servicePromise === null && oldTsdk !== this.tsdk) {
this.startService();
}
......@@ -230,7 +230,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
// this.output.show(true);
}
private get packageInfo(): IPackageInfo {
private get packageInfo(): IPackageInfo | null {
if (this._packageInfo !== undefined) {
return this._packageInfo;
......@@ -264,7 +264,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return Promise.reject<cp.ChildProcess>(this.lastError);
}
this.startService();
return this.servicePromise;
if (this.servicePromise) {
return this.servicePromise;
}
return Promise.reject<cp.ChildProcess>(new Error('Could not create TS service'));
}
private startService(resendModels: boolean = false): void {
......@@ -362,7 +365,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
let version = this.getTypeScriptVersion(modulePath);
if (!version) {
version = workspace.getConfiguration().get<string>('typescript.tsdk_version', undefined);
version = workspace.getConfiguration().get<string | undefined>('typescript.tsdk_version', undefined);
}
if (version) {
this._apiVersion = new API(version);
......@@ -469,7 +472,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
let args: Proto.SetCompilerOptionsForInferredProjectsArgs = {
options: compilerOptions
};
this.execute('compilerOptionsForInferredProjects', args).then(null, (err) => {
this.execute('compilerOptionsForInferredProjects', args, true).catch((err) => {
this.error(`'compilerOptionsForInferredProjects' request failed with error.`, err);
});
}
......@@ -479,7 +482,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
}
}
private getTypeScriptVersion(serverPath: string): string {
private getTypeScriptVersion(serverPath: string): string | undefined {
let p = serverPath.split(path.sep);
if (p.length <= 2) {
return undefined;
......@@ -491,13 +494,13 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return undefined;
}
let contents = fs.readFileSync(fileName).toString();
let desc = null;
let desc: any = null;
try {
desc = JSON.parse(contents);
} catch (err) {
return undefined;
}
if (!desc.version) {
if (!desc || !desc.version) {
return undefined;
}
return desc.version;
......@@ -528,7 +531,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
}
}
public asAbsolutePath(resource: Uri): string {
public asAbsolutePath(resource: Uri): string | null {
if (resource.scheme !== 'file') {
return null;
}
......@@ -563,7 +566,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
promise: null,
callbacks: null
};
let result: Promise<any> = null;
let result: Promise<any> = Promise.resolve(null);
if (expectsResult) {
result = new Promise<any>((resolve, reject) => {
requestInfo.callbacks = { c: resolve, e: reject, start: Date.now() };
......@@ -584,7 +587,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private sendNextRequests(): void {
while (this.pendingResponses === 0 && this.requestQueue.length > 0) {
this.sendRequest(this.requestQueue.shift());
const item = this.requestQueue.shift();
if (item) {
this.sendRequest(item);
}
}
}
......@@ -691,9 +697,9 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (this.trace === Trace.Off) {
return;
}
let data: string = undefined;
let data: string | undefined = undefined;
if (this.trace === Trace.Verbose && request.arguments) {
data = `Arguments: ${JSON.stringify(request.arguments, null, 4)}`;
data = `Arguments: ${JSON.stringify(request.arguments, [], 4)}`;
}
this.logTrace(`Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${this.requestQueue.length}`, data);
}
......@@ -702,9 +708,9 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (this.trace === Trace.Off) {
return;
}
let data: string = undefined;
let data: string | undefined = undefined;
if (this.trace === Trace.Verbose && response.body) {
data = `Result: ${JSON.stringify(response.body, null, 4)}`;
data = `Result: ${JSON.stringify(response.body, [], 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);
}
......@@ -713,9 +719,9 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (this.trace === Trace.Off) {
return;
}
let data: string = undefined;
let data: string | undefined = undefined;
if (this.trace === Trace.Verbose && event.body) {
data = `Data: ${JSON.stringify(event.body, null, 4)}`;
data = `Data: ${JSON.stringify(event.body, [], 4)}`;
}
this.logTrace(`Event received: ${event.event} (${event.seq}).`, data);
}
......
......@@ -13,9 +13,9 @@ export class Delayer<T> {
public defaultDelay: number;
private timeout: any; // Timer
private completionPromise: Promise<T>;
private onSuccess: (value?: T | Thenable<T>) => void;
private task: ITask<T>;
private completionPromise: Promise<T> | null;
private onSuccess: ((value?: T | Thenable<T>) => void) | null;
private task: ITask<T> | null;
constructor(defaultDelay: number) {
this.defaultDelay = defaultDelay;
......@@ -37,7 +37,7 @@ export class Delayer<T> {
}).then(() => {
this.completionPromise = null;
this.onSuccess = null;
var result = this.task();
var result = this.task && this.task();
this.task = null;
return result;
});
......@@ -46,20 +46,24 @@ export class Delayer<T> {
if (delay >= 0 || this.timeout === null) {
this.timeout = setTimeout(() => {
this.timeout = null;
this.onSuccess(null);
if (this.onSuccess) {
this.onSuccess(undefined);
}
}, delay >= 0 ? delay : this.defaultDelay);
}
return this.completionPromise;
}
public forceDelivery(): Promise<T> {
public forceDelivery(): Promise<T> | null {
if (!this.completionPromise) {
return null;
}
this.cancelTimeout();
let result = this.completionPromise;
this.onSuccess(null);
if (this.onSuccess) {
this.onSuccess(undefined);
}
return result;
}
......
......@@ -53,7 +53,7 @@ function generatePatchedEnv(env: any, stdInPipeName: string, stdOutPipeName: str
return newEnv;
}
export function fork(modulePath: string, args: string[], options: IForkOptions, callback: (error: any, cp: cp.ChildProcess) => void): void {
export function fork(modulePath: string, args: string[], options: IForkOptions, callback: (error: any, cp: cp.ChildProcess | null) => void): void {
var callbackCalled = false;
var resolve = (result: cp.ChildProcess) => {
......
......@@ -31,8 +31,8 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
const projectHintIgnoreList = memento.get<string[]>('projectHintIgnoreList', []);
for (let path of projectHintIgnoreList) {
if (path === null) {
path = undefined;
if (!path) {
path = 'undefined';
}
projectHinted[path] = true;
}
......@@ -53,7 +53,7 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
delete projectHinted[e.document.fileName];
}));
function onEditor(editor: vscode.TextEditor): void {
function onEditor(editor: vscode.TextEditor | undefined): void {
if (!editor
|| !vscode.languages.match(selector, editor.document)
|| !client.asAbsolutePath(editor.document.uri)) {
......@@ -63,20 +63,26 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
}
const file = client.asAbsolutePath(editor.document.uri);
if (!file) {
return;
}
isOpen(file).then(value => {
if (!value) {
return;
}
return client.execute('projectInfo', { file, needFileNameList: true }).then(res => {
if (!res.body) {
return;
}
let {configFileName, fileNames} = res.body;
if (projectHinted[configFileName] === true) {
return;
}
if (fileNames.length > fileLimit) {
if (fileNames && fileNames.length > fileLimit) {
let largeRoots = computeLargeRoots(configFileName, fileNames).map(f => `'/${f}/'`).join(', ');
currentHint = {
......@@ -91,10 +97,11 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
item.hide();
let configFileUri: vscode.Uri;
if (dirname(configFileName).indexOf(vscode.workspace.rootPath) === 0) {
let rootPath = vscode.workspace.rootPath;
if (rootPath && dirname(configFileName).indexOf('' + rootPath) === 0) {
configFileUri = vscode.Uri.file(configFileName);
} else {
configFileUri = vscode.Uri.parse('untitled://' + join(vscode.workspace.rootPath, 'jsconfig.json'));
configFileUri = vscode.Uri.parse('untitled://' + join(rootPath, 'jsconfig.json'));
}
return vscode.workspace.openTextDocument(configFileUri)
......
......@@ -25,7 +25,7 @@ class ProtocolBuffer {
}
public append(data: string | Buffer): void {
let toAppend: Buffer = null;
let toAppend: Buffer | null = null;
if (Buffer.isBuffer(data)) {
toAppend = <Buffer>data;
} else {
......@@ -70,7 +70,7 @@ class ProtocolBuffer {
return result;
}
public tryReadContent(length: number): string {
public tryReadContent(length: number): string | null {
if (this.index < length) {
return null;
}
......@@ -84,7 +84,7 @@ class ProtocolBuffer {
return result;
}
public tryReadLine(): string {
public tryReadLine(): string | null {
let end: number = 0;
while (end < this.index && this.buffer[end] !== BackslashR && this.buffer[end] !== BackslashN) {
end++;
......
......@@ -6,7 +6,8 @@
"es5",
"es2015.promise"
],
"outDir": "./out"
"outDir": "./out",
"strictNullChecks": true
},
"exclude": [
"node_modules",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册