Fix leaked Emitters (#246125)

* Fix leaked Emitter

* dispose RangesLimitReporter

* fix tests
This commit is contained in:
Martin Aeschlimann 2025-04-11 19:47:25 +02:00 committed by GitHub
parent febbcf78c8
commit 48cb4f823e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 109 additions and 55 deletions

View File

@ -7,7 +7,7 @@ import * as nls from '../../../nls.js';
import { Emitter, Event } from '../../../base/common/event.js';
import { ILanguageExtensionPoint } from './language.js';
import { Registry } from '../../../platform/registry/common/platform.js';
import { IDisposable } from '../../../base/common/lifecycle.js';
import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
import { Mimes } from '../../../base/common/mime.js';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../platform/configuration/common/configurationRegistry.js';
@ -16,14 +16,15 @@ export const Extensions = {
ModesRegistry: 'editor.modesRegistry'
};
export class EditorModesRegistry {
export class EditorModesRegistry extends Disposable {
private readonly _languages: ILanguageExtensionPoint[];
private readonly _onDidChangeLanguages = new Emitter<void>();
private readonly _onDidChangeLanguages = this._register(new Emitter<void>());
public readonly onDidChangeLanguages: Event<void> = this._onDidChangeLanguages.event;
constructor() {
super();
this._languages = [];
}

View File

@ -47,7 +47,7 @@ export class ColorDetector extends Disposable implements IEditorContribution {
private readonly _ruleFactory: DynamicCssRules;
private readonly _decoratorLimitReporter = new DecoratorLimitReporter();
private readonly _decoratorLimitReporter = this._register(new DecoratorLimitReporter());
constructor(
private readonly _editor: ICodeEditor,
@ -269,8 +269,8 @@ export class ColorDetector extends Disposable implements IEditorContribution {
}
}
export class DecoratorLimitReporter {
private _onDidChange = new Emitter<void>();
export class DecoratorLimitReporter extends Disposable {
private _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange: Event<void> = this._onDidChange.event;
private _computed: number = 0;

View File

@ -124,7 +124,7 @@ export class FoldingController extends Disposable implements IEditorContribution
super();
this.editor = editor;
this._foldingLimitReporter = new RangesLimitReporter(editor);
this._foldingLimitReporter = this._register(new RangesLimitReporter(editor));
const options = this.editor.getOptions();
this._isEnabled = options.get(EditorOption.folding);
@ -513,15 +513,16 @@ export class FoldingController extends Disposable implements IEditorContribution
}
}
export class RangesLimitReporter implements FoldingLimitReporter {
export class RangesLimitReporter extends Disposable implements FoldingLimitReporter {
constructor(private readonly editor: ICodeEditor) {
super();
}
public get limit() {
return this.editor.getOptions().get(EditorOption.foldingMaximumRegions);
}
private _onDidChange = new Emitter<void>();
private _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange: Event<void> = this._onDidChange.event;
private _computed: number = 0;

View File

@ -316,7 +316,7 @@ abstract class StickyModelFromCandidateFoldingProvider extends StickyModelCandid
constructor(editor: IActiveCodeEditor) {
super(editor);
this._foldingLimitReporter = new RangesLimitReporter(editor);
this._foldingLimitReporter = this._register(new RangesLimitReporter(editor));
}
protected createStickyModel(token: CancellationToken, model: FoldingRegions): StickyModel {

View File

@ -13,6 +13,7 @@ import { getLanguageTagSettingPlainKey } from './configuration.js';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
import { Registry } from '../../registry/common/platform.js';
import { IPolicy, PolicyName } from '../../../base/common/policy.js';
import { Disposable } from '../../../base/common/lifecycle.js';
export enum EditPresentationTypes {
Multiline = 'multilineText',
@ -272,7 +273,7 @@ export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configur
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
class ConfigurationRegistry implements IConfigurationRegistry {
class ConfigurationRegistry extends Disposable implements IConfigurationRegistry {
private readonly registeredConfigurationDefaults: IConfigurationDefaults[] = [];
private readonly configurationDefaultsOverrides: Map<string, { configurationDefaultOverrides: IConfigurationDefaultOverride[]; configurationDefaultOverrideValue?: IConfigurationDefaultOverrideValue }>;
@ -284,13 +285,14 @@ class ConfigurationRegistry implements IConfigurationRegistry {
private readonly resourceLanguageSettingsSchema: IJSONSchema;
private readonly overrideIdentifiers = new Set<string>();
private readonly _onDidSchemaChange = new Emitter<void>();
private readonly _onDidSchemaChange = this._register(new Emitter<void>());
readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
private readonly _onDidUpdateConfiguration = new Emitter<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>();
private readonly _onDidUpdateConfiguration = this._register(new Emitter<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>());
readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
constructor() {
super();
this.configurationDefaultsOverrides = new Map();
this.defaultLanguageConfigurationOverridesNode = {
id: 'defaultOverrides',

View File

@ -5,7 +5,7 @@
import { Emitter, Event } from '../../../base/common/event.js';
import { getCompressedContent, IJSONSchema } from '../../../base/common/jsonSchema.js';
import { DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
import * as platform from '../../registry/common/platform.js';
export const Extensions = {
@ -65,15 +65,15 @@ function normalizeId(id: string) {
class JSONContributionRegistry implements IJSONContributionRegistry {
class JSONContributionRegistry extends Disposable implements IJSONContributionRegistry {
private readonly schemasById: { [id: string]: IJSONSchema } = {};
private readonly schemaAssociations: { [uri: string]: string[] } = {};
private readonly _onDidChangeSchema = new Emitter<string>();
private readonly _onDidChangeSchema = this._register(new Emitter<string>());
readonly onDidChangeSchema: Event<string> = this._onDidChangeSchema.event;
private readonly _onDidChangeSchemaAssociations = new Emitter<void>();
private readonly _onDidChangeSchemaAssociations = this._register(new Emitter<void>());
readonly onDidChangeSchemaAssociations: Event<void> = this._onDidChangeSchemaAssociations.event;
public registerSchema(uri: string, unresolvedSchemaContent: IJSONSchema, store?: DisposableStore): void {

View File

@ -48,6 +48,16 @@ class RegistryImpl implements IRegistry {
public as(id: string): any {
return this.data.get(id) || null;
}
public dispose() {
this.data.forEach((value) => {
if (Types.isFunction(value.dispose)) {
value.dispose();
}
});
this.data.clear();
}
}
export const Registry: IRegistry = new RegistryImpl();

View File

@ -12,6 +12,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../j
import * as platform from '../../registry/common/platform.js';
import { IColorTheme } from './themeService.js';
import * as nls from '../../../nls.js';
import { Disposable } from '../../../base/common/lifecycle.js';
// ------ API types
@ -133,9 +134,9 @@ export interface IColorRegistry {
type IJSONSchemaForColors = IJSONSchema & { properties: { [name: string]: { oneOf: [IJSONSchemaWithSnippets, IJSONSchema] } } };
type IJSONSchemaWithSnippets = IJSONSchema & { defaultSnippets: IJSONSchemaSnippet[] };
class ColorRegistry implements IColorRegistry {
class ColorRegistry extends Disposable implements IColorRegistry {
private readonly _onDidChangeSchema = new Emitter<void>();
private readonly _onDidChangeSchema = this._register(new Emitter<void>());
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
private colorsById: { [key: string]: ColorContribution };
@ -143,6 +144,7 @@ class ColorRegistry implements IColorRegistry {
private colorReferenceSchema: IJSONSchema & { enum: string[]; enumDescriptions: string[] } = { type: 'string', enum: [], enumDescriptions: [] };
constructor() {
super();
this.colorsById = {};
}
@ -214,7 +216,7 @@ class ColorRegistry implements IColorRegistry {
return this.colorReferenceSchema;
}
public toString() {
public override toString() {
const sorter = (a: string, b: string) => {
const cat1 = a.indexOf('.') === -1 ? 0 : 1;
const cat2 = b.indexOf('.') === -1 ? 0 : 1;

View File

@ -14,6 +14,7 @@ import { URI } from '../../../base/common/uri.js';
import { localize } from '../../../nls.js';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
import * as platform from '../../registry/common/platform.js';
import { Disposable } from '../../../base/common/lifecycle.js';
// ------ API types
@ -156,9 +157,9 @@ export const fontColorRegex = /^#[0-9a-fA-F]{0,6}$/;
export const fontIdErrorMessage = localize('schema.fontId.formatError', 'The font ID must only contain letters, numbers, underscores and dashes.');
class IconRegistry implements IIconRegistry {
class IconRegistry extends Disposable implements IIconRegistry {
private readonly _onDidChange = new Emitter<void>();
private readonly _onDidChange = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
private iconsById: { [key: string]: IconContribution };
@ -182,6 +183,7 @@ class IconRegistry implements IIconRegistry {
private iconFontsById: { [key: string]: IconFontDefinition };
constructor() {
super();
this.iconsById = {};
this.iconFontsById = {};
}
@ -263,7 +265,7 @@ class IconRegistry implements IIconRegistry {
return this.iconFontsById[id];
}
public toString() {
public override toString() {
const sorter = (i1: IconContribution, i2: IconContribution) => {
return i1.id.localeCompare(i2.id);
};

View File

@ -134,13 +134,14 @@ export interface IThemingRegistry {
readonly onThemingParticipantAdded: Event<IThemingParticipant>;
}
class ThemingRegistry implements IThemingRegistry {
class ThemingRegistry extends Disposable implements IThemingRegistry {
private themingParticipants: IThemingParticipant[] = [];
private readonly onThemingParticipantAddedEmitter: Emitter<IThemingParticipant>;
constructor() {
super();
this.themingParticipants = [];
this.onThemingParticipantAddedEmitter = new Emitter<IThemingParticipant>();
this.onThemingParticipantAddedEmitter = this._register(new Emitter<IThemingParticipant>());
}
public onColorThemeChange(participant: IThemingParticipant): IDisposable {

View File

@ -7,6 +7,7 @@ import { RunOnceScheduler } from '../../../base/common/async.js';
import { Color } from '../../../base/common/color.js';
import { Emitter, Event } from '../../../base/common/event.js';
import { IJSONSchema, IJSONSchemaMap } from '../../../base/common/jsonSchema.js';
import { Disposable } from '../../../base/common/lifecycle.js';
import * as nls from '../../../nls.js';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
import * as platform from '../../registry/common/platform.js';
@ -261,9 +262,9 @@ export interface ITokenClassificationRegistry {
getTokenStylingSchema(): IJSONSchema;
}
class TokenClassificationRegistry implements ITokenClassificationRegistry {
class TokenClassificationRegistry extends Disposable implements ITokenClassificationRegistry {
private readonly _onDidChangeSchema = new Emitter<void>();
private readonly _onDidChangeSchema = this._register(new Emitter<void>());
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
private currentTypeNumber = 0;
@ -347,6 +348,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
};
constructor() {
super();
this.tokenTypeById = Object.create(null);
this.tokenModifierById = Object.create(null);
this.typeHierarchy = Object.create(null);
@ -471,7 +473,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public toString() {
public override toString() {
const sorter = (a: string, b: string) => {
const cat1 = a.indexOf('.') === -1 ? 0 : 1;
const cat2 = b.indexOf('.') === -1 ? 0 : 1;

View File

@ -13,6 +13,7 @@ import { NullLogService } from '../../../../platform/log/common/log.js';
import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionsActivator, IExtensionsActivatorHost } from '../../common/extHostExtensionActivator.js';
import { ExtensionDescriptionRegistry, IActivationEventsReader } from '../../../services/extensions/common/extensionDescriptionRegistry.js';
import { ExtensionActivationReason, MissingExtensionDependency } from '../../../services/extensions/common/extensions.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
suite('ExtensionsActivator', () => {
@ -23,26 +24,30 @@ suite('ExtensionsActivator', () => {
const idC = new ExtensionIdentifier(`c`);
test('calls activate only once with sequential activations', async () => {
const disposables = new DisposableStore();
const host = new SimpleExtensionsActivatorHost();
const activator = createActivator(host, [
desc(idA)
]);
], [], disposables);
await activator.activateByEvent('*', false);
assert.deepStrictEqual(host.activateCalls, [idA]);
await activator.activateByEvent('*', false);
assert.deepStrictEqual(host.activateCalls, [idA]);
disposables.dispose();
});
test('calls activate only once with parallel activations', async () => {
const disposables = new DisposableStore();
const extActivation = new ExtensionActivationPromiseSource();
const host = new PromiseExtensionsActivatorHost([
[idA, extActivation]
]);
const activator = createActivator(host, [
desc(idA, [], ['evt1', 'evt2'])
]);
], [], disposables);
const activate1 = activator.activateByEvent('evt1', false);
const activate2 = activator.activateByEvent('evt2', false);
@ -53,9 +58,12 @@ suite('ExtensionsActivator', () => {
await activate2;
assert.deepStrictEqual(host.activateCalls, [idA]);
disposables.dispose();
});
test('activates dependencies first', async () => {
const disposables = new DisposableStore();
const extActivationA = new ExtensionActivationPromiseSource();
const extActivationB = new ExtensionActivationPromiseSource();
const host = new PromiseExtensionsActivatorHost([
@ -65,7 +73,7 @@ suite('ExtensionsActivator', () => {
const activator = createActivator(host, [
desc(idA, [idB], ['evt1']),
desc(idB, [], ['evt1']),
]);
], [], disposables);
const activate = activator.activateByEvent('evt1', false);
@ -81,22 +89,28 @@ suite('ExtensionsActivator', () => {
await activate;
assert.deepStrictEqual(host.activateCalls, [idB, idA]);
disposables.dispose();
});
test('Supports having resolved extensions', async () => {
const disposables = new DisposableStore();
const host = new SimpleExtensionsActivatorHost();
const bExt = desc(idB);
delete (<Mutable<IExtensionDescription>>bExt).main;
delete (<Mutable<IExtensionDescription>>bExt).browser;
const activator = createActivator(host, [
desc(idA, [idB])
], [bExt]);
], [bExt], disposables);
await activator.activateByEvent('*', false);
assert.deepStrictEqual(host.activateCalls, [idA]);
disposables.dispose();
});
test('Supports having external extensions', async () => {
const disposables = new DisposableStore();
const extActivationA = new ExtensionActivationPromiseSource();
const extActivationB = new ExtensionActivationPromiseSource();
const host = new PromiseExtensionsActivatorHost([
@ -107,7 +121,7 @@ suite('ExtensionsActivator', () => {
(<Mutable<IExtensionDescription>>bExt).api = 'none';
const activator = createActivator(host, [
desc(idA, [idB])
], [bExt]);
], [bExt], disposables);
const activate = activator.activateByEvent('*', false);
@ -121,14 +135,17 @@ suite('ExtensionsActivator', () => {
await activate;
assert.deepStrictEqual(host.activateCalls, [idB, idA]);
disposables.dispose();
});
test('Error: activateById with missing extension', async () => {
const disposables = new DisposableStore();
const host = new SimpleExtensionsActivatorHost();
const activator = createActivator(host, [
desc(idA),
desc(idB),
]);
], [], disposables);
let error: Error | undefined = undefined;
try {
@ -138,21 +155,27 @@ suite('ExtensionsActivator', () => {
}
assert.strictEqual(typeof error === 'undefined', false);
disposables.dispose();
});
test('Error: dependency missing', async () => {
const disposables = new DisposableStore();
const host = new SimpleExtensionsActivatorHost();
const activator = createActivator(host, [
desc(idA, [idB]),
]);
], [], disposables);
await activator.activateByEvent('*', false);
assert.deepStrictEqual(host.errors.length, 1);
assert.deepStrictEqual(host.errors[0][0], idA);
disposables.dispose();
});
test('Error: dependency activation failed', async () => {
const disposables = new DisposableStore();
const extActivationA = new ExtensionActivationPromiseSource();
const extActivationB = new ExtensionActivationPromiseSource();
const host = new PromiseExtensionsActivatorHost([
@ -162,7 +185,7 @@ suite('ExtensionsActivator', () => {
const activator = createActivator(host, [
desc(idA, [idB]),
desc(idB)
]);
], [], disposables);
const activate = activator.activateByEvent('*', false);
extActivationB.reject(new Error(`b fails!`));
@ -171,9 +194,12 @@ suite('ExtensionsActivator', () => {
assert.deepStrictEqual(host.errors.length, 2);
assert.deepStrictEqual(host.errors[0][0], idB);
assert.deepStrictEqual(host.errors[1][0], idA);
disposables.dispose();
});
test('issue #144518: Problem with git extension and vscode-icons', async () => {
const disposables = new DisposableStore();
const extActivationA = new ExtensionActivationPromiseSource();
const extActivationB = new ExtensionActivationPromiseSource();
const extActivationC = new ExtensionActivationPromiseSource();
@ -186,7 +212,7 @@ suite('ExtensionsActivator', () => {
desc(idA, [idB]),
desc(idB),
desc(idC),
]);
], [], disposables);
activator.activateByEvent('*', false);
assert.deepStrictEqual(host.activateCalls, [idB, idC]);
@ -196,6 +222,8 @@ suite('ExtensionsActivator', () => {
assert.deepStrictEqual(host.activateCalls, [idB, idC, idA]);
extActivationA.resolve();
disposables.dispose();
});
class SimpleExtensionsActivatorHost implements IExtensionsActivatorHost {
@ -255,10 +283,10 @@ suite('ExtensionsActivator', () => {
}
};
function createActivator(host: IExtensionsActivatorHost, extensionDescriptions: IExtensionDescription[], otherHostExtensionDescriptions: IExtensionDescription[] = []): ExtensionsActivator {
const registry = new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions);
const globalRegistry = new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions.concat(otherHostExtensionDescriptions));
return new ExtensionsActivator(registry, globalRegistry, host, new NullLogService());
function createActivator(host: IExtensionsActivatorHost, extensionDescriptions: IExtensionDescription[], otherHostExtensionDescriptions: IExtensionDescription[] = [], disposables: DisposableStore): ExtensionsActivator {
const registry = disposables.add(new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions));
const globalRegistry = disposables.add(new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions.concat(otherHostExtensionDescriptions)));
return disposables.add(new ExtensionsActivator(registry, globalRegistry, host, new NullLogService()));
}
function desc(id: ExtensionIdentifier, deps: ExtensionIdentifier[] = [], activationEvents: string[] = ['*']): IExtensionDescription {

View File

@ -5,7 +5,7 @@
import { Emitter, Event } from '../../../../../base/common/event.js';
import { IMarkdownString } from '../../../../../base/common/htmlContent.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { ContextKeyExpression } from '../../../../../platform/contextkey/common/contextkey.js';
import { Registry } from '../../../../../platform/registry/common/platform.js';
@ -27,9 +27,9 @@ export interface IChatViewsWelcomeContributionRegistry {
register(descriptor: IChatViewsWelcomeDescriptor): void;
}
class ChatViewsWelcomeContributionRegistry implements IChatViewsWelcomeContributionRegistry {
class ChatViewsWelcomeContributionRegistry extends Disposable implements IChatViewsWelcomeContributionRegistry {
private readonly descriptors: IChatViewsWelcomeDescriptor[] = [];
private readonly _onDidChange = new Emitter<void>();
private readonly _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange: Event<void> = this._onDidChange.event;
public register(descriptor: IChatViewsWelcomeDescriptor): void {

View File

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter } from '../../../../base/common/event.js';
import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
import { URI } from '../../../../base/common/uri.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
@ -36,8 +36,8 @@ export interface IExplorerFileContributionRegistry {
register(descriptor: IExplorerFileContributionDescriptor): void;
}
class ExplorerFileContributionRegistry implements IExplorerFileContributionRegistry {
private readonly _onDidRegisterDescriptor = new Emitter<IExplorerFileContributionDescriptor>();
class ExplorerFileContributionRegistry extends Disposable implements IExplorerFileContributionRegistry {
private readonly _onDidRegisterDescriptor = this._register(new Emitter<IExplorerFileContributionDescriptor>());
public readonly onDidRegisterDescriptor = this._onDidRegisterDescriptor.event;
private readonly descriptors: IExplorerFileContributionDescriptor[] = [];

View File

@ -41,7 +41,7 @@ import { mainWindow } from '../../../../base/browser/window.js';
import { IPreferencesService } from '../../../services/preferences/common/preferences.js';
import { Toggle } from '../../../../base/browser/ui/toggle/toggle.js';
import { defaultToggleStyles } from '../../../../platform/theme/browser/defaultStyles.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
export const manageExtensionIcon = registerIcon('theme-selection-manage-extension', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the theme selection quick pick.'));
@ -53,7 +53,7 @@ enum ConfigureItem {
CUSTOM_TOP_ENTRY = 'customTopEntry'
}
class MarketplaceThemesPicker {
class MarketplaceThemesPicker implements IDisposable {
private readonly _installedExtensions: Promise<Set<string>>;
private readonly _marketplaceExtensions: Set<string> = new Set();
private readonly _marketplaceThemes: ThemeItem[] = [];
@ -275,6 +275,7 @@ class MarketplaceThemesPicker {
this._queryDelayer.dispose();
this._marketplaceExtensions.clear();
this._marketplaceThemes.length = 0;
this._onDidChange.dispose();
}
}

View File

@ -26,7 +26,7 @@ export interface IReadOnlyExtensionDescriptionRegistry {
getExtensionDescriptionByIdOrUUID(extensionId: ExtensionIdentifier | string, uuid: string | undefined): IExtensionDescription | undefined;
}
export class ExtensionDescriptionRegistry implements IReadOnlyExtensionDescriptionRegistry {
export class ExtensionDescriptionRegistry extends Disposable implements IReadOnlyExtensionDescriptionRegistry {
public static isHostExtension(extensionId: ExtensionIdentifier | string, myRegistry: ExtensionDescriptionRegistry, globalRegistry: ExtensionDescriptionRegistry): boolean {
if (myRegistry.getExtensionDescription(extensionId)) {
@ -44,7 +44,7 @@ export class ExtensionDescriptionRegistry implements IReadOnlyExtensionDescripti
return false;
}
private readonly _onDidChange = new Emitter<void>();
private readonly _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange = this._onDidChange.event;
private _versionId: number = 0;
@ -57,6 +57,7 @@ export class ExtensionDescriptionRegistry implements IReadOnlyExtensionDescripti
private readonly _activationEventsReader: IActivationEventsReader,
extensionDescriptions: IExtensionDescription[]
) {
super();
this._extensionDescriptions = extensionDescriptions;
this._initialize();
}

View File

@ -28,6 +28,8 @@ suite('ExtensionDescriptionRegistry', () => {
registry.deltaExtensions([extensionA2], [idA]);
assert.deepStrictEqual(registry.getAllExtensionDescriptions(), [extensionA2]);
registry.dispose();
});
function desc(id: ExtensionIdentifier, version: string, activationEvents: string[] = ['*']): IExtensionDescription {

View File

@ -10,6 +10,7 @@ import { RawContextKey } from '../../../../platform/contextkey/common/contextkey
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { LogLevel } from '../../../../platform/log/common/log.js';
import { Range } from '../../../../editor/common/core/range.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
/**
* Mime type used by the output editor.
@ -271,16 +272,16 @@ export interface IOutputChannelRegistry {
removeChannel(id: string): void;
}
class OutputChannelRegistry implements IOutputChannelRegistry {
class OutputChannelRegistry extends Disposable implements IOutputChannelRegistry {
private channels = new Map<string, IOutputChannelDescriptor>();
private readonly _onDidRegisterChannel = new Emitter<string>();
private readonly _onDidRegisterChannel = this._register(new Emitter<string>());
readonly onDidRegisterChannel = this._onDidRegisterChannel.event;
private readonly _onDidRemoveChannel = new Emitter<IOutputChannelDescriptor>();
private readonly _onDidRemoveChannel = this._register(new Emitter<IOutputChannelDescriptor>());
readonly onDidRemoveChannel = this._onDidRemoveChannel.event;
private readonly _onDidUpdateChannelFiles = new Emitter<IMultiSourceOutputChannelDescriptor>();
private readonly _onDidUpdateChannelFiles = this._register(new Emitter<IMultiSourceOutputChannelDescriptor>());
readonly onDidUpdateChannelSources = this._onDidUpdateChannelFiles.event;
public registerChannel(descriptor: IOutputChannelDescriptor): void {