提交 fdfe718a 编写于 作者: D Daniel Imms

Draw underline when ctrl is held for word link providers

上级 e917e5f4
......@@ -33,7 +33,7 @@ import { isEqualOrParent } from 'vs/base/common/resources';
import { ISearchService } from 'vs/workbench/services/search/common/search';
import { QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
import { TerminalHover } from 'vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget';
import { TerminalHover, ILinkHoverTargetOptions } from 'vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget';
const pathPrefix = '(\\.\\.?|\\~)';
const pathSeparatorClause = '\\/';
......@@ -89,7 +89,7 @@ export class TerminalLinkManager extends DisposableStore {
private _processCwd: string | undefined;
private _gitDiffPreImagePattern: RegExp;
private _gitDiffPostImagePattern: RegExp;
private readonly _tooltipCallback: (event: MouseEvent, uri: string, location: IViewportRange, linkHandler: (url: string) => void) => boolean | void;
private readonly _tooltipCallback: (uri: string, location: IViewportRange, linkHandler: (url: string) => void, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => boolean | void;
private _linkMatchers: number[] = [];
private _webLinksAddon: ITerminalAddon | undefined;
private _linkProviders: IDisposable[] = [];
......@@ -134,7 +134,7 @@ export class TerminalLinkManager extends DisposableStore {
// Matches '+++ b/src/file1', capturing 'src/file1' in group 1
this._gitDiffPostImagePattern = /^\+\+\+ b\/(\S*)/;
this._tooltipCallback = (e: MouseEvent, uri: string, linkRange: IViewportRange, linkHandler: (url: string) => void) => {
this._tooltipCallback = (uri: string, viewportRange: IViewportRange, linkHandler: (url: string) => void, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => {
if (!this._widgetManager) {
return;
}
......@@ -149,13 +149,7 @@ export class TerminalLinkManager extends DisposableStore {
height: this._xterm.rows
};
this._showHover(
linkRange,
cellDimensions,
terminalDimensions,
this._getLinkHoverString(uri),
linkHandler
);
this._showHover({ viewportRange, cellDimensions, terminalDimensions, modifierDownCallback, modifierUpCallback }, this._getLinkHoverString(uri), linkHandler);
};
if (this._configHelper.config.experimentalLinkProvider) {
......@@ -179,14 +173,12 @@ export class TerminalLinkManager extends DisposableStore {
}
private _showHover(
viewportRange: IViewportRange,
cellDimensions: { width: number, height: number },
terminalDimensions: { width: number, height: number },
targetOptions: ILinkHoverTargetOptions,
text: IMarkdownString,
linkHandler: (url: string) => void
) {
if (this._widgetManager) {
const widget = this._instantiationService.createInstance(TerminalHover, viewportRange, cellDimensions, terminalDimensions, text, linkHandler);
const widget = this._instantiationService.createInstance(TerminalHover, targetOptions, text, linkHandler);
this._widgetManager.attachWidget(widget);
}
}
......@@ -219,7 +211,7 @@ export class TerminalLinkManager extends DisposableStore {
public registerCustomLinkHandler(regex: RegExp, handler: (uri: string) => void, matchIndex?: number, validationCallback?: XtermLinkMatcherValidationCallback): number {
const tooltipCallback = (event: MouseEvent, uri: string, location: IViewportRange) => {
this._tooltipCallback(event, uri, location, handler);
this._tooltipCallback(uri, location, handler);
};
const options: ILinkMatcherOptions = {
matchIndex,
......@@ -242,7 +234,7 @@ export class TerminalLinkManager extends DisposableStore {
this._handleHypertextLink(link);
});
const tooltipCallback = (event: MouseEvent, uri: string, location: IViewportRange) => {
this._tooltipCallback(event, uri, location, this._handleHypertextLink.bind(this));
this._tooltipCallback(uri, location, this._handleHypertextLink.bind(this));
};
this._webLinksAddon = new WebLinksAddon(wrappedHandler, {
validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateWebLink(callback),
......@@ -258,7 +250,7 @@ export class TerminalLinkManager extends DisposableStore {
this._handleLocalLink(url);
});
const tooltipCallback = (event: MouseEvent, uri: string, location: IViewportRange) => {
this._tooltipCallback(event, uri, location, this._handleLocalLink.bind(this));
this._tooltipCallback(uri, location, this._handleLocalLink.bind(this));
};
this._linkMatchers.push(this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, {
validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback),
......@@ -273,7 +265,7 @@ export class TerminalLinkManager extends DisposableStore {
this._handleLocalLink(url);
});
const tooltipCallback = (event: MouseEvent, uri: string, location: IViewportRange) => {
this._tooltipCallback(event, uri, location, this._handleLocalLink.bind(this));
this._tooltipCallback(uri, location, this._handleLocalLink.bind(this));
};
const options = {
matchIndex: 1,
......@@ -289,7 +281,7 @@ export class TerminalLinkManager extends DisposableStore {
public registerLinkProvider(): void {
// Web links
const tooltipWebCallback = (event: MouseEvent, link: string, location: IViewportRange) => {
this._tooltipCallback(event, link, location, this._handleProtocolLink.bind(this, link));
this._tooltipCallback(link, location, this._handleProtocolLink.bind(this, link));
};
const wrappedActivateCallback = this._wrapLinkHandler(this._handleProtocolLink.bind(this));
this._linkProviders.push(this._xterm.registerLinkProvider(
......@@ -298,7 +290,7 @@ export class TerminalLinkManager extends DisposableStore {
// Validated local links
const tooltipValidatedLocalCallback = (event: MouseEvent, link: string, location: IViewportRange) => {
this._tooltipCallback(event, link, location, this._handleLocalLink.bind(this, link));
this._tooltipCallback(link, location, this._handleLocalLink.bind(this, link));
};
const wrappedTextLinkActivateCallback = this._wrapLinkHandler(this._handleLocalLink.bind(this));
const wrappedDirectoryLinkActivateCallback = this._wrapLinkHandler2(this._handleLocalFolderLink.bind(this));
......@@ -330,8 +322,8 @@ export class TerminalLinkManager extends DisposableStore {
// Fallback to searching quick access
this._quickInputService.quickAccess.show(link);
};
const tooltipWordCallback = (event: MouseEvent, link: string, location: IViewportRange) => {
this._tooltipCallback(event, link, location, wordHandler);
const tooltipWordCallback = (event: MouseEvent, link: string, location: IViewportRange, modifierDownCallback: () => void, modifierUpCallback: () => void) => {
this._tooltipCallback(link, location, wordHandler, modifierDownCallback, modifierUpCallback);
};
const wrappedWordActivateCallback = this._wrapLinkHandler(wordHandler);
this._linkProviders.push(this._xterm.registerLinkProvider(
......
......@@ -7,14 +7,14 @@ import { Terminal, ILinkProvider, IViewportRange, IBufferCellPosition, ILink } f
import { convertBufferRangeToViewport, TOOLTIP_HOVER_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
import { IDisposable } from 'vs/base/common/lifecycle';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
export class TerminalWordLinkProvider implements ILinkProvider {
constructor(
private readonly _xterm: Terminal,
private readonly _activateCallback: (event: MouseEvent, uri: string) => void,
private readonly _tooltipCallback: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void,
private readonly _tooltipCallback: (event: MouseEvent, uri: string, location: IViewportRange, modifierDownCallback: () => void, modifierUpCallback: () => void) => boolean | void,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
}
......@@ -72,19 +72,52 @@ export class TerminalWordLinkProvider implements ILinkProvider {
}
documentMouseOutListener?.dispose();
};
callback({
// TODO: This could be handled better my sharing tooltip hover state between link providers
// Listen for modifier before handing it off to the hover to handle so it gets disposed correctly
const disposables: IDisposable[] = [
dom.addDisposableListener(document, 'keydown', e => {
if (e.ctrlKey && link.hideDecorations) {
link.hideDecorations = false;
}
}),
dom.addDisposableListener(document, 'keyup', e => {
if (!e.ctrlKey) {
link.hideDecorations = true;
}
})
];
const link: ILink = {
text,
range,
// hideDecorations: true,
hideDecorations: true,
activate: (event: MouseEvent, text: string) => this._activateCallback(event, text),
hover: (event: MouseEvent, text: string) => {
documentMouseOutListener = addDisposableListener(document, EventType.MOUSE_OVER, () => clearTimer());
documentMouseOutListener = dom.addDisposableListener(document, dom.EventType.MOUSE_OVER, () => clearTimer());
timeout = window.setTimeout(() => {
this._tooltipCallback(event, text, convertBufferRangeToViewport(range, this._xterm.buffer.active.viewportY));
this._tooltipCallback(
event,
text,
convertBufferRangeToViewport(range, this._xterm.buffer.active.viewportY),
() => {
console.log('down');
link.hideDecorations = false;
},
() => {
console.log('up');
link.hideDecorations = true;
}
);
dispose(disposables);
clearTimer();
}, TOOLTIP_HOVER_THRESHOLD);
},
leave: () => clearTimer()
});
leave: () => {
dispose(disposables);
clearTimer();
}
};
callback(link);
}
}
......@@ -6,21 +6,27 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { Widget } from 'vs/base/browser/ui/widget';
import { IViewportRange } from 'xterm';
import { ITerminalWidget, IHoverAnchor, IHoverTarget, HorizontalAnchorSide, VerticalAnchorSide } from 'vs/workbench/contrib/terminal/browser/widgets/widgets';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { HoverWidget } from 'vs/workbench/contrib/terminal/browser/widgets/hoverWidget';
import * as dom from 'vs/base/browser/dom';
import { IViewportRange } from 'xterm';
const $ = dom.$;
export interface ILinkHoverTargetOptions {
readonly viewportRange: IViewportRange;
readonly cellDimensions: { width: number, height: number };
readonly terminalDimensions: { width: number, height: number };
readonly modifierDownCallback?: () => void;
readonly modifierUpCallback?: () => void;
}
export class TerminalHover extends Disposable implements ITerminalWidget {
readonly id = 'hover';
constructor(
private _viewportRange: IViewportRange,
private _cellDimensions: { width: number, height: number },
private _terminalDimensions: { width: number, height: number },
private _targetOptions: ILinkHoverTargetOptions,
private _text: IMarkdownString,
private _linkHandler: (url: string) => void,
@IInstantiationService private readonly _instantiationService: IInstantiationService
......@@ -29,7 +35,7 @@ export class TerminalHover extends Disposable implements ITerminalWidget {
}
attach(container: HTMLElement): void {
const target = new CellHoverTarget(container, this._viewportRange, this._cellDimensions, this._terminalDimensions);
const target = new CellHoverTarget(container, this._targetOptions);
this._register(this._instantiationService.createInstance(HoverWidget, container, target, this._text, this._linkHandler, []));
}
}
......@@ -42,32 +48,30 @@ class CellHoverTarget extends Widget implements IHoverTarget {
constructor(
private readonly _container: HTMLElement,
viewportRange: IViewportRange,
cellDimensions: { width: number, height: number },
terminalDimensions: { width: number, height: number }
o: ILinkHoverTargetOptions
) {
super();
this._domNode = $('div.terminal-hover-targets');
const targets: HTMLElement[] = [];
const rowCount = viewportRange.end.y - viewportRange.start.y + 1;
const rowCount = o.viewportRange.end.y - o.viewportRange.start.y + 1;
// Add top target row
const width = (viewportRange.end.y > viewportRange.start.y ? terminalDimensions.width - viewportRange.start.x : viewportRange.end.x - viewportRange.start.x + 1) * cellDimensions.width;
const width = (o.viewportRange.end.y > o.viewportRange.start.y ? o.terminalDimensions.width - o.viewportRange.start.x : o.viewportRange.end.x - o.viewportRange.start.x + 1) * o.cellDimensions.width;
const topTarget = $('div.terminal-hover-target.hoverHighlight');
topTarget.style.left = `${(terminalDimensions.height - viewportRange.start.y - 1) * cellDimensions.height}px`;
topTarget.style.bottom = `${viewportRange.start.x * cellDimensions.width}px`;
topTarget.style.left = `${o.viewportRange.start.x * o.cellDimensions.width}px`;
topTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.start.y - 1) * o.cellDimensions.height}px`;
topTarget.style.width = `${width}px`;
topTarget.style.height = `${cellDimensions.height}px`;
topTarget.style.height = `${o.cellDimensions.height}px`;
targets.push(this._domNode.appendChild(topTarget));
// Add middle target rows
if (rowCount > 2) {
const middleTarget = $('div.terminal-hover-target.hoverHighlight');
middleTarget.style.left = `0px`;
middleTarget.style.bottom = `${(terminalDimensions.height - viewportRange.start.y - 1 - (rowCount - 2)) * cellDimensions.height}px`;
middleTarget.style.width = `${terminalDimensions.width * cellDimensions.width}px`;
middleTarget.style.height = `${(rowCount - 2) * cellDimensions.height}px`;
middleTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.start.y - 1 - (rowCount - 2)) * o.cellDimensions.height}px`;
middleTarget.style.width = `${o.terminalDimensions.width * o.cellDimensions.width}px`;
middleTarget.style.height = `${(rowCount - 2) * o.cellDimensions.height}px`;
targets.push(this._domNode.appendChild(middleTarget));
}
......@@ -75,14 +79,30 @@ class CellHoverTarget extends Widget implements IHoverTarget {
if (rowCount > 1) {
const bottomTarget = $('div.terminal-hover-target.hoverHighlight');
bottomTarget.style.left = `0px`;
bottomTarget.style.bottom = `${(terminalDimensions.height - viewportRange.end.y - 1) * cellDimensions.height}px`;
bottomTarget.style.width = `${(viewportRange.end.x + 1) * cellDimensions.width}px`;
bottomTarget.style.height = `${cellDimensions.height}px`;
bottomTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.end.y - 1) * o.cellDimensions.height}px`;
bottomTarget.style.width = `${(o.viewportRange.end.x + 1) * o.cellDimensions.width}px`;
bottomTarget.style.height = `${o.cellDimensions.height}px`;
targets.push(this._domNode.appendChild(bottomTarget));
}
this.targetElements = targets;
if (o.modifierDownCallback && o.modifierUpCallback) {
let down = false;
this._register(dom.addDisposableListener(document, 'keydown', e => {
if (e.ctrlKey && !down) {
down = true;
o.modifierDownCallback!();
}
}));
this._register(dom.addDisposableListener(document, 'keyup', e => {
if (!e.ctrlKey) {
down = false;
o.modifierUpCallback!();
}
}));
}
this._container.appendChild(this._domNode);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册