Update word link provider with new model

pull/97065/head
Daniel Imms 2020-05-06 04:04:11 -07:00
parent 000c0e1308
commit 5fc2e26dd9
4 changed files with 97 additions and 68 deletions

View File

@ -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[];
}

View File

@ -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,21 @@ 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();
// this.dispose();
}, 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 +95,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 {

View File

@ -297,22 +297,22 @@ export class TerminalLinkManager extends DisposableStore {
public registerLinkProvider(): void {
// Protocol links
const wrappedActivateCallback = this._wrapLinkHandler((_, link) => this._handleProtocolLink(link));
const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback2.bind(this));
this._linkProviders.push(this._xterm.registerLinkProvider(protocolProvider));
// const wrappedActivateCallback = this._wrapLinkHandler((_, link) => this._handleProtocolLink(link));
// const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback2.bind(this));
// this._linkProviders.push(this._xterm.registerLinkProvider(protocolProvider));
// Validated local links
if (this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION).enableFileLinks) {
const wrappedTextLinkActivateCallback = this._wrapLinkHandler((_, link) => this._handleLocalLink(link));
const validatedProvider = this._instantiationService.createInstance(TerminalValidatedLocalLinkProvider,
this._xterm,
this._processManager.os || OS,
wrappedTextLinkActivateCallback,
this._wrapLinkHandler.bind(this),
this._tooltipCallback2.bind(this),
async (link, cb) => cb(await this._resolvePath(link)));
this._linkProviders.push(this._xterm.registerLinkProvider(validatedProvider));
}
// // Validated local links
// if (this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION).enableFileLinks) {
// const wrappedTextLinkActivateCallback = this._wrapLinkHandler((_, link) => this._handleLocalLink(link));
// const validatedProvider = this._instantiationService.createInstance(TerminalValidatedLocalLinkProvider,
// this._xterm,
// this._processManager.os || OS,
// wrappedTextLinkActivateCallback,
// this._wrapLinkHandler.bind(this),
// this._tooltipCallback2.bind(this),
// async (link, cb) => cb(await this._resolvePath(link)));
// this._linkProviders.push(this._xterm.registerLinkProvider(validatedProvider));
// }
// Word links
const wordProvider = this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this));

View File

@ -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,48 @@ 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();
// Add a link if this is a separator
if (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) {