mirror of
https://github.com/Microsoft/vscode
synced 2024-11-05 18:29:38 +00:00
#1927 Render multiline diagnostics in problems view
This commit is contained in:
parent
27fd58c206
commit
f9db8d784f
6 changed files with 162 additions and 94 deletions
|
@ -35,6 +35,11 @@
|
|||
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message .details {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.monaco-editor .marker-widget .descriptioncontainer .message .source,
|
||||
|
|
|
@ -80,20 +80,40 @@ class MessageWidget {
|
|||
|
||||
update({ source, message, relatedInformation, code }: IMarker): void {
|
||||
|
||||
if (source) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
dom.clearNode(this._messageBlock);
|
||||
let lastLineElement = this._messageBlock;
|
||||
for (const line of lines) {
|
||||
lastLineElement = document.createElement('div');
|
||||
lastLineElement.innerText = line;
|
||||
this._editor.applyFontInfo(lastLineElement);
|
||||
this._messageBlock.appendChild(lastLineElement);
|
||||
}
|
||||
if (source || code) {
|
||||
const detailsElement = document.createElement('span');
|
||||
dom.addClass(detailsElement, 'details');
|
||||
lastLineElement.appendChild(detailsElement);
|
||||
if (source) {
|
||||
const sourceElement = document.createElement('span');
|
||||
sourceElement.innerText = source;
|
||||
dom.addClass(sourceElement, 'source');
|
||||
detailsElement.appendChild(sourceElement);
|
||||
}
|
||||
if (code) {
|
||||
const codeElement = document.createElement('span');
|
||||
codeElement.innerText = `(${code})`;
|
||||
dom.addClass(codeElement, 'code');
|
||||
detailsElement.appendChild(codeElement);
|
||||
}
|
||||
} else {
|
||||
this._lines = 1;
|
||||
this._longestLineLength = message.length;
|
||||
}
|
||||
|
||||
dom.clearNode(this._relatedBlock);
|
||||
|
||||
if (isNonEmptyArray(relatedInformation)) {
|
||||
this._relatedBlock.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * .66)}px`;
|
||||
this._lines += 1;
|
||||
|
@ -120,26 +140,6 @@ class MessageWidget {
|
|||
}
|
||||
}
|
||||
|
||||
dom.clearNode(this._messageBlock);
|
||||
if (source) {
|
||||
const sourceElement = document.createElement('div');
|
||||
sourceElement.innerText = `[${source}] `;
|
||||
dom.addClass(sourceElement, 'source');
|
||||
this._editor.applyFontInfo(sourceElement);
|
||||
this._messageBlock.appendChild(sourceElement);
|
||||
}
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.innerText = message;
|
||||
this._editor.applyFontInfo(messageElement);
|
||||
this._messageBlock.appendChild(messageElement);
|
||||
if (code) {
|
||||
const codeElement = document.createElement('div');
|
||||
codeElement.innerText = ` [${code}]`;
|
||||
dom.addClass(codeElement, 'code');
|
||||
this._editor.applyFontInfo(codeElement);
|
||||
this._messageBlock.appendChild(codeElement);
|
||||
}
|
||||
|
||||
const fontInfo = this._editor.getConfiguration().fontInfo;
|
||||
const scrollWidth = Math.ceil(fontInfo.typicalFullwidthCharacterWidth * this._longestLineLength * 0.75);
|
||||
const scrollHeight = fontInfo.lineHeight * this._lines;
|
||||
|
|
|
@ -80,6 +80,14 @@ export class Marker {
|
|||
readonly relatedInformation: RelatedInformation[] = []
|
||||
) { }
|
||||
|
||||
private _lines: string[];
|
||||
get lines(): string[] {
|
||||
if (!this._lines) {
|
||||
this._lines = this.marker.message.split(/\r\n|\r|\n/g);
|
||||
}
|
||||
return this._lines;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return JSON.stringify({
|
||||
...this.marker,
|
||||
|
|
|
@ -15,7 +15,7 @@ import Messages from 'vs/workbench/parts/markers/electron-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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { QuickFixAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
@ -26,6 +26,7 @@ import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/marke
|
|||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
export type TreeElement = ResourceMarkers | Marker | RelatedInformation;
|
||||
|
||||
|
@ -36,12 +37,7 @@ interface IResourceMarkersTemplateData {
|
|||
}
|
||||
|
||||
interface IMarkerTemplateData {
|
||||
icon: HTMLElement;
|
||||
actionBar: ActionBar;
|
||||
source: HighlightedLabel;
|
||||
description: HighlightedLabel;
|
||||
lnCol: HTMLElement;
|
||||
code: HighlightedLabel;
|
||||
markerWidget: MarkerWidget;
|
||||
}
|
||||
|
||||
interface IRelatedInformationTemplateData {
|
||||
|
@ -78,7 +74,10 @@ const enum TemplateId {
|
|||
|
||||
export class VirtualDelegate implements IListVirtualDelegate<TreeElement> {
|
||||
|
||||
getHeight(): number {
|
||||
getHeight(element: TreeElement): number {
|
||||
if (element instanceof Marker) {
|
||||
return element.lines.length * 22;
|
||||
}
|
||||
return 22;
|
||||
}
|
||||
|
||||
|
@ -110,7 +109,7 @@ interface ResourceMarkersFilterData {
|
|||
|
||||
interface MarkerFilterData {
|
||||
type: FilterDataType.Marker;
|
||||
messageMatches: IMatch[];
|
||||
lineMatches: IMatch[][];
|
||||
sourceMatches: IMatch[];
|
||||
codeMatches: IMatch[];
|
||||
}
|
||||
|
@ -218,44 +217,91 @@ export class MarkerRenderer implements ITreeRenderer<Marker, MarkerFilterData, I
|
|||
|
||||
renderTemplate(container: HTMLElement): IMarkerTemplateData {
|
||||
const data: IMarkerTemplateData = Object.create(null);
|
||||
const actionsContainer = dom.append(container, dom.$('.actions'));
|
||||
data.actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider });
|
||||
data.icon = dom.append(container, dom.$('.icon'));
|
||||
data.source = new HighlightedLabel(dom.append(container, dom.$('')), false);
|
||||
data.description = new HighlightedLabel(dom.append(container, dom.$('.marker-description')), false);
|
||||
data.code = new HighlightedLabel(dom.append(container, dom.$('')), false);
|
||||
data.lnCol = dom.append(container, dom.$('span.marker-line'));
|
||||
data.markerWidget = new MarkerWidget(container, this.actionItemProvider, this.instantiationService);
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<Marker, MarkerFilterData>, _: number, templateData: IMarkerTemplateData): void {
|
||||
const marker = node.element.marker;
|
||||
const sourceMatches = node.filterData && node.filterData.sourceMatches || [];
|
||||
const messageMatches = node.filterData && node.filterData.messageMatches || [];
|
||||
const codeMatches = node.filterData && node.filterData.codeMatches || [];
|
||||
|
||||
templateData.icon.className = 'icon ' + MarkerRenderer.iconClassNameFor(marker);
|
||||
|
||||
templateData.source.set(marker.source, sourceMatches);
|
||||
dom.toggleClass(templateData.source.element, 'marker-source', !!marker.source);
|
||||
|
||||
templateData.actionBar.clear();
|
||||
const quickFixAction = this.instantiationService.createInstance(QuickFixAction, node.element);
|
||||
templateData.actionBar.push([quickFixAction], { icon: true, label: false });
|
||||
|
||||
templateData.description.set(marker.message, messageMatches);
|
||||
templateData.description.element.title = marker.message;
|
||||
|
||||
dom.toggleClass(templateData.code.element, 'marker-code', !!marker.code);
|
||||
templateData.code.set(marker.code, codeMatches);
|
||||
|
||||
templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn);
|
||||
templateData.markerWidget.render(node.element, node.filterData);
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IMarkerTemplateData): void {
|
||||
templateData.description.dispose();
|
||||
templateData.source.dispose();
|
||||
templateData.actionBar.dispose();
|
||||
templateData.markerWidget.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MarkerWidget extends Disposable {
|
||||
|
||||
private readonly actionBar: ActionBar;
|
||||
private readonly icon: HTMLElement;
|
||||
private readonly messageContainer: HTMLElement;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement, actionItemProvider: IActionItemProvider,
|
||||
private instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
const actionsContainer = dom.append(parent, dom.$('.actions'));
|
||||
this.actionBar = this._register(new ActionBar(actionsContainer, { actionItemProvider }));
|
||||
this.icon = dom.append(parent, dom.$('.icon'));
|
||||
this.messageContainer = dom.append(parent, dom.$('.marker-message'));
|
||||
this._register(toDisposable(() => this.disposables = dispose(this.disposables)));
|
||||
}
|
||||
|
||||
render(element: Marker, filterData: MarkerFilterData): void {
|
||||
const { marker, lines } = element;
|
||||
if (this.disposables.length) {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
dom.clearNode(this.messageContainer);
|
||||
|
||||
this.icon.className = 'icon ' + MarkerWidget.iconClassNameFor(marker);
|
||||
|
||||
this.actionBar.clear();
|
||||
const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element);
|
||||
this.actionBar.push([quickFixAction], { icon: true, label: false });
|
||||
this.onDidQuickFixesActionEnable(quickFixAction.enabled);
|
||||
quickFixAction.onDidChange(({ enabled }) => {
|
||||
if (!isUndefinedOrNull(enabled)) {
|
||||
this.onDidQuickFixesActionEnable(enabled);
|
||||
}
|
||||
}, this, this.disposables);
|
||||
|
||||
const lineMatches = filterData && filterData.lineMatches || [];
|
||||
let lastLineElement = this.messageContainer;
|
||||
for (let index = 0; index < lines.length; index++) {
|
||||
lastLineElement = dom.append(this.messageContainer, dom.$('.marker-message-line'));
|
||||
const highlightedLabel = new HighlightedLabel(lastLineElement, false);
|
||||
highlightedLabel.set(lines[index], lineMatches[index]);
|
||||
this.disposables.push(highlightedLabel);
|
||||
}
|
||||
|
||||
this.renderDetails(marker, filterData, lastLineElement);
|
||||
}
|
||||
|
||||
private onDidQuickFixesActionEnable(enabled: boolean): void {
|
||||
dom.toggleClass(this.icon, 'quickFix', enabled);
|
||||
}
|
||||
|
||||
private renderDetails(marker: IMarker, filterData: MarkerFilterData, parent: HTMLElement): void {
|
||||
dom.addClass(parent, 'details-container');
|
||||
const sourceMatches = filterData && filterData.sourceMatches || [];
|
||||
const codeMatches = filterData && filterData.codeMatches || [];
|
||||
|
||||
const source = new HighlightedLabel(dom.append(parent, dom.$('')), false);
|
||||
source.set(marker.source, sourceMatches);
|
||||
dom.toggleClass(source.element, 'marker-source', !!marker.source);
|
||||
|
||||
const code = new HighlightedLabel(dom.append(parent, dom.$('')), false);
|
||||
code.set(marker.code, codeMatches);
|
||||
dom.toggleClass(code.element, 'marker-code', !!marker.code);
|
||||
|
||||
const lnCol = dom.append(parent, dom.$('span.marker-line'));
|
||||
lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn);
|
||||
|
||||
this.disposables.push(...[source, code]);
|
||||
}
|
||||
|
||||
private static iconClassNameFor(element: IMarker): string {
|
||||
|
@ -369,12 +415,15 @@ export class Filter implements ITreeFilter<TreeElement, FilterData> {
|
|||
return true;
|
||||
}
|
||||
|
||||
const messageMatches = FilterOptions._messageFilter(this.options.textFilter, marker.marker.message);
|
||||
const lineMatches: IMatch[][] = [];
|
||||
for (const line of marker.lines) {
|
||||
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);
|
||||
|
||||
if (messageMatches || sourceMatches || codeMatches) {
|
||||
return { visibility: true, data: { type: FilterDataType.Marker, messageMatches: messageMatches || [], sourceMatches: sourceMatches || [], codeMatches: codeMatches || [] } };
|
||||
if (sourceMatches || codeMatches || lineMatches.some(lineMatch => lineMatch.length > 0)) {
|
||||
return { visibility: true, data: { type: FilterDataType.Marker, lineMatches, sourceMatches: sourceMatches || [], codeMatches: codeMatches || [] } };
|
||||
}
|
||||
|
||||
return parentVisibility;
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents {
|
||||
display: flex;
|
||||
line-height: 22px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.hc-black .markers-panel .markers-panel-container .tree-container .monaco-tl-contents {
|
||||
|
@ -108,25 +109,29 @@
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-description {
|
||||
margin-right: 5px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
white-space: pre;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source,
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-code {
|
||||
margin-right: 5px;
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-message .details-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source:before,
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-code:before {
|
||||
content: '[';
|
||||
content: '(';
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source:after,
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-code:after {
|
||||
content: ']';
|
||||
content: ')';
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-message,
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container .marker-source,
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .details-container .marker-line {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source,
|
||||
|
@ -143,7 +148,6 @@
|
|||
|
||||
.markers-panel .monaco-tl-contents > .icon {
|
||||
height: 22px;
|
||||
margin-right: 6px;
|
||||
flex: 0 0 16px;
|
||||
}
|
||||
|
||||
|
@ -180,17 +184,19 @@
|
|||
background: url('lightbulb-dark.svg') center/80% no-repeat;
|
||||
}
|
||||
|
||||
.markers-panel .monaco-tl-contents > .actions {
|
||||
flex: 0 0 16px;
|
||||
}
|
||||
|
||||
.markers-panel .monaco-tl-contents > .actions .monaco-action-bar {
|
||||
.markers-panel .monaco-tl-contents .actions .monaco-action-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.markers-panel .monaco-tl-row:hover .monaco-tl-contents > .actions .monaco-action-bar,
|
||||
.markers-panel .monaco-tl-row.selected .monaco-tl-contents > .actions .monaco-action-bar,
|
||||
.markers-panel .monaco-tl-row.focused .monaco-tl-contents > .actions .monaco-action-bar {
|
||||
.markers-panel .monaco-list-row:hover .monaco-tl-contents > .icon.quickFix,
|
||||
.markers-panel .monaco-list-row.selected .monaco-tl-contents > .icon.quickFix,
|
||||
.markers-panel .monaco-list-row.focused .monaco-tl-contents > .icon.quickFix {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.markers-panel .monaco-list-row:hover .monaco-tl-contents .actions .monaco-action-bar,
|
||||
.markers-panel .monaco-list-row.selected .monaco-tl-contents .actions .monaco-action-bar,
|
||||
.markers-panel .monaco-list-row.focused .monaco-tl-contents .actions .monaco-action-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ export default class Messages {
|
|||
public static MARKERS_PANEL_SINGLE_UNKNOWN_LABEL: string = nls.localize('markers.panel.single.unknown.label', "1 Unknown");
|
||||
public static readonly MARKERS_PANEL_MULTIPLE_UNKNOWNS_LABEL = (noOfUnknowns: number): string => { return nls.localize('markers.panel.multiple.unknowns.label', "{0} Unknowns", '' + noOfUnknowns); };
|
||||
|
||||
public static readonly MARKERS_PANEL_AT_LINE_COL_NUMBER = (ln: number, col: number): string => { return nls.localize('markers.panel.at.ln.col.number', "({0}, {1})", '' + ln, '' + col); };
|
||||
public static readonly MARKERS_PANEL_AT_LINE_COL_NUMBER = (ln: number, col: number): string => { return nls.localize('markers.panel.at.ln.col.number', "[{0}, {1}]", '' + ln, '' + col); };
|
||||
|
||||
public static readonly MARKERS_TREE_ARIA_LABEL_RESOURCE = (noOfProblems: number, fileName: string, folder: string): string => { return nls.localize('problems.tree.aria.label.resource', "{0} problems in file {1} of folder {2}", noOfProblems, fileName, folder); };
|
||||
public static readonly MARKERS_TREE_ARIA_LABEL_MARKER = (marker: Marker): string => {
|
||||
|
|
Loading…
Reference in a new issue