From 16c6b81b3e54ce8cab06fbd9649cce211f672944 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jul 2020 11:33:44 -0700 Subject: [PATCH] Flatten conditional registration This allows us to pass a precomputed, flat list of requirements when registering a language feature --- .../src/features/callHierarchy.ts | 21 ++-- .../src/features/completions.ts | 13 +- .../src/features/fixAll.ts | 6 +- .../src/features/folding.ts | 6 +- .../src/features/formatting.ts | 6 +- .../src/features/implementationsCodeLens.ts | 6 +- .../src/features/jsDocCompletions.ts | 6 +- .../src/features/organizeImports.ts | 10 +- .../src/features/refactor.ts | 6 +- .../src/features/referencesCodeLens.ts | 6 +- .../src/features/semanticTokens.ts | 18 +-- .../src/features/smartSelect.ts | 9 +- .../src/features/tagClosing.ts | 50 +++----- .../src/features/updatePathsOnRename.ts | 9 +- .../src/utils/dependentRegistration.ts | 119 ++++++++++-------- 15 files changed, 161 insertions(+), 130 deletions(-) diff --git a/extensions/typescript-language-features/src/features/callHierarchy.ts b/extensions/typescript-language-features/src/features/callHierarchy.ts index 3e84d255ee5..1bc85e4ca11 100644 --- a/extensions/typescript-language-features/src/features/callHierarchy.ts +++ b/extensions/typescript-language-features/src/features/callHierarchy.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; -import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import type * as Proto from '../protocol'; import * as path from 'path'; +import * as vscode from 'vscode'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import { parseKindModifier } from '../utils/modifiers'; +import * as typeConverters from '../utils/typeConverters'; namespace Experimental { export interface CallHierarchyItem extends Proto.CallHierarchyItem { @@ -120,7 +120,10 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient ) { - return new VersionDependentRegistration(client, TypeScriptCallHierarchySupport.minVersion, - () => vscode.languages.registerCallHierarchyProvider(selector, - new TypeScriptCallHierarchySupport(client))); + return conditionalRegistration([ + requireMinVersion(client, TypeScriptCallHierarchySupport.minVersion), + ], () => { + return vscode.languages.registerCallHierarchyProvider(selector, + new TypeScriptCallHierarchySupport(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 79f617ba4d7..5ef98b8b07e 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -12,14 +12,14 @@ import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeAction } from '../utils/codeAction'; import { Command, CommandManager } from '../utils/commandManager'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { parseKindModifier } from '../utils/modifiers'; import * as Previewer from '../utils/previewer'; import { snippetForFunctionCall } from '../utils/snippetForFunctionCall'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import TypingsStatus from '../utils/typingsStatus'; import FileConfigurationManager from './fileConfigurationManager'; -import { parseKindModifier } from '../utils/modifiers'; const localize = nls.loadMessageBundle(); @@ -804,8 +804,11 @@ export function register( telemetryReporter: TelemetryReporter, onCompletionAccepted: (item: vscode.CompletionItem) => void ) { - return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => - vscode.languages.registerCompletionItemProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.enabled'), + ], () => { + return vscode.languages.registerCompletionItemProvider(selector, new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, telemetryReporter, onCompletionAccepted), - ...TypeScriptCompletionItemProvider.triggerCharacters)); + ...TypeScriptCompletionItemProvider.triggerCharacters); + }); } diff --git a/extensions/typescript-language-features/src/features/fixAll.ts b/extensions/typescript-language-features/src/features/fixAll.ts index 86dd8116d10..422c26e4131 100644 --- a/extensions/typescript-language-features/src/features/fixAll.ts +++ b/extensions/typescript-language-features/src/features/fixAll.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import * as errorCodes from '../utils/errorCodes'; import * as fixNames from '../utils/fixNames'; import * as typeConverters from '../utils/typeConverters'; @@ -254,7 +254,9 @@ export function register( fileConfigurationManager: FileConfigurationManager, diagnosticsManager: DiagnosticsManager, ) { - return new VersionDependentRegistration(client, API.v300, () => { + return conditionalRegistration([ + requireMinVersion(client, API.v300) + ], () => { const provider = new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager); return vscode.languages.registerCodeActionsProvider(selector, provider, provider.metadata); }); diff --git a/extensions/typescript-language-features/src/features/folding.ts b/extensions/typescript-language-features/src/features/folding.ts index f9f04296bdd..c61028cb6ef 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/features/folding.ts @@ -8,7 +8,7 @@ import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { @@ -76,7 +76,9 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new VersionDependentRegistration(client, TypeScriptFoldingProvider.minVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, TypeScriptFoldingProvider.minVersion), + ], () => { return vscode.languages.registerFoldingRangeProvider(selector, new TypeScriptFoldingProvider(client)); }); diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index fbe1db881e0..525567e0bb2 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -89,7 +89,9 @@ export function register( client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager ) { - return new ConfigurationDependentRegistration(modeId, 'format.enable', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'format.enable'), + ], () => { const formattingProvider = new TypeScriptFormattingProvider(client, fileConfigurationManager); return vscode.Disposable.from( vscode.languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n'), diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index c6ea7ca6dee..1e8b5bdeec4 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens, getSymbolRange } from './baseCodeLensProvider'; import { CachedResponse } from '../tsServer/cachedResponse'; import * as typeConverters from '../utils/typeConverters'; @@ -94,7 +94,9 @@ export function register( client: ITypeScriptServiceClient, cachedResponse: CachedResponse, ) { - return new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'implementationsCodeLens.enabled'), + ], () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); }); diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/features/jsDocCompletions.ts index 80b0e219705..9c930ac5ad7 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/features/jsDocCompletions.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; @@ -114,7 +114,9 @@ export function register( modeId: string, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new ConfigurationDependentRegistration(modeId, 'suggest.completeJSDocs', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.completeJSDocs') + ], () => { return vscode.languages.registerCompletionItemProvider(selector, new JsDocCompletionProvider(client), '*'); diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index b5d492063f9..186d12e3f2e 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -8,12 +8,12 @@ import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; +import { nulToken } from '../utils/cancellation'; import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { TelemetryReporter } from '../utils/telemetry'; import * as typeconverts from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; -import { TelemetryReporter } from '../utils/telemetry'; -import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -105,7 +105,9 @@ export function register( fileConfigurationManager: FileConfigurationManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, OrganizeImportsCodeActionProvider.minVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, OrganizeImportsCodeActionProvider.minVersion) + ], () => { const organizeImportsProvider = new OrganizeImportsCodeActionProvider(client, commandManager, fileConfigurationManager, telemetryReporter); return vscode.languages.registerCodeActionsProvider(selector, organizeImportsProvider, diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index ca56c6d6902..9c6ac23977c 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -11,7 +11,7 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import * as fileSchemes from '../utils/fileSchemes'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; @@ -402,7 +402,9 @@ export function register( commandManager: CommandManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, TypeScriptRefactorProvider.minVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, TypeScriptRefactorProvider.minVersion) + ], () => { return vscode.languages.registerCodeActionsProvider(selector, new TypeScriptRefactorProvider(client, formattingOptionsManager, commandManager, telemetryReporter), TypeScriptRefactorProvider.metadata); diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 0cf8d3f0a51..1bf3c404ad7 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -9,7 +9,7 @@ import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { CachedResponse } from '../tsServer/cachedResponse'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; @@ -127,7 +127,9 @@ export function register( client: ITypeScriptServiceClient, cachedResponse: CachedResponse, ) { - return new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'referencesCodeLens.enabled') + ], () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptReferencesCodeLensProvider(client, cachedResponse, modeId)); }); diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index 92b6e479330..4546a1e6bc8 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; -import * as Proto from '../protocol'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import API from '../utils/api'; - // all constants are const -import { TokenType, TokenModifier, TokenEncodingConsts, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import { TokenEncodingConsts, TokenModifier, TokenType, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; + const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.${VersionRequirement.minor}`); @@ -18,7 +18,9 @@ const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}. const CONTENT_LENGTH_LIMIT = 100000; export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { - return new VersionDependentRegistration(client, minTypeScriptVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, minTypeScriptVersion), + ], () => { const provider = new DocumentSemanticTokensProvider(client); return vscode.Disposable.from( // register only as a range provider diff --git a/extensions/typescript-language-features/src/features/smartSelect.ts b/extensions/typescript-language-features/src/features/smartSelect.ts index 651535c5bcf..e61c168bd8c 100644 --- a/extensions/typescript-language-features/src/features/smartSelect.ts +++ b/extensions/typescript-language-features/src/features/smartSelect.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; class SmartSelection implements vscode.SelectionRangeProvider { @@ -52,6 +52,9 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, SmartSelection.minVersion, () => - vscode.languages.registerSelectionRangeProvider(selector, new SmartSelection(client))); + return conditionalRegistration([ + requireMinVersion(client, SmartSelection.minVersion), + ], () => { + return vscode.languages.registerSelectionRangeProvider(selector, new SmartSelection(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index 03b65c4ea2a..40fee040f3e 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion, requireConfiguration, Condition } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as typeConverters from '../utils/typeConverters'; @@ -135,32 +135,19 @@ class TagClosing extends Disposable { } } -export class ActiveDocumentDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly selector: vscode.DocumentSelector, - register: () => vscode.Disposable, - ) { - super(); - this._registration = this._register(new ConditionalRegistration(register)); - vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); - vscode.workspace.onDidOpenTextDocument(this.onDidOpenDocument, this, this._disposables); - this.update(); - } - - private update() { - const editor = vscode.window.activeTextEditor; - const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); - this._registration.update(enabled); - } - - private onDidOpenDocument(openedDocument: vscode.TextDocument) { - if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === openedDocument) { - // The active document's language may have changed - this.update(); - } - } +function requireActiveDocument( + selector: vscode.DocumentSelector +) { + return new Condition( + () => { + const editor = vscode.window.activeTextEditor; + return !!(editor && vscode.languages.match(selector, editor.document)); + }, + handler => { + return vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(handler), + vscode.workspace.onDidOpenTextDocument(handler)); + }); } export function register( @@ -168,8 +155,9 @@ export function register( modeId: string, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, TagClosing.minVersion, () => - new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => - new ActiveDocumentDependentRegistration(selector, () => - new TagClosing(client)))); + return conditionalRegistration([ + requireMinVersion(client, TagClosing.minVersion), + requireConfiguration(modeId, 'autoClosingTags'), + requireActiveDocument(selector) + ], () => new TagClosing(client)); } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 16b764d5256..408d5100017 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -11,7 +11,7 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; import { nulToken } from '../utils/cancellation'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; import { doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; @@ -294,6 +294,9 @@ export function register( fileConfigurationManager: FileConfigurationManager, handles: (uri: vscode.Uri) => Promise, ) { - return new VersionDependentRegistration(client, UpdateImportsOnFileRenameHandler.minVersion, () => - new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles)); + return conditionalRegistration([ + requireMinVersion(client, UpdateImportsOnFileRenameHandler.minVersion), + ], () => { + return new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles); + }); } diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 75dac667204..6a87f286e6d 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -8,24 +8,54 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from './api'; import { Disposable } from './dispose'; -export class ConditionalRegistration { +export class Condition extends Disposable { + private _value: boolean; + + constructor( + private readonly getValue: () => boolean, + onUpdate: (handler: () => void) => void, + ) { + super(); + this._value = this.getValue(); + + onUpdate(() => { + const newValue = this.getValue(); + if (newValue !== this._value) { + this._value = newValue; + this._onDidChange.fire(); + } + }); + } + + public get value(): boolean { return this._value; } + + private readonly _onDidChange = this._register(new vscode.EventEmitter()); + public readonly onDidChange = this._onDidChange.event; +} + +class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; public constructor( - private readonly _doRegister: () => vscode.Disposable - ) { } - - public dispose() { - if (this.registration) { - this.registration.dispose(); - this.registration = undefined; + private readonly conditions: readonly Condition[], + private readonly doRegister: () => vscode.Disposable + ) { + for (const condition of conditions) { + condition.onDidChange(() => this.update()); } + this.update(); } - public update(enabled: boolean) { + public dispose() { + this.registration?.dispose(); + this.registration = undefined; + } + + private update() { + const enabled = this.conditions.every(condition => condition.value); if (enabled) { if (!this.registration) { - this.registration = this._doRegister(); + this.registration = this.doRegister(); } } else { if (this.registration) { @@ -36,49 +66,32 @@ export class ConditionalRegistration { } } -export class VersionDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly minVersion: API, - register: () => vscode.Disposable, - ) { - super(); - - this._registration = this._register(new ConditionalRegistration(register)); - - this._register(this.client.onTsServerStarted(() => { - this.update(this.client.apiVersion); - })); - - this.update(client.apiVersion); - } - - private update(api: API) { - this._registration.update(api.gte(this.minVersion)); - } +export function conditionalRegistration( + conditions: readonly Condition[], + doRegister: () => vscode.Disposable, +): vscode.Disposable { + return new ConditionalRegistration(conditions, doRegister); } - -export class ConfigurationDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly language: string, - private readonly configValue: string, - register: () => vscode.Disposable, - ) { - super(); - this._registration = this._register(new ConditionalRegistration(register)); - - this._register(vscode.workspace.onDidChangeConfiguration(this.update, this)); - - this.update(); - } - - private update() { - const config = vscode.workspace.getConfiguration(this.language, null); - this._registration.update(!!config.get(this.configValue)); - } +export function requireMinVersion( + client: ITypeScriptServiceClient, + minVersion: API, +) { + return new Condition( + () => client.apiVersion.gte(minVersion), + client.onTsServerStarted + ); +} + +export function requireConfiguration( + language: string, + configValue: string, +) { + return new Condition( + () => { + const config = vscode.workspace.getConfiguration(language, null); + return !!config.get(configValue); + }, + vscode.workspace.onDidChangeConfiguration + ); }