diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 06b26347c42..74f66478bd3 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -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); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/markers/electron-browser/markersFilterOptions.ts b/src/vs/workbench/contrib/markers/electron-browser/markersFilterOptions.ts index d60a281553c..ba24dc97c4e 100644 --- a/src/vs/workbench/contrib/markers/electron-browser/markersFilterOptions.ts +++ b/src/vs/workbench/contrib/markers/electron-browser/markersFilterOptions.ts @@ -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(); } diff --git a/src/vs/workbench/contrib/markers/electron-browser/markersPanel.ts b/src/vs/workbench/contrib/markers/electron-browser/markersPanel.ts index 3f43d091c5e..e8efccd8d4d 100644 --- a/src/vs/workbench/contrib/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/electron-browser/markersPanel.ts @@ -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 = { diff --git a/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts index b666987f50f..0a8d4a38d84 100644 --- a/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/electron-browser/markersTreeViewer.ts @@ -406,7 +406,7 @@ export class RelatedInformationRenderer implements ITreeRenderer { - options = new FilterOptions(); + constructor(public options: FilterOptions) { } filter(element: TreeElement, parentVisibility: TreeVisibility): TreeFilterResult { if (element instanceof ResourceMarkers) { @@ -423,7 +423,7 @@ export class Filter implements ITreeFilter { 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 { 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; }