diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index ec96749244c..184dff077d9 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -15,7 +15,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { INavigator } from 'vs/base/common/iterator'; import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet'; -import { Match, FileMatch, FileMatchOrMatch, FolderMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { Match, FileMatch, FileMatchOrMatch, FolderMatch, RenderableMatch } from 'vs/workbench/parts/search/common/searchModel'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import * as Constants from 'vs/workbench/parts/search/common/constants'; import { CollapseAllAction as TreeCollapseAction } from 'vs/base/parts/tree/browser/treeDefaults'; @@ -465,7 +465,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { /** * Returns element to focus after removing the given element */ - public getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: FileMatchOrMatch): FileMatchOrMatch { + public getElementToFocusAfterRemoved(viewer: ITree, elementToBeRemoved: RenderableMatch): RenderableMatch { let elementToFocus = this.getNextElementAfterRemoved(viewer, elementToBeRemoved); if (!elementToFocus) { elementToFocus = this.getPreviousElementAfterRemoved(viewer, elementToBeRemoved); @@ -473,9 +473,12 @@ export abstract class AbstractSearchAndReplaceAction extends Action { return elementToFocus; } - public getNextElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch { + public getNextElementAfterRemoved(viewer: ITree, element: RenderableMatch): RenderableMatch { let navigator: INavigator = this.getNavigatorAt(element, viewer); - if (element instanceof FileMatch) { + if (element instanceof FolderMatch) { + // If file match is removed then next element is the next file match + while (!!navigator.next() && !(navigator.current() instanceof FolderMatch)) { }; + } else if (element instanceof FileMatch) { // If file match is removed then next element is the next file match while (!!navigator.next() && !(navigator.current() instanceof FileMatch)) { }; } else { @@ -484,7 +487,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { return navigator.current(); } - public getPreviousElementAfterRemoved(viewer: ITree, element: FileMatchOrMatch): FileMatchOrMatch { + public getPreviousElementAfterRemoved(viewer: ITree, element: RenderableMatch): RenderableMatch { let navigator: INavigator = this.getNavigatorAt(element, viewer); let previousElement = navigator.previous(); if (element instanceof Match && element.parent().matches().length === 1) { @@ -495,7 +498,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { return previousElement; } - private getNavigatorAt(element: FileMatchOrMatch, viewer: ITree): INavigator { + private getNavigatorAt(element: RenderableMatch, viewer: ITree): INavigator { let navigator: INavigator = viewer.getNavigator(); while (navigator.current() !== element && !!navigator.next()) { } return navigator; @@ -504,7 +507,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { export class RemoveAction extends AbstractSearchAndReplaceAction { - constructor(private viewer: ITree, private element: FileMatchOrMatch) { + constructor(private viewer: ITree, private element: RenderableMatch) { super('remove', nls.localize('RemoveAction.label', "Remove"), 'action-remove'); } @@ -515,13 +518,18 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { } let elementToRefresh: any; - if (this.element instanceof FileMatch) { - let parent: FolderMatch = this.element.parent(); - parent.remove(this.element); + const element = this.element; + if (element instanceof FolderMatch) { + let parent = element.parent(); + parent.remove(element); elementToRefresh = parent; - } else { - let parent: FileMatch = this.element.parent(); - parent.remove(this.element); + } else if (element instanceof FileMatch) { + let parent = element.parent(); + parent.remove(element); + elementToRefresh = parent; + } else if (element instanceof Match) { + let parent = element.parent(); + parent.remove(element); elementToRefresh = parent.count() === 0 ? parent.parent() : parent; } @@ -620,7 +628,7 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { return null; } - private hasSameParent(element: FileMatchOrMatch): boolean { + private hasSameParent(element: RenderableMatch): boolean { return element && element instanceof Match && element.parent().resource() === this.element.parent().resource(); } diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index dea1947b59d..943bad09187 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -114,6 +114,7 @@ export class SearchSorter implements ISorter { interface IFolderMatchTemplate { label: FileLabel; badge: CountBadge; + actions: ActionBar; } interface IFileMatchTemplate { @@ -193,7 +194,8 @@ export class SearchRenderer extends Disposable implements IRenderer { const label = this.instantiationService.createInstance(FileLabel, folderMatchElement, void 0); const badge = new CountBadge(DOM.append(folderMatchElement, DOM.$('.badge'))); this._register(attachBadgeStyler(badge, this.themeService)); - return { label, badge }; + const actions = new ActionBar(folderMatchElement, { animated: false }); + return { label, badge, actions }; } private renderFileMatchTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileMatchTemplate { @@ -234,6 +236,9 @@ export class SearchRenderer extends Disposable implements IRenderer { let count = folderMatch.fileCount(); templateData.badge.setCount(count); templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchFileMatches', "{0} files found", count) : nls.localize('searchFileMatch', "{0} file found", count)); + + templateData.actions.clear(); + templateData.actions.push([new RemoveAction(tree, folderMatch)], { icon: true, label: false }); } private renderFileMatch(tree: ITree, fileMatch: FileMatch, templateData: IFileMatchTemplate): void { diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index d523c6c2663..4e45a8e1d6b 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -549,8 +549,12 @@ export class SearchResult extends Disposable { this.disposeMatches(); } - public remove(match: FileMatch): void { - this.getFolderMatch(match.resource()).remove(match); + public remove(match: FileMatch | FolderMatch): void { + if (match instanceof FileMatch) { + this.getFolderMatch(match.resource()).remove(match); + } else { + match.clear(); + } } public replace(match: FileMatch): TPromise { @@ -788,6 +792,8 @@ export class SearchModel extends Disposable { export type FileMatchOrMatch = FileMatch | Match; +export type RenderableMatch = FolderMatch | FileMatch | Match; + export class SearchWorkbenchService implements ISearchWorkbenchService { _serviceBrand: any;