提交 ed604d1f 编写于 作者: J Joao Moreno

hook in filter to problems tree

上级 7d6c474a
......@@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence';
import { Iterator, ISequence } from 'vs/base/common/iterator';
import { Emitter, Event } from 'vs/base/common/event';
import { tail2 } from 'vs/base/common/arrays';
import { ITreeFilterResult, TreeVisibility, ITreeFilter, ITreeOptions, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree';
import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeOptions, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree';
interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
readonly parent: IMutableTreeNode<T, TFilterData> | undefined;
......@@ -19,7 +19,7 @@ interface IMutableTreeNode<T, TFilterData> extends ITreeNode<T, TFilterData> {
visible: boolean;
}
function isFilterResult<T>(obj: any): obj is ITreeFilterResult<T> {
function isFilterResult<T>(obj: any): obj is ITreeFilterDataResult<T> {
return typeof obj === 'object' && 'visibility' in obj && 'data' in obj;
}
......@@ -30,8 +30,10 @@ function treeNodeToElement<T>(node: IMutableTreeNode<T, any>): ITreeElement<T> {
return { element, children, collapsed };
}
function getVisibleState(visibility: TreeVisibility): boolean | undefined {
function getVisibleState(visibility: boolean | TreeVisibility): boolean | undefined {
switch (visibility) {
case true: return true;
case false: return false;
case TreeVisibility.Hidden: return false;
case TreeVisibility.Visible: return true;
case TreeVisibility.Recurse: return undefined;
......
......@@ -12,13 +12,15 @@ export const enum TreeVisibility {
Recurse // TODO@joao come up with a better name
}
export interface ITreeFilterResult<TFilterData> {
visibility: TreeVisibility;
export interface ITreeFilterDataResult<TFilterData> {
visibility: boolean | TreeVisibility;
data: TFilterData;
}
export type TreeFilterResult<TFilterData> = boolean | TreeVisibility | ITreeFilterDataResult<TFilterData>;
export interface ITreeFilter<T, TFilterData = void> {
filter(element: T): boolean | TreeVisibility | ITreeFilterResult<TFilterData>;
filter(element: T): TreeFilterResult<TFilterData>;
}
export interface ITreeOptions<T, TFilterData = void> {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Messages from 'vs/workbench/parts/markers/electron-browser/messages';
import { IFilter, or, matchesPrefix, matchesContiguousSubString, matchesFuzzy } from 'vs/base/common/filters';
import { ParsedExpression, IExpression, splitGlobAware, getEmptyExpression, parse } from 'vs/base/common/glob';
import * as strings from 'vs/base/common/strings';
export class FilterOptions {
static readonly _filter: IFilter = or(matchesPrefix, matchesContiguousSubString);
static readonly _fuzzyFilter: IFilter = or(matchesPrefix, matchesContiguousSubString, matchesFuzzy);
readonly filterErrors: boolean = false;
readonly filterWarnings: boolean = false;
readonly filterInfos: boolean = false;
readonly excludePattern: ParsedExpression = null;
readonly includePattern: ParsedExpression = null;
readonly textFilter: string = '';
constructor(readonly filter: string = '', excludePatterns: IExpression = {}) {
filter = filter.trim();
for (const key of Object.keys(excludePatterns)) {
if (excludePatterns[key]) {
this.setPattern(excludePatterns, key);
}
delete excludePatterns[key];
}
const includePatterns: IExpression = getEmptyExpression();
if (filter) {
const filters = splitGlobAware(filter, ',').map(s => s.trim()).filter(s => !!s.length);
for (const f of filters) {
this.filterErrors = this.filterErrors || this.matches(f, Messages.MARKERS_PANEL_FILTER_ERRORS);
this.filterWarnings = this.filterWarnings || this.matches(f, Messages.MARKERS_PANEL_FILTER_WARNINGS);
this.filterInfos = this.filterInfos || this.matches(f, Messages.MARKERS_PANEL_FILTER_INFOS);
if (strings.startsWith(f, '!')) {
this.setPattern(excludePatterns, strings.ltrim(f, '!'));
} else {
this.setPattern(includePatterns, f);
this.textFilter += ` ${f}`;
}
}
}
if (Object.keys(excludePatterns).length) {
this.excludePattern = parse(excludePatterns);
}
if (Object.keys(includePatterns).length) {
this.includePattern = parse(includePatterns);
}
this.textFilter = this.textFilter.trim();
}
private setPattern(expression: IExpression, pattern: string) {
if (pattern[0] === '.') {
pattern = '*' + pattern; // convert ".js" to "*.js"
}
expression[`**/${pattern}/**`] = true;
expression[`**/${pattern}`] = true;
}
private matches(prefix: string, word: string): boolean {
let result = matchesPrefix(prefix, word);
return result && result.length > 0;
}
}
......@@ -75,65 +75,6 @@ export class RelatedInformation {
constructor(readonly raw: IRelatedInformation) { }
}
// TODO@joao
// export class FilterOptions {
// static readonly _filter: IFilter = or(matchesPrefix, matchesContiguousSubString);
// static readonly _fuzzyFilter: IFilter = or(matchesPrefix, matchesContiguousSubString, matchesFuzzy);
// readonly filterErrors: boolean = false;
// readonly filterWarnings: boolean = false;
// readonly filterInfos: boolean = false;
// readonly excludePattern: glob.ParsedExpression = null;
// readonly includePattern: glob.ParsedExpression = null;
// readonly textFilter: string = '';
// constructor(readonly filter: string = '', excludePatterns: glob.IExpression = {}) {
// filter = filter.trim();
// for (const key of Object.keys(excludePatterns)) {
// if (excludePatterns[key]) {
// this.setPattern(excludePatterns, key);
// }
// delete excludePatterns[key];
// }
// const includePatterns: glob.IExpression = glob.getEmptyExpression();
// if (filter) {
// const filters = glob.splitGlobAware(filter, ',').map(s => s.trim()).filter(s => !!s.length);
// for (const f of filters) {
// this.filterErrors = this.filterErrors || this.matches(f, Messages.MARKERS_PANEL_FILTER_ERRORS);
// this.filterWarnings = this.filterWarnings || this.matches(f, Messages.MARKERS_PANEL_FILTER_WARNINGS);
// this.filterInfos = this.filterInfos || this.matches(f, Messages.MARKERS_PANEL_FILTER_INFOS);
// if (strings.startsWith(f, '!')) {
// this.setPattern(excludePatterns, strings.ltrim(f, '!'));
// } else {
// this.setPattern(includePatterns, f);
// this.textFilter += ` ${f}`;
// }
// }
// }
// if (Object.keys(excludePatterns).length) {
// this.excludePattern = glob.parse(excludePatterns);
// }
// if (Object.keys(includePatterns).length) {
// this.includePattern = glob.parse(includePatterns);
// }
// this.textFilter = this.textFilter.trim();
// }
// private setPattern(expression: glob.IExpression, pattern: string) {
// if (pattern[0] === '.') {
// pattern = '*' + pattern; // convert ".js" to "*.js"
// }
// expression[`**/${pattern}/**`] = true;
// expression[`**/${pattern}`] = true;
// }
// private matches(prefix: string, word: string): boolean {
// let result = matchesPrefix(prefix, word);
// return result && result.length > 0;
// }
// }
export class MarkersModel {
private cachedSortedResources: ResourceMarkers[] | undefined = undefined;
......
......@@ -16,7 +16,7 @@ import Constants from 'vs/workbench/parts/markers/electron-browser/constants';
import { Marker, ResourceMarkers, RelatedInformation, MarkersModel } from 'vs/workbench/parts/markers/electron-browser/markersModel';
import * as Viewer from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions';
import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem, IMarkersFilterActionChangeEvent } 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';
......@@ -31,6 +31,11 @@ import { Iterator } from 'vs/base/common/iterator';
import { ITreeElement } from 'vs/base/browser/ui/tree/tree';
import { debounceEvent } from 'vs/base/common/event';
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions';
import { IExpression, getEmptyExpression } from 'vs/base/common/glob';
import { mixin, deepClone } from 'vs/base/common/objects';
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { isAbsolute, join } from 'vs/base/common/paths';
type TreeElement = ResourceMarkers | Marker | RelatedInformation;
......@@ -70,6 +75,8 @@ export class MarkersPanel extends Panel {
private panelSettings: any;
private panelFoucusContextKey: IContextKey<boolean>;
private filter: Viewer.Filter;
private currentResourceGotAddedToMarkersData: boolean = false;
constructor(
......@@ -80,7 +87,8 @@ export class MarkersPanel extends Panel {
@IThemeService themeService: IThemeService,
@IMarkersWorkbenchService private markersWorkbenchService: IMarkersWorkbenchService,
@IStorageService storageService: IStorageService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
) {
super(Constants.MARKERS_PANEL_ID, telemetryService, themeService);
this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService);
......@@ -103,7 +111,7 @@ export class MarkersPanel extends Panel {
this.createActions();
this.createListeners();
// this.updateFilter();
this.updateFilter();
this.onDidFocus(() => this.panelFoucusContextKey.set(true));
this.onDidBlur(() => this.panelFoucusContextKey.set(false));
......@@ -211,9 +219,49 @@ export class MarkersPanel extends Panel {
return TPromise.as(null);
}
// private updateFilter() {
// this.markersWorkbenchService.filter({ filterText: this.filterAction.filterText, useFilesExclude: this.filterAction.useFilesExclude });
// }
private updateFilter() {
const excludeExpression = this.getExcludeExpression(this.filterAction.useFilesExclude);
this.filter.options = new FilterOptions(this.filterAction.filterText, excludeExpression);
this.tree.refilter();
}
private getExcludeExpression(useFilesExclude: boolean): IExpression {
if (!useFilesExclude) {
return {};
}
const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
if (workspaceFolders.length) {
const result = getEmptyExpression();
for (const workspaceFolder of workspaceFolders) {
mixin(result, this.getExcludesForFolder(workspaceFolder));
}
return result;
} else {
return this.getFilesExclude();
}
}
private getExcludesForFolder(workspaceFolder: IWorkspaceFolder): IExpression {
const expression = this.getFilesExclude(workspaceFolder.uri);
return this.getAbsoluteExpression(expression, workspaceFolder.uri.fsPath);
}
private getFilesExclude(resource?: URI): IExpression {
return deepClone(this.configurationService.getValue('files.exclude', { resource })) || {};
}
private getAbsoluteExpression(expr: IExpression, root: string): IExpression {
return Object.keys(expr)
.reduce((absExpr: IExpression, key: string) => {
if (expr[key] && !isAbsolute(key)) {
const absPattern = join(root, key);
absExpr[absPattern] = expr[key];
}
return absExpr;
}, Object.create(null));
}
private createMessageBox(parent: HTMLElement): void {
this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container'));
......@@ -239,12 +287,15 @@ export class MarkersPanel extends Panel {
this.instantiationService.createInstance(Viewer.MarkerRenderer, a => this.getActionItem(a)),
this.instantiationService.createInstance(Viewer.RelatedInformationRenderer)
];
this.filter = new Viewer.Filter();
this.tree = this.instantiationService.createInstance(WorkbenchObjectTree,
this.treeContainer,
virtualDelegate,
renderers,
{}
{
filter: this.filter
}
) as any as WorkbenchObjectTree<TreeElement>;
// this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, {
......@@ -296,11 +347,11 @@ export class MarkersPanel extends Panel {
this._register(onModelChange(this.onDidChangeModel, this));
this._register(this.editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(this.tree.onDidChangeSelection(() => this.onSelected()));
// this._register(this.filterAction.onDidChange((event: IMarkersFilterActionChangeEvent) => {
// if (event.filterText || event.useFilesExclude) {
// this.updateFilter();
// }
// }));
this._register(this.filterAction.onDidChange((event: IMarkersFilterActionChangeEvent) => {
if (event.filterText || event.useFilesExclude) {
this.updateFilter();
}
}));
this.actions.forEach(a => this._register(a));
}
......
......@@ -16,7 +16,6 @@ import Messages from 'vs/workbench/parts/markers/electron-browser/messages';
import Constants from 'vs/workbench/parts/markers/electron-browser/constants';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachInputBoxStyler, attachStylerCallback, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { IMarkersWorkbenchService } from 'vs/workbench/parts/markers/electron-browser/markers';
......@@ -134,7 +133,7 @@ export class MarkersFilterActionItem extends BaseActionItem {
@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);
......@@ -277,12 +276,12 @@ export class MarkersFilterActionItem extends BaseActionItem {
}
}
// TODO@joao
private reportFilteringUsed(): void {
let data = {};
console.warn('reportFilteringUsed not implemented'); // TODO@joao
// data['errors'] = this.markersWorkbenchService.markersModel.filterOptions.filterErrors;
// data['warnings'] = this.markersWorkbenchService.markersModel.filterOptions.filterWarnings;
// data['infos'] = this.markersWorkbenchService.markersModel.filterOptions.filterInfos;
// let data = {};
// data['errors'] = this.filterOptions.filterErrors;
// data['warnings'] = this.filterOptions.filterWarnings;
// data['infos'] = this.filterOptions.filterInfos;
/* __GDPR__
"problems.filter" : {
"errors" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
......@@ -290,7 +289,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);
}
}
......
......@@ -3,16 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise, Promise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom';
import * as network from 'vs/base/common/network';
import * as paths from 'vs/base/common/paths';
import { IDataSource, ITree, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
import { ITree, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
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, ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/parts/markers/electron-browser/markersModel';
import { ResourceMarkers, Marker, RelatedInformation } 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';
......@@ -24,6 +23,9 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { dirname } from 'vs/base/common/resources';
import { IVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer } from 'vs/base/browser/ui/tree/abstractTree';
import { ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree';
import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions';
import { IMatch } from 'vs/base/common/filters';
interface IResourceMarkersTemplateData {
resourceLabel: ResourceLabel;
......@@ -46,55 +48,6 @@ interface IRelatedInformationTemplateData {
description: HighlightedLabel;
}
export class DataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
if (element instanceof MarkersModel) {
return 'root';
}
// if (element instanceof NodeWithId) {
// return element.id;
// }
return '';
}
public hasChildren(tree: ITree, element: any): boolean {
return element instanceof MarkersModel || element instanceof ResourceMarkers || (element instanceof Marker && element.relatedInformation.length > 0);
}
public getChildren(tree: ITree, element: any): Promise {
if (element instanceof MarkersModel) {
return Promise.as(element.resourceMarkers);
}
if (element instanceof ResourceMarkers) {
return Promise.as(element.markers);
}
if (element instanceof Marker && element.relatedInformation.length > 0) {
return Promise.as(element.relatedInformation);
}
return null;
}
public getParent(tree: ITree, element: any): Promise {
return TPromise.as(null);
}
public shouldAutoexpand(tree: ITree, element: any): boolean {
if (element instanceof MarkersModel) {
return true;
}
if (element instanceof ResourceMarkers) {
return true;
}
if (element instanceof Marker && element.relatedInformation.length > 0) {
return true;
}
return false;
}
}
export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider {
constructor(
......@@ -313,4 +266,109 @@ export class RelatedInformationRenderer implements ITreeRenderer<RelatedInformat
templateData.description.dispose();
templateData.resourceLabel.dispose();
}
}
const enum FilterDataType {
ResourceMarkers,
Marker,
RelatedInformation
}
interface ResourceMarkersFilterData {
type: FilterDataType.ResourceMarkers;
uriMatches: IMatch[];
}
interface MarkerFilterData {
type: FilterDataType.Marker;
messageMatches: IMatch[];
sourceMatches: IMatch[];
codeMatches: IMatch[];
}
interface RelatedInformationFilterData {
type: FilterDataType.RelatedInformation;
}
type FilterData = ResourceMarkersFilterData | MarkerFilterData | RelatedInformationFilterData;
export class Filter implements ITreeFilter<ResourceMarkers | Marker | RelatedInformation, FilterData> {
options = new FilterOptions();
filter(element: ResourceMarkers | Marker | RelatedInformation): TreeFilterResult<FilterData> {
if (element instanceof ResourceMarkers) {
return this.filterResourceMarkers(element);
} else if (element instanceof Marker) {
return this.filterMarker(element);
} else {
return this.filterRelatedInformation(element);
}
}
private filterResourceMarkers(resourceMarkers: ResourceMarkers): TreeFilterResult<FilterData> {
if (resourceMarkers.resource.scheme === network.Schemas.walkThrough || resourceMarkers.resource.scheme === network.Schemas.walkThroughSnippet) {
return false;
}
if (this.options.excludePattern && !!this.options.excludePattern(resourceMarkers.resource.fsPath)) {
return false;
}
if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) {
return true;
}
const uriMatches = FilterOptions._filter(this.options.textFilter, paths.basename(resourceMarkers.resource.fsPath));
if (this.options.textFilter && uriMatches) {
return { visibility: true, data: { type: FilterDataType.ResourceMarkers, uriMatches } };
}
return false;
}
private filterMarker(marker: Marker): TreeFilterResult<FilterData> {
if (this.options.filterErrors && MarkerSeverity.Error === marker.marker.severity) {
return true;
}
if (this.options.filterWarnings && MarkerSeverity.Warning === marker.marker.severity) {
return true;
}
if (this.options.filterInfos && MarkerSeverity.Info === marker.marker.severity) {
return true;
}
if (!this.options.textFilter) {
return true;
}
const messageMatches = FilterOptions._fuzzyFilter(this.options.textFilter, marker.marker.message);
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 || [] } };
}
return TreeVisibility.Recurse;
}
private filterRelatedInformation(relatedInformation: RelatedInformation): TreeFilterResult<FilterData> {
if (!this.options.textFilter) {
return true;
}
const uriMatches = FilterOptions._filter(this.options.textFilter, paths.basename(relatedInformation.raw.resource.fsPath));
const messageMatches = FilterOptions._filter(this.options.textFilter, paths.basename(relatedInformation.raw.message));
if (uriMatches || messageMatches) {
return { visibility: true, data: { type: FilterDataType.RelatedInformation, uriMatches: uriMatches || [], messageMatches: messageMatches || [] } };
}
return false;
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册