mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from vscode fcf3346a8e9f5ee1e00674461d9e2c2292a14ee3 (#12295)
* Merge from vscode fcf3346a8e9f5ee1e00674461d9e2c2292a14ee3 * Fix test build break * Update distro * Fix build errors * Update distro * Update REH build file * Update build task names for REL * Fix product build yaml * Fix product REH task name * Fix type in task name * Update linux build step * Update windows build tasks * Turn off server publish * Disable REH * Fix typo * Bump distro * Update vscode tests * Bump distro * Fix type in disto * Bump distro * Turn off docker build * Remove docker step from release Co-authored-by: ADS Merger <andresse@microsoft.com> Co-authored-by: Karl Burtram <karlb@microsoft.com>
This commit is contained in:
@@ -65,7 +65,7 @@ export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvide
|
||||
}, startLine);
|
||||
const matchingText = lineContent.substr(link.startIndex, link.length) || '';
|
||||
const activateLink = this._wrapLinkHandler((_, text) => link.activate(text));
|
||||
return this._instantiationService.createInstance(TerminalLink, bufferRange, matchingText, this._xterm.buffer.active.viewportY, activateLink, this._tooltipCallback, true, link.label);
|
||||
return this._instantiationService.createInstance(TerminalLink, this._xterm, bufferRange, matchingText, this._xterm.buffer.active.viewportY, activateLink, this._tooltipCallback, true, link.label);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { IViewportRange, IBufferRange, ILink, ILinkDecorations } from 'xterm';
|
||||
import type { IViewportRange, IBufferRange, ILink, ILinkDecorations, Terminal } from 'xterm';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
@@ -23,10 +23,11 @@ export class TerminalLink extends DisposableStore implements ILink {
|
||||
private _tooltipScheduler: RunOnceScheduler | undefined;
|
||||
private _hoverListeners: DisposableStore | undefined;
|
||||
|
||||
private readonly _onLeave = new Emitter<void>();
|
||||
public get onLeave(): Event<void> { return this._onLeave.event; }
|
||||
private readonly _onInvalidated = new Emitter<void>();
|
||||
public get onInvalidated(): Event<void> { return this._onInvalidated.event; }
|
||||
|
||||
constructor(
|
||||
private readonly _xterm: Terminal,
|
||||
public readonly range: IBufferRange,
|
||||
public readonly text: string,
|
||||
private readonly _viewportY: number,
|
||||
@@ -57,17 +58,26 @@ export class TerminalLink extends DisposableStore implements ILink {
|
||||
|
||||
hover(event: MouseEvent, text: string): void {
|
||||
// Listen for modifier before handing it off to the hover to handle so it gets disposed correctly
|
||||
this.add(dom.addDisposableListener(document, 'keydown', e => {
|
||||
if (this._isModifierDown(e)) {
|
||||
this._hoverListeners = new DisposableStore();
|
||||
this._hoverListeners.add(dom.addDisposableListener(document, 'keydown', e => {
|
||||
if (!e.repeat && this._isModifierDown(e)) {
|
||||
this._enableDecorations();
|
||||
}
|
||||
}));
|
||||
this.add(dom.addDisposableListener(document, 'keyup', e => {
|
||||
if (!this._isModifierDown(e)) {
|
||||
this._hoverListeners.add(dom.addDisposableListener(document, 'keyup', e => {
|
||||
if (!e.repeat && !this._isModifierDown(e)) {
|
||||
this._disableDecorations();
|
||||
}
|
||||
}));
|
||||
|
||||
// Listen for when the terminal renders on the same line as the link
|
||||
this._hoverListeners.add(this._xterm.onRender(e => {
|
||||
const viewportRangeY = this.range.start.y - this._viewportY;
|
||||
if (viewportRangeY >= e.start && viewportRangeY <= e.end) {
|
||||
this._onInvalidated.fire();
|
||||
}
|
||||
}));
|
||||
|
||||
// Only show the tooltip and highlight for high confidence links (not word/search workspace
|
||||
// links). Feedback was that this makes using the terminal overly noisy.
|
||||
if (this._isHighConfidenceLink) {
|
||||
@@ -88,7 +98,6 @@ export class TerminalLink extends DisposableStore implements ILink {
|
||||
}
|
||||
|
||||
const origin = { x: event.pageX, y: event.pageY };
|
||||
this._hoverListeners = new DisposableStore();
|
||||
this._hoverListeners.add(dom.addDisposableListener(document, dom.EventType.MOUSE_MOVE, e => {
|
||||
// Update decorations
|
||||
if (this._isModifierDown(e)) {
|
||||
@@ -111,7 +120,6 @@ export class TerminalLink extends DisposableStore implements ILink {
|
||||
this._hoverListeners = undefined;
|
||||
this._tooltipScheduler?.dispose();
|
||||
this._tooltipScheduler = undefined;
|
||||
this._onLeave.fire();
|
||||
}
|
||||
|
||||
private _enableDecorations(): void {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ITextEditorSelection } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import type { Terminal, IViewportRange, ILinkProvider } from 'xterm';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { posix, win32 } from 'vs/base/common/path';
|
||||
import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { OperatingSystem, isMacintosh, OS } from 'vs/base/common/platform';
|
||||
@@ -116,7 +116,7 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
const widget = this._instantiationService.createInstance(TerminalHover, targetOptions, text, linkHandler);
|
||||
const attached = this._widgetManager.attachWidget(widget);
|
||||
if (attached) {
|
||||
link?.onLeave(() => attached.dispose());
|
||||
link?.onInvalidated(() => attached.dispose());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +296,7 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
let uri: URI;
|
||||
if (this._processManager.remoteAuthority) {
|
||||
uri = URI.from({
|
||||
scheme: REMOTE_HOST_SCHEME,
|
||||
scheme: Schemas.vscodeRemote,
|
||||
authority: this._processManager.remoteAuthority,
|
||||
path: linkUrl
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ export class TerminalProtocolLinkProvider extends TerminalBaseLinkProvider {
|
||||
? (typeof link.url === 'string' ? URI.parse(link.url) : link.url)
|
||||
: undefined;
|
||||
const label = (uri?.scheme === 'file') ? OPEN_FILE_LABEL : undefined;
|
||||
return this._instantiationService.createInstance(TerminalLink, range, link.url?.toString() || '', this._xterm.buffer.active.viewportY, this._activateCallback, this._tooltipCallback, true, label);
|
||||
return this._instantiationService.createInstance(TerminalLink, this._xterm, range, link.url?.toString() || '', this._xterm.buffer.active.viewportY, this._activateCallback, this._tooltipCallback, true, label);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ const pathPrefix = '(\\.\\.?|\\~)';
|
||||
const pathSeparatorClause = '\\/';
|
||||
// '":; are allowed in paths but they are often separators so ignore them
|
||||
// Also disallow \\ to prevent a catastropic backtracking case #24798
|
||||
const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]+\'":;\\\\]';
|
||||
const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]\'":;\\\\]';
|
||||
/** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */
|
||||
export const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)';
|
||||
|
||||
export const winDrivePrefix = '(?:\\\\\\\\\\?\\\\)?[a-zA-Z]:';
|
||||
const winPathPrefix = '(' + winDrivePrefix + '|\\.\\.?|\\~)';
|
||||
const winPathSeparatorClause = '(\\\\|\\/)';
|
||||
const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]+\'":;]';
|
||||
const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]\'":;]';
|
||||
/** A regex that matches paths in the form \\?\c:\foo c:\foo, ~\foo, .\foo, ..\foo, foo\bar */
|
||||
export const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)';
|
||||
|
||||
@@ -144,7 +144,7 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider
|
||||
this._activateFileCallback(event, text);
|
||||
}
|
||||
});
|
||||
r(this._instantiationService.createInstance(TerminalLink, bufferRange, link, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, true, label));
|
||||
r(this._instantiationService.createInstance(TerminalLink, this._xterm, bufferRange, link, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, true, label));
|
||||
} else {
|
||||
r(undefined);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider {
|
||||
// Add a link if this is a separator
|
||||
if (width !== 0 && wordSeparators.indexOf(chars) >= 0) {
|
||||
if (startX !== -1) {
|
||||
result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService));
|
||||
result.push(this._createTerminalLink(startX, x, y, text, activateCallback));
|
||||
text = '';
|
||||
startX = -1;
|
||||
}
|
||||
@@ -70,12 +70,30 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider {
|
||||
|
||||
// Add the final link if there is one
|
||||
if (startX !== -1) {
|
||||
result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: line.length, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService));
|
||||
result.push(this._createTerminalLink(startX, line.length, y, text, activateCallback));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _createTerminalLink(startX: number, endX: number, y: number, text: string, activateCallback: XtermLinkMatcherHandler): TerminalLink {
|
||||
// Remove trailing colon if there is one so the link is more useful
|
||||
if (text.length > 0 && text.charAt(text.length - 1) === ':') {
|
||||
text = text.slice(0, -1);
|
||||
endX--;
|
||||
}
|
||||
return this._instantiationService.createInstance(TerminalLink,
|
||||
this._xterm,
|
||||
{ start: { x: startX + 1, y }, end: { x: endX, y } },
|
||||
text,
|
||||
this._xterm.buffer.active.viewportY,
|
||||
activateCallback,
|
||||
this._tooltipCallback,
|
||||
false,
|
||||
localize('searchWorkspace', 'Search workspace')
|
||||
);
|
||||
}
|
||||
|
||||
private async _activate(link: string) {
|
||||
const results = await this._searchService.fileSearch(
|
||||
this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, {
|
||||
|
||||
@@ -652,7 +652,7 @@ export function registerTerminalActions() {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
|
||||
const instance = terminalService.getActiveOrCreateInstance();
|
||||
let editor = codeEditorService.getFocusedCodeEditor();
|
||||
let editor = codeEditorService.getActiveCodeEditor();
|
||||
if (!editor || !editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_FONT_WEIGHT, DEFAULT_BOLD_FONT_WEIGHT, FontWeight } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification';
|
||||
import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
@@ -67,7 +67,11 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
}
|
||||
|
||||
private _updateConfig(): void {
|
||||
this.config = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION);
|
||||
const configValues = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION);
|
||||
configValues.fontWeight = this._normalizeFontWeight(configValues.fontWeight, DEFAULT_FONT_WEIGHT);
|
||||
configValues.fontWeightBold = this._normalizeFontWeight(configValues.fontWeightBold, DEFAULT_BOLD_FONT_WEIGHT);
|
||||
|
||||
this.config = configValues;
|
||||
}
|
||||
|
||||
public configFontIsMonospace(): boolean {
|
||||
@@ -157,7 +161,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
const editorConfig = this._configurationService.getValue<IEditorOptions>('editor');
|
||||
|
||||
let fontFamily = this.config.fontFamily || editorConfig.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
|
||||
let fontSize = this._toInteger(this.config.fontSize, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize);
|
||||
let fontSize = this._clampInt(this.config.fontSize, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize);
|
||||
|
||||
// Work around bad font on Fedora/Ubuntu
|
||||
if (!this.config.fontFamily) {
|
||||
@@ -168,7 +172,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
fontFamily = '\'Ubuntu Mono\', monospace';
|
||||
|
||||
// Ubuntu mono is somehow smaller, so set fontSize a bit larger to get the same perceived size.
|
||||
fontSize = this._toInteger(fontSize + 2, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize);
|
||||
fontSize = this._clampInt(fontSize + 2, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +275,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
return !!isWorkspaceShellAllowed;
|
||||
}
|
||||
|
||||
private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number {
|
||||
private _clampInt<T>(source: any, minimum: number, maximum: number, fallback: T): number | T {
|
||||
let r = parseInt(source, 10);
|
||||
if (isNaN(r)) {
|
||||
return fallback;
|
||||
@@ -340,4 +344,11 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
const extensions = await this._extensionManagementService.getInstalled(ExtensionType.User);
|
||||
return extensions.some(e => e.identifier.id === id);
|
||||
}
|
||||
|
||||
private _normalizeFontWeight(input: any, defaultWeight: FontWeight): FontWeight {
|
||||
if (input === 'normal' || input === 'bold') {
|
||||
return input;
|
||||
}
|
||||
return this._clampInt(input, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, defaultWeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/te
|
||||
import type { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm';
|
||||
import type { SearchAddon, ISearchOptions } from 'xterm-addon-search';
|
||||
import type { Unicode11Addon } from 'xterm-addon-unicode11';
|
||||
import type { WebglAddon } from 'xterm-addon-webgl';
|
||||
import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon';
|
||||
import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/addons/navigationModeAddon';
|
||||
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
@@ -105,6 +106,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
private _widgetManager: TerminalWidgetManager = this._instantiationService.createInstance(TerminalWidgetManager);
|
||||
private _linkManager: TerminalLinkManager | undefined;
|
||||
private _environmentInfo: { widget: EnvironmentVariableInfoWidget, disposable: IDisposable } | undefined;
|
||||
private _webglAddon: WebglAddon | undefined;
|
||||
private _commandTrackerAddon: CommandTrackerAddon | undefined;
|
||||
private _navigationModeAddon: INavigationMode & ITerminalAddon | undefined;
|
||||
|
||||
@@ -496,9 +498,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this._container.appendChild(this._wrapperElement);
|
||||
xterm.open(this._xtermElement);
|
||||
if (this._configHelper.config.rendererType === 'experimentalWebgl') {
|
||||
this._terminalInstanceService.getXtermWebglConstructor().then(Addon => {
|
||||
xterm.loadAddon(new Addon());
|
||||
});
|
||||
this._enableWebglRenderer();
|
||||
}
|
||||
|
||||
if (!xterm.element || !xterm.textarea) {
|
||||
@@ -767,7 +767,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
if (!this._xterm) {
|
||||
return;
|
||||
}
|
||||
this._xterm.refresh(0, this._xterm.rows - 1);
|
||||
this._webglAddon?.clearTextureAtlas();
|
||||
// TODO: Do canvas renderer too?
|
||||
}
|
||||
|
||||
public focus(force?: boolean): void {
|
||||
@@ -1227,13 +1228,26 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this._safeSetOption('macOptionClickForcesSelection', config.macOptionClickForcesSelection);
|
||||
this._safeSetOption('rightClickSelectsWord', config.rightClickBehavior === 'selectWord');
|
||||
this._safeSetOption('wordSeparator', config.wordSeparators);
|
||||
if (config.rendererType !== 'experimentalWebgl') {
|
||||
if (config.rendererType === 'experimentalWebgl') {
|
||||
this._enableWebglRenderer();
|
||||
} else {
|
||||
this._webglAddon?.dispose();
|
||||
this._webglAddon = undefined;
|
||||
// Never set webgl as it's an addon not a rendererType
|
||||
this._safeSetOption('rendererType', config.rendererType === 'auto' ? 'canvas' : config.rendererType);
|
||||
}
|
||||
this._refreshEnvironmentVariableInfoWidgetState(this._processManager.environmentVariableInfo);
|
||||
}
|
||||
|
||||
private async _enableWebglRenderer(): Promise<void> {
|
||||
if (!this._xterm || this._webglAddon) {
|
||||
return;
|
||||
}
|
||||
const Addon = await this._terminalInstanceService.getXtermWebglConstructor();
|
||||
this._webglAddon = new Addon();
|
||||
this._xterm.loadAddon(this._webglAddon);
|
||||
}
|
||||
|
||||
private async _updateUnicodeVersion(): Promise<void> {
|
||||
if (!this._xterm) {
|
||||
throw new Error('Cannot update unicode version before xterm has been initialized');
|
||||
|
||||
@@ -43,9 +43,9 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
|
||||
private readonly _onRequestLatency = this._register(new Emitter<void>());
|
||||
public readonly onRequestLatency: Event<void> = this._onRequestLatency.event;
|
||||
|
||||
private _pendingInitialCwdRequests: ((value?: string | Thenable<string>) => void)[] = [];
|
||||
private _pendingCwdRequests: ((value?: string | Thenable<string>) => void)[] = [];
|
||||
private _pendingLatencyRequests: ((value?: number | Thenable<number>) => void)[] = [];
|
||||
private _pendingInitialCwdRequests: ((value: string | PromiseLike<string>) => void)[] = [];
|
||||
private _pendingCwdRequests: ((value: string | PromiseLike<string>) => void)[] = [];
|
||||
private _pendingLatencyRequests: ((value: number | PromiseLike<number>) => void)[] = [];
|
||||
|
||||
constructor(
|
||||
public terminalId: number,
|
||||
|
||||
@@ -25,7 +25,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { isWindows, isMacintosh, OperatingSystem, isWeb } from 'vs/base/common/platform';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -232,7 +231,9 @@ export class TerminalService implements ITerminalService {
|
||||
private _removeTab(tab: ITerminalTab): void {
|
||||
// Get the index of the tab and remove it from the list
|
||||
const index = this._terminalTabs.indexOf(tab);
|
||||
const wasActiveTab = tab === this.getActiveTab();
|
||||
const activeTab = this.getActiveTab();
|
||||
const activeTabIndex = activeTab ? this._terminalTabs.indexOf(activeTab) : -1;
|
||||
const wasActiveTab = tab === activeTab;
|
||||
if (index !== -1) {
|
||||
this._terminalTabs.splice(index, 1);
|
||||
}
|
||||
@@ -247,6 +248,9 @@ export class TerminalService implements ITerminalService {
|
||||
if (activeInstance) {
|
||||
activeInstance.focus(true);
|
||||
}
|
||||
} else if (activeTabIndex >= this._terminalTabs.length) {
|
||||
const newIndex = this._terminalTabs.length - 1;
|
||||
this.setActiveTabByIndex(newIndex);
|
||||
}
|
||||
|
||||
// Hide the panel if there are no more instances, provided that VS Code is not shutting
|
||||
@@ -454,7 +458,7 @@ export class TerminalService implements ITerminalService {
|
||||
}
|
||||
|
||||
private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined {
|
||||
return find(this._terminalTabs, tab => tab.terminalInstances.indexOf(instance) !== -1);
|
||||
return this._terminalTabs.find(tab => tab.terminalInstances.indexOf(instance) !== -1);
|
||||
}
|
||||
|
||||
public async showPanel(focus?: boolean): Promise<void> {
|
||||
|
||||
@@ -41,13 +41,16 @@ export class TerminalHover extends Disposable implements ITerminalWidget {
|
||||
|
||||
attach(container: HTMLElement): void {
|
||||
const target = new CellHoverTarget(container, this._targetOptions);
|
||||
this._hoverService.showHover({
|
||||
const hover = this._hoverService.showHover({
|
||||
target,
|
||||
text: this._text,
|
||||
linkHandler: this._linkHandler,
|
||||
// .xterm-hover lets xterm know that the hover is part of a link
|
||||
additionalClasses: ['xterm-hover']
|
||||
});
|
||||
if (hover) {
|
||||
this._register(hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,13 @@ export const DEFAULT_LETTER_SPACING = 0;
|
||||
export const MINIMUM_LETTER_SPACING = -5;
|
||||
export const DEFAULT_LINE_HEIGHT = 1;
|
||||
|
||||
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
export const MINIMUM_FONT_WEIGHT = 1;
|
||||
export const MAXIMUM_FONT_WEIGHT = 1000;
|
||||
export const DEFAULT_FONT_WEIGHT = 'normal';
|
||||
export const DEFAULT_BOLD_FONT_WEIGHT = 'bold';
|
||||
export const SUGGESTIONS_FONT_WEIGHT = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
|
||||
|
||||
export type FontWeight = 'normal' | 'bold' | number;
|
||||
|
||||
export interface ITerminalConfiguration {
|
||||
shell: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform';
|
||||
|
||||
export const terminalConfiguration: IConfigurationNode = {
|
||||
@@ -136,15 +136,41 @@ export const terminalConfiguration: IConfigurationNode = {
|
||||
default: 1
|
||||
},
|
||||
'terminal.integrated.fontWeight': {
|
||||
type: 'string',
|
||||
enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
description: localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."),
|
||||
'anyOf': [
|
||||
{
|
||||
type: 'number',
|
||||
minimum: MINIMUM_FONT_WEIGHT,
|
||||
maximum: MAXIMUM_FONT_WEIGHT,
|
||||
errorMessage: localize('terminal.integrated.fontWeightError', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.")
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$'
|
||||
},
|
||||
{
|
||||
enum: SUGGESTIONS_FONT_WEIGHT,
|
||||
}
|
||||
],
|
||||
description: localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."),
|
||||
default: 'normal'
|
||||
},
|
||||
'terminal.integrated.fontWeightBold': {
|
||||
type: 'string',
|
||||
enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
description: localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."),
|
||||
'anyOf': [
|
||||
{
|
||||
type: 'number',
|
||||
minimum: MINIMUM_FONT_WEIGHT,
|
||||
maximum: MAXIMUM_FONT_WEIGHT,
|
||||
errorMessage: localize('terminal.integrated.fontWeightError', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.")
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$'
|
||||
},
|
||||
{
|
||||
enum: SUGGESTIONS_FONT_WEIGHT,
|
||||
}
|
||||
],
|
||||
description: localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."),
|
||||
default: 'bold'
|
||||
},
|
||||
'terminal.integrated.cursorBlinking': {
|
||||
|
||||
@@ -46,11 +46,7 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench
|
||||
}
|
||||
|
||||
private _onOsResume(): void {
|
||||
const activeTab = this._terminalService.getActiveTab();
|
||||
if (!activeTab) {
|
||||
return;
|
||||
}
|
||||
activeTab.terminalInstances.forEach(instance => instance.forceRedraw());
|
||||
this._terminalService.terminalInstances.forEach(instance => instance.forceRedraw());
|
||||
}
|
||||
|
||||
private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise<void> {
|
||||
|
||||
@@ -36,6 +36,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
private _titleInterval: NodeJS.Timer | null = null;
|
||||
private _writeQueue: string[] = [];
|
||||
private _writeTimeout: NodeJS.Timeout | undefined;
|
||||
private _delayedResizer: DelayedResizer | undefined;
|
||||
private readonly _initialCwd: string;
|
||||
private readonly _ptyOptions: pty.IPtyForkOptions | pty.IWindowsPtyForkOptions;
|
||||
|
||||
@@ -80,6 +81,17 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
// This option will force conpty to not redraw the whole viewport on launch
|
||||
conptyInheritCursor: useConpty && !!_shellLaunchConfig.initialText
|
||||
};
|
||||
// Delay resizes to avoid conpty not respecting very early resize calls
|
||||
if (platform.isWindows && useConpty && cols === 0 && rows === 0 && this._shellLaunchConfig.executable?.endsWith('Git\\bin\\bash.exe')) {
|
||||
this._delayedResizer = new DelayedResizer();
|
||||
this._register(this._delayedResizer.onTrigger(dimensions => {
|
||||
this._delayedResizer?.dispose();
|
||||
this._delayedResizer = undefined;
|
||||
if (dimensions.cols && dimensions.rows) {
|
||||
this.resize(dimensions.cols, dimensions.rows);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public async start(): Promise<ITerminalLaunchError | undefined> {
|
||||
@@ -286,6 +298,14 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
if (this._ptyProcess) {
|
||||
cols = Math.max(cols, 1);
|
||||
rows = Math.max(rows, 1);
|
||||
|
||||
// Delay resize if needed
|
||||
if (this._delayedResizer) {
|
||||
this._delayedResizer.cols = cols;
|
||||
this._delayedResizer.rows = rows;
|
||||
return;
|
||||
}
|
||||
|
||||
this._logService.trace('IPty#resize', cols, rows);
|
||||
try {
|
||||
this._ptyProcess.resize(cols, rows);
|
||||
@@ -344,3 +364,32 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the latest resize event to be trigger at a later point.
|
||||
*/
|
||||
class DelayedResizer extends Disposable {
|
||||
public rows: number | undefined;
|
||||
public cols: number | undefined;
|
||||
private _timeout: NodeJS.Timeout;
|
||||
|
||||
private readonly _onTrigger = this._register(new Emitter<{ rows?: number, cols?: number }>());
|
||||
public get onTrigger(): Event<{ rows?: number, cols?: number }> { return this._onTrigger.event; }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._timeout = setTimeout(() => {
|
||||
this._onTrigger.fire({ rows: this.rows, cols: this.cols });
|
||||
}, 1000);
|
||||
this._register({
|
||||
dispose: () => {
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ const unixLinks = [
|
||||
'./foo',
|
||||
'../foo',
|
||||
'/foo/bar',
|
||||
'foo/bar'
|
||||
'/foo/bar+more',
|
||||
'foo/bar',
|
||||
'foo/bar+more',
|
||||
];
|
||||
|
||||
const windowsLinks = [
|
||||
@@ -33,10 +35,12 @@ const windowsLinks = [
|
||||
'~/foo',
|
||||
'c:/foo/bar',
|
||||
'c:\\foo\\bar',
|
||||
'c:\\foo\\bar+more',
|
||||
'c:\\foo/bar\\baz',
|
||||
'foo/bar',
|
||||
'foo/bar',
|
||||
'foo\\bar'
|
||||
'foo\\bar',
|
||||
'foo\\bar+more',
|
||||
];
|
||||
|
||||
interface LinkFormatInfo {
|
||||
|
||||
@@ -79,4 +79,13 @@ suite('Workbench - TerminalWordLinkProvider', () => {
|
||||
{ range: [[5, 1], [7, 1]], text: 'bar' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('should remove trailing colon in the link results', async () => {
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } });
|
||||
await assertLink('foo:5:6: bar:0:32:', [
|
||||
{ range: [[1, 1], [7, 1]], text: 'foo:5:6' },
|
||||
{ range: [[10, 1], [17, 1]], text: 'bar:0:32' }
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -7,13 +7,14 @@ import * as assert from 'assert';
|
||||
import { Extensions as ThemeingExtensions, IColorRegistry, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ansiColorIdentifiers, registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ColorScheme } from 'vs/platform/theme/common/theme';
|
||||
|
||||
registerColors();
|
||||
|
||||
let themingRegistry = Registry.as<IColorRegistry>(ThemeingExtensions.ColorContribution);
|
||||
function getMockTheme(type: ThemeType): IColorTheme {
|
||||
function getMockTheme(type: ColorScheme): IColorTheme {
|
||||
let theme = {
|
||||
selector: '',
|
||||
label: '',
|
||||
@@ -30,7 +31,7 @@ function getMockTheme(type: ThemeType): IColorTheme {
|
||||
suite('Workbench - TerminalColorRegistry', () => {
|
||||
|
||||
test('hc colors', function () {
|
||||
let theme = getMockTheme('hc');
|
||||
let theme = getMockTheme(ColorScheme.HIGH_CONTRAST);
|
||||
let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true));
|
||||
|
||||
assert.deepEqual(colors, [
|
||||
@@ -55,7 +56,7 @@ suite('Workbench - TerminalColorRegistry', () => {
|
||||
});
|
||||
|
||||
test('light colors', function () {
|
||||
let theme = getMockTheme('light');
|
||||
let theme = getMockTheme(ColorScheme.LIGHT);
|
||||
let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true));
|
||||
|
||||
assert.deepEqual(colors, [
|
||||
@@ -80,7 +81,7 @@ suite('Workbench - TerminalColorRegistry', () => {
|
||||
});
|
||||
|
||||
test('dark colors', function () {
|
||||
let theme = getMockTheme('dark');
|
||||
let theme = getMockTheme(ColorScheme.DARK);
|
||||
let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true));
|
||||
|
||||
assert.deepEqual(colors, [
|
||||
|
||||
Reference in New Issue
Block a user