提交 d50aae12 编写于 作者: B Benjamin Pasero

introduce and use ResourceGlobMatcher

上级 63def6eb
......@@ -36,7 +36,7 @@ import { CloseEditorsInGroupAction, SplitEditorAction, CloseEditorAction, KeepEd
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { ResourceContextKey } from 'vs/workbench/common/resourceContextKey';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Themable } from 'vs/workbench/common/theme';
......
......@@ -2,9 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import objects = require('vs/base/common/objects');
import paths = require('vs/base/common/paths');
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ParsedExpression, IExpression } from 'vs/base/common/glob';
import { basename } from 'vs/base/common/paths';
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -47,4 +55,116 @@ export class ResourceContextKey implements IContextKey<URI> {
public get(): URI {
return this._resourceKey.get();
}
}
export class ResourceGlobMatcher {
private static readonly NO_ROOT = null;
private _onExpressionChange: Emitter<void>;
private toUnbind: IDisposable[];
private mapRootToParsedExpression: Map<string, ParsedExpression>;
private mapRootToExpressionConfig: Map<string, IExpression>;
constructor(
private globFn: (root?: URI) => IExpression,
private parseFn: (expression: IExpression) => ParsedExpression,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IConfigurationService private configurationService: IConfigurationService
) {
this.toUnbind = [];
this.mapRootToParsedExpression = new Map<string, ParsedExpression>();
this.mapRootToExpressionConfig = new Map<string, IExpression>();
this._onExpressionChange = new Emitter<void>();
this.toUnbind.push(this._onExpressionChange);
this.updateExcludes(false);
this.registerListeners();
}
public get onExpressionChange(): Event<void> {
return this._onExpressionChange.event;
}
private registerListeners(): void {
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged()));
this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots()));
}
private onConfigurationChanged(): void {
this.updateExcludes(true);
}
private onDidChangeWorkspaceRoots(): void {
this.updateExcludes(true);
}
private updateExcludes(fromEvent: boolean): void {
let changed = false;
// Add excludes per workspaces that got added
if (this.contextService.hasWorkspace()) {
this.contextService.getWorkspace2().roots.forEach(root => {
const rootExcludes = this.globFn(root);
if (!this.mapRootToExpressionConfig.has(root.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(root.toString()), rootExcludes)) {
changed = true;
this.mapRootToParsedExpression.set(root.toString(), this.parseFn(rootExcludes));
this.mapRootToExpressionConfig.set(root.toString(), objects.clone(rootExcludes));
}
});
}
// Remove excludes per workspace no longer present
this.mapRootToExpressionConfig.forEach((value, root) => {
if (root === ResourceGlobMatcher.NO_ROOT) {
return; // always keep this one
}
if (!this.contextService.getRoot(URI.parse(root))) {
this.mapRootToParsedExpression.delete(root);
this.mapRootToExpressionConfig.delete(root);
changed = true;
}
});
// Always set for resources outside root as well
const globalExcludes = this.globFn();
if (!this.mapRootToExpressionConfig.has(ResourceGlobMatcher.NO_ROOT) || !objects.equals(this.mapRootToExpressionConfig.get(ResourceGlobMatcher.NO_ROOT), globalExcludes)) {
changed = true;
this.mapRootToParsedExpression.set(ResourceGlobMatcher.NO_ROOT, this.parseFn(globalExcludes));
this.mapRootToExpressionConfig.set(ResourceGlobMatcher.NO_ROOT, objects.clone(globalExcludes));
}
if (fromEvent && changed) {
this._onExpressionChange.fire();
}
}
public matches(resource: URI): boolean {
const root = this.contextService.getRoot(resource);
const expressionForRoot = this.mapRootToParsedExpression.get(root ? root.toString() : ResourceGlobMatcher.NO_ROOT);
// If the resource if from a workspace, convert its absolute path to a relative
// path so that glob patterns have a higher probability to match. For example
// a glob pattern of "src/**" will not match on an absolute path "/folder/src/file.txt"
// but can match on "src/file.txt"
let resourcePathToMatch: string;
if (root) {
resourcePathToMatch = paths.normalize(paths.relative(root.fsPath, resource.fsPath));
} else {
resourcePathToMatch = resource.fsPath;
}
return !!expressionForRoot(resourcePathToMatch);
}
public dispose(): void {
this.toUnbind = dispose(this.toUnbind);
}
}
\ No newline at end of file
......@@ -40,7 +40,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ResourceContextKey } from 'vs/workbench/common/resourceContextKey';
import { ResourceContextKey, ResourceGlobMatcher } from 'vs/workbench/common/resources';
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { isLinux } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
......@@ -76,6 +76,8 @@ export class ExplorerView extends CollapsibleView {
private filesExplorerFocussedContext: IContextKey<boolean>;
private explorerFocussedContext: IContextKey<boolean>;
private fileEventsFilter: ResourceGlobMatcher;
private shouldRefresh: boolean;
private autoReveal: boolean;
......@@ -115,6 +117,15 @@ export class ExplorerView extends CollapsibleView {
this.filesExplorerFocussedContext = FilesExplorerFocussedContext.bindTo(contextKeyService);
this.explorerFocussedContext = ExplorerFocussedContext.bindTo(contextKeyService);
this.fileEventsFilter = instantiationService.createInstance(ResourceGlobMatcher, root => this.getFileEventsExcludes(root), (expression: glob.IExpression) => glob.parse(expression));
}
private getFileEventsExcludes(root?: URI): glob.IExpression {
const scope = root ? { resource: root } : void 0;
const configuration = this.configurationService.getConfiguration<IFilesConfiguration>(undefined, scope);
return (configuration && configuration.files && configuration.files.exclude) || Object.create(null);
}
public renderHeader(container: HTMLElement): void {
......@@ -619,16 +630,12 @@ export class ExplorerView extends CollapsibleView {
return false; // we only want added / removed
}
// Getting closest root which is not correct in all cases
const root = this.contextService.getRoot(change.resource);
if (!root) {
return false;
if (!this.contextService.isInsideWorkspace(change.resource)) {
return false; // exclude changes for resources outside of workspace
}
const configuration = this.configurationService.getConfiguration<IFilesConfiguration>(undefined, { resource: root });
const excludesConfig = (configuration && configuration.files && configuration.files.exclude) || Object.create(null);
if (glob.match(excludesConfig, paths.normalize(paths.relative(root.fsPath, change.resource.fsPath)))) {
return false; // hidden through pattern
if (this.fileEventsFilter.matches(change.resource)) {
return false; // excluded via files.exclude setting
}
return true;
......
......@@ -8,7 +8,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import errors = require('vs/base/common/errors');
import URI from 'vs/base/common/uri';
import objects = require('vs/base/common/objects');
import { IEditor } from 'vs/editor/common/editorCommon';
import { IEditor as IBaseEditor, IEditorInput, ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor';
import { EditorInput, IEditorCloseEvent, IEditorRegistry, Extensions, toResource, IEditorGroup } from 'vs/workbench/common/editor';
......@@ -27,9 +26,10 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
import { IWindowService } from 'vs/platform/windows/common/windows';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { getExcludes, ISearchConfiguration } from 'vs/platform/search/common/search';
import { ParsedExpression, parse, IExpression } from 'vs/base/common/glob';
import { parse, IExpression } from 'vs/base/common/glob';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { getPathLabel } from "vs/base/common/labels";
import { IInstantiationService } from "vs/platform/instantiation/common/instantiation";
import { ResourceGlobMatcher } from "vs/workbench/common/resources";
/**
* Stores the selection & view state of an editor and allows to compare it to other selection states.
......@@ -81,67 +81,18 @@ interface ISerializedFileHistoryEntry {
export abstract class BaseHistoryService {
protected toUnbind: IDisposable[];
protected mapRootToExcludes: Map<string, ParsedExpression>;
private mapRootToExpression: Map<string, IExpression>;
private activeEditorListeners: IDisposable[];
constructor(
protected editorGroupService: IEditorGroupService,
protected editorService: IWorkbenchEditorService,
protected contextService: IWorkspaceContextService,
private configurationService: IConfigurationService
protected editorService: IWorkbenchEditorService
) {
this.toUnbind = [];
this.activeEditorListeners = [];
this.mapRootToExcludes = new Map<string, ParsedExpression>();
this.mapRootToExpression = new Map<string, IExpression>();
// Listeners
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged()));
this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(() => this.onDidChangeWorkspaceRoots()));
this.updateExcludes(false);
}
private onConfigurationChanged(): void {
this.updateExcludes(true);
}
private onDidChangeWorkspaceRoots(): void {
this.updateExcludes(true);
}
private updateExcludes(fromEvent: boolean): void {
let changed = false;
// Parse excludes per workspace
if (this.contextService.hasWorkspace()) {
this.contextService.getWorkspace2().roots.forEach(root => {
const rootExcludes = getExcludes(this.configurationService.getConfiguration<ISearchConfiguration>(void 0, { resource: root }));
if (!this.mapRootToExpression.has(root.toString()) || !objects.equals(this.mapRootToExpression.get(root.toString()), rootExcludes)) {
changed = true;
this.mapRootToExcludes.set(root.toString(), parse(rootExcludes));
this.mapRootToExpression.set(root.toString(), objects.clone(rootExcludes));
}
});
}
// Always set for files outside root as well
const globalExcludes = getExcludes(this.configurationService.getConfiguration<ISearchConfiguration>());
if (!this.mapRootToExpression.has(null) || !objects.equals(this.mapRootToExpression.get(null), globalExcludes)) {
changed = true;
this.mapRootToExcludes.set(null, parse(globalExcludes));
this.mapRootToExpression.set(null, objects.clone(globalExcludes));
}
if (fromEvent && changed) {
this.handleExcludesChange();
}
}
private onEditorsChanged(): void {
......@@ -205,35 +156,43 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
private recentlyClosedFiles: IRecentlyClosedFile[];
private loaded: boolean;
private registry: IEditorRegistry;
private resourceFilter: ResourceGlobMatcher;
constructor(
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IStorageService private storageService: IStorageService,
@IConfigurationService configurationService: IConfigurationService,
@IConfigurationService private configurationService: IConfigurationService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IFileService private fileService: IFileService,
@IWindowService private windowService: IWindowService
@IWindowService private windowService: IWindowService,
@IInstantiationService private instantiationService: IInstantiationService,
) {
super(editorGroupService, editorService, contextService, configurationService);
super(editorGroupService, editorService);
this.index = -1;
this.stack = [];
this.recentlyClosedFiles = [];
this.loaded = false;
this.registry = Registry.as<IEditorRegistry>(Extensions.Editors);
this.resourceFilter = instantiationService.createInstance(ResourceGlobMatcher, root => this.getExcludes(root), (expression: IExpression) => parse(expression));
this.registerListeners();
}
private getExcludes(root?: URI): IExpression {
const scope = root ? { resource: root } : void 0;
return getExcludes(this.configurationService.getConfiguration<ISearchConfiguration>(void 0, scope));
}
private registerListeners(): void {
this.toUnbind.push(this.lifecycleService.onShutdown(reason => this.save()));
this.toUnbind.push(this.editorGroupService.onEditorOpenFail(editor => this.remove(editor)));
this.toUnbind.push(this.editorGroupService.getStacksModel().onEditorClosed(event => this.onEditorClosed(event)));
// File changes
this.toUnbind.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.toUnbind.push(this.resourceFilter.onExpressionChange(() => this.handleExcludesChange()));
}
private onFileChanges(e: FileChangesEvent): void {
......@@ -422,10 +381,8 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
}
const resourceInput = input as IResourceInput;
const rootForInput = this.contextService.getRoot(resourceInput.resource);
const excludesForInput = this.mapRootToExcludes.get(rootForInput ? rootForInput.toString() : null);
return !excludesForInput(getPathLabel(resourceInput.resource, this.contextService));
return !this.resourceFilter.matches(resourceInput.resource);
}
protected handleExcludesChange(): void {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册