diff --git a/src/vs/workbench/parts/markers/electron-browser/constants.ts b/src/vs/workbench/parts/markers/electron-browser/constants.ts index 7bd0e2e7c408b3d83d2617af1ae3a2d66a5037a4..a9e0b9e8f6de7f5041bf0a4059ac2e7d1754b69e 100644 --- a/src/vs/workbench/parts/markers/electron-browser/constants.ts +++ b/src/vs/workbench/parts/markers/electron-browser/constants.ts @@ -12,6 +12,8 @@ export default { RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID: 'problems.action.copyRelatedInformationMessage', FOCUS_PROBLEMS_FROM_FILTER: 'problems.action.focusProblemsFromFilter', MARKERS_PANEL_FOCUS_FILTER: 'problems.action.focusFilter', + MARKERS_PANEL_SHOW_MULTILINE_MESSAGE: 'problems.action.showMultilineMessage', + MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', MARKER_SHOW_PANEL_ID: 'workbench.action.showErrorsWarnings', diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts index 66e1c9423efe12d3b960e540200e4d69d67bfe1a..4afdc0bf6c376d3d4e055cba2d071599f75e8985 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts @@ -5,7 +5,7 @@ import { clipboard } from 'electron'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -155,7 +155,38 @@ registerAction({ }, } }); - +registerAction({ + id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, + handler(accessor) { + const panelService = accessor.get(IPanelService); + const panel = panelService.getActivePanel(); + if (panel instanceof MarkersPanel) { + panel.markersViewState.multiline = true; + } + }, + title: localize('show multiline', "Show message in multiple lines"), + category: localize('problems', "Problems"), + menu: { + menuId: MenuId.CommandPalette, + when: new RawContextKey('activePanel', Constants.MARKERS_PANEL_ID) + } +}); +registerAction({ + id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, + handler(accessor) { + const panelService = accessor.get(IPanelService); + const panel = panelService.getActivePanel(); + if (panel instanceof MarkersPanel) { + panel.markersViewState.multiline = false; + } + }, + title: localize('show singleline', "Show message in single line"), + category: localize('problems', "Problems"), + menu: { + menuId: MenuId.CommandPalette, + when: new RawContextKey('activePanel', Constants.MARKERS_PANEL_ID) + } +}); function copyMarker(panelService: IPanelService) { const activePanel = panelService.getActivePanel(); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index b8f9e41abf72204f050aa67d40ab9212df099ccf..18eb2ab1d2b23c0d964ae43fca1246f633a14166 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -14,7 +14,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/ import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkersModel } from 'vs/workbench/parts/markers/electron-browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem, IMarkersFilterActionChangeEvent, IMarkerFilterController, ToggleLineModeAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; +import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem, IMarkersFilterActionChangeEvent, IMarkerFilterController } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; @@ -86,7 +86,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private cachedFilterStats: { total: number; filtered: number; } | undefined = undefined; private currentResourceGotAddedToMarkersData: boolean = false; - private markersViewState: MarkersViewState; + readonly markersViewState: MarkersViewState; private disposables: IDisposable[] = []; constructor( @@ -107,7 +107,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService); this.panelState = this.getMemento(StorageScope.WORKSPACE); this.markersViewState = new MarkersViewState(this.panelState['multiline']); - this.markersViewState.onDidChangeViewState(this.refreshPanel, this, this.disposables); + this.markersViewState.onDidChangeViewState(this.onDidChangeViewState, this, this.disposables); this.setCurrentActiveEditor(); } @@ -217,6 +217,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } } + private onDidChangeViewState(marker?: Marker): void { + this.refreshPanel(); + } + private updateFilter() { this.cachedFilterStats = undefined; const excludeExpression = this.getExcludeExpression(this.filterAction.useFilesExclude); @@ -355,7 +359,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { }); this.filterAction = this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], useFilesExclude: !!this.panelState['useFilesExclude'] }); - this.actions = [this.filterAction, this.instantiationService.createInstance(ToggleLineModeAction, this.markersViewState), this.collapseAllAction]; + this.actions = [this.filterAction, this.collapseAllAction]; } private createListeners(): void { @@ -373,6 +377,16 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } private onDidChangeModel(resources: URI[]) { + for (const resource of resources) { + const resourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(resource); + if (resourceMarkers) { + for (const marker of resourceMarkers.markers) { + this.markersViewState.add(marker); + } + } else { + this.markersViewState.remove(resource); + } + } this.currentResourceGotAddedToMarkersData = this.currentResourceGotAddedToMarkersData || this.isCurrentResourceGotAddedToMarkersData(resources); this.refreshPanel(); this.updateRangeHighlights(); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index eaf79bb7e5acdb7a1bdd9b3b358ecb22ad5647e0..f42b506f93f3148cbb4ad9b57c6b8222209b80d3 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -32,7 +32,6 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { Event } from 'vs/base/common/event'; import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; export class ToggleMarkersPanelAction extends TogglePanelAction { @@ -290,34 +289,6 @@ export class MarkersFilterActionItem extends BaseActionItem { } } -export class ToggleLineModeAction extends Action { - - public static readonly ID: string = 'workbench.actions.problems.toggleLineMode'; - - private disposables: IDisposable[] = []; - - constructor(private readonly viewState: MarkersViewState) { - super(ToggleLineModeAction.ID); - this.update(); - viewState.onDidChangeViewState(this.update, this, this.disposables); - } - - private update(): void { - this.tooltip = this.viewState.multiline ? localize('singleline', "Show message in single line") : localize('multiline', "Show message in multiple lines"); - this.class = this.viewState.multiline ? 'octicon octicon-fold' : 'octicon octicon-unfold'; - } - - run(): Promise { - this.viewState.multiline = !this.viewState.multiline; - return Promise.resolve(); - } - - dispose(): void { - dispose(this.disposables); - } - -} - export class QuickFixAction extends Action { public static readonly ID: string = 'workbench.actions.problems.quickfix'; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 25a414ab8ccef91e9680b5577ba68b7674476385..9dfc41aa0b655944a38ea5bd6e4a2a95fde60cfa 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -27,6 +27,9 @@ import { IMatch } from 'vs/base/common/filters'; import { Event, Emitter } from 'vs/base/common/event'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { isUndefinedOrNull } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { localize } from 'vs/nls'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -77,8 +80,10 @@ export class VirtualDelegate implements IListVirtualDelegate { constructor(private readonly markersViewState: MarkersViewState) { } getHeight(element: TreeElement): number { - if (element instanceof Marker && this.markersViewState.multiline) { - return element.lines.length * 22; + if (element instanceof Marker) { + const viewState = this.markersViewState.getViewState(element); + const noOfLines = !viewState || viewState.multiline ? element.lines.length : 1; + return noOfLines * 22; } return 22; } @@ -238,6 +243,7 @@ class MarkerWidget extends Disposable { private readonly actionBar: ActionBar; private readonly icon: HTMLElement; + private readonly multilineActionbar: ActionBar; private readonly messageAndDetailsContainer: HTMLElement; private disposables: IDisposable[] = []; @@ -250,32 +256,55 @@ class MarkerWidget extends Disposable { super(); this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), { actionItemProvider })); this.icon = dom.append(parent, dom.$('.icon')); + this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { actionItemProvider })); this.messageAndDetailsContainer = dom.append(parent, dom.$('.marker-message-details')); this._register(toDisposable(() => this.disposables = dispose(this.disposables))); } render(element: Marker, filterData: MarkerFilterData): void { - const { marker, lines } = element; + this.actionBar.clear(); + this.multilineActionbar.clear(); if (this.disposables.length) { this.disposables = dispose(this.disposables); } dom.clearNode(this.messageAndDetailsContainer); - this.icon.className = 'marker-icon ' + MarkerWidget.iconClassNameFor(marker); + this.renderQuickfixActionbar(element); + this.icon.className = 'marker-icon ' + MarkerWidget.iconClassNameFor(element.marker); + this.renderMultilineActionbar(element); - this.actionBar.clear(); - const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element); + this.renderMessageAndDetails(element, filterData); + } + + private renderQuickfixActionbar(marker: Marker): void { + const quickFixAction = this.instantiationService.createInstance(QuickFixAction, marker); this.actionBar.push([quickFixAction], { icon: true, label: false }); - this.onDidQuickFixesActionEnable(quickFixAction.enabled); + dom.toggleClass(this.icon, 'quickFix', quickFixAction.enabled); quickFixAction.onDidChange(({ enabled }) => { if (!isUndefinedOrNull(enabled)) { - this.onDidQuickFixesActionEnable(enabled); + dom.toggleClass(this.icon, 'quickFix', enabled); } }, this, this.disposables); + } + + private renderMultilineActionbar(marker: Marker): void { + const viewState = this.markersViewState.getViewState(marker); + const multiline = viewState && viewState.multiline; + const action = new Action('problems.action.toggleMultiline'); + action.enabled = viewState && marker.lines.length > 1; + action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); + action.class = multiline ? 'octicon octicon-chevron-up' : 'octicon octicon-chevron-down'; + action.run = () => { if (viewState) { viewState.multiline = !viewState.multiline; } return Promise.resolve(); }; + this.multilineActionbar.push([action], { icon: true, label: false }); + } + private renderMessageAndDetails(element: Marker, filterData: MarkerFilterData) { + const { marker, lines } = element; + const viewState = this.markersViewState.getViewState(element); + const multiline = !viewState || viewState.multiline; const lineMatches = filterData && filterData.lineMatches || []; const messageContainer = dom.append(this.messageAndDetailsContainer, dom.$('.marker-message')); - dom.toggleClass(messageContainer, 'multiline', this.markersViewState.multiline); + dom.toggleClass(messageContainer, 'multiline', multiline); let lastLineElement = messageContainer; for (let index = 0; index < lines.length; index++) { @@ -284,11 +313,7 @@ class MarkerWidget extends Disposable { highlightedLabel.set(lines[index], lineMatches[index]); this.disposables.push(highlightedLabel); } - this.renderDetails(marker, filterData, this.markersViewState.multiline ? lastLineElement : this.messageAndDetailsContainer); - } - - private onDidQuickFixesActionEnable(enabled: boolean): void { - dom.toggleClass(this.icon, 'quickFix', enabled); + this.renderDetails(marker, filterData, multiline ? lastLineElement : this.messageAndDetailsContainer); } private renderDetails(marker: IMarker, filterData: MarkerFilterData, parent: HTMLElement): void { @@ -451,25 +476,103 @@ export class Filter implements ITreeFilter { } } -export class MarkersViewState extends Disposable { +export class MarkerViewState extends Disposable { private readonly _onDidChangeViewState: Emitter = this._register(new Emitter()); readonly onDidChangeViewState: Event = this._onDidChangeViewState.event; + private _multiline: boolean = true; + get multiline(): boolean { + return this._multiline; + } + + set multiline(value: boolean) { + if (this._multiline !== value) { + this._multiline = value; + this._onDidChangeViewState.fire(); + } + } +} + +export class MarkersViewState extends Disposable { + + private readonly _onDidChangeViewState: Emitter = this._register(new Emitter()); + readonly onDidChangeViewState: Event = this._onDidChangeViewState.event; + + private readonly markersViewStates: Map = new Map(); + private readonly markersPerResource: Map = new Map(); + + private bulkUpdate: boolean = false; + constructor(multiline: boolean = true) { super(); this._multiline = multiline; } + add(marker: Marker): void { + if (!this.markersViewStates.has(marker.hash)) { + const disposables: IDisposable[] = []; + const viewState = new MarkerViewState(); + viewState.multiline = this.multiline; + viewState.onDidChangeViewState(() => { + if (!this.bulkUpdate) { + this._onDidChangeViewState.fire(marker); + } + }, this, disposables); + this.markersViewStates.set(marker.hash, { viewState, disposables }); + + const markers = this.markersPerResource.get(marker.resource.toString()) || []; + markers.push(marker); + this.markersPerResource.set(marker.resource.toString(), markers); + } + } + + remove(resource: URI): void { + const markers = this.markersPerResource.get(resource.toString()) || []; + for (const marker of markers) { + const value = this.markersViewStates.get(marker.hash); + if (value) { + dispose(value.disposables); + } + this.markersViewStates.delete(marker.hash); + } + this.markersPerResource.delete(resource.toString()); + } + + getViewState(marker: Marker): MarkerViewState | null { + const value = this.markersViewStates.get(marker.hash); + return value ? value.viewState : null; + } + private _multiline: boolean = true; get multiline(): boolean { return this._multiline; } set multiline(value: boolean) { + let changed = false; if (this._multiline !== value) { this._multiline = value; + changed = true; + } + this.bulkUpdate = true; + this.markersViewStates.forEach(({ viewState }) => { + if (viewState.multiline !== value) { + viewState.multiline = value; + changed = true; + } + }); + this.bulkUpdate = false; + if (changed) { this._onDidChangeViewState.fire(); } } + + dispose(): void { + this.markersViewStates.forEach(({ disposables }) => dispose(disposables)); + this.markersViewStates.clear(); + this.markersPerResource.clear(); + super.dispose(); + } + } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/media/markers.css b/src/vs/workbench/parts/markers/electron-browser/media/markers.css index 21e810d88316565e87c1ffcc45756396bc169e58..1374b2fe177a5868798a8de565ae083da89d04c2 100644 --- a/src/vs/workbench/parts/markers/electron-browser/media/markers.css +++ b/src/vs/workbench/parts/markers/electron-browser/media/markers.css @@ -137,12 +137,16 @@ 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 .monaco-tl-contents .marker-icon, +.markers-panel .monaco-tl-contents .actions .action-item { + margin-right: 6px; +} + .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source, .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .related-info-resource, .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .related-info-resource-separator, @@ -209,6 +213,7 @@ display: block; } +.markers-panel .monaco-tl-contents .multiline-actions .action-label, .markers-panel .monaco-tl-contents .actions .action-label { width: 16px; height: 100%; @@ -216,6 +221,11 @@ background-repeat: no-repeat; } +.markers-panel .monaco-tl-contents .multiline-actions .action-label { + line-height: 22px; +} + +.markers-panel .monaco-tl-contents .multiline-actions .action-item.disabled, .markers-panel .monaco-tl-contents .actions .action-item.disabled { display: none; } \ No newline at end of file