#68927 Use resource glob matcher

This commit is contained in:
Sandeep Somavarapu 2019-02-19 16:28:26 +01:00
parent aca0a5ed9a
commit 1fe15c5f0a
4 changed files with 59 additions and 59 deletions

View file

@ -10,6 +10,8 @@ import { equalsIgnoreCase } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { CharCode } from 'vs/base/common/charCode';
import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob';
import { TernarySearchTree } from 'vs/base/common/map';
export function getComparisonKey(resource: URI): string {
return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString();
@ -295,3 +297,30 @@ export namespace DataUri {
return metadata;
}
}
export class ResourceGlobMatcher {
private readonly globalExpression: ParsedExpression;
private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>();
constructor(
globalExpression: IExpression,
rootExpressions: { root: URI, expression: IExpression }[]
) {
this.globalExpression = parse(globalExpression);
for (const expression of rootExpressions) {
this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) });
}
}
matches(resource: URI): boolean {
const rootExpression = this.expressionsByRoot.findSubstr(resource.toString());
if (rootExpression) {
if (!!rootExpression.expression(relativePath(rootExpression.root, resource))) {
return true;
}
}
return !!this.globalExpression(resource.path);
}
}

View file

@ -5,8 +5,10 @@
import Messages from 'vs/workbench/contrib/markers/electron-browser/messages';
import { IFilter, matchesPrefix, matchesFuzzy, matchesFuzzy2 } from 'vs/base/common/filters';
import { ParsedExpression, IExpression, splitGlobAware, getEmptyExpression, parse } from 'vs/base/common/glob';
import { IExpression, splitGlobAware, getEmptyExpression } from 'vs/base/common/glob';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ResourceGlobMatcher } from 'vs/base/common/resources';
export class FilterOptions {
@ -16,19 +18,17 @@ export class FilterOptions {
readonly filterErrors: boolean = false;
readonly filterWarnings: boolean = false;
readonly filterInfos: boolean = false;
readonly excludePattern: ParsedExpression | null = null;
readonly includePattern: ParsedExpression | null = null;
readonly textFilter: string = '';
readonly excludesMatcher: ResourceGlobMatcher;
readonly includesMatcher: ResourceGlobMatcher;
constructor(readonly filter: string = '', excludePatterns: IExpression = {}) {
constructor(readonly filter: string = '', filesExclude: { root: URI, expression: IExpression }[] | 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();
const filesExcludeByRoot = Array.isArray(filesExclude) ? filesExclude : [];
const excludesExpression: IExpression = Array.isArray(filesExclude) ? getEmptyExpression() : filesExclude;
const includeExpression: IExpression = getEmptyExpression();
if (filter) {
const filters = splitGlobAware(filter, ',').map(s => s.trim()).filter(s => !!s.length);
for (const f of filters) {
@ -36,19 +36,16 @@ export class FilterOptions {
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, '!'));
this.setPattern(excludesExpression, strings.ltrim(f, '!'));
} else {
this.setPattern(includePatterns, f);
this.setPattern(includeExpression, f);
this.textFilter += ` ${f}`;
}
}
}
if (Object.keys(excludePatterns).length) {
this.excludePattern = parse(excludePatterns);
}
if (Object.keys(includePatterns).length) {
this.includePattern = parse(includePatterns);
}
this.excludesMatcher = new ResourceGlobMatcher(excludesExpression, filesExcludeByRoot);
this.includesMatcher = new ResourceGlobMatcher(includeExpression, []);
this.textFilter = this.textFilter.trim();
}

View file

@ -29,11 +29,9 @@ import { ITreeElement, ITreeNode, ITreeContextMenuEvent } from 'vs/base/browser/
import { Relay, Event, Emitter } from 'vs/base/common/event';
import { WorkbenchObjectTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
import { FilterOptions } from 'vs/workbench/contrib/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 { joinWithSlashes } from 'vs/base/common/extpath';
import { isAbsolute } from 'vs/base/common/path';
import { IExpression } from 'vs/base/common/glob';
import { deepClone } from 'vs/base/common/objects';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewModel, ResourceDragAndDrop } from 'vs/workbench/contrib/markers/electron-browser/markersTreeViewer';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
@ -248,8 +246,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
private updateFilter() {
this.cachedFilterStats = undefined;
const excludeExpression = this.getExcludeExpression(this.filterAction.useFilesExclude);
this.filter.options = new FilterOptions(this.filterAction.filterText, excludeExpression);
this.filter.options = new FilterOptions(this.filterAction.filterText, this.getFilesExcludeExpressions());
this.tree.refilter();
this._onDidFilter.fire();
@ -258,44 +255,21 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this.renderMessage();
}
private getExcludeExpression(useFilesExclude: boolean): IExpression {
if (!useFilesExclude) {
return {};
private getFilesExcludeExpressions(): { root: URI, expression: IExpression }[] | IExpression {
if (!this.filterAction.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);
return workspaceFolders.length
? workspaceFolders.map(workspaceFolder => ({ root: workspaceFolder.uri, expression: this.getFilesExclude(workspaceFolder.uri) }))
: this.getFilesExclude();
}
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 = joinWithSlashes(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'));
this.messageBoxContainer.setAttribute('aria-labelledby', 'markers-panel-arialabel');
@ -320,7 +294,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this.instantiationService.createInstance(MarkerRenderer, this.markersViewModel),
this.instantiationService.createInstance(RelatedInformationRenderer)
];
this.filter = new Filter();
this.filter = new Filter(new FilterOptions());
const accessibilityProvider = this.instantiationService.createInstance(MarkersTreeAccessibilityProvider);
const identityProvider = {

View file

@ -406,7 +406,7 @@ export class RelatedInformationRenderer implements ITreeRenderer<RelatedInformat
export class Filter implements ITreeFilter<TreeElement, FilterData> {
options = new FilterOptions();
constructor(public options: FilterOptions) { }
filter(element: TreeElement, parentVisibility: TreeVisibility): TreeFilterResult<FilterData> {
if (element instanceof ResourceMarkers) {
@ -423,7 +423,7 @@ export class Filter implements ITreeFilter<TreeElement, FilterData> {
return false;
}
if (this.options.excludePattern && !!this.options.excludePattern(resourceMarkers.resource.fsPath)) {
if (this.options.excludesMatcher.matches(resourceMarkers.resource)) {
return false;
}
@ -433,7 +433,7 @@ export class Filter implements ITreeFilter<TreeElement, FilterData> {
return { visibility: true, data: { type: FilterDataType.ResourceMarkers, uriMatches } };
}
if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) {
if (this.options.includesMatcher.matches(resourceMarkers.resource)) {
return true;
}