From 7352793725ebd1d7e5ef2004d9fab0ae9046df3b Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 19 Sep 2024 16:13:34 +0200 Subject: [PATCH 1/4] chat - add command center entry for global chat action --- src/vs/platform/actions/common/actions.ts | 1 + .../chat/browser/actions/chatActions.ts | 20 +++++++++++++++++-- .../browser/actions/chatContextActions.ts | 19 +++++++++++++++--- .../browser/actions/chatQuickInputActions.ts | 5 +++++ .../contrib/chat/browser/chat.contribution.ts | 5 +++++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index ca3f1eec12998..88f57f951932f 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -227,6 +227,7 @@ export class MenuId { static readonly ChatInputSide = new MenuId('ChatInputSide'); static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext'); static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext'); + static readonly ChatCommandCenter = new MenuId('ChatCommandCenter'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar'); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index a432f89aed396..6349fc7c926fa 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -12,7 +12,7 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { EditorAction2, ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { localize, localize2 } from '../../../../../nls.js'; -import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IsLinuxContext, IsWindowsContext } from '../../../../../platform/contextkey/common/contextkeys.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; @@ -67,7 +67,7 @@ class OpenChatGlobalAction extends Action2 { id: CHAT_OPEN_ACTION_ID, title: localize2('openChat', "Open Chat"), icon: Codicon.commentDiscussion, - f1: false, + f1: true, category: CHAT_CATEGORY, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -75,6 +75,11 @@ class OpenChatGlobalAction extends Action2 { mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI } + }, + menu: { + id: MenuId.ChatCommandCenter, + group: 'navigation', + order: 1 } }); } @@ -348,3 +353,14 @@ export function stringifyItem(item: IChatRequestViewModel | IChatResponseViewMod return (includeName ? `${item.username}: ` : '') + item.response.toString(); } } + + +// --- command center chat + +MenuRegistry.appendMenuItem(MenuId.CommandCenter, { + submenu: MenuId.ChatCommandCenter, + title: localize('title4', "Chat"), + icon: Codicon.commentDiscussion, + when: ContextKeyExpr.and(CONTEXT_CHAT_ENABLED, ContextKeyExpr.has('config.chat.commandCenter.enabled')), + order: 10001, +}); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index 81f4e2391665d..594d41b01d305 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -35,6 +35,7 @@ import { ILanguageModelToolsService } from '../../common/languageModelToolsServi import { AnythingQuickAccessProvider } from '../../../search/browser/anythingQuickAccess.js'; import { ISymbolQuickPickItem, SymbolsQuickAccessProvider } from '../../../search/browser/symbolsQuickAccess.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; +import { ActiveEditorContext } from '../../../../common/contextkeys.js'; export function registerChatContextActions() { registerAction2(AttachContextAction); @@ -98,9 +99,15 @@ class AttachFileAction extends Action2 { constructor() { super({ id: AttachFileAction.ID, - title: localize2('workbench.action.chat.attachFile.label', "Attach File"), + title: localize2('workbench.action.chat.attachFile.label', "Add File to Chat"), category: CHAT_CATEGORY, - f1: false + f1: false, + precondition: ActiveEditorContext.isEqualTo('workbench.editors.files.textFileEditor'), + menu: { + id: MenuId.ChatCommandCenter, + group: 'attach', + order: 1, + } }); } @@ -124,7 +131,13 @@ class AttachSelectionAction extends Action2 { id: AttachSelectionAction.ID, title: localize2('workbench.action.chat.attachSelection.label', "Add Selection to Chat"), category: CHAT_CATEGORY, - f1: false + f1: false, + precondition: ActiveEditorContext.isEqualTo('workbench.editors.files.textFileEditor'), + menu: { + id: MenuId.ChatCommandCenter, + group: 'attach', + order: 2, + } }); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index c035d6c4aed7b..7283f63612367 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -114,6 +114,11 @@ class QuickChatGlobalAction extends Action2 { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KeyI } }, + menu: { + id: MenuId.ChatCommandCenter, + group: 'navigation', + order: 5 + }, metadata: { description: localize('toggle.desc', 'Toggle the quick chat'), args: [{ diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index c81f94fb7c24d..55a62f312d767 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -99,6 +99,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('interactiveSession.editor.lineHeight', "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size."), default: 0 }, + 'chat.commandCenter.enabled': { + type: 'boolean', + markdownDescription: nls.localize('chat.commandCenter.enabled', "Controls whether the command center shows a menu for chat actions (requires {0}).", '`#window.commandCenter#`'), + default: true + }, 'chat.experimental.implicitContext': { type: 'boolean', description: nls.localize('chat.experimental.implicitContext', "Controls whether a checkbox is shown to allow the user to determine which implicit context is included with a chat participant's prompt."), From 13e8b84d719456b9becf39feaadd9573ae0038f2 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 19 Sep 2024 16:48:07 +0200 Subject: [PATCH 2/4] add `IActionViewItemService` that allows the chat feature to style the command center chat submenu to use the default agents icon --- .../actions/browser/actionViewItemService.ts | 67 +++++++++++++++++++ src/vs/platform/actions/browser/toolbar.ts | 28 +++++++- .../chat/browser/actions/chatActions.ts | 54 ++++++++++++++- .../contrib/chat/browser/chat.contribution.ts | 3 +- 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 src/vs/platform/actions/browser/actionViewItemService.ts diff --git a/src/vs/platform/actions/browser/actionViewItemService.ts b/src/vs/platform/actions/browser/actionViewItemService.ts new file mode 100644 index 0000000000000..5690bd8e6b842 --- /dev/null +++ b/src/vs/platform/actions/browser/actionViewItemService.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IActionViewItemProvider } from '../../../base/browser/ui/actionbar/actionbar.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { MenuId } from '../common/actions.js'; + + +export const IActionViewItemService = createDecorator('IActionViewItemService'); + +export interface IActionViewItemService { + + _serviceBrand: undefined; + + onDidChange: Event; + + register(menu: MenuId, commandId: string, provider: IActionViewItemProvider, event?: Event): IDisposable; + + lookUp(menu: MenuId, commandId: string): IActionViewItemProvider | undefined; +} + + +class ActionViewItemService implements IActionViewItemService { + + declare _serviceBrand: undefined; + + private readonly _providers = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + + dispose(): void { + this._onDidChange.dispose(); + } + + register(menu: MenuId, commandId: string, provider: IActionViewItemProvider, event?: Event): IDisposable { + const id = this._makeKey(menu, commandId); + if (this._providers.has(id)) { + throw new Error(`A provider for the command ${commandId} and menu ${menu} is already registered.`); + } + this._providers.set(id, provider); + + const listener = event?.(() => { + this._onDidChange.fire(menu); + }); + + return toDisposable(() => { + listener?.dispose(); + this._providers.delete(id); + }); + } + + lookUp(menu: MenuId, commandId: string): IActionViewItemProvider | undefined { + return this._providers.get(this._makeKey(menu, commandId)); + } + + private _makeKey(menu: MenuId, commandId: string) { + return menu.id + commandId; + } +} + +registerSingleton(IActionViewItemService, ActionViewItemService, InstantiationType.Delayed); diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index 2099111ee0da9..270e7c1433ee1 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -14,7 +14,7 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Iterable } from '../../../base/common/iterator.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; import { localize } from '../../../nls.js'; -import { createAndFillInActionBarActions } from './menuEntryActionViewItem.js'; +import { createActionViewItem, createAndFillInActionBarActions } from './menuEntryActionViewItem.js'; import { IMenuActionOptions, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from '../common/actions.js'; import { createConfigureKeybindingAction } from '../common/menuService.js'; import { ICommandService } from '../../commands/common/commands.js'; @@ -22,6 +22,8 @@ import { IContextKeyService } from '../../contextkey/common/contextkey.js'; import { IContextMenuService } from '../../contextview/browser/contextView.js'; import { IKeybindingService } from '../../keybinding/common/keybinding.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; +import { IActionViewItemService } from './actionViewItemService.js'; +import { IInstantiationService } from '../../instantiation/common/instantiation.js'; export const enum HiddenItemStrategy { /** This toolbar doesn't support hiding*/ @@ -335,8 +337,24 @@ export class MenuWorkbenchToolBar extends WorkbenchToolBar { @IKeybindingService keybindingService: IKeybindingService, @ICommandService commandService: ICommandService, @ITelemetryService telemetryService: ITelemetryService, + @IActionViewItemService actionViewService: IActionViewItemService, + @IInstantiationService instaService: IInstantiationService, ) { - super(container, { resetMenu: menuId, ...options }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService); + super(container, { + resetMenu: menuId, + ...options, + actionViewItemProvider: (action, opts) => { + let provider = actionViewService.lookUp(menuId, action.id); + if (!provider) { + provider = options?.actionViewItemProvider; + } + const viewItem = provider?.(action, opts); + if (viewItem) { + return viewItem; + } + return createActionViewItem(instaService, action, options); + } + }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService); // update logic const menu = this._store.add(menuService.createMenu(menuId, contextKeyService, { emitEventsForSubmenuChanges: true })); @@ -357,6 +375,12 @@ export class MenuWorkbenchToolBar extends WorkbenchToolBar { updateToolbar(); this._onDidChangeMenuItems.fire(this); })); + + this._store.add(actionViewService.onDidChange(e => { + if (e === menuId) { + updateToolbar(); + } + })); updateToolbar(); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 6349fc7c926fa..04ac36c73de3e 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -12,7 +12,7 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { EditorAction2, ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { localize, localize2 } from '../../../../../nls.js'; -import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2, SubmenuItemAction } from '../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IsLinuxContext, IsWindowsContext } from '../../../../../platform/contextkey/common/contextkeys.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; @@ -29,6 +29,12 @@ import { IChatWidgetHistoryService } from '../../common/chatWidgetHistoryService import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { ACTIVE_GROUP, IEditorService } from '../../../../services/editor/common/editorService.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; +import { IWorkbenchContribution } from '../../../../common/contributions.js'; +import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; +import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { SubmenuEntryActionViewItem } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { assertType } from '../../../../../base/common/types.js'; export interface IChatViewTitleActionContext { chatView: ChatViewPane; @@ -364,3 +370,49 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { when: ContextKeyExpr.and(CONTEXT_CHAT_ENABLED, ContextKeyExpr.has('config.chat.commandCenter.enabled')), order: 10001, }); + +export class ChatCommandCenterRendering implements IWorkbenchContribution { + + static readonly ID = 'chat.commandCenterRendering'; + + private readonly _store = new DisposableStore(); + + constructor( + @IActionViewItemService actionViewItemService: IActionViewItemService, + @IChatAgentService agentService: IChatAgentService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + + // TODO@jrieken this isn't proper + const key = `submenuitem.${MenuId.ChatCommandCenter.id}`; + + this._store.add(actionViewItemService.register(MenuId.CommandCenter, key, (action, options) => { + + const agent = agentService.getDefaultAgent(ChatAgentLocation.Panel); + if (!agent?.metadata.themeIcon) { + return undefined; + } + + if (!(action instanceof SubmenuItemAction)) { + return undefined; + } + + return instantiationService.createInstance(class extends SubmenuEntryActionViewItem { + + override render(container: HTMLElement): void { + super.render(container); + assertType(this.element); + + const icon = ThemeIcon.asClassNameArray(agent.metadata.themeIcon!); + this.element.classList.add(...icon); + } + + }, action, options); + + }, agentService.onDidChangeAgents)); + } + + dispose() { + this._store.dispose(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 55a62f312d767..31402f5a47d47 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -19,7 +19,7 @@ import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/edit import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; import { ChatAccessibilityHelp } from './actions/chatAccessibilityHelp.js'; -import { registerChatActions } from './actions/chatActions.js'; +import { ChatCommandCenterRendering, registerChatActions } from './actions/chatActions.js'; import { ACTION_ID_NEW_CHAT, registerNewChatActions } from './actions/chatClearActions.js'; import { registerChatCodeBlockActions, registerChatCodeCompareBlockActions } from './actions/chatCodeblockActions.js'; import { registerChatContextActions } from './actions/chatContextActions.js'; @@ -272,6 +272,7 @@ registerWorkbenchContribution2(ChatExtensionPointHandler.ID, ChatExtensionPointH registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually); registerWorkbenchContribution2(ChatGettingStartedContribution.ID, ChatGettingStartedContribution, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(ChatCommandCenterRendering.ID, ChatCommandCenterRendering, WorkbenchPhase.AfterRestored); registerChatActions(); registerChatCopyActions(); From 7fe225727eb906c16594ded2f949fd97e52cfc7f Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 19 Sep 2024 17:29:06 +0200 Subject: [PATCH 3/4] add missing null service for tests --- .../actions/browser/actionViewItemService.ts | 13 +++++++++++++ .../test/browser/inlineChatController.test.ts | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/vs/platform/actions/browser/actionViewItemService.ts b/src/vs/platform/actions/browser/actionViewItemService.ts index 5690bd8e6b842..fa5739eec6520 100644 --- a/src/vs/platform/actions/browser/actionViewItemService.ts +++ b/src/vs/platform/actions/browser/actionViewItemService.ts @@ -24,6 +24,19 @@ export interface IActionViewItemService { lookUp(menu: MenuId, commandId: string): IActionViewItemProvider | undefined; } +export class NullActionViewItemService implements IActionViewItemService { + _serviceBrand: undefined; + + onDidChange: Event = Event.None; + + register(menu: MenuId, commandId: string, provider: IActionViewItemProvider, event?: Event): IDisposable { + return toDisposable(() => { }); + } + + lookUp(menu: MenuId, commandId: string): IActionViewItemProvider | undefined { + return undefined; + } +} class ActionViewItemService implements IActionViewItemService { diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 02e6af0ef3f60..bf65a8ad638a9 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -66,6 +66,7 @@ import { IInlineChatSessionService } from '../../browser/inlineChatSessionServic import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl.js'; import { TestWorkerService } from './testWorkerService.js'; import { ILanguageModelsService, LanguageModelsService } from '../../../chat/common/languageModels.js'; +import { IActionViewItemService, NullActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; suite('InteractiveChatController', function () { @@ -131,6 +132,7 @@ suite('InteractiveChatController', function () { const serviceCollection = new ServiceCollection( [IConfigurationService, new TestConfigurationService()], [IChatVariablesService, new SyncDescriptor(ChatVariablesService)], + [IActionViewItemService, new SyncDescriptor(NullActionViewItemService)], [ILogService, new NullLogService()], [ITelemetryService, NullTelemetryService], [IHoverService, NullHoverService], From e237898520afb9135b4ff32c65a53da55331841d Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 19 Sep 2024 18:08:14 +0200 Subject: [PATCH 4/4] add missing null service for tests --- .../inlineChat/test/browser/inlineChatController.test.ts | 2 -- src/vs/workbench/test/browser/workbenchTestServices.ts | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index bf65a8ad638a9..02e6af0ef3f60 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -66,7 +66,6 @@ import { IInlineChatSessionService } from '../../browser/inlineChatSessionServic import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl.js'; import { TestWorkerService } from './testWorkerService.js'; import { ILanguageModelsService, LanguageModelsService } from '../../../chat/common/languageModels.js'; -import { IActionViewItemService, NullActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; suite('InteractiveChatController', function () { @@ -132,7 +131,6 @@ suite('InteractiveChatController', function () { const serviceCollection = new ServiceCollection( [IConfigurationService, new TestConfigurationService()], [IChatVariablesService, new SyncDescriptor(ChatVariablesService)], - [IActionViewItemService, new SyncDescriptor(NullActionViewItemService)], [ILogService, new NullLogService()], [ITelemetryService, NullTelemetryService], [IHoverService, NullHoverService], diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 742212cbe2206..f25d89a5e4dd8 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -181,6 +181,7 @@ import { EnvironmentVariableService } from '../../contrib/terminal/common/enviro import { ContextMenuService } from '../../../platform/contextview/browser/contextMenuService.js'; import { IHoverService } from '../../../platform/hover/browser/hover.js'; import { NullHoverService } from '../../../platform/hover/test/browser/nullHoverService.js'; +import { IActionViewItemService, NullActionViewItemService } from '../../../platform/actions/browser/actionViewItemService.js'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -257,7 +258,10 @@ export function workbenchInstantiationService( }, disposables: Pick = new DisposableStore() ): TestInstantiationService { - const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, disposables.add(new TestLifecycleService())]))); + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection( + [ILifecycleService, disposables.add(new TestLifecycleService())], + [IActionViewItemService, new SyncDescriptor(NullActionViewItemService)], + ))); instantiationService.stub(IProductService, TestProductService); instantiationService.stub(IEditorWorkerService, new TestEditorWorkerService());