提交 99f78e83 编写于 作者: J Joao Moreno

markers: filter stats

上级 87e290d0
......@@ -247,6 +247,10 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
// Tree
getNode(location?: TRef): ITreeNode<T, TFilterData> {
return this.model.getNode(location);
}
collapse(location: TRef): boolean {
return this.model.setCollapsed(location, true);
}
......
......@@ -116,16 +116,16 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
}
getListIndex(location: number[]): number {
return this.getNodeWithListIndex(location).listIndex;
return this.getTreeNodeWithListIndex(location).listIndex;
}
setCollapsed(location: number[], collapsed: boolean): boolean {
const { node, listIndex, revealed } = this.getNodeWithListIndex(location);
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
return this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed, collapsed));
}
toggleCollapsed(location: number[]): void {
const { node, listIndex, revealed } = this.getNodeWithListIndex(location);
const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed));
}
......@@ -146,7 +146,7 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
}
isCollapsed(location: number[]): boolean {
return this.getNode(location).collapsed;
return this.getTreeNode(location).collapsed;
}
refilter(): void {
......@@ -348,10 +348,8 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
}
}
/**
* Cheaper version of findNode, which doesn't require list indices.
*/
private getNode(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root): IMutableTreeNode<T, TFilterData> {
// cheap
private getTreeNode(location: number[], node: IMutableTreeNode<T, TFilterData> = this.root): IMutableTreeNode<T, TFilterData> {
if (location.length === 0) {
return node;
}
......@@ -362,10 +360,11 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
throw new Error('Invalid tree location');
}
return this.getNode(rest, node.children[index]);
return this.getTreeNode(rest, node.children[index]);
}
private getNodeWithListIndex(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean } {
// expensive
private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode<T, TFilterData>, listIndex: number, revealed: boolean } {
const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location);
const index = location[location.length - 1];
......@@ -399,6 +398,10 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed);
}
getNode(location: number[] = []): ITreeNode<T, TFilterData> {
return this.getTreeNode(location);
}
// TODO@joao perf!
getNodeLocation(node: ITreeNode<T, TFilterData>): number[] {
const location = [];
......@@ -421,12 +424,12 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
getParentElement(location: number[]): T | null {
const parentLocation = this.getParentNodeLocation(location);
const node = this.getNode(parentLocation);
const node = this.getTreeNode(parentLocation);
return node === this.root ? null : node.element;
}
getFirstElementChild(location: number[]): T | null {
const node = this.getNode(location);
const node = this.getTreeNode(location);
if (node.children.length === 0) {
return null;
......@@ -436,7 +439,7 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
}
getLastElementAncestor(location: number[]): T | null {
const node = this.getNode(location);
const node = this.getTreeNode(location);
if (node.children.length === 0) {
return null;
......
......@@ -90,6 +90,11 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData = void> imp
this.model.refilter();
}
getNode(element: T = null): ITreeNode<T, TFilterData> {
const location = this.getElementLocation(element);
return this.model.getNode(location);
}
getNodeLocation(node: ITreeNode<T, TFilterData>): T {
return node.element;
}
......
......@@ -58,6 +58,7 @@ export interface ITreeModel<T, TFilterData, TRef> {
isCollapsed(ref: TRef): boolean;
refilter(): void;
getNode(location?: TRef): ITreeNode<T, any>;
getNodeLocation(node: ITreeNode<T, any>): TRef;
getParentNodeLocation(location: TRef): TRef | null;
......
......@@ -59,7 +59,7 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb
}
private refreshBadge(): void {
const { total } = this.markersModel.stats();
const total = this.markersModel.resourceMarkers.reduce((r, rm) => r + rm.markers.length, 0);
const message = localize('totalProblems', 'Total {0} Problems', total);
this.activityService.showActivity(Constants.MARKERS_PANEL_ID, new NumberBadge(total, () => message));
}
......
......@@ -125,16 +125,6 @@ export class MarkersModel {
this._onDidChange.fire(resource);
}
stats(): { total: number, filtered: number } {
let total = 0;
// let filtered = 0;
this.resourcesByUri.forEach(resource => {
total += resource.markers.length;
// filtered += resource.filteredCount; // TODO@joao
});
return { total, filtered: total };
}
dispose(): void {
this._onDidChange.dispose();
this.resourcesByUri.clear();
......
......@@ -15,7 +15,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 } 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';
......@@ -28,7 +28,7 @@ import { localize } from 'vs/nls';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Iterator } from 'vs/base/common/iterator';
import { ITreeElement, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { debounceEvent, Relay } from 'vs/base/common/event';
import { debounceEvent, Relay, Event, Emitter } from 'vs/base/common/event';
import { WorkbenchObjectTree, ObjectTreeResourceNavigator } from 'vs/platform/list/browser/listService';
import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions';
import { IExpression, getEmptyExpression } from 'vs/base/common/glob';
......@@ -61,7 +61,7 @@ function createModelIterator(model: MarkersModel): Iterator<ITreeElement<TreeEle
});
}
export class MarkersPanel extends Panel {
export class MarkersPanel extends Panel implements IMarkerFilterController {
private lastSelectedRelativeTop: number = 0;
private currentActiveResource: URI = null;
......@@ -82,6 +82,10 @@ export class MarkersPanel extends Panel {
private filter: Filter;
private _onDidFilter = new Emitter<void>();
readonly onDidFilter: Event<void> = this._onDidFilter.event;
private cachedFilterStats: { total: number; filtered: number; } | undefined = undefined;
private currentResourceGotAddedToMarkersData: boolean = false;
constructor(
......@@ -217,20 +221,22 @@ export class MarkersPanel extends Panel {
// TODO@joao
private refreshPanel(): TPromise<any> {
if (this.isVisible()) {
this.cachedFilterStats = undefined;
this.collapseAllAction.enabled = true /* this.markersWorkbenchService.markersModel.hasFilteredResources() */;
dom.toggleClass(this.treeContainer, 'hidden', false/* !this.markersWorkbenchService.markersModel.hasFilteredResources() */);
this.renderMessage();
// if (this.markersWorkbenchService.markersModel.hasFilteredResources()) {
this.tree.setChildren(null, createModelIterator(this.markersWorkbenchService.markersModel));
// }
this._onDidFilter.fire();
}
return TPromise.as(null);
}
private updateFilter() {
this.cachedFilterStats = undefined;
const excludeExpression = this.getExcludeExpression(this.filterAction.useFilesExclude);
this.filter.options = new FilterOptions(this.filterAction.filterText, excludeExpression);
this.tree.refilter();
this._onDidFilter.fire();
}
private getExcludeExpression(useFilesExclude: boolean): IExpression {
......@@ -408,10 +414,9 @@ export class MarkersPanel extends Panel {
// TODO@joao
private renderMessage(): void {
dom.clearNode(this.messageBoxContainer);
const markersModel = this.markersWorkbenchService.markersModel;
// if (markersModel.hasFilteredResources()) {
this.messageBoxContainer.style.display = 'none';
const { total, filtered } = markersModel.stats();
const { total, filtered } = this.getFilterStats();
if (filtered === total) {
this.ariaLabelElement.setAttribute('aria-label', localize('No problems filtered', "Showing {0} problems", total));
} else {
......@@ -598,7 +603,7 @@ export class MarkersPanel extends Panel {
public getActionItem(action: IAction): IActionItem {
if (action.id === MarkersFilterAction.ID) {
this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction);
this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction, this);
return this.filterInputActionItem;
}
if (action.id === QuickFixAction.ID) {
......@@ -607,6 +612,36 @@ export class MarkersPanel extends Panel {
return super.getActionItem(action);
}
getFilterOptions(): FilterOptions {
return this.filter.options;
}
getFilterStats(): { total: number; filtered: number; } {
if (!this.cachedFilterStats) {
this.cachedFilterStats = this.computeFilterStats();
}
return this.cachedFilterStats;
}
private computeFilterStats(): { total: number; filtered: number; } {
const root = this.tree.getNode();
let total = 0;
let filtered = 0;
for (const resourceMarkerNode of root.children) {
for (const markerNode of resourceMarkerNode.children) {
total++;
if (resourceMarkerNode.visible && markerNode.visible) {
filtered++;
}
}
}
return { total, filtered };
}
public shutdown(): void {
// store memento
this.panelSettings['filter'] = this.filterAction.filterText;
......
......@@ -40,6 +40,9 @@ import { IMarkerData } from 'vs/platform/markers/common/markers';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { URI } from 'vs/base/common/uri';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
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';
export class ToggleMarkersPanelAction extends TogglePanelAction {
......@@ -118,6 +121,12 @@ export class MarkersFilterAction extends Action {
}
}
export interface IMarkerFilterController {
onDidFilter: Event<void>;
getFilterOptions(): FilterOptions;
getFilterStats(): { total: number, filtered: number };
}
export class MarkersFilterActionItem extends BaseActionItem {
private delayedFilterUpdate: Delayer<void>;
......@@ -129,11 +138,11 @@ export class MarkersFilterActionItem extends BaseActionItem {
constructor(
readonly action: MarkersFilterAction,
private filterController: IMarkerFilterController,
@IInstantiationService private instantiationService: IInstantiationService,
@IContextViewService private contextViewService: IContextViewService,
@IThemeService private themeService: IThemeService,
@IMarkersWorkbenchService private markersWorkbenchService: IMarkersWorkbenchService,
// @ITelemetryService private telemetryService: ITelemetryService,
@ITelemetryService private telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super(null, action);
......@@ -209,7 +218,7 @@ export class MarkersFilterActionItem extends BaseActionItem {
this.filterBadge.style.borderColor = border;
}));
this.updateBadge();
this._register(this.markersWorkbenchService.markersModel.onDidChange(() => this.updateBadge()));
this._register(this.filterController.onDidFilter(() => this.updateBadge()));
}
private createFilesExcludeCheckbox(container: HTMLElement): void {
......@@ -240,7 +249,7 @@ export class MarkersFilterActionItem extends BaseActionItem {
}
private updateBadge(): void {
const { total, filtered } = this.markersWorkbenchService.markersModel.stats();
const { total, filtered } = this.filterController.getFilterStats();
DOM.toggleClass(this.filterBadge, 'hidden', total === filtered || filtered === 0);
this.filterBadge.textContent = localize('showing filtered problems', "Showing {0} of {1}", filtered, total);
this.adjustInputBox();
......@@ -276,12 +285,12 @@ export class MarkersFilterActionItem extends BaseActionItem {
}
}
// TODO@joao
private reportFilteringUsed(): void {
// let data = {};
// data['errors'] = this.filterOptions.filterErrors;
// data['warnings'] = this.filterOptions.filterWarnings;
// data['infos'] = this.filterOptions.filterInfos;
const filterOptions = this.filterController.getFilterOptions();
const data = {};
data['errors'] = filterOptions.filterErrors;
data['warnings'] = filterOptions.filterWarnings;
data['infos'] = filterOptions.filterInfos;
/* __GDPR__
"problems.filter" : {
"errors" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
......@@ -289,7 +298,7 @@ export class MarkersFilterActionItem extends BaseActionItem {
"infos": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
// this.telemetryService.publicLog('problems.filter', data);
this.telemetryService.publicLog('problems.filter', data);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册