diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index b64f47fad6d..c5c03a86786 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -178,6 +178,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._linkProvider = undefined; } + public $registerProcessSupport(isSupported: boolean): void { + this._terminalService.registerProcessSupport(isSupported); + } + private _onActiveTerminalChanged(terminalId: number | null): void { this._proxy.$acceptActiveTerminalChanged(terminalId); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e09f81490e8..9baee2b3b22 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -450,6 +450,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $stopSendingDataEvents(): void; $startLinkProvider(): void; $stopLinkProvider(): void; + $registerProcessSupport(isSupported: boolean): void; $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void; // Process diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index ad449efd0da..7107003f8f7 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -339,6 +339,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public get onDidWriteTerminalData(): Event { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; } constructor( + supportsProcesses: boolean, @IExtHostRpcService extHostRpc: IExtHostRpcService ) { this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService); @@ -347,6 +348,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ onFirstListenerAdd: () => this._proxy.$startSendingDataEvents(), onLastListenerRemove: () => this._proxy.$stopSendingDataEvents() }); + this._proxy.$registerProcessSupport(supportsProcesses); } public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal; @@ -805,6 +807,12 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable } export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService + ) { + super(false, extHostRpc); + } + public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { throw new NotSupportedError(); } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index c777688550a..54d99efad93 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -39,7 +39,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { @IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, @ILogService private _logService: ILogService ) { - super(extHostRpc); + super(true, extHostRpc); this._updateLastActiveWorkspace(); this._updateVariableResolver(); this._registerListeners(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index a2915b88d67..45fe642e2df 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -21,7 +21,7 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; import { registerTerminalActions, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, KillTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, ToggleTerminalAction, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED } from 'vs/workbench/contrib/terminal/common/terminal'; import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; @@ -88,7 +88,7 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(KillTerminalAct actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CreateNewTerminalAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } -}), 'Terminal: Create New Integrated Terminal', category); +}), 'Terminal: Create New Integrated Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAllTerminalAction, { // Don't use ctrl+a by default as that would override the common go to start // of prompt shell binding diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index a1f25f55fb9..a7db7423cfa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -76,6 +76,7 @@ export interface ITerminalService { configHelper: ITerminalConfigHelper; terminalInstances: ITerminalInstance[]; terminalTabs: ITerminalTab[]; + isProcessSupportRegistered: boolean; onActiveTabChanged: Event; onTabDisposed: Event; @@ -90,6 +91,7 @@ export interface ITerminalService { onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; onRequestAvailableShells: Event; + onDidRegisterProcessSupport: Event; /** * Creates a terminal. @@ -136,6 +138,7 @@ export interface ITerminalService { findNext(): void; findPrevious(): void; + registerProcessSupport(isSupported: boolean): void; /** * Registers a link provider that enables integrators to add links to the terminal. * @param linkProvider When registered, the link provider is asked whenever a cell is hovered diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index dd4b2216ff2..bbb14f6a0b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -6,7 +6,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED } from 'vs/workbench/contrib/terminal/common/terminal'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -367,7 +367,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this)); this._register(_terminalService.onTabDisposed(this._updateItems, this)); - this._register(attachSelectBoxStyler(this.selectBox, _themeService)); + this._register(attachSelectBoxStyler(this.selectBox, this._themeService)); } render(container: HTMLElement): void { @@ -437,7 +437,8 @@ export function registerTerminalActions() { id: TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE, title: { value: localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"), original: 'Create New Integrated Terminal (In Active Workspace)' }, f1: true, - category + category, + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 15154160cbd..de6a1f38ff7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -23,12 +23,13 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; +import { isWindows, isMacintosh, OperatingSystem, isWeb } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; import { find } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; interface IExtHostReadyEntry { promise: Promise; @@ -52,10 +53,12 @@ export class TerminalService implements ITerminalService { private _activeTabIndex: number; private _linkProviders: Set = new Set(); private _linkProviderDisposables: Map = new Map(); + private _processSupportContextKey: IContextKey; public get activeTabIndex(): number { return this._activeTabIndex; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } + public get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); } private _configHelper: TerminalConfigHelper; private _terminalContainer: HTMLElement | undefined; @@ -91,6 +94,8 @@ export class TerminalService implements ITerminalService { public get onTabDisposed(): Event { return this._onTabDisposed.event; } private readonly _onRequestAvailableShells = new Emitter(); public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } + private readonly _onDidRegisterProcessSupport = new Emitter(); + public get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @@ -103,7 +108,8 @@ export class TerminalService implements ITerminalService { @IQuickInputService private _quickInputService: IQuickInputService, @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, - @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -121,7 +127,9 @@ export class TerminalService implements ITerminalService { }); this.onInstanceLinksReady(instance => this._setInstanceLinkProviders(instance)); - this._handleContextKeys(); + this._handleInstanceContextKeys(); + this._processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED.bindTo(this._contextKeyService); + this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); } public setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void { @@ -132,13 +140,11 @@ export class TerminalService implements ITerminalService { this._configHelper.setLinuxDistro(linuxDistro); } - private _handleContextKeys(): void { + private _handleInstanceContextKeys(): void { const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); - const updateTerminalContextKeys = () => { terminalIsOpenContext.set(this.terminalInstances.length > 0); }; - this.onInstancesChanged(() => updateTerminalContextKeys()); } @@ -411,6 +417,14 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); } + public registerProcessSupport(isSupported: boolean): void { + if (!isSupported) { + return; + } + this._processSupportContextKey.set(isSupported); + this._onDidRegisterProcessSupport.fire(); + } + public registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable { const disposables: IDisposable[] = []; this._linkProviders.add(linkProvider); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 55d0d5e1865..c526770faa6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -19,7 +19,6 @@ import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -56,14 +55,25 @@ export class TerminalViewPane extends ViewPane { @IThemeService protected readonly themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService private readonly _notificationService: INotificationService, - @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, ) { super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService); + this._terminalService.onDidRegisterProcessSupport(() => { + if (this._actions) { + for (const action of this._actions) { + action.enabled = true; + } + } + this._onDidChangeViewWelcomeState.fire(); + }); } protected renderBody(container: HTMLElement): void { super.renderBody(container); + if (this.shouldShowWelcome()) { + return; + } + this._parentDomElement = container; dom.addClass(this._parentDomElement, 'integrated-terminal'); this._fontStyleElement = document.createElement('style'); @@ -120,6 +130,10 @@ export class TerminalViewPane extends ViewPane { protected layoutBody(height: number, width: number): void { super.layoutBody(height, width); + if (this.shouldShowWelcome()) { + return; + } + this._bodyDimensions.width = width; this._bodyDimensions.height = height; this._terminalService.terminalTabs.forEach(t => t.layout(width, height)); @@ -138,9 +152,12 @@ export class TerminalViewPane extends ViewPane { this._splitTerminalAction, this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) ]; - this._actions.forEach(a => { - this._register(a); - }); + for (const action of this._actions) { + if (!this._terminalService.isProcessSupportRegistered) { + action.enabled = false; + } + this._register(action); + } } return this._actions; } @@ -180,7 +197,7 @@ export class TerminalViewPane extends ViewPane { } public getActionViewItem(action: Action): IActionViewItem | undefined { - if (action.id === SwitchTerminalAction.ID) { + if (action.id === SwitchTerminalAction.ID && this._terminalService.isProcessSupportRegistered) { return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action); } @@ -188,10 +205,7 @@ export class TerminalViewPane extends ViewPane { } public focus(): void { - const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance) { - activeInstance.focusWhenReady(true); - } + this._terminalService.getActiveInstance()?.focusWhenReady(true); } public focusFindWidget() { @@ -331,9 +345,11 @@ export class TerminalViewPane extends ViewPane { theme = this.themeService.getColorTheme(); } - if (this._findWidget) { - this._findWidget.updateTheme(theme); - } + this._findWidget?.updateTheme(theme); + } + + shouldShowWelcome(): boolean { + return !this._terminalService.isProcessSupportRegistered; } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5edbd197dbd..9c4323340a0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -12,7 +12,7 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -export const TERMINAL_VIEW_ID = 'workbench.panel.terminal'; +export const TERMINAL_VIEW_ID = 'terminal'; /** A context key that is set when there is at least one opened integrated terminal. */ export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); @@ -46,6 +46,8 @@ export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalProcessSupported', false); + export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverMeasureRenderTime';