mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 16:50:30 -04:00
Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc (#9646)
* Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc * fix strict
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { IQuickPickSeparator, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction } from 'vs/platform/quickinput/browser/pickerQuickAccess';
|
||||
import { matchesFuzzy } from 'vs/base/common/filters';
|
||||
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -22,6 +22,12 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider<IPick
|
||||
super(TerminalQuickAccessProvider.PREFIX);
|
||||
}
|
||||
|
||||
protected configure(picker: IQuickPick<IPickerQuickAccessItem>): void {
|
||||
|
||||
// Allow to open terminals in background without closing picker
|
||||
picker.canAcceptInBackground = true;
|
||||
}
|
||||
|
||||
protected getPicks(filter: string): Array<IPickerQuickAccessItem | IQuickPickSeparator> {
|
||||
const terminalPicks: Array<IPickerQuickAccessItem | IQuickPickSeparator> = [];
|
||||
|
||||
@@ -60,9 +66,9 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider<IPick
|
||||
|
||||
return TriggerAction.NO_ACTION;
|
||||
},
|
||||
accept: () => {
|
||||
accept: (keyMod, event) => {
|
||||
this.terminalService.setActiveInstance(terminal);
|
||||
this.terminalService.showPanel(true);
|
||||
this.terminalService.showPanel(!event.inBackground);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,8 +294,8 @@ configurationRegistry.registerConfiguration({
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
'terminal.integrated.allowMenubarMnemonics': {
|
||||
markdownDescription: nls.localize('terminal.integrated.allowMenubarMnemonics', "Whether to allow menubar mnemonics (eg. alt+f) to trigger the open the menubar. Note that this will cause all alt keystrokes will skip the shell when true."),
|
||||
'terminal.integrated.allowMnemonics': {
|
||||
markdownDescription: nls.localize('terminal.integrated.allowMnemonics', "Whether to allow menubar mnemonics (eg. alt+f) to trigger the open the menubar. Note that this will cause all alt keystrokes will skip the shell when true."),
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
|
||||
@@ -131,6 +131,14 @@ export interface ITerminalService {
|
||||
findNext(): void;
|
||||
findPrevious(): void;
|
||||
|
||||
/**
|
||||
* Link handlers can be registered here to allow intercepting links clicked in the terminal.
|
||||
* When a link is clicked, the link will be considered handled when the first interceptor
|
||||
* resolves with true. It will be considered not handled when _all_ link handlers resolve with
|
||||
* false, or 3 seconds have elapsed.
|
||||
*/
|
||||
addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable;
|
||||
|
||||
selectDefaultWindowsShell(): Promise<void>;
|
||||
|
||||
setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
|
||||
@@ -179,6 +187,18 @@ export enum WindowsShellType {
|
||||
}
|
||||
export type TerminalShellType = WindowsShellType | undefined;
|
||||
|
||||
export const LINK_INTERCEPT_THRESHOLD = 3000;
|
||||
|
||||
export interface ITerminalBeforeHandleLinkEvent {
|
||||
terminal?: ITerminalInstance;
|
||||
/** The text of the link */
|
||||
link: string;
|
||||
/** Call with whether the link was handled by the interceptor */
|
||||
resolve(wasHandled: boolean): void;
|
||||
}
|
||||
|
||||
export type TerminalLinkHandlerCallback = (e: ITerminalBeforeHandleLinkEvent) => Promise<boolean>;
|
||||
|
||||
export interface ITerminalInstance {
|
||||
/**
|
||||
* The ID of the terminal instance, this is an arbitrary number only used to identify the
|
||||
@@ -240,6 +260,11 @@ export interface ITerminalInstance {
|
||||
*/
|
||||
onExit: Event<number | undefined>;
|
||||
|
||||
/**
|
||||
* Attach a listener to intercept and handle link clicks in the terminal.
|
||||
*/
|
||||
onBeforeHandleLink: Event<ITerminalBeforeHandleLinkEvent>;
|
||||
|
||||
readonly exitCode: number | undefined;
|
||||
|
||||
processReady: Promise<void>;
|
||||
|
||||
@@ -30,7 +30,7 @@ import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGR
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { ITerminalInstanceService, ITerminalInstance, TerminalShellType } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
|
||||
import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm';
|
||||
import { SearchAddon, ISearchOptions } from 'xterm-addon-search';
|
||||
@@ -272,6 +272,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
public get onMaximumDimensionsChanged(): Event<void> { return this._onMaximumDimensionsChanged.event; }
|
||||
private readonly _onFocus = new Emitter<ITerminalInstance>();
|
||||
public get onFocus(): Event<ITerminalInstance> { return this._onFocus.event; }
|
||||
private readonly _onBeforeHandleLink = new Emitter<ITerminalBeforeHandleLinkEvent>();
|
||||
public get onBeforeHandleLink(): Event<ITerminalBeforeHandleLinkEvent> { return this._onBeforeHandleLink.event; }
|
||||
|
||||
public constructor(
|
||||
private readonly _terminalFocusContextKey: IContextKey<boolean>,
|
||||
@@ -523,6 +525,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
});
|
||||
}
|
||||
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, xterm, this._processManager, this._configHelper);
|
||||
this._linkHandler.onBeforeHandleLink(e => {
|
||||
e.terminal = this;
|
||||
this._onBeforeHandleLink.fire(e);
|
||||
});
|
||||
});
|
||||
|
||||
this._commandTrackerAddon = new CommandTrackerAddon();
|
||||
@@ -615,7 +621,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
|
||||
// Skip processing by xterm.js of keyboard events that match menu bar mnemonics
|
||||
if (this._configHelper.config.allowMenubarMnemonics && event.altKey) {
|
||||
if (this._configHelper.config.allowMnemonics && event.altKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { Terminal, ILinkMatcherOptions, IViewportRange } from 'xterm';
|
||||
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 { ITerminalInstanceService, ITerminalBeforeHandleLinkEvent, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { OperatingSystem, isMacintosh } from 'vs/base/common/platform';
|
||||
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const pathPrefix = '(\\.\\.?|\\~)';
|
||||
const pathSeparatorClause = '\\/';
|
||||
@@ -58,7 +60,7 @@ const CUSTOM_LINK_PRIORITY = -1;
|
||||
/** Lowest */
|
||||
const LOCAL_LINK_PRIORITY = -2;
|
||||
|
||||
export type XtermLinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;
|
||||
export type XtermLinkMatcherHandler = (event: MouseEvent, link: string) => Promise<void>;
|
||||
export type XtermLinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
||||
|
||||
interface IPath {
|
||||
@@ -66,14 +68,28 @@ interface IPath {
|
||||
normalize(path: string): string;
|
||||
}
|
||||
|
||||
export class TerminalLinkHandler {
|
||||
private readonly _hoverDisposables = new DisposableStore();
|
||||
export class TerminalLinkHandler extends DisposableStore {
|
||||
private _widgetManager: TerminalWidgetManager | undefined;
|
||||
private _processCwd: string | undefined;
|
||||
private _gitDiffPreImagePattern: RegExp;
|
||||
private _gitDiffPostImagePattern: RegExp;
|
||||
private readonly _tooltipCallback: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void;
|
||||
private readonly _leaveCallback: () => void;
|
||||
private _hasBeforeHandleLinkListeners = false;
|
||||
|
||||
protected static _LINK_INTERCEPT_THRESHOLD = LINK_INTERCEPT_THRESHOLD;
|
||||
public static readonly LINK_INTERCEPT_THRESHOLD = TerminalLinkHandler._LINK_INTERCEPT_THRESHOLD;
|
||||
|
||||
private readonly _onBeforeHandleLink = this.add(new Emitter<ITerminalBeforeHandleLinkEvent>({
|
||||
onFirstListenerAdd: () => this._hasBeforeHandleLinkListeners = true,
|
||||
onLastListenerRemove: () => this._hasBeforeHandleLinkListeners = false
|
||||
}));
|
||||
/**
|
||||
* Allows intercepting links and handling them outside of the default link handler. When fired
|
||||
* the listener has a set amount of time to handle the link or the default handler will fire.
|
||||
* This was designed to only be handled by a single listener.
|
||||
*/
|
||||
public get onBeforeHandleLink(): Event<ITerminalBeforeHandleLinkEvent> { return this._onBeforeHandleLink.event; }
|
||||
|
||||
constructor(
|
||||
private _xterm: Terminal,
|
||||
@@ -83,8 +99,11 @@ export class TerminalLinkHandler {
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
super();
|
||||
|
||||
// Matches '--- a/src/file1', capturing 'src/file1' in group 1
|
||||
this._gitDiffPreImagePattern = /^--- a\/(\S*)/;
|
||||
// Matches '+++ b/src/file1', capturing 'src/file1' in group 1
|
||||
@@ -211,19 +230,40 @@ export class TerminalLinkHandler {
|
||||
this._xterm.registerLinkMatcher(this._gitDiffPostImagePattern, wrappedHandler, options);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._hoverDisposables.dispose();
|
||||
}
|
||||
|
||||
private _wrapLinkHandler(handler: (uri: string) => boolean | void): XtermLinkMatcherHandler {
|
||||
return (event: MouseEvent, uri: string) => {
|
||||
protected _wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler {
|
||||
return async (event: MouseEvent, link: string) => {
|
||||
// Prevent default electron link handling so Alt+Click mode works normally
|
||||
event.preventDefault();
|
||||
// Require correct modifier on click
|
||||
if (!this._isLinkActivationModifierDown(event)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return handler(uri);
|
||||
|
||||
// Allow the link to be intercepted if there are listeners
|
||||
if (this._hasBeforeHandleLinkListeners) {
|
||||
const wasHandled = await new Promise<boolean>(r => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
canceled = true;
|
||||
this._logService.error('An extension intecepted a terminal link but did not return');
|
||||
r(false);
|
||||
}, TerminalLinkHandler.LINK_INTERCEPT_THRESHOLD);
|
||||
let canceled = false;
|
||||
const resolve = (handled: boolean) => {
|
||||
if (!canceled) {
|
||||
clearTimeout(timeoutId);
|
||||
r(handled);
|
||||
}
|
||||
};
|
||||
this._onBeforeHandleLink.fire({ link, resolve });
|
||||
});
|
||||
if (!wasHandled) {
|
||||
handler(link);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Just call the handler if there is no before listener
|
||||
handler(link);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -244,18 +284,17 @@ export class TerminalLinkHandler {
|
||||
return this._gitDiffPostImagePattern;
|
||||
}
|
||||
|
||||
private _handleLocalLink(link: string): PromiseLike<any> {
|
||||
return this._resolvePath(link).then(resolvedLink => {
|
||||
if (!resolvedLink) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link);
|
||||
const selection: ITextEditorSelection = {
|
||||
startLineNumber: lineColumnInfo.lineNumber,
|
||||
startColumn: lineColumnInfo.columnNumber
|
||||
};
|
||||
return this._editorService.openEditor({ resource: resolvedLink, options: { pinned: true, selection } });
|
||||
});
|
||||
private async _handleLocalLink(link: string): Promise<void> {
|
||||
const resolvedLink = await this._resolvePath(link);
|
||||
if (!resolvedLink) {
|
||||
return;
|
||||
}
|
||||
const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link);
|
||||
const selection: ITextEditorSelection = {
|
||||
startLineNumber: lineColumnInfo.lineNumber,
|
||||
startColumn: lineColumnInfo.columnNumber
|
||||
};
|
||||
await this._editorService.openEditor({ resource: resolvedLink, options: { pinned: true, selection } });
|
||||
}
|
||||
|
||||
private _validateLocalLink(link: string, callback: (isValid: boolean) => void): void {
|
||||
@@ -270,7 +309,7 @@ export class TerminalLinkHandler {
|
||||
this._openerService.open(url, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) });
|
||||
}
|
||||
|
||||
private _isLinkActivationModifierDown(event: MouseEvent): boolean {
|
||||
protected _isLinkActivationModifierDown(event: MouseEvent): boolean {
|
||||
const editorConf = this._configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
|
||||
if (editorConf.multiCursorModifier === 'ctrlCmd') {
|
||||
return !!event.altKey;
|
||||
@@ -346,19 +385,19 @@ export class TerminalLinkHandler {
|
||||
return link;
|
||||
}
|
||||
|
||||
private _resolvePath(link: string): PromiseLike<URI | null> {
|
||||
private async _resolvePath(link: string): Promise<URI | undefined> {
|
||||
if (!this._processManager) {
|
||||
throw new Error('Process manager is required');
|
||||
}
|
||||
|
||||
const preprocessedLink = this._preprocessPath(link);
|
||||
if (!preprocessedLink) {
|
||||
return Promise.resolve(null);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const linkUrl = this.extractLinkUrl(preprocessedLink);
|
||||
if (!linkUrl) {
|
||||
return Promise.resolve(null);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -373,18 +412,20 @@ export class TerminalLinkHandler {
|
||||
uri = URI.file(linkUrl);
|
||||
}
|
||||
|
||||
return this._fileService.resolve(uri).then(stat => {
|
||||
try {
|
||||
const stat = await this._fileService.resolve(uri);
|
||||
if (stat.isDirectory) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
return uri;
|
||||
}).catch(() => {
|
||||
}
|
||||
catch (e) {
|
||||
// Does not exist
|
||||
return null;
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
} catch {
|
||||
// Errors in parsing the path
|
||||
return Promise.resolve(null);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab';
|
||||
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
|
||||
import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, TerminalLinkHandlerCallback, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
|
||||
@@ -30,6 +30,7 @@ import { IOpenFileRequest } from 'vs/platform/windows/common/windows';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
interface IExtHostReadyEntry {
|
||||
promise: Promise<void>;
|
||||
@@ -50,6 +51,7 @@ export class TerminalService implements ITerminalService {
|
||||
private _findState: FindReplaceState;
|
||||
private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {};
|
||||
private _activeTabIndex: number;
|
||||
private _linkHandlers: { [key: string]: TerminalLinkHandlerCallback } = {};
|
||||
|
||||
public get activeTabIndex(): number { return this._activeTabIndex; }
|
||||
public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; }
|
||||
@@ -411,6 +413,50 @@ export class TerminalService implements ITerminalService {
|
||||
instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance)));
|
||||
instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance)));
|
||||
instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged));
|
||||
instance.addDisposable(instance.onBeforeHandleLink(async e => {
|
||||
// No link handlers have been registered
|
||||
const keys = Object.keys(this._linkHandlers);
|
||||
if (keys.length === 0) {
|
||||
e.resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire each link interceptor and wait for either a true, all false or the cancel time
|
||||
let resolved = false;
|
||||
const promises: Promise<boolean>[] = [];
|
||||
const timeout = setTimeout(() => {
|
||||
resolved = true;
|
||||
e.resolve(false);
|
||||
}, LINK_INTERCEPT_THRESHOLD);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const p = this._linkHandlers[keys[i]](e);
|
||||
p.then(handled => {
|
||||
if (!resolved && handled) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
e.resolve(true);
|
||||
}
|
||||
});
|
||||
promises.push(p);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
e.resolve(false);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable {
|
||||
this._linkHandlers[key] = callback;
|
||||
return {
|
||||
dispose: () => {
|
||||
if (this._linkHandlers[key] === callback) {
|
||||
delete this._linkHandlers[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined {
|
||||
|
||||
@@ -107,7 +107,7 @@ export interface ITerminalConfiguration {
|
||||
scrollback: number;
|
||||
commandsToSkipShell: string[];
|
||||
allowChords: boolean;
|
||||
allowMenubarMnemonics: boolean;
|
||||
allowMnemonics: boolean;
|
||||
cwd: string;
|
||||
confirmOnExit: boolean;
|
||||
enableBell: boolean;
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler';
|
||||
import { TerminalLinkHandler, LineColumnInfo, XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
|
||||
class TestTerminalLinkHandler extends TerminalLinkHandler {
|
||||
public get localLinkRegex(): RegExp {
|
||||
@@ -24,6 +25,13 @@ class TestTerminalLinkHandler extends TerminalLinkHandler {
|
||||
public preprocessPath(link: string): string | null {
|
||||
return this._preprocessPath(link);
|
||||
}
|
||||
protected _isLinkActivationModifierDown(event: MouseEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
public wrapLinkHandler(handler: (link: string) => void): XtermLinkMatcherHandler {
|
||||
TerminalLinkHandler._LINK_INTERCEPT_THRESHOLD = 0;
|
||||
return this._wrapLinkHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
class TestXterm {
|
||||
@@ -81,7 +89,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Windows,
|
||||
userHome: ''
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) {
|
||||
assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl);
|
||||
assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl);
|
||||
@@ -157,7 +165,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Linux,
|
||||
userHome: ''
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) {
|
||||
assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl);
|
||||
assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl);
|
||||
@@ -225,7 +233,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Windows,
|
||||
userHome: 'C:\\Users\\Me'
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
linkHandler.processCwd = 'C:\\base';
|
||||
|
||||
assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1');
|
||||
@@ -238,7 +246,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Windows,
|
||||
userHome: 'C:\\Users\\M e'
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
linkHandler.processCwd = 'C:\\base dir';
|
||||
|
||||
assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1');
|
||||
@@ -252,7 +260,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Linux,
|
||||
userHome: '/home/me'
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
linkHandler.processCwd = '/base';
|
||||
|
||||
assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1');
|
||||
@@ -265,7 +273,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Linux,
|
||||
userHome: '/home/me'
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
|
||||
assert.equal(linkHandler.preprocessPath('./src/file1'), null);
|
||||
assert.equal(linkHandler.preprocessPath('src/file2'), null);
|
||||
@@ -279,7 +287,7 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Linux,
|
||||
userHome: ''
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!);
|
||||
} as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!);
|
||||
|
||||
function assertAreGoodMatches(matches: RegExpMatchArray | null) {
|
||||
if (matches) {
|
||||
@@ -302,4 +310,35 @@ suite('Workbench - TerminalLinkHandler', () => {
|
||||
assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null'), false);
|
||||
assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null '), false);
|
||||
});
|
||||
|
||||
suite('wrapLinkHandler', () => {
|
||||
const nullMouseEvent: any = Object.freeze({ preventDefault: () => { } });
|
||||
|
||||
test('should allow intercepting of links with onBeforeHandleLink', async () => {
|
||||
const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, {
|
||||
os: OperatingSystem.Linux,
|
||||
userHome: ''
|
||||
} as any, testConfigHelper, null!, null!, new TestConfigurationService(), new MockTerminalInstanceService(), null!, null!);
|
||||
linkHandler.onBeforeHandleLink(e => {
|
||||
if (e.link === 'https://www.microsoft.com') {
|
||||
intercepted = true;
|
||||
e.resolve(true);
|
||||
}
|
||||
e.resolve(false);
|
||||
});
|
||||
const wrappedHandler = linkHandler.wrapLinkHandler(() => defaultHandled = true);
|
||||
|
||||
let defaultHandled = false;
|
||||
let intercepted = false;
|
||||
await wrappedHandler(nullMouseEvent, 'https://www.visualstudio.com');
|
||||
assert.equal(intercepted, false);
|
||||
assert.equal(defaultHandled, true);
|
||||
|
||||
defaultHandled = false;
|
||||
intercepted = false;
|
||||
await wrappedHandler(nullMouseEvent, 'https://www.microsoft.com');
|
||||
assert.equal(intercepted, true);
|
||||
assert.equal(defaultHandled, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,8 +21,8 @@ function getMockTheme(type: ThemeType): IColorTheme {
|
||||
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
|
||||
defines: () => true,
|
||||
getTokenStyleMetadata: () => undefined,
|
||||
tokenColorMap: []
|
||||
|
||||
tokenColorMap: [],
|
||||
semanticHighlighting: false
|
||||
};
|
||||
return theme;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user