mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 17:20:28 -04:00
Merge from vscode 1b314ab317fbff7d799b21754326b7d849889ceb
This commit is contained in:
@@ -9,6 +9,7 @@ import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/termin
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider';
|
||||
import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
|
||||
|
||||
/**
|
||||
* An adapter to convert a simple external link provider into an internal link provider that
|
||||
@@ -20,6 +21,7 @@ export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvide
|
||||
private readonly _xterm: Terminal,
|
||||
private readonly _instance: ITerminalInstance,
|
||||
private readonly _externalLinkProvider: ITerminalExternalLinkProvider,
|
||||
private readonly _wrapLinkHandler: (handler: (event: MouseEvent | undefined, link: string) => void) => XtermLinkMatcherHandler,
|
||||
private readonly _tooltipCallback: (link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => void,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
@@ -45,6 +47,10 @@ export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvide
|
||||
}
|
||||
|
||||
const lineContent = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols);
|
||||
if (lineContent.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const externalLinks = await this._externalLinkProvider.provideLinks(this._instance, lineContent);
|
||||
if (!externalLinks) {
|
||||
return [];
|
||||
@@ -58,7 +64,8 @@ export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvide
|
||||
endLineNumber: 1
|
||||
}, startLine);
|
||||
const matchingText = lineContent.substr(link.startIndex, link.length) || '';
|
||||
return this._instantiationService.createInstance(TerminalLink, bufferRange, matchingText, this._xterm.buffer.active.viewportY, (_, text) => link.activate(text), this._tooltipCallback, true, link.label);
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,13 +94,16 @@ export function convertBufferRangeToViewport(bufferRange: IBufferRange, viewport
|
||||
}
|
||||
|
||||
export function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number, cols: number): string {
|
||||
let line = '';
|
||||
let content = '';
|
||||
for (let i = lineStart; i <= lineEnd; i++) {
|
||||
// Make sure only 0 to cols are considered as resizing when windows mode is enabled will
|
||||
// retain buffer data outside of the terminal width as reflow is disabled.
|
||||
line += buffer.getLine(i)!.translateToString(true, 0, cols);
|
||||
const line = buffer.getLine(i);
|
||||
if (line) {
|
||||
content += line.translateToString(true, 0, cols);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
return content;
|
||||
}
|
||||
|
||||
export function positionIsInRange(position: IBufferCellPosition, range: IBufferRange): boolean {
|
||||
|
||||
@@ -156,7 +156,7 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
}
|
||||
|
||||
public registerExternalLinkProvider(instance: ITerminalInstance, linkProvider: ITerminalExternalLinkProvider): IDisposable {
|
||||
const wrappedLinkProvider = this._instantiationService.createInstance(TerminalExternalLinkProviderAdapter, this._xterm, instance, linkProvider, this._tooltipCallback2.bind(this));
|
||||
const wrappedLinkProvider = this._instantiationService.createInstance(TerminalExternalLinkProviderAdapter, this._xterm, instance, linkProvider, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this));
|
||||
const newLinkProvider = this._xterm.registerLinkProvider(wrappedLinkProvider);
|
||||
// Re-register the standard link providers so they are a lower priority that the new one
|
||||
this._registerStandardLinkProviders();
|
||||
|
||||
@@ -141,6 +141,9 @@ function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyE
|
||||
});
|
||||
}
|
||||
|
||||
// The text representation of `^<letter>` is `'A'.charCodeAt(0) + 1`.
|
||||
const CTRL_LETTER_OFFSET = 64;
|
||||
|
||||
if (BrowserFeatures.clipboard.readText) {
|
||||
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(TerminalPasteAction, {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
|
||||
@@ -152,7 +155,7 @@ if (BrowserFeatures.clipboard.readText) {
|
||||
// disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen
|
||||
// reader.
|
||||
if (platform.isWindows) {
|
||||
registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - 64), { // ctrl+v
|
||||
registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v
|
||||
when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_V
|
||||
});
|
||||
@@ -160,10 +163,18 @@ if (BrowserFeatures.clipboard.readText) {
|
||||
}
|
||||
|
||||
// Delete word left: ctrl+w
|
||||
registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - 64), {
|
||||
registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - CTRL_LETTER_OFFSET), {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Backspace,
|
||||
mac: { primary: KeyMod.Alt | KeyCode.Backspace }
|
||||
});
|
||||
if (platform.isWindows) {
|
||||
// Delete word left: ctrl+h
|
||||
// Windows cmd.exe requires ^H to delete full word left
|
||||
registerSendSequenceKeybinding(String.fromCharCode('H'.charCodeAt(0) - CTRL_LETTER_OFFSET), {
|
||||
when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.CommandPrompt)),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Backspace,
|
||||
});
|
||||
}
|
||||
// Delete word right: alt+d
|
||||
registerSendSequenceKeybinding('\x1bd', {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Delete,
|
||||
|
||||
@@ -294,7 +294,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
if (!exeBasedExtensionTips || !exeBasedExtensionTips.wsl) {
|
||||
return;
|
||||
}
|
||||
const extId = exeBasedExtensionTips.wsl.recommendations[0];
|
||||
const extId = Object.keys(exeBasedExtensionTips.wsl.recommendations).find(extId => exeBasedExtensionTips.wsl.recommendations[extId].important);
|
||||
if (extId && ! await this.isExtensionInstalled(extId)) {
|
||||
this._notificationService.prompt(
|
||||
Severity.Info,
|
||||
|
||||
@@ -25,9 +25,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
// TODO@daniel code layering
|
||||
// eslint-disable-next-line code-layering, code-import-patterns
|
||||
import { INativeOpenFileRequest } from 'vs/platform/windows/node/window';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
@@ -109,6 +106,8 @@ export class TerminalService implements ITerminalService {
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IViewsService private _viewsService: IViewsService,
|
||||
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
|
||||
// HACK: Ideally TerminalNativeService would depend on TerminalService and inject the
|
||||
// additional native functionality into it.
|
||||
@optional(ITerminalNativeService) terminalNativeService: ITerminalNativeService
|
||||
) {
|
||||
// @optional could give undefined and properly typing it breaks service registration
|
||||
@@ -120,7 +119,14 @@ export class TerminalService implements ITerminalService {
|
||||
lifecycleService.onBeforeShutdown(async event => event.veto(this._onBeforeShutdown()));
|
||||
lifecycleService.onShutdown(() => this._onShutdown());
|
||||
if (this._terminalNativeService) {
|
||||
this._terminalNativeService.onOpenFileRequest(e => this._onOpenFileRequest(e));
|
||||
this._terminalNativeService.onRequestFocusActiveInstance(() => {
|
||||
if (this.terminalInstances.length > 0) {
|
||||
const terminal = this.getActiveInstance();
|
||||
if (terminal) {
|
||||
terminal.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
this._terminalNativeService.onOsResume(() => this._onOsResume());
|
||||
}
|
||||
this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService);
|
||||
@@ -220,22 +226,6 @@ export class TerminalService implements ITerminalService {
|
||||
this.terminalInstances.forEach(instance => instance.dispose(true));
|
||||
}
|
||||
|
||||
private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise<void> {
|
||||
// if the request to open files is coming in from the integrated terminal (identified though
|
||||
// the termProgram variable) and we are instructed to wait for editors close, wait for the
|
||||
// marker file to get deleted and then focus back to the integrated terminal.
|
||||
if (request.termProgram === 'vscode' && request.filesToWait && this._terminalNativeService) {
|
||||
const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri);
|
||||
await this._terminalNativeService.whenFileDeleted(waitMarkerFileUri);
|
||||
if (this.terminalInstances.length > 0) {
|
||||
const terminal = this.getActiveInstance();
|
||||
if (terminal) {
|
||||
terminal.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _onOsResume(): void {
|
||||
const activeTab = this.getActiveTab();
|
||||
if (!activeTab) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { IOpenFileRequest } from 'vs/platform/windows/common/windows';
|
||||
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
|
||||
@@ -238,7 +237,7 @@ export interface ITerminalNativeService {
|
||||
|
||||
readonly linuxDistro: LinuxDistro;
|
||||
|
||||
readonly onOpenFileRequest: Event<IOpenFileRequest>;
|
||||
readonly onRequestFocusActiveInstance: Event<void>;
|
||||
readonly onOsResume: Event<void>;
|
||||
|
||||
getWindowsBuildNumber(): number;
|
||||
|
||||
@@ -218,7 +218,7 @@ export const terminalConfiguration: IConfigurationNode = {
|
||||
default: false
|
||||
},
|
||||
'terminal.integrated.commandsToSkipShell': {
|
||||
markdownDescription: localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by VS Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open. Use the command prefixed with `-` to remove default commands from the list.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')),
|
||||
markdownDescription: localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n---\n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n*The following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, [open the default settings JSON](command:workbench.action.openRawDefaultSettings 'Open Default Settings (JSON)') and search for the first command from the list below.*\n\n---\n\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
|
||||
@@ -17,14 +17,15 @@ import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/elect
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { INativeOpenFileRequest } from 'vs/platform/windows/node/window';
|
||||
|
||||
export class TerminalNativeService extends Disposable implements ITerminalNativeService {
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
public get linuxDistro(): LinuxDistro { return linuxDistro; }
|
||||
|
||||
private readonly _onOpenFileRequest = this._register(new Emitter<IOpenFileRequest>());
|
||||
public get onOpenFileRequest(): Event<IOpenFileRequest> { return this._onOpenFileRequest.event; }
|
||||
private readonly _onRequestFocusActiveInstance = this._register(new Emitter<void>());
|
||||
public get onRequestFocusActiveInstance(): Event<void> { return this._onRequestFocusActiveInstance.event; }
|
||||
private readonly _onOsResume = this._register(new Emitter<void>());
|
||||
public get onOsResume(): Event<void> { return this._onOsResume.event; }
|
||||
|
||||
@@ -36,7 +37,7 @@ export class TerminalNativeService extends Disposable implements ITerminalNative
|
||||
) {
|
||||
super();
|
||||
|
||||
ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this._onOpenFileRequest.fire(request));
|
||||
ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this._onOpenFileRequest(request));
|
||||
this._register(electronService.onOSResume(() => this._onOsResume.fire()));
|
||||
|
||||
const connection = remoteAgentService.getConnection();
|
||||
@@ -45,6 +46,17 @@ export class TerminalNativeService extends Disposable implements ITerminalNative
|
||||
}
|
||||
}
|
||||
|
||||
private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise<void> {
|
||||
// if the request to open files is coming in from the integrated terminal (identified though
|
||||
// the termProgram variable) and we are instructed to wait for editors close, wait for the
|
||||
// marker file to get deleted and then focus back to the integrated terminal.
|
||||
if (request.termProgram === 'vscode' && request.filesToWait) {
|
||||
const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri);
|
||||
await this.whenFileDeleted(waitMarkerFileUri);
|
||||
this._onRequestFocusActiveInstance.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public whenFileDeleted(path: URI): Promise<void> {
|
||||
// Complete when wait marker file is deleted
|
||||
return new Promise<void>(resolve => {
|
||||
|
||||
@@ -111,7 +111,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
try {
|
||||
const result = await stat(slc.executable);
|
||||
if (!result.isFile() && !result.isSymbolicLink()) {
|
||||
return { message: localize('launchFail.executableIsNotFileOrSymlink', "Shell path \"{0}\" is not a file of a symlink", slc.executable) };
|
||||
return { message: localize('launchFail.executableIsNotFileOrSymlink', "Path to shell executable \"{0}\" is not a file of a symlink", slc.executable) };
|
||||
}
|
||||
} catch (err) {
|
||||
if (err?.code === 'ENOENT') {
|
||||
@@ -120,7 +120,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
const envPaths: string[] | undefined = (slc.env && slc.env.PATH) ? slc.env.PATH.split(path.delimiter) : undefined;
|
||||
const executable = await findExecutable(slc.executable!, cwd, envPaths);
|
||||
if (!executable) {
|
||||
return { message: localize('launchFail.executableDoesNotExist', "Shell path \"{0}\" does not exist", slc.executable) };
|
||||
return { message: localize('launchFail.executableDoesNotExist', "Path to shell executable \"{0}\" does not exist", slc.executable) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||
this._ptyProcess.resize(cols, rows);
|
||||
} catch (e) {
|
||||
// Swallow error if the pty has already exited
|
||||
if (this._exitCode !== undefined) {
|
||||
this._logService.trace('IPty#resize exception ' + e.message);
|
||||
if (this._exitCode !== undefined && e.message !== 'ioctl(2) failed, EBADF') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user