diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index dee3929baba..83bd22e3eca 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -9,19 +9,20 @@ import * as vscode from 'vscode'; import { Api, getExtensionApi } from './api'; import { CommandManager } from './commands/commandManager'; import { registerBaseCommands } from './commands/index'; +import { ElectronServiceConfigurationProvider } from './configuration/configuration.electron'; import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { ExperimentationService } from './experimentationService'; import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost'; +import { Logger } from './logging/logger'; import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron'; import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron'; +import { PluginManager } from './tsServer/plugins'; import { ElectronServiceProcessFactory } from './tsServer/serverProcess.electron'; import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron'; import { ActiveJsTsEditorTracker } from './ui/activeJsTsEditorTracker'; -import { ElectronServiceConfigurationProvider } from './configuration/configuration.electron'; import { onCaseInsensitiveFileSystem } from './utils/fs.electron'; -import { Logger } from './logging/logger'; +import { Lazy } from './utils/lazy'; import { getPackageInfo } from './utils/packageInfo'; -import { PluginManager } from './tsServer/plugins'; import * as temp from './utils/temp.electron'; export function activate( @@ -75,7 +76,7 @@ export function activate( registerBaseCommands(commandManager, lazyClientHost, pluginManager, activeJsTsEditorTracker); import('./task/taskProvider').then(module => { - context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient))); + context.subscriptions.push(module.register(new Lazy(() => lazyClientHost.value.serviceClient))); }); import('./languageFeatures/tsconfig').then(module => { diff --git a/extensions/typescript-language-features/src/lazyClientHost.ts b/extensions/typescript-language-features/src/lazyClientHost.ts index 6d2bb34604f..3fc8a37cd17 100644 --- a/extensions/typescript-language-features/src/lazyClientHost.ts +++ b/extensions/typescript-language-features/src/lazyClientHost.ts @@ -16,7 +16,7 @@ import ManagedFileContextManager from './ui/managedFileContext'; import { ServiceConfigurationProvider } from './configuration/configuration'; import * as fileSchemes from './configuration/fileSchemes'; import { standardLanguageDescriptions, isJsConfigOrTsConfigFileName } from './configuration/languageDescription'; -import { Lazy, lazy } from './utils/lazy'; +import { Lazy } from './utils/lazy'; import { Logger } from './logging/logger'; import { PluginManager } from './tsServer/plugins'; @@ -37,7 +37,7 @@ export function createLazyClientHost( }, onCompletionAccepted: (item: vscode.CompletionItem) => void, ): Lazy { - return lazy(() => { + return new Lazy(() => { const clientHost = new TypeScriptServiceClientHost( standardLanguageDescriptions, context, diff --git a/extensions/typescript-language-features/src/utils/lazy.ts b/extensions/typescript-language-features/src/utils/lazy.ts index 23c000bc65d..7114ece99b1 100644 --- a/extensions/typescript-language-features/src/utils/lazy.ts +++ b/extensions/typescript-language-features/src/utils/lazy.ts @@ -3,37 +3,45 @@ * 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; -} +export class Lazy { -class LazyValue implements Lazy { - private _hasValue: boolean = false; + private _didRun: boolean = false; private _value?: T; + private _error: Error | undefined; constructor( - private readonly _getValue: () => T + private readonly executor: () => T, ) { } + /** + * True if the lazy value has been resolved. + */ + get hasValue() { return this._didRun; } + + /** + * Get the wrapped value. + * + * This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only + * resolved once. `getValue` will re-throw exceptions that are hit while resolving the value + */ get value(): T { - if (!this._hasValue) { - this._hasValue = true; - this._value = this._getValue(); + if (!this._didRun) { + try { + this._value = this.executor(); + } catch (err) { + this._error = err; + } finally { + this._didRun = true; + } + } + if (this._error) { + throw this._error; } return this._value!; } - get hasValue(): boolean { - return this._hasValue; - } - - public map(f: (x: T) => R): Lazy { - return new LazyValue(() => f(this.value)); - } + /** + * Get the wrapped value without forcing evaluation. + */ + get rawValue(): T | undefined { return this._value; } } - -export function lazy(getValue: () => T): Lazy { - return new LazyValue(getValue); -} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/temp.electron.ts b/extensions/typescript-language-features/src/utils/temp.electron.ts index cf099411246..84dd358b47c 100644 --- a/extensions/typescript-language-features/src/utils/temp.electron.ts +++ b/extensions/typescript-language-features/src/utils/temp.electron.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { lazy } from './lazy'; +import { Lazy } from './lazy'; function makeRandomHexString(length: number): string { const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; @@ -18,12 +18,12 @@ function makeRandomHexString(length: number): string { return result; } -const rootTempDir = lazy(() => { +const rootTempDir = new Lazy(() => { const filename = `vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`; return path.join(os.tmpdir(), filename); }); -export const instanceTempDir = lazy(() => { +export const instanceTempDir = new Lazy(() => { const dir = path.join(rootTempDir.value, makeRandomHexString(20)); fs.mkdirSync(dir, { recursive: true }); return dir;