diff --git a/extensions/typescript/src/commands.ts b/extensions/typescript/src/commands.ts index 704fec3df5e..357f063b20e 100644 --- a/extensions/typescript/src/commands.ts +++ b/extensions/typescript/src/commands.ts @@ -7,16 +7,17 @@ import * as vscode from 'vscode'; import { TypeScriptServiceClientHost } from './typescriptMain'; import { Command } from './utils/commandManager'; +import { Lazy } from './utils/lazy'; export class ReloadTypeScriptProjectsCommand implements Command { public readonly id = 'typescript.reloadProjects'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost + private readonly lazyClientHost: Lazy ) { } public execute() { - this.lazyClientHost().reloadProjects(); + this.lazyClientHost.value.reloadProjects(); } } @@ -24,11 +25,11 @@ export class ReloadJavaScriptProjectsCommand implements Command { public readonly id = 'javascript.reloadProjects'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost + private readonly lazyClientHost: Lazy ) { } public execute() { - this.lazyClientHost().reloadProjects(); + this.lazyClientHost.value.reloadProjects(); } } @@ -36,11 +37,11 @@ export class SelectTypeScriptVersionCommand implements Command { public readonly id = 'typescript.selectTypeScriptVersion'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost + private readonly lazyClientHost: Lazy ) { } public execute() { - this.lazyClientHost().serviceClient.onVersionStatusClicked(); + this.lazyClientHost.value.serviceClient.onVersionStatusClicked(); } } @@ -48,11 +49,11 @@ export class OpenTsServerLogCommand implements Command { public readonly id = 'typescript.openTsServerLog'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost + private readonly lazyClientHost: Lazy ) { } public execute() { - this.lazyClientHost().serviceClient.openTsServerLogFile(); + this.lazyClientHost.value.serviceClient.openTsServerLogFile(); } } @@ -60,11 +61,11 @@ export class RestartTsServerCommand implements Command { public readonly id = 'typescript.restartTsServer'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost + private readonly lazyClientHost: Lazy ) { } public execute() { - this.lazyClientHost().serviceClient.restartTsServer(); + this.lazyClientHost.value.serviceClient.restartTsServer(); } } @@ -72,13 +73,13 @@ export class TypeScriptGoToProjectConfigCommand implements Command { public readonly id = 'typescript.goToProjectConfig'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost, + private readonly lazyClientHost: Lazy, ) { } public execute() { const editor = vscode.window.activeTextEditor; if (editor) { - this.lazyClientHost().goToProjectConfig(true, editor.document.uri); + this.lazyClientHost.value.goToProjectConfig(true, editor.document.uri); } } } @@ -87,13 +88,13 @@ export class JavaScriptGoToProjectConfigCommand implements Command { public readonly id = 'javascript.goToProjectConfig'; public constructor( - private readonly lazyClientHost: () => TypeScriptServiceClientHost, + private readonly lazyClientHost: Lazy, ) { } public execute() { const editor = vscode.window.activeTextEditor; if (editor) { - this.lazyClientHost().goToProjectConfig(false, editor.document.uri); + this.lazyClientHost.value.goToProjectConfig(false, editor.document.uri); } } } \ No newline at end of file diff --git a/extensions/typescript/src/extension.ts b/extensions/typescript/src/extension.ts index 88267812286..605713d2205 100644 --- a/extensions/typescript/src/extension.ts +++ b/extensions/typescript/src/extension.ts @@ -15,6 +15,7 @@ import * as languageModeIds from './utils/languageModeIds'; import * as languageConfigurations from './utils/languageConfigurations'; import { standardLanguageDescriptions } from './utils/languageDescription'; import ManagedFileContextManager from './utils/managedFileContext'; +import { lazy, Lazy } from './utils/lazy'; export function activate( context: vscode.ExtensionContext @@ -26,9 +27,9 @@ export function activate( const lazyClientHost = createLazyClientHost(context, plugins, commandManager); - context.subscriptions.push(new ManagedFileContextManager(resource => lazyClientHost().serviceClient.normalizePath(resource))); + context.subscriptions.push(new ManagedFileContextManager(resource => lazyClientHost.value.serviceClient.normalizePath(resource))); registerCommands(commandManager, lazyClientHost); - context.subscriptions.push(new TypeScriptTaskProviderManager(() => lazyClientHost().serviceClient)); + context.subscriptions.push(new TypeScriptTaskProviderManager(lazyClientHost.map(x => x.serviceClient))); context.subscriptions.push(vscode.languages.setLanguageConfiguration(languageModeIds.jsxTags, languageConfigurations.jsxTags)); @@ -37,7 +38,8 @@ export function activate( if (supportedLanguage.indexOf(textDocument.languageId) >= 0) { openListener.dispose(); // Force activation - void lazyClientHost(); + // tslint:disable-next-line:no-unused-expression + void lazyClientHost.value; return true; } return false; @@ -54,26 +56,23 @@ function createLazyClientHost( context: vscode.ExtensionContext, plugins: TypeScriptServerPlugin[], commandManager: CommandManager -) { - let clientHost: TypeScriptServiceClientHost | undefined = undefined; - return () => { - if (!clientHost) { - clientHost = new TypeScriptServiceClientHost(standardLanguageDescriptions, context.workspaceState, plugins, commandManager); - context.subscriptions.push(clientHost); - const host = clientHost; - clientHost.serviceClient.onReady().then(() => { - context.subscriptions.push(ProjectStatus.create(host.serviceClient, host.serviceClient.telemetryReporter, path => new Promise(resolve => setTimeout(() => resolve(host.handles(path)), 750)), context.workspaceState)); - }, () => { - // Nothing to do here. The client did show a message; - }); - } +): Lazy { + return lazy(() => { + const clientHost = new TypeScriptServiceClientHost(standardLanguageDescriptions, context.workspaceState, plugins, commandManager); + context.subscriptions.push(clientHost); + const host = clientHost; + clientHost.serviceClient.onReady().then(() => { + context.subscriptions.push(ProjectStatus.create(host.serviceClient, host.serviceClient.telemetryReporter, path => new Promise(resolve => setTimeout(() => resolve(host.handles(path)), 750)), context.workspaceState)); + }, () => { + // Nothing to do here. The client did show a message; + }); return clientHost; - }; + }); } function registerCommands( commandManager: CommandManager, - lazyClientHost: () => TypeScriptServiceClientHost + lazyClientHost: Lazy ) { commandManager.register(new commands.ReloadTypeScriptProjectsCommand(lazyClientHost)); commandManager.register(new commands.ReloadJavaScriptProjectsCommand(lazyClientHost)); diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 452fcec9e08..12244de22f2 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -15,6 +15,7 @@ import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; import * as nls from 'vscode-nls'; +import { Lazy } from '../utils/lazy'; const localize = nls.loadMessageBundle(); type AutoDetect = 'on' | 'off' | 'build' | 'watch'; @@ -42,7 +43,7 @@ class TscTaskProvider implements vscode.TaskProvider { private readonly disposables: vscode.Disposable[] = []; public constructor( - private readonly lazyClient: () => ITypeScriptServiceClient + private readonly client: Lazy ) { this.tsconfigProvider = new TsConfigProvider(); @@ -104,7 +105,7 @@ class TscTaskProvider implements vscode.TaskProvider { } try { - const res: Proto.ProjectInfoResponse = await this.lazyClient().execute( + const res: Proto.ProjectInfoResponse = await this.client.value.execute( 'projectInfo', { file, needFileNameList: false }, token); @@ -166,7 +167,7 @@ class TscTaskProvider implements vscode.TaskProvider { if (editor) { const document = editor.document; if (document && (document.languageId === 'typescript' || document.languageId === 'typescriptreact')) { - return this.lazyClient().normalizePath(document.uri); + return this.client.value.normalizePath(document.uri); } } return null; @@ -242,7 +243,7 @@ export default class TypeScriptTaskProviderManager { private readonly disposables: vscode.Disposable[] = []; constructor( - private readonly lazyClient: () => ITypeScriptServiceClient + private readonly client: Lazy ) { vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); this.onConfigurationChanged(); @@ -262,7 +263,7 @@ export default class TypeScriptTaskProviderManager { this.taskProviderSub.dispose(); this.taskProviderSub = undefined; } else if (!this.taskProviderSub && autoDetect !== 'off') { - this.taskProviderSub = vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(this.lazyClient)); + this.taskProviderSub = vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(this.client)); } } } \ No newline at end of file diff --git a/extensions/typescript/src/utils/lazy.ts b/extensions/typescript/src/utils/lazy.ts new file mode 100644 index 00000000000..de62fd580ee --- /dev/null +++ b/extensions/typescript/src/utils/lazy.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface Lazy { + value: T; + hasValue: boolean; + map(f: (x: T) => R): Lazy; +} + +class LazyValue implements Lazy { + private _hasValue: boolean = false; + private _value: T; + + constructor( + private readonly _getValue: () => T + ) { } + + get value(): T { + if (!this._hasValue) { + this._hasValue = true; + this._value = this._getValue(); + } + return this._value; + } + + get hasValue(): boolean { + return this._hasValue; + } + + public map(f: (x: T) => R): Lazy { + return new LazyValue(() => f(this.value)); + } +} + +export function lazy(getValue: () => T): Lazy { + return new LazyValue(getValue); +} \ No newline at end of file