diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index d4f4202be0fee..cce3bcfa8cccc 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index c3a03ba964eab..495ec6e264a3f 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -10,7 +10,8 @@ import { URI } from 'vs/base/common/uri'; import { IExternalTerminalConfiguration, IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { ITerminalService as IIntegratedTerminalService, KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService as IIntegratedTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; diff --git a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts index 8c11af8160440..2d09e3264f6b5 100644 --- a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts @@ -9,7 +9,7 @@ import { QuickOpenModel, QuickOpenEntryGroup, QuickOpenEntry } from 'vs/base/par import { QuickOpenHandler, QuickOpenAction } from 'vs/workbench/browser/quickopen'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { Action } from 'vs/base/common/actions'; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 2353b3f217ded..e5f2168812fcd 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -49,7 +49,7 @@ import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/p import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo, ITaskExecuteResult } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { @@ -71,7 +71,6 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { format } from 'vs/base/common/jsonFormatter'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f03460bfdeef7..ce5593930c6d7 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -27,7 +27,8 @@ import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalInstanceService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors'; import { @@ -42,7 +43,6 @@ import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Schemas } from 'vs/base/common/network'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 738b3c092ace6..d0a22799c303e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -23,9 +23,9 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; @@ -34,6 +34,7 @@ import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalS import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalShellConfig'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; registerSingleton(ITerminalService, TerminalService, true); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 21244047743ee..f2600039f9714 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -6,11 +6,16 @@ import { Terminal as XTermTerminal } from 'xterm'; import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { URI } from 'vs/base/common/uri'; +export const ITerminalService = createDecorator('terminalService'); export const ITerminalInstanceService = createDecorator('terminalInstanceService'); /** @@ -37,3 +42,404 @@ export interface ITerminalInstanceService { export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper { panelContainer: HTMLElement | undefined; } + +export const enum Direction { + Left = 0, + Right = 1, + Up = 2, + Down = 3 +} + +export interface ITerminalTab { + activeInstance: ITerminalInstance | null; + terminalInstances: ITerminalInstance[]; + title: string; + onDisposed: Event; + onInstancesChanged: Event; + + focusPreviousPane(): void; + focusNextPane(): void; + resizePane(direction: Direction): void; + setActiveInstanceByIndex(index: number): void; + attachToElement(element: HTMLElement): void; + setVisible(visible: boolean): void; + layout(width: number, height: number): void; + addDisposable(disposable: IDisposable): void; + split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance | undefined; +} + +export interface ITerminalService { + _serviceBrand: undefined; + + activeTabIndex: number; + configHelper: ITerminalConfigHelper; + terminalInstances: ITerminalInstance[]; + terminalTabs: ITerminalTab[]; + + onActiveTabChanged: Event; + onTabDisposed: Event; + onInstanceCreated: Event; + onInstanceDisposed: Event; + onInstanceProcessIdReady: Event; + onInstanceDimensionsChanged: Event; + onInstanceMaximumDimensionsChanged: Event; + onInstanceRequestSpawnExtHostProcess: Event; + onInstanceRequestStartExtensionTerminal: Event; + onInstancesChanged: Event; + onInstanceTitleChanged: Event; + onActiveInstanceChanged: Event; + onRequestAvailableShells: Event; + + /** + * Creates a terminal. + * @param shell The shell launch configuration to use. + */ + createTerminal(shell?: IShellLaunchConfig): ITerminalInstance; + + /** + * Creates a raw terminal instance, this should not be used outside of the terminal part. + */ + createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; + getInstanceFromId(terminalId: number): ITerminalInstance | undefined; + getInstanceFromIndex(terminalIndex: number): ITerminalInstance; + getTabLabels(): string[]; + getActiveInstance(): ITerminalInstance | null; + setActiveInstance(terminalInstance: ITerminalInstance): void; + setActiveInstanceByIndex(terminalIndex: number): void; + getActiveOrCreateInstance(): ITerminalInstance; + splitInstance(instance: ITerminalInstance, shell?: IShellLaunchConfig): ITerminalInstance | null; + + getActiveTab(): ITerminalTab | null; + setActiveTabToNext(): void; + setActiveTabToPrevious(): void; + setActiveTabByIndex(tabIndex: number): void; + + /** + * Fire the onActiveTabChanged event, this will trigger the terminal dropdown to be updated, + * among other things. + */ + refreshActiveTab(): void; + + showPanel(focus?: boolean): Promise; + hidePanel(): void; + focusFindWidget(): Promise; + hideFindWidget(): void; + getFindState(): FindReplaceState; + findNext(): void; + findPrevious(): void; + + selectDefaultWindowsShell(): Promise; + + setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; + manageWorkspaceShellPermissions(): void; + + /** + * Takes a path and returns the properly escaped path to send to the terminal. + * On Windows, this included trying to prepare the path for WSL if needed. + * + * @param executable The executable off the shellLaunchConfig + * @param title The terminal's title + * @param path The path to be escaped and formatted. + * @returns An escaped version of the path to be execuded in the terminal. + */ + preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; + + extHostReady(remoteAuthority: string): void; + requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; + requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void; +} + +export interface ISearchOptions { + /** + * Whether the find should be done as a regex. + */ + regex?: boolean; + /** + * Whether only whole words should match. + */ + wholeWord?: boolean; + /** + * Whether find should pay attention to case. + */ + caseSensitive?: boolean; + /** + * Whether the search should start at the current search position (not the next row) + */ + incremental?: boolean; +} + +export interface ITerminalInstance { + /** + * The ID of the terminal instance, this is an arbitrary number only used to identify the + * terminal instance. + */ + readonly id: number; + + readonly cols: number; + readonly rows: number; + readonly maxCols: number; + readonly maxRows: number; + + /** + * The process ID of the shell process, this is undefined when there is no process associated + * with this terminal. + */ + processId: number | undefined; + + /** + * An event that fires when the terminal instance's title changes. + */ + onTitleChanged: Event; + + /** + * An event that fires when the terminal instance is disposed. + */ + onDisposed: Event; + + onFocused: Event; + onProcessIdReady: Event; + onRequestExtHostProcess: Event; + onDimensionsChanged: Event; + onMaximumDimensionsChanged: Event; + + onFocus: Event; + + /** + * Attach a listener to the raw data stream coming from the pty, including ANSI escape + * sequences. + */ + onData: Event; + + /** + * Attach a listener to listen for new lines added to this terminal instance. + * + * @param listener The listener function which takes new line strings added to the terminal, + * excluding ANSI escape sequences. The line event will fire when an LF character is added to + * the terminal (ie. the line is not wrapped). Note that this means that the line data will + * not fire for the last line, until either the line is ended with a LF character of the process + * is exited. The lineData string will contain the fully wrapped line, not containing any LF/CR + * characters. + */ + onLineData: Event; + + /** + * Attach a listener that fires when the terminal's pty process exits. The number in the event + * is the processes' exit code, an exit code of null means the process was killed as a result of + * the ITerminalInstance being disposed. + */ + onExit: Event; + + processReady: Promise; + + /** + * The title of the terminal. This is either title or the process currently running or an + * explicit name given to the terminal instance through the extension API. + */ + readonly title: string; + + /** + * The focus state of the terminal before exiting. + */ + readonly hadFocusOnExit: boolean; + + /** + * False when the title is set by an API or the user. We check this to make sure we + * do not override the title when the process title changes in the terminal. + */ + isTitleSetByProcess: boolean; + + /** + * The shell launch config used to launch the shell. + */ + readonly shellLaunchConfig: IShellLaunchConfig; + + /** + * Whether to disable layout for the terminal. This is useful when the size of the terminal is + * being manipulating (e.g. adding a split pane) and we want the terminal to ignore particular + * resize events. + */ + disableLayout: boolean; + + /** + * An object that tracks when commands are run and enables navigating and selecting between + * them. + */ + readonly commandTracker: ICommandTracker | undefined; + + readonly navigationMode: INavigationMode | undefined; + + /** + * Dispose the terminal instance, removing it from the panel/service and freeing up resources. + * + * @param immediate Whether the kill should be immediate or not. Immediate should only be used + * when VS Code is shutting down or in cases where the terminal dispose was user initiated. + * The immediate===false exists to cover an edge case where the final output of the terminal can + * get cut off. If immediate kill any terminal processes immediately. + */ + dispose(immediate?: boolean): void; + + /** + * Forces the terminal to redraw its viewport. + */ + forceRedraw(): void; + + /** + * Registers a link matcher, allowing custom link patterns to be matched and handled. + * @param regex The regular expression the search for, specifically this searches the + * textContent of the rows. You will want to use \s to match a space ' ' character for example. + * @param handler The callback when the link is called. + * @param matchIndex The index of the link from the regex.match(html) call. This defaults to 0 + * (for regular expressions without capture groups). + * @param validationCallback A callback which can be used to validate the link after it has been + * added to the DOM. + * @return The ID of the new matcher, this can be used to deregister. + */ + registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void): number; + + /** + * Deregisters a link matcher if it has been registered. + * @param matcherId The link matcher's ID (returned after register) + * @return Whether a link matcher was found and deregistered. + */ + deregisterLinkMatcher(matcherId: number): void; + + /** + * Check if anything is selected in terminal. + */ + hasSelection(): boolean; + + /** + * Copies the terminal selection to the clipboard. + */ + copySelection(): Promise; + + /** + * Current selection in the terminal. + */ + readonly selection: string | undefined; + + /** + * Clear current selection. + */ + clearSelection(): void; + + /** + * Select all text in the terminal. + */ + selectAll(): void; + + /** + * Find the next instance of the term + */ + findNext(term: string, searchOptions: ISearchOptions): boolean; + + /** + * Find the previous instance of the term + */ + findPrevious(term: string, searchOptions: ISearchOptions): boolean; + + /** + * Notifies the terminal that the find widget's focus state has been changed. + */ + notifyFindWidgetFocusChanged(isFocused: boolean): void; + + /** + * Focuses the terminal instance if it's able to (xterm.js instance exists). + * + * @param focus Force focus even if there is a selection. + */ + focus(force?: boolean): void; + + /** + * Focuses the terminal instance when it's ready (the xterm.js instance is created). Use this + * when the terminal is being shown. + * + * @param focus Force focus even if there is a selection. + */ + focusWhenReady(force?: boolean): Promise; + + /** + * Focuses and pastes the contents of the clipboard into the terminal instance. + */ + paste(): Promise; + + /** + * Send text to the terminal instance. The text is written to the stdin of the underlying pty + * process (shell) of the terminal instance. + * + * @param text The text to send. + * @param addNewLine Whether to add a new line to the text being sent, this is normally + * required to run a command in the terminal. The character(s) added are \n or \r\n + * depending on the platform. This defaults to `true`. + */ + sendText(text: string, addNewLine: boolean): void; + + /** + * Write text directly to the terminal, skipping the process if it exists. + * @param text The text to write. + */ + write(text: string): void; + + /** Scroll the terminal buffer down 1 line. */ + scrollDownLine(): void; + /** Scroll the terminal buffer down 1 page. */ + scrollDownPage(): void; + /** Scroll the terminal buffer to the bottom. */ + scrollToBottom(): void; + /** Scroll the terminal buffer up 1 line. */ + scrollUpLine(): void; + /** Scroll the terminal buffer up 1 page. */ + scrollUpPage(): void; + /** Scroll the terminal buffer to the top. */ + scrollToTop(): void; + + /** + * Clears the terminal buffer, leaving only the prompt line. + */ + clear(): void; + + /** + * Attaches the terminal instance to an element on the DOM, before this is called the terminal + * instance process may run in the background but cannot be displayed on the UI. + * + * @param container The element to attach the terminal instance to. + */ + attachToElement(container: HTMLElement): void; + + /** + * Configure the dimensions of the terminal instance. + * + * @param dimension The dimensions of the container. + */ + layout(dimension: { width: number, height: number }): void; + + /** + * Sets whether the terminal instance's element is visible in the DOM. + * + * @param visible Whether the element is visible. + */ + setVisible(visible: boolean): void; + + /** + * Immediately kills the terminal's current pty process and launches a new one to replace it. + * + * @param shell The new launch configuration. + */ + reuseTerminal(shell: IShellLaunchConfig): void; + + /** + * Sets the title of the terminal instance. + */ + setTitle(title: string, eventSource: TitleEventSource): void; + + waitForTitle(): Promise; + + setDimensions(dimensions: ITerminalDimensions): void; + + addDisposable(disposable: IDisposable): void; + + toggleEscapeSequenceLogging(): void; + + getInitialCwd(): Promise; + getCwd(): Promise; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 4e3d56aab4e16..0636426e08075 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_PANEL_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -24,7 +24,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { Command } from 'vs/editor/browser/editorExtensions'; import { timeout } from 'vs/base/common/async'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; @@ -35,6 +34,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { ITerminalInstance, ITerminalService, Direction } from 'vs/workbench/contrib/terminal/browser/terminal'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -549,7 +549,7 @@ export class FocusActiveTerminalAction extends Action { } public run(event?: any): Promise { - const instance = this.terminalService.getActiveOrCreateInstance(true); + const instance = this.terminalService.getActiveOrCreateInstance(); if (!instance) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalCommands.ts b/src/vs/workbench/contrib/terminal/browser/terminalCommands.ts new file mode 100644 index 0000000000000..8f0f92efe05a9 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalCommands.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; + +export function setupTerminalCommands(): void { + registerOpenTerminalAtIndexCommands(); +} + +function registerOpenTerminalAtIndexCommands(): void { + for (let i = 0; i < 9; i++) { + const terminalIndex = i; + const visibleIndex = i + 1; + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: `workbench.action.terminal.focusAtIndex${visibleIndex}`, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: 0, + handler: accessor => { + const terminalService = accessor.get(ITerminalService); + terminalService.setActiveInstanceByIndex(terminalIndex); + return terminalService.showPanel(true); + } + }); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index a3d01c1c6e4fe..ecd768a82512c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -5,9 +5,10 @@ import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; export class TerminalFindWidget extends SimpleFindWidget { protected _findInputFocused: IContextKey; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 2cc1f6ed4919a..d3dde9111c423 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,14 +25,13 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 5c3eae374efb5..77ff7a7285921 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -24,6 +24,7 @@ import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/c 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'; const FIND_FOCUS_CLASS = 'find-focused'; diff --git a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts similarity index 95% rename from src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts rename to src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index dfc07d6efe775..345840a641062 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import * as nls from 'vs/nls'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; let hasReceivedResponse: boolean = false; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 22a80f60ca54b..24c4feeaebb9d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -10,7 +10,7 @@ import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalCon import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy'; +import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts index 5a86378ef50cd..cfe9620f43f2d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; -import { ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; import { stripWildcards } from 'vs/base/common/strings'; import { matchesFuzzy } from 'vs/base/common/filters'; @@ -134,4 +134,4 @@ export class TerminalPickerHandler extends QuickOpenHandler { } return nls.localize('noTerminalsFound', "No terminals open"); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index daed808061268..28d6d55692ee0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import * as nls from 'vs/nls'; +import { TERMINAL_PANEL_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -18,38 +17,578 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IBrowserTerminalConfigHelper, ITerminalService, ITerminalInstance, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +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 { basename } from 'vs/base/common/path'; +import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; + +interface IExtHostReadyEntry { + promise: Promise; + resolve: () => void; +} + +export class TerminalService implements ITerminalService { + public _serviceBrand: undefined; + + protected _isShuttingDown: boolean; + protected _terminalFocusContextKey: IContextKey; + protected _findWidgetVisible: IContextKey; + protected _terminalTabs: ITerminalTab[] = []; + protected _backgroundedTerminalInstances: ITerminalInstance[] = []; + protected get _terminalInstances(): ITerminalInstance[] { + return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); + } + private _findState: FindReplaceState; + private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {}; + private _activeTabIndex: number; + + public get activeTabIndex(): number { return this._activeTabIndex; } + public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } + public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } -export class TerminalService extends CommonTerminalService implements ITerminalService { private _configHelper: IBrowserTerminalConfigHelper; private _terminalContainer: HTMLElement | undefined; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } + protected readonly _onActiveTabChanged = new Emitter(); + public get onActiveTabChanged(): Event { return this._onActiveTabChanged.event; } + protected readonly _onInstanceCreated = new Emitter(); + public get onInstanceCreated(): Event { return this._onInstanceCreated.event; } + protected readonly _onInstanceDisposed = new Emitter(); + public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } + protected readonly _onInstanceProcessIdReady = new Emitter(); + public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } + protected readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); + public get onInstanceRequestSpawnExtHostProcess(): Event { return this._onInstanceRequestSpawnExtHostProcess.event; } + protected readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); + public get onInstanceRequestStartExtensionTerminal(): Event { return this._onInstanceRequestStartExtensionTerminal.event; } + protected readonly _onInstanceDimensionsChanged = new Emitter(); + public get onInstanceDimensionsChanged(): Event { return this._onInstanceDimensionsChanged.event; } + protected readonly _onInstanceMaximumDimensionsChanged = new Emitter(); + public get onInstanceMaximumDimensionsChanged(): Event { return this._onInstanceMaximumDimensionsChanged.event; } + protected readonly _onInstancesChanged = new Emitter(); + public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } + protected readonly _onInstanceTitleChanged = new Emitter(); + public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } + protected readonly _onActiveInstanceChanged = new Emitter(); + public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } + protected readonly _onTabDisposed = new Emitter(); + public get onTabDisposed(): Event { return this._onTabDisposed.event; } + protected readonly _onRequestAvailableShells = new Emitter(); + public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } + constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @IPanelService panelService: IPanelService, + @IContextKeyService private _contextKeyService: IContextKeyService, + @IPanelService private _panelService: IPanelService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, @ILifecycleService lifecycleService: ILifecycleService, - @IStorageService storageService: IStorageService, - @INotificationService notificationService: INotificationService, - @IDialogService dialogService: IDialogService, - @IInstantiationService protected readonly _instantiationService: IInstantiationService, - @IExtensionService extensionService: IExtensionService, - @IFileService fileService: IFileService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @ITerminalNativeService readonly terminalNativeService: ITerminalNativeService, - @IQuickInputService readonly quickInputService: IQuickInputService, - @IConfigurationService readonly configurationService: IConfigurationService + @INotificationService private _notificationService: INotificationService, + @IDialogService private _dialogService: IDialogService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IExtensionService private _extensionService: IExtensionService, + @IFileService private _fileService: IFileService, + @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, + @ITerminalNativeService private _terminalNativeService: ITerminalNativeService, + @IQuickInputService private _quickInputService: IQuickInputService, + @IConfigurationService private _configurationService: IConfigurationService ) { - super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService, terminalNativeService, quickInputService, configurationService); - this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, this.terminalNativeService.linuxDistro); + this._activeTabIndex = 0; + this._isShuttingDown = false; + this._findState = new FindReplaceState(); + lifecycleService.onBeforeShutdown(event => event.veto(this._onBeforeShutdown())); + lifecycleService.onShutdown(() => this._onShutdown()); + this._terminalNativeService.onOpenFileRequest(e => this._onOpenFileRequest(e)); + this._terminalNativeService.onOsResume(() => this._onOsResume()); + this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); + this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, this._terminalNativeService.linuxDistro); + this.onTabDisposed(tab => this._removeTab(tab)); + this.onActiveTabChanged(() => { + const instance = this.getActiveInstance(); + this._onActiveInstanceChanged.fire(instance ? instance : undefined); + }); + + this._handleContextKeys(); + } + + private _handleContextKeys(): void { + const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); + + const updateTerminalContextKeys = () => { + terminalIsOpenContext.set(this.terminalInstances.length > 0); + }; + + this.onInstancesChanged(() => updateTerminalContextKeys()); + } + + public getActiveOrCreateInstance(): ITerminalInstance { + const activeInstance = this.getActiveInstance(); + return activeInstance ? activeInstance : this.createTerminal(undefined); + } + + public requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void { + this._extensionService.whenInstalledExtensionsRegistered().then(async () => { + // Wait for the remoteAuthority to be ready (and listening for events) before firing + // the event to spawn the ext host process + const conn = this._remoteAgentService.getConnection(); + const remoteAuthority = conn ? conn.remoteAuthority : 'null'; + await this._whenExtHostReady(remoteAuthority); + this._onInstanceRequestSpawnExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed }); + }); + } + + public requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void { + this._onInstanceRequestStartExtensionTerminal.fire({ proxy, cols, rows }); + } + + public async extHostReady(remoteAuthority: string): Promise { + this._createExtHostReadyEntry(remoteAuthority); + this._extHostsReady[remoteAuthority]!.resolve(); + } + + private async _whenExtHostReady(remoteAuthority: string): Promise { + this._createExtHostReadyEntry(remoteAuthority); + return this._extHostsReady[remoteAuthority]!.promise; + } + + private _createExtHostReadyEntry(remoteAuthority: string): void { + if (this._extHostsReady[remoteAuthority]) { + return; + } + + let resolve!: () => void; + const promise = new Promise(r => resolve = r); + this._extHostsReady[remoteAuthority] = { promise, resolve }; + } + + private _onBeforeShutdown(): boolean | Promise { + if (this.terminalInstances.length === 0) { + // No terminal instances, don't veto + return false; + } + + if (this.configHelper.config.confirmOnExit) { + // veto if configured to show confirmation and the user choosed not to exit + return this._showTerminalCloseConfirmation().then(veto => { + if (!veto) { + this._isShuttingDown = true; + } + return veto; + }); + } + + this._isShuttingDown = true; + + return false; + } + + private _onShutdown(): void { + // Dispose of all instances + this.terminalInstances.forEach(instance => instance.dispose(true)); + } + + private _onOpenFileRequest(request: IOpenFileRequest): void { + // if the request to open files is coming in from the integrated terminal (identified though + // the termProgram variable) and we are instructed to wait for editors close, wait for the + // marker file to get deleted and then focus back to the integrated terminal. + if (request.termProgram === 'vscode' && request.filesToWait) { + const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri); + this._terminalNativeService.whenFileDeleted(waitMarkerFileUri).then(() => { + if (this.terminalInstances.length > 0) { + const terminal = this.getActiveInstance(); + if (terminal) { + terminal.focus(); + } + } + }); + } + } + + private _onOsResume(): void { + const activeTab = this.getActiveTab(); + if (!activeTab) { + return; + } + activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); + } + + public getTabLabels(): string[] { + return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); + } + + public getFindState(): FindReplaceState { + return this._findState; + } + + private _removeTab(tab: ITerminalTab): void { + // Get the index of the tab and remove it from the list + const index = this._terminalTabs.indexOf(tab); + const wasActiveTab = tab === this.getActiveTab(); + if (index !== -1) { + this._terminalTabs.splice(index, 1); + } + + // Adjust focus if the tab was active + if (wasActiveTab && this._terminalTabs.length > 0) { + // TODO: Only focus the new tab if the removed tab had focus? + // const hasFocusOnExit = tab.activeInstance.hadFocusOnExit; + const newIndex = index < this._terminalTabs.length ? index : this._terminalTabs.length - 1; + this.setActiveTabByIndex(newIndex); + const activeInstance = this.getActiveInstance(); + if (activeInstance) { + activeInstance.focus(true); + } + } + + // Hide the panel if there are no more instances, provided that VS Code is not shutting + // down. When shutting down the panel is locked in place so that it is restored upon next + // launch. + if (this._terminalTabs.length === 0 && !this._isShuttingDown) { + this.hidePanel(); + this._onActiveInstanceChanged.fire(undefined); + } + + // Fire events + this._onInstancesChanged.fire(); + if (wasActiveTab) { + this._onActiveTabChanged.fire(); + } + } + + public refreshActiveTab(): void { + // Fire active instances changed + this._onActiveTabChanged.fire(); + } + + public getActiveTab(): ITerminalTab | null { + if (this._activeTabIndex < 0 || this._activeTabIndex >= this._terminalTabs.length) { + return null; + } + return this._terminalTabs[this._activeTabIndex]; + } + + public getActiveInstance(): ITerminalInstance | null { + const tab = this.getActiveTab(); + if (!tab) { + return null; + } + return tab.activeInstance; + } + + public getInstanceFromId(terminalId: number): ITerminalInstance | undefined { + let bgIndex = -1; + this._backgroundedTerminalInstances.forEach((terminalInstance, i) => { + if (terminalInstance.id === terminalId) { + bgIndex = i; + } + }); + if (bgIndex !== -1) { + return this._backgroundedTerminalInstances[bgIndex]; + } + try { + return this.terminalInstances[this._getIndexFromId(terminalId)]; + } catch { + return undefined; + } + } + + public getInstanceFromIndex(terminalIndex: number): ITerminalInstance { + return this.terminalInstances[terminalIndex]; + } + + public setActiveInstance(terminalInstance: ITerminalInstance): void { + // If this was a hideFromUser terminal created by the API this was triggered by show, + // in which case we need to create the terminal tab + if (terminalInstance.shellLaunchConfig.hideFromUser) { + this._showBackgroundTerminal(terminalInstance); + } + this.setActiveInstanceByIndex(this._getIndexFromId(terminalInstance.id)); + } + + public setActiveTabByIndex(tabIndex: number): void { + if (tabIndex >= this._terminalTabs.length) { + return; + } + + const didTabChange = this._activeTabIndex !== tabIndex; + this._activeTabIndex = tabIndex; + + this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); + if (didTabChange) { + this._onActiveTabChanged.fire(); + } + } + + private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } | null { + let currentTabIndex = 0; + while (index >= 0 && currentTabIndex < this._terminalTabs.length) { + const tab = this._terminalTabs[currentTabIndex]; + const count = tab.terminalInstances.length; + if (index < count) { + return { + tab, + tabIndex: currentTabIndex, + instance: tab.terminalInstances[index], + localInstanceIndex: index + }; + } + index -= count; + currentTabIndex++; + } + return null; + } + + public setActiveInstanceByIndex(terminalIndex: number): void { + const query = this._getInstanceFromGlobalInstanceIndex(terminalIndex); + if (!query) { + return; + } + + query.tab.setActiveInstanceByIndex(query.localInstanceIndex); + const didTabChange = this._activeTabIndex !== query.tabIndex; + this._activeTabIndex = query.tabIndex; + this._terminalTabs.forEach((t, i) => t.setVisible(i === query.tabIndex)); + + // Only fire the event if there was a change + if (didTabChange) { + this._onActiveTabChanged.fire(); + } + } + + public setActiveTabToNext(): void { + if (this._terminalTabs.length <= 1) { + return; + } + let newIndex = this._activeTabIndex + 1; + if (newIndex >= this._terminalTabs.length) { + newIndex = 0; + } + this.setActiveTabByIndex(newIndex); + } + + public setActiveTabToPrevious(): void { + if (this._terminalTabs.length <= 1) { + return; + } + let newIndex = this._activeTabIndex - 1; + if (newIndex < 0) { + newIndex = this._terminalTabs.length - 1; + } + this.setActiveTabByIndex(newIndex); + } + + public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): ITerminalInstance | null { + const tab = this._getTabForInstance(instanceToSplit); + if (!tab) { + return null; + } + + const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig); + if (!instance) { + this._showNotEnoughSpaceToast(); + return null; + } + + this._initInstanceListeners(instance); + this._onInstancesChanged.fire(); + + this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); + return instance; + } + + protected _initInstanceListeners(instance: ITerminalInstance): void { + instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); + instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); + instance.addDisposable(instance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); + instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance))); + instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance))); + instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); + } + + private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | null { + for (const tab of this._terminalTabs) { + if (tab.terminalInstances.indexOf(instance) !== -1) { + return tab; + } + } + return null; + } + + public showPanel(focus?: boolean): Promise { + return new Promise((complete) => { + const panel = this._panelService.getActivePanel(); + if (!panel || panel.getId() !== TERMINAL_PANEL_ID) { + this._panelService.openPanel(TERMINAL_PANEL_ID, focus); + if (focus) { + // Do the focus call asynchronously as going through the + // command palette will force editor focus + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focusWhenReady(true).then(() => complete(undefined)); + } else { + complete(undefined); + } + }, 0); + } else { + complete(undefined); + } + } else { + if (focus) { + // Do the focus call asynchronously as going through the + // command palette will force editor focus + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focusWhenReady(true).then(() => complete(undefined)); + } else { + complete(undefined); + } + }, 0); + } else { + complete(undefined); + } + } + return undefined; + }); + } + + private _getIndexFromId(terminalId: number): number { + let terminalIndex = -1; + this.terminalInstances.forEach((terminalInstance, i) => { + if (terminalInstance.id === terminalId) { + terminalIndex = i; + } + }); + if (terminalIndex === -1) { + throw new Error(`Terminal with ID ${terminalId} does not exist (has it already been disposed?)`); + } + return terminalIndex; + } + + public async manageWorkspaceShellPermissions(): Promise { + const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration") }; + const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration") }; + const value = await this._quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); + if (!value) { + return; + } + this.configHelper.setWorkspaceShellAllowed(value === allowItem); + } + + protected async _showTerminalCloseConfirmation(): Promise { + let message: string; + if (this.terminalInstances.length === 1) { + message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); + } else { + message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); + } + const res = await this._dialogService.confirm({ + message, + type: 'warning', + }); + return !res.confirmed; + } + + protected _showNotEnoughSpaceToast(): void { + this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); } + protected _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { + if (potentialPaths.length === 0) { + return Promise.resolve(null); + } + const current = potentialPaths.shift(); + if (current! === '') { + return this._validateShellPaths(label, potentialPaths); + } + return this._fileService.exists(URI.file(current!)).then(exists => { + if (!exists) { + return this._validateShellPaths(label, potentialPaths); + } + return [label, current] as [string, string]; + }); + } + + public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { + return new Promise(c => { + if (!executable) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + + const pathBasename = basename(executable, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + const lowerExecutable = executable.toLowerCase(); + if (this._terminalNativeService.getWindowsBuildNumber() >= 17063 && + (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1))) { + c(this._terminalNativeService.getWslPath(originalPath)); + return; + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + return; + } + c(escapeNonWindowsPath(originalPath)); + }); + } + + public selectDefaultWindowsShell(): Promise { + return this._detectWindowsShells().then(shells => { + const options: IPickOptions = { + placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") + }; + const quickPickItems = shells.map((s): IQuickPickItem => { + return { label: s.label, description: s.path }; + }); + return this._quickInputService.pick(quickPickItems, options).then(async value => { + if (!value) { + return undefined; + } + const shell = value.description; + const env = await this._remoteAgentService.getEnvironment(); + let platformKey: string; + if (env) { + platformKey = env.os === OperatingSystem.Windows ? 'windows' : (env.os === OperatingSystem.Macintosh ? 'osx' : 'linux'); + } else { + platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); + } + await this._configurationService.updateValue(`terminal.integrated.shell.${platformKey}`, shell, ConfigurationTarget.USER).then(() => shell); + return Promise.resolve(); + }); + }); + } + + private _detectWindowsShells(): Promise { + return new Promise(r => this._onRequestAvailableShells.fire(r)); + } + + public createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance { const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalFocusContextKey, this._configHelper, container, shellLaunchConfig); this._onInstanceCreated.fire(instance); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 8f2fd4fbaeed8..3d4d50cdc9f6e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as aria from 'vs/base/browser/ui/aria/aria'; import * as nls from 'vs/nls'; -import { ITerminalInstance, IShellLaunchConfig, ITerminalTab, Direction, ITerminalService, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITerminalInstance, Direction, ITerminalTab, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; const SPLIT_PANE_MIN_SIZE = 120; const TERMINAL_MIN_USEFUL_SIZE = 250; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 0f7bb6b9ff577..87448aa36fba2 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -6,10 +6,9 @@ import * as nls from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { OperatingSystem } from 'vs/base/common/platform'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; @@ -48,7 +47,6 @@ export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverM // trying to create the corressponding object on the ext host. export const EXT_HOST_CREATION_DELAY = 100; -export const ITerminalService = createDecorator('terminalService'); export const ITerminalNativeService = createDecorator('terminalNativeService'); export const TerminalCursorStyle = { @@ -217,89 +215,6 @@ export interface IShellLaunchConfig { hideFromUser?: boolean; } -export interface ITerminalService { - _serviceBrand: undefined; - - activeTabIndex: number; - configHelper: ITerminalConfigHelper; - terminalInstances: ITerminalInstance[]; - terminalTabs: ITerminalTab[]; - - onActiveTabChanged: Event; - onTabDisposed: Event; - onInstanceCreated: Event; - onInstanceDisposed: Event; - onInstanceProcessIdReady: Event; - onInstanceDimensionsChanged: Event; - onInstanceMaximumDimensionsChanged: Event; - onInstanceRequestSpawnExtHostProcess: Event; - onInstanceRequestStartExtensionTerminal: Event; - onInstancesChanged: Event; - onInstanceTitleChanged: Event; - onActiveInstanceChanged: Event; - onRequestAvailableShells: Event; - - /** - * Creates a terminal. - * @param shell The shell launch configuration to use. - */ - createTerminal(shell?: IShellLaunchConfig): ITerminalInstance; - - /** - * Creates a raw terminal instance, this should not be used outside of the terminal part. - */ - // tslint:disable-next-line: no-dom-globals TODO@daniel - createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; - getInstanceFromId(terminalId: number): ITerminalInstance | undefined; - getInstanceFromIndex(terminalIndex: number): ITerminalInstance; - getTabLabels(): string[]; - getActiveInstance(): ITerminalInstance | null; - setActiveInstance(terminalInstance: ITerminalInstance): void; - setActiveInstanceByIndex(terminalIndex: number): void; - getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; - splitInstance(instance: ITerminalInstance, shell?: IShellLaunchConfig): ITerminalInstance | null; - - getActiveTab(): ITerminalTab | null; - setActiveTabToNext(): void; - setActiveTabToPrevious(): void; - setActiveTabByIndex(tabIndex: number): void; - - /** - * Fire the onActiveTabChanged event, this will trigger the terminal dropdown to be updated, - * among other things. - */ - refreshActiveTab(): void; - - showPanel(focus?: boolean): Promise; - hidePanel(): void; - focusFindWidget(): Promise; - hideFindWidget(): void; - getFindState(): FindReplaceState; - findNext(): void; - findPrevious(): void; - - selectDefaultWindowsShell(): Promise; - - // tslint:disable-next-line: no-dom-globals - setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - manageWorkspaceShellPermissions(): void; - - /** - * Takes a path and returns the properly escaped path to send to the terminal. - * On Windows, this included trying to prepare the path for WSL if needed. - * - * @param executable The executable off the shellLaunchConfig - * @param title The terminal's title - * @param path The path to be escaped and formatted. - * @returns An escaped version of the path to be execuded in the terminal. - */ - preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; - - extHostReady(remoteAuthority: string): void; - requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; - requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void; -} - /** * Provides access to native or electron APIs to other terminal services. */ @@ -321,32 +236,6 @@ export interface IShellDefinition { path: string; } -export const enum Direction { - Left = 0, - Right = 1, - Up = 2, - Down = 3 -} - -export interface ITerminalTab { - activeInstance: ITerminalInstance | null; - terminalInstances: ITerminalInstance[]; - title: string; - onDisposed: Event; - onInstancesChanged: Event; - - focusPreviousPane(): void; - focusNextPane(): void; - resizePane(direction: Direction): void; - setActiveInstanceByIndex(index: number): void; - // tslint:disable-next-line: no-dom-globals - attachToElement(element: HTMLElement): void; - setVisible(visible: boolean): void; - layout(width: number, height: number): void; - addDisposable(disposable: IDisposable): void; - split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance | undefined; -} - export interface ITerminalDimensions { /** * The columns of the terminal. @@ -359,302 +248,6 @@ export interface ITerminalDimensions { readonly rows: number; } -interface ISearchOptions { - /** - * Whether the find should be done as a regex. - */ - regex?: boolean; - /** - * Whether only whole words should match. - */ - wholeWord?: boolean; - /** - * Whether find should pay attention to case. - */ - caseSensitive?: boolean; - /** - * Whether the search should start at the current search position (not the next row) - */ - incremental?: boolean; -} - -export interface ITerminalInstance { - /** - * The ID of the terminal instance, this is an arbitrary number only used to identify the - * terminal instance. - */ - readonly id: number; - - readonly cols: number; - readonly rows: number; - readonly maxCols: number; - readonly maxRows: number; - - /** - * The process ID of the shell process, this is undefined when there is no process associated - * with this terminal. - */ - processId: number | undefined; - - /** - * An event that fires when the terminal instance's title changes. - */ - onTitleChanged: Event; - - /** - * An event that fires when the terminal instance is disposed. - */ - onDisposed: Event; - - onFocused: Event; - onProcessIdReady: Event; - onRequestExtHostProcess: Event; - onDimensionsChanged: Event; - onMaximumDimensionsChanged: Event; - - onFocus: Event; - - /** - * Attach a listener to the raw data stream coming from the pty, including ANSI escape - * sequences. - */ - onData: Event; - - /** - * Attach a listener to listen for new lines added to this terminal instance. - * - * @param listener The listener function which takes new line strings added to the terminal, - * excluding ANSI escape sequences. The line event will fire when an LF character is added to - * the terminal (ie. the line is not wrapped). Note that this means that the line data will - * not fire for the last line, until either the line is ended with a LF character of the process - * is exited. The lineData string will contain the fully wrapped line, not containing any LF/CR - * characters. - */ - onLineData: Event; - - /** - * Attach a listener that fires when the terminal's pty process exits. The number in the event - * is the processes' exit code, an exit code of null means the process was killed as a result of - * the ITerminalInstance being disposed. - */ - onExit: Event; - - processReady: Promise; - - /** - * The title of the terminal. This is either title or the process currently running or an - * explicit name given to the terminal instance through the extension API. - */ - readonly title: string; - - /** - * The focus state of the terminal before exiting. - */ - readonly hadFocusOnExit: boolean; - - /** - * False when the title is set by an API or the user. We check this to make sure we - * do not override the title when the process title changes in the terminal. - */ - isTitleSetByProcess: boolean; - - /** - * The shell launch config used to launch the shell. - */ - readonly shellLaunchConfig: IShellLaunchConfig; - - /** - * Whether to disable layout for the terminal. This is useful when the size of the terminal is - * being manipulating (e.g. adding a split pane) and we want the terminal to ignore particular - * resize events. - */ - disableLayout: boolean; - - /** - * An object that tracks when commands are run and enables navigating and selecting between - * them. - */ - readonly commandTracker: ICommandTracker | undefined; - - readonly navigationMode: INavigationMode | undefined; - - /** - * Dispose the terminal instance, removing it from the panel/service and freeing up resources. - * - * @param immediate Whether the kill should be immediate or not. Immediate should only be used - * when VS Code is shutting down or in cases where the terminal dispose was user initiated. - * The immediate===false exists to cover an edge case where the final output of the terminal can - * get cut off. If immediate kill any terminal processes immediately. - */ - dispose(immediate?: boolean): void; - - /** - * Forces the terminal to redraw its viewport. - */ - forceRedraw(): void; - - /** - * Registers a link matcher, allowing custom link patterns to be matched and handled. - * @param regex The regular expression the search for, specifically this searches the - * textContent of the rows. You will want to use \s to match a space ' ' character for example. - * @param handler The callback when the link is called. - * @param matchIndex The index of the link from the regex.match(html) call. This defaults to 0 - * (for regular expressions without capture groups). - * @param validationCallback A callback which can be used to validate the link after it has been - * added to the DOM. - * @return The ID of the new matcher, this can be used to deregister. - */ - registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void): number; - - /** - * Deregisters a link matcher if it has been registered. - * @param matcherId The link matcher's ID (returned after register) - * @return Whether a link matcher was found and deregistered. - */ - deregisterLinkMatcher(matcherId: number): void; - - /** - * Check if anything is selected in terminal. - */ - hasSelection(): boolean; - - /** - * Copies the terminal selection to the clipboard. - */ - copySelection(): Promise; - - /** - * Current selection in the terminal. - */ - readonly selection: string | undefined; - - /** - * Clear current selection. - */ - clearSelection(): void; - - /** - * Select all text in the terminal. - */ - selectAll(): void; - - /** - * Find the next instance of the term - */ - findNext(term: string, searchOptions: ISearchOptions): boolean; - - /** - * Find the previous instance of the term - */ - findPrevious(term: string, searchOptions: ISearchOptions): boolean; - - /** - * Notifies the terminal that the find widget's focus state has been changed. - */ - notifyFindWidgetFocusChanged(isFocused: boolean): void; - - /** - * Focuses the terminal instance if it's able to (xterm.js instance exists). - * - * @param focus Force focus even if there is a selection. - */ - focus(force?: boolean): void; - - /** - * Focuses the terminal instance when it's ready (the xterm.js instance is created). Use this - * when the terminal is being shown. - * - * @param focus Force focus even if there is a selection. - */ - focusWhenReady(force?: boolean): Promise; - - /** - * Focuses and pastes the contents of the clipboard into the terminal instance. - */ - paste(): Promise; - - /** - * Send text to the terminal instance. The text is written to the stdin of the underlying pty - * process (shell) of the terminal instance. - * - * @param text The text to send. - * @param addNewLine Whether to add a new line to the text being sent, this is normally - * required to run a command in the terminal. The character(s) added are \n or \r\n - * depending on the platform. This defaults to `true`. - */ - sendText(text: string, addNewLine: boolean): void; - - /** - * Write text directly to the terminal, skipping the process if it exists. - * @param text The text to write. - */ - write(text: string): void; - - /** Scroll the terminal buffer down 1 line. */ - scrollDownLine(): void; - /** Scroll the terminal buffer down 1 page. */ - scrollDownPage(): void; - /** Scroll the terminal buffer to the bottom. */ - scrollToBottom(): void; - /** Scroll the terminal buffer up 1 line. */ - scrollUpLine(): void; - /** Scroll the terminal buffer up 1 page. */ - scrollUpPage(): void; - /** Scroll the terminal buffer to the top. */ - scrollToTop(): void; - - /** - * Clears the terminal buffer, leaving only the prompt line. - */ - clear(): void; - - /** - * Attaches the terminal instance to an element on the DOM, before this is called the terminal - * instance process may run in the background but cannot be displayed on the UI. - * - * @param container The element to attach the terminal instance to. - */ - // tslint:disable-next-line: no-dom-globals - attachToElement(container: HTMLElement): void; - - /** - * Configure the dimensions of the terminal instance. - * - * @param dimension The dimensions of the container. - */ - layout(dimension: { width: number, height: number }): void; - - /** - * Sets whether the terminal instance's element is visible in the DOM. - * - * @param visible Whether the element is visible. - */ - setVisible(visible: boolean): void; - - /** - * Immediately kills the terminal's current pty process and launches a new one to replace it. - * - * @param shell The new launch configuration. - */ - reuseTerminal(shell: IShellLaunchConfig): void; - - /** - * Sets the title of the terminal instance. - */ - setTitle(title: string, eventSource: TitleEventSource): void; - - waitForTitle(): Promise; - - setDimensions(dimensions: ITerminalDimensions): void; - - addDisposable(disposable: IDisposable): void; - - toggleEscapeSequenceLogging(): void; - - getInitialCwd(): Promise; - getCwd(): Promise; -} - export interface ICommandTracker { scrollToPreviousCommand(): void; scrollToNextCommand(): void; @@ -813,3 +406,69 @@ export interface ITerminalChildProcess { getCwd(): Promise; getLatency(): Promise; } + +export const enum TERMINAL_COMMAND_ID { + FIND_NEXT = 'workbench.action.terminal.findNext', + FIND_NEXT_TERMINAL_FOCUS = 'workbench.action.terminal.findNextTerminalFocus', + FIND_PREVIOUS = 'workbench.action.terminal.findPrevious', + FIND_PREVIOUS_TERMINAL_FOCUS = 'workbench.action.terminal.findPreviousTerminalFocus', + TOGGLE = 'workbench.action.terminal.toggleTerminal', + KILL = 'workbench.action.terminal.kill', + QUICK_KILL = 'workbench.action.terminal.quickKill', + COPY_SELECTION = 'workbench.action.terminal.copySelection', + SELECT_ALL = 'workbench.action.terminal.selectAll', + DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', + DELETE_WORD_RIGHT = 'workbench.action.terminal.deleteWordRight', + DELETE_TO_LINE_START = 'workbench.action.terminal.deleteToLineStart', + MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', + MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', + NEW = 'workbench.action.terminal.new', + NEW_LOCAL = 'workbench.action.terminal.newLocal', + NEW_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.newInActiveWorkspace', + SPLIT = 'workbench.action.terminal.split', + SPLIT_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.splitInActiveWorkspace', + FOCUS_PREVIOUS_PANE = 'workbench.action.terminal.focusPreviousPane', + FOCUS_NEXT_PANE = 'workbench.action.terminal.focusNextPane', + RESIZE_PANE_LEFT = 'workbench.action.terminal.resizePaneLeft', + RESIZE_PANE_RIGHT = 'workbench.action.terminal.resizePaneRight', + RESIZE_PANE_UP = 'workbench.action.terminal.resizePaneUp', + RESIZE_PANE_DOWN = 'workbench.action.terminal.resizePaneDown', + FOCUS = 'workbench.action.terminal.focus', + FOCUS_NEXT = 'workbench.action.terminal.focusNext', + FOCUS_PREVIOUS = 'workbench.action.terminal.focusPrevious', + PASTE = 'workbench.action.terminal.paste', + SELECT_DEFAULT_SHELL = 'workbench.action.terminal.selectDefaultShell', + RUN_SELECTED_TEXT = 'workbench.action.terminal.runSelectedText', + RUN_ACTIVE_FILE = 'workbench.action.terminal.runActiveFile', + SWITCH_TERMINAL = 'workbench.action.terminal.switchTerminal', + SCROLL_DOWN_LINE = 'workbench.action.terminal.scrollDown', + SCROLL_DOWN_PAGE = 'workbench.action.terminal.scrollDownPage', + SCROLL_TO_BOTTOM = 'workbench.action.terminal.scrollToBottom', + SCROLL_UP_LINE = 'workbench.action.terminal.scrollUp', + SCROLL_UP_PAGE = 'workbench.action.terminal.scrollUpPage', + SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', + CLEAR = 'workbench.action.terminal.clear', + CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', + MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', + RENAME = 'workbench.action.terminal.rename', + FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', + FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', + QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', + SCROLL_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.scrollToPreviousCommand', + SCROLL_TO_NEXT_COMMAND = 'workbench.action.terminal.scrollToNextCommand', + SELECT_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.selectToPreviousCommand', + SELECT_TO_NEXT_COMMAND = 'workbench.action.terminal.selectToNextCommand', + SELECT_TO_PREVIOUS_LINE = 'workbench.action.terminal.selectToPreviousLine', + SELECT_TO_NEXT_LINE = 'workbench.action.terminal.selectToNextLine', + TOGGLE_ESCAPE_SEQUENCE_LOGGING = 'toggleEscapeSequenceLogging', + SEND_SEQUENCE = 'workbench.action.terminal.sendSequence', + TOGGLE_FIND_REGEX = 'workbench.action.terminal.toggleFindRegex', + TOGGLE_FIND_WHOLE_WORD = 'workbench.action.terminal.toggleFindWholeWord', + TOGGLE_FIND_CASE_SENSITIVE = 'workbench.action.terminal.toggleFindCaseSensitive', + TOGGLE_FIND_REGEX_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindRegexTerminalFocus', + TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindWholeWordTerminalFocus', + TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindCaseSensitiveTerminalFocus', + NAVIGATION_MODE_EXIT = 'workbench.action.terminal.navigationModeExit', + NAVIGATION_MODE_FOCUS_NEXT = 'workbench.action.terminal.navigationModeFocusNext', + NAVIGATION_MODE_FOCUS_PREVIOUS = 'workbench.action.terminal.navigationModeFocusPrevious' +} diff --git a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts deleted file mode 100644 index ddd108304ae11..0000000000000 --- a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts +++ /dev/null @@ -1,96 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; - -export const enum TERMINAL_COMMAND_ID { - FIND_NEXT = 'workbench.action.terminal.findNext', - FIND_NEXT_TERMINAL_FOCUS = 'workbench.action.terminal.findNextTerminalFocus', - FIND_PREVIOUS = 'workbench.action.terminal.findPrevious', - FIND_PREVIOUS_TERMINAL_FOCUS = 'workbench.action.terminal.findPreviousTerminalFocus', - TOGGLE = 'workbench.action.terminal.toggleTerminal', - KILL = 'workbench.action.terminal.kill', - QUICK_KILL = 'workbench.action.terminal.quickKill', - COPY_SELECTION = 'workbench.action.terminal.copySelection', - SELECT_ALL = 'workbench.action.terminal.selectAll', - DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', - DELETE_WORD_RIGHT = 'workbench.action.terminal.deleteWordRight', - DELETE_TO_LINE_START = 'workbench.action.terminal.deleteToLineStart', - MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', - MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', - NEW = 'workbench.action.terminal.new', - NEW_LOCAL = 'workbench.action.terminal.newLocal', - NEW_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.newInActiveWorkspace', - SPLIT = 'workbench.action.terminal.split', - SPLIT_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.splitInActiveWorkspace', - FOCUS_PREVIOUS_PANE = 'workbench.action.terminal.focusPreviousPane', - FOCUS_NEXT_PANE = 'workbench.action.terminal.focusNextPane', - RESIZE_PANE_LEFT = 'workbench.action.terminal.resizePaneLeft', - RESIZE_PANE_RIGHT = 'workbench.action.terminal.resizePaneRight', - RESIZE_PANE_UP = 'workbench.action.terminal.resizePaneUp', - RESIZE_PANE_DOWN = 'workbench.action.terminal.resizePaneDown', - FOCUS = 'workbench.action.terminal.focus', - FOCUS_NEXT = 'workbench.action.terminal.focusNext', - FOCUS_PREVIOUS = 'workbench.action.terminal.focusPrevious', - PASTE = 'workbench.action.terminal.paste', - SELECT_DEFAULT_SHELL = 'workbench.action.terminal.selectDefaultShell', - RUN_SELECTED_TEXT = 'workbench.action.terminal.runSelectedText', - RUN_ACTIVE_FILE = 'workbench.action.terminal.runActiveFile', - SWITCH_TERMINAL = 'workbench.action.terminal.switchTerminal', - SCROLL_DOWN_LINE = 'workbench.action.terminal.scrollDown', - SCROLL_DOWN_PAGE = 'workbench.action.terminal.scrollDownPage', - SCROLL_TO_BOTTOM = 'workbench.action.terminal.scrollToBottom', - SCROLL_UP_LINE = 'workbench.action.terminal.scrollUp', - SCROLL_UP_PAGE = 'workbench.action.terminal.scrollUpPage', - SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', - CLEAR = 'workbench.action.terminal.clear', - CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', - MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', - RENAME = 'workbench.action.terminal.rename', - FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', - FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', - QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', - SCROLL_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.scrollToPreviousCommand', - SCROLL_TO_NEXT_COMMAND = 'workbench.action.terminal.scrollToNextCommand', - SELECT_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.selectToPreviousCommand', - SELECT_TO_NEXT_COMMAND = 'workbench.action.terminal.selectToNextCommand', - SELECT_TO_PREVIOUS_LINE = 'workbench.action.terminal.selectToPreviousLine', - SELECT_TO_NEXT_LINE = 'workbench.action.terminal.selectToNextLine', - TOGGLE_ESCAPE_SEQUENCE_LOGGING = 'toggleEscapeSequenceLogging', - SEND_SEQUENCE = 'workbench.action.terminal.sendSequence', - TOGGLE_FIND_REGEX = 'workbench.action.terminal.toggleFindRegex', - TOGGLE_FIND_WHOLE_WORD = 'workbench.action.terminal.toggleFindWholeWord', - TOGGLE_FIND_CASE_SENSITIVE = 'workbench.action.terminal.toggleFindCaseSensitive', - TOGGLE_FIND_REGEX_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindRegexTerminalFocus', - TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindWholeWordTerminalFocus', - TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindCaseSensitiveTerminalFocus', - NAVIGATION_MODE_EXIT = 'workbench.action.terminal.navigationModeExit', - NAVIGATION_MODE_FOCUS_NEXT = 'workbench.action.terminal.navigationModeFocusNext', - NAVIGATION_MODE_FOCUS_PREVIOUS = 'workbench.action.terminal.navigationModeFocusPrevious' -} - -export function setupTerminalCommands(): void { - registerOpenTerminalAtIndexCommands(); -} - -function registerOpenTerminalAtIndexCommands(): void { - for (let i = 0; i < 9; i++) { - const terminalIndex = i; - const visibleIndex = i + 1; - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: `workbench.action.terminal.focusAtIndex${visibleIndex}`, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: 0, - handler: accessor => { - const terminalService = accessor.get(ITerminalService); - terminalService.setActiveInstanceByIndex(terminalIndex); - return terminalService.showPanel(true); - } - }); - } -} diff --git a/src/vs/workbench/contrib/terminal/common/terminalMenu.ts b/src/vs/workbench/contrib/terminal/common/terminalMenu.ts index 7493d800d2d7f..1b93dc8db1f2c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalMenu.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalMenu.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export function setupTerminalMenu() { diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts deleted file mode 100644 index 3303cd18301d5..0000000000000 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ /dev/null @@ -1,595 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalNativeService, IShellDefinition, IAvailableShellsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { URI } from 'vs/base/common/uri'; -import { FindReplaceState } from 'vs/editor/contrib/find/findState'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IFileService } from 'vs/platform/files/common/files'; -import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; -import { basename } from 'vs/base/common/path'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { IPickOptions, IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; - -interface IExtHostReadyEntry { - promise: Promise; - resolve: () => void; -} - -export abstract class TerminalService implements ITerminalService { - public _serviceBrand: undefined; - - protected _isShuttingDown: boolean; - protected _terminalFocusContextKey: IContextKey; - protected _findWidgetVisible: IContextKey; - protected _terminalTabs: ITerminalTab[] = []; - protected _backgroundedTerminalInstances: ITerminalInstance[] = []; - protected get _terminalInstances(): ITerminalInstance[] { - return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); - } - private _findState: FindReplaceState; - private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {}; - private _activeTabIndex: number; - - public get activeTabIndex(): number { return this._activeTabIndex; } - public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } - public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } - - protected readonly _onActiveTabChanged = new Emitter(); - public get onActiveTabChanged(): Event { return this._onActiveTabChanged.event; } - protected readonly _onInstanceCreated = new Emitter(); - public get onInstanceCreated(): Event { return this._onInstanceCreated.event; } - protected readonly _onInstanceDisposed = new Emitter(); - public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } - protected readonly _onInstanceProcessIdReady = new Emitter(); - public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } - protected readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); - public get onInstanceRequestSpawnExtHostProcess(): Event { return this._onInstanceRequestSpawnExtHostProcess.event; } - protected readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); - public get onInstanceRequestStartExtensionTerminal(): Event { return this._onInstanceRequestStartExtensionTerminal.event; } - protected readonly _onInstanceDimensionsChanged = new Emitter(); - public get onInstanceDimensionsChanged(): Event { return this._onInstanceDimensionsChanged.event; } - protected readonly _onInstanceMaximumDimensionsChanged = new Emitter(); - public get onInstanceMaximumDimensionsChanged(): Event { return this._onInstanceMaximumDimensionsChanged.event; } - protected readonly _onInstancesChanged = new Emitter(); - public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } - protected readonly _onInstanceTitleChanged = new Emitter(); - public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } - protected readonly _onActiveInstanceChanged = new Emitter(); - public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } - protected readonly _onTabDisposed = new Emitter(); - public get onTabDisposed(): Event { return this._onTabDisposed.event; } - protected readonly _onRequestAvailableShells = new Emitter(); - public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } - - public abstract get configHelper(): ITerminalConfigHelper; - - constructor( - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IPanelService protected readonly _panelService: IPanelService, - @ILifecycleService readonly lifecycleService: ILifecycleService, - @IStorageService protected readonly _storageService: IStorageService, - @INotificationService protected readonly _notificationService: INotificationService, - @IDialogService private readonly _dialogService: IDialogService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IFileService protected readonly _fileService: IFileService, - @IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService, - @ITerminalNativeService private readonly _terminalNativeService: ITerminalNativeService, - @IQuickInputService private readonly _quickInputService: IQuickInputService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - this._activeTabIndex = 0; - this._isShuttingDown = false; - this._findState = new FindReplaceState(); - lifecycleService.onBeforeShutdown(event => event.veto(this._onBeforeShutdown())); - lifecycleService.onShutdown(() => this._onShutdown()); - this._terminalNativeService.onOpenFileRequest(e => this._onOpenFileRequest(e)); - this._terminalNativeService.onOsResume(() => this._onOsResume()); - this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); - this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); - this.onTabDisposed(tab => this._removeTab(tab)); - this.onActiveTabChanged(() => { - const instance = this.getActiveInstance(); - this._onActiveInstanceChanged.fire(instance ? instance : undefined); - }); - - this._handleContextKeys(); - } - - private _handleContextKeys(): void { - const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); - - const updateTerminalContextKeys = () => { - terminalIsOpenContext.set(this.terminalInstances.length > 0); - }; - - this.onInstancesChanged(() => updateTerminalContextKeys()); - } - - protected abstract _showBackgroundTerminal(instance: ITerminalInstance): void; - - public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; - // tslint:disable-next-line: no-dom-globals - public abstract createInstance(container: HTMLElement, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; - // tslint:disable-next-line: no-dom-globals - public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - - public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { - const activeInstance = this.getActiveInstance(); - return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); - } - - public requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void { - this._extensionService.whenInstalledExtensionsRegistered().then(async () => { - // Wait for the remoteAuthority to be ready (and listening for events) before firing - // the event to spawn the ext host process - const conn = this._remoteAgentService.getConnection(); - const remoteAuthority = conn ? conn.remoteAuthority : 'null'; - await this._whenExtHostReady(remoteAuthority); - this._onInstanceRequestSpawnExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed }); - }); - } - - public requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void { - this._onInstanceRequestStartExtensionTerminal.fire({ proxy, cols, rows }); - } - - public async extHostReady(remoteAuthority: string): Promise { - this._createExtHostReadyEntry(remoteAuthority); - this._extHostsReady[remoteAuthority]!.resolve(); - } - - private async _whenExtHostReady(remoteAuthority: string): Promise { - this._createExtHostReadyEntry(remoteAuthority); - return this._extHostsReady[remoteAuthority]!.promise; - } - - private _createExtHostReadyEntry(remoteAuthority: string): void { - if (this._extHostsReady[remoteAuthority]) { - return; - } - - let resolve!: () => void; - const promise = new Promise(r => resolve = r); - this._extHostsReady[remoteAuthority] = { promise, resolve }; - } - - private _onBeforeShutdown(): boolean | Promise { - if (this.terminalInstances.length === 0) { - // No terminal instances, don't veto - return false; - } - - if (this.configHelper.config.confirmOnExit) { - // veto if configured to show confirmation and the user choosed not to exit - return this._showTerminalCloseConfirmation().then(veto => { - if (!veto) { - this._isShuttingDown = true; - } - return veto; - }); - } - - this._isShuttingDown = true; - - return false; - } - - private _onShutdown(): void { - // Dispose of all instances - this.terminalInstances.forEach(instance => instance.dispose(true)); - } - - private _onOpenFileRequest(request: IOpenFileRequest): void { - // if the request to open files is coming in from the integrated terminal (identified though - // the termProgram variable) and we are instructed to wait for editors close, wait for the - // marker file to get deleted and then focus back to the integrated terminal. - if (request.termProgram === 'vscode' && request.filesToWait) { - const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri); - this._terminalNativeService.whenFileDeleted(waitMarkerFileUri).then(() => { - if (this.terminalInstances.length > 0) { - const terminal = this.getActiveInstance(); - if (terminal) { - terminal.focus(); - } - } - }); - } - } - - private _onOsResume(): void { - const activeTab = this.getActiveTab(); - if (!activeTab) { - return; - } - activeTab.terminalInstances.forEach(instance => instance.forceRedraw()); - } - - public getTabLabels(): string[] { - return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); - } - - public getFindState(): FindReplaceState { - return this._findState; - } - - private _removeTab(tab: ITerminalTab): void { - // Get the index of the tab and remove it from the list - const index = this._terminalTabs.indexOf(tab); - const wasActiveTab = tab === this.getActiveTab(); - if (index !== -1) { - this._terminalTabs.splice(index, 1); - } - - // Adjust focus if the tab was active - if (wasActiveTab && this._terminalTabs.length > 0) { - // TODO: Only focus the new tab if the removed tab had focus? - // const hasFocusOnExit = tab.activeInstance.hadFocusOnExit; - const newIndex = index < this._terminalTabs.length ? index : this._terminalTabs.length - 1; - this.setActiveTabByIndex(newIndex); - const activeInstance = this.getActiveInstance(); - if (activeInstance) { - activeInstance.focus(true); - } - } - - // Hide the panel if there are no more instances, provided that VS Code is not shutting - // down. When shutting down the panel is locked in place so that it is restored upon next - // launch. - if (this._terminalTabs.length === 0 && !this._isShuttingDown) { - this.hidePanel(); - this._onActiveInstanceChanged.fire(undefined); - } - - // Fire events - this._onInstancesChanged.fire(); - if (wasActiveTab) { - this._onActiveTabChanged.fire(); - } - } - - public refreshActiveTab(): void { - // Fire active instances changed - this._onActiveTabChanged.fire(); - } - - public getActiveTab(): ITerminalTab | null { - if (this._activeTabIndex < 0 || this._activeTabIndex >= this._terminalTabs.length) { - return null; - } - return this._terminalTabs[this._activeTabIndex]; - } - - public getActiveInstance(): ITerminalInstance | null { - const tab = this.getActiveTab(); - if (!tab) { - return null; - } - return tab.activeInstance; - } - - public getInstanceFromId(terminalId: number): ITerminalInstance | undefined { - let bgIndex = -1; - this._backgroundedTerminalInstances.forEach((terminalInstance, i) => { - if (terminalInstance.id === terminalId) { - bgIndex = i; - } - }); - if (bgIndex !== -1) { - return this._backgroundedTerminalInstances[bgIndex]; - } - try { - return this.terminalInstances[this._getIndexFromId(terminalId)]; - } catch { - return undefined; - } - } - - public getInstanceFromIndex(terminalIndex: number): ITerminalInstance { - return this.terminalInstances[terminalIndex]; - } - - public setActiveInstance(terminalInstance: ITerminalInstance): void { - // If this was a hideFromUser terminal created by the API this was triggered by show, - // in which case we need to create the terminal tab - if (terminalInstance.shellLaunchConfig.hideFromUser) { - this._showBackgroundTerminal(terminalInstance); - } - this.setActiveInstanceByIndex(this._getIndexFromId(terminalInstance.id)); - } - - public setActiveTabByIndex(tabIndex: number): void { - if (tabIndex >= this._terminalTabs.length) { - return; - } - - const didTabChange = this._activeTabIndex !== tabIndex; - this._activeTabIndex = tabIndex; - - this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); - if (didTabChange) { - this._onActiveTabChanged.fire(); - } - } - - private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } | null { - let currentTabIndex = 0; - while (index >= 0 && currentTabIndex < this._terminalTabs.length) { - const tab = this._terminalTabs[currentTabIndex]; - const count = tab.terminalInstances.length; - if (index < count) { - return { - tab, - tabIndex: currentTabIndex, - instance: tab.terminalInstances[index], - localInstanceIndex: index - }; - } - index -= count; - currentTabIndex++; - } - return null; - } - - public setActiveInstanceByIndex(terminalIndex: number): void { - const query = this._getInstanceFromGlobalInstanceIndex(terminalIndex); - if (!query) { - return; - } - - query.tab.setActiveInstanceByIndex(query.localInstanceIndex); - const didTabChange = this._activeTabIndex !== query.tabIndex; - this._activeTabIndex = query.tabIndex; - this._terminalTabs.forEach((t, i) => t.setVisible(i === query.tabIndex)); - - // Only fire the event if there was a change - if (didTabChange) { - this._onActiveTabChanged.fire(); - } - } - - public setActiveTabToNext(): void { - if (this._terminalTabs.length <= 1) { - return; - } - let newIndex = this._activeTabIndex + 1; - if (newIndex >= this._terminalTabs.length) { - newIndex = 0; - } - this.setActiveTabByIndex(newIndex); - } - - public setActiveTabToPrevious(): void { - if (this._terminalTabs.length <= 1) { - return; - } - let newIndex = this._activeTabIndex - 1; - if (newIndex < 0) { - newIndex = this._terminalTabs.length - 1; - } - this.setActiveTabByIndex(newIndex); - } - - public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): ITerminalInstance | null { - const tab = this._getTabForInstance(instanceToSplit); - if (!tab) { - return null; - } - - const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig); - if (!instance) { - this._showNotEnoughSpaceToast(); - return null; - } - - this._initInstanceListeners(instance); - this._onInstancesChanged.fire(); - - this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); - return instance; - } - - protected _initInstanceListeners(instance: ITerminalInstance): void { - instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); - instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); - instance.addDisposable(instance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); - instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance))); - instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance))); - instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); - } - - private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | null { - for (const tab of this._terminalTabs) { - if (tab.terminalInstances.indexOf(instance) !== -1) { - return tab; - } - } - return null; - } - - public showPanel(focus?: boolean): Promise { - return new Promise((complete) => { - const panel = this._panelService.getActivePanel(); - if (!panel || panel.getId() !== TERMINAL_PANEL_ID) { - this._panelService.openPanel(TERMINAL_PANEL_ID, focus); - if (focus) { - // Do the focus call asynchronously as going through the - // command palette will force editor focus - setTimeout(() => { - const instance = this.getActiveInstance(); - if (instance) { - instance.focusWhenReady(true).then(() => complete(undefined)); - } else { - complete(undefined); - } - }, 0); - } else { - complete(undefined); - } - } else { - if (focus) { - // Do the focus call asynchronously as going through the - // command palette will force editor focus - setTimeout(() => { - const instance = this.getActiveInstance(); - if (instance) { - instance.focusWhenReady(true).then(() => complete(undefined)); - } else { - complete(undefined); - } - }, 0); - } else { - complete(undefined); - } - } - return undefined; - }); - } - - public abstract hidePanel(): void; - - public abstract focusFindWidget(): Promise; - public abstract hideFindWidget(): void; - - public abstract findNext(): void; - public abstract findPrevious(): void; - - private _getIndexFromId(terminalId: number): number { - let terminalIndex = -1; - this.terminalInstances.forEach((terminalInstance, i) => { - if (terminalInstance.id === terminalId) { - terminalIndex = i; - } - }); - if (terminalIndex === -1) { - throw new Error(`Terminal with ID ${terminalId} does not exist (has it already been disposed?)`); - } - return terminalIndex; - } - - public async manageWorkspaceShellPermissions(): Promise { - const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration") }; - const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration") }; - const value = await this._quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); - if (!value) { - return; - } - this.configHelper.setWorkspaceShellAllowed(value === allowItem); - } - - protected async _showTerminalCloseConfirmation(): Promise { - let message: string; - if (this.terminalInstances.length === 1) { - message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); - } else { - message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); - } - const res = await this._dialogService.confirm({ - message, - type: 'warning', - }); - return !res.confirmed; - } - - protected _showNotEnoughSpaceToast(): void { - this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); - } - - protected _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { - if (potentialPaths.length === 0) { - return Promise.resolve(null); - } - const current = potentialPaths.shift(); - if (current! === '') { - return this._validateShellPaths(label, potentialPaths); - } - return this._fileService.exists(URI.file(current!)).then(exists => { - if (!exists) { - return this._validateShellPaths(label, potentialPaths); - } - return [label, current] as [string, string]; - }); - } - - public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { - return new Promise(c => { - if (!executable) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - - const pathBasename = basename(executable, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - title === 'pwsh' || - pathBasename === 'powershell' || - title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - const lowerExecutable = executable.toLowerCase(); - if (this._terminalNativeService.getWindowsBuildNumber() >= 17063 && - (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1))) { - c(this._terminalNativeService.getWslPath(originalPath)); - return; - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - return; - } - c(escapeNonWindowsPath(originalPath)); - }); - } - - public selectDefaultWindowsShell(): Promise { - return this._detectWindowsShells().then(shells => { - const options: IPickOptions = { - placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") - }; - const quickPickItems = shells.map((s): IQuickPickItem => { - return { label: s.label, description: s.path }; - }); - return this._quickInputService.pick(quickPickItems, options).then(async value => { - if (!value) { - return undefined; - } - const shell = value.description; - const env = await this._remoteAgentService.getEnvironment(); - let platformKey: string; - if (env) { - platformKey = env.os === OperatingSystem.Windows ? 'windows' : (env.os === OperatingSystem.Macintosh ? 'osx' : 'linux'); - } else { - platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); - } - await this._configurationService.updateValue(`terminal.integrated.shell.${platformKey}`, shell, ConfigurationTarget.USER).then(() => shell); - return Promise.resolve(); - }); - }); - } - - private _detectWindowsShells(): Promise { - return new Promise(r => this._onRequestAvailableShells.fire(r)); - } -} diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index dd0874bfceddc..fd87ba3c73492 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { ITerminalInstance, IWindowsShellHelper, IShellLaunchConfig, ITerminalChildProcess, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; -import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; +import { ITerminalInstanceService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IWindowsShellHelper, IShellLaunchConfig, ITerminalChildProcess, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/electron-browser/windowsShellHelper'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts index 30a6841ac76a0..17302176a3891 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts @@ -13,7 +13,7 @@ import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/termi import { execFile } from 'child_process'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/node/terminalRemote'; +import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/electron-browser/terminalRemote'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class TerminalNativeService implements ITerminalNativeService { @@ -78,4 +78,4 @@ export class TerminalNativeService implements ITerminalNativeService { public getWindowsBuildNumber(): number { return getWindowsBuildNumber(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts similarity index 90% rename from src/vs/workbench/contrib/terminal/node/terminalRemote.ts rename to src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts index 3ebea6304cf6e..2b7d5e39af35e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts @@ -7,11 +7,11 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TERMINAL_ACTION_CATEGORY, ITerminalService, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { TERMINAL_ACTION_CATEGORY, TitleEventSource, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { homedir } from 'os'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; export function registerRemoteContributions() { const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts similarity index 95% rename from src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts rename to src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts index 0b20c735a89c3..e36a8d72f006d 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper.ts @@ -5,10 +5,11 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { ITerminalInstance, IWindowsShellHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'xterm'; import * as WindowsProcessTreeType from 'windows-process-tree'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; const SHELL_EXECUTABLES = [ 'cmd.exe', diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 5a1ee63bf03d3..a18b827e544f6 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -22,12 +22,12 @@ import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/bro import { StartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { FindInFilesActionId } from 'vs/workbench/contrib/search/common/constants'; import { QUICKOPEN_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import * as dom from 'vs/base/browser/dom'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; const $ = dom.$;