Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.85.1 OS Version: Ubuntu 23.04 Steps to Reproduce: the command palette using ctrl+shift+p (or cmd+shift+p on mac) the escape key, closing the command palette 7 times that instance count has increased by 7, instance count has increased by 211, instance count has increased by 240 and instance count has increased by 302. The total disposable count has increased by 1137 from to . One class that has increased the most in the above test was . Looking where relates to the quick input I found this code in where actions are created: Perhaps it could be this code that causes the to be created. It seems the actions aren't registered/disposed like other disposables.
Thanks for reporting this! I think I've cleaned up the ... now we use the simpler non-disposable kind... I'll chip away at the rest over time.
Thanks a lot, I see now: No more Actions are leaked (211 -0) Disposable stores are 211 less leaked (240 -29) Emitter are 211 less leaked (302 -91) MutableDisposable instance count still increases (7 -7) I'll try to find more information for the MutableDisposables and DisposableStores where exactly they are created! Thanks again! Edit: The MutableDisposables are created in : They are kept alive by a 200ms timeout and disposed after 200ms so it's no problem: Just the test executing too fast / finishing before the MutableDisposables were disposed. :)

Discussion:
import { IListOptions, IListStyles, List } from 'vs/base/browser/ui/list/listWidget'; import { IProgressBarStyles, ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; <del> import { Action } from 'vs/base/common/actions'; </del> import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons';

import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, IQuickWidget, ItemActivation, NO_KEY_MODS, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; import { QuickInputBox } from './quickInputBox'; import { QuickInputList, QuickInputListFocus } from './quickInputList'; <del> import { getIconClass, renderQuickInputDescription } from './quickInputUtils'; </del> <ins> import { quickInputButtonToAction, renderQuickInputDescription } from './quickInputUtils'; </ins> export interface IQuickInputOptions { idPrefix: string;

if (this.buttonsUpdated) { this.buttonsUpdated = false; this.ui.leftActionBar.clear(); <del> const leftButtons = this.buttons.filter(button => button === backButton); this.ui.leftActionBar.push(leftButtons.map((button, index) => { const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => { this.onDidTriggerButtonEmitter.fire(button); }); action.tooltip = button.tooltip || ''; return action; }), { icon: true, label: false }); </del> <ins> const leftButtons = this.buttons .filter(button => button === backButton) .map((button, index) => quickInputButtonToAction( button, `id-${index}`, async () => this.onDidTriggerButtonEmitter.fire(button) )); this.ui.leftActionBar.push(leftButtons, { icon: true, label: false }); </ins> this.ui.rightActionBar.clear(); <del> const rightButtons = this.buttons.filter(button => button !== backButton); this.ui.rightActionBar.push(rightButtons.map((button, index) => { const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => { this.onDidTriggerButtonEmitter.fire(button); }); action.tooltip = button.tooltip || ''; return action; }), { icon: true, label: false }); </del> <ins> const rightButtons = this.buttons .filter(button => button !== backButton) .map((button, index) => quickInputButtonToAction( button, `id-${index}`, async () => this.onDidTriggerButtonEmitter.fire(button) )); this.ui.rightActionBar.push(rightButtons, { icon: true, label: false }); </ins> } if (this.togglesUpdated) { this.togglesUpdated = false;

import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider, IListOptions, IListStyles, List } from 'vs/base/browser/ui/list/listWidget'; <del> import { IAction } from 'vs/base/common/actions'; </del> import { range } from 'vs/base/common/arrays'; import { ThrottledDelayer } from 'vs/base/common/async'; import { compareAnything } from 'vs/base/common/comparers';

import 'vs/css!./media/quickInput'; import { localize } from 'vs/nls'; import { IQuickInputOptions } from 'vs/platform/quickinput/browser/quickInput'; <del> import { getIconClass } from 'vs/platform/quickinput/browser/quickInputUtils'; </del> <ins> import { quickInputButtonToAction } from 'vs/platform/quickinput/browser/quickInputUtils'; </ins> import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, QuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { Lazy } from 'vs/base/common/lazy'; import { URI } from 'vs/base/common/uri';

// Actions const buttons = mainItem.buttons; if (buttons && buttons.length) { <del> data.actionBar.push(buttons.map((button, index): IAction => { let cssClasses = button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined); if (button.alwaysVisible) { cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible'; } return { id: `id-${index}`, class: cssClasses, enabled: true, label: '', tooltip: button.tooltip || '', run: () => { mainItem.type !== 'separator' ? element.fireButtonTriggered({ button, item: mainItem }) : element.fireSeparatorButtonTriggered({ button, separator: mainItem }); } }; }), { icon: true, label: false }); </del> <ins> data.actionBar.push(buttons.map((button, index) => quickInputButtonToAction( button, `id-${index}`, () => mainItem.type !== 'separator' ? element.fireButtonTriggered({ button, item: mainItem }) : element.fireSeparatorButtonTriggered({ button, separator: mainItem }) )), { icon: true, label: false }); </ins> data.entry.classList.add('has-actions'); } else { data.entry.classList.remove('has-actions');

import 'vs/css!./media/quickInput'; import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; <ins> import { IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; import { IAction } from 'vs/base/common/actions'; </ins> const iconPathToClass: Record<string, string> = {}; const iconClassGenerator = new IdGenerator('quick-input-button-icon-'); <del> export function getIconClass(iconPath: { dark: URI; light?: URI } | undefined): string | undefined { </del> <ins> function getIconClass(iconPath: { dark: URI; light?: URI } | undefined): string | undefined { </ins> if (!iconPath) { return undefined; }

return iconClass; } <ins> export function quickInputButtonToAction(button: IQuickInputButton, id: string, run: () => unknown): IAction { let cssClasses = button.iconClass || getIconClass(button.iconPath); if (button.alwaysVisible) { cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible'; } return { id, label: '', tooltip: button.tooltip || '', class: cssClasses, enabled: true, run }; } </ins> export function renderQuickInputDescription(description: string, container: HTMLElement, actionHandler: { callback: (content: string) => void; disposables: DisposableStore }) { dom.reset(container); const parsed = parseLinkedText(description);