未验证 提交 7493a604 编写于 作者: D Daniel Imms 提交者: GitHub

Merge pull request #83620 from jmbockhorst/followLink

Make terminal link behavior consistent with the editor
......@@ -8,15 +8,22 @@
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
}
.monaco-workbench .terminal-message-widget {
font-size: 12px;
line-height: 19px;
padding: 4px 5px;
padding: 4px 8px;
animation: fadein 100ms linear;
white-space: nowrap;
/* Must be drawn on the top of the terminal's canvases */
z-index: 20;
}
\ No newline at end of file
}
.monaco-workbench .terminal-message-widget p {
margin: 0px;
}
.monaco-workbench .terminal-message-widget a {
color: #3794ff;
}
......@@ -39,6 +39,7 @@ import { CommandTrackerAddon } from 'vs/workbench/contrib/terminal/browser/addon
import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/addons/navigationModeAddon';
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
// How long in milliseconds should an average frame take to render for a notification to appear
// which suggests the fallback DOM-based renderer
......@@ -286,7 +287,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILogService private readonly _logService: ILogService,
@IStorageService private readonly _storageService: IStorageService,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
@IOpenerService private readonly _openerService: IOpenerService
) {
super();
......@@ -666,7 +668,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._refreshSelectionContextKey();
}));
const widgetManager = new TerminalWidgetManager(this._wrapperElement);
const widgetManager = new TerminalWidgetManager(this._wrapperElement, this._openerService);
this._widgetManager = widgetManager;
this._processManager.onProcessReady(() => this._linkHandler?.setWidgetManager(widgetManager));
......
......@@ -18,6 +18,7 @@ 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 { OperatingSystem, isMacintosh } from 'vs/base/common/platform';
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
const pathPrefix = '(\\.\\.?|\\~)';
const pathSeparatorClause = '\\/';
......@@ -116,7 +117,7 @@ export class TerminalLinkHandler {
const leftPosition = location.start.x * (charWidth! + (font.letterSpacing / window.devicePixelRatio));
const bottomPosition = offsetRow * (Math.ceil(charHeight! * window.devicePixelRatio) * font.lineHeight) / window.devicePixelRatio;
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(), verticalAlignment);
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(uri), verticalAlignment);
} else {
const target = (e.target as HTMLElement);
const colWidth = target.offsetWidth / this._xterm.cols;
......@@ -124,7 +125,7 @@ export class TerminalLinkHandler {
const leftPosition = location.start.x * colWidth;
const bottomPosition = offsetRow * rowHeight;
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(), verticalAlignment);
this._widgetManager.showMessage(leftPosition, bottomPosition, this._getLinkHoverString(uri), verticalAlignment);
}
};
this._leaveCallback = () => {
......@@ -277,19 +278,29 @@ export class TerminalLinkHandler {
return isMacintosh ? event.metaKey : event.ctrlKey;
}
private _getLinkHoverString(): string {
private _getLinkHoverString(uri: string): IMarkdownString {
const editorConf = this._configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
let label = '';
if (editorConf.multiCursorModifier === 'ctrlCmd') {
if (isMacintosh) {
return nls.localize('terminalLinkHandler.followLinkAlt.mac', "Option + click to follow link");
label = nls.localize('terminalLinkHandler.followLinkAlt.mac', "Option + click");
} else {
return nls.localize('terminalLinkHandler.followLinkAlt', "Alt + click to follow link");
label = nls.localize('terminalLinkHandler.followLinkAlt', "Alt + click");
}
} else {
if (isMacintosh) {
label = nls.localize('terminalLinkHandler.followLinkCmd', "Cmd + click");
} else {
label = nls.localize('terminalLinkHandler.followLinkCtrl', "Ctrl + click");
}
}
if (isMacintosh) {
return nls.localize('terminalLinkHandler.followLinkCmd', "Cmd + click to follow link");
}
return nls.localize('terminalLinkHandler.followLinkCtrl', "Ctrl + click to follow link");
const message: IMarkdownString = new MarkdownString(`[Follow Link](${uri}) (${label})`, true);
message.uris = {
[uri]: URI.parse(uri).toJSON()
};
return message;
}
private get osPath(): IPath {
......
......@@ -4,6 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
export enum WidgetVerticalAlignment {
Bottom,
......@@ -20,7 +24,8 @@ export class TerminalWidgetManager implements IDisposable {
private readonly _messageListeners = new DisposableStore();
constructor(
terminalWrapper: HTMLElement
terminalWrapper: HTMLElement,
private readonly _openerService: IOpenerService
) {
this._container = document.createElement('div');
this._container.classList.add('terminal-widget-overlay');
......@@ -48,20 +53,22 @@ export class TerminalWidgetManager implements IDisposable {
mutationObserver.observe(this._xtermViewport, { attributes: true, attributeFilter: ['style'] });
}
public showMessage(left: number, y: number, text: string, verticalAlignment: WidgetVerticalAlignment = WidgetVerticalAlignment.Bottom): void {
public showMessage(left: number, y: number, text: IMarkdownString, verticalAlignment: WidgetVerticalAlignment = WidgetVerticalAlignment.Bottom): void {
if (!this._container) {
return;
}
dispose(this._messageWidget);
this._messageListeners.clear();
this._messageWidget = new MessageWidget(this._container, left, y, text, verticalAlignment);
this._messageWidget = new MessageWidget(this._container, left, y, text, verticalAlignment, this._openerService);
}
public closeMessage(): void {
this._messageListeners.clear();
if (this._messageWidget) {
this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget));
}
setTimeout(() => {
if (this._messageWidget && !this._messageWidget.mouseOver) {
this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget));
}
}, 50);
}
private _refreshHeight(): void {
......@@ -73,13 +80,16 @@ export class TerminalWidgetManager implements IDisposable {
}
class MessageWidget {
private _domNode: HTMLDivElement;
private _domNode: HTMLElement;
private _mouseOver = false;
private readonly _messageListeners = new DisposableStore();
public get left(): number { return this._left; }
public get y(): number { return this._y; }
public get text(): string { return this._text; }
public get text(): IMarkdownString { return this._text; }
public get domNode(): HTMLElement { return this._domNode; }
public get verticalAlignment(): WidgetVerticalAlignment { return this._verticalAlignment; }
public get mouseOver(): boolean { return this._mouseOver; }
public static fadeOut(messageWidget: MessageWidget): IDisposable {
let handle: any;
......@@ -98,10 +108,16 @@ class MessageWidget {
private _container: HTMLElement,
private _left: number,
private _y: number,
private _text: string,
private _verticalAlignment: WidgetVerticalAlignment
private _text: IMarkdownString,
private _verticalAlignment: WidgetVerticalAlignment,
private readonly _openerService: IOpenerService
) {
this._domNode = document.createElement('div');
this._domNode = renderMarkdown(this._text, {
actionHandler: {
callback: this._handleLinkClicked.bind(this),
disposeables: this._messageListeners
}
});
this._domNode.style.position = 'absolute';
this._domNode.style.left = `${_left}px`;
......@@ -114,7 +130,15 @@ class MessageWidget {
}
this._domNode.classList.add('terminal-message-widget', 'fadeIn');
this._domNode.textContent = _text;
this._domNode.addEventListener('mouseenter', () => {
this._mouseOver = true;
});
this._domNode.addEventListener('mouseleave', () => {
this._mouseOver = false;
this._messageListeners.add(MessageWidget.fadeOut(this));
});
this._container.appendChild(this._domNode);
}
......@@ -122,5 +146,11 @@ class MessageWidget {
if (this.domNode.parentElement === this._container) {
this._container.removeChild(this.domNode);
}
this._messageListeners.dispose();
}
private _handleLinkClicked(content: string) {
this._openerService.open(URI.parse(content));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册