From 410509137b22c0411307be1256acda7347453bf4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 25 Jul 2018 18:17:56 -0700 Subject: [PATCH] Add base Disposable class to help manage disposables --- .../src/features/bufferSyncSupport.ts | 23 ++++---- .../src/features/languageConfiguration.ts | 12 ++--- .../src/features/tagClosing.ts | 20 +++---- .../src/languageProvider.ts | 53 +++++++++---------- .../src/typeScriptServiceClientHost.ts | 48 +++++++---------- .../src/typescriptServiceClient.ts | 45 ++++++---------- .../src/utils/dependentRegistration.ts | 14 ++--- .../src/utils/dispose.ts | 25 ++++++++- 8 files changed, 113 insertions(+), 127 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index f6b4e8c174e..d4be308cfb0 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import { CancellationTokenSource, Disposable, EventEmitter, TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, Uri, workspace } from 'vscode'; +import { CancellationTokenSource, EventEmitter, TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, Uri, workspace } from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as languageModeIds from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; import * as typeConverters from '../utils/typeConverters'; @@ -168,14 +168,13 @@ class GetErrRequest { } } -export default class BufferSyncSupport { +export default class BufferSyncSupport extends Disposable { private readonly client: ITypeScriptServiceClient; private _validateJavaScript: boolean = true; private _validateTypeScript: boolean = true; private readonly modeIds: Set; - private readonly disposables: Disposable[] = []; private readonly syncedBuffers: SyncedBufferMap; private readonly pendingDiagnostics: PendingDiagnostics; private readonly diagnosticDelayer: Delayer; @@ -186,6 +185,7 @@ export default class BufferSyncSupport { client: ITypeScriptServiceClient, modeIds: string[] ) { + super(); this.client = client; this.modeIds = new Set(modeIds); @@ -196,10 +196,10 @@ export default class BufferSyncSupport { this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); this.updateConfiguration(); - workspace.onDidChangeConfiguration(this.updateConfiguration, this, this.disposables); + workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); } - private readonly _onDelete = new EventEmitter(); + private readonly _onDelete = this._register(new EventEmitter()); public readonly onDelete = this._onDelete.event; public listen(): void { @@ -207,9 +207,9 @@ export default class BufferSyncSupport { return; } this.listening = true; - workspace.onDidOpenTextDocument(this.openTextDocument, this, this.disposables); - workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this.disposables); - workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this.disposables); + workspace.onDidOpenTextDocument(this.openTextDocument, this, this._disposables); + workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this._disposables); + workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this._disposables); workspace.textDocuments.forEach(this.openTextDocument, this); } @@ -231,11 +231,6 @@ export default class BufferSyncSupport { } } - public dispose(): void { - disposeAll(this.disposables); - this._onDelete.dispose(); - } - public openTextDocument(document: TextDocument): void { if (!this.modeIds.has(document.languageId)) { return; diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/features/languageConfiguration.ts index b3eb4ef23dd..8ee1cfecdad 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/features/languageConfiguration.ts @@ -9,7 +9,7 @@ * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as languageModeIds from '../utils/languageModeIds'; const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { @@ -64,10 +64,10 @@ const jsxTagsLanguageConfiguration: vscode.LanguageConfiguration = { ], }; -export class LanguageConfigurationManager { - private readonly _registrations: vscode.Disposable[] = []; +export class LanguageConfigurationManager extends Disposable { constructor() { + super(); const standardLanguages = [ languageModeIds.javascript, languageModeIds.javascriptreact, @@ -82,10 +82,6 @@ export class LanguageConfigurationManager { } private registerConfiguration(language: string, config: vscode.LanguageConfiguration) { - this._registrations.push(vscode.languages.setLanguageConfiguration(language, config)); - } - - dispose() { - disposeAll(this._registrations); + this._register(vscode.languages.setLanguageConfiguration(language, config)); } } diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index 66af4920ef9..40839c30c65 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -8,19 +8,19 @@ import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as typeConverters from '../utils/typeConverters'; -class TagClosing { +class TagClosing extends Disposable { private _disposed = false; private _timeout: NodeJS.Timer | undefined = undefined; private _cancel: vscode.CancellationTokenSource | undefined = undefined; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly client: ITypeScriptServiceClient ) { + super(); vscode.workspace.onDidChangeTextDocument( event => this.onDidChangeTextDocument(event.document, event.contentChanges), null, @@ -28,10 +28,9 @@ class TagClosing { } public dispose() { + super.dispose(); this._disposed = true; - disposeAll(this._disposables); - if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; @@ -136,24 +135,19 @@ class TagClosing { } } -export class ActiveDocumentDependentRegistration { +export class ActiveDocumentDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly selector: vscode.DocumentSelector, register: () => vscode.Disposable, ) { - this._registration = new ConditionalRegistration(register); + super(); + this._registration = this._register(new ConditionalRegistration(register)); vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); this.update(); } - public dispose() { - disposeAll(this._disposables); - this._registration.dispose(); - } - private update() { const editor = vscode.window.activeTextEditor; const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 31ba616b8c1..aad5f1a3a44 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -10,7 +10,7 @@ import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; import { CommandManager } from './utils/commandManager'; -import { disposeAll } from './utils/dispose'; +import { Disposable } from './utils/dispose'; import * as fileSchemes from './utils/fileSchemes'; import { LanguageDescription } from './utils/languageDescription'; import { memoize } from './utils/memoize'; @@ -21,8 +21,7 @@ import TypingsStatus from './utils/typingsStatus'; const validateSetting = 'validate.enable'; const suggestionSetting = 'suggestionActions.enabled'; -export default class LanguageProvider { - private readonly disposables: vscode.Disposable[] = []; +export default class LanguageProvider extends Disposable { constructor( private readonly client: TypeScriptServiceClient, @@ -32,7 +31,8 @@ export default class LanguageProvider { private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager ) { - vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables); + super(); + vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); client.onReady(async () => { @@ -40,9 +40,6 @@ export default class LanguageProvider { }); } - public dispose(): void { - disposeAll(this.disposables); - } @memoize private get documentSelector(): vscode.DocumentFilter[] { @@ -60,27 +57,27 @@ export default class LanguageProvider { const cachedResponse = new CachedNavTreeResponse(); - this.disposables.push((await import('./features/completions')).register(selector, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); - this.disposables.push((await import('./features/definitions')).register(selector, this.client)); - this.disposables.push((await import('./features/directiveCommentCompletions')).register(selector, this.client)); - this.disposables.push((await import('./features/documentHighlight')).register(selector, this.client)); - this.disposables.push((await import('./features/documentSymbol')).register(selector, this.client)); - this.disposables.push((await import('./features/folding')).register(selector, this.client)); - this.disposables.push((await import('./features/formatting')).register(selector, this.description.id, this.client, this.fileConfigurationManager)); - this.disposables.push((await import('./features/hover')).register(selector, this.client)); - this.disposables.push((await import('./features/implementations')).register(selector, this.client)); - this.disposables.push((await import('./features/implementationsCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this.disposables.push((await import('./features/jsDocCompletions')).register(selector, this.client, this.commandManager)); - this.disposables.push((await import('./features/organizeImports')).register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter)); - this.disposables.push((await import('./features/quickFix')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter)); - this.disposables.push((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); - this.disposables.push((await import('./features/references')).register(selector, this.client)); - this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this.disposables.push((await import('./features/rename')).register(selector, this.client)); - this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client)); - this.disposables.push((await import('./features/tagClosing')).register(selector, this.description.id, this.client)); - this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client)); - this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); + this._register((await import('./features/completions')).register(selector, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); + this._register((await import('./features/definitions')).register(selector, this.client)); + this._register((await import('./features/directiveCommentCompletions')).register(selector, this.client)); + this._register((await import('./features/documentHighlight')).register(selector, this.client)); + this._register((await import('./features/documentSymbol')).register(selector, this.client)); + this._register((await import('./features/folding')).register(selector, this.client)); + this._register((await import('./features/formatting')).register(selector, this.description.id, this.client, this.fileConfigurationManager)); + this._register((await import('./features/hover')).register(selector, this.client)); + this._register((await import('./features/implementations')).register(selector, this.client)); + this._register((await import('./features/implementationsCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); + this._register((await import('./features/jsDocCompletions')).register(selector, this.client, this.commandManager)); + this._register((await import('./features/organizeImports')).register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter)); + this._register((await import('./features/quickFix')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter)); + this._register((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); + this._register((await import('./features/references')).register(selector, this.client)); + this._register((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); + this._register((await import('./features/rename')).register(selector, this.client)); + this._register((await import('./features/signatureHelp')).register(selector, this.client)); + this._register((await import('./features/tagClosing')).register(selector, this.description.id, this.client)); + this._register((await import('./features/typeDefinitions')).register(selector, this.client)); + this._register((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); } private configurationChanged(): void { diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 8b716b6dd68..522c1f8364e 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -8,7 +8,7 @@ * https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences * ------------------------------------------------------------------------------------------ */ -import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Disposable, Memento, Range, Uri, workspace } from 'vscode'; +import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Memento, Range, Uri, workspace } from 'vscode'; import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; import { register as registerUpdatePathsOnRename } from './features/updatePathsOnRename'; @@ -18,7 +18,7 @@ import * as PConst from './protocol.const'; import TypeScriptServiceClient from './typescriptServiceClient'; import API from './utils/api'; import { CommandManager } from './utils/commandManager'; -import { disposeAll } from './utils/dispose'; +import { Disposable } from './utils/dispose'; import { LanguageDescription, DiagnosticLanguage } from './utils/languageDescription'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import { TypeScriptServerPlugin } from './utils/plugins'; @@ -36,13 +36,11 @@ const styleCheckDiagnostics = [ 7030 // not all code paths return a value ]; -export default class TypeScriptServiceClientHost { - private readonly ataProgressReporter: AtaProgressReporter; +export default class TypeScriptServiceClientHost extends Disposable { private readonly typingsStatus: TypingsStatus; private readonly client: TypeScriptServiceClient; private readonly languages: LanguageProvider[] = []; private readonly languagePerId = new Map(); - private readonly disposables: Disposable[] = []; private readonly versionStatus: VersionStatus; private readonly fileConfigurationManager: FileConfigurationManager; @@ -55,6 +53,7 @@ export default class TypeScriptServiceClientHost { private readonly commandManager: CommandManager, logDirectoryProvider: LogDirectoryProvider ) { + super(); const handleProjectCreateOrDelete = () => { this.client.execute('reloadProjects', null, false); this.triggerAllDiagnostics(); @@ -65,10 +64,10 @@ export default class TypeScriptServiceClientHost { }, 1500); }; const configFileWatcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json'); - this.disposables.push(configFileWatcher); - configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this.disposables); - configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this.disposables); - configFileWatcher.onDidChange(handleProjectChange, this, this.disposables); + this._register(configFileWatcher); + configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this._disposables); + configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this._disposables); + configFileWatcher.onDidChange(handleProjectChange, this, this._disposables); const allModeIds = this.getAllModeIds(descriptions); this.client = new TypeScriptServiceClient( @@ -77,30 +76,30 @@ export default class TypeScriptServiceClientHost { plugins, logDirectoryProvider, allModeIds); - this.disposables.push(this.client); + this._register(this.client); this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => { this.diagnosticsReceived(kind, resource, diagnostics); - }, null, this.disposables); + }, null, this._disposables); - this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this.disposables); - this.client.onResendModelsRequested(() => this.populateService(), null, this.disposables); + this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this._disposables); + this.client.onResendModelsRequested(() => this.populateService(), null, this._disposables); this.versionStatus = new VersionStatus(resource => this.client.toPath(resource)); - this.disposables.push(this.versionStatus); + this._register(this.versionStatus); - this.typingsStatus = new TypingsStatus(this.client); - this.ataProgressReporter = new AtaProgressReporter(this.client); - this.fileConfigurationManager = new FileConfigurationManager(this.client); + this._register(new AtaProgressReporter(this.client)); + this.typingsStatus = this._register(new TypingsStatus(this.client)); + this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client)); for (const description of descriptions) { const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); this.languages.push(manager); - this.disposables.push(manager); + this._register(manager); this.languagePerId.set(description.id, manager); } - this.disposables.push(registerUpdatePathsOnRename(this.client, this.fileConfigurationManager, uri => this.handles(uri))); + this._register(registerUpdatePathsOnRename(this.client, this.fileConfigurationManager, uri => this.handles(uri))); this.client.ensureServiceStarted(); this.client.onReady(() => { @@ -125,7 +124,7 @@ export default class TypeScriptServiceClientHost { }; const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); this.languages.push(manager); - this.disposables.push(manager); + this._register(manager); this.languagePerId.set(description.id, manager); } }); @@ -134,7 +133,7 @@ export default class TypeScriptServiceClientHost { this.triggerAllDiagnostics(); }); - workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables); + workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); } @@ -146,13 +145,6 @@ export default class TypeScriptServiceClientHost { return allModeIds; } - public dispose(): void { - disposeAll(this.disposables); - this.typingsStatus.dispose(); - this.ataProgressReporter.dispose(); - this.fileConfigurationManager.dispose(); - } - public get serviceClient(): TypeScriptServiceClient { return this.client; } diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 88ad494c3f6..1655c6b5419 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -6,7 +6,7 @@ import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, commands, Disposable, env, EventEmitter, Memento, MessageItem, Uri, window, workspace } from 'vscode'; +import { CancellationToken, commands, env, EventEmitter, Memento, MessageItem, Uri, window, workspace } from 'vscode'; import * as nls from 'vscode-nls'; import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; @@ -14,7 +14,7 @@ import * as Proto from './protocol'; import { ITypeScriptServiceClient } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; -import { disposeAll } from './utils/dispose'; +import { Disposable } from './utils/dispose'; import * as electron from './utils/electron'; import * as fileSchemes from './utils/fileSchemes'; import * as is from './utils/is'; @@ -30,9 +30,6 @@ import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionPro import { ICallback, Reader } from './utils/wireProtocol'; - - - const localize = nls.loadMessageBundle(); interface CallbackItem { @@ -159,7 +156,7 @@ export interface TsDiagnostics { readonly diagnostics: Proto.Diagnostic[]; } -export default class TypeScriptServiceClient implements ITypeScriptServiceClient { +export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; private pathSeparator: string; @@ -194,8 +191,6 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient */ private _tsserverVersion: string | undefined; - private readonly disposables: Disposable[] = []; - public readonly bufferSyncSupport: BufferSyncSupport; public readonly diagnosticsManager: DiagnosticsManager; @@ -206,6 +201,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient private readonly logDirectoryProvider: LogDirectoryProvider, allModeIds: string[] ) { + super(); this.pathSeparator = path.sep; this.lastStart = Date.now(); @@ -235,7 +231,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.diagnosticsManager = new DiagnosticsManager('typescript'); this.bufferSyncSupport.onDelete(resource => { this.diagnosticsManager.delete(resource); - }, null, this.disposables); + }, null, this._disposables); workspace.onDidChangeConfiguration(() => { const oldConfiguration = this._configuration; @@ -256,9 +252,9 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.restartTsServer(); } } - }, this, this.disposables); + }, this, this._disposables); this.telemetryReporter = new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString); - this.disposables.push(this.telemetryReporter); + this._register(this.telemetryReporter); } public get configuration() { @@ -266,22 +262,15 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } public dispose() { + super.dispose(); + this.bufferSyncSupport.dispose(); - this._onTsServerStarted.dispose(); - this._onDidBeginInstallTypings.dispose(); - this._onDidEndInstallTypings.dispose(); - this._onTypesInstallerInitializationFailed.dispose(); if (this.servicePromise) { this.servicePromise.then(childProcess => { childProcess.kill(); }).then(undefined, () => void 0); } - - disposeAll(this.disposables); - this._onDiagnosticsReceived.dispose(); - this._onConfigDiagnosticsReceived.dispose(); - this._onResendModelsRequested.dispose(); } public restartTsServer(): void { @@ -302,28 +291,28 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } } - private readonly _onTsServerStarted = new EventEmitter(); + private readonly _onTsServerStarted = this._register(new EventEmitter()); public readonly onTsServerStarted = this._onTsServerStarted.event; - private readonly _onDiagnosticsReceived = new EventEmitter(); + private readonly _onDiagnosticsReceived = this._register(new EventEmitter()); public readonly onDiagnosticsReceived = this._onDiagnosticsReceived.event; - private readonly _onConfigDiagnosticsReceived = new EventEmitter(); + private readonly _onConfigDiagnosticsReceived = this._register(new EventEmitter()); public readonly onConfigDiagnosticsReceived = this._onConfigDiagnosticsReceived.event; - private readonly _onResendModelsRequested = new EventEmitter(); + private readonly _onResendModelsRequested = this._register(new EventEmitter()); public readonly onResendModelsRequested = this._onResendModelsRequested.event; - private readonly _onProjectLanguageServiceStateChanged = new EventEmitter(); + private readonly _onProjectLanguageServiceStateChanged = this._register(new EventEmitter()); public readonly onProjectLanguageServiceStateChanged = this._onProjectLanguageServiceStateChanged.event; - private readonly _onDidBeginInstallTypings = new EventEmitter(); + private readonly _onDidBeginInstallTypings = this._register(new EventEmitter()); public readonly onDidBeginInstallTypings = this._onDidBeginInstallTypings.event; - private readonly _onDidEndInstallTypings = new EventEmitter(); + private readonly _onDidEndInstallTypings = this._register(new EventEmitter()); public readonly onDidEndInstallTypings = this._onDidEndInstallTypings.event; - private readonly _onTypesInstallerInitializationFailed = new EventEmitter(); + private readonly _onTypesInstallerInitializationFailed = this._register(new EventEmitter()); public readonly onTypesInstallerInitializationFailed = this._onTypesInstallerInitializationFailed.event; public get apiVersion(): API { diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 232d6b8e2cf..434379f57e1 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from './api'; -import { disposeAll } from './dispose'; +import { Disposable } from './dispose'; export class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; @@ -36,15 +36,15 @@ export class ConditionalRegistration { } } -export class VersionDependentRegistration { +export class VersionDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly client: ITypeScriptServiceClient, private readonly minVersion: API, register: () => vscode.Disposable, ) { + super(); this._registration = new ConditionalRegistration(register); this.update(client.apiVersion); @@ -55,7 +55,7 @@ export class VersionDependentRegistration { } public dispose() { - disposeAll(this._disposables); + super.dispose(); this._registration.dispose(); } @@ -65,22 +65,22 @@ export class VersionDependentRegistration { } -export class ConfigurationDependentRegistration { +export class ConfigurationDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly language: string, private readonly configValue: string, register: () => vscode.Disposable, ) { + super(); this._registration = new ConditionalRegistration(register); this.update(); vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables); } public dispose() { - disposeAll(this._disposables); + super.dispose(); this._registration.dispose(); } diff --git a/extensions/typescript-language-features/src/utils/dispose.ts b/extensions/typescript-language-features/src/utils/dispose.ts index b7c72f8b8b4..a0b91f441b2 100644 --- a/extensions/typescript-language-features/src/utils/dispose.ts +++ b/extensions/typescript-language-features/src/utils/dispose.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; -export function disposeAll(disposables: vscode.Disposable[]) { +function disposeAll(disposables: vscode.Disposable[]) { while (disposables.length) { const item = disposables.pop(); if (item) { @@ -13,3 +13,26 @@ export function disposeAll(disposables: vscode.Disposable[]) { } } } + +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: vscode.Disposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } +} \ No newline at end of file -- GitLab