Merge pull request #97065 from microsoft/tyriar/provide_links
Adopt xterm provideLink -> provideLinks API change
This commit is contained in:
commit
e5be2d8ad7
|
@ -60,10 +60,10 @@
|
|||
"vscode-ripgrep": "^1.5.8",
|
||||
"vscode-sqlite3": "4.0.10",
|
||||
"vscode-textmate": "5.1.1",
|
||||
"xterm": "4.6.0-beta.38",
|
||||
"xterm": "4.6.0-beta.44",
|
||||
"xterm-addon-search": "0.7.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.2.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.6",
|
||||
"xterm-addon-webgl": "0.7.0-beta.10",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
"vscode-proxy-agent": "^0.5.2",
|
||||
"vscode-ripgrep": "^1.5.8",
|
||||
"vscode-textmate": "5.1.1",
|
||||
"xterm": "4.6.0-beta.38",
|
||||
"xterm": "4.6.0-beta.44",
|
||||
"xterm-addon-search": "0.7.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.2.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.6",
|
||||
"xterm-addon-webgl": "0.7.0-beta.10",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
"semver-umd": "^5.5.6",
|
||||
"vscode-oniguruma": "1.3.0",
|
||||
"vscode-textmate": "5.1.1",
|
||||
"xterm": "4.6.0-beta.38",
|
||||
"xterm": "4.6.0-beta.44",
|
||||
"xterm-addon-search": "0.7.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.2.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.5",
|
||||
"xterm-addon-web-links": "0.4.0-beta.6",
|
||||
"xterm-addon-webgl": "0.7.0-beta.10"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,17 @@ xterm-addon-unicode11@0.2.0-beta.5:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298"
|
||||
integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q==
|
||||
|
||||
xterm-addon-web-links@0.4.0-beta.5:
|
||||
version "0.4.0-beta.5"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.5.tgz#523fd0a1c5668370d73e05019ed16eaf596894c8"
|
||||
integrity sha512-Qe0idPpSokCNvGrthSBjdrOZrsgXwnLYbzuv0JoEec/A9HVcxKmZ+ktw7fOA2gT/zbcwtrA5FWrir3GlRHglCQ==
|
||||
xterm-addon-web-links@0.4.0-beta.6:
|
||||
version "0.4.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa"
|
||||
integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ==
|
||||
|
||||
xterm-addon-webgl@0.7.0-beta.10:
|
||||
version "0.7.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4"
|
||||
integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg==
|
||||
|
||||
xterm@4.6.0-beta.38:
|
||||
version "4.6.0-beta.38"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.38.tgz#8472b168941500c3071aba482c2b5c6040951ec7"
|
||||
integrity sha512-Q+nOalMD1MDGOqXdtkGZmOQqbSBU+71vhlX2RBwQoSpJa1QBrKDAhSlN/J+/XvouvVEtCiEFDeacF4EufMEIMg==
|
||||
xterm@4.6.0-beta.44:
|
||||
version "4.6.0-beta.44"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615"
|
||||
integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg==
|
||||
|
|
|
@ -414,20 +414,20 @@ xterm-addon-unicode11@0.2.0-beta.5:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298"
|
||||
integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q==
|
||||
|
||||
xterm-addon-web-links@0.4.0-beta.5:
|
||||
version "0.4.0-beta.5"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.5.tgz#523fd0a1c5668370d73e05019ed16eaf596894c8"
|
||||
integrity sha512-Qe0idPpSokCNvGrthSBjdrOZrsgXwnLYbzuv0JoEec/A9HVcxKmZ+ktw7fOA2gT/zbcwtrA5FWrir3GlRHglCQ==
|
||||
xterm-addon-web-links@0.4.0-beta.6:
|
||||
version "0.4.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa"
|
||||
integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ==
|
||||
|
||||
xterm-addon-webgl@0.7.0-beta.10:
|
||||
version "0.7.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4"
|
||||
integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg==
|
||||
|
||||
xterm@4.6.0-beta.38:
|
||||
version "4.6.0-beta.38"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.38.tgz#8472b168941500c3071aba482c2b5c6040951ec7"
|
||||
integrity sha512-Q+nOalMD1MDGOqXdtkGZmOQqbSBU+71vhlX2RBwQoSpJa1QBrKDAhSlN/J+/XvouvVEtCiEFDeacF4EufMEIMg==
|
||||
xterm@4.6.0-beta.44:
|
||||
version "4.6.0-beta.44"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615"
|
||||
integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg==
|
||||
|
||||
yauzl@^2.9.2:
|
||||
version "2.10.0"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILinkProvider, ILink } from 'xterm';
|
||||
import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink';
|
||||
|
||||
export abstract class TerminalBaseLinkProvider implements ILinkProvider {
|
||||
private _activeLinks: TerminalLink[] | undefined;
|
||||
|
||||
async provideLinks(bufferLineNumber: number, callback: (links: ILink[] | undefined) => void): Promise<void> {
|
||||
this._activeLinks?.forEach(l => l.dispose);
|
||||
this._activeLinks = await this._provideLinks(bufferLineNumber);
|
||||
callback(this._activeLinks);
|
||||
}
|
||||
|
||||
protected abstract _provideLinks(bufferLineNumber: number): Promise<TerminalLink[]> | TerminalLink[];
|
||||
}
|
|
@ -20,6 +20,9 @@ export const FOLDER_NOT_IN_WORKSPACE_LABEL = localize('openFolder', 'Open folder
|
|||
export class TerminalLink extends DisposableStore implements ILink {
|
||||
decorations: ILinkDecorations;
|
||||
|
||||
private _tooltipScheduler: RunOnceScheduler | undefined;
|
||||
private _hoverListeners: DisposableStore | undefined;
|
||||
|
||||
private readonly _onLeave = new Emitter<void>();
|
||||
public get onLeave(): Event<void> { return this._onLeave.event; }
|
||||
|
||||
|
@ -40,6 +43,14 @@ export class TerminalLink extends DisposableStore implements ILink {
|
|||
};
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this._hoverListeners?.dispose();
|
||||
this._hoverListeners = undefined;
|
||||
this._tooltipScheduler?.dispose();
|
||||
this._tooltipScheduler = undefined;
|
||||
}
|
||||
|
||||
activate(event: MouseEvent | undefined, text: string): void {
|
||||
this._activateCallback(event, text);
|
||||
}
|
||||
|
@ -58,20 +69,23 @@ export class TerminalLink extends DisposableStore implements ILink {
|
|||
}));
|
||||
|
||||
const timeout = this._configurationService.getValue<number>('editor.hover.delay');
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
this._tooltipScheduler = new RunOnceScheduler(() => {
|
||||
this._tooltipCallback(
|
||||
this,
|
||||
convertBufferRangeToViewport(this.range, this._viewportY),
|
||||
this._isHighConfidenceLink ? () => this._enableDecorations() : undefined,
|
||||
this._isHighConfidenceLink ? () => this._disableDecorations() : undefined
|
||||
);
|
||||
this.dispose();
|
||||
// Clear out scheduler until next hover event
|
||||
this._tooltipScheduler?.dispose();
|
||||
this._tooltipScheduler = undefined;
|
||||
}, timeout);
|
||||
this.add(scheduler);
|
||||
scheduler.schedule();
|
||||
this.add(this._tooltipScheduler);
|
||||
this._tooltipScheduler.schedule();
|
||||
|
||||
const origin = { x: event.pageX, y: event.pageY };
|
||||
this.add(dom.addDisposableListener(document, dom.EventType.MOUSE_MOVE, e => {
|
||||
this._hoverListeners = new DisposableStore();
|
||||
this._hoverListeners.add(dom.addDisposableListener(document, dom.EventType.MOUSE_MOVE, e => {
|
||||
// Update decorations
|
||||
if (this._isModifierDown(e)) {
|
||||
this._enableDecorations();
|
||||
|
@ -83,14 +97,17 @@ export class TerminalLink extends DisposableStore implements ILink {
|
|||
if (Math.abs(e.pageX - origin.x) > window.devicePixelRatio * 2 || Math.abs(e.pageY - origin.y) > window.devicePixelRatio * 2) {
|
||||
origin.x = e.pageX;
|
||||
origin.y = e.pageY;
|
||||
scheduler.schedule();
|
||||
this._tooltipScheduler?.schedule();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
leave(): void {
|
||||
this._hoverListeners?.dispose();
|
||||
this._hoverListeners = undefined;
|
||||
this._tooltipScheduler?.dispose();
|
||||
this._tooltipScheduler = undefined;
|
||||
this._onLeave.fire();
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
private _enableDecorations(): void {
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Terminal, IViewportRange, ILinkProvider, IBufferCellPosition, ILink, IBufferLine } from 'xterm';
|
||||
import { Terminal, IViewportRange, IBufferLine } from 'xterm';
|
||||
import { ILinkComputerTarget, LinkComputer } from 'vs/editor/common/modes/linkComputer';
|
||||
import { getXtermLineContent, convertLinkRangeToBuffer, positionIsInRange } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers';
|
||||
import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers';
|
||||
import { TerminalLink, OPEN_FILE_LABEL } from 'vs/workbench/contrib/terminal/browser/links/terminalLink';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider';
|
||||
|
||||
export class TerminalProtocolLinkProvider implements ILinkProvider {
|
||||
export class TerminalProtocolLinkProvider extends TerminalBaseLinkProvider {
|
||||
private _linkComputerTarget: ILinkComputerTarget | undefined;
|
||||
|
||||
constructor(
|
||||
|
@ -19,10 +20,11 @@ export class TerminalProtocolLinkProvider implements ILinkProvider {
|
|||
private readonly _tooltipCallback: (link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => void,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void {
|
||||
let startLine = position.y - 1;
|
||||
protected _provideLinks(y: number): TerminalLink[] {
|
||||
let startLine = y - 1;
|
||||
let endLine = startLine;
|
||||
|
||||
const lines: IBufferLine[] = [
|
||||
|
@ -42,24 +44,16 @@ export class TerminalProtocolLinkProvider implements ILinkProvider {
|
|||
this._linkComputerTarget = new TerminalLinkAdapter(this._xterm, startLine, endLine);
|
||||
const links = LinkComputer.computeLinks(this._linkComputerTarget);
|
||||
|
||||
let found = false;
|
||||
links.forEach(link => {
|
||||
return links.map(link => {
|
||||
const range = convertLinkRangeToBuffer(lines, this._xterm.cols, link.range, startLine);
|
||||
|
||||
// Check if the link if within the mouse position
|
||||
if (positionIsInRange(position, range)) {
|
||||
found = true;
|
||||
const uri = link.url
|
||||
? (typeof link.url === 'string' ? URI.parse(link.url) : link.url)
|
||||
: undefined;
|
||||
const label = (uri?.scheme === 'file') ? OPEN_FILE_LABEL : undefined;
|
||||
callback(this._instantiationService.createInstance(TerminalLink, range, link.url?.toString() || '', this._xterm.buffer.active.viewportY, this._activateCallback, this._tooltipCallback, true, label));
|
||||
}
|
||||
const uri = link.url
|
||||
? (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);
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
callback(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Terminal, ILinkProvider, IViewportRange, IBufferCellPosition, ILink, IBufferLine } from 'xterm';
|
||||
import { getXtermLineContent, convertLinkRangeToBuffer, positionIsInRange } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers';
|
||||
import { Terminal, IViewportRange, IBufferLine } from 'xterm';
|
||||
import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TerminalLink, OPEN_FILE_LABEL, FOLDER_IN_WORKSPACE_LABEL, FOLDER_NOT_IN_WORKSPACE_LABEL } from 'vs/workbench/contrib/terminal/browser/links/terminalLink';
|
||||
|
@ -14,6 +14,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
|
|||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
|
||||
import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider';
|
||||
|
||||
const pathPrefix = '(\\.\\.?|\\~)';
|
||||
const pathSeparatorClause = '\\/';
|
||||
|
@ -41,7 +42,7 @@ const lineAndColumnClause = [
|
|||
'(([^:\\s\\(\\)<>\'\"\\[\\]]*)(:(\\d+))?(:(\\d+))?)' // (file path):336, (file path):336:9
|
||||
].join('|').replace(/ /g, `[${'\u00A0'} ]`);
|
||||
|
||||
export class TerminalValidatedLocalLinkProvider implements ILinkProvider {
|
||||
export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider {
|
||||
constructor(
|
||||
private readonly _xterm: Terminal,
|
||||
private readonly _processOperatingSystem: OperatingSystem,
|
||||
|
@ -54,10 +55,12 @@ export class TerminalValidatedLocalLinkProvider implements ILinkProvider {
|
|||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
|
||||
@IHostService private readonly _hostService: IHostService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void) {
|
||||
let startLine = position.y - 1;
|
||||
protected async _provideLinks(y: number): Promise<TerminalLink[]> {
|
||||
const result: TerminalLink[] = [];
|
||||
let startLine = y - 1;
|
||||
let endLine = startLine;
|
||||
|
||||
const lines: IBufferLine[] = [
|
||||
|
@ -121,34 +124,31 @@ export class TerminalValidatedLocalLinkProvider implements ILinkProvider {
|
|||
endLineNumber: 1
|
||||
}, startLine);
|
||||
|
||||
if (positionIsInRange(position, bufferRange)) {
|
||||
const validatedLink = await new Promise<ILink | undefined>(r => {
|
||||
this._validationCallback(link, (result) => {
|
||||
if (result) {
|
||||
const label = result.isDirectory
|
||||
? (this._isDirectoryInsideWorkspace(result.uri) ? FOLDER_IN_WORKSPACE_LABEL : FOLDER_NOT_IN_WORKSPACE_LABEL)
|
||||
: OPEN_FILE_LABEL;
|
||||
const activateCallback = this._wrapLinkHandler((event: MouseEvent | undefined, text: string) => {
|
||||
if (result.isDirectory) {
|
||||
this._handleLocalFolderLink(result.uri);
|
||||
} else {
|
||||
this._activateFileCallback(event, text);
|
||||
}
|
||||
});
|
||||
r(this._instantiationService.createInstance(TerminalLink, bufferRange, link, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, true, label));
|
||||
} else {
|
||||
r(undefined);
|
||||
}
|
||||
});
|
||||
const validatedLink = await new Promise<TerminalLink | undefined>(r => {
|
||||
this._validationCallback(link, (result) => {
|
||||
if (result) {
|
||||
const label = result.isDirectory
|
||||
? (this._isDirectoryInsideWorkspace(result.uri) ? FOLDER_IN_WORKSPACE_LABEL : FOLDER_NOT_IN_WORKSPACE_LABEL)
|
||||
: OPEN_FILE_LABEL;
|
||||
const activateCallback = this._wrapLinkHandler((event: MouseEvent | undefined, text: string) => {
|
||||
if (result.isDirectory) {
|
||||
this._handleLocalFolderLink(result.uri);
|
||||
} else {
|
||||
this._activateFileCallback(event, text);
|
||||
}
|
||||
});
|
||||
r(this._instantiationService.createInstance(TerminalLink, bufferRange, link, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, true, label));
|
||||
} else {
|
||||
r(undefined);
|
||||
}
|
||||
});
|
||||
if (validatedLink) {
|
||||
callback(validatedLink);
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (validatedLink) {
|
||||
result.push(validatedLink);
|
||||
}
|
||||
}
|
||||
|
||||
callback(undefined);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected get _localLinkRegex(): RegExp {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Terminal, ILinkProvider, IViewportRange, IBufferCellPosition, ILink } from 'xterm';
|
||||
import { Terminal, IViewportRange } from 'xterm';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink';
|
||||
|
@ -15,8 +15,9 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
|||
import { QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
|
||||
import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider';
|
||||
|
||||
export class TerminalWordLinkProvider implements ILinkProvider {
|
||||
export class TerminalWordLinkProvider extends TerminalBaseLinkProvider {
|
||||
private readonly _fileQueryBuilder = this._instantiationService.createInstance(QueryBuilder);
|
||||
|
||||
constructor(
|
||||
|
@ -30,54 +31,49 @@ export class TerminalWordLinkProvider implements ILinkProvider {
|
|||
@ISearchService private readonly _searchService: ISearchService,
|
||||
@IEditorService private readonly _editorService: IEditorService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void {
|
||||
const start: IBufferCellPosition = { x: position.x, y: position.y };
|
||||
const end: IBufferCellPosition = { x: position.x, y: position.y };
|
||||
|
||||
protected _provideLinks(y: number): TerminalLink[] {
|
||||
// TODO: Support wrapping
|
||||
// Expand to the left until a word separator is hit
|
||||
const line = this._xterm.buffer.active.getLine(position.y - 1)!;
|
||||
let text = '';
|
||||
start.x++; // The hovered cell is considered first
|
||||
for (let x = position.x; x > 0; x--) {
|
||||
const cell = line.getCell(x - 1);
|
||||
if (!cell) {
|
||||
break;
|
||||
}
|
||||
const char = cell.getChars();
|
||||
const config = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION);
|
||||
if (cell.getWidth() !== 0 && config.wordSeparators.indexOf(char) >= 0) {
|
||||
break;
|
||||
}
|
||||
start.x = x;
|
||||
text = char + text;
|
||||
}
|
||||
|
||||
// No links were found (the hovered cell is whitespace)
|
||||
if (text.length === 0) {
|
||||
callback(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand to the right until a word separator is hit
|
||||
for (let x = position.x + 1; x <= line.length; x++) {
|
||||
const cell = line.getCell(x - 1);
|
||||
if (!cell) {
|
||||
break;
|
||||
}
|
||||
const char = cell.getChars();
|
||||
const config = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION);
|
||||
if (cell.getWidth() !== 0 && config.wordSeparators.indexOf(char) >= 0) {
|
||||
break;
|
||||
}
|
||||
end.x = x;
|
||||
text += char;
|
||||
}
|
||||
|
||||
// Dispose of all old links if new links are provides, links are only cached for the current line
|
||||
const result: TerminalLink[] = [];
|
||||
const wordSeparators = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION).wordSeparators;
|
||||
const activateCallback = this._wrapLinkHandler((_, link) => this._activate(link));
|
||||
callback(new TerminalLink({ start, end }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService));
|
||||
|
||||
const line = this._xterm.buffer.active.getLine(y - 1)!;
|
||||
let text = '';
|
||||
let startX = -1;
|
||||
const cellData = line.getCell(0)!;
|
||||
for (let x = 0; x < line.length; x++) {
|
||||
line.getCell(x, cellData);
|
||||
const chars = cellData.getChars();
|
||||
const width = cellData.getWidth();
|
||||
|
||||
// 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));
|
||||
text = '';
|
||||
startX = -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mark the start of a link if it hasn't started yet
|
||||
if (startX === -1) {
|
||||
startX = x;
|
||||
}
|
||||
|
||||
text += chars;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _activate(link: string) {
|
||||
|
|
|
@ -34,7 +34,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
|||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
|
||||
import { terminalConfiguration, getTerminalShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
|
||||
import { terminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
|
||||
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
// Register services
|
||||
|
@ -58,11 +58,6 @@ CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPick
|
|||
// Register configurations
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration(terminalConfiguration);
|
||||
if (platform.isWeb) {
|
||||
// Desktop shell configuration are registered in electron-browser as their default values rely
|
||||
// on process.env
|
||||
configurationRegistry.registerConfiguration(getTerminalShellConfiguration());
|
||||
}
|
||||
|
||||
// Register views
|
||||
const VIEW_CONTAINER = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
|
@ -164,17 +159,6 @@ if (BrowserFeatures.clipboard.readText) {
|
|||
}
|
||||
}
|
||||
|
||||
if (platform.isWeb) {
|
||||
// Register standard external terminal keybinding as integrated terminal when in web as the
|
||||
// external terminal is not available
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: TERMINAL_COMMAND_ID.NEW,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C
|
||||
});
|
||||
}
|
||||
|
||||
// Delete word left: ctrl+w
|
||||
registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - 64), {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Backspace,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { getTerminalShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
|
||||
|
||||
// Desktop shell configuration are registered in electron-browser as their default values rely
|
||||
// on process.env
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration(getTerminalShellConfiguration());
|
||||
|
||||
// Register standard external terminal keybinding as integrated terminal when in web as the
|
||||
// external terminal is not available
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: TERMINAL_COMMAND_ID.NEW,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C
|
||||
});
|
|
@ -34,6 +34,10 @@ export class TerminalHover extends Disposable implements ITerminalWidget {
|
|||
super();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
attach(container: HTMLElement): void {
|
||||
const target = new CellHoverTarget(container, this._targetOptions);
|
||||
this._register(this._instantiationService.createInstance(HoverWidget, container, target, this._text, this._linkHandler, []));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { TerminalProtocolLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider';
|
||||
import { Terminal, ILink, IBufferRange, IBufferCellPosition } from 'xterm';
|
||||
import { Terminal, ILink } from 'xterm';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -18,77 +18,69 @@ suite('Workbench - TerminalWebLinkProvider', () => {
|
|||
instantiationService.stub(IConfigurationService, TestConfigurationService);
|
||||
});
|
||||
|
||||
async function assertLink(text: string, expected: { text: string, range: [number, number][] }) {
|
||||
async function assertLink(text: string, expected: { text: string, range: [number, number][] }[]) {
|
||||
const xterm = new Terminal();
|
||||
const provider = instantiationService.createInstance(TerminalProtocolLinkProvider, xterm, () => { }, () => { });
|
||||
|
||||
// Write the text and wait for the parser to finish
|
||||
await new Promise<void>(r => xterm.write(text, r));
|
||||
|
||||
// Calculate positions just outside of link boundaries
|
||||
const noLinkPositions: IBufferCellPosition[] = [
|
||||
{ x: expected.range[0][0] - 1, y: expected.range[0][1] },
|
||||
{ x: expected.range[1][0] + 1, y: expected.range[1][1] }
|
||||
];
|
||||
|
||||
// Ensure outside positions do not detect the link
|
||||
for (let i = 0; i < noLinkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(noLinkPositions[i], r));
|
||||
assert.equal(link, undefined, `Just outside range boundary should not result in link, link found at (${link?.range.start.x}, ${link?.range.start.y}) to (${link?.range.end.x}, ${link?.range.end.y}) while checking (${noLinkPositions[i].x}, ${noLinkPositions[i].y})\nExpected link text=${expected.text}\nActual link text=${link?.text}`);
|
||||
}
|
||||
|
||||
// Convert range from [[startx, starty], [endx, endy]] to an IBufferRange
|
||||
const linkRange: IBufferRange = {
|
||||
start: { x: expected.range[0][0], y: expected.range[0][1] },
|
||||
end: { x: expected.range[1][0], y: expected.range[1][1] },
|
||||
};
|
||||
|
||||
// Calculate positions inside the link boundaries
|
||||
const linkPositions: IBufferCellPosition[] = [
|
||||
linkRange.start,
|
||||
linkRange.end
|
||||
];
|
||||
|
||||
// Ensure inside positions do detect the link
|
||||
for (let i = 0; i < linkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(linkPositions[i], r));
|
||||
assert.deepEqual(link?.text, expected.text);
|
||||
assert.deepEqual(link?.range, linkRange);
|
||||
}
|
||||
// Ensure all links are provided
|
||||
const links = (await new Promise<ILink[] | undefined>(r => provider.provideLinks(1, r)))!;
|
||||
assert.equal(links.length, expected.length);
|
||||
const actual = links.map(e => ({
|
||||
text: e.text,
|
||||
range: e.range
|
||||
}));
|
||||
const expectedVerbose = expected.map(e => ({
|
||||
text: e.text,
|
||||
range: {
|
||||
start: { x: e.range[0][0], y: e.range[0][1] },
|
||||
end: { x: e.range[1][0], y: e.range[1][1] },
|
||||
}
|
||||
}));
|
||||
assert.deepEqual(actual, expectedVerbose);
|
||||
}
|
||||
|
||||
// These tests are based on LinkComputer.test.ts
|
||||
test('LinkComputer cases', async () => {
|
||||
await assertLink('x = "http://foo.bar";', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('x = (http://foo.bar);', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('x = \'http://foo.bar\';', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('x = http://foo.bar ;', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('x = <http://foo.bar>;', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('x = {http://foo.bar};', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('(see http://foo.bar)', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('[see http://foo.bar]', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('{see http://foo.bar}', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('<see http://foo.bar>', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('<url>http://foo.bar</url>', { range: [[6, 1], [19, 1]], text: 'http://foo.bar' });
|
||||
await assertLink('// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409', { range: [[30, 1], [7, 2]], text: 'https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409' });
|
||||
await assertLink('// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx', { range: [[30, 1], [28, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' });
|
||||
await assertLink('// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js', { range: [[4, 1], [9, 2]], text: 'https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js' });
|
||||
await assertLink('<!-- !!! Do not remove !!! WebContentRef(link:https://go.microsoft.com/fwlink/?LinkId=166007, area:Admin, updated:2015, nextUpdate:2016, tags:SqlServer) !!! Do not remove !!! -->', { range: [[49, 1], [14, 2]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' });
|
||||
await assertLink('For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.</value>', { range: [[23, 1], [68, 1]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' });
|
||||
await assertLink('For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.</value>', { range: [[23, 1], [21, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' });
|
||||
await assertLink('x = "https://en.wikipedia.org/wiki/Zürich";', { range: [[6, 1], [41, 1]], text: 'https://en.wikipedia.org/wiki/Zürich' });
|
||||
await assertLink('請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。', { range: [[8, 1], [53, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' });
|
||||
await assertLink('(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051)', { range: [[10, 1], [55, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' });
|
||||
await assertLink('x = "file:///foo.bar";', { range: [[6, 1], [20, 1]], text: 'file:///foo.bar' });
|
||||
await assertLink('x = "file://c:/foo.bar";', { range: [[6, 1], [22, 1]], text: 'file://c:/foo.bar' });
|
||||
await assertLink('x = "file://shares/foo.bar";', { range: [[6, 1], [26, 1]], text: 'file://shares/foo.bar' });
|
||||
await assertLink('x = "file://shäres/foo.bar";', { range: [[6, 1], [26, 1]], text: 'file://shäres/foo.bar' });
|
||||
await assertLink('Some text, then http://www.bing.com.', { range: [[17, 1], [35, 1]], text: 'http://www.bing.com' });
|
||||
await assertLink('let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', { range: [[12, 1], [78, 1]], text: 'http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items' });
|
||||
await assertLink('7. At this point, ServiceMain has been called. There is no functionality presently in ServiceMain, but you can consult the [MSDN documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx) to add functionality as desired!', { range: [[66, 2], [64, 3]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx' });
|
||||
await assertLink('let x = "http://[::1]:5000/connect/token"', { range: [[10, 1], [40, 1]], text: 'http://[::1]:5000/connect/token' });
|
||||
await assertLink('2. Navigate to **https://portal.azure.com**', { range: [[18, 1], [41, 1]], text: 'https://portal.azure.com' });
|
||||
await assertLink('POST|https://portal.azure.com|2019-12-05|', { range: [[6, 1], [29, 1]], text: 'https://portal.azure.com' });
|
||||
await assertLink('aa https://foo.bar/[this is foo site] aa', { range: [[5, 1], [38, 1]], text: 'https://foo.bar/[this is foo site]' });
|
||||
await assertLink('x = "http://foo.bar";', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('x = (http://foo.bar);', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('x = \'http://foo.bar\';', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('x = http://foo.bar ;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('x = <http://foo.bar>;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('x = {http://foo.bar};', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('(see http://foo.bar)', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('[see http://foo.bar]', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('{see http://foo.bar}', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('<see http://foo.bar>', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('<url>http://foo.bar</url>', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]);
|
||||
await assertLink('// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409', [{ range: [[30, 1], [7, 2]], text: 'https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409' }]);
|
||||
await assertLink('// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx', [{ range: [[30, 1], [28, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]);
|
||||
await assertLink('// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js', [{ range: [[4, 1], [9, 2]], text: 'https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js' }]);
|
||||
await assertLink('<!-- !!! Do not remove !!! WebContentRef(link:https://go.microsoft.com/fwlink/?LinkId=166007, area:Admin, updated:2015, nextUpdate:2016, tags:SqlServer) !!! Do not remove !!! -->', [{ range: [[49, 1], [14, 2]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]);
|
||||
await assertLink('For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.</value>', [{ range: [[23, 1], [68, 1]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]);
|
||||
await assertLink('For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.</value>', [{ range: [[23, 1], [21, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]);
|
||||
await assertLink('x = "https://en.wikipedia.org/wiki/Zürich";', [{ range: [[6, 1], [41, 1]], text: 'https://en.wikipedia.org/wiki/Zürich' }]);
|
||||
await assertLink('請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。', [{ range: [[8, 1], [53, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]);
|
||||
await assertLink('(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051)', [{ range: [[10, 1], [55, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]);
|
||||
await assertLink('x = "file:///foo.bar";', [{ range: [[6, 1], [20, 1]], text: 'file:///foo.bar' }]);
|
||||
await assertLink('x = "file://c:/foo.bar";', [{ range: [[6, 1], [22, 1]], text: 'file://c:/foo.bar' }]);
|
||||
await assertLink('x = "file://shares/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shares/foo.bar' }]);
|
||||
await assertLink('x = "file://shäres/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shäres/foo.bar' }]);
|
||||
await assertLink('Some text, then http://www.bing.com.', [{ range: [[17, 1], [35, 1]], text: 'http://www.bing.com' }]);
|
||||
await assertLink('let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', [{ range: [[12, 1], [78, 1]], text: 'http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items' }]);
|
||||
await assertLink('7. At this point, ServiceMain has been called. There is no functionality presently in ServiceMain, but you can consult the [MSDN documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx) to add functionality as desired!', [{ range: [[66, 2], [64, 3]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx' }]);
|
||||
await assertLink('let x = "http://[::1]:5000/connect/token"', [{ range: [[10, 1], [40, 1]], text: 'http://[::1]:5000/connect/token' }]);
|
||||
await assertLink('2. Navigate to **https://portal.azure.com**', [{ range: [[18, 1], [41, 1]], text: 'https://portal.azure.com' }]);
|
||||
await assertLink('POST|https://portal.azure.com|2019-12-05|', [{ range: [[6, 1], [29, 1]], text: 'https://portal.azure.com' }]);
|
||||
await assertLink('aa https://foo.bar/[this is foo site] aa', [{ range: [[5, 1], [38, 1]], text: 'https://foo.bar/[this is foo site]' }]);
|
||||
});
|
||||
|
||||
test('should support multiple link results', async () => {
|
||||
await assertLink('http://foo.bar http://bar.foo', [
|
||||
{ range: [[1, 1], [14, 1]], text: 'http://foo.bar' },
|
||||
{ range: [[16, 1], [29, 1]], text: 'http://bar.foo' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { TerminalValidatedLocalLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider';
|
||||
import { Terminal, ILink, IBufferRange, IBufferCellPosition } from 'xterm';
|
||||
import { Terminal, ILink } from 'xterm';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { format } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
@ -76,43 +76,28 @@ suite('Workbench - TerminalValidatedLocalLinkProvider', () => {
|
|||
instantiationService.stub(IConfigurationService, TestConfigurationService);
|
||||
});
|
||||
|
||||
async function assertLink(text: string, os: OperatingSystem, expected: { text: string, range: [number, number][] }) {
|
||||
async function assertLink(text: string, os: OperatingSystem, expected: { text: string, range: [number, number][] }[]) {
|
||||
const xterm = new Terminal();
|
||||
const provider = instantiationService.createInstance(TerminalValidatedLocalLinkProvider, xterm, os, () => { }, () => { }, () => { }, (_: string, cb: (result: { uri: URI, isDirectory: boolean } | undefined) => void) => { cb({ uri: URI.file('/'), isDirectory: false }); });
|
||||
|
||||
// Write the text and wait for the parser to finish
|
||||
await new Promise<void>(r => xterm.write(text, r));
|
||||
|
||||
// Calculate positions just outside of link boundaries
|
||||
const noLinkPositions: IBufferCellPosition[] = [
|
||||
{ x: expected.range[0][0] - 1, y: expected.range[0][1] },
|
||||
{ x: expected.range[1][0] + 1, y: expected.range[1][1] }
|
||||
];
|
||||
|
||||
// Ensure outside positions do not detect the link
|
||||
for (let i = 0; i < noLinkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(noLinkPositions[i], r));
|
||||
assert.equal(link, undefined, `Just outside range boundary should not result in link, link found at (${link?.range.start.x}, ${link?.range.start.y}) to (${link?.range.end.x}, ${link?.range.end.y}) while checking (${noLinkPositions[i].x}, ${noLinkPositions[i].y})\nExpected link text=${expected.text}\nActual link text=${link?.text}`);
|
||||
}
|
||||
|
||||
// Convert range from [[startx, starty], [endx, endy]] to an IBufferRange
|
||||
const linkRange: IBufferRange = {
|
||||
start: { x: expected.range[0][0], y: expected.range[0][1] },
|
||||
end: { x: expected.range[1][0], y: expected.range[1][1] },
|
||||
};
|
||||
|
||||
// Calculate positions inside the link boundaries
|
||||
const linkPositions: IBufferCellPosition[] = [
|
||||
linkRange.start,
|
||||
linkRange.end
|
||||
];
|
||||
|
||||
// Ensure inside positions do detect the link
|
||||
for (let i = 0; i < linkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(linkPositions[i], r));
|
||||
assert.deepEqual(link?.text, expected.text);
|
||||
assert.deepEqual(link?.range, linkRange);
|
||||
}
|
||||
// Ensure all links are provided
|
||||
const links = (await new Promise<ILink[] | undefined>(r => provider.provideLinks(1, r)))!;
|
||||
assert.equal(links.length, expected.length);
|
||||
const actual = links.map(e => ({
|
||||
text: e.text,
|
||||
range: e.range
|
||||
}));
|
||||
const expectedVerbose = expected.map(e => ({
|
||||
text: e.text,
|
||||
range: {
|
||||
start: { x: e.range[0][0], y: e.range[0][1] },
|
||||
end: { x: e.range[1][0], y: e.range[1][1] },
|
||||
}
|
||||
}));
|
||||
assert.deepEqual(actual, expectedVerbose);
|
||||
}
|
||||
|
||||
suite('Linux/macOS', () => {
|
||||
|
@ -122,19 +107,21 @@ suite('Workbench - TerminalValidatedLocalLinkProvider', () => {
|
|||
const linkFormat = supportedLinkFormats[i];
|
||||
test(`Format: ${linkFormat.urlFormat}`, async () => {
|
||||
const formattedLink = format(linkFormat.urlFormat, baseLink, linkFormat.line, linkFormat.column);
|
||||
await assertLink(formattedLink, OperatingSystem.Linux, { text: formattedLink, range: [[1, 1], [formattedLink.length, 1]] });
|
||||
await assertLink(` ${formattedLink} `, OperatingSystem.Linux, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(`(${formattedLink})`, OperatingSystem.Linux, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(`[${formattedLink}]`, OperatingSystem.Linux, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(formattedLink, OperatingSystem.Linux, [{ text: formattedLink, range: [[1, 1], [formattedLink.length, 1]] }]);
|
||||
await assertLink(` ${formattedLink} `, OperatingSystem.Linux, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
await assertLink(`(${formattedLink})`, OperatingSystem.Linux, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
await assertLink(`[${formattedLink}]`, OperatingSystem.Linux, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
test('Git diff links', async () => {
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[14, 1], [20, 1]] });
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[24, 1], [30, 1]] });
|
||||
await assertLink(`--- a/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[7, 1], [13, 1]] });
|
||||
await assertLink(`+++ b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[7, 1], [13, 1]] });
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, [
|
||||
{ text: 'foo/bar', range: [[14, 1], [20, 1]] },
|
||||
{ text: 'foo/bar', range: [[24, 1], [30, 1]] }
|
||||
]);
|
||||
await assertLink(`--- a/foo/bar`, OperatingSystem.Linux, [{ text: 'foo/bar', range: [[7, 1], [13, 1]] }]);
|
||||
await assertLink(`+++ b/foo/bar`, OperatingSystem.Linux, [{ text: 'foo/bar', range: [[7, 1], [13, 1]] }]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -145,19 +132,28 @@ suite('Workbench - TerminalValidatedLocalLinkProvider', () => {
|
|||
const linkFormat = supportedLinkFormats[i];
|
||||
test(`Format: ${linkFormat.urlFormat}`, async () => {
|
||||
const formattedLink = format(linkFormat.urlFormat, baseLink, linkFormat.line, linkFormat.column);
|
||||
await assertLink(formattedLink, OperatingSystem.Windows, { text: formattedLink, range: [[1, 1], [formattedLink.length, 1]] });
|
||||
await assertLink(` ${formattedLink} `, OperatingSystem.Windows, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(`(${formattedLink})`, OperatingSystem.Windows, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(`[${formattedLink}]`, OperatingSystem.Windows, { text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] });
|
||||
await assertLink(formattedLink, OperatingSystem.Windows, [{ text: formattedLink, range: [[1, 1], [formattedLink.length, 1]] }]);
|
||||
await assertLink(` ${formattedLink} `, OperatingSystem.Windows, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
await assertLink(`(${formattedLink})`, OperatingSystem.Windows, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
await assertLink(`[${formattedLink}]`, OperatingSystem.Windows, [{ text: formattedLink, range: [[2, 1], [formattedLink.length + 1, 1]] }]);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
test('Git diff links', async () => {
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[14, 1], [20, 1]] });
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[24, 1], [30, 1]] });
|
||||
await assertLink(`--- a/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[7, 1], [13, 1]] });
|
||||
await assertLink(`+++ b/foo/bar`, OperatingSystem.Linux, { text: 'foo/bar', range: [[7, 1], [13, 1]] });
|
||||
await assertLink(`diff --git a/foo/bar b/foo/bar`, OperatingSystem.Linux, [
|
||||
{ text: 'foo/bar', range: [[14, 1], [20, 1]] },
|
||||
{ text: 'foo/bar', range: [[24, 1], [30, 1]] }
|
||||
]);
|
||||
await assertLink(`--- a/foo/bar`, OperatingSystem.Linux, [{ text: 'foo/bar', range: [[7, 1], [13, 1]] }]);
|
||||
await assertLink(`+++ b/foo/bar`, OperatingSystem.Linux, [{ text: 'foo/bar', range: [[7, 1], [13, 1]] }]);
|
||||
});
|
||||
});
|
||||
|
||||
test('should support multiple link results', async () => {
|
||||
await assertLink('./foo ./bar', OperatingSystem.Linux, [
|
||||
{ range: [[1, 1], [5, 1]], text: './foo' },
|
||||
{ range: [[7, 1], [11, 1]], text: './bar' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Terminal, ILink, IBufferRange, IBufferCellPosition } from 'xterm';
|
||||
import { Terminal, ILink } from 'xterm';
|
||||
import { TerminalWordLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
|
@ -21,68 +21,62 @@ suite('Workbench - TerminalWordLinkProvider', () => {
|
|||
instantiationService.stub(IConfigurationService, configurationService);
|
||||
});
|
||||
|
||||
async function assertLink(text: string, expected: { text: string, range: [number, number][] }) {
|
||||
async function assertLink(text: string, expected: { text: string, range: [number, number][] }[]) {
|
||||
const xterm = new Terminal();
|
||||
const provider = instantiationService.createInstance(TerminalWordLinkProvider, xterm, () => { }, () => { });
|
||||
const provider: TerminalWordLinkProvider = instantiationService.createInstance(TerminalWordLinkProvider, xterm, () => { }, () => { });
|
||||
|
||||
// Write the text and wait for the parser to finish
|
||||
await new Promise<void>(r => xterm.write(text, r));
|
||||
|
||||
// Calculate positions just outside of link boundaries
|
||||
const noLinkPositions: IBufferCellPosition[] = [
|
||||
{ x: expected.range[0][0] - 1, y: expected.range[0][1] },
|
||||
{ x: expected.range[1][0] + 1, y: expected.range[1][1] }
|
||||
];
|
||||
|
||||
// Ensure outside positions do not detect the link
|
||||
for (let i = 0; i < noLinkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(noLinkPositions[i], r));
|
||||
assert.equal(link, undefined, `Just outside range boundary should not result in link, link found at (${link?.range.start.x}, ${link?.range.start.y}) to (${link?.range.end.x}, ${link?.range.end.y}) while checking (${noLinkPositions[i].x}, ${noLinkPositions[i].y})\nExpected link text=${expected.text}\nActual link text=${link?.text}`);
|
||||
}
|
||||
|
||||
// Convert range from [[startx, starty], [endx, endy]] to an IBufferRange
|
||||
const linkRange: IBufferRange = {
|
||||
start: { x: expected.range[0][0], y: expected.range[0][1] },
|
||||
end: { x: expected.range[1][0], y: expected.range[1][1] },
|
||||
};
|
||||
|
||||
// Calculate positions inside the link boundaries
|
||||
const linkPositions: IBufferCellPosition[] = [
|
||||
linkRange.start,
|
||||
linkRange.end
|
||||
];
|
||||
|
||||
// Ensure inside positions do detect the link
|
||||
for (let i = 0; i < linkPositions.length; i++) {
|
||||
const link = await new Promise<ILink | undefined>(r => provider.provideLink(linkPositions[i], r));
|
||||
assert.deepEqual(link?.text, expected.text);
|
||||
assert.deepEqual(link?.range, linkRange);
|
||||
}
|
||||
// Ensure all links are provided
|
||||
const links = (await new Promise<ILink[] | undefined>(r => provider.provideLinks(1, r)))!;
|
||||
assert.equal(links.length, expected.length);
|
||||
const actual = links.map(e => ({
|
||||
text: e.text,
|
||||
range: e.range
|
||||
}));
|
||||
const expectedVerbose = expected.map(e => ({
|
||||
text: e.text,
|
||||
range: {
|
||||
start: { x: e.range[0][0], y: e.range[0][1] },
|
||||
end: { x: e.range[1][0], y: e.range[1][1] },
|
||||
}
|
||||
}));
|
||||
assert.deepEqual(actual, expectedVerbose);
|
||||
}
|
||||
|
||||
test('should link words as defined by wordSeparators', async () => {
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ()[]' } });
|
||||
await assertLink('foo', { range: [[1, 1], [3, 1]], text: 'foo' });
|
||||
await assertLink(' foo ', { range: [[2, 1], [4, 1]], text: 'foo' });
|
||||
await assertLink('(foo)', { range: [[2, 1], [4, 1]], text: 'foo' });
|
||||
await assertLink('[foo]', { range: [[2, 1], [4, 1]], text: 'foo' });
|
||||
await assertLink('{foo}', { range: [[1, 1], [5, 1]], text: '{foo}' });
|
||||
await assertLink('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]);
|
||||
await assertLink('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]);
|
||||
await assertLink(' foo ', [{ range: [[2, 1], [4, 1]], text: 'foo' }]);
|
||||
await assertLink('(foo)', [{ range: [[2, 1], [4, 1]], text: 'foo' }]);
|
||||
await assertLink('[foo]', [{ range: [[2, 1], [4, 1]], text: 'foo' }]);
|
||||
await assertLink('{foo}', [{ range: [[1, 1], [5, 1]], text: '{foo}' }]);
|
||||
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } });
|
||||
await assertLink('foo', { range: [[1, 1], [3, 1]], text: 'foo' });
|
||||
await assertLink(' foo ', { range: [[2, 1], [4, 1]], text: 'foo' });
|
||||
await assertLink('(foo)', { range: [[1, 1], [5, 1]], text: '(foo)' });
|
||||
await assertLink('[foo]', { range: [[1, 1], [5, 1]], text: '[foo]' });
|
||||
await assertLink('{foo}', { range: [[1, 1], [5, 1]], text: '{foo}' });
|
||||
await assertLink('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]);
|
||||
await assertLink(' foo ', [{ range: [[2, 1], [4, 1]], text: 'foo' }]);
|
||||
await assertLink('(foo)', [{ range: [[1, 1], [5, 1]], text: '(foo)' }]);
|
||||
await assertLink('[foo]', [{ range: [[1, 1], [5, 1]], text: '[foo]' }]);
|
||||
await assertLink('{foo}', [{ range: [[1, 1], [5, 1]], text: '{foo}' }]);
|
||||
});
|
||||
|
||||
test('should support wide characters', async () => {
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' []' } });
|
||||
await assertLink('aabbccdd.txt ', { range: [[1, 1], [12, 1]], text: 'aabbccdd.txt' });
|
||||
await assertLink('我是学生.txt ', { range: [[1, 1], [12, 1]], text: '我是学生.txt' });
|
||||
await assertLink(' aabbccdd.txt ', { range: [[2, 1], [13, 1]], text: 'aabbccdd.txt' });
|
||||
await assertLink(' 我是学生.txt ', { range: [[2, 1], [13, 1]], text: '我是学生.txt' });
|
||||
await assertLink(' [aabbccdd.txt] ', { range: [[3, 1], [14, 1]], text: 'aabbccdd.txt' });
|
||||
await assertLink(' [我是学生.txt] ', { range: [[3, 1], [14, 1]], text: '我是学生.txt' });
|
||||
await assertLink('aabbccdd.txt ', [{ range: [[1, 1], [12, 1]], text: 'aabbccdd.txt' }]);
|
||||
await assertLink('我是学生.txt ', [{ range: [[1, 1], [12, 1]], text: '我是学生.txt' }]);
|
||||
await assertLink(' aabbccdd.txt ', [{ range: [[2, 1], [13, 1]], text: 'aabbccdd.txt' }]);
|
||||
await assertLink(' 我是学生.txt ', [{ range: [[2, 1], [13, 1]], text: '我是学生.txt' }]);
|
||||
await assertLink(' [aabbccdd.txt] ', [{ range: [[3, 1], [14, 1]], text: 'aabbccdd.txt' }]);
|
||||
await assertLink(' [我是学生.txt] ', [{ range: [[3, 1], [14, 1]], text: '我是学生.txt' }]);
|
||||
});
|
||||
|
||||
test('should support multiple link results', async () => {
|
||||
await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } });
|
||||
await assertLink('foo bar', [
|
||||
{ range: [[1, 1], [3, 1]], text: 'foo' },
|
||||
{ range: [[5, 1], [7, 1]], text: 'bar' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -116,6 +116,7 @@ import 'vs/workbench/contrib/webview/browser/webviewService';
|
|||
import 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
|
||||
|
||||
// Terminal
|
||||
import 'vs/workbench/contrib/terminal/browser/terminal.web.contribution';
|
||||
import 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
|
||||
|
||||
// Tasks
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -10052,20 +10052,20 @@ xterm-addon-unicode11@0.2.0-beta.5:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298"
|
||||
integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q==
|
||||
|
||||
xterm-addon-web-links@0.4.0-beta.5:
|
||||
version "0.4.0-beta.5"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.5.tgz#523fd0a1c5668370d73e05019ed16eaf596894c8"
|
||||
integrity sha512-Qe0idPpSokCNvGrthSBjdrOZrsgXwnLYbzuv0JoEec/A9HVcxKmZ+ktw7fOA2gT/zbcwtrA5FWrir3GlRHglCQ==
|
||||
xterm-addon-web-links@0.4.0-beta.6:
|
||||
version "0.4.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa"
|
||||
integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ==
|
||||
|
||||
xterm-addon-webgl@0.7.0-beta.10:
|
||||
version "0.7.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4"
|
||||
integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg==
|
||||
|
||||
xterm@4.6.0-beta.38:
|
||||
version "4.6.0-beta.38"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.38.tgz#8472b168941500c3071aba482c2b5c6040951ec7"
|
||||
integrity sha512-Q+nOalMD1MDGOqXdtkGZmOQqbSBU+71vhlX2RBwQoSpJa1QBrKDAhSlN/J+/XvouvVEtCiEFDeacF4EufMEIMg==
|
||||
xterm@4.6.0-beta.44:
|
||||
version "4.6.0-beta.44"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615"
|
||||
integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg==
|
||||
|
||||
y18n@^3.2.1:
|
||||
version "3.2.1"
|
||||
|
|
Loading…
Reference in a new issue