mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 16:50:30 -04:00
Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 (#8911)
* Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 * update distro * fix layering * update distro * fix tests
This commit is contained in:
@@ -8,15 +8,22 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-message-widget {
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
padding: 4px 5px;
|
||||
padding: 4px 8px;
|
||||
animation: fadein 100ms linear;
|
||||
white-space: nowrap;
|
||||
/* Must be drawn on the top of the terminal's canvases */
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-message-widget p {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.monaco-workbench .terminal-message-widget a {
|
||||
color: #3794ff;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,17 @@ configurationRegistry.registerConfiguration({
|
||||
default: DEFAULT_LINE_HEIGHT
|
||||
},
|
||||
'terminal.integrated.minimumContrastRatio': {
|
||||
markdownDescription: nls.localize('terminal.integrated.minimumContrastRatio', "When set the foreground color of each cell will change to try meet the contrast ratio specified. Example values:\n\n- 1: The default, do nothing.\n- 4.5: Minimum for [WCAG AA compliance](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html).\n- 7: Minimum for [WCAG AAA compliance](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html).\n- 21: White on black or black on white."),
|
||||
markdownDescription: nls.localize('terminal.integrated.minimumContrastRatio', "When set the foreground color of each cell will change to try meet the contrast ratio specified. Example values:\n\n- 1: The default, do nothing.\n- 4.5: [WCAG AA compliance (minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html).\n- 7: [WCAG AAA compliance (enhanced)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html).\n- 21: White on black or black on white."),
|
||||
type: 'number',
|
||||
default: 1
|
||||
},
|
||||
'terminal.integrated.fastScrollSensitivity': {
|
||||
markdownDescription: nls.localize('terminal.integrated.fastScrollSensitivity', "Scrolling speed multiplier when pressing `Alt`."),
|
||||
type: 'number',
|
||||
default: 5
|
||||
},
|
||||
'terminal.integrated.mouseWheelScrollSensitivity': {
|
||||
markdownDescription: nls.localize('terminal.integrated.mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaY` of mouse wheel scroll events."),
|
||||
type: 'number',
|
||||
default: 1
|
||||
},
|
||||
@@ -196,6 +206,11 @@ configurationRegistry.registerConfiguration({
|
||||
enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE],
|
||||
default: TerminalCursorStyle.BLOCK
|
||||
},
|
||||
'terminal.integrated.cursorWidth': {
|
||||
markdownDescription: nls.localize('terminal.integrated.cursorWidth', "Controls the width of the cursor when `#terminal.integrated.cursorStyle#` is set to `line`."),
|
||||
type: 'number',
|
||||
default: 1
|
||||
},
|
||||
'terminal.integrated.scrollback': {
|
||||
description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."),
|
||||
type: 'number',
|
||||
|
||||
@@ -39,6 +39,7 @@ import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addon
|
||||
import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/addons/navigationModeAddon';
|
||||
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
// How long in milliseconds should an average frame take to render for a notification to appear
|
||||
// which suggests the fallback DOM-based renderer
|
||||
@@ -286,7 +287,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService
|
||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -478,7 +480,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
fastScrollSensitivity: editorOptions.fastScrollSensitivity,
|
||||
scrollSensitivity: editorOptions.mouseWheelScrollSensitivity,
|
||||
rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType,
|
||||
wordSeparator: ' ()[]{}\',:;"`'
|
||||
wordSeparator: ' ()[]{}\',"`'
|
||||
});
|
||||
this._xterm = xterm;
|
||||
this._xtermCore = (xterm as any)._core as XTermCore;
|
||||
@@ -666,7 +668,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this._refreshSelectionContextKey();
|
||||
}));
|
||||
|
||||
const widgetManager = new TerminalWidgetManager(this._wrapperElement);
|
||||
const widgetManager = new TerminalWidgetManager(this._wrapperElement, this._openerService);
|
||||
this._widgetManager = widgetManager;
|
||||
this._processManager.onProcessReady(() => this._linkHandler?.setWidgetManager(widgetManager));
|
||||
|
||||
@@ -700,7 +702,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
// Discard first frame time as it's normal to take longer
|
||||
frameTimes.shift();
|
||||
|
||||
const medianTime = frameTimes.sort()[Math.floor(frameTimes.length / 2)];
|
||||
const medianTime = frameTimes.sort((a, b) => a - b)[Math.floor(frameTimes.length / 2)];
|
||||
if (medianTime > SLOW_CANVAS_RENDER_THRESHOLD) {
|
||||
const promptChoices: IPromptChoice[] = [
|
||||
{
|
||||
@@ -1227,10 +1229,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
const config = this._configHelper.config;
|
||||
this._setCursorBlink(config.cursorBlinking);
|
||||
this._setCursorStyle(config.cursorStyle);
|
||||
this._setCursorWidth(config.cursorWidth);
|
||||
this._setCommandsToSkipShell(config.commandsToSkipShell);
|
||||
this._setEnableBell(config.enableBell);
|
||||
this._safeSetOption('scrollback', config.scrollback);
|
||||
this._safeSetOption('minimumContrastRatio', config.minimumContrastRatio);
|
||||
this._safeSetOption('fastScrollSensitivity', config.fastScrollSensitivity);
|
||||
this._safeSetOption('scrollSensitivity', config.mouseWheelScrollSensitivity);
|
||||
this._safeSetOption('macOptionIsMeta', config.macOptionIsMeta);
|
||||
this._safeSetOption('macOptionClickForcesSelection', config.macOptionClickForcesSelection);
|
||||
this._safeSetOption('rightClickSelectsWord', config.rightClickBehavior === 'selectWord');
|
||||
@@ -1238,10 +1243,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
// Never set webgl as it's an addon not a rendererType
|
||||
this._safeSetOption('rendererType', config.rendererType === 'auto' ? 'canvas' : config.rendererType);
|
||||
}
|
||||
|
||||
const editorOptions = this._configurationService.getValue<IEditorOptions>('editor');
|
||||
this._safeSetOption('fastScrollSensitivity', editorOptions.fastScrollSensitivity);
|
||||
this._safeSetOption('scrollSensitivity', editorOptions.mouseWheelScrollSensitivity);
|
||||
}
|
||||
|
||||
public updateAccessibilitySupport(): void {
|
||||
@@ -1271,6 +1272,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
}
|
||||
|
||||
private _setCursorWidth(width: number): void {
|
||||
if (this._xterm && this._xterm.getOption('cursorWidth') !== width) {
|
||||
this._xterm.setOption('cursorWidth', width);
|
||||
}
|
||||
}
|
||||
|
||||
private _setCommandsToSkipShell(commands: string[]): void {
|
||||
const excludeCommands = commands.filter(command => command[0] === '-').map(command => command.slice(1));
|
||||
this._skipTerminalCommands = DEFAULT_COMMANDS_TO_SKIP_SHELL.filter(defaultCommand => {
|
||||
@@ -1337,6 +1344,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this._safeSetOption('fontWeight', config.fontWeight);
|
||||
this._safeSetOption('fontWeightBold', config.fontWeightBold);
|
||||
this._safeSetOption('drawBoldTextInBrightColors', config.drawBoldTextInBrightColors);
|
||||
|
||||
// Any of the above setting changes could have changed the dimensions of the
|
||||
// terminal, re-evaluate now.
|
||||
this._initDimensions();
|
||||
cols = this.cols;
|
||||
rows = this.rows;
|
||||
}
|
||||
|
||||
if (isNaN(cols) || isNaN(rows)) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { posix, win32 } from 'vs/base/common/path';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { OperatingSystem, isMacintosh } from 'vs/base/common/platform';
|
||||
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
|
||||
|
||||
const pathPrefix = '(\\.\\.?|\\~)';
|
||||
const pathSeparatorClause = '\\/';
|
||||
@@ -116,7 +117,7 @@ export class TerminalLinkHandler {
|
||||
const leftPosition = location.start.x * (charWidth! + (font.letterSpacing / window.devicePixelRatio));
|
||||
const bottomPosition = offsetRow * (Math.ceil(charHeight! * window.devicePixelRatio) * font.lineHeight) / window.devicePixelRatio;
|
||||
|
||||
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(), verticalAlignment);
|
||||
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(uri), verticalAlignment);
|
||||
} else {
|
||||
const target = (e.target as HTMLElement);
|
||||
const colWidth = target.offsetWidth / this._xterm.cols;
|
||||
@@ -124,7 +125,7 @@ export class TerminalLinkHandler {
|
||||
|
||||
const leftPosition = location.start.x * colWidth;
|
||||
const bottomPosition = offsetRow * rowHeight;
|
||||
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(), verticalAlignment);
|
||||
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(uri), verticalAlignment);
|
||||
}
|
||||
};
|
||||
this._leaveCallback = () => {
|
||||
@@ -277,19 +278,29 @@ export class TerminalLinkHandler {
|
||||
return isMacintosh ? event.metaKey : event.ctrlKey;
|
||||
}
|
||||
|
||||
private _getLinkHoverString(): string {
|
||||
private _getLinkHoverString(uri: string): IMarkdownString {
|
||||
const editorConf = this._configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
|
||||
|
||||
let label = '';
|
||||
if (editorConf.multiCursorModifier === 'ctrlCmd') {
|
||||
if (isMacintosh) {
|
||||
return nls.localize('terminalLinkHandler.followLinkAlt.mac', "Option + click to follow link");
|
||||
label = nls.localize('terminalLinkHandler.followLinkAlt.mac', "Option + click");
|
||||
} else {
|
||||
return nls.localize('terminalLinkHandler.followLinkAlt', "Alt + click to follow link");
|
||||
label = nls.localize('terminalLinkHandler.followLinkAlt', "Alt + click");
|
||||
}
|
||||
} else {
|
||||
if (isMacintosh) {
|
||||
label = nls.localize('terminalLinkHandler.followLinkCmd', "Cmd + click");
|
||||
} else {
|
||||
label = nls.localize('terminalLinkHandler.followLinkCtrl', "Ctrl + click");
|
||||
}
|
||||
}
|
||||
if (isMacintosh) {
|
||||
return nls.localize('terminalLinkHandler.followLinkCmd', "Cmd + click to follow link");
|
||||
}
|
||||
return nls.localize('terminalLinkHandler.followLinkCtrl', "Ctrl + click to follow link");
|
||||
|
||||
const message: IMarkdownString = new MarkdownString(`[Follow Link](${uri}) (${label})`, true);
|
||||
message.uris = {
|
||||
[uri]: URI.parse(uri).toJSON()
|
||||
};
|
||||
return message;
|
||||
}
|
||||
|
||||
private get osPath(): IPath {
|
||||
|
||||
@@ -88,7 +88,7 @@ export class TerminalPanel extends Panel {
|
||||
label: nls.localize('terminal.useMonospace', "Use 'monospace'"),
|
||||
run: () => this._configurationService.updateValue('terminal.integrated.fontFamily', 'monospace'),
|
||||
}];
|
||||
this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts."), choices);
|
||||
this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts. Be sure to restart VS Code if this is a newly installed font."), choices);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -97,18 +97,14 @@ export class TerminalPanel extends Panel {
|
||||
|
||||
this._register(this.onDidChangeVisibility(visible => {
|
||||
if (visible) {
|
||||
if (this._terminalService.terminalInstances.length > 0) {
|
||||
this._updateFont();
|
||||
this._updateTheme();
|
||||
} else {
|
||||
// Check if instances were already restored as part of workbench restore
|
||||
if (this._terminalService.terminalInstances.length === 0) {
|
||||
this._terminalService.createTerminal();
|
||||
}
|
||||
if (this._terminalService.terminalInstances.length > 0) {
|
||||
this._updateFont();
|
||||
this._updateTheme();
|
||||
}
|
||||
const hadTerminals = this._terminalService.terminalInstances.length > 0;
|
||||
if (!hadTerminals) {
|
||||
this._terminalService.createTerminal();
|
||||
}
|
||||
this._updateFont();
|
||||
this._updateTheme();
|
||||
if (hadTerminals) {
|
||||
this._terminalService.getActiveTab()?.setVisible(visible);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
/** The amount of time to consider terminal errors to be related to the launch */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
@@ -194,22 +195,22 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
): Promise<ITerminalChildProcess> {
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
|
||||
const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
|
||||
if (!shellLaunchConfig.executable) {
|
||||
const defaultConfig = await this._terminalInstanceService.getDefaultShellAndArgs(false);
|
||||
shellLaunchConfig.executable = defaultConfig.shell;
|
||||
shellLaunchConfig.args = defaultConfig.args;
|
||||
} else {
|
||||
shellLaunchConfig.executable = this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, shellLaunchConfig.executable);
|
||||
shellLaunchConfig.executable = this._configurationResolverService.resolve(lastActiveWorkspace, shellLaunchConfig.executable);
|
||||
if (shellLaunchConfig.args) {
|
||||
if (Array.isArray(shellLaunchConfig.args)) {
|
||||
const resolvedArgs: string[] = [];
|
||||
for (const arg of shellLaunchConfig.args) {
|
||||
resolvedArgs.push(this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, arg));
|
||||
resolvedArgs.push(this._configurationResolverService.resolve(lastActiveWorkspace, arg));
|
||||
}
|
||||
shellLaunchConfig.args = resolvedArgs;
|
||||
} else {
|
||||
shellLaunchConfig.args = this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, shellLaunchConfig.args);
|
||||
shellLaunchConfig.args = this._configurationResolverService.resolve(lastActiveWorkspace, shellLaunchConfig.args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +218,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||
const initialCwd = terminalEnvironment.getCwd(
|
||||
shellLaunchConfig,
|
||||
this._environmentService.userHome,
|
||||
lastActiveWorkspace ? lastActiveWorkspace : undefined,
|
||||
lastActiveWorkspace,
|
||||
this._configurationResolverService,
|
||||
activeWorkspaceRootUri,
|
||||
this._configHelper.config.cwd,
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export enum WidgetVerticalAlignment {
|
||||
Bottom,
|
||||
@@ -20,7 +24,8 @@ export class TerminalWidgetManager implements IDisposable {
|
||||
private readonly _messageListeners = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
terminalWrapper: HTMLElement
|
||||
terminalWrapper: HTMLElement,
|
||||
private readonly _openerService: IOpenerService
|
||||
) {
|
||||
this._container = document.createElement('div');
|
||||
this._container.classList.add('terminal-widget-overlay');
|
||||
@@ -48,20 +53,22 @@ export class TerminalWidgetManager implements IDisposable {
|
||||
mutationObserver.observe(this._xtermViewport, { attributes: true, attributeFilter: ['style'] });
|
||||
}
|
||||
|
||||
public showMessage(left: number, y: number, text: string, verticalAlignment: WidgetVerticalAlignment = WidgetVerticalAlignment.Bottom): void {
|
||||
public showMessage(left: number, y: number, text: IMarkdownString, verticalAlignment: WidgetVerticalAlignment = WidgetVerticalAlignment.Bottom): void {
|
||||
if (!this._container) {
|
||||
return;
|
||||
}
|
||||
dispose(this._messageWidget);
|
||||
this._messageListeners.clear();
|
||||
this._messageWidget = new MessageWidget(this._container, left, y, text, verticalAlignment);
|
||||
this._messageWidget = new MessageWidget(this._container, left, y, text, verticalAlignment, this._openerService);
|
||||
}
|
||||
|
||||
public closeMessage(): void {
|
||||
this._messageListeners.clear();
|
||||
if (this._messageWidget) {
|
||||
this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget));
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this._messageWidget && !this._messageWidget.mouseOver) {
|
||||
this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget));
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
private _refreshHeight(): void {
|
||||
@@ -73,13 +80,16 @@ export class TerminalWidgetManager implements IDisposable {
|
||||
}
|
||||
|
||||
class MessageWidget {
|
||||
private _domNode: HTMLDivElement;
|
||||
private _domNode: HTMLElement;
|
||||
private _mouseOver = false;
|
||||
private readonly _messageListeners = new DisposableStore();
|
||||
|
||||
public get left(): number { return this._left; }
|
||||
public get y(): number { return this._y; }
|
||||
public get text(): string { return this._text; }
|
||||
public get text(): IMarkdownString { return this._text; }
|
||||
public get domNode(): HTMLElement { return this._domNode; }
|
||||
public get verticalAlignment(): WidgetVerticalAlignment { return this._verticalAlignment; }
|
||||
public get mouseOver(): boolean { return this._mouseOver; }
|
||||
|
||||
public static fadeOut(messageWidget: MessageWidget): IDisposable {
|
||||
let handle: any;
|
||||
@@ -98,10 +108,16 @@ class MessageWidget {
|
||||
private _container: HTMLElement,
|
||||
private _left: number,
|
||||
private _y: number,
|
||||
private _text: string,
|
||||
private _verticalAlignment: WidgetVerticalAlignment
|
||||
private _text: IMarkdownString,
|
||||
private _verticalAlignment: WidgetVerticalAlignment,
|
||||
private readonly _openerService: IOpenerService
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode = renderMarkdown(this._text, {
|
||||
actionHandler: {
|
||||
callback: this._handleLinkClicked.bind(this),
|
||||
disposeables: this._messageListeners
|
||||
}
|
||||
});
|
||||
this._domNode.style.position = 'absolute';
|
||||
this._domNode.style.left = `${_left}px`;
|
||||
|
||||
@@ -114,7 +130,15 @@ class MessageWidget {
|
||||
}
|
||||
|
||||
this._domNode.classList.add('terminal-message-widget', 'fadeIn');
|
||||
this._domNode.textContent = _text;
|
||||
this._domNode.addEventListener('mouseenter', () => {
|
||||
this._mouseOver = true;
|
||||
});
|
||||
|
||||
this._domNode.addEventListener('mouseleave', () => {
|
||||
this._mouseOver = false;
|
||||
this._messageListeners.add(MessageWidget.fadeOut(this));
|
||||
});
|
||||
|
||||
this._container.appendChild(this._domNode);
|
||||
}
|
||||
|
||||
@@ -122,5 +146,11 @@ class MessageWidget {
|
||||
if (this.domNode.parentElement === this._container) {
|
||||
this._container.removeChild(this.domNode);
|
||||
}
|
||||
|
||||
this._messageListeners.dispose();
|
||||
}
|
||||
|
||||
private _handleLinkClicked(content: string) {
|
||||
this._openerService.open(URI.parse(content));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,14 @@ export interface ITerminalConfiguration {
|
||||
rightClickBehavior: 'default' | 'copyPaste' | 'paste' | 'selectWord';
|
||||
cursorBlinking: boolean;
|
||||
cursorStyle: string;
|
||||
cursorWidth: number;
|
||||
drawBoldTextInBrightColors: boolean;
|
||||
fastScrollSensitivity: number;
|
||||
fontFamily: string;
|
||||
fontWeight: FontWeight;
|
||||
fontWeightBold: FontWeight;
|
||||
minimumContrastRatio: number;
|
||||
mouseWheelScrollSensitivity: number;
|
||||
// fontLigatures: boolean;
|
||||
fontSize: number;
|
||||
letterSpacing: number;
|
||||
|
||||
@@ -14,13 +14,16 @@ interface TerminalDataBuffer extends IDisposable {
|
||||
export class TerminalDataBufferer implements IDisposable {
|
||||
private readonly _terminalBufferMap = new Map<number, TerminalDataBuffer>();
|
||||
|
||||
constructor(private readonly _callback: (id: number, data: string) => void) {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (const buffer of this._terminalBufferMap.values()) {
|
||||
buffer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
startBuffering(id: number, event: Event<string>, callback: (id: number, data: string) => void, throttleBy: number = 5): IDisposable {
|
||||
startBuffering(id: number, event: Event<string>, throttleBy: number = 5): IDisposable {
|
||||
let disposable: IDisposable;
|
||||
disposable = event((e: string) => {
|
||||
let buffer = this._terminalBufferMap.get(id);
|
||||
@@ -30,16 +33,13 @@ export class TerminalDataBufferer implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
this._terminalBufferMap.delete(id);
|
||||
callback(id, buffer!.data.join(''));
|
||||
}, throttleBy);
|
||||
const timeoutId = setTimeout(() => this._flushBuffer(id), throttleBy);
|
||||
buffer = {
|
||||
data: [e],
|
||||
timeoutId: timeoutId,
|
||||
dispose: () => {
|
||||
clearTimeout(timeoutId);
|
||||
this._terminalBufferMap.delete(id);
|
||||
this._flushBuffer(id);
|
||||
disposable.dispose();
|
||||
}
|
||||
};
|
||||
@@ -54,4 +54,12 @@ export class TerminalDataBufferer implements IDisposable {
|
||||
buffer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private _flushBuffer(id: number): void {
|
||||
const buffer = this._terminalBufferMap.get(id);
|
||||
if (buffer) {
|
||||
this._terminalBufferMap.delete(id);
|
||||
this._callback(id, buffer.data.join(''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,10 @@ function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnv
|
||||
}
|
||||
}
|
||||
|
||||
function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
|
||||
function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | undefined): ITerminalEnvironment {
|
||||
Object.keys(env).forEach((key) => {
|
||||
const value = env[key];
|
||||
if (typeof value === 'string' && lastActiveWorkspaceRoot !== null) {
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
env[key] = configurationResolverService.resolve(lastActiveWorkspaceRoot, value);
|
||||
} catch (e) {
|
||||
@@ -346,7 +346,7 @@ function getShellSetting(
|
||||
|
||||
export function createTerminalEnvironment(
|
||||
shellLaunchConfig: IShellLaunchConfig,
|
||||
lastActiveWorkspace: IWorkspaceFolder | null,
|
||||
lastActiveWorkspace: IWorkspaceFolder | undefined,
|
||||
envFromConfig: { userValue?: ITerminalEnvironment, value?: ITerminalEnvironment, defaultValue?: ITerminalEnvironment },
|
||||
configurationResolverService: IConfigurationResolverService | undefined,
|
||||
isWorkspaceShellAllowed: boolean,
|
||||
|
||||
@@ -11,20 +11,28 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
suite('Workbench - TerminalDataBufferer', () => {
|
||||
let bufferer: TerminalDataBufferer;
|
||||
let counter: { [id: number]: number };
|
||||
let data: { [id: number]: string };
|
||||
|
||||
setup(async () => {
|
||||
bufferer = new TerminalDataBufferer();
|
||||
counter = {};
|
||||
data = {};
|
||||
bufferer = new TerminalDataBufferer((id, e) => {
|
||||
if (!(id in counter)) {
|
||||
counter[id] = 0;
|
||||
}
|
||||
counter[id]++;
|
||||
if (!(id in data)) {
|
||||
data[id] = '';
|
||||
}
|
||||
data[id] = e;
|
||||
});
|
||||
});
|
||||
|
||||
test('start', async () => {
|
||||
let terminalOnData = new Emitter<string>();
|
||||
let counter = 0;
|
||||
let data: string | undefined;
|
||||
const terminalOnData = new Emitter<string>();
|
||||
|
||||
bufferer.startBuffering(1, terminalOnData.event, (id, e) => {
|
||||
counter++;
|
||||
data = e;
|
||||
}, 0);
|
||||
bufferer.startBuffering(1, terminalOnData.event, 0);
|
||||
|
||||
terminalOnData.fire('1');
|
||||
terminalOnData.fire('2');
|
||||
@@ -34,33 +42,21 @@ suite('Workbench - TerminalDataBufferer', () => {
|
||||
|
||||
terminalOnData.fire('4');
|
||||
|
||||
assert.equal(counter, 1);
|
||||
assert.equal(data, '123');
|
||||
assert.equal(counter[1], 1);
|
||||
assert.equal(data[1], '123');
|
||||
|
||||
await wait(0);
|
||||
|
||||
assert.equal(counter, 2);
|
||||
assert.equal(data, '4');
|
||||
assert.equal(counter[1], 2);
|
||||
assert.equal(data[1], '4');
|
||||
});
|
||||
|
||||
test('start 2', async () => {
|
||||
let terminal1OnData = new Emitter<string>();
|
||||
let terminal1Counter = 0;
|
||||
let terminal1Data: string | undefined;
|
||||
const terminal1OnData = new Emitter<string>();
|
||||
const terminal2OnData = new Emitter<string>();
|
||||
|
||||
bufferer.startBuffering(1, terminal1OnData.event, (id, e) => {
|
||||
terminal1Counter++;
|
||||
terminal1Data = e;
|
||||
}, 0);
|
||||
|
||||
let terminal2OnData = new Emitter<string>();
|
||||
let terminal2Counter = 0;
|
||||
let terminal2Data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(2, terminal2OnData.event, (id, e) => {
|
||||
terminal2Counter++;
|
||||
terminal2Data = e;
|
||||
}, 0);
|
||||
bufferer.startBuffering(1, terminal1OnData.event, 0);
|
||||
bufferer.startBuffering(2, terminal2OnData.event, 0);
|
||||
|
||||
terminal1OnData.fire('1');
|
||||
terminal2OnData.fire('4');
|
||||
@@ -70,60 +66,41 @@ suite('Workbench - TerminalDataBufferer', () => {
|
||||
terminal2OnData.fire('6');
|
||||
terminal2OnData.fire('7');
|
||||
|
||||
assert.equal(terminal1Counter, 0);
|
||||
assert.equal(terminal1Data, undefined);
|
||||
assert.equal(terminal2Counter, 0);
|
||||
assert.equal(terminal2Data, undefined);
|
||||
assert.equal(counter[1], undefined);
|
||||
assert.equal(data[1], undefined);
|
||||
assert.equal(counter[2], undefined);
|
||||
assert.equal(data[2], undefined);
|
||||
|
||||
await wait(0);
|
||||
|
||||
assert.equal(terminal1Counter, 1);
|
||||
assert.equal(terminal1Data, '123');
|
||||
assert.equal(terminal2Counter, 1);
|
||||
assert.equal(terminal2Data, '4567');
|
||||
assert.equal(counter[1], 1);
|
||||
assert.equal(data[1], '123');
|
||||
assert.equal(counter[2], 1);
|
||||
assert.equal(data[2], '4567');
|
||||
});
|
||||
|
||||
test('stop', async () => {
|
||||
let terminalOnData = new Emitter<string>();
|
||||
let counter = 0;
|
||||
let data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(1, terminalOnData.event, (id, e) => {
|
||||
counter++;
|
||||
data = e;
|
||||
}, 0);
|
||||
bufferer.startBuffering(1, terminalOnData.event, 0);
|
||||
|
||||
terminalOnData.fire('1');
|
||||
terminalOnData.fire('2');
|
||||
terminalOnData.fire('3');
|
||||
|
||||
bufferer.stopBuffering(1);
|
||||
|
||||
await wait(0);
|
||||
|
||||
assert.equal(counter, 0);
|
||||
assert.equal(data, undefined);
|
||||
assert.equal(counter[1], 1);
|
||||
assert.equal(data[1], '123');
|
||||
});
|
||||
|
||||
test('start 2 stop 1', async () => {
|
||||
let terminal1OnData = new Emitter<string>();
|
||||
let terminal1Counter = 0;
|
||||
let terminal1Data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(1, terminal1OnData.event, (id, e) => {
|
||||
terminal1Counter++;
|
||||
terminal1Data = e;
|
||||
}, 0);
|
||||
|
||||
let terminal2OnData = new Emitter<string>();
|
||||
let terminal2Counter = 0;
|
||||
let terminal2Data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(2, terminal2OnData.event, (id, e) => {
|
||||
terminal2Counter++;
|
||||
terminal2Data = e;
|
||||
}, 0);
|
||||
const terminal1OnData = new Emitter<string>();
|
||||
const terminal2OnData = new Emitter<string>();
|
||||
|
||||
bufferer.startBuffering(1, terminal1OnData.event, 0);
|
||||
bufferer.startBuffering(2, terminal2OnData.event, 0);
|
||||
|
||||
terminal1OnData.fire('1');
|
||||
terminal2OnData.fire('4');
|
||||
@@ -133,39 +110,26 @@ suite('Workbench - TerminalDataBufferer', () => {
|
||||
terminal2OnData.fire('6');
|
||||
terminal2OnData.fire('7');
|
||||
|
||||
assert.equal(terminal1Counter, 0);
|
||||
assert.equal(terminal1Data, undefined);
|
||||
assert.equal(terminal2Counter, 0);
|
||||
assert.equal(terminal2Data, undefined);
|
||||
assert.equal(counter[1], undefined);
|
||||
assert.equal(data[1], undefined);
|
||||
assert.equal(counter[2], undefined);
|
||||
assert.equal(data[2], undefined);
|
||||
|
||||
bufferer.stopBuffering(1);
|
||||
await wait(0);
|
||||
|
||||
assert.equal(terminal1Counter, 0);
|
||||
assert.equal(terminal1Data, undefined);
|
||||
assert.equal(terminal2Counter, 1);
|
||||
assert.equal(terminal2Data, '4567');
|
||||
assert.equal(counter[1], 1);
|
||||
assert.equal(data[1], '123');
|
||||
assert.equal(counter[2], 1);
|
||||
assert.equal(data[2], '4567');
|
||||
});
|
||||
|
||||
test('dispose', async () => {
|
||||
let terminal1OnData = new Emitter<string>();
|
||||
let terminal1Counter = 0;
|
||||
let terminal1Data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(1, terminal1OnData.event, (id, e) => {
|
||||
terminal1Counter++;
|
||||
terminal1Data = e;
|
||||
}, 0);
|
||||
|
||||
let terminal2OnData = new Emitter<string>();
|
||||
let terminal2Counter = 0;
|
||||
let terminal2Data: string | undefined;
|
||||
|
||||
bufferer.startBuffering(2, terminal2OnData.event, (id, e) => {
|
||||
terminal2Counter++;
|
||||
terminal2Data = e;
|
||||
}, 0);
|
||||
test('dispose should flush remaining data events', async () => {
|
||||
const terminal1OnData = new Emitter<string>();
|
||||
const terminal2OnData = new Emitter<string>();
|
||||
|
||||
bufferer.startBuffering(1, terminal1OnData.event, 0);
|
||||
bufferer.startBuffering(2, terminal2OnData.event, 0);
|
||||
|
||||
terminal1OnData.fire('1');
|
||||
terminal2OnData.fire('4');
|
||||
@@ -175,17 +139,17 @@ suite('Workbench - TerminalDataBufferer', () => {
|
||||
terminal2OnData.fire('6');
|
||||
terminal2OnData.fire('7');
|
||||
|
||||
assert.equal(terminal1Counter, 0);
|
||||
assert.equal(terminal1Data, undefined);
|
||||
assert.equal(terminal2Counter, 0);
|
||||
assert.equal(terminal2Data, undefined);
|
||||
assert.equal(counter[1], undefined);
|
||||
assert.equal(data[1], undefined);
|
||||
assert.equal(counter[2], undefined);
|
||||
assert.equal(data[2], undefined);
|
||||
|
||||
bufferer.dispose();
|
||||
await wait(0);
|
||||
|
||||
assert.equal(terminal1Counter, 0);
|
||||
assert.equal(terminal1Data, undefined);
|
||||
assert.equal(terminal2Counter, 0);
|
||||
assert.equal(terminal2Data, undefined);
|
||||
assert.equal(counter[1], 1);
|
||||
assert.equal(data[1], '123');
|
||||
assert.equal(counter[2], 1);
|
||||
assert.equal(data[2], '4567');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user