提交 38549a60 编写于 作者: P Pine Wu

Allow link on Diagnostic#code for #11847

Squashed commit of the following:

commit 76689c23011bf960f5ba3e199659e8180455466d
Author: Pine Wu <octref@gmail.com>
Date:   Fri Jan 24 10:19:44 2020 +0100

    Clarify API

commit 88d772d62da5d912b54285aa52dc6d6a108ca587
Author: Pine Wu <octref@gmail.com>
Date:   Fri Jan 24 10:17:28 2020 +0100

    Tooltip

commit 11ef8012ae3e560d6db6fff26ba3c258e7eae4a6
Author: Pine Wu <octref@gmail.com>
Date:   Thu Jan 23 16:50:28 2020 +0100

    Hover to show link color and use cmd/alt click

commit 38655a70b3eed56b99e8018fb5c79bd41b6c4f56
Author: Pine Wu <octref@gmail.com>
Date:   Thu Jan 23 15:21:13 2020 +0100

    Add hack to always render underline a bit below

commit 959f6b13bf81885cc370f6099e5123142089599e
Author: Pine Wu <octref@gmail.com>
Date:   Thu Jan 23 11:32:37 2020 +0100

    Fix compile error

commit b5c50e872935e74503451bd2801227f82b8410f4
Author: Pine Wu <octref@gmail.com>
Date:   Thu Jan 23 11:19:52 2020 +0100

    Bring code link everywhere for Diagnostics

commit f88cd4cc4e2064fd96853373e26c12670161bf60
Author: Pine Wu <octref@gmail.com>
Date:   Wed Jan 22 17:24:54 2020 +0100

    Add Diagnostic#code2 and render it in hover/inline/panel

commit 2a13e3e4f62fd3707f2c03c1e814a8fdc557c367
Author: Pine Wu <octref@gmail.com>
Date:   Wed Jan 22 13:49:42 2020 +0100

    WIP
上级 e4eec1db
......@@ -27,6 +27,8 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { Action } from 'vs/base/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { isEqual } from 'vs/base/common/resources';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
class MarkerModel {
......@@ -209,7 +211,9 @@ export class MarkerController implements IEditorContribution {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IOpenerService private readonly _openerService: IOpenerService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
this._editor = editor;
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
......@@ -243,7 +247,7 @@ export class MarkerController implements IEditorContribution {
new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }),
new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } })
];
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService);
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService, this._openerService, this._configurationService);
this._widgetVisible.set(true);
this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose);
......
......@@ -26,6 +26,11 @@ import { IAction } from 'vs/base/common/actions';
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { OperatingSystem, OS } from 'vs/base/common/platform';
type ModifierKey = 'meta' | 'ctrl' | 'alt';
class MessageWidget {
......@@ -39,7 +44,16 @@ class MessageWidget {
private readonly _relatedDiagnostics = new WeakMap<HTMLElement, IRelatedInformation>();
private readonly _disposables: DisposableStore = new DisposableStore();
constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) {
private _clickModifierKey: ModifierKey;
private _codeLink?: HTMLElement;
constructor(
parent: HTMLElement,
editor: ICodeEditor,
onRelatedInformation: (related: IRelatedInformation) => void,
private readonly _openerService: IOpenerService,
private readonly _configurationService: IConfigurationService
) {
this._editor = editor;
const domNode = document.createElement('div');
......@@ -74,6 +88,16 @@ class MessageWidget {
domNode.style.top = `-${e.scrollTop}px`;
}));
this._disposables.add(this._scrollable);
this._clickModifierKey = this._getClickModifierKey();
this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('editor.multiCursorModifier')) {
this._clickModifierKey = this._getClickModifierKey();
if (this._codeLink) {
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
}
}
}));
}
dispose(): void {
......@@ -81,12 +105,20 @@ class MessageWidget {
}
update({ source, message, relatedInformation, code }: IMarker): void {
let sourceAndCodeLength = (source?.length || 0) + '()'.length;
if (code) {
if (typeof code === 'string') {
sourceAndCodeLength += code.length;
} else {
sourceAndCodeLength += code.value.length;
}
}
const lines = message.split(/\r\n|\r|\n/g);
this._lines = lines.length;
this._longestLineLength = 0;
for (const line of lines) {
this._longestLineLength = Math.max(line.length, this._longestLineLength);
this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength);
}
dom.clearNode(this._messageBlock);
......@@ -111,10 +143,28 @@ class MessageWidget {
detailsElement.appendChild(sourceElement);
}
if (code) {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
if (typeof code === 'string') {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
this._codeLink.setAttribute('href', `${code.link.toString()}`);
this._codeLink.onclick = (e) => {
e.preventDefault();
if ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey)) {
this._openerService.open(code.link);
e.stopPropagation();
}
};
const codeElement = dom.append(this._codeLink, dom.$('span'));
codeElement.innerText = code.value;
detailsElement.appendChild(this._codeLink);
}
}
}
......@@ -161,6 +211,31 @@ class MessageWidget {
getHeightInLines(): number {
return Math.min(17, this._lines);
}
private _getClickModifierKey(): ModifierKey {
const value = this._configurationService.getValue<'ctrlCmd' | 'alt'>('editor.multiCursorModifier');
if (value === 'ctrlCmd') {
return 'alt';
} else {
if (OS === OperatingSystem.Macintosh) {
return 'meta';
} else {
return 'ctrl';
}
}
}
private _getCodelinkTooltip(): string {
const tooltipLabel = nls.localize('links.navigate.follow', 'Follow link');
const tooltipKeybinding = this._clickModifierKey === 'ctrl'
? nls.localize('links.navigate.kb.meta', 'ctrl + click')
:
this._clickModifierKey === 'meta'
? OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.meta.mac', 'cmd + click') : nls.localize('links.navigate.kb.meta', 'ctrl + click')
: OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.alt.mac', 'option + click') : nls.localize('links.navigate.kb.alt', 'alt + click');
return `${tooltipLabel} (${tooltipKeybinding})`;
}
}
export class MarkerNavigationWidget extends PeekViewWidget {
......@@ -180,7 +255,9 @@ export class MarkerNavigationWidget extends PeekViewWidget {
constructor(
editor: ICodeEditor,
private readonly actions: ReadonlyArray<IAction>,
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _openerService: IOpenerService,
private readonly _configurationService: IConfigurationService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
this._severity = MarkerSeverity.Warning;
......@@ -250,7 +327,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
this._container = document.createElement('div');
container.appendChild(this._container);
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related));
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService, this._configurationService);
this._disposables.add(this._message);
}
......@@ -329,8 +406,9 @@ export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationI
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));
registerThemingParticipant((theme, collector) => {
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`);
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${linkFg}; }`);
collector.addRule(`.monaco-editor .marker-widget a.code-link span:hover { color: ${linkFg}; }`);
}
});
......@@ -45,10 +45,27 @@
}
.monaco-editor .marker-widget .descriptioncontainer .message .source,
.monaco-editor .marker-widget .descriptioncontainer .message .code {
.monaco-editor .marker-widget .descriptioncontainer .message span.code {
opacity: 0.6;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link {
opacity: 0.6;
color: inherit;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:before {
content: '(';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:after {
content: ')';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
text-underline-position: under;
}
.monaco-editor .marker-widget .descriptioncontainer .filename {
cursor: pointer;
}
......@@ -110,3 +110,20 @@
font-size: inherit;
vertical-align: middle;
}
.monaco-editor-hover .hover-contents a.code-link:before {
content: '(';
}
.monaco-editor-hover .hover-contents a.code-link:after {
content: ')';
}
.monaco-editor-hover .hover-contents a.code-link {
color: inherit;
}
.monaco-editor-hover .hover-contents a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
text-underline-position: under;
}
......@@ -27,6 +27,7 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDeco
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ModesHoverController implements IEditorContribution {
......@@ -66,7 +67,8 @@ export class ModesHoverController implements IEditorContribution {
@IModeService private readonly _modeService: IModeService,
@IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService
@IThemeService private readonly _themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
this._isMouseDown = false;
this._hoverClicked = false;
......@@ -205,7 +207,7 @@ export class ModesHoverController implements IEditorContribution {
}
private _createHoverWidgets() {
this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService);
this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService, this._configurationService);
this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
}
......
......@@ -22,7 +22,7 @@ import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { coalesce, isNonEmptyArray, asArray } from 'vs/base/common/arrays';
import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { basename } from 'vs/base/common/resources';
......@@ -39,6 +39,9 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { OperatingSystem, OS } from 'vs/base/common/platform';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
const $ = dom.$;
......@@ -191,6 +194,8 @@ const markerCodeActionTrigger: CodeActionTrigger = {
filter: { include: CodeActionKind.QuickFix }
};
type ModifierKey = 'meta' | 'ctrl' | 'alt';
export class ModesContentHoverWidget extends ContentHoverWidget {
static readonly ID = 'editor.contrib.modesContentHoverWidget';
......@@ -204,6 +209,9 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private _shouldFocus: boolean;
private _colorPicker: ColorPickerWidget | null;
private _clickModifierKey: ModifierKey;
private _codeLink?: HTMLElement;
private readonly renderDisposable = this._register(new MutableDisposable<IDisposable>());
constructor(
......@@ -213,6 +221,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private readonly _keybindingService: IKeybindingService,
private readonly _modeService: IModeService,
private readonly _openerService: IOpenerService = NullOpenerService,
private readonly _configurationService: IConfigurationService
) {
super(ModesContentHoverWidget.ID, editor);
......@@ -249,6 +258,16 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._renderMessages(this._lastRange, this._messages);
}
}));
this._clickModifierKey = this._getClickModifierKey();
this._register((this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('editor.multiCursorModifier')) {
this._clickModifierKey = this._getClickModifierKey();
if (this._codeLink) {
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
}
}
})));
}
dispose(): void {
......@@ -500,10 +519,38 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
messageElement.innerText = message;
if (source || code) {
const detailsElement = dom.append(markerElement, $('span'));
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
if (typeof code === 'string') {
const detailsElement = dom.append(markerElement, $('span'));
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
} else {
if (code) {
const sourceAndCodeElement = $('span');
if (source) {
const sourceElement = dom.append(sourceAndCodeElement, $('span'));
sourceElement.innerText = source;
}
this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
this._codeLink.setAttribute('href', code.link.toString());
this._codeLink.onclick = (e) => {
e.preventDefault();
if ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey)) {
this._openerService.open(code.link);
e.stopPropagation();
}
};
const codeElement = dom.append(this._codeLink, $('span'));
codeElement.innerText = code.value;
const detailsElement = dom.append(markerElement, sourceAndCodeElement);
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
}
}
}
if (isNonEmptyArray(relatedInformation)) {
......@@ -624,6 +671,31 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
className: 'hoverHighlight'
});
private _getClickModifierKey(): ModifierKey {
const value = this._configurationService.getValue<'ctrlCmd' | 'alt'>('editor.multiCursorModifier');
if (value === 'ctrlCmd') {
return 'alt';
} else {
if (OS === OperatingSystem.Macintosh) {
return 'meta';
} else {
return 'ctrl';
}
}
}
private _getCodelinkTooltip(): string {
const tooltipLabel = nls.localize('links.navigate.follow', 'Follow link');
const tooltipKeybinding = this._clickModifierKey === 'ctrl'
? nls.localize('links.navigate.kb.meta', 'ctrl + click')
:
this._clickModifierKey === 'meta'
? OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.meta.mac', 'cmd + click') : nls.localize('links.navigate.kb.meta', 'ctrl + click')
: OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.alt.mac', 'option + click') : nls.localize('links.navigate.kb.alt', 'alt + click');
return `${tooltipLabel} (${tooltipKeybinding})`;
}
}
function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean {
......@@ -648,3 +720,11 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean {
}
return true;
}
registerThemingParticipant((theme, collector) => {
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`);
}
});
......@@ -1176,7 +1176,10 @@ declare namespace monaco.editor {
owner: string;
resource: Uri;
severity: MarkerSeverity;
code?: string;
code?: string | {
value: string;
link: Uri;
};
message: string;
source?: string;
startLineNumber: number;
......@@ -1191,7 +1194,10 @@ declare namespace monaco.editor {
* A structure defining a problem/warning/etc.
*/
export interface IMarkerData {
code?: string;
code?: string | {
value: string;
link: Uri;
};
severity: MarkerSeverity;
message: string;
source?: string;
......
......@@ -87,7 +87,7 @@ export namespace MarkerSeverity {
* A structure defining a problem/warning/etc.
*/
export interface IMarkerData {
code?: string;
code?: string | { value: string; link: URI };
severity: MarkerSeverity;
message: string;
source?: string;
......@@ -108,7 +108,7 @@ export interface IMarker {
owner: string;
resource: URI;
severity: MarkerSeverity;
code?: string;
code?: string | { value: string; link: URI };
message: string;
source?: string;
startLineNumber: number;
......@@ -140,7 +140,11 @@ export namespace IMarkerData {
result.push(emptyString);
}
if (markerData.code) {
result.push(markerData.code.replace('¦', '\¦'));
if (typeof markerData.code === 'string') {
result.push(markerData.code.replace('¦', '\¦'));
} else {
result.push(markerData.code.value.replace('¦', '\¦'));
}
} else {
result.push(emptyString);
}
......
......@@ -1446,4 +1446,26 @@ declare module 'vscode' {
}
//#endregion
//#region Diagnostic links https://github.com/microsoft/vscode/issues/11847
export interface Diagnostic {
/**
* Will be merged into `Diagnostic#code`
*/
code2?: {
/**
* A code or identifier for this diagnostic.
* Should be used for later processing, e.g. when providing [code actions](#CodeActionContext).
*/
value: string | number;
/**
* A link to a URI with more information about the diagnostic error.
*/
link: Uri;
}
}
//#endregion
}
......@@ -54,6 +54,9 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
relatedInformation.resource = URI.revive(relatedInformation.resource);
}
}
if (marker.code && typeof marker.code !== 'string') {
marker.code.link = URI.revive(marker.code.link);
}
}
}
this._markerService.changeOne(owner, URI.revive(uri), markers);
......
......@@ -127,11 +127,19 @@ export namespace DiagnosticTag {
export namespace Diagnostic {
export function from(value: vscode.Diagnostic): IMarkerData {
let code: string | { value: string; link: URI } | undefined = isString(value.code) || isNumber(value.code) ? String(value.code) : undefined;
if (value.code2) {
code = {
value: String(value.code2.value),
link: value.code2.link
};
}
return {
...Range.from(value.range),
message: value.message,
source: value.source,
code: isString(value.code) || isNumber(value.code) ? String(value.code) : undefined,
code,
severity: DiagnosticSeverity.from(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined,
......@@ -141,7 +149,7 @@ export namespace Diagnostic {
export function to(value: IMarkerData): vscode.Diagnostic {
const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity));
res.source = value.source;
res.code = value.code;
res.code = isString(value.code) ? value.code : value.code?.value;
res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to);
res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to));
return res;
......
......@@ -14,7 +14,7 @@ import { ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/contri
import Messages from 'vs/workbench/contrib/markers/browser/messages';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { QuickFixAction, QuickFixActionViewItem } from 'vs/workbench/contrib/markers/browser/markersViewActions';
......@@ -43,6 +43,10 @@ import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/commo
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { CodeActionTriggerType } from 'vs/editor/common/modes';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { OS, OperatingSystem } from 'vs/base/common/platform';
export type TreeElement = ResourceMarkers | Marker | RelatedInformation;
......@@ -217,14 +221,16 @@ export class MarkerRenderer implements ITreeRenderer<Marker, MarkerFilterData, I
constructor(
private readonly markersViewState: MarkersViewModel,
@IInstantiationService protected instantiationService: IInstantiationService
@IInstantiationService protected instantiationService: IInstantiationService,
@IOpenerService protected openerService: IOpenerService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) { }
templateId = TemplateId.Marker;
renderTemplate(container: HTMLElement): IMarkerTemplateData {
const data: IMarkerTemplateData = Object.create(null);
data.markerWidget = new MarkerWidget(container, this.markersViewState, this.instantiationService);
data.markerWidget = new MarkerWidget(container, this.markersViewState, this.openerService, this._configurationService, this.instantiationService);
return data;
}
......@@ -238,6 +244,8 @@ export class MarkerRenderer implements ITreeRenderer<Marker, MarkerFilterData, I
}
type ModifierKey = 'meta' | 'ctrl' | 'alt';
class MarkerWidget extends Disposable {
private readonly actionBar: ActionBar;
......@@ -246,18 +254,25 @@ class MarkerWidget extends Disposable {
private readonly messageAndDetailsContainer: HTMLElement;
private readonly disposables = this._register(new DisposableStore());
private _clickModifierKey: ModifierKey;
private _codeLink?: HTMLElement;
constructor(
private parent: HTMLElement,
private readonly markersViewModel: MarkersViewModel,
instantiationService: IInstantiationService
private readonly _openerService: IOpenerService,
private readonly _configurationService: IConfigurationService,
_instantiationService: IInstantiationService
) {
super();
this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), {
actionViewItemProvider: (action: QuickFixAction) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionViewItem, action) : undefined
actionViewItemProvider: (action: QuickFixAction) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined
}));
this.icon = dom.append(parent, dom.$(''));
this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions'))));
this.messageAndDetailsContainer = dom.append(parent, dom.$('.marker-message-details-container'));
this._clickModifierKey = this._getClickModifierKey();
}
render(element: Marker, filterData: MarkerFilterData | undefined): void {
......@@ -273,6 +288,15 @@ class MarkerWidget extends Disposable {
this.renderMessageAndDetails(element, filterData);
this.disposables.add(dom.addDisposableListener(this.parent, dom.EventType.MOUSE_OVER, () => this.markersViewModel.onMarkerMouseHover(element)));
this.disposables.add(dom.addDisposableListener(this.parent, dom.EventType.MOUSE_LEAVE, () => this.markersViewModel.onMarkerMouseLeave(element)));
this.disposables.add((this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('editor.multiCursorModifier')) {
this._clickModifierKey = this._getClickModifierKey();
if (this._codeLink) {
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
}
}
})));
}
private renderQuickfixActionbar(marker: Marker): void {
......@@ -335,15 +359,63 @@ class MarkerWidget extends Disposable {
source.set(marker.source, sourceMatches);
if (marker.code) {
const code = new HighlightedLabel(dom.append(parent, dom.$('.marker-code')), false);
const codeMatches = filterData && filterData.codeMatches || [];
code.set(marker.code, codeMatches);
if (typeof marker.code === 'string') {
const code = new HighlightedLabel(dom.append(parent, dom.$('.marker-code')), false);
const codeMatches = filterData && filterData.codeMatches || [];
code.set(marker.code, codeMatches);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('title', this._getCodelinkTooltip());
const codeUri = marker.code.link;
const codeLink = codeUri.toString();
dom.append(parent, this._codeLink);
this._codeLink.setAttribute('href', codeLink);
this._codeLink.onclick = (e) => {
e.preventDefault();
if ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey)) {
this._openerService.open(codeUri);
e.stopPropagation();
}
};
const code = new HighlightedLabel(dom.append(this._codeLink, dom.$('.marker-code')), false);
const codeMatches = filterData && filterData.codeMatches || [];
code.set(marker.code.value, codeMatches);
}
}
}
const lnCol = dom.append(parent, dom.$('span.marker-line'));
lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn);
}
private _getClickModifierKey(): ModifierKey {
const value = this._configurationService.getValue<'ctrlCmd' | 'alt'>('editor.multiCursorModifier');
if (value === 'ctrlCmd') {
return 'alt';
} else {
if (OS === OperatingSystem.Macintosh) {
return 'meta';
} else {
return 'ctrl';
}
}
}
private _getCodelinkTooltip(): string {
const tooltipLabel = localize('links.navigate.follow', 'Follow link');
const tooltipKeybinding = this._clickModifierKey === 'ctrl'
? localize('links.navigate.kb.meta', 'ctrl + click')
:
this._clickModifierKey === 'meta'
? OS === OperatingSystem.Macintosh ? localize('links.navigate.kb.meta.mac', 'cmd + click') : localize('links.navigate.kb.meta', 'ctrl + click')
: OS === OperatingSystem.Macintosh ? localize('links.navigate.kb.alt.mac', 'option + click') : localize('links.navigate.kb.alt', 'alt + click');
return `${tooltipLabel} (${tooltipKeybinding})`;
}
}
export class RelatedInformationRenderer implements ITreeRenderer<RelatedInformation, RelatedInformationFilterData, IRelatedInformationTemplateData> {
......@@ -451,7 +523,14 @@ export class Filter implements ITreeFilter<TreeElement, FilterData> {
lineMatches.push(FilterOptions._messageFilter(this.options.textFilter, line) || []);
}
const sourceMatches = marker.marker.source && FilterOptions._filter(this.options.textFilter, marker.marker.source);
const codeMatches = marker.marker.code && FilterOptions._filter(this.options.textFilter, marker.marker.code);
let codeMatches: IMatch[] | null | undefined;
if (marker.marker.code) {
const codeText = typeof marker.marker.code === 'string' ? marker.marker.code : marker.marker.code.value;
codeMatches = FilterOptions._filter(this.options.textFilter, codeText);
} else {
codeMatches = undefined;
}
if (sourceMatches || codeMatches || lineMatches.some(lineMatch => lineMatch.length > 0)) {
return { visibility: true, data: { type: FilterDataType.Marker, lineMatches, sourceMatches: sourceMatches || [], codeMatches: codeMatches || [] } };
......@@ -760,3 +839,10 @@ export class ResourceDragAndDrop implements ITreeDragAndDrop<TreeElement> {
drop(data: IDragAndDropData, targetElement: TreeElement, targetIndex: number, originalEvent: DragEvent): void {
}
}
registerThemingParticipant((theme, collector) => {
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container a.code-link span:hover { color: ${linkFg}; }`);
}
});
......@@ -133,6 +133,14 @@
display: flex;
}
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container a.code-link {
color: inherit;
}
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container a.code-link .monaco-highlighted-label {
text-decoration: underline;
text-underline-position: under;
}
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-code:before {
content: '(';
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册