mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463 (#7206)
* Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463 * fix config changes * fix strictnull checks
This commit is contained in:
@@ -72,8 +72,6 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .panel.integrated-terminal .split-view-view {
|
||||
/* Make relative as terminal absolute positioning needs it deeper in the tree */
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -194,10 +195,16 @@ configurationRegistry.registerConfiguration({
|
||||
type: 'number',
|
||||
default: 1000
|
||||
},
|
||||
'terminal.integrated.setLocaleVariables': {
|
||||
markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal."),
|
||||
type: 'boolean',
|
||||
default: true
|
||||
'terminal.integrated.detectLocale': {
|
||||
markdownDescription: nls.localize('terminal.integrated.detectLocale', "Controls whether to detect and set the `$LANG` environment variable to a UTF-8 compliant option since VS Code's terminal only supports UTF-8 encoded data coming from the shell."),
|
||||
type: 'string',
|
||||
enum: ['auto', 'off', 'on'],
|
||||
enumDescriptions: [
|
||||
nls.localize('terminal.integrated.detectLocale.auto', "Set the `$LANG` environment variable if the existing variable does not exist or it does not end in `'.UTF-8'`."),
|
||||
nls.localize('terminal.integrated.detectLocale.off', "Do not set the `$LANG` environment variable."),
|
||||
nls.localize('terminal.integrated.detectLocale.on', "Always set the `$LANG` environment variable.")
|
||||
],
|
||||
default: 'auto'
|
||||
},
|
||||
'terminal.integrated.rendererType': {
|
||||
type: 'string',
|
||||
|
||||
@@ -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<ITerminalService>('terminalService');
|
||||
export const ITerminalInstanceService = createDecorator<ITerminalInstanceService>('terminalInstanceService');
|
||||
|
||||
/**
|
||||
@@ -19,7 +24,7 @@ export const ITerminalInstanceService = createDecorator<ITerminalInstanceService
|
||||
* dependency on ITerminalService.
|
||||
*/
|
||||
export interface ITerminalInstanceService {
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: undefined;
|
||||
|
||||
// These events are optional as the requests they make are only needed on the browser side
|
||||
onRequestDefaultShellAndArgs?: Event<IDefaultShellAndArgsRequest>;
|
||||
@@ -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<ITerminalTab>;
|
||||
onInstancesChanged: Event<void>;
|
||||
|
||||
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<boolean>, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance | undefined;
|
||||
}
|
||||
|
||||
export interface ITerminalService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
activeTabIndex: number;
|
||||
configHelper: ITerminalConfigHelper;
|
||||
terminalInstances: ITerminalInstance[];
|
||||
terminalTabs: ITerminalTab[];
|
||||
|
||||
onActiveTabChanged: Event<void>;
|
||||
onTabDisposed: Event<ITerminalTab>;
|
||||
onInstanceCreated: Event<ITerminalInstance>;
|
||||
onInstanceDisposed: Event<ITerminalInstance>;
|
||||
onInstanceProcessIdReady: Event<ITerminalInstance>;
|
||||
onInstanceDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceMaximumDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceRequestSpawnExtHostProcess: Event<ISpawnExtHostProcessRequest>;
|
||||
onInstanceRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest>;
|
||||
onInstancesChanged: Event<void>;
|
||||
onInstanceTitleChanged: Event<ITerminalInstance>;
|
||||
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
|
||||
onRequestAvailableShells: Event<IAvailableShellsRequest>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
hidePanel(): void;
|
||||
focusFindWidget(): Promise<void>;
|
||||
hideFindWidget(): void;
|
||||
getFindState(): FindReplaceState;
|
||||
findNext(): void;
|
||||
findPrevious(): void;
|
||||
|
||||
selectDefaultWindowsShell(): Promise<void>;
|
||||
|
||||
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<string>;
|
||||
|
||||
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<ITerminalInstance>;
|
||||
|
||||
/**
|
||||
* An event that fires when the terminal instance is disposed.
|
||||
*/
|
||||
onDisposed: Event<ITerminalInstance>;
|
||||
|
||||
onFocused: Event<ITerminalInstance>;
|
||||
onProcessIdReady: Event<ITerminalInstance>;
|
||||
onRequestExtHostProcess: Event<ITerminalInstance>;
|
||||
onDimensionsChanged: Event<void>;
|
||||
onMaximumDimensionsChanged: Event<void>;
|
||||
|
||||
onFocus: Event<ITerminalInstance>;
|
||||
|
||||
/**
|
||||
* Attach a listener to the raw data stream coming from the pty, including ANSI escape
|
||||
* sequences.
|
||||
*/
|
||||
onData: Event<string>;
|
||||
|
||||
/**
|
||||
* 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<string>;
|
||||
|
||||
/**
|
||||
* 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<number>;
|
||||
|
||||
processReady: Promise<void>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
|
||||
/**
|
||||
* Focuses and pastes the contents of the clipboard into the terminal instance.
|
||||
*/
|
||||
paste(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 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<string>;
|
||||
|
||||
setDimensions(dimensions: ITerminalDimensions): void;
|
||||
|
||||
addDisposable(disposable: IDisposable): void;
|
||||
|
||||
toggleEscapeSequenceLogging(): void;
|
||||
|
||||
getInitialCwd(): Promise<string>;
|
||||
getCwd(): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -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 ';
|
||||
|
||||
@@ -231,8 +231,8 @@ export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction {
|
||||
label: string,
|
||||
@ITerminalService terminalService: ITerminalService
|
||||
) {
|
||||
// Send alt+D
|
||||
super(id, label, '\x1bD', terminalService);
|
||||
// Send alt+d
|
||||
super(id, label, '\x1bd', terminalService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,7 +549,7 @@ export class FocusActiveTerminalAction extends Action {
|
||||
}
|
||||
|
||||
public run(event?: any): Promise<any> {
|
||||
const instance = this.terminalService.getActiveOrCreateInstance(true);
|
||||
const instance = this.terminalService.getActiveOrCreateInstance();
|
||||
if (!instance) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { Terminal as XTermTerminal } from 'xterm';
|
||||
import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification';
|
||||
import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -21,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
|
||||
const MINIMUM_FONT_SIZE = 6;
|
||||
const MAXIMUM_FONT_SIZE = 25;
|
||||
@@ -129,7 +129,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
* Gets the font information based on the terminal.integrated.fontFamily
|
||||
* terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties
|
||||
*/
|
||||
public getFont(xterm?: XTermTerminal, excludeDimensions?: boolean): ITerminalFont {
|
||||
public getFont(xtermCore?: XTermCore, excludeDimensions?: boolean): ITerminalFont {
|
||||
const editorConfig = this._configurationService.getValue<IEditorOptions>('editor');
|
||||
|
||||
let fontFamily = this.config.fontFamily || editorConfig.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
|
||||
@@ -161,15 +161,15 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
}
|
||||
|
||||
// Get the character dimensions from xterm if it's available
|
||||
if (xterm) {
|
||||
if (xterm._core._charSizeService && xterm._core._charSizeService.width && xterm._core._charSizeService.height) {
|
||||
if (xtermCore) {
|
||||
if (xtermCore._charSizeService && xtermCore._charSizeService.width && xtermCore._charSizeService.height) {
|
||||
return {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
letterSpacing,
|
||||
lineHeight,
|
||||
charHeight: xterm._core._charSizeService.height,
|
||||
charWidth: xterm._core._charSizeService.width
|
||||
charHeight: xtermCore._charSizeService.height,
|
||||
charWidth: xtermCore._charSizeService.width
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<boolean>;
|
||||
|
||||
@@ -25,19 +25,19 @@ 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';
|
||||
import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon';
|
||||
import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/addons/navigationModeAddon';
|
||||
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
|
||||
// How long in milliseconds should an average frame take to render for a notification to appear
|
||||
// which suggests the fallback DOM-based renderer
|
||||
@@ -184,6 +184,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
private _title: string = '';
|
||||
private _wrapperElement: (HTMLElement & { xterm?: XTermTerminal }) | undefined;
|
||||
private _xterm: XTermTerminal | undefined;
|
||||
private _xtermCore: XTermCore | undefined;
|
||||
private _xtermSearch: SearchAddon | undefined;
|
||||
private _xtermElement: HTMLDivElement | undefined;
|
||||
private _terminalHasTextContextKey: IContextKey<boolean>;
|
||||
@@ -351,7 +352,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
return null;
|
||||
}
|
||||
|
||||
const font = this._configHelper.getFont(this._xterm);
|
||||
const font = this._configHelper.getFont(this._xtermCore);
|
||||
if (!font.charWidth || !font.charHeight) {
|
||||
this._setLastKnownColsAndRows();
|
||||
return null;
|
||||
@@ -399,7 +400,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
|
||||
private _getDimension(width: number, height: number): ICanvasDimensions | undefined {
|
||||
// The font needs to have been initialized
|
||||
const font = this._configHelper.getFont(this._xterm);
|
||||
const font = this._configHelper.getFont(this._xtermCore);
|
||||
if (!font || !font.charWidth || !font.charHeight) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -412,8 +413,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
// needs to happen otherwise its scrollTop value is invalid when the panel is toggled as
|
||||
// it gets removed and then added back to the DOM (resetting scrollTop to 0).
|
||||
// Upstream issue: https://github.com/sourcelair/xterm.js/issues/291
|
||||
if (this._xterm) {
|
||||
this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY);
|
||||
if (this._xterm && this._xtermCore) {
|
||||
this._xtermCore._onScroll.fire(this._xterm.buffer.viewportY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +473,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
rendererType: config.rendererType === 'auto' ? 'canvas' : config.rendererType
|
||||
});
|
||||
this._xterm = xterm;
|
||||
this._xtermCore = (xterm as any)._core as XTermCore;
|
||||
this.updateAccessibilitySupport();
|
||||
this._terminalInstanceService.getXtermSearchConstructor().then(Addon => {
|
||||
this._xtermSearch = new Addon();
|
||||
@@ -674,9 +676,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
|
||||
private async _measureRenderTime(): Promise<void> {
|
||||
const xterm = await this._xtermReadyPromise;
|
||||
await this._xtermReadyPromise;
|
||||
const frameTimes: number[] = [];
|
||||
const textRenderLayer = xterm._core._renderService._renderer._renderLayers[0];
|
||||
const textRenderLayer = this._xtermCore!._renderService._renderer._renderLayers[0];
|
||||
const originalOnGridChanged = textRenderLayer.onGridChanged;
|
||||
|
||||
const evaluateCanvasRenderer = () => {
|
||||
@@ -893,12 +895,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
if (this._wrapperElement) {
|
||||
dom.toggleClass(this._wrapperElement, 'active', visible);
|
||||
}
|
||||
if (visible && this._xterm) {
|
||||
if (visible && this._xterm && this._xtermCore) {
|
||||
// Trigger a manual scroll event which will sync the viewport and scroll bar. This is
|
||||
// necessary if the number of rows in the terminal has decreased while it was in the
|
||||
// background since scrollTop changes take no effect but the terminal's position does
|
||||
// change since the number of visible rows decreases.
|
||||
this._xterm._core._onScroll.fire(this._xterm.buffer.viewportY);
|
||||
this._xtermCore._onScroll.fire(this._xterm.buffer.viewportY);
|
||||
if (this._container && this._container.parentElement) {
|
||||
// Force a layout when the instance becomes invisible. This is particularly important
|
||||
// for ensuring that terminals that are created in the background by an extension will
|
||||
@@ -1322,11 +1324,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
let cols = this.cols;
|
||||
let rows = this.rows;
|
||||
|
||||
if (this._xterm) {
|
||||
if (this._xterm && this._xtermCore) {
|
||||
// Only apply these settings when the terminal is visible so that
|
||||
// the characters are measured correctly.
|
||||
if (this._isVisible) {
|
||||
const font = this._configHelper.getFont(this._xterm);
|
||||
const font = this._configHelper.getFont(this._xtermCore);
|
||||
const config = this._configHelper.config;
|
||||
this._safeSetOption('letterSpacing', font.letterSpacing);
|
||||
this._safeSetOption('lineHeight', font.lineHeight);
|
||||
@@ -1354,7 +1356,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
// maximize on Windows/Linux would fire an event saying that the terminal was not
|
||||
// visible.
|
||||
if (this._xterm.getOption('rendererType') === 'canvas') {
|
||||
this._xterm._core._renderService._onIntersectionChange({ intersectionRatio: 1 });
|
||||
this._xtermCore._renderService._onIntersectionChange({ intersectionRatio: 1 });
|
||||
// HACK: Force a refresh of the screen to ensure links are refresh corrected.
|
||||
// This can probably be removed when the above hack is fixed in Chromium.
|
||||
this._xterm.refresh(0, this._xterm.rows - 1);
|
||||
|
||||
@@ -17,7 +17,7 @@ let WebLinksAddon: typeof XTermWebLinksAddon;
|
||||
let SearchAddon: typeof XTermSearchAddon;
|
||||
|
||||
export class TerminalInstanceService implements ITerminalInstanceService {
|
||||
public _serviceBrand: any;
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private readonly _onRequestDefaultShellAndArgs = new Emitter<IDefaultShellAndArgsRequest>();
|
||||
public get onRequestDefaultShellAndArgs(): Event<IDefaultShellAndArgsRequest> { return this._onRequestDefaultShellAndArgs.event; }
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export class TerminalNativeService implements ITerminalNativeService {
|
||||
public _serviceBrand: any;
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
public get linuxDistro(): LinuxDistro { return LinuxDistro.Unknown; }
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
@@ -227,7 +227,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
|
||||
this._configHelper.showRecommendations(shellLaunchConfig);
|
||||
const baseEnv = this._configHelper.config.inheritEnv ? processEnv : await this._terminalInstanceService.getMainProcessParentEnv();
|
||||
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, baseEnv);
|
||||
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
|
||||
|
||||
const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled;
|
||||
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
* Licensed under the Source EULA. 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<void>;
|
||||
resolve: () => void;
|
||||
}
|
||||
|
||||
export class TerminalService implements ITerminalService {
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
protected _isShuttingDown: boolean;
|
||||
protected _terminalFocusContextKey: IContextKey<boolean>;
|
||||
protected _findWidgetVisible: IContextKey<boolean>;
|
||||
protected _terminalTabs: ITerminalTab[] = [];
|
||||
protected _backgroundedTerminalInstances: ITerminalInstance[] = [];
|
||||
protected get _terminalInstances(): ITerminalInstance[] {
|
||||
return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), <ITerminalInstance[]>[]);
|
||||
}
|
||||
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<void>();
|
||||
public get onActiveTabChanged(): Event<void> { return this._onActiveTabChanged.event; }
|
||||
protected readonly _onInstanceCreated = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceCreated(): Event<ITerminalInstance> { return this._onInstanceCreated.event; }
|
||||
protected readonly _onInstanceDisposed = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
|
||||
protected readonly _onInstanceProcessIdReady = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
|
||||
protected readonly _onInstanceRequestSpawnExtHostProcess = new Emitter<ISpawnExtHostProcessRequest>();
|
||||
public get onInstanceRequestSpawnExtHostProcess(): Event<ISpawnExtHostProcessRequest> { return this._onInstanceRequestSpawnExtHostProcess.event; }
|
||||
protected readonly _onInstanceRequestStartExtensionTerminal = new Emitter<IStartExtensionTerminalRequest>();
|
||||
public get onInstanceRequestStartExtensionTerminal(): Event<IStartExtensionTerminalRequest> { return this._onInstanceRequestStartExtensionTerminal.event; }
|
||||
protected readonly _onInstanceDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceDimensionsChanged.event; }
|
||||
protected readonly _onInstanceMaximumDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceMaximumDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceMaximumDimensionsChanged.event; }
|
||||
protected readonly _onInstancesChanged = new Emitter<void>();
|
||||
public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
|
||||
protected readonly _onInstanceTitleChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceTitleChanged(): Event<ITerminalInstance> { return this._onInstanceTitleChanged.event; }
|
||||
protected readonly _onActiveInstanceChanged = new Emitter<ITerminalInstance | undefined>();
|
||||
public get onActiveInstanceChanged(): Event<ITerminalInstance | undefined> { return this._onActiveInstanceChanged.event; }
|
||||
protected readonly _onTabDisposed = new Emitter<ITerminalTab>();
|
||||
public get onTabDisposed(): Event<ITerminalTab> { return this._onTabDisposed.event; }
|
||||
protected readonly _onRequestAvailableShells = new Emitter<IAvailableShellsRequest>();
|
||||
public get onRequestAvailableShells(): Event<IAvailableShellsRequest> { 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<void> {
|
||||
this._createExtHostReadyEntry(remoteAuthority);
|
||||
this._extHostsReady[remoteAuthority]!.resolve();
|
||||
}
|
||||
|
||||
private async _whenExtHostReady(remoteAuthority: string): Promise<void> {
|
||||
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<void>(r => resolve = r);
|
||||
this._extHostsReady[remoteAuthority] = { promise, resolve };
|
||||
}
|
||||
|
||||
private _onBeforeShutdown(): boolean | Promise<boolean> {
|
||||
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<void> {
|
||||
return new Promise<void>((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<void> {
|
||||
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<boolean> {
|
||||
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<string> {
|
||||
return new Promise<string>(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<void> {
|
||||
return this._detectWindowsShells().then(shells => {
|
||||
const options: IPickOptions<IQuickPickItem> = {
|
||||
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<IShellDefinition[]> {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
34
src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts
vendored
Normal file
34
src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface XTermCore {
|
||||
_onScroll: IEventEmitter<number>;
|
||||
_onKey: IEventEmitter<{ key: string }>;
|
||||
|
||||
_charSizeService: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
_coreService: {
|
||||
triggerDataEvent(data: string, wasUserInput?: boolean): void;
|
||||
};
|
||||
|
||||
_renderService: {
|
||||
_renderer: {
|
||||
_renderLayers: any[];
|
||||
};
|
||||
_onIntersectionChange: any;
|
||||
};
|
||||
|
||||
// TODO: Remove below once a synchronous write API is added
|
||||
// The below are only used in tests
|
||||
writeBuffer: string[];
|
||||
_innerWrite(): void;
|
||||
}
|
||||
|
||||
export interface IEventEmitter<T> {
|
||||
fire(e: T): void;
|
||||
}
|
||||
@@ -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<ITerminalService>('terminalService');
|
||||
export const ITerminalNativeService = createDecorator<ITerminalNativeService>('terminalNativeService');
|
||||
|
||||
export const TerminalCursorStyle = {
|
||||
@@ -100,7 +98,7 @@ export interface ITerminalConfiguration {
|
||||
fontSize: number;
|
||||
letterSpacing: number;
|
||||
lineHeight: number;
|
||||
setLocaleVariables: boolean;
|
||||
detectLocale: 'auto' | 'off' | 'on';
|
||||
scrollback: number;
|
||||
commandsToSkipShell: string[];
|
||||
cwd: string;
|
||||
@@ -217,94 +215,11 @@ export interface IShellLaunchConfig {
|
||||
hideFromUser?: boolean;
|
||||
}
|
||||
|
||||
export interface ITerminalService {
|
||||
_serviceBrand: any;
|
||||
|
||||
activeTabIndex: number;
|
||||
configHelper: ITerminalConfigHelper;
|
||||
terminalInstances: ITerminalInstance[];
|
||||
terminalTabs: ITerminalTab[];
|
||||
|
||||
onActiveTabChanged: Event<void>;
|
||||
onTabDisposed: Event<ITerminalTab>;
|
||||
onInstanceCreated: Event<ITerminalInstance>;
|
||||
onInstanceDisposed: Event<ITerminalInstance>;
|
||||
onInstanceProcessIdReady: Event<ITerminalInstance>;
|
||||
onInstanceDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceMaximumDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceRequestSpawnExtHostProcess: Event<ISpawnExtHostProcessRequest>;
|
||||
onInstanceRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest>;
|
||||
onInstancesChanged: Event<void>;
|
||||
onInstanceTitleChanged: Event<ITerminalInstance>;
|
||||
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
|
||||
onRequestAvailableShells: Event<IAvailableShellsRequest>;
|
||||
|
||||
/**
|
||||
* 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
|
||||
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<void>;
|
||||
hidePanel(): void;
|
||||
focusFindWidget(): Promise<void>;
|
||||
hideFindWidget(): void;
|
||||
getFindState(): FindReplaceState;
|
||||
findNext(): void;
|
||||
findPrevious(): void;
|
||||
|
||||
selectDefaultWindowsShell(): Promise<void>;
|
||||
|
||||
// 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<string>;
|
||||
|
||||
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.
|
||||
*/
|
||||
export interface ITerminalNativeService {
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly linuxDistro: LinuxDistro;
|
||||
|
||||
@@ -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<ITerminalTab>;
|
||||
onInstancesChanged: Event<void>;
|
||||
|
||||
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<boolean>, 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<ITerminalInstance>;
|
||||
|
||||
/**
|
||||
* An event that fires when the terminal instance is disposed.
|
||||
*/
|
||||
onDisposed: Event<ITerminalInstance>;
|
||||
|
||||
onFocused: Event<ITerminalInstance>;
|
||||
onProcessIdReady: Event<ITerminalInstance>;
|
||||
onRequestExtHostProcess: Event<ITerminalInstance>;
|
||||
onDimensionsChanged: Event<void>;
|
||||
onMaximumDimensionsChanged: Event<void>;
|
||||
|
||||
onFocus: Event<ITerminalInstance>;
|
||||
|
||||
/**
|
||||
* Attach a listener to the raw data stream coming from the pty, including ANSI escape
|
||||
* sequences.
|
||||
*/
|
||||
onData: Event<string>;
|
||||
|
||||
/**
|
||||
* 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<string>;
|
||||
|
||||
/**
|
||||
* 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<number>;
|
||||
|
||||
processReady: Promise<void>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
|
||||
/**
|
||||
* 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<void>;
|
||||
|
||||
/**
|
||||
* Focuses and pastes the contents of the clipboard into the terminal instance.
|
||||
*/
|
||||
paste(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 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<string>;
|
||||
|
||||
setDimensions(dimensions: ITerminalDimensions): void;
|
||||
|
||||
addDisposable(disposable: IDisposable): void;
|
||||
|
||||
toggleEscapeSequenceLogging(): void;
|
||||
|
||||
getInitialCwd(): Promise<string>;
|
||||
getCwd(): Promise<string>;
|
||||
}
|
||||
|
||||
export interface ICommandTracker {
|
||||
scrollToPreviousCommand(): void;
|
||||
scrollToNextCommand(): void;
|
||||
@@ -813,3 +406,69 @@ export interface ITerminalChildProcess {
|
||||
getCwd(): Promise<string>;
|
||||
getLatency(): Promise<number>;
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,13 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s
|
||||
}
|
||||
}
|
||||
|
||||
export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void {
|
||||
export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, version: string | undefined, locale: string | undefined, detectLocale: 'auto' | 'off' | 'on'): void {
|
||||
env['TERM_PROGRAM'] = 'vscode';
|
||||
if (version) {
|
||||
env['TERM_PROGRAM_VERSION'] = version;
|
||||
}
|
||||
if (setLocaleVariables) {
|
||||
env['LANG'] = _getLangEnvVariable(locale);
|
||||
if (shouldSetLangEnvVariable(env, detectLocale)) {
|
||||
env['LANG'] = getLangEnvVariable(locale);
|
||||
}
|
||||
env['COLORTERM'] = 'truecolor';
|
||||
}
|
||||
@@ -88,37 +88,87 @@ function resolveConfigurationVariables(configurationResolverService: IConfigurat
|
||||
return env;
|
||||
}
|
||||
|
||||
function _getLangEnvVariable(locale?: string) {
|
||||
export function shouldSetLangEnvVariable(env: platform.IProcessEnvironment, detectLocale: 'auto' | 'off' | 'on'): boolean {
|
||||
if (detectLocale === 'on') {
|
||||
return true;
|
||||
}
|
||||
if (detectLocale === 'auto') {
|
||||
return !env['LANG'] || env['LANG'].search(/\.UTF\-8$/) === -1;
|
||||
}
|
||||
return false; // 'off'
|
||||
}
|
||||
|
||||
export function getLangEnvVariable(locale?: string): string {
|
||||
const parts = locale ? locale.split('-') : [];
|
||||
const n = parts.length;
|
||||
if (n === 0) {
|
||||
// Fallback to en_US to prevent possible encoding issues.
|
||||
// Fallback to en_US if the locale is unknown
|
||||
return 'en_US.UTF-8';
|
||||
}
|
||||
if (n === 1) {
|
||||
// app.getLocale can return just a language without a variant, fill in the variant for
|
||||
// supported languages as many shells expect a 2-part locale.
|
||||
// The local may only contain the language, not the variant, if this is the case guess the
|
||||
// variant such that it can be used as a valid $LANG variable. The language variant chosen
|
||||
// is the original and/or most prominent with help from
|
||||
// https://stackoverflow.com/a/2502675/1156119
|
||||
// The list of locales was generated by running `locale -a` on macOS
|
||||
const languageVariants: { [key: string]: string } = {
|
||||
af: 'ZA',
|
||||
am: 'ET',
|
||||
be: 'BY',
|
||||
bg: 'BG',
|
||||
ca: 'ES',
|
||||
cs: 'CZ',
|
||||
da: 'DK',
|
||||
// de: 'AT',
|
||||
// de: 'CH',
|
||||
de: 'DE',
|
||||
el: 'GR',
|
||||
// en: 'AU',
|
||||
// en: 'CA',
|
||||
// en: 'GB',
|
||||
// en: 'IE',
|
||||
// en: 'NZ',
|
||||
en: 'US',
|
||||
es: 'ES',
|
||||
et: 'EE',
|
||||
eu: 'ES',
|
||||
fi: 'FI',
|
||||
// fr: 'BE',
|
||||
// fr: 'CA',
|
||||
// fr: 'CH',
|
||||
fr: 'FR',
|
||||
he: 'IL',
|
||||
hr: 'HR',
|
||||
hu: 'HU',
|
||||
hy: 'AM',
|
||||
is: 'IS',
|
||||
// it: 'CH',
|
||||
it: 'IT',
|
||||
ja: 'JP',
|
||||
kk: 'KZ',
|
||||
ko: 'KR',
|
||||
lt: 'LT',
|
||||
// nl: 'BE',
|
||||
nl: 'NL',
|
||||
no: 'NO',
|
||||
pl: 'PL',
|
||||
pt: 'BR',
|
||||
// pt: 'PT',
|
||||
ro: 'RO',
|
||||
ru: 'RU',
|
||||
sk: 'SK',
|
||||
zh: 'CN'
|
||||
sl: 'SI',
|
||||
sr: 'YU',
|
||||
sv: 'SE',
|
||||
tr: 'TR',
|
||||
uk: 'UA',
|
||||
zh: 'CN',
|
||||
};
|
||||
if (parts[0] in languageVariants) {
|
||||
parts.push(languageVariants[parts[0]]);
|
||||
}
|
||||
} else {
|
||||
// Ensure the variant is uppercase
|
||||
// Ensure the variant is uppercase to be a valid $LANG
|
||||
parts[1] = parts[1].toUpperCase();
|
||||
}
|
||||
return parts.join('_') + '.UTF-8';
|
||||
@@ -294,7 +344,7 @@ export function createTerminalEnvironment(
|
||||
configurationResolverService: IConfigurationResolverService | undefined,
|
||||
isWorkspaceShellAllowed: boolean,
|
||||
version: string | undefined,
|
||||
setLocaleVariables: boolean,
|
||||
detectLocale: 'auto' | 'off' | 'on',
|
||||
baseEnv: platform.IProcessEnvironment
|
||||
): platform.IProcessEnvironment {
|
||||
// Create a terminal environment based on settings, launch config and permissions
|
||||
@@ -329,7 +379,7 @@ export function createTerminalEnvironment(
|
||||
mergeEnvironments(env, shellLaunchConfig.env);
|
||||
|
||||
// Adding other env keys necessary to create the process
|
||||
addTerminalEnvironmentKeys(env, version, platform.locale, setLocaleVariables);
|
||||
addTerminalEnvironmentKeys(env, version, platform.locale, detectLocale);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,595 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. 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<void>;
|
||||
resolve: () => void;
|
||||
}
|
||||
|
||||
export abstract class TerminalService implements ITerminalService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
protected _isShuttingDown: boolean;
|
||||
protected _terminalFocusContextKey: IContextKey<boolean>;
|
||||
protected _findWidgetVisible: IContextKey<boolean>;
|
||||
protected _terminalTabs: ITerminalTab[] = [];
|
||||
protected _backgroundedTerminalInstances: ITerminalInstance[] = [];
|
||||
protected get _terminalInstances(): ITerminalInstance[] {
|
||||
return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), <ITerminalInstance[]>[]);
|
||||
}
|
||||
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<void>();
|
||||
public get onActiveTabChanged(): Event<void> { return this._onActiveTabChanged.event; }
|
||||
protected readonly _onInstanceCreated = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceCreated(): Event<ITerminalInstance> { return this._onInstanceCreated.event; }
|
||||
protected readonly _onInstanceDisposed = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
|
||||
protected readonly _onInstanceProcessIdReady = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
|
||||
protected readonly _onInstanceRequestSpawnExtHostProcess = new Emitter<ISpawnExtHostProcessRequest>();
|
||||
public get onInstanceRequestSpawnExtHostProcess(): Event<ISpawnExtHostProcessRequest> { return this._onInstanceRequestSpawnExtHostProcess.event; }
|
||||
protected readonly _onInstanceRequestStartExtensionTerminal = new Emitter<IStartExtensionTerminalRequest>();
|
||||
public get onInstanceRequestStartExtensionTerminal(): Event<IStartExtensionTerminalRequest> { return this._onInstanceRequestStartExtensionTerminal.event; }
|
||||
protected readonly _onInstanceDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceDimensionsChanged.event; }
|
||||
protected readonly _onInstanceMaximumDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceMaximumDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceMaximumDimensionsChanged.event; }
|
||||
protected readonly _onInstancesChanged = new Emitter<void>();
|
||||
public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
|
||||
protected readonly _onInstanceTitleChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceTitleChanged(): Event<ITerminalInstance> { return this._onInstanceTitleChanged.event; }
|
||||
protected readonly _onActiveInstanceChanged = new Emitter<ITerminalInstance | undefined>();
|
||||
public get onActiveInstanceChanged(): Event<ITerminalInstance | undefined> { return this._onActiveInstanceChanged.event; }
|
||||
protected readonly _onTabDisposed = new Emitter<ITerminalTab>();
|
||||
public get onTabDisposed(): Event<ITerminalTab> { return this._onTabDisposed.event; }
|
||||
protected readonly _onRequestAvailableShells = new Emitter<IAvailableShellsRequest>();
|
||||
public get onRequestAvailableShells(): Event<IAvailableShellsRequest> { 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<void> {
|
||||
this._createExtHostReadyEntry(remoteAuthority);
|
||||
this._extHostsReady[remoteAuthority]!.resolve();
|
||||
}
|
||||
|
||||
private async _whenExtHostReady(remoteAuthority: string): Promise<void> {
|
||||
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<void>(r => resolve = r);
|
||||
this._extHostsReady[remoteAuthority] = { promise, resolve };
|
||||
}
|
||||
|
||||
private _onBeforeShutdown(): boolean | Promise<boolean> {
|
||||
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<void> {
|
||||
return new Promise<void>((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<void>;
|
||||
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<void> {
|
||||
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<boolean> {
|
||||
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<string> {
|
||||
return new Promise<string>(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<void> {
|
||||
return this._detectWindowsShells().then(shells => {
|
||||
const options: IPickOptions<IQuickPickItem> = {
|
||||
placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings")
|
||||
};
|
||||
const quickPickItems = shells.map(s => {
|
||||
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<IShellDefinition[]> {
|
||||
return new Promise(r => this._onRequestAvailableShells.fire(r));
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under the Source EULA. 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';
|
||||
@@ -27,7 +27,7 @@ let WebLinksAddon: typeof XTermWebLinksAddon;
|
||||
let SearchAddon: typeof XTermSearchAddon;
|
||||
|
||||
export class TerminalInstanceService implements ITerminalInstanceService {
|
||||
public _serviceBrand: any;
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
|
||||
@@ -13,11 +13,11 @@ 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 {
|
||||
public _serviceBrand: any;
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
public get linuxDistro(): LinuxDistro { return linuxDistro; }
|
||||
|
||||
@@ -78,4 +78,4 @@ export class TerminalNativeService implements ITerminalNativeService {
|
||||
public getWindowsBuildNumber(): number {
|
||||
return getWindowsBuildNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
@@ -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',
|
||||
@@ -4,17 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Terminal, TerminalCore } from 'xterm';
|
||||
import { Terminal } from 'xterm';
|
||||
import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
interface TestTerminalCore extends TerminalCore {
|
||||
writeBuffer: string[];
|
||||
_innerWrite(): void;
|
||||
}
|
||||
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
|
||||
interface TestTerminal extends Terminal {
|
||||
_core: TestTerminalCore;
|
||||
_core: XTermCore;
|
||||
}
|
||||
|
||||
function syncWrite(term: TestTerminal, data: string): void {
|
||||
|
||||
@@ -35,7 +35,7 @@ class MockTerminalInstanceService implements ITerminalInstanceService {
|
||||
getDefaultShellAndArgs(): Promise<{ shell: string; args: string | string[] | undefined; }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: undefined;
|
||||
getXtermConstructor(): Promise<any> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
@@ -5,30 +5,110 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { URI as Uri } from 'vs/base/common/uri';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { addTerminalEnvironmentKeys, mergeEnvironments, getCwd, getDefaultShell, getLangEnvVariable, shouldSetLangEnvVariable } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
|
||||
suite('Workbench - TerminalEnvironment', () => {
|
||||
test('addTerminalEnvironmentKeys', () => {
|
||||
const env: { [key: string]: any } = { FOO: 'bar' };
|
||||
const locale = 'en-au';
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env, '1.2.3', locale, true);
|
||||
assert.equal(env['TERM_PROGRAM'], 'vscode');
|
||||
assert.equal(env['TERM_PROGRAM_VERSION'], '1.2.3');
|
||||
assert.equal(env['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8');
|
||||
suite('addTerminalEnvironmentKeys', () => {
|
||||
test('should set expected variables', () => {
|
||||
const env: { [key: string]: any } = {};
|
||||
addTerminalEnvironmentKeys(env, '1.2.3', 'en', 'on');
|
||||
assert.equal(env['TERM_PROGRAM'], 'vscode');
|
||||
assert.equal(env['TERM_PROGRAM_VERSION'], '1.2.3');
|
||||
assert.equal(env['COLORTERM'], 'truecolor');
|
||||
assert.equal(env['LANG'], 'en_US.UTF-8');
|
||||
});
|
||||
test('should use language variant for LANG that is provided in locale', () => {
|
||||
const env: { [key: string]: any } = {};
|
||||
addTerminalEnvironmentKeys(env, '1.2.3', 'en-au', 'on');
|
||||
assert.equal(env['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8');
|
||||
});
|
||||
test('should fallback to en_US when no locale is provided', () => {
|
||||
const env2: { [key: string]: any } = { FOO: 'bar' };
|
||||
addTerminalEnvironmentKeys(env2, '1.2.3', undefined, 'on');
|
||||
assert.equal(env2['LANG'], 'en_US.UTF-8', 'LANG is equal to en_US.UTF-8 as fallback.'); // More info on issue #14586
|
||||
});
|
||||
test('should fallback to en_US when an invalid locale is provided', () => {
|
||||
const env3 = { LANG: 'replace' };
|
||||
addTerminalEnvironmentKeys(env3, '1.2.3', undefined, 'on');
|
||||
assert.equal(env3['LANG'], 'en_US.UTF-8', 'LANG is set to the fallback LANG');
|
||||
});
|
||||
test('should override existing LANG', () => {
|
||||
const env4 = { LANG: 'en_AU.UTF-8' };
|
||||
addTerminalEnvironmentKeys(env4, '1.2.3', undefined, 'on');
|
||||
assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG');
|
||||
});
|
||||
});
|
||||
|
||||
const env2: { [key: string]: any } = { FOO: 'bar' };
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env2, '1.2.3', undefined, true);
|
||||
assert.equal(env2['LANG'], 'en_US.UTF-8', 'LANG is equal to en_US.UTF-8 as fallback.'); // More info on issue #14586
|
||||
suite('shouldSetLangEnvVariable', () => {
|
||||
test('auto', () => {
|
||||
assert.equal(shouldSetLangEnvVariable({}, 'auto'), true);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'auto'), true);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'auto'), false);
|
||||
});
|
||||
test('off', () => {
|
||||
assert.equal(shouldSetLangEnvVariable({}, 'off'), false);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'off'), false);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'off'), false);
|
||||
});
|
||||
test('on', () => {
|
||||
assert.equal(shouldSetLangEnvVariable({}, 'on'), true);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US' }, 'on'), true);
|
||||
assert.equal(shouldSetLangEnvVariable({ LANG: 'en-US.UTF-8' }, 'on'), true);
|
||||
});
|
||||
});
|
||||
|
||||
const env3 = { LANG: 'replace' };
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true);
|
||||
assert.equal(env3['LANG'], 'en_US.UTF-8', 'LANG is set to the fallback LANG');
|
||||
|
||||
const env4 = { LANG: 'en_US.UTF-8' };
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true);
|
||||
assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG');
|
||||
suite('getLangEnvVariable', () => {
|
||||
test('should fallback to en_US when no locale is provided', () => {
|
||||
assert.equal(getLangEnvVariable(undefined), 'en_US.UTF-8');
|
||||
assert.equal(getLangEnvVariable(''), 'en_US.UTF-8');
|
||||
});
|
||||
test('should fallback to default language variants when variant isn\'t provided', () => {
|
||||
assert.equal(getLangEnvVariable('af'), 'af_ZA.UTF-8');
|
||||
assert.equal(getLangEnvVariable('am'), 'am_ET.UTF-8');
|
||||
assert.equal(getLangEnvVariable('be'), 'be_BY.UTF-8');
|
||||
assert.equal(getLangEnvVariable('bg'), 'bg_BG.UTF-8');
|
||||
assert.equal(getLangEnvVariable('ca'), 'ca_ES.UTF-8');
|
||||
assert.equal(getLangEnvVariable('cs'), 'cs_CZ.UTF-8');
|
||||
assert.equal(getLangEnvVariable('da'), 'da_DK.UTF-8');
|
||||
assert.equal(getLangEnvVariable('de'), 'de_DE.UTF-8');
|
||||
assert.equal(getLangEnvVariable('el'), 'el_GR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('en'), 'en_US.UTF-8');
|
||||
assert.equal(getLangEnvVariable('es'), 'es_ES.UTF-8');
|
||||
assert.equal(getLangEnvVariable('et'), 'et_EE.UTF-8');
|
||||
assert.equal(getLangEnvVariable('eu'), 'eu_ES.UTF-8');
|
||||
assert.equal(getLangEnvVariable('fi'), 'fi_FI.UTF-8');
|
||||
assert.equal(getLangEnvVariable('fr'), 'fr_FR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('he'), 'he_IL.UTF-8');
|
||||
assert.equal(getLangEnvVariable('hr'), 'hr_HR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('hu'), 'hu_HU.UTF-8');
|
||||
assert.equal(getLangEnvVariable('hy'), 'hy_AM.UTF-8');
|
||||
assert.equal(getLangEnvVariable('is'), 'is_IS.UTF-8');
|
||||
assert.equal(getLangEnvVariable('it'), 'it_IT.UTF-8');
|
||||
assert.equal(getLangEnvVariable('ja'), 'ja_JP.UTF-8');
|
||||
assert.equal(getLangEnvVariable('kk'), 'kk_KZ.UTF-8');
|
||||
assert.equal(getLangEnvVariable('ko'), 'ko_KR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('lt'), 'lt_LT.UTF-8');
|
||||
assert.equal(getLangEnvVariable('nl'), 'nl_NL.UTF-8');
|
||||
assert.equal(getLangEnvVariable('no'), 'no_NO.UTF-8');
|
||||
assert.equal(getLangEnvVariable('pl'), 'pl_PL.UTF-8');
|
||||
assert.equal(getLangEnvVariable('pt'), 'pt_BR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('ro'), 'ro_RO.UTF-8');
|
||||
assert.equal(getLangEnvVariable('ru'), 'ru_RU.UTF-8');
|
||||
assert.equal(getLangEnvVariable('sk'), 'sk_SK.UTF-8');
|
||||
assert.equal(getLangEnvVariable('sl'), 'sl_SI.UTF-8');
|
||||
assert.equal(getLangEnvVariable('sr'), 'sr_YU.UTF-8');
|
||||
assert.equal(getLangEnvVariable('sv'), 'sv_SE.UTF-8');
|
||||
assert.equal(getLangEnvVariable('tr'), 'tr_TR.UTF-8');
|
||||
assert.equal(getLangEnvVariable('uk'), 'uk_UA.UTF-8');
|
||||
assert.equal(getLangEnvVariable('zh'), 'zh_CN.UTF-8');
|
||||
});
|
||||
test('should set language variant based on full locale', () => {
|
||||
assert.equal(getLangEnvVariable('en-AU'), 'en_AU.UTF-8');
|
||||
assert.equal(getLangEnvVariable('en-au'), 'en_AU.UTF-8');
|
||||
assert.equal(getLangEnvVariable('fa-ke'), 'fa_KE.UTF-8');
|
||||
});
|
||||
});
|
||||
|
||||
suite('mergeEnvironments', () => {
|
||||
@@ -39,7 +119,7 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
const other = {
|
||||
c: 'd'
|
||||
};
|
||||
terminalEnvironment.mergeEnvironments(parent, other);
|
||||
mergeEnvironments(parent, other);
|
||||
assert.deepEqual(parent, {
|
||||
a: 'b',
|
||||
c: 'd'
|
||||
@@ -56,7 +136,7 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
const other = {
|
||||
A: 'c'
|
||||
};
|
||||
terminalEnvironment.mergeEnvironments(parent, other);
|
||||
mergeEnvironments(parent, other);
|
||||
assert.deepEqual(parent, {
|
||||
a: 'c'
|
||||
});
|
||||
@@ -70,7 +150,7 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
const other: IStringDictionary<string | null> = {
|
||||
a: null
|
||||
};
|
||||
terminalEnvironment.mergeEnvironments(parent, other);
|
||||
mergeEnvironments(parent, other);
|
||||
assert.deepEqual(parent, {
|
||||
c: 'd'
|
||||
});
|
||||
@@ -87,7 +167,7 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
const other: IStringDictionary<string | null> = {
|
||||
A: null
|
||||
};
|
||||
terminalEnvironment.mergeEnvironments(parent, other);
|
||||
mergeEnvironments(parent, other);
|
||||
assert.deepEqual(parent, {
|
||||
c: 'd'
|
||||
});
|
||||
@@ -101,37 +181,37 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
}
|
||||
|
||||
test('should default to userHome for an empty workspace', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, undefined), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, undefined), '/userHome/');
|
||||
});
|
||||
|
||||
test('should use to the workspace if it exists', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/foo'), undefined), '/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/foo'), undefined), '/foo');
|
||||
});
|
||||
|
||||
test('should use an absolute custom cwd as is', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, '/foo'), '/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, '/foo'), '/foo');
|
||||
});
|
||||
|
||||
test('should normalize a relative custom cwd against the workspace path', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), 'foo'), '/bar/foo');
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), './foo'), '/bar/foo');
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), '../foo'), '/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), 'foo'), '/bar/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), './foo'), '/bar/foo');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, Uri.file('/bar'), '../foo'), '/foo');
|
||||
});
|
||||
|
||||
test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, 'foo'), '/userHome/');
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, './foo'), '/userHome/');
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, '../foo'), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, 'foo'), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, './foo'), '/userHome/');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined, undefined, '../foo'), '/userHome/');
|
||||
});
|
||||
|
||||
test('should ignore custom cwd when told to ignore', () => {
|
||||
assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, undefined, Uri.file('/bar'), '/foo'), '/bar');
|
||||
assertPathsMatch(getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', undefined, undefined, Uri.file('/bar'), '/foo'), '/bar');
|
||||
});
|
||||
});
|
||||
|
||||
suite('getDefaultShell', () => {
|
||||
test('should change Sysnative to System32 in non-WoW64 systems', () => {
|
||||
const shell = terminalEnvironment.getDefaultShell(key => {
|
||||
const shell = getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': { user: 'C:\\Windows\\Sysnative\\cmd.exe', value: undefined, default: undefined }
|
||||
} as any)[key];
|
||||
@@ -140,7 +220,7 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
});
|
||||
|
||||
test('should not change Sysnative to System32 in WoW64 systems', () => {
|
||||
const shell = terminalEnvironment.getDefaultShell(key => {
|
||||
const shell = getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': { user: 'C:\\Windows\\Sysnative\\cmd.exe', value: undefined, default: undefined }
|
||||
} as any)[key];
|
||||
@@ -149,21 +229,21 @@ suite('Workbench - TerminalEnvironment', () => {
|
||||
});
|
||||
|
||||
test('should use automationShell when specified', () => {
|
||||
const shell1 = terminalEnvironment.getDefaultShell(key => {
|
||||
const shell1 = getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': { user: 'shell', value: undefined, default: undefined },
|
||||
'terminal.integrated.automationShell.windows': { user: undefined, value: undefined, default: undefined }
|
||||
} as any)[key];
|
||||
}, false, 'DEFAULT', false, 'C:\\Windows', undefined, undefined, {} as any, false, platform.Platform.Windows);
|
||||
assert.equal(shell1, 'shell', 'automationShell was false');
|
||||
const shell2 = terminalEnvironment.getDefaultShell(key => {
|
||||
const shell2 = getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': { user: 'shell', value: undefined, default: undefined },
|
||||
'terminal.integrated.automationShell.windows': { user: undefined, value: undefined, default: undefined }
|
||||
} as any)[key];
|
||||
}, false, 'DEFAULT', false, 'C:\\Windows', undefined, undefined, {} as any, true, platform.Platform.Windows);
|
||||
assert.equal(shell2, 'shell', 'automationShell was true');
|
||||
const shell3 = terminalEnvironment.getDefaultShell(key => {
|
||||
const shell3 = getDefaultShell(key => {
|
||||
return ({
|
||||
'terminal.integrated.shell.windows': { user: 'shell', value: undefined, default: undefined },
|
||||
'terminal.integrated.automationShell.windows': { user: 'automationShell', value: undefined, default: undefined }
|
||||
|
||||
Reference in New Issue
Block a user