diff --git a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts index 3dc33976b98..df19cf3f10f 100644 --- a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts +++ b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts @@ -32,8 +32,8 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem { dropdownMenuActions: IAction[], className: string, private readonly _contextMenuProvider: IContextMenuProvider, - _keybindingService: IKeybindingService, - _notificationService: INotificationService + @IKeybindingService _keybindingService: IKeybindingService, + @INotificationService _notificationService: INotificationService ) { super(null, primaryAction); this._primaryAction = new MenuEntryActionViewItem(primaryAction, _keybindingService, _notificationService); diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index c6644120aac..998eb37cfd9 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -594,7 +594,7 @@ export interface IBaseUnresolvedTerminalProfile { args?: string | string[] | undefined; isAutoDetected?: boolean; overrideName?: boolean; - icon?: ThemeIcon | URI | { light: URI, dark: URI }; + icon?: string | ThemeIcon | URI | { light: URI, dark: URI }; color?: string; env?: ITerminalEnvironment; } diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 6f9b221b428..ebb842c80fb 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -9,7 +9,7 @@ import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node import * as cp from 'child_process'; import { ILogService } from 'vs/platform/log/common/log'; import * as pfs from 'vs/base/node/pfs'; -import { ITerminalEnvironment, ITerminalProfile, ITerminalProfileObject, ProfileSource, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { ITerminalEnvironment, ITerminalProfile, ITerminalProfileObject, ProfileSource, TerminalIcon, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { Codicon } from 'vs/base/common/codicons'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -156,14 +156,14 @@ async function transformToTerminalProfiles( // if there are configured args, override the default ones args = profile.args || source.args; if (profile.icon) { - icon = profile.icon; + icon = validateIcon(profile.icon); } else if (source.icon) { icon = source.icon; } } else { originalPaths = Array.isArray(profile.path) ? profile.path : [profile.path]; args = isWindows ? profile.args : Array.isArray(profile.args) ? profile.args : undefined; - icon = profile.icon || undefined; + icon = validateIcon(profile.icon) || undefined; } const paths = (await variableResolver?.(originalPaths)) || originalPaths.slice(); @@ -180,6 +180,13 @@ async function transformToTerminalProfiles( return resultProfiles; } +function validateIcon(icon: string | TerminalIcon | undefined): TerminalIcon | undefined { + if (typeof icon === 'string') { + return { id: icon }; + } + return icon; +} + async function initializeWindowsProfiles(testPwshSourcePaths?: string[]): Promise { if (profileSources && !testPwshSourcePaths) { return; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ad6316ee5fd..fbe7f6f59dc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -153,6 +153,7 @@ export interface ITerminalService extends ITerminalInstanceHost { /** * Creates a raw terminal instance, this should not be used outside of the terminal part. */ + createInstance(profile: ITerminalProfile): ITerminalInstance; createInstance(shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; getInstanceFromId(terminalId: number): ITerminalInstance | undefined; getInstanceFromIndex(terminalIndex: number): ITerminalInstance; @@ -197,7 +198,9 @@ export interface ITerminalService extends ITerminalInstanceHost { setEditable(instance: ITerminalInstance, data: IEditableData | null): Promise; safeDisposeTerminal(instance: ITerminalInstance): Promise; - getFindHost(): ITerminalFindHost; + getDefaultInstanceHost(): ITerminalInstanceHost; + getInstanceHost(target: TerminalLocation | undefined): ITerminalInstanceHost; + getFindHost(instance?: ITerminalInstance): ITerminalFindHost; } /** @@ -214,6 +217,7 @@ export interface ITerminalEditorService extends ITerminalInstanceHost, ITerminal getOrCreateEditorInput(instance: ITerminalInstance | SerializedTerminalEditorInput): TerminalEditorInput; detachActiveEditorInstance(): ITerminalInstance; detachInstance(instance: ITerminalInstance): void; + splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance; } /** diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index f59efaa997b..b254ee0e393 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -30,7 +30,7 @@ import { ILocalTerminalService, ITerminalProfile, TerminalSettingId, TitleEventS import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; -import { Direction, IRemoteTerminalService, ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Direction, ICreateTerminalOptions, IRemoteTerminalService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TerminalCommandId, TerminalLocation, TERMINAL_ACTION_CATEGORY } from 'vs/workbench/contrib/terminal/common/terminal'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; @@ -141,22 +141,27 @@ export function registerTerminalActions() { }, }); } - async run(accessor: ServicesAccessor, eventOrProfile: unknown | ITerminalProfile, profile?: ITerminalProfile) { - let event: MouseEvent | undefined; - if (eventOrProfile && typeof eventOrProfile === 'object' && 'profileName' in eventOrProfile) { - profile = eventOrProfile as ITerminalProfile; - } else { - event = eventOrProfile as MouseEvent; - } + async run(accessor: ServicesAccessor, eventOrOptionsOrProfile: MouseEvent | ICreateTerminalOptions | ITerminalProfile | undefined, profile?: ITerminalProfile) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); const workspaceContextService = accessor.get(IWorkspaceContextService); const commandService = accessor.get(ICommandService); + + let event: MouseEvent | PointerEvent | KeyboardEvent | undefined; + let options: ICreateTerminalOptions | undefined; + if (eventOrOptionsOrProfile instanceof MouseEvent || eventOrOptionsOrProfile instanceof PointerEvent || eventOrOptionsOrProfile instanceof KeyboardEvent) { + event = eventOrOptionsOrProfile; + options = profile ? { config: profile } : undefined; + } else { + options = convertOptionsOrProfileToOptions(eventOrOptionsOrProfile); + } + const folders = workspaceContextService.getWorkspace().folders; - if (event instanceof MouseEvent && (event.altKey || event.ctrlKey)) { + if (event && (event.altKey || event.ctrlKey)) { const activeInstance = terminalService.activeInstance; if (activeInstance) { const cwd = await getCwdForSplit(terminalService.configHelper, activeInstance); - terminalService.splitInstance(activeInstance, profile, cwd); + terminalService.splitInstance(activeInstance, options?.config, cwd); return; } } @@ -177,8 +182,8 @@ export function registerTerminalActions() { cwd = workspace.uri; } - if (profile) { - instance = terminalService.createTerminal({ config: profile, cwd }); + if (options) { + instance = terminalService.createTerminal(options); } else { instance = await terminalService.showProfileQuickPick('createInstance', cwd); } @@ -187,7 +192,7 @@ export function registerTerminalActions() { terminalService.setActiveInstance(instance); } } - await accessor.get(ITerminalGroupService).showPanel(true); + await terminalGroupService.showPanel(true); } }); @@ -299,8 +304,9 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - accessor.get(ITerminalGroupService).activeGroup?.focusPreviousPane(); - await accessor.get(ITerminalGroupService).showPanel(true); + const terminalGroupService = accessor.get(ITerminalGroupService); + terminalGroupService.activeGroup?.focusPreviousPane(); + await terminalGroupService.showPanel(true); } }); registerAction2(class extends Action2 { @@ -324,8 +330,9 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - accessor.get(ITerminalGroupService).activeGroup?.focusNextPane(); - await accessor.get(ITerminalGroupService).showPanel(true); + const terminalGroupService = accessor.get(ITerminalGroupService); + terminalGroupService.activeGroup?.focusNextPane(); + await terminalGroupService.showPanel(true); } }); registerAction2(class extends Action2 { @@ -464,8 +471,9 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - accessor.get(ITerminalGroupService).setActiveGroupToNext(); - await accessor.get(ITerminalGroupService).showPanel(true); + const terminalGroupService = accessor.get(ITerminalGroupService); + terminalGroupService.setActiveGroupToNext(); + await terminalGroupService.showPanel(true); } }); registerAction2(class extends Action2 { @@ -487,8 +495,9 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - accessor.get(ITerminalGroupService).setActiveGroupToPrevious(); - await accessor.get(ITerminalGroupService).showPanel(true); + const terminalGroupService = accessor.get(ITerminalGroupService); + terminalGroupService.setActiveGroupToPrevious(); + await terminalGroupService.showPanel(true); } }); registerAction2(class extends Action2 { @@ -503,6 +512,7 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); const codeEditorService = accessor.get(ICodeEditorService); const instance = terminalService.getActiveOrCreateInstance(); @@ -519,7 +529,7 @@ export function registerTerminalActions() { text = editor.getModel().getValueInRange(selection, endOfLinePreference); } instance.sendText(text, true); - return accessor.get(ITerminalGroupService).showPanel(); + return terminalGroupService.showPanel(); } }); registerAction2(class extends Action2 { @@ -534,6 +544,8 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); + const terminalInstanceService = accessor.get(ITerminalInstanceService); const codeEditorService = accessor.get(ICodeEditorService); const notificationService = accessor.get(INotificationService); @@ -550,9 +562,9 @@ export function registerTerminalActions() { // TODO: Convert this to ctrl+c, ctrl+v for pwsh? const instance = terminalService.getActiveOrCreateInstance(); - const path = await accessor.get(ITerminalInstanceService).preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType, instance.isRemote); + const path = await terminalInstanceService.preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType, instance.isRemote); instance.sendText(path, true); - return accessor.get(ITerminalGroupService).showPanel(); + return terminalGroupService.showPanel(); } }); registerAction2(class extends Action2 { @@ -1246,13 +1258,10 @@ export function registerTerminalActions() { }); } run(accessor: ServicesAccessor) { - if (accessor.get(ITerminalService).activeInstance?.target === TerminalLocation.Editor) { - const state = accessor.get(ITerminalEditorService).getFindState(); - state.change({ matchCase: !state.matchCase }, false); - } else { - const state = accessor.get(ITerminalGroupService).getFindState(); - state.change({ matchCase: !state.matchCase }, false); - } + const terminalService = accessor.get(ITerminalService); + const instanceHost = terminalService.getFindHost(); + const state = instanceHost.getFindState(); + state.change({ matchCase: !state.matchCase }, false); } }); registerAction2(class extends Action2 { @@ -1272,13 +1281,10 @@ export function registerTerminalActions() { }); } run(accessor: ServicesAccessor) { - if (accessor.get(ITerminalService).activeInstance?.target === TerminalLocation.Editor) { - const state = accessor.get(ITerminalEditorService).getFindState(); - state.change({ wholeWord: !state.wholeWord }, false); - } else { - const state = accessor.get(ITerminalGroupService).getFindState(); - state.change({ wholeWord: !state.wholeWord }, false); - } + const terminalService = accessor.get(ITerminalService); + const instanceHost = terminalService.getFindHost(); + const state = instanceHost.getFindState(); + state.change({ wholeWord: !state.wholeWord }, false); } }); registerAction2(class extends Action2 { @@ -1298,13 +1304,10 @@ export function registerTerminalActions() { }); } run(accessor: ServicesAccessor) { - if (accessor.get(ITerminalService).activeInstance?.target === TerminalLocation.Editor) { - const state = accessor.get(ITerminalEditorService).getFindState(); - state.change({ matchCase: !state.matchCase }, false); - } else { - const state = accessor.get(ITerminalGroupService).getFindState(); - state.change({ matchCase: !state.matchCase }, false); - } + const terminalService = accessor.get(ITerminalService); + const instanceHost = terminalService.getFindHost(); + const state = instanceHost.getFindState(); + state.change({ matchCase: !state.matchCase }, false); } }); registerAction2(class extends Action2 { @@ -1440,21 +1443,24 @@ export function registerTerminalActions() { } }); } - async run(accessor: ServicesAccessor, profile?: ITerminalProfile) { - const terminalService = accessor.get(ITerminalService); + async run(accessor: ServicesAccessor, optionsOrProfile?: ICreateTerminalOptions | ITerminalProfile) { const commandService = accessor.get(ICommandService); - await terminalService.doWithActiveInstance(async t => { - const cwd = await getCwdForSplit(terminalService.configHelper, t, accessor.get(IWorkspaceContextService).getWorkspace().folders, accessor.get(ICommandService)); - if (cwd === undefined) { - return undefined; - } - if (t.target === TerminalLocation.Editor) { - commandService.executeCommand('workbench.action.splitEditor'); - } else { - terminalService.splitInstance(t, profile, cwd); - return accessor.get(ITerminalGroupService).showPanel(true); - } - }); + const terminalGroupService = accessor.get(ITerminalGroupService); + const terminalService = accessor.get(ITerminalService); + const workspaceContextService = accessor.get(IWorkspaceContextService); + const options = convertOptionsOrProfileToOptions(optionsOrProfile); + const activeInstance = terminalService.getInstanceHost(options?.target).activeInstance; + if (!activeInstance) { + return; + } + const cwd = await getCwdForSplit(terminalService.configHelper, activeInstance, workspaceContextService.getWorkspace().folders, commandService); + if (cwd === undefined) { + return undefined; + } + const instance = terminalService.splitInstance(activeInstance, options?.config, cwd); + if (instance?.target !== TerminalLocation.Editor) { + return terminalGroupService.showPanel(true); + } } }); registerAction2(class extends Action2 { @@ -1478,6 +1484,7 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); const instances = getSelectedInstances(accessor); if (instances) { for (const t of instances) { @@ -1485,7 +1492,7 @@ export function registerTerminalActions() { terminalService.doWithActiveInstance(async instance => { const cwd = await getCwdForSplit(terminalService.configHelper, instance); terminalService.splitInstance(instance, { cwd }); - await accessor.get(ITerminalGroupService).showPanel(true); + await terminalGroupService.showPanel(true); }); } } @@ -1556,14 +1563,12 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); - const commandService = accessor.get(ICommandService); + const terminalGroupService = accessor.get(ITerminalGroupService); await terminalService.doWithActiveInstance(async t => { - if (t.target === TerminalLocation.Editor) { - commandService.executeCommand('workbench.action.splitEditor'); - } else { - const cwd = await getCwdForSplit(terminalService.configHelper, t); - terminalService.splitInstance(t, { cwd }); - await accessor.get(ITerminalGroupService).showPanel(true); + const cwd = await getCwdForSplit(terminalService.configHelper, t); + const instance = terminalService.splitInstance(t, { cwd }); + if (instance?.target !== TerminalLocation.Editor) { + await terminalGroupService.showPanel(true); } }); } @@ -1611,6 +1616,7 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor, event: unknown) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); const workspaceContextService = accessor.get(IWorkspaceContextService); const commandService = accessor.get(ICommandService); const folders = workspaceContextService.getWorkspace().folders; @@ -1646,7 +1652,7 @@ export function registerTerminalActions() { } terminalService.setActiveInstance(instance); } - await accessor.get(ITerminalGroupService).showPanel(true); + await terminalGroupService.showPanel(true); } }); registerAction2(class extends Action2 { @@ -1661,16 +1667,15 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - const terminalService = accessor.get(ITerminalService); - await terminalService.doWithActiveInstance(async t => { - if (t.target === TerminalLocation.Editor) { - return; - } - t.dispose(true); - if (terminalService.instances.length > 0) { - await accessor.get(ITerminalGroupService).showPanel(true); - } - }); + const terminalGroupService = accessor.get(ITerminalGroupService); + const instance = terminalGroupService.activeInstance; + if (!instance) { + return; + } + instance.dispose(true); + if (terminalGroupService.instances.length > 0) { + await terminalGroupService.showPanel(true); + } } }); @@ -1859,6 +1864,7 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor, item?: string) { const terminalService = accessor.get(ITerminalService); + const terminalGroupService = accessor.get(ITerminalGroupService); if (!item || !item.split) { return Promise.resolve(null); } @@ -1872,8 +1878,8 @@ export function registerTerminalActions() { } const indexMatches = terminalIndexRe.exec(item); if (indexMatches) { - accessor.get(ITerminalGroupService).setActiveGroupByIndex(Number(indexMatches[1]) - 1); - return accessor.get(ITerminalGroupService).showPanel(true); + terminalGroupService.setActiveGroupByIndex(Number(indexMatches[1]) - 1); + return terminalGroupService.showPanel(true); } const quickSelectProfiles = terminalService.availableProfiles; @@ -1942,3 +1948,10 @@ export function validateTerminalName(name: string): { content: string, severity: return null; } + +function convertOptionsOrProfileToOptions(optionsOrProfile?: ICreateTerminalOptions | ITerminalProfile): ICreateTerminalOptions | undefined { + if (typeof optionsOrProfile === 'object' && 'profileName' in optionsOrProfile) { + return { config: optionsOrProfile as ITerminalProfile }; + } + return optionsOrProfile; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts index f151e8accec..f30860faa08 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts @@ -4,20 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension } from 'vs/base/browser/dom'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Codicon } from 'vs/base/common/codicons'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { localize } from 'vs/nls'; +import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; +import { IMenu, IMenuActionOptions, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITerminalProfile } from 'vs/platform/terminal/common/terminal'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; -import { ITerminalEditorService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ICreateTerminalOptions, ITerminalEditorService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; -import { KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalTabContextMenuGroup } from 'vs/workbench/contrib/terminal/browser/terminalMenus'; +import { ITerminalProfileResolverService, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, TerminalCommandId, TerminalLocation } from 'vs/workbench/contrib/terminal/common/terminal'; +import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; const xtermSelector = '.terminal.xterm'; @@ -33,6 +43,8 @@ export class TerminalEditor extends EditorPane { private _lastDimension?: Dimension; + private readonly _dropdownMenu: IMenu; + private _findWidget: TerminalFindWidget; private _findWidgetVisible: IContextKey; private _findState: FindReplaceState; @@ -44,13 +56,20 @@ export class TerminalEditor extends EditorPane { @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, + @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, + // @ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService, + @ITerminalService private readonly _terminalService: ITerminalService, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService menuService: IMenuService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, ) { super(TerminalEditor.ID, telemetryService, themeService, storageService); this._findState = new FindReplaceState(); this._findWidget = instantiationService.createInstance(TerminalFindWidget, this._findState); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(contextKeyService); + this._dropdownMenu = this._register(menuService.createMenu(MenuId.TerminalNewDropdownContext, contextKeyService)); } override async setInput(newInput: TerminalEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken) { @@ -96,6 +115,84 @@ export class TerminalEditor extends EditorPane { return this._editorInput?.terminalInstance?.setVisible(visible); } + override getActionViewItem(action: IAction): IActionViewItem | undefined { + switch (action.id) { + case TerminalCommandId.CreateWithProfileButton: { + const actions = this._getTabActionBarArgs(this._terminalService.availableProfiles); + const button = this._instantiationService.createInstance(DropdownWithPrimaryActionViewItem, actions.primaryAction, actions.dropdownAction, actions.dropdownMenuActions, actions.className, this._contextMenuService); + return button; + } + } + return super.getActionViewItem(action); + } + + private _getTabActionBarArgs(profiles: ITerminalProfile[]): { + primaryAction: MenuItemAction, + dropdownAction: IAction, + dropdownMenuActions: IAction[], + className: string, + dropdownIcon?: string + } { + const dropdownActions: IAction[] = []; + const submenuActions: IAction[] = []; + + const defaultProfileName = this._terminalProfileResolverService.defaultProfileName; + for (const p of profiles) { + const isDefault = p.profileName === defaultProfileName; + const options: IMenuActionOptions = { + arg: { + config: p, + target: TerminalLocation.Editor + } as ICreateTerminalOptions, + shouldForwardArgs: true + }; + if (isDefault) { + dropdownActions.unshift(this._instantiationService.createInstance(MenuItemAction, { id: TerminalCommandId.NewWithProfile, title: localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, options)); + submenuActions.unshift(this._instantiationService.createInstance(MenuItemAction, { id: TerminalCommandId.Split, title: localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, options)); + } else { + dropdownActions.push(this._instantiationService.createInstance(MenuItemAction, { id: TerminalCommandId.NewWithProfile, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, options)); + submenuActions.push(this._instantiationService.createInstance(MenuItemAction, { id: TerminalCommandId.Split, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, options)); + } + } + + // TODO: Support contributed profiles with editor target + // for (const contributed of this._terminalContributionService.terminalProfiles) { + // dropdownActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title.replace(/[\n\r\t]/g, ''), undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.extensionIdentifier, contributed.id, false))); + // submenuActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title.replace(/[\n\r\t]/g, ''), undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.extensionIdentifier, contributed.id, true))); + // } + + if (dropdownActions.length > 0) { + dropdownActions.push(new SubmenuAction('split.profile', 'Split...', submenuActions)); + dropdownActions.push(new Separator()); + } + + for (const [, configureActions] of this._dropdownMenu.getActions()) { + for (const action of configureActions) { + // make sure the action is a MenuItemAction + if ('alt' in action) { + dropdownActions.push(action); + } + } + } + + const primaryAction = this._instantiationService.createInstance( + MenuItemAction, + { + id: TerminalCommandId.CreateTerminalEditor, + title: localize('terminal.new', "New Terminal"), + icon: Codicon.plus + }, + { + id: 'workbench.action.splitEditor', + title: terminalStrings.split.value, + icon: Codicon.splitHorizontal + }, + undefined); + + const dropdownAction = new Action('refresh profiles', 'Launch Profile...', 'codicon-chevron-down', true); + return { primaryAction, dropdownAction, dropdownMenuActions: dropdownActions, className: 'terminal-tab-actions' }; + } + focusFindWidget() { if (this._parentElement && !this._parentElement?.querySelector(findWidgetSelector)) { this._parentElement.querySelector(xtermSelector)!.appendChild(this._findWidget.getDomNode()); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts index c972f400ec5..f9802f69658 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts @@ -19,6 +19,7 @@ export class TerminalEditorInput extends EditorInput { static readonly ID = 'workbench.editors.terminal'; private _isDetached = false; + private _copyInstance?: ITerminalInstance; override get typeId(): string { return TerminalEditorInput.ID; @@ -29,10 +30,19 @@ export class TerminalEditorInput extends EditorInput { } override copy(): IEditorInput { - const instance = this._terminalInstanceService.createInstance({}, TerminalLocation.Editor); + const instance = this._copyInstance || this._terminalInstanceService.createInstance({}, TerminalLocation.Editor); + this._copyInstance = undefined; return this._instantiationService.createInstance(TerminalEditorInput, instance); } + /** + * Sets what instance to use for the next call to IEditorInput.copy, this is used to define what + * terminal instance is used when the editor's split command is run. + */ + setCopyInstance(instance: ITerminalInstance) { + this._copyInstance = instance; + } + /** * Returns the terminal instance for this input if it has not yet been detached from the input. */ @@ -48,7 +58,7 @@ export class TerminalEditorInput extends EditorInput { private readonly _terminalInstance: ITerminalInstance, @IThemeService private readonly _themeService: IThemeService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, - private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); this._register(this._terminalInstance.onTitleChanged(() => this._onDidChangeLabel.fire())); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts index f008d5eae96..bee83193e9c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts @@ -6,16 +6,18 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; import { IEditorInput } from 'vs/workbench/common/editor'; -import { ITerminalEditorService, ITerminalFindHost, ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalEditorService, ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalEditor } from 'vs/workbench/contrib/terminal/browser/terminalEditor'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; import { SerializedTerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorSerializer'; import { TerminalLocation } from 'vs/workbench/contrib/terminal/common/terminal'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -export class TerminalEditorService extends Disposable implements ITerminalEditorService, ITerminalFindHost { +export class TerminalEditorService extends Disposable implements ITerminalEditorService { declare _serviceBrand: undefined; instances: ITerminalInstance[] = []; @@ -32,6 +34,7 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor get onDidChangeInstances(): Event { return this._onDidChangeInstances.event; } constructor( + @ICommandService private readonly _commandService: ICommandService, @IEditorService private readonly _editorService: IEditorService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IInstantiationService private readonly _instantiationService: IInstantiationService @@ -125,7 +128,7 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor }); } - getOrCreateEditorInput(instance: ITerminalInstance | SerializedTerminalEditorInput): TerminalEditorInput { + getOrCreateEditorInput(instance: ITerminalInstance | SerializedTerminalEditorInput, isFutureSplit: boolean = false): TerminalEditorInput { let cachedEditor; if ('id' in instance) { cachedEditor = this._editorInputs.get(instance.id); @@ -151,6 +154,14 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor return input; } + splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): ITerminalInstance { + const input = this.getOrCreateEditorInput(instanceToSplit); + const instance = this._terminalInstanceService.createInstance(shellLaunchConfig, TerminalLocation.Editor); + input.setCopyInstance(instance); + this._commandService.executeCommand('workbench.action.splitEditor'); + return instance; + } + detachActiveEditorInstance(): ITerminalInstance { const activeEditor = this._editorService.activeEditor; if (!(activeEditor instanceof TerminalEditorInput)) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 64f19777902..49a8caf1585 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -10,7 +10,7 @@ import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode1 import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILocalTerminalService, IShellLaunchConfig, TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { ILocalTerminalService, IShellLaunchConfig, ITerminalProfile, TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; import { basename } from 'vs/base/common/path'; @@ -19,6 +19,7 @@ import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, TerminalLocation } from 'vs/workbench/contrib/terminal/common/terminal'; +import { URI } from 'vs/base/common/uri'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; @@ -47,20 +48,48 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst this._configHelper = _instantiationService.createInstance(TerminalConfigHelper); } - createInstance(launchConfig: IShellLaunchConfig, target?: TerminalLocation): ITerminalInstance { + createInstance(profile: ITerminalProfile, target?: TerminalLocation): ITerminalInstance; + createInstance(shellLaunchConfig: IShellLaunchConfig, target?: TerminalLocation): ITerminalInstance; + createInstance(config: IShellLaunchConfig | ITerminalProfile, target?: TerminalLocation): ITerminalInstance { + const shellLaunchConfig = this._convertProfileToShellLaunchConfig(config); const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalFocusContextKey, this._terminalShellTypeContextKey, this._terminalAltBufferActiveContextKey, this._configHelper, - launchConfig + shellLaunchConfig ); - if (target) { - instance.target = TerminalLocation.Editor; - } + instance.target = target; return instance; } + private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + // Profile was provided + if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { + const profile = shellLaunchConfigOrProfile; + return { + executable: profile.path, + args: profile.args, + env: profile.env, + icon: profile.icon, + color: profile.color, + name: profile.overrideName ? profile.profileName : undefined, + cwd + }; + } + + // Shell launch config was provided + if (shellLaunchConfigOrProfile) { + if (cwd) { + shellLaunchConfigOrProfile.cwd = cwd; + } + return shellLaunchConfigOrProfile; + } + + // Return empty shell launch config + return {}; + } + async getXtermConstructor(): Promise { if (!Terminal) { Terminal = (await import('xterm')).Terminal; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index 93bfeecd360..174b7397e12 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -529,4 +529,14 @@ export function setupTerminalMenus(): void { when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeTerminal), group: '2_files' }); + + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: TerminalCommandId.CreateWithProfileButton, + title: TerminalCommandId.CreateWithProfileButton + }, + group: 'navigation', + order: 0, + when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeTerminal) + }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index aefa304fc97..37a7f8fb982 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -597,10 +597,6 @@ export class TerminalService implements ITerminalService { splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance | null; splitInstance(instanceToSplit: ITerminalInstance, profile: ITerminalProfile, cwd?: string | URI): ITerminalInstance | null splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile = {}, cwd?: string | URI): ITerminalInstance | null { - const group = this._terminalGroupService.getGroupForInstance(instanceToSplit); - if (!group) { - return null; - } const shellLaunchConfig = this._convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile, cwd); // Use the URI from the base instance if it exists, this will correctly split local terminals @@ -612,11 +608,27 @@ export class TerminalService implements ITerminalService { this._evaluateLocalCwd(shellLaunchConfig); } - const instance = group.split(shellLaunchConfig); + // Handle editor terminals + let instance: ITerminalInstance; + switch (instanceToSplit.target) { + case TerminalLocation.Editor: + instance = this._terminalEditorService.splitInstance(instanceToSplit, shellLaunchConfig); + break; + case TerminalLocation.TerminalView: + default: + const group = this._terminalGroupService.getGroupForInstance(instanceToSplit); + if (!group) { + return null; + } + instance = group.split(shellLaunchConfig); + break; + } this._initInstanceListeners(instance); - this._terminalGroupService.groups.forEach((g, i) => g.setVisible(i === this._terminalGroupService.activeGroupIndex)); + if (instanceToSplit.target !== TerminalLocation.Editor) { + this._terminalGroupService.groups.forEach((g, i) => g.setVisible(i === this._terminalGroupService.activeGroupIndex)); + } return instance; } @@ -865,7 +877,7 @@ export class TerminalService implements ITerminalService { return; } if (type === 'createInstance') { - const activeInstance = this.activeInstance; + const activeInstance = this.getDefaultInstanceHost().activeInstance; let instance; if ('id' in value.profile) { @@ -910,8 +922,25 @@ export class TerminalService implements ITerminalService { return undefined; } - getFindHost(): ITerminalFindHost { - return this.activeInstance?.target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; + getDefaultInstanceHost(): ITerminalInstanceHost { + if (this.configHelper.config.defaultLocation === TerminalLocation.Editor) { + return this._terminalEditorService; + } + return this._terminalGroupService; + } + + getInstanceHost(target: TerminalLocation | undefined): ITerminalInstanceHost { + if (target) { + if (target === TerminalLocation.Editor) { + return this._terminalEditorService; + } + return this._terminalGroupService; + } + return this; + } + + getFindHost(instance: ITerminalInstance | undefined = this.activeInstance): ITerminalFindHost { + return instance?.target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; } async createContributedTerminalProfile(extensionIdentifier: string, id: string, isSplitTerminal: boolean): Promise { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index d33d6f1e077..0d9c3777d41 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -14,17 +14,17 @@ import { IThemeService, IColorTheme, registerThemingParticipant, ICssStyleCollec import { switchTerminalActionViewItemSeparator, switchTerminalShowTabsTitle } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR, TERMINAL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { ITerminalGroupService, ITerminalInstance, ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ICreateTerminalOptions, ITerminalGroupService, ITerminalInstance, ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; -import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { ITerminalProfileResolverService, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IMenu, IMenuActionOptions, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { ITerminalProfileResolverService, TerminalCommandId, TerminalLocation } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalSettingId, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -43,6 +43,7 @@ import { URI } from 'vs/base/common/uri'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class TerminalViewPane extends ViewPane { private _actions: IAction[] | undefined; @@ -178,6 +179,26 @@ export class TerminalViewPane extends ViewPane { override getActionViewItem(action: Action): IActionViewItem | undefined { switch (action.id) { + case TerminalCommandId.Split: { + // Split needs to be special cased to force splitting within the panel, not the editor + const panelOnlySplitAction: IAction = { + id: action.id, + checked: action.checked, + class: action.class, + enabled: action.enabled, + label: action.label, + dispose: action.dispose.bind(action), + tooltip: action.tooltip, + run: () => { + const instance = this._terminalGroupService.activeInstance; + if (instance) { + return this._terminalService.splitInstance(instance); + } + return; + } + }; + return new ActionViewItem(action, panelOnlySplitAction, { icon: true, label: false, keybinding: this._getKeybindingLabel(action) }); + } case TerminalCommandId.SwitchTerminal: { return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action); } @@ -200,6 +221,10 @@ export class TerminalViewPane extends ViewPane { return super.getActionViewItem(action); } + private _getKeybindingLabel(action: IAction): string | undefined { + return withNullAsUndefined(this._keybindingService.lookupKeybinding(action.id)?.getLabel()); + } + private _updateTabActionBar(profiles: ITerminalProfile[]): void { const actions = this._getTabActionBarArgs(profiles); this._tabButtons?.update(actions.dropdownAction, actions.dropdownMenuActions); @@ -218,12 +243,19 @@ export class TerminalViewPane extends ViewPane { const defaultProfileName = this._terminalProfileResolverService.defaultProfileName; for (const p of profiles) { const isDefault = p.profileName === defaultProfileName; + const options: IMenuActionOptions = { + arg: { + config: p, + target: TerminalLocation.TerminalView + } as ICreateTerminalOptions, + shouldForwardArgs: true + }; if (isDefault) { - dropdownActions.unshift(new MenuItemAction({ id: TerminalCommandId.NewWithProfile, title: nls.localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, { arg: p, shouldForwardArgs: true }, this._contextKeyService, this._commandService)); - submenuActions.unshift(new MenuItemAction({ id: TerminalCommandId.Split, title: nls.localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, { arg: p, shouldForwardArgs: true }, this._contextKeyService, this._commandService)); + dropdownActions.unshift(new MenuItemAction({ id: TerminalCommandId.NewWithProfile, title: nls.localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, options, this._contextKeyService, this._commandService)); + submenuActions.unshift(new MenuItemAction({ id: TerminalCommandId.Split, title: nls.localize('defaultTerminalProfile', "{0} (Default)", p.profileName), category: TerminalTabContextMenuGroup.Profile }, undefined, options, this._contextKeyService, this._commandService)); } else { - dropdownActions.push(new MenuItemAction({ id: TerminalCommandId.NewWithProfile, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, { arg: p, shouldForwardArgs: true }, this._contextKeyService, this._commandService)); - submenuActions.push(new MenuItemAction({ id: TerminalCommandId.Split, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, { arg: p, shouldForwardArgs: true }, this._contextKeyService, this._commandService)); + dropdownActions.push(new MenuItemAction({ id: TerminalCommandId.NewWithProfile, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, options, this._contextKeyService, this._commandService)); + submenuActions.push(new MenuItemAction({ id: TerminalCommandId.Split, title: p.profileName.replace(/[\n\r\t]/g, ''), category: TerminalTabContextMenuGroup.Profile }, undefined, options, this._contextKeyService, this._commandService)); } } @@ -258,7 +290,10 @@ export class TerminalViewPane extends ViewPane { title: terminalStrings.split.value, icon: Codicon.splitHorizontal }, - undefined); + { + shouldForwardArgs: true, + arg: { target: TerminalLocation.TerminalView } as ICreateTerminalOptions, + }); const dropdownAction = new Action('refresh profiles', 'Launch Profile...', 'codicon-chevron-down', true); return { primaryAction, dropdownAction, dropdownMenuActions: dropdownActions, className: 'terminal-tab-actions' }; @@ -410,7 +445,7 @@ class SingleTerminalTabActionViewItem extends MenuEntryActionViewItem { override async onClick(event: MouseEvent): Promise { if (event.altKey && this._menuItemAction.alt) { - this._commandService.executeCommand(this._menuItemAction.alt.id); + this._commandService.executeCommand(this._menuItemAction.alt.id, { target: TerminalLocation.TerminalView } as ICreateTerminalOptions); } else { this._openContextMenu(); }