From 07e5e714c7e57ec4aec09ec4e5941825d01cda37 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Mar 2018 13:45:21 +0530 Subject: [PATCH] #10271 Show diagnostics with children in problems panel --- .../markers/electron-browser/markersModel.ts | 129 ++++++++++---- .../markers/electron-browser/markersPanel.ts | 30 ++-- .../electron-browser/markersTreeViewer.ts | 164 ++++++++++++------ .../markers/electron-browser/messages.ts | 26 +-- .../electron-browser/markersModel.test.ts | 34 ++-- 5 files changed, 247 insertions(+), 136 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index 6b785692bec..6249d008567 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -7,26 +7,32 @@ import * as paths from 'vs/base/common/paths'; import URI from 'vs/base/common/uri'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { IFilter, IMatch, or, matchesContiguousSubString, matchesPrefix, matchesFuzzy } from 'vs/base/common/filters'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; import { Schemas } from 'vs/base/common/network'; import { groupBy, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; -export class Resource { +export abstract class NodeWithId { + constructor(readonly id: string) { } +} + +export class ResourceData extends NodeWithId { private _name: string = null; private _path: string = null; - filteredMarkersCount: number = 0; + count: number = 0; uriMatches: IMatch[] = []; constructor( + id: string, readonly uri: URI, - readonly markers: Marker[], + readonly data: T[] ) { - markers.sort(Marker.compare); + super(id); + this.count = this.data.length; } public get path(): string { @@ -42,10 +48,21 @@ export class Resource { } return this._name; } +} - static compare(a: Resource, b: Resource): number { - let [firstMarkerOfA] = a.markers; - let [firstMarkerOfB] = b.markers; +export class ResourceMarkers extends ResourceData { + + constructor( + uri: URI, + markers: Marker[] + ) { + const sorted = markers.sort(Marker.compare); + super(uri.toString(), uri, sorted); + } + + static compare(a: ResourceMarkers, b: ResourceMarkers): number { + let [firstMarkerOfA] = a.data; + let [firstMarkerOfB] = b.data; let res = 0; if (firstMarkerOfA && firstMarkerOfB) { res = MarkerSeverity.compare(firstMarkerOfA.raw.severity, firstMarkerOfB.raw.severity); @@ -57,16 +74,19 @@ export class Resource { } } -export class Marker { +export class Marker extends NodeWithId { isSelected: boolean = false; messageMatches: IMatch[] = []; sourceMatches: IMatch[] = []; + resourceRelatedInformation: ResourceData[] = []; constructor( - readonly id: string, + id: string, readonly raw: IMarker, - ) { } + ) { + super(id); + } public get resource(): URI { return this.raw.resource; @@ -93,6 +113,15 @@ export class Marker { } } +export class RelatedInformation extends NodeWithId { + constructor( + id: string, + readonly relatedInformation: IRelatedInformation, + public matches: IMatch[]) { + super(id); + } +} + export class FilterOptions { static readonly _filter: IFilter = or(matchesPrefix, matchesContiguousSubString); @@ -126,12 +155,12 @@ export class FilterOptions { export class MarkersModel { - private _cachedSortedResources: Resource[]; - private _markersByResource: Map; + private _cachedSortedResources: ResourceMarkers[]; + private _markersByResource: Map; private _filterOptions: FilterOptions; constructor(markers: IMarker[] = []) { - this._markersByResource = new Map(); + this._markersByResource = new Map(); this._filterOptions = new FilterOptions(); for (const group of groupBy(markers, MarkersModel._compareMarkersByUri)) { @@ -154,16 +183,16 @@ export class MarkersModel { return this._filterOptions; } - public get resources(): Resource[] { + public get resources(): ResourceMarkers[] { if (!this._cachedSortedResources) { - this._cachedSortedResources = values(this._markersByResource).sort(Resource.compare); + this._cachedSortedResources = values(this._markersByResource).sort(ResourceMarkers.compare); } return this._cachedSortedResources; } - public forEachFilteredResource(callback: (resource: Resource) => any) { + public forEachFilteredResource(callback: (resource: ResourceMarkers) => any) { this._markersByResource.forEach(resource => { - if (resource.filteredMarkersCount > 0) { + if (resource.count > 0) { callback(resource); } }); @@ -172,7 +201,7 @@ export class MarkersModel { public hasFilteredResources(): boolean { let res = false; this._markersByResource.forEach(resource => { - res = res || resource.filteredMarkersCount > 0; + res = res || resource.count > 0; }); return res; } @@ -189,8 +218,8 @@ export class MarkersModel { let total = 0; let filtered = 0; this._markersByResource.forEach(resource => { - total += resource.markers.length; - filtered += resource.filteredMarkersCount; + total += resource.data.length; + filtered += resource.count; }); return { total, filtered }; @@ -212,13 +241,17 @@ export class MarkersModel { if (!this._filterOptions.hasFilters()) { // reset all filters/matches this._markersByResource.forEach(resource => { - resource.filteredMarkersCount = resource.markers.length; + resource.count = resource.data.length; resource.uriMatches = []; - for (const marker of resource.markers) { + for (const marker of resource.data) { marker.isSelected = true; marker.messageMatches = []; marker.sourceMatches = []; + marker.resourceRelatedInformation.forEach(r => { + r.uriMatches = []; + r.data.forEach(d => d.matches = []); + }); } }); } else { @@ -226,21 +259,25 @@ export class MarkersModel { this._markersByResource.forEach(resource => { resource.uriMatches = this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, paths.basename(resource.uri.fsPath)) : []; - resource.filteredMarkersCount = 0; + resource.count = 0; - for (const marker of resource.markers) { + for (const marker of resource.data) { marker.messageMatches = this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, marker.raw.message) : []; marker.sourceMatches = marker.raw.source && this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, marker.raw.source) : []; marker.isSelected = this.filterMarker(marker.raw); if (marker.isSelected) { - resource.filteredMarkersCount += 1; + resource.count += 1; } + marker.resourceRelatedInformation.forEach(r => { + r.uriMatches = this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, paths.basename(r.uri.fsPath)) : []; + r.data.forEach(d => d.matches = this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, d.relatedInformation.message) : []); + }); } }); } } - private createResource(uri: URI, rawMarkers: IMarker[]): Resource { + private createResource(uri: URI, rawMarkers: IMarker[]): ResourceMarkers { let markers: Marker[] = []; let filteredCount = 0; @@ -252,19 +289,36 @@ export class MarkersModel { } } - const resource = new Resource(uri, markers); - resource.filteredMarkersCount = filteredCount; + const resource = new ResourceMarkers(uri, markers); + resource.count = filteredCount; resource.uriMatches = this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, paths.basename(uri.fsPath)) : []; return resource; } - private createMarker(marker: IMarker, index: number, uri: string): Marker { - const res = new Marker(uri + index, marker); - res.messageMatches = this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, marker.message) : []; - res.sourceMatches = marker.source && this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, marker.source) : []; - res.isSelected = this.filterMarker(marker); - return res; + private createMarker(rawMarker: IMarker, index: number, uri: string): Marker { + const marker = new Marker(uri + index, rawMarker); + marker.messageMatches = this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, rawMarker.message) : []; + marker.sourceMatches = rawMarker.source && this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, rawMarker.source) : []; + marker.isSelected = this.filterMarker(rawMarker); + if (rawMarker.relatedInformation) { + marker.resourceRelatedInformation = groupBy(rawMarker.relatedInformation, MarkersModel._compareMarkersByUri) + .map(group => { + const id = uri + index + uri + index.toString(); + const r = new ResourceData( + id, + group[0].resource, + group.map((relatedInformation, index) => + new RelatedInformation( + id + index, + relatedInformation, + this._filterOptions.hasFilters() ? FilterOptions._fuzzyFilter(this._filterOptions.filter, relatedInformation.message) : [] + ))); + r.uriMatches = this._filterOptions.hasFilters() ? FilterOptions._filter(this._filterOptions.filter, paths.basename(r.uri.fsPath)) : []; + return r; + }); + } + return marker; } private filterMarker(marker: IMarker): boolean { @@ -292,6 +346,11 @@ export class MarkersModel { if (!!marker.source && !!FilterOptions._filter(this._filterOptions.filter, marker.source)) { return true; } + if (!!marker.relatedInformation && marker.relatedInformation.some(r => + !!FilterOptions._filter(this._filterOptions.filter, paths.basename(r.resource.fsPath)) || ! + !FilterOptions._filter(this._filterOptions.filter, r.message))) { + return true; + } return false; } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 1c1d7e03e04..7eece96e64d 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -18,7 +18,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { Panel } from 'vs/workbench/browser/panel'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; -import { Marker, Resource } from 'vs/workbench/parts/markers/electron-browser/markersModel'; +import { Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/parts/markers/electron-browser/markersModel'; import { Controller } from 'vs/workbench/parts/markers/electron-browser/markersTreeController'; import * as Viewer from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -134,25 +134,20 @@ export class MarkersPanel extends Panel { } public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { - if (element instanceof Marker) { - const marker: Marker = element; - /* __GDPR__ - "problems.marker.opened" : { - "source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('problems.marker.opened', { source: marker.raw.source }); + const { resource, selection } = element instanceof Marker ? { resource: element.resource, selection: element.range } : + element instanceof RelatedInformation ? { resource: element.relatedInformation.resource, selection: element.relatedInformation } : { resource: null, selection: null }; + if (resource && selection) { this.editorService.openEditor({ - resource: marker.resource, + resource, options: { - selection: marker.range, + selection, preserveFocus, pinned, revealIfVisible: true }, }, sideByside).done(editor => { if (editor && preserveFocus) { - this.rangeHighlightDecorations.highlightRange(marker, editor.getControl()); + this.rangeHighlightDecorations.highlightRange({ resource, range: selection }, editor.getControl()); } else { this.rangeHighlightDecorations.removeHighlightRange(); } @@ -195,7 +190,7 @@ export class MarkersPanel extends Panel { this.treeContainer = dom.append(parent, dom.$('.tree-container')); dom.addClass(this.treeContainer, 'show-file-icons'); const renderer = this.instantiationService.createInstance(Viewer.Renderer); - const dnd = this.instantiationService.createInstance(SimpleFileResourceDragAndDrop, obj => obj instanceof Resource ? obj.uri : void 0); + const dnd = this.instantiationService.createInstance(SimpleFileResourceDragAndDrop, obj => obj instanceof ResourceMarkers ? obj.uri : void 0); const controller = this.instantiationService.createInstance(Controller); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new Viewer.DataSource(), @@ -205,7 +200,6 @@ export class MarkersPanel extends Panel { accessibilityProvider: new Viewer.MarkersTreeAccessibilityProvider(), dnd }, { - indentPixels: 0, twistiePixels: 20, ariaLabel: Messages.MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE }); @@ -333,8 +327,8 @@ export class MarkersPanel extends Panel { } } - private getResourceForCurrentActiveResource(): Resource { - let res: Resource = null; + private getResourceForCurrentActiveResource(): ResourceMarkers { + let res: ResourceMarkers = null; if (this.currentActiveResource) { this.markersWorkbenchService.markersModel.forEachFilteredResource(resource => { if (!res && resource.uri.toString() === this.currentActiveResource.toString()) { @@ -345,7 +339,7 @@ export class MarkersPanel extends Panel { return res; } - private hasSelectedMarkerFor(resource: Resource): boolean { + private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { let selectedElement = this.tree.getSelection(); if (selectedElement && selectedElement.length > 0) { if (selectedElement[0] instanceof Marker) { @@ -379,7 +373,7 @@ export class MarkersPanel extends Panel { return super.getActionItem(action); } - public getFocusElement(): Resource | Marker { + public getFocusElement(): ResourceMarkers | Marker { return this.tree.getFocus(); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index f3be8338727..baffdcbe266 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -12,24 +12,20 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { FileLabel, ResourceLabel } from 'vs/workbench/browser/labels'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { MarkersModel, Resource, Marker } from 'vs/workbench/parts/markers/electron-browser/markersModel'; +import { MarkersModel, ResourceMarkers, Marker, ResourceData, RelatedInformation, NodeWithId } from 'vs/workbench/parts/markers/electron-browser/markersModel'; 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 } from 'vs/base/common/lifecycle'; -interface IAnyResourceTemplateData { - count: CountBadge; - styler: IDisposable; -} - -interface IResourceTemplateData extends IAnyResourceTemplateData { +interface IResourceTemplateData { resourceLabel: ResourceLabel; } -interface IFileResourceTemplateData extends IAnyResourceTemplateData { - fileLabel: FileLabel; +interface IProblemResourceTemplateData extends IResourceTemplateData { + count: CountBadge; + styler: IDisposable; } interface IMarkerTemplateData { @@ -39,30 +35,35 @@ interface IMarkerTemplateData { lnCol: HTMLElement; } +interface IRelatedInformationTemplateData { + description: HighlightedLabel; + lnCol: HTMLElement; +} + export class DataSource implements IDataSource { - public getId(tree: ITree, element: MarkersModel | Resource | Marker): string { + public getId(tree: ITree, element: any): string { if (element instanceof MarkersModel) { return 'root'; } - if (element instanceof Resource) { - return element.uri.toString(); - } - if (element instanceof Marker) { + if (element instanceof NodeWithId) { return element.id; } return ''; } public hasChildren(tree: ITree, element: any): boolean { - return element instanceof MarkersModel || element instanceof Resource; + return element instanceof MarkersModel || element instanceof ResourceData || (element instanceof Marker && element.resourceRelatedInformation.length > 0); } - public getChildren(tree: ITree, element: MarkersModel | Resource): Promise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof MarkersModel) { return Promise.as(element.resources); } - if (element instanceof Resource) { - return Promise.as(element.markers); + if (element instanceof ResourceData) { + return Promise.as(element.data); + } + if (element instanceof Marker && element.resourceRelatedInformation.length > 0) { + return Promise.as(element.resourceRelatedInformation); } return null; } @@ -73,9 +74,9 @@ export class DataSource implements IDataSource { } export class DataFilter implements IFilter { - public isVisible(tree: ITree, element: Resource | Marker | any): boolean { - if (element instanceof Resource) { - return element.filteredMarkersCount > 0; + public isVisible(tree: ITree, element: any): boolean { + if (element instanceof ResourceMarkers) { + return element.count > 0; } if (element instanceof Marker) { return element.isSelected; @@ -86,9 +87,12 @@ export class DataFilter implements IFilter { export class Renderer implements IRenderer { - private static readonly RESOURCE_TEMPLATE_ID = 'resource-template'; - private static readonly FILE_RESOURCE_TEMPLATE_ID = 'file-resource-template'; + private static readonly PROBLEM_RESOURCE_TEMPLATE_ID = 'problem-resource-template'; + private static readonly PROBLEM_FILE_RESOURCE_TEMPLATE_ID = 'problem-file-resource-template'; + private static readonly RELATED_RESOURCE_TEMPLATE_ID = 'related-resource-template'; + private static readonly RELATED_FILE_RESOURCE_TEMPLATE_ID = 'related-file-resource-template'; private static readonly MARKER_TEMPLATE_ID = 'marker-template'; + private static readonly RELATED_INFORMATION_TEMPLATE_ID = 'related-info-template'; constructor( @IInstantiationService private instantiationService: IInstantiationService, @@ -101,35 +105,63 @@ export class Renderer implements IRenderer { } public getTemplateId(tree: ITree, element: any): string { - if (element instanceof Resource) { - if ((element).uri.scheme === network.Schemas.file || (element).uri.scheme === network.Schemas.untitled) { - return Renderer.FILE_RESOURCE_TEMPLATE_ID; + if (element instanceof ResourceMarkers) { + if ((element).uri.scheme === network.Schemas.file || (element).uri.scheme === network.Schemas.untitled) { + return Renderer.PROBLEM_FILE_RESOURCE_TEMPLATE_ID; } else { - return Renderer.RESOURCE_TEMPLATE_ID; + return Renderer.PROBLEM_RESOURCE_TEMPLATE_ID; + } + } + if (element instanceof ResourceData) { + if ((element).uri.scheme === network.Schemas.file || (element).uri.scheme === network.Schemas.untitled) { + return Renderer.RELATED_FILE_RESOURCE_TEMPLATE_ID; + } else { + return Renderer.RELATED_RESOURCE_TEMPLATE_ID; } } if (element instanceof Marker) { return Renderer.MARKER_TEMPLATE_ID; } + if (element instanceof RelatedInformation) { + return Renderer.RELATED_INFORMATION_TEMPLATE_ID; + } return ''; } public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { dom.addClass(container, 'markers-panel-tree-entry'); switch (templateId) { - case Renderer.FILE_RESOURCE_TEMPLATE_ID: + case Renderer.PROBLEM_FILE_RESOURCE_TEMPLATE_ID: + return this.renderProblemFileResourceTemplate(container); + case Renderer.PROBLEM_RESOURCE_TEMPLATE_ID: + return this.renderProblemResourceTemplate(container); + case Renderer.RELATED_FILE_RESOURCE_TEMPLATE_ID: return this.renderFileResourceTemplate(container); - case Renderer.RESOURCE_TEMPLATE_ID: + case Renderer.RELATED_RESOURCE_TEMPLATE_ID: return this.renderResourceTemplate(container); case Renderer.MARKER_TEMPLATE_ID: return this.renderMarkerTemplate(container); + case Renderer.RELATED_INFORMATION_TEMPLATE_ID: + return this.renderRelatedInformationTemplate(container); } } - private renderFileResourceTemplate(container: HTMLElement): IFileResourceTemplateData { - const data: IFileResourceTemplateData = Object.create(null); + private renderFileResourceTemplate(container: HTMLElement): IResourceTemplateData { + const data: IProblemResourceTemplateData = Object.create(null); const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); - data.fileLabel = this.instantiationService.createInstance(FileLabel, resourceLabelContainer, { supportHighlights: true }); + data.resourceLabel = this.instantiationService.createInstance(FileLabel, resourceLabelContainer, { supportHighlights: true }); + return data; + } + + private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { + const data: IProblemResourceTemplateData = Object.create(null); + const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); + data.resourceLabel = this.instantiationService.createInstance(ResourceLabel, resourceLabelContainer, { supportHighlights: true }); + return data; + } + + private renderProblemFileResourceTemplate(container: HTMLElement): IProblemResourceTemplateData { + const data = this.renderFileResourceTemplate(container); const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); data.count = new CountBadge(badgeWrapper); @@ -138,10 +170,8 @@ export class Renderer implements IRenderer { return data; } - private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { - const data: IResourceTemplateData = Object.create(null); - const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); - data.resourceLabel = this.instantiationService.createInstance(ResourceLabel, resourceLabelContainer, { supportHighlights: true }); + private renderProblemResourceTemplate(container: HTMLElement): IProblemResourceTemplateData { + const data = this.renderResourceTemplate(container); const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); data.count = new CountBadge(badgeWrapper); @@ -159,23 +189,36 @@ export class Renderer implements IRenderer { return data; } + private renderRelatedInformationTemplate(container: HTMLElement): IRelatedInformationTemplateData { + const data: IRelatedInformationTemplateData = Object.create(null); + data.description = new HighlightedLabel(dom.append(container, dom.$('.marker-description'))); + data.lnCol = dom.append(container, dom.$('span.marker-line')); + return data; + } + public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { switch (templateId) { - case Renderer.FILE_RESOURCE_TEMPLATE_ID: - case Renderer.RESOURCE_TEMPLATE_ID: - return this.renderResourceElement(tree, element, templateData); + case Renderer.PROBLEM_FILE_RESOURCE_TEMPLATE_ID: + case Renderer.PROBLEM_RESOURCE_TEMPLATE_ID: + case Renderer.RELATED_FILE_RESOURCE_TEMPLATE_ID: + case Renderer.RELATED_RESOURCE_TEMPLATE_ID: + return this.renderResourceElement(tree, >element, templateData); case Renderer.MARKER_TEMPLATE_ID: return this.renderMarkerElement(tree, (element), templateData); + case Renderer.RELATED_INFORMATION_TEMPLATE_ID: + return this.renderRelatedInformationElement(tree, (element), templateData); } } - private renderResourceElement(tree: ITree, element: Resource, templateData: IAnyResourceTemplateData) { - if ((templateData).fileLabel) { - (templateData).fileLabel.setFile(element.uri, { matches: element.uriMatches }); - } else if ((templateData).resourceLabel) { - (templateData).resourceLabel.setLabel({ name: element.name, description: element.uri.toString(), resource: element.uri }, { matches: element.uriMatches }); + private renderResourceElement(tree: ITree, element: ResourceData, templateData: IResourceTemplateData | IProblemResourceTemplateData) { + if (templateData.resourceLabel instanceof FileLabel) { + templateData.resourceLabel.setFile(element.uri, { matches: element.uriMatches }); + } else { + templateData.resourceLabel.setLabel({ name: element.name, description: element.uri.toString(), resource: element.uri }, { matches: element.uriMatches }); + } + if ((templateData).count) { + (templateData).count.setCount(element.count); } - templateData.count.setCount(element.filteredMarkersCount); } private renderMarkerElement(tree: ITree, element: Marker, templateData: IMarkerTemplateData) { @@ -190,6 +233,12 @@ export class Renderer implements IRenderer { templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn); } + private renderRelatedInformationElement(tree: ITree, element: RelatedInformation, templateData: IRelatedInformationTemplateData) { + templateData.description.set(element.relatedInformation.message, element.matches); + templateData.description.element.title = element.relatedInformation.message; + templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.relatedInformation.startLineNumber, element.relatedInformation.startColumn); + } + private static iconClassNameFor(element: IMarker): string { switch (element.severity) { case MarkerSeverity.Hint: @@ -205,15 +254,16 @@ export class Renderer implements IRenderer { } public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { - if (templateId === Renderer.FILE_RESOURCE_TEMPLATE_ID) { - (templateData).fileLabel.dispose(); - (templateData).styler.dispose(); - } else if (templateId === Renderer.RESOURCE_TEMPLATE_ID) { - (templateData).resourceLabel.dispose(); - (templateData).styler.dispose(); + if (templateId === Renderer.PROBLEM_RESOURCE_TEMPLATE_ID || templateId === Renderer.PROBLEM_FILE_RESOURCE_TEMPLATE_ID) { + (templateData).resourceLabel.dispose(); + (templateData).styler.dispose(); + } else if (templateId === Renderer.RELATED_RESOURCE_TEMPLATE_ID || templateId === Renderer.RELATED_FILE_RESOURCE_TEMPLATE_ID) { + (templateData).resourceLabel.dispose(); } else if (templateId === Renderer.MARKER_TEMPLATE_ID) { (templateData).description.dispose(); (templateData).source.dispose(); + } else if (templateId === Renderer.RELATED_INFORMATION_TEMPLATE_ID) { + (templateData).description.dispose(); } } } @@ -221,11 +271,17 @@ export class Renderer implements IRenderer { export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider { public getAriaLabel(tree: ITree, element: any): string { - if (element instanceof Resource) { - return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.name, element.filteredMarkersCount); + if (element instanceof ResourceMarkers) { + return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.name, element.count); } if (element instanceof Marker) { - return Messages.MARKERS_TREE_ARIA_LABEL_MARKER(element.raw); + return Messages.MARKERS_TREE_ARIA_LABEL_MARKER(element); + } + if (element instanceof ResourceData) { + return element.name; + } + if (element instanceof RelatedInformation) { + return Messages.MARKERS_TREE_ARIA_LABEL_RELATED_INFORMATION(element.relatedInformation); } return null; } diff --git a/src/vs/workbench/parts/markers/electron-browser/messages.ts b/src/vs/workbench/parts/markers/electron-browser/messages.ts index 067591c0773..a0357bec1da 100644 --- a/src/vs/workbench/parts/markers/electron-browser/messages.ts +++ b/src/vs/workbench/parts/markers/electron-browser/messages.ts @@ -5,7 +5,8 @@ 'use strict'; import * as nls from 'vs/nls'; -import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; +import { Marker } from './markersModel'; export default class Messages { @@ -40,23 +41,24 @@ export default class Messages { 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 = (fileName: string, noOfProblems: number): string => { return nls.localize('problems.tree.aria.label.resource', "{0} with {1} problems", fileName, noOfProblems); }; - public static readonly MARKERS_TREE_ARIA_LABEL_MARKER = (marker: IMarker): string => { - switch (marker.severity) { + public static readonly MARKERS_TREE_ARIA_LABEL_MARKER = (marker: Marker): string => { + const relatedInformationMessage = marker.resourceRelatedInformation.length ? nls.localize('problems.tree.aria.label.marker.relatedInformation', " This problem has references to {0} locations.", marker.resourceRelatedInformation.reduce((count, r) => count + r.count, 0)) : ''; + switch (marker.raw.severity) { case MarkerSeverity.Error: - return marker.source ? nls.localize('problems.tree.aria.label.error.marker', "Error generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) - : nls.localize('problems.tree.aria.label.error.marker.nosource', "Error: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + return marker.raw.source ? nls.localize('problems.tree.aria.label.error.marker', "Error generated by {0}: {1} at line {2} and character {3}.{4}", marker.raw.source, marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage) + : nls.localize('problems.tree.aria.label.error.marker.nosource', "Error: {0} at line {1} and character {2}.{3}", marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage); case MarkerSeverity.Warning: - return marker.source ? nls.localize('problems.tree.aria.label.warning.marker', "Warning generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) - : nls.localize('problems.tree.aria.label.warning.marker.nosource', "Warning: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + return marker.raw.source ? nls.localize('problems.tree.aria.label.warning.marker', "Warning generated by {0}: {1} at line {2} and character {3}.{4}", marker.raw.source, marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage) + : nls.localize('problems.tree.aria.label.warning.marker.nosource', "Warning: {0} at line {1} and character {2}.{3}", marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage, relatedInformationMessage); case MarkerSeverity.Info: - return marker.source ? nls.localize('problems.tree.aria.label.info.marker', "Info generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) - : nls.localize('problems.tree.aria.label.info.marker.nosource', "Info: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + return marker.raw.source ? nls.localize('problems.tree.aria.label.info.marker', "Info generated by {0}: {1} at line {2} and character {3}.{4}", marker.raw.source, marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage) + : nls.localize('problems.tree.aria.label.info.marker.nosource', "Info: {0} at line {1} and character {2}.{3}", marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage); default: - return marker.source ? nls.localize('problems.tree.aria.label.marker', "Problem generated by {0}: {1} at line {2} and character {3}", marker.source, marker.message, marker.startLineNumber, marker.startColumn) - : nls.localize('problems.tree.aria.label.marker.nosource', "Problem: {0} at line {1} and character {2}", marker.message, marker.startLineNumber, marker.startColumn); + return marker.raw.source ? nls.localize('problems.tree.aria.label.marker', "Problem generated by {0}: {1} at line {2} and character {3}.{4}", marker.raw.source, marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage) + : nls.localize('problems.tree.aria.label.marker.nosource', "Problem: {0} at line {1} and character {2}.{3}", marker.raw.message, marker.raw.startLineNumber, marker.raw.startColumn, relatedInformationMessage); } } - + public static readonly MARKERS_TREE_ARIA_LABEL_RELATED_INFORMATION = (relatedInformation: IRelatedInformation): string => nls.localize('problems.tree.aria.label.relatedinfo.message', "{0} at line {1} and character {2}", relatedInformation.message, relatedInformation.startLineNumber, relatedInformation.startColumn); public static SHOW_ERRORS_WARNINGS_ACTION_LABEL: string = nls.localize('errors.warnings.show.label', "Show Errors and Warnings"); } diff --git a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts index d62b12bb489..af1c476e33a 100644 --- a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts +++ b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts @@ -8,19 +8,19 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { MarkersModel, Marker, Resource } from 'vs/workbench/parts/markers/electron-browser/markersModel'; +import { MarkersModel, Marker, ResourceMarkers } from 'vs/workbench/parts/markers/electron-browser/markersModel'; class TestMarkersModel extends MarkersModel { - get filteredResources(): Resource[] { - let res: Resource[] = []; + get filteredResources(): ResourceMarkers[] { + let res: ResourceMarkers[] = []; this.forEachFilteredResource(resource => res.push(resource)); return res; } static compare(a: any, b: any): number { - if (a instanceof Resource && b instanceof Resource) { - return Resource.compare(a, b); + if (a instanceof ResourceMarkers && b instanceof ResourceMarkers) { + return ResourceMarkers.compare(a, b); } if (a instanceof Marker && b instanceof Marker) { return Marker.compare(a, b); @@ -45,22 +45,22 @@ suite('MarkersModel Test', () => { assert.equal(4, actuals.length); assert.ok(compareResource(actuals[0], 'res1')); - assert.equal(2, actuals[0].markers.length); - assert.ok(hasMarker(actuals[0].markers, marker1)); - assert.ok(hasMarker(actuals[0].markers, marker3)); + assert.equal(2, actuals[0].data.length); + assert.ok(hasMarker(actuals[0].data, marker1)); + assert.ok(hasMarker(actuals[0].data, marker3)); assert.ok(compareResource(actuals[1], 'res2')); - assert.equal(2, actuals[1].markers.length); - assert.ok(hasMarker(actuals[1].markers, marker2)); - assert.ok(hasMarker(actuals[1].markers, marker6)); + assert.equal(2, actuals[1].data.length); + assert.ok(hasMarker(actuals[1].data, marker2)); + assert.ok(hasMarker(actuals[1].data, marker6)); assert.ok(compareResource(actuals[2], 'res3')); - assert.equal(1, actuals[2].markers.length); - assert.ok(hasMarker(actuals[2].markers, marker4)); + assert.equal(1, actuals[2].data.length); + assert.ok(hasMarker(actuals[2].data, marker4)); assert.ok(compareResource(actuals[3], 'res4')); - assert.equal(1, actuals[3].markers.length); - assert.ok(hasMarker(actuals[3].markers, marker5)); + assert.equal(1, actuals[3].data.length); + assert.ok(hasMarker(actuals[3].data, marker5)); }); test('sort palces resources with no errors at the end', function () { @@ -119,7 +119,7 @@ suite('MarkersModel Test', () => { let marker15 = anErrorWithRange(8, 2, 8, 4); let testObject = new TestMarkersModel([marker1, marker2, marker3, marker4, marker5, marker6, marker7, marker8, marker9, marker10, marker11, marker12, marker13, marker14, marker15]); - let actuals = testObject.filteredResources[0].markers.sort(TestMarkersModel.compare); + let actuals = testObject.filteredResources[0].data.sort(TestMarkersModel.compare); assert.equal(actuals[0].raw, marker6); assert.equal(actuals[1].raw, marker14); @@ -153,7 +153,7 @@ suite('MarkersModel Test', () => { }).length === 1; } - function compareResource(a: Resource, b: string): boolean { + function compareResource(a: ResourceMarkers, b: string): boolean { return a.uri.toString() === URI.file(b).toString(); }