mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-25 04:36:23 +00:00
testing: use a real terminal in test Test Results view (#184963)
* wip * wip * it works * address pr comments * add cwd capability to test peek view * add padding and fix peek display issues * ignore scroll events with default prevented * fixup tests * comments * comments
This commit is contained in:
@@ -373,6 +373,9 @@ export abstract class AbstractScrollableElement extends Widget {
|
||||
}
|
||||
|
||||
private _onMouseWheel(e: StandardWheelEvent): void {
|
||||
if (e.browserEvent?.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
const classifier = MouseWheelClassifier.INSTANCE;
|
||||
if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
|
||||
.monaco-workbench .xterm-viewport {
|
||||
/* Use the hack presented in https://stackoverflow.com/a/38748186/1156119 to get opacity transitions working on the scrollbar */
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
@@ -12,45 +11,35 @@
|
||||
transition: background-color 800ms linear;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
|
||||
.monaco-workbench .xterm-viewport {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport::-webkit-scrollbar,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport::-webkit-scrollbar {
|
||||
.monaco-workbench .xterm-viewport::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport::-webkit-scrollbar-track,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport::-webkit-scrollbar-track {
|
||||
.monaco-workbench .xterm-viewport::-webkit-scrollbar-track {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport::-webkit-scrollbar-thumb,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport::-webkit-scrollbar-thumb {
|
||||
.monaco-workbench .xterm-viewport::-webkit-scrollbar-thumb {
|
||||
min-height: 20px;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm:hover .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport {
|
||||
.monaco-workbench .force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .xterm:hover .xterm-viewport {
|
||||
transition: opacity 100ms linear;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||||
.monaco-workbench .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||||
transition: opacity 0ms linear;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm .xterm-viewport::-webkit-scrollbar-thumb:window-inactive,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:window-inactive {
|
||||
.monaco-workbench .xterm .xterm-viewport::-webkit-scrollbar-thumb:window-inactive {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-groups-container,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-group,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-split-pane,
|
||||
.monaco-workbench .editor-instance .terminal-split-pane,
|
||||
.monaco-workbench .editor-instance .terminal-outer-container {
|
||||
.monaco-workbench .terminal-editor .terminal-split-pane,
|
||||
.monaco-workbench .terminal-editor .terminal-outer-container {
|
||||
height: 100%;
|
||||
}
|
||||
.monaco-workbench .part.sidebar .pane-body.integrated-terminal .terminal-outer-container,
|
||||
@@ -48,7 +48,7 @@
|
||||
background-color: var(--vscode-terminal-tab-activeBorder);
|
||||
}
|
||||
/* Override monaco's styles for terminal editors */
|
||||
.monaco-workbench .editor-instance .xterm textarea:focus {
|
||||
.monaco-workbench .terminal-editor .xterm textarea:focus {
|
||||
opacity: 0 !important;
|
||||
outline: 0 !important;
|
||||
}
|
||||
@@ -62,20 +62,23 @@
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-wrapper {
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper {
|
||||
background-color: var(--vscode-terminal-background, --vscode-editorPane-background);
|
||||
}
|
||||
.monaco-workbench .editor-instance .terminal-wrapper,
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper {
|
||||
display: block;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm {
|
||||
.monaco-workbench .xterm {
|
||||
/* All terminals have at least 10px left/right edge padding and 2 padding on the bottom (so underscores on last line are visible */
|
||||
padding: 0 10px 2px;
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-editor .xterm,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm {
|
||||
/* Bottom align the terminal within the split pane */
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -88,23 +91,23 @@
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-wrapper.fixed-dims .xterm,
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper.fixed-dims .xterm,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper.fixed-dims .xterm {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport,
|
||||
.monaco-workbench .terminal-editor .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-decoration-overview-ruler,
|
||||
.monaco-workbench .terminal-editor .xterm-decoration-overview-ruler,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-decoration-overview-ruler {
|
||||
z-index: 31; /* Must be higher than .xterm-viewport */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-screen,
|
||||
.monaco-workbench .terminal-editor .xterm-screen,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-screen {
|
||||
z-index: 31;
|
||||
}
|
||||
@@ -127,7 +130,7 @@
|
||||
.xterm.xterm-cursor-pointer .xterm-screen { cursor: pointer; }
|
||||
.xterm.column-select.focus .xterm-screen { cursor: crosshair; }
|
||||
|
||||
.monaco-workbench .editor-instance .xterm {
|
||||
.monaco-workbench .terminal-editor .xterm {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
@@ -136,34 +139,34 @@
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm,
|
||||
.monaco-workbench .terminal-editor .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm a:not(.xterm-invalid-link),
|
||||
.monaco-workbench .terminal-editor .xterm a:not(.xterm-invalid-link),
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm a:not(.xterm-invalid-link) {
|
||||
/* To support message box sizing */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-wrapper > div,
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper > div,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper > div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport,
|
||||
.monaco-workbench .terminal-editor .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-wrapper.fixed-dims,
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper.fixed-dims,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper.fixed-dims {
|
||||
/* The viewport should be positioned against this so it does't conflict with a fixed dimensions terminal horizontal scroll bar*/
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .terminal-wrapper:not(.fixed-dims) .xterm-viewport,
|
||||
.monaco-workbench .terminal-editor .terminal-wrapper:not(.fixed-dims) .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper:not(.fixed-dims) .xterm-viewport {
|
||||
/* Override xterm.js' width as we want to size the viewport to fill the panel so the scrollbar is on the right edge */
|
||||
width: auto !important;
|
||||
@@ -245,7 +248,7 @@
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-helper-textarea:focus {
|
||||
.monaco-workbench .xterm .xterm-helper-textarea:focus {
|
||||
/* Override the general vscode style applies `opacity:1!important` to textareas */
|
||||
opacity: 0 !important;
|
||||
}
|
||||
@@ -522,34 +525,26 @@
|
||||
background-color: var(--vscode-terminal-hoverHighlightBackground);
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .editor-instance .xterm:hover .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport {
|
||||
.force-scrollbar .xterm .xterm-viewport,
|
||||
.monaco-workbench .xterm.focus .xterm-viewport,
|
||||
.monaco-workbench .xterm:focus .xterm-viewport,
|
||||
.monaco-workbench .xterm:hover .xterm-viewport {
|
||||
background-color: var(--vscode-scrollbarSlider-background) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
|
||||
.monaco-workbench .xterm-viewport {
|
||||
scrollbar-color: var(--vscode-scrollbarSlider-background) transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||||
.monaco-workbench .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--vscode-scrollbarSlider-hoverBackground);
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm-viewport:hover,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport:hover {
|
||||
.monaco-workbench .xterm-viewport:hover {
|
||||
scrollbar-color: var(--vscode-scrollbarSlider-hoverBackground) transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench .editor-instance .xterm .xterm-viewport::-webkit-scrollbar-thumb:active,
|
||||
.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:active {
|
||||
.monaco-workbench .xterm .xterm-viewport::-webkit-scrollbar-thumb:active {
|
||||
background-color: var(--vscode-scrollbarSlider-activeBackground);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,11 +139,21 @@ export const enum TerminalConnectionState {
|
||||
Connected
|
||||
}
|
||||
|
||||
export interface IDetachedXTermOptions {
|
||||
cols: number;
|
||||
rows: number;
|
||||
colorProvider: IXtermColorProvider;
|
||||
capabilities?: ITerminalCapabilityStore;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
export interface ITerminalService extends ITerminalInstanceHost {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
/** Gets all terminal instances, including editor and terminal view (group) instances. */
|
||||
readonly instances: readonly ITerminalInstance[];
|
||||
/** Gets detached terminal instances created via {@link createDetachedXterm}. */
|
||||
readonly detachedXterms: Iterable<IXtermTerminal>;
|
||||
configHelper: ITerminalConfigHelper;
|
||||
isProcessSupportRegistered: boolean;
|
||||
readonly connectionState: TerminalConnectionState;
|
||||
@@ -171,6 +181,13 @@ export interface ITerminalService extends ITerminalInstanceHost {
|
||||
*/
|
||||
createTerminal(options?: ICreateTerminalOptions): Promise<ITerminalInstance>;
|
||||
|
||||
/**
|
||||
* Creates a detached xterm instance which is not attached to the DOM or
|
||||
* tracked as a terminal instance.
|
||||
* @params options The options to create the terminal with
|
||||
*/
|
||||
createDetachedXterm(options: IDetachedXTermOptions): Promise<IDetachedXtermTerminal>;
|
||||
|
||||
/**
|
||||
* Creates a raw terminal instance, this should not be used outside of the terminal part.
|
||||
*/
|
||||
@@ -708,11 +725,6 @@ export interface ITerminalInstance {
|
||||
*/
|
||||
resetFocusContextKey(): void;
|
||||
|
||||
/**
|
||||
* Select all text in the terminal.
|
||||
*/
|
||||
selectAll(): void;
|
||||
|
||||
/**
|
||||
* Focuses the terminal instance if it's able to (the xterm.js instance must exist).
|
||||
*
|
||||
@@ -947,7 +959,14 @@ export const enum XtermTerminalConstants {
|
||||
SearchHighlightLimit = 1000
|
||||
}
|
||||
|
||||
export interface IXtermTerminal {
|
||||
export interface IXtermAttachToElementOptions {
|
||||
/**
|
||||
* Whether GPU rendering should be enabled for this element, defaults to true.
|
||||
*/
|
||||
enableGpu: boolean;
|
||||
}
|
||||
|
||||
export interface IXtermTerminal extends IDisposable {
|
||||
/**
|
||||
* An object that tracks when commands are run and enables navigating and selecting between
|
||||
* them.
|
||||
@@ -962,6 +981,11 @@ export interface IXtermTerminal {
|
||||
readonly onDidChangeSelection: Event<void>;
|
||||
readonly onDidChangeFindResults: Event<{ resultIndex: number; resultCount: number }>;
|
||||
|
||||
/**
|
||||
* Event fired when focus enters (fires with true) or leaves (false) the terminal.
|
||||
*/
|
||||
readonly onDidChangeFocus: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Gets a view of the current texture atlas used by the renderers.
|
||||
*/
|
||||
@@ -972,6 +996,18 @@ export interface IXtermTerminal {
|
||||
*/
|
||||
readonly isStdinDisabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether the terminal is currently focused.
|
||||
*/
|
||||
readonly isFocused: boolean;
|
||||
|
||||
/**
|
||||
* Attached the terminal to the given element
|
||||
* @param container Container the terminal will be rendered in
|
||||
* @param options Additional options for mounting the terminal in an element
|
||||
*/
|
||||
attachToElement(container: HTMLElement, options?: Partial<IXtermAttachToElementOptions>): void;
|
||||
|
||||
findResult?: { resultIndex: number; resultCount: number };
|
||||
|
||||
/**
|
||||
@@ -994,6 +1030,34 @@ export interface IXtermTerminal {
|
||||
*/
|
||||
getFont(): ITerminalFont;
|
||||
|
||||
/**
|
||||
* Gets whether there's any terminal selection.
|
||||
*/
|
||||
hasSelection(): boolean;
|
||||
|
||||
/**
|
||||
* Clears any terminal selection.
|
||||
*/
|
||||
clearSelection(): void;
|
||||
|
||||
/**
|
||||
* Selects all terminal contents/
|
||||
*/
|
||||
selectAll(): void;
|
||||
|
||||
/**
|
||||
* Copies the terminal selection.
|
||||
* @param {boolean} copyAsHtml Whether to copy selection as HTML, defaults to false.
|
||||
*/
|
||||
copySelection(copyAsHtml?: boolean): void;
|
||||
|
||||
/**
|
||||
* Focuses the terminal. Warning: {@link ITerminalInstance.focus} should be
|
||||
* preferred when dealing with terminal instances in order to get
|
||||
* accessibility triggers.
|
||||
*/
|
||||
focus(): 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;
|
||||
@@ -1022,12 +1086,30 @@ export interface IXtermTerminal {
|
||||
*/
|
||||
getBufferReverseIterator(): IterableIterator<string>;
|
||||
|
||||
/**
|
||||
* Gets the buffer contents as HTML.
|
||||
*/
|
||||
getContentsAsHtml(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Refreshes the terminal after it has been moved.
|
||||
*/
|
||||
refresh(): void;
|
||||
}
|
||||
|
||||
export interface IDetachedXtermTerminal extends IXtermTerminal {
|
||||
|
||||
/**
|
||||
* Writes data to the terminal.
|
||||
*/
|
||||
write(data: string | Uint8Array): void;
|
||||
|
||||
/**
|
||||
* Resizes the terminal.
|
||||
*/
|
||||
resize(columns: number, rows: number): void;
|
||||
}
|
||||
|
||||
export interface IInternalXtermTerminal {
|
||||
/**
|
||||
* Writes text directly to the terminal, bypassing the process.
|
||||
|
||||
@@ -32,7 +32,7 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac
|
||||
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/contextkeys';
|
||||
import { Direction, ICreateTerminalOptions, ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { Direction, ICreateTerminalOptions, ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
|
||||
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, ITerminalProfileResolverService, ITerminalProfileService, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
@@ -60,6 +60,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
||||
import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { killTerminalIcon, newTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
||||
export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';
|
||||
export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs");
|
||||
@@ -179,6 +180,32 @@ export function registerActiveInstanceAction(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around {@link registerTerminalAction} that ensures an active terminal
|
||||
* exists and provides it to the run function.
|
||||
*
|
||||
* This includes detached xterm terminals that are not managed by an {@link ITerminalInstance}.
|
||||
*/
|
||||
export function registerActiveXtermAction(
|
||||
options: IAction2Options & { run: (activeTerminal: IXtermTerminal, accessor: ServicesAccessor, instance?: ITerminalInstance, args?: unknown) => void | Promise<unknown> }
|
||||
): IDisposable {
|
||||
const originalRun = options.run;
|
||||
return registerTerminalAction({
|
||||
...options,
|
||||
run: (c, accessor, args) => {
|
||||
const activeDetached = Iterable.find(c.service.detachedXterms, d => d.isFocused);
|
||||
if (activeDetached) {
|
||||
return originalRun(activeDetached, accessor, undefined, args);
|
||||
}
|
||||
|
||||
const activeInstance = c.service.activeInstance;
|
||||
if (activeInstance?.xterm) {
|
||||
return originalRun(activeInstance.xterm, accessor, activeInstance, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface ITerminalServicesCollection {
|
||||
service: ITerminalService;
|
||||
groupService: ITerminalGroupService;
|
||||
@@ -569,59 +596,59 @@ export function registerTerminalActions() {
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollDownLine,
|
||||
title: { value: localize('workbench.action.terminal.scrollDown', "Scroll Down (Line)"), original: 'Scroll Down (Line)' },
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown,
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollDownLine()
|
||||
run: (xterm) => xterm.scrollDownLine()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollDownPage,
|
||||
title: { value: localize('workbench.action.terminal.scrollDownPage', "Scroll Down (Page)"), original: 'Scroll Down (Page)' },
|
||||
keybinding: {
|
||||
primary: KeyMod.Shift | KeyCode.PageDown,
|
||||
mac: { primary: KeyCode.PageDown },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollDownPage()
|
||||
run: (xterm) => xterm.scrollDownPage()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollToBottom,
|
||||
title: { value: localize('workbench.action.terminal.scrollToBottom', "Scroll to Bottom"), original: 'Scroll to Bottom' },
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.End,
|
||||
linux: { primary: KeyMod.Shift | KeyCode.End },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollToBottom()
|
||||
run: (xterm) => xterm.scrollToBottom()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollUpLine,
|
||||
title: { value: localize('workbench.action.terminal.scrollUp', "Scroll Up (Line)"), original: 'Scroll Up (Line)' },
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp,
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollUpLine()
|
||||
run: (xterm) => xterm.scrollUpLine()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollUpPage,
|
||||
title: { value: localize('workbench.action.terminal.scrollUpPage', "Scroll Up (Page)"), original: 'Scroll Up (Page)' },
|
||||
f1: true,
|
||||
@@ -629,38 +656,38 @@ export function registerTerminalActions() {
|
||||
keybinding: {
|
||||
primary: KeyMod.Shift | KeyCode.PageUp,
|
||||
mac: { primary: KeyCode.PageUp },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollUpPage()
|
||||
run: (xterm) => xterm.scrollUpPage()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ScrollToTop,
|
||||
title: { value: localize('workbench.action.terminal.scrollToTop', "Scroll to Top"), original: 'Scroll to Top' },
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Home,
|
||||
linux: { primary: KeyMod.Shift | KeyCode.Home },
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => activeInstance.scrollToTop()
|
||||
run: (xterm) => xterm.scrollToTop()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.ClearSelection,
|
||||
title: { value: localize('workbench.action.terminal.clearSelection', "Clear Selection"), original: 'Clear Selection' },
|
||||
keybinding: {
|
||||
primary: KeyCode.Escape,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.textSelected, TerminalContextKeys.notFindVisible),
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focusInAny, TerminalContextKeys.textSelected, TerminalContextKeys.notFindVisible),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: (activeInstance) => {
|
||||
if (activeInstance.hasSelection()) {
|
||||
activeInstance.clearSelection();
|
||||
run: (xterm) => {
|
||||
if (xterm.hasSelection()) {
|
||||
xterm.clearSelection();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -879,23 +906,25 @@ export function registerTerminalActions() {
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.SelectToPreviousLine,
|
||||
title: { value: localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line"), original: 'Select To Previous Line' },
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: async (activeInstance) => {
|
||||
activeInstance.xterm?.markTracker.selectToPreviousLine();
|
||||
activeInstance.focus();
|
||||
run: async (xterm, _, instance) => {
|
||||
xterm.markTracker.selectToPreviousLine();
|
||||
// prefer to call focus on the TerminalInstance for additional accessibility triggers
|
||||
(instance || xterm).focus();
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.SelectToNextLine,
|
||||
title: { value: localize('workbench.action.terminal.selectToNextLine', "Select To Next Line"), original: 'Select To Next Line' },
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
run: async (activeInstance) => {
|
||||
activeInstance.xterm?.markTracker.selectToNextLine();
|
||||
activeInstance.focus();
|
||||
run: async (xterm, _, instance) => {
|
||||
xterm.markTracker.selectToNextLine();
|
||||
// prefer to call focus on the TerminalInstance for additional accessibility triggers
|
||||
(instance || xterm).focus();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1152,7 +1181,7 @@ export function registerTerminalActions() {
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.SelectAll,
|
||||
title: { value: localize('workbench.action.terminal.selectAll', "Select All"), original: 'Select All' },
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated),
|
||||
@@ -1165,9 +1194,9 @@ export function registerTerminalActions() {
|
||||
// makes it easier for users to see how it works though.
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.KeyA },
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: TerminalContextKeys.focus
|
||||
when: TerminalContextKeys.focusInAny
|
||||
}],
|
||||
run: (activeInstance) => activeInstance.selectAll()
|
||||
run: (xterm) => xterm.selectAll()
|
||||
});
|
||||
|
||||
registerTerminalAction({
|
||||
@@ -1455,42 +1484,48 @@ export function registerTerminalActions() {
|
||||
|
||||
// Some commands depend on platform features
|
||||
if (BrowserFeatures.clipboard.writeText) {
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.CopySelection,
|
||||
title: { value: localize('workbench.action.terminal.copySelection', "Copy Selection"), original: 'Copy Selection' },
|
||||
// TODO: Why is copy still showing up when text isn't selected?
|
||||
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected),
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.textSelectedInFocused, ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected)),
|
||||
keybinding: [{
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyC,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.KeyC },
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.textSelected, TerminalContextKeys.focus)
|
||||
when: ContextKeyExpr.or(
|
||||
ContextKeyExpr.and(TerminalContextKeys.textSelected, TerminalContextKeys.focus),
|
||||
TerminalContextKeys.textSelectedInFocused,
|
||||
)
|
||||
}],
|
||||
run: (activeInstance) => activeInstance.copySelection()
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.CopyAndClearSelection,
|
||||
title: { value: localize('workbench.action.terminal.copyAndClearSelection', "Copy and Clear Selection"), original: 'Copy and Clear Selection' },
|
||||
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected),
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.textSelectedInFocused, ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected)),
|
||||
keybinding: [{
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.KeyC },
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.textSelected, TerminalContextKeys.focus)
|
||||
when: ContextKeyExpr.or(
|
||||
ContextKeyExpr.and(TerminalContextKeys.textSelected, TerminalContextKeys.focus),
|
||||
TerminalContextKeys.textSelectedInFocused,
|
||||
)
|
||||
}],
|
||||
run: async (activeInstance) => {
|
||||
await activeInstance.copySelection();
|
||||
activeInstance.clearSelection();
|
||||
run: async (xterm) => {
|
||||
await xterm.copySelection();
|
||||
xterm.clearSelection();
|
||||
}
|
||||
});
|
||||
|
||||
registerActiveInstanceAction({
|
||||
registerActiveXtermAction({
|
||||
id: TerminalCommandId.CopySelectionAsHtml,
|
||||
title: { value: localize('workbench.action.terminal.copySelectionAsHtml', "Copy Selection as HTML"), original: 'Copy Selection as HTML' },
|
||||
f1: true,
|
||||
category,
|
||||
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected),
|
||||
run: (activeInstance) => activeInstance.copySelection(true)
|
||||
precondition: ContextKeyExpr.or(TerminalContextKeys.textSelectedInFocused, ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected)),
|
||||
run: (xterm) => xterm.copySelection(true)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ export class TerminalEditor extends EditorPane {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
this._editorInstanceElement = parent;
|
||||
this._overflowGuardElement = dom.$('.terminal-overflow-guard');
|
||||
this._overflowGuardElement = dom.$('.terminal-overflow-guard.terminal-editor');
|
||||
this._editorInstanceElement.appendChild(this._overflowGuardElement);
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
@@ -17,25 +17,28 @@ import { ErrorNoTelemetry, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ISeparator, template } from 'vs/base/common/labels';
|
||||
import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { isMacintosh, isWindows, OperatingSystem, OS } from 'vs/base/common/platform';
|
||||
import { OS, OperatingSystem, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
@@ -45,18 +48,24 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IMarkProperties, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { TerminalCapabilityStoreMultiplexer } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariableShared';
|
||||
import { IProcessDataEvent, IProcessPropertyMap, IReconnectionProperties, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, PosixShellType, ProcessPropertyType, ShellIntegrationStatus, TerminalExitReason, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal';
|
||||
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
|
||||
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
|
||||
import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { IRequestAddInstanceToGroupEvent, ITerminalContribution, ITerminalInstance, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
|
||||
import { TerminalExtensionsRegistry } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { getColorClass, getColorStyleElement, getStandardColors } from 'vs/workbench/contrib/terminal/browser/terminalIcon';
|
||||
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
|
||||
import { showRunRecentQuickPick } from 'vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick';
|
||||
@@ -64,31 +73,21 @@ import { ITerminalStatusList, TerminalStatus, TerminalStatusList } from 'vs/work
|
||||
import { getTerminalResourcesFromDragEvent, getTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri';
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/xterm/lineDataEventAddon';
|
||||
import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { XtermTerminal, getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariableShared';
|
||||
import { getCommandHistory, getDirectoryHistory } from 'vs/workbench/contrib/terminal/common/history';
|
||||
import { DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalProcessManager, ITerminalProfileResolverService, ProcessState, TerminalCommandId, TERMINAL_CREATION_COMMANDS, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalProcessManager, ITerminalProfileResolverService, ProcessState, TERMINAL_CREATION_COMMANDS, TERMINAL_VIEW_ID, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
import { getWorkspaceForTerminal, preparePathForShell } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import type { IMarker, Terminal as XTermTerminal } from 'xterm';
|
||||
import { IAudioCueService, AudioCue } from 'vs/platform/audioCues/browser/audioCueService';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { getWorkspaceForTerminal, preparePathForShell } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget';
|
||||
import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { TerminalExtensionsRegistry } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
|
||||
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
|
||||
import type { IMarker, Terminal as XTermTerminal } from 'xterm';
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
@@ -105,19 +104,6 @@ const enum Constants {
|
||||
}
|
||||
|
||||
let xtermConstructor: Promise<typeof XTermTerminal> | undefined;
|
||||
function getXtermConstructor(keybinding?: ResolvedKeybinding): Promise<typeof XTermTerminal> {
|
||||
if (xtermConstructor) {
|
||||
return xtermConstructor;
|
||||
}
|
||||
xtermConstructor = Promises.withAsyncBody<typeof XTermTerminal>(async (resolve) => {
|
||||
const Terminal = (await import('xterm')).Terminal;
|
||||
// Localize strings
|
||||
Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input');
|
||||
Terminal.strings.tooMuchOutput = keybinding ? nls.localize('terminal.integrated.useAccessibleBuffer', 'Use the accessible buffer {0} to manually review output', keybinding.getLabel()) : nls.localize('terminal.integrated.useAccessibleBufferNoKb', 'Use the Terminal: Focus Accessible Buffer command to manually review output');
|
||||
resolve(Terminal);
|
||||
});
|
||||
return xtermConstructor;
|
||||
}
|
||||
|
||||
interface ICanvasDimensions {
|
||||
width: number;
|
||||
@@ -642,28 +628,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
|
||||
const font = this.xterm ? this.xterm.getFont() : this._configHelper.getFont();
|
||||
if (!font.charWidth || !font.charHeight) {
|
||||
const newRC = getXtermScaledDimensions(font, dimension.width, dimension.height);
|
||||
if (!newRC) {
|
||||
this._setLastKnownColsAndRows();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Because xterm.js converts from CSS pixels to actual pixels through
|
||||
// the use of canvas, window.devicePixelRatio needs to be used here in
|
||||
// order to be precise. font.charWidth/charHeight alone as insufficient
|
||||
// when window.devicePixelRatio changes.
|
||||
const scaledWidthAvailable = dimension.width * window.devicePixelRatio;
|
||||
|
||||
const scaledCharWidth = font.charWidth * window.devicePixelRatio + font.letterSpacing;
|
||||
const newCols = Math.max(Math.floor(scaledWidthAvailable / scaledCharWidth), 1);
|
||||
|
||||
const scaledHeightAvailable = dimension.height * window.devicePixelRatio;
|
||||
const scaledCharHeight = Math.ceil(font.charHeight * window.devicePixelRatio);
|
||||
const scaledLineHeight = Math.floor(scaledCharHeight * font.lineHeight);
|
||||
const newRows = Math.max(Math.floor(scaledHeightAvailable / scaledLineHeight), 1);
|
||||
|
||||
if (this._cols !== newCols || this._rows !== newRows) {
|
||||
this._cols = newCols;
|
||||
this._rows = newRows;
|
||||
if (this._cols !== newRC.cols || this._rows !== newRC.rows) {
|
||||
this._cols = newRC.cols;
|
||||
this._rows = newRC.rows;
|
||||
this._fireMaximumDimensionsChanged();
|
||||
}
|
||||
|
||||
@@ -704,11 +677,26 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
get persistentProcessId(): number | undefined { return this._processManager.persistentProcessId; }
|
||||
get shouldPersist(): boolean { return this._processManager.shouldPersist && !this.shellLaunchConfig.isTransient && (!this.reconnectionProperties || this._configurationService.getValue(TaskSettingId.Reconnection) === true); }
|
||||
|
||||
public static getXtermConstructor(keybindingService: IKeybindingService, contextKeyService: IContextKeyService) {
|
||||
const keybinding = keybindingService.lookupKeybinding(TerminalCommandId.FocusAccessibleBuffer, contextKeyService);
|
||||
if (xtermConstructor) {
|
||||
return xtermConstructor;
|
||||
}
|
||||
xtermConstructor = Promises.withAsyncBody<typeof XTermTerminal>(async (resolve) => {
|
||||
const Terminal = (await import('xterm')).Terminal;
|
||||
// Localize strings
|
||||
Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input');
|
||||
Terminal.strings.tooMuchOutput = keybinding ? nls.localize('terminal.integrated.useAccessibleBuffer', 'Use the accessible buffer {0} to manually review output', keybinding.getLabel()) : nls.localize('terminal.integrated.useAccessibleBufferNoKb', 'Use the Terminal: Focus Accessible Buffer command to manually review output');
|
||||
resolve(Terminal);
|
||||
});
|
||||
return xtermConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create xterm.js instance and attach data listeners.
|
||||
*/
|
||||
protected async _createXterm(): Promise<XtermTerminal> {
|
||||
const Terminal = await getXtermConstructor(this._keybindingService.lookupKeybinding(TerminalCommandId.FocusAccessibleBuffer, this._contextKeyService));
|
||||
const Terminal = await TerminalInstance.getXtermConstructor(this._keybindingService, this._contextKeyService);
|
||||
if (this._isDisposed) {
|
||||
throw new ErrorNoTelemetry('Terminal disposed of during xterm.js creation');
|
||||
}
|
||||
@@ -1072,25 +1060,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
|
||||
async copySelection(asHtml?: boolean, command?: ITerminalCommand): Promise<void> {
|
||||
const xterm = await this._xtermReadyPromise;
|
||||
if (this.hasSelection() || (asHtml && command)) {
|
||||
if (asHtml) {
|
||||
const textAsHtml = await xterm.getSelectionAsHtml(command);
|
||||
function listener(e: any) {
|
||||
if (!e.clipboardData.types.includes('text/plain')) {
|
||||
e.clipboardData.setData('text/plain', command?.getOutput() ?? '');
|
||||
}
|
||||
e.clipboardData.setData('text/html', textAsHtml);
|
||||
e.preventDefault();
|
||||
}
|
||||
document.addEventListener('copy', listener);
|
||||
document.execCommand('copy');
|
||||
document.removeEventListener('copy', listener);
|
||||
} else {
|
||||
await this._clipboardService.writeText(xterm.raw.getSelection());
|
||||
}
|
||||
} else {
|
||||
this._notificationService.warn(nls.localize('terminal.integrated.copySelection.noSelection', 'The terminal has no selection to copy'));
|
||||
}
|
||||
await xterm.copySelection(asHtml, command);
|
||||
}
|
||||
|
||||
get selection(): string | undefined {
|
||||
@@ -1101,12 +1071,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this.xterm?.raw.clearSelection();
|
||||
}
|
||||
|
||||
selectAll(): void {
|
||||
// Focus here to ensure the terminal context key is set
|
||||
this.xterm?.raw.focus();
|
||||
this.xterm?.raw.selectAll();
|
||||
}
|
||||
|
||||
private _refreshAltBufferContextKey() {
|
||||
this._terminalAltBufferActiveContextKey.set(!!(this.xterm && this.xterm.raw.buffer.active === this.xterm.raw.buffer.alternate));
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys';
|
||||
import { IEditableData, IViewsService } from 'vs/workbench/common/views';
|
||||
import { ICreateTerminalOptions, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ICreateTerminalOptions, IDetachedXTermOptions, IDetachedXtermTerminal, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, IXtermTerminal, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { getCwdForSplit } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
|
||||
@@ -47,12 +47,17 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILifecycleService, ShutdownReason, StartupKind, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
|
||||
export class TerminalService implements ITerminalService {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
private _hostActiveTerminals: Map<ITerminalInstanceHost, ITerminalInstance | undefined> = new Map();
|
||||
|
||||
private _detachedXterms = new Set<IXtermTerminal>();
|
||||
private _terminalEditorActive: IContextKey<boolean>;
|
||||
private readonly _terminalShellTypeContextKey: IContextKey<string>;
|
||||
|
||||
@@ -80,6 +85,9 @@ export class TerminalService implements ITerminalService {
|
||||
get instances(): ITerminalInstance[] {
|
||||
return this._terminalGroupService.instances.concat(this._terminalEditorService.instances);
|
||||
}
|
||||
get detachedXterms(): Iterable<IXtermTerminal> {
|
||||
return this._detachedXterms;
|
||||
}
|
||||
|
||||
private _reconnectedTerminals: Map<string, ITerminalInstance[]> = new Map();
|
||||
getReconnectedTerminals(reconnectionOwner: string): ITerminalInstance[] | undefined {
|
||||
@@ -163,7 +171,8 @@ export class TerminalService implements ITerminalService {
|
||||
@IExtensionService private readonly _extensionService: IExtensionService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService
|
||||
) {
|
||||
this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper);
|
||||
// the below avoids having to poll routinely.
|
||||
@@ -971,6 +980,31 @@ export class TerminalService implements ITerminalService {
|
||||
return this._createTerminal(shellLaunchConfig, location, options);
|
||||
}
|
||||
|
||||
async createDetachedXterm(options: IDetachedXTermOptions): Promise<IDetachedXtermTerminal> {
|
||||
const ctor = await TerminalInstance.getXtermConstructor(this._keybindingService, this._contextKeyService);
|
||||
const instance = this._instantiationService.createInstance(
|
||||
XtermTerminal,
|
||||
ctor,
|
||||
this._configHelper,
|
||||
options.cols,
|
||||
options.rows,
|
||||
options.colorProvider,
|
||||
options.capabilities || new TerminalCapabilityStore(),
|
||||
'',
|
||||
undefined,
|
||||
false,
|
||||
);
|
||||
|
||||
if (options.readonly) {
|
||||
instance.raw.attachCustomKeyEventHandler(() => false);
|
||||
}
|
||||
|
||||
this._detachedXterms.add(instance);
|
||||
instance.onDidDispose(() => this._detachedXterms.delete(instance));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private async _resolveCwd(shellLaunchConfig: IShellLaunchConfig, splitActiveTerminal: boolean, options?: ICreateTerminalOptions): Promise<void> {
|
||||
const cwd = shellLaunchConfig.cwd;
|
||||
if (!cwd) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { Unicode11Addon as Unicode11AddonType } from 'xterm-addon-unicode11
|
||||
import type { WebglAddon as WebglAddonType } from 'xterm-addon-webgl';
|
||||
import type { SerializeAddon as SerializeAddonType } from 'xterm-addon-serialize';
|
||||
import type { ImageAddon as ImageAddonType } from 'xterm-addon-image';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
@@ -18,7 +19,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IShellIntegration, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
|
||||
import { ITerminalFont } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { isSafari } from 'vs/base/browser/browser';
|
||||
import { IMarkTracker, IInternalXtermTerminal, IXtermTerminal, ISuggestController, IXtermColorProvider, XtermTerminalConstants } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IMarkTracker, IInternalXtermTerminal, IXtermTerminal, ISuggestController, IXtermColorProvider, XtermTerminalConstants, IXtermAttachToElementOptions, IDetachedXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { TerminalStorageKeys } from 'vs/workbench/contrib/terminal/common/terminalStorageKeys';
|
||||
@@ -35,7 +36,9 @@ import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from '
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { SuggestAddon } from 'vs/workbench/contrib/terminal/browser/xterm/suggestAddon';
|
||||
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
const enum RenderConstants {
|
||||
/**
|
||||
@@ -108,7 +111,7 @@ function getFullBufferLineAsString(lineIndex: number, buffer: IBuffer): { lineDa
|
||||
* Wraps the xterm object with additional functionality. Interaction with the backing process is out
|
||||
* of the scope of this class.
|
||||
*/
|
||||
export class XtermTerminal extends DisposableStore implements IXtermTerminal, IInternalXtermTerminal {
|
||||
export class XtermTerminal extends DisposableStore implements IXtermTerminal, IDetachedXtermTerminal, IInternalXtermTerminal {
|
||||
/** The raw xterm.js instance */
|
||||
readonly raw: RawXtermTerminal;
|
||||
|
||||
@@ -123,14 +126,14 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
}
|
||||
private _core: IXtermCore;
|
||||
private static _suggestedRendererType: 'canvas' | 'dom' | undefined = undefined;
|
||||
private _container?: HTMLElement;
|
||||
private _attached?: { container: HTMLElement; options: IXtermAttachToElementOptions };
|
||||
|
||||
// Always on addons
|
||||
private _markNavigationAddon: MarkNavigationAddon;
|
||||
private _shellIntegrationAddon: ShellIntegrationAddon;
|
||||
|
||||
private _decorationAddon: DecorationAddon;
|
||||
private _suggestAddon: SuggestAddon;
|
||||
private _suggestAddon?: SuggestAddon;
|
||||
|
||||
// Optional addons
|
||||
private _canvasAddon?: CanvasAddonType;
|
||||
@@ -139,6 +142,9 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
private _webglAddon?: WebglAddonType;
|
||||
private _serializeAddon?: SerializeAddonType;
|
||||
private _imageAddon?: ImageAddonType;
|
||||
private readonly _attachedDisposables = this.add(new DisposableStore());
|
||||
private readonly _anyTerminalFocusContextKey: IContextKey<boolean>;
|
||||
private readonly _anyFocusedTerminalHasSelection: IContextKey<boolean>;
|
||||
|
||||
private _lastFindResult: { resultIndex: number; resultCount: number } | undefined;
|
||||
get findResult(): { resultIndex: number; resultCount: number } | undefined { return this._lastFindResult; }
|
||||
@@ -156,6 +162,10 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
readonly onDidChangeFindResults = this._onDidChangeFindResults.event;
|
||||
private readonly _onDidChangeSelection = new Emitter<void>();
|
||||
readonly onDidChangeSelection = this._onDidChangeSelection.event;
|
||||
private readonly _onDidChangeFocus = new Emitter<boolean>();
|
||||
readonly onDidChangeFocus = this._onDidChangeFocus.event;
|
||||
private readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose = this._onDidDispose.event;
|
||||
|
||||
get markTracker(): IMarkTracker { return this._markNavigationAddon; }
|
||||
get shellIntegration(): IShellIntegration { return this._shellIntegrationAddon; }
|
||||
@@ -169,6 +179,10 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
return createImageBitmap(canvas);
|
||||
}
|
||||
|
||||
public get isFocused() {
|
||||
return !!this.raw.element?.contains(document.activeElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param xtermCtor The xterm.js constructor, this is passed in so it can be fetched lazily
|
||||
* outside of this class such that {@link raw} is not nullable.
|
||||
@@ -181,7 +195,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
private readonly _backgroundColorProvider: IXtermColorProvider,
|
||||
private readonly _capabilities: ITerminalCapabilityStore,
|
||||
shellIntegrationNonce: string,
|
||||
private readonly _terminalSuggestWidgetVisibleContextKey: IContextKey<boolean>,
|
||||
private readonly _terminalSuggestWidgetVisibleContextKey: IContextKey<boolean> | undefined,
|
||||
disableShellIntegrationReporting: boolean,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@@ -189,7 +203,9 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IClipboardService private readonly _clipboardService: IClipboardService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super();
|
||||
const font = this._configHelper.getFont(undefined, true);
|
||||
@@ -242,7 +258,12 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
this.add(this._themeService.onDidColorThemeChange(theme => this._updateTheme(theme)));
|
||||
|
||||
// Refire events
|
||||
this.add(this.raw.onSelectionChange(() => this._onDidChangeSelection.fire()));
|
||||
this.add(this.raw.onSelectionChange(() => {
|
||||
this._onDidChangeSelection.fire();
|
||||
if (this.isFocused) {
|
||||
this._anyFocusedTerminalHasSelection.set(this.raw.hasSelection());
|
||||
}
|
||||
}));
|
||||
|
||||
// Load addons
|
||||
this._updateUnicodeVersion();
|
||||
@@ -255,14 +276,29 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon, shellIntegrationNonce, disableShellIntegrationReporting, this._telemetryService);
|
||||
this.raw.loadAddon(this._shellIntegrationAddon);
|
||||
|
||||
this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService);
|
||||
this._anyFocusedTerminalHasSelection = TerminalContextKeys.textSelectedInFocused.bindTo(contextKeyService);
|
||||
|
||||
// Load the suggest addon, this should be loaded regardless of the setting as the sequences
|
||||
// may still come in
|
||||
this._suggestAddon = this._instantiationService.createInstance(SuggestAddon, this._terminalSuggestWidgetVisibleContextKey);
|
||||
this.raw.loadAddon(this._suggestAddon);
|
||||
this._suggestAddon.onAcceptedCompletion(async text => {
|
||||
this._onDidRequestFocus.fire();
|
||||
this._onDidRequestSendText.fire(text);
|
||||
});
|
||||
if (this._terminalSuggestWidgetVisibleContextKey) {
|
||||
this._suggestAddon = this._instantiationService.createInstance(SuggestAddon, this._terminalSuggestWidgetVisibleContextKey);
|
||||
this.raw.loadAddon(this._suggestAddon);
|
||||
this._suggestAddon.onAcceptedCompletion(async text => {
|
||||
this._onDidRequestFocus.fire();
|
||||
this._onDidRequestSendText.fire(text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getContentsAsHtml(): Promise<string> {
|
||||
if (!this._serializeAddon) {
|
||||
const Addon = await this._getSerializeAddonConstructor();
|
||||
this._serializeAddon = new Addon();
|
||||
this.raw.loadAddon(this._serializeAddon);
|
||||
}
|
||||
|
||||
return this._serializeAddon.serializeAsHTML();
|
||||
}
|
||||
|
||||
async getSelectionAsHtml(command?: ITerminalCommand): Promise<string> {
|
||||
@@ -286,22 +322,50 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
return result;
|
||||
}
|
||||
|
||||
attachToElement(container: HTMLElement): HTMLElement {
|
||||
if (!this._container) {
|
||||
attachToElement(container: HTMLElement, partialOptions?: Partial<IXtermAttachToElementOptions>): HTMLElement {
|
||||
const options: IXtermAttachToElementOptions = { enableGpu: true, ...partialOptions };
|
||||
if (!this._attached) {
|
||||
this.raw.open(container);
|
||||
}
|
||||
|
||||
// TODO: Move before open to the DOM renderer doesn't initialize
|
||||
if (this._shouldLoadWebgl()) {
|
||||
this._enableWebglRenderer();
|
||||
} else if (this._shouldLoadCanvas()) {
|
||||
this._enableCanvasRenderer();
|
||||
if (options.enableGpu) {
|
||||
if (this._shouldLoadWebgl()) {
|
||||
this._enableWebglRenderer();
|
||||
} else if (this._shouldLoadCanvas()) {
|
||||
this._enableCanvasRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
this._suggestAddon.setContainer(container);
|
||||
if (!this.raw.element || !this.raw.textarea) {
|
||||
throw new Error('xterm elements not set after open');
|
||||
}
|
||||
|
||||
this._container = container;
|
||||
const ad = this._attachedDisposables;
|
||||
ad.clear();
|
||||
ad.add(dom.addDisposableListener(this.raw.textarea, 'focus', () => this._setFocused(true)));
|
||||
ad.add(dom.addDisposableListener(this.raw.textarea, 'blur', () => this._setFocused(false)));
|
||||
ad.add(dom.addDisposableListener(this.raw.textarea, 'focusout', () => this._setFocused(false)));
|
||||
|
||||
this._suggestAddon?.setContainer(container);
|
||||
|
||||
this._attached = { container, options };
|
||||
// Screen must be created at this point as xterm.open is called
|
||||
return this._container.querySelector('.xterm-screen')!;
|
||||
return this._attached?.container.querySelector('.xterm-screen')!;
|
||||
}
|
||||
|
||||
private _setFocused(isFocused: boolean) {
|
||||
this._onDidChangeFocus.fire(isFocused);
|
||||
this._anyTerminalFocusContextKey.set(isFocused);
|
||||
this._anyFocusedTerminalHasSelection.set(isFocused && this.raw.hasSelection());
|
||||
}
|
||||
|
||||
write(data: string | Uint8Array): void {
|
||||
this.raw.write(data);
|
||||
}
|
||||
|
||||
resize(columns: number, rows: number): void {
|
||||
this.raw.resize(columns, rows);
|
||||
}
|
||||
|
||||
updateConfig(): void {
|
||||
@@ -324,14 +388,16 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
this.raw.options.wordSeparator = config.wordSeparators;
|
||||
this.raw.options.customGlyphs = config.customGlyphs;
|
||||
this.raw.options.smoothScrollDuration = config.smoothScrolling ? RenderConstants.SmoothScrollDuration : 0;
|
||||
if (this._shouldLoadWebgl()) {
|
||||
this._enableWebglRenderer();
|
||||
} else {
|
||||
this._disposeOfWebglRenderer();
|
||||
if (this._shouldLoadCanvas()) {
|
||||
this._enableCanvasRenderer();
|
||||
if (this._attached?.options.enableGpu) {
|
||||
if (this._shouldLoadWebgl()) {
|
||||
this._enableWebglRenderer();
|
||||
} else {
|
||||
this._disposeOfCanvasRenderer();
|
||||
this._disposeOfWebglRenderer();
|
||||
if (this._shouldLoadCanvas()) {
|
||||
this._enableCanvasRenderer();
|
||||
} else {
|
||||
this._disposeOfCanvasRenderer();
|
||||
}
|
||||
}
|
||||
}
|
||||
this._refreshImageAddon();
|
||||
@@ -496,6 +562,45 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
this._capabilities.get(TerminalCapability.CommandDetection)?.handleCommandStart();
|
||||
}
|
||||
|
||||
hasSelection(): boolean {
|
||||
return this.raw.hasSelection();
|
||||
}
|
||||
|
||||
clearSelection(): void {
|
||||
this.raw.clearSelection();
|
||||
}
|
||||
|
||||
selectAll(): void {
|
||||
this.raw.focus();
|
||||
this.raw.selectAll();
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.raw.focus();
|
||||
}
|
||||
|
||||
async copySelection(asHtml?: boolean, command?: ITerminalCommand): Promise<void> {
|
||||
if (this.hasSelection() || (asHtml && command)) {
|
||||
if (asHtml) {
|
||||
const textAsHtml = await this.getSelectionAsHtml(command);
|
||||
function listener(e: any) {
|
||||
if (!e.clipboardData.types.includes('text/plain')) {
|
||||
e.clipboardData.setData('text/plain', command?.getOutput() ?? '');
|
||||
}
|
||||
e.clipboardData.setData('text/html', textAsHtml);
|
||||
e.preventDefault();
|
||||
}
|
||||
document.addEventListener('copy', listener);
|
||||
document.execCommand('copy');
|
||||
document.removeEventListener('copy', listener);
|
||||
} else {
|
||||
await this._clipboardService.writeText(this.raw.getSelection());
|
||||
}
|
||||
} else {
|
||||
this._notificationService.warn(localize('terminal.integrated.copySelection.noSelection', 'The terminal has no selection to copy'));
|
||||
}
|
||||
}
|
||||
|
||||
private _setCursorBlink(blink: boolean): void {
|
||||
if (this.raw.options.cursorBlink !== blink) {
|
||||
this.raw.options.cursorBlink = blink;
|
||||
@@ -765,4 +870,33 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
|
||||
_writeText(data: string): void {
|
||||
this.raw.write(data);
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
this._anyTerminalFocusContextKey.reset();
|
||||
this._anyFocusedTerminalHasSelection.reset();
|
||||
this._onDidDispose.fire();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export function getXtermScaledDimensions(font: ITerminalFont, width: number, height: number) {
|
||||
if (!font.charWidth || !font.charHeight) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Because xterm.js converts from CSS pixels to actual pixels through
|
||||
// the use of canvas, window.devicePixelRatio needs to be used here in
|
||||
// order to be precise. font.charWidth/charHeight alone as insufficient
|
||||
// when window.devicePixelRatio changes.
|
||||
const scaledWidthAvailable = width * window.devicePixelRatio;
|
||||
|
||||
const scaledCharWidth = font.charWidth * window.devicePixelRatio + font.letterSpacing;
|
||||
const cols = Math.max(Math.floor(scaledWidthAvailable / scaledCharWidth), 1);
|
||||
|
||||
const scaledHeightAvailable = height * window.devicePixelRatio;
|
||||
const scaledCharHeight = Math.ceil(font.charHeight * window.devicePixelRatio);
|
||||
const scaledLineHeight = Math.floor(scaledCharHeight * font.lineHeight);
|
||||
const rows = Math.max(Math.floor(scaledHeightAvailable / scaledLineHeight), 1);
|
||||
|
||||
return { rows, cols };
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export const enum TerminalContextKeyStrings {
|
||||
HasFixedWidth = 'terminalHasFixedWidth',
|
||||
ProcessSupported = 'terminalProcessSupported',
|
||||
Focus = 'terminalFocus',
|
||||
FocusInAny = 'terminalFocusInAny',
|
||||
AccessibleBufferFocus = 'terminalAccessibleBufferFocus',
|
||||
EditorFocus = 'terminalEditorFocus',
|
||||
TabsFocus = 'terminalTabsFocus',
|
||||
@@ -26,6 +27,7 @@ export const enum TerminalContextKeyStrings {
|
||||
A11yTreeFocus = 'terminalA11yTreeFocus',
|
||||
ViewShowing = 'terminalViewShowing',
|
||||
TextSelected = 'terminalTextSelected',
|
||||
TextSelectedInFocused = 'terminalTextSelectedInFocused',
|
||||
FindVisible = 'terminalFindVisible',
|
||||
FindInputFocused = 'terminalFindInputFocused',
|
||||
FindFocused = 'terminalFindFocused',
|
||||
@@ -43,6 +45,9 @@ export namespace TerminalContextKeys {
|
||||
/** Whether the terminal is focused. */
|
||||
export const focus = new RawContextKey<boolean>(TerminalContextKeyStrings.Focus, false, localize('terminalFocusContextKey', "Whether the terminal is focused."));
|
||||
|
||||
/** Whether any terminal is focused, including detached terminals used in other UI. */
|
||||
export const focusInAny = new RawContextKey<boolean>(TerminalContextKeyStrings.FocusInAny, false, localize('terminalFocusInAnyContextKey', "Whether any terminal is focused, including detached terminals used in other UI."));
|
||||
|
||||
/** Whether the accessible buffer is focused. */
|
||||
export const accessibleBufferFocus = new RawContextKey<boolean>(TerminalContextKeyStrings.AccessibleBufferFocus, false, localize('terminalAccessibleBufferFocusContextKey', "Whether the terminal accessible buffer is focused."));
|
||||
|
||||
@@ -94,6 +99,9 @@ export namespace TerminalContextKeys {
|
||||
/** Whether text is selected in the active terminal. */
|
||||
export const textSelected = new RawContextKey<boolean>(TerminalContextKeyStrings.TextSelected, false, localize('terminalTextSelectedContextKey', "Whether text is selected in the active terminal."));
|
||||
|
||||
/** Whether text is selected in a focused terminal. `textSelected` counts text selected in an active in a terminal view or an editor, where `textSelectedInFocused` simply counts text in an element with DOM focus. */
|
||||
export const textSelectedInFocused = new RawContextKey<boolean>(TerminalContextKeyStrings.TextSelectedInFocused, false, localize('terminalTextSelectedInFocusedContextKey', "Whether text is selected in a focused terminal."));
|
||||
|
||||
/** Whether text is NOT selected in the active terminal. */
|
||||
export const notTextSelected = textSelected.toNegated();
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestSer
|
||||
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { Color, RGBA } from 'vs/base/common/color';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
class TestWebglAddon implements WebglAddon {
|
||||
static shouldThrow = false;
|
||||
@@ -117,6 +118,7 @@ suite('XtermTerminal', () => {
|
||||
instantiationService.stub(IViewDescriptorService, viewDescriptorService);
|
||||
instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService));
|
||||
instantiationService.stub(ILifecycleService, new TestLifecycleService());
|
||||
instantiationService.stub(IContextKeyService, new MockContextKeyService());
|
||||
|
||||
configHelper = instantiationService.createInstance(TerminalConfigHelper);
|
||||
xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true);
|
||||
@@ -251,7 +253,7 @@ suite('XtermTerminal', () => {
|
||||
|
||||
// Open xterm as otherwise the webgl addon won't activate
|
||||
const container = document.createElement('div');
|
||||
xterm.raw.open(container);
|
||||
xterm.attachToElement(container);
|
||||
|
||||
// Auto should activate the webgl addon
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { ...defaultTerminalConfig, gpuAcceleration: 'auto' } });
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as assert from 'assert';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
@@ -56,6 +57,7 @@ suite('Buffer Content Tracker', () => {
|
||||
instantiationService.stub(ILogService, new NullLogService());
|
||||
instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService));
|
||||
instantiationService.stub(ILifecycleService, new TestLifecycleService());
|
||||
instantiationService.stub(IContextKeyService, new MockContextKeyService());
|
||||
configHelper = instantiationService.createInstance(TerminalConfigHelper);
|
||||
capabilities = new TerminalCapabilityStore();
|
||||
if (!isWindows) {
|
||||
|
||||
@@ -186,8 +186,8 @@
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
.monaco-editor .zone-widget.test-output-peek .test-output-peek-message-container,
|
||||
.monaco-editor .zone-widget.test-output-peek .test-output-peek-tree {
|
||||
.test-output-peek-message-container,
|
||||
.test-output-peek-tree {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import 'vs/css!./testingOutputPeek';
|
||||
import { ICodeEditor, IDiffEditorConstructionOptions, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -44,7 +43,7 @@ import { IEditor, IEditorContribution, ScrollType } from 'vs/editor/common/edito
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer';
|
||||
import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView';
|
||||
import { IPeekViewService, PeekViewWidget, peekViewResultsBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
@@ -62,10 +61,17 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
|
||||
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IDetachedXtermTerminal, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display';
|
||||
import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay';
|
||||
import * as icons from 'vs/workbench/contrib/testing/browser/icons';
|
||||
@@ -719,11 +725,12 @@ class TestResultsViewContent extends Disposable {
|
||||
this.splitView = new SplitView(containerElement, { orientation: Orientation.HORIZONTAL });
|
||||
|
||||
const { historyVisible, showRevealLocationOnMessages } = this.options;
|
||||
const isInPeekView = this.editor !== undefined;
|
||||
const messageContainer = dom.append(containerElement, dom.$('.test-output-peek-message-container'));
|
||||
this.contentProviders = [
|
||||
this._register(this.instantiationService.createInstance(DiffContentProvider, this.editor, messageContainer)),
|
||||
this._register(this.instantiationService.createInstance(MarkdownTestMessagePeek, messageContainer)),
|
||||
this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)),
|
||||
this._register(this.instantiationService.createInstance(PlainTextMessagePeek, messageContainer, isInPeekView)),
|
||||
];
|
||||
|
||||
const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree'));
|
||||
@@ -1139,65 +1146,155 @@ class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
private readonly widget = this._register(new MutableDisposable<CodeEditorWidget>());
|
||||
private readonly model = this._register(new MutableDisposable());
|
||||
private dimension?: dom.IDimension;
|
||||
private dimensions?: dom.IDimension;
|
||||
private readonly terminalCwd = this._register(new MutableObservableValue<string>(''));
|
||||
|
||||
/** Active terminal instance. */
|
||||
private readonly terminal = this._register(new MutableDisposable<IDetachedXtermTerminal>());
|
||||
/** Listener for streaming result data */
|
||||
private readonly outputDataListener = this._register(new MutableDisposable());
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor | undefined,
|
||||
private readonly container: HTMLElement,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITextModelService private readonly modelService: ITextModelService,
|
||||
private readonly isInPeekView: boolean,
|
||||
@ITestResultService private readonly resultService: ITestResultService,
|
||||
@ITerminalService private readonly terminalService: ITerminalService,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
private async makeTerminal() {
|
||||
const prev = this.terminal.value;
|
||||
if (prev) {
|
||||
prev.clearBuffer();
|
||||
prev.clearSearchDecorations();
|
||||
// clearBuffer tries to retain the prompt line, but this doesn't exist for tests.
|
||||
// So clear the screen (J) and move to home (H) to ensure previous data is cleaned up.
|
||||
prev.write(`\x1b[2J\x1b[0;0H`);
|
||||
return prev;
|
||||
}
|
||||
|
||||
const capabilities = new TerminalCapabilityStore();
|
||||
const cwd = this.terminalCwd;
|
||||
capabilities.add(TerminalCapability.CwdDetection, {
|
||||
type: TerminalCapability.CwdDetection,
|
||||
get cwds() { return [cwd.value]; },
|
||||
onDidChangeCwd: cwd.onDidChange,
|
||||
getCwd: () => cwd.value,
|
||||
updateCwd: () => { },
|
||||
});
|
||||
|
||||
return this.terminal.value = await this.terminalService.createDetachedXterm({
|
||||
rows: 10,
|
||||
cols: 80,
|
||||
readonly: true,
|
||||
capabilities,
|
||||
colorProvider: {
|
||||
getBackgroundColor: theme => {
|
||||
const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR);
|
||||
if (terminalBackground) {
|
||||
return terminalBackground;
|
||||
}
|
||||
if (this.isInPeekView) {
|
||||
return theme.getColor(peekViewResultsBackground);
|
||||
}
|
||||
const location = this.viewDescriptorService.getViewLocationById(Testing.ResultsViewId);
|
||||
return location === ViewContainerLocation.Panel
|
||||
? theme.getColor(PANEL_BACKGROUND)
|
||||
: theme.getColor(SIDE_BAR_BACKGROUND);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async update(subject: InspectSubject) {
|
||||
let uri: URI;
|
||||
this.outputDataListener.clear();
|
||||
|
||||
if (subject instanceof MessageSubject) {
|
||||
const message = subject.messages[subject.messageIndex];
|
||||
if (isDiffable(message) || typeof message.message !== 'string') {
|
||||
return this.clear();
|
||||
}
|
||||
uri = subject.messageUri;
|
||||
|
||||
this.updateCwd(subject.test.uri);
|
||||
const terminal = await this.makeTerminal();
|
||||
terminal.write(message.message);
|
||||
this.layoutTerminal(terminal);
|
||||
this.attachTerminalToDom(terminal);
|
||||
} else {
|
||||
uri = subject.outputUri;
|
||||
}
|
||||
|
||||
|
||||
const modelRef = this.model.value = await this.modelService.createModelReference(uri);
|
||||
if (!this.widget.value) {
|
||||
this.widget.value = this.editor ? this.instantiationService.createInstance(
|
||||
EmbeddedCodeEditorWidget,
|
||||
this.container,
|
||||
commonEditorOptions,
|
||||
{},
|
||||
this.editor,
|
||||
) : this.instantiationService.createInstance(
|
||||
CodeEditorWidget,
|
||||
this.container,
|
||||
commonEditorOptions,
|
||||
{ isSimpleWidget: true }
|
||||
);
|
||||
|
||||
if (this.dimension) {
|
||||
this.widget.value.layout(this.dimension);
|
||||
const result = this.resultService.getResult(subject.resultId);
|
||||
const task = result?.tasks[subject.taskIndex];
|
||||
if (!task) {
|
||||
return this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
this.widget.value.setModel(modelRef.object.textEditorModel);
|
||||
this.widget.value.updateOptions(commonEditorOptions);
|
||||
// Update the cwd and use the first test to try to hint at the correct cwd,
|
||||
// but often this will fall back to the first workspace folder.
|
||||
this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri);
|
||||
|
||||
const terminal = await this.makeTerminal();
|
||||
if (result instanceof LiveTestResult) {
|
||||
let hadData = false;
|
||||
for (const buffer of task.output.buffers) {
|
||||
hadData ||= buffer.byteLength > 0;
|
||||
terminal.write(buffer.buffer);
|
||||
}
|
||||
if (!hadData && !task.running) {
|
||||
this.writeNotice(terminal, localize('runNoOutout', 'The test run did not record any output.'));
|
||||
}
|
||||
} else {
|
||||
this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.'));
|
||||
}
|
||||
|
||||
this.attachTerminalToDom(terminal);
|
||||
this.outputDataListener.value = task.output.onDidWriteData(e => terminal.write(e.buffer));
|
||||
}
|
||||
}
|
||||
|
||||
private updateCwd(testUri?: URI) {
|
||||
const wf = (testUri && this.workspaceContext.getWorkspaceFolder(testUri))
|
||||
|| this.workspaceContext.getWorkspace().folders[0];
|
||||
if (wf) {
|
||||
this.terminalCwd.value = wf.uri.fsPath;
|
||||
}
|
||||
}
|
||||
|
||||
private writeNotice(terminal: IDetachedXtermTerminal, str: string) {
|
||||
terminal.write(`\x1b[2m${str}\x1b[0m`);
|
||||
}
|
||||
|
||||
private attachTerminalToDom(terminal: IDetachedXtermTerminal) {
|
||||
terminal.write('\x1b[?25l'); // hide cursor
|
||||
requestAnimationFrame(() => this.layoutTerminal(terminal));
|
||||
terminal.attachToElement(this.container, { enableGpu: false });
|
||||
}
|
||||
|
||||
private clear() {
|
||||
this.model.clear();
|
||||
this.widget.clear();
|
||||
this.outputDataListener.clear();
|
||||
this.terminal.clear();
|
||||
}
|
||||
|
||||
public layout(dimensions: dom.IDimension) {
|
||||
this.dimension = dimensions;
|
||||
this.widget.value?.layout(dimensions);
|
||||
this.dimensions = dimensions;
|
||||
if (this.terminal.value) {
|
||||
this.layoutTerminal(this.terminal.value, dimensions.width, dimensions.height);
|
||||
}
|
||||
}
|
||||
|
||||
private layoutTerminal(
|
||||
xterm: IDetachedXtermTerminal,
|
||||
width = this.dimensions?.width ?? this.container.clientWidth,
|
||||
height = this.dimensions?.height ?? this.container.clientHeight
|
||||
) {
|
||||
width -= 10 + 20; // scrollbar width + margin
|
||||
const scaled = getXtermScaledDimensions(xterm.getFont(), width, height);
|
||||
if (scaled) {
|
||||
xterm.resize(scaled.cols, scaled.rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1515,6 +1612,10 @@ class OutputPeekTree extends Disposable {
|
||||
const resultNode = cc.get(result)! as TestResultElement;
|
||||
const disposable = new DisposableStore();
|
||||
disposable.add(result.onNewTask(() => {
|
||||
if (result.tasks.length === 1) {
|
||||
this.requestReveal.fire(new TaskSubject(result.id, 0)); // reveal the first task in new runs
|
||||
}
|
||||
|
||||
if (this.tree.hasElement(resultNode)) {
|
||||
this.tree.setChildren(resultNode, getResultChildren(result), { diffIdentityProvider });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user