Introduce basic lazy type to encapsulate the idea of a lazy value

This commit is contained in:
Matt Bierner
2017-11-30 17:43:59 -08:00
parent 5630128b42
commit bcdeb87b29
4 changed files with 77 additions and 37 deletions

View File

@@ -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<TypeScriptServiceClientHost>
) { }
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<TypeScriptServiceClientHost>
) { }
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<TypeScriptServiceClientHost>
) { }
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<TypeScriptServiceClientHost>
) { }
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<TypeScriptServiceClientHost>
) { }
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<TypeScriptServiceClientHost>,
) { }
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<TypeScriptServiceClientHost>,
) { }
public execute() {
const editor = vscode.window.activeTextEditor;
if (editor) {
this.lazyClientHost().goToProjectConfig(false, editor.document.uri);
this.lazyClientHost.value.goToProjectConfig(false, editor.document.uri);
}
}
}

View File

@@ -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<boolean>(resolve => setTimeout(() => resolve(host.handles(path)), 750)), context.workspaceState));
}, () => {
// Nothing to do here. The client did show a message;
});
}
): Lazy<TypeScriptServiceClientHost> {
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<boolean>(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<TypeScriptServiceClientHost>
) {
commandManager.register(new commands.ReloadTypeScriptProjectsCommand(lazyClientHost));
commandManager.register(new commands.ReloadJavaScriptProjectsCommand(lazyClientHost));

View File

@@ -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<ITypeScriptServiceClient>
) {
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<ITypeScriptServiceClient>
) {
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));
}
}
}

View File

@@ -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<T> {
value: T;
hasValue: boolean;
map<R>(f: (x: T) => R): Lazy<R>;
}
class LazyValue<T> implements Lazy<T> {
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<R>(f: (x: T) => R): Lazy<R> {
return new LazyValue(() => f(this.value));
}
}
export function lazy<T>(getValue: () => T): Lazy<T> {
return new LazyValue<T>(getValue);
}