From 63f2422d52d598ee887a80c572a62b75608e6b5b Mon Sep 17 00:00:00 2001 From: okmttdhr <10101114fin@gmail.com> Date: Sat, 23 Nov 2019 10:11:33 +0900 Subject: [PATCH 1/4] Add expand-all action in search --- .../search/browser/search.contribution.ts | 3 +- .../contrib/search/browser/searchActions.ts | 54 +++++++++++++++++++ .../contrib/search/browser/searchView.ts | 7 +-- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 7676390160619..d3dd446bdff3b 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler'; import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; -import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction, RerunEditorSearchAction, RerunEditorSearchWithContextAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction, RerunEditorSearchAction, RerunEditorSearchWithContextAction, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; @@ -632,6 +632,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); registry.registerWorkbenchAction(SyncActionDescriptor.create(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL), 'Search: Expand All', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSearchOnTypeAction, ToggleSearchOnTypeAction.ID, ToggleSearchOnTypeAction.LABEL), 'Search: Toggle Search on Type', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index b8346873f7470..27ad3edae728a 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -367,6 +367,60 @@ export class CollapseDeepestExpandedLevelAction extends Action { } } +export class ExpandAllAction extends Action { + + static readonly ID: string = 'search.action.expandSearchResults'; + static LABEL: string = nls.localize('ExpandAllAction.label', "Expand All"); + + constructor(id: string, label: string, + @IViewletService private readonly viewletService: IViewletService, + @IPanelService private readonly panelService: IPanelService + ) { + super(id, label, 'search-action codicon-chrome-restore'); + this.update(); + } + + update(): void { + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = !!searchView && searchView.hasSearchResults(); + } + + run(): Promise { + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + const viewer = searchView.getControl(); + + const navigator = viewer.navigate(); + let node = navigator.first(); + let expandFolderMatchLevel = false; + if (node instanceof FolderMatch) { + expandFolderMatchLevel = true; + while (node = navigator.next()) { + if (node instanceof FileMatch) { + expandFolderMatchLevel = false; + break; + } + } + } + + if (expandFolderMatchLevel) { + node = navigator.first(); + do { + if (node instanceof FolderMatch) { + viewer.expand(node); + } + } while (node = navigator.next()); + } else { + viewer.expandAll(); + } + + viewer.domFocus(); + viewer.focusFirst(); + } + return Promise.resolve(undefined); + } +} + export class ClearSearchResultsAction extends Action { static readonly ID: string = 'search.action.clearSearchResults'; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 419611a94625b..9aeff69aa39eb 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -43,7 +43,7 @@ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/act import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IEditor } from 'vs/workbench/common/editor'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; -import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel } from 'vs/workbench/contrib/search/browser/searchActions'; +import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -114,7 +114,7 @@ export class SearchView extends ViewletPane { private state: SearchUIState = SearchUIState.Idle; - private actions: Array = []; + private actions: Array = []; private cancelAction: CancelSearchAction; private refreshAction: RefreshAction; private contextMenu: IMenu | null = null; @@ -210,7 +210,8 @@ export class SearchView extends ViewletPane { this.actions = [ this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), - this._register(this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL)) + this._register(this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL)), + this._register(this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL)) ]; if (this.searchConfig.enableSearchEditorPreview) { From 0d679763c97d966646e2c948433279b239339490 Mon Sep 17 00:00:00 2001 From: okmttdhr <10101114fin@gmail.com> Date: Wed, 4 Dec 2019 22:08:04 +0900 Subject: [PATCH 2/4] Add ToggleCollapseAndExpandAction in search --- .../contrib/search/browser/searchActions.ts | 64 +++++++++++++------ .../contrib/search/browser/searchView.ts | 10 +-- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 27ad3edae728a..be46840c6962d 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -389,33 +389,57 @@ export class ExpandAllAction extends Action { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { const viewer = searchView.getControl(); + viewer.expandAll(); + viewer.domFocus(); + viewer.focusFirst(); + } + return Promise.resolve(undefined); + } +} + +export class ToggleCollapseAndExpandAction extends Action { + static readonly ID: string = 'search.action.collapseOrExpandSearchResults'; + static LABEL: string = nls.localize('ToggleCollapseAndExpandAction.label', "Toggle Collapse and Expand"); + + constructor(id: string, label: string, + private collapseAction: CollapseDeepestExpandedLevelAction, + private expandAction: ExpandAllAction, + @IViewletService private readonly viewletService: IViewletService, + @IPanelService private readonly panelService: IPanelService + ) { + super(id, label, collapseAction.class); + this.update(); + } + + update(): void { + const searchView = getSearchView(this.viewletService, this.panelService); + this.enabled = !!searchView && searchView.hasSearchResults(); + } + isSomeCollapsible(): boolean { + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView) { + const viewer = searchView.getControl(); const navigator = viewer.navigate(); let node = navigator.first(); - let expandFolderMatchLevel = false; - if (node instanceof FolderMatch) { - expandFolderMatchLevel = true; - while (node = navigator.next()) { - if (node instanceof FileMatch) { - expandFolderMatchLevel = false; - break; - } + while (node = navigator.next()) { + if (!viewer.isCollapsed(node)) { + return true; } } + } + return false; + } - if (expandFolderMatchLevel) { - node = navigator.first(); - do { - if (node instanceof FolderMatch) { - viewer.expand(node); - } - } while (node = navigator.next()); - } else { - viewer.expandAll(); + run(): Promise { + if (this.isSomeCollapsible()) { + this.collapseAction.run(); + if (!this.isSomeCollapsible()) { + this.class = this.expandAction.class; } - - viewer.domFocus(); - viewer.focusFirst(); + } else { + this.expandAction.run(); + this.class = this.collapseAction.class; } return Promise.resolve(undefined); } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 9aeff69aa39eb..d4bb5c962bd38 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -43,7 +43,7 @@ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/act import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IEditor } from 'vs/workbench/common/editor'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; -import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel, ExpandAllAction, ToggleCollapseAndExpandAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -114,7 +114,7 @@ export class SearchView extends ViewletPane { private state: SearchUIState = SearchUIState.Idle; - private actions: Array = []; + private actions: Array = []; private cancelAction: CancelSearchAction; private refreshAction: RefreshAction; private contextMenu: IMenu | null = null; @@ -208,10 +208,12 @@ export class SearchView extends ViewletPane { this.addToSearchHistoryDelayer = this._register(new Delayer(500)); + const collapseDeepestExpandedLevelAction = this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL); + const expandAllAction = this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL); + this.actions = [ this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), - this._register(this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL)), - this._register(this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL)) + this._register(this.instantiationService.createInstance(ToggleCollapseAndExpandAction, ToggleCollapseAndExpandAction.ID, ToggleCollapseAndExpandAction.LABEL, collapseDeepestExpandedLevelAction, expandAllAction)), ]; if (this.searchConfig.enableSearchEditorPreview) { From 1b04d72d3f1a30ba373ec1247f73f1bb796ed420 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 12 Dec 2019 20:46:47 -0800 Subject: [PATCH 3/4] Update tree collapse state on manual interaction. Fix bug skipping checking collapse state of first node. --- .../contrib/search/browser/searchActions.ts | 36 +++++++++++-------- .../contrib/search/browser/searchView.ts | 16 ++++++--- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index be46840c6962d..67c8afe1b7e68 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -401,6 +401,9 @@ export class ToggleCollapseAndExpandAction extends Action { static readonly ID: string = 'search.action.collapseOrExpandSearchResults'; static LABEL: string = nls.localize('ToggleCollapseAndExpandAction.label', "Toggle Collapse and Expand"); + // Cache to keep from crawling the tree too often. + private action: CollapseDeepestExpandedLevelAction | ExpandAllAction | undefined; + constructor(id: string, label: string, private collapseAction: CollapseDeepestExpandedLevelAction, private expandAction: ExpandAllAction, @@ -414,34 +417,39 @@ export class ToggleCollapseAndExpandAction extends Action { update(): void { const searchView = getSearchView(this.viewletService, this.panelService); this.enabled = !!searchView && searchView.hasSearchResults(); + this.onTreeCollapseStateChange(); + } + + onTreeCollapseStateChange() { + this.action = undefined; + this.determineAction(); } - isSomeCollapsible(): boolean { + private determineAction(): CollapseDeepestExpandedLevelAction | ExpandAllAction { + if (this.action !== undefined) { return this.action; } + this.action = this.isSomeCollapsible() ? this.collapseAction : this.expandAction; + this.class = this.action.class; + return this.action; + } + + private isSomeCollapsible(): boolean { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { const viewer = searchView.getControl(); const navigator = viewer.navigate(); let node = navigator.first(); - while (node = navigator.next()) { + do { if (!viewer.isCollapsed(node)) { return true; } - } + } while (node = navigator.next()); } return false; } - run(): Promise { - if (this.isSomeCollapsible()) { - this.collapseAction.run(); - if (!this.isSomeCollapsible()) { - this.class = this.expandAction.class; - } - } else { - this.expandAction.run(); - this.class = this.collapseAction.class; - } - return Promise.resolve(undefined); + + async run(): Promise { + await this.determineAction().run(); } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index d4bb5c962bd38..9c1ef9e2056be 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -114,7 +114,8 @@ export class SearchView extends ViewletPane { private state: SearchUIState = SearchUIState.Idle; - private actions: Array = []; + private actions: Array = []; + private toggleCollapseAction: ToggleCollapseAndExpandAction; private cancelAction: CancelSearchAction; private refreshAction: RefreshAction; private contextMenu: IMenu | null = null; @@ -143,6 +144,8 @@ export class SearchView extends ViewletPane { private currentSearchQ = Promise.resolve(); private addToSearchHistoryDelayer: Delayer; + private toggleCollapseStateDelayer: Delayer; + constructor( private position: SearchViewPosition, options: IViewletPaneOptions, @@ -205,15 +208,14 @@ export class SearchView extends ViewletPane { this._register(this.searchHistoryService.onDidClearHistory(() => this.clearHistory())); this.delayedRefresh = this._register(new Delayer(250)); - this.addToSearchHistoryDelayer = this._register(new Delayer(500)); + this.toggleCollapseStateDelayer = this._register(new Delayer(100)); const collapseDeepestExpandedLevelAction = this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL); const expandAllAction = this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL); this.actions = [ this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), - this._register(this.instantiationService.createInstance(ToggleCollapseAndExpandAction, ToggleCollapseAndExpandAction.ID, ToggleCollapseAndExpandAction.LABEL, collapseDeepestExpandedLevelAction, expandAllAction)), ]; if (this.searchConfig.enableSearchEditorPreview) { @@ -224,6 +226,7 @@ export class SearchView extends ViewletPane { this.refreshAction = this._register(this.instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL)); this.cancelAction = this._register(this.instantiationService.createInstance(CancelSearchAction, CancelSearchAction.ID, CancelSearchAction.LABEL)); + this.toggleCollapseAction = this._register(this.instantiationService.createInstance(ToggleCollapseAndExpandAction, ToggleCollapseAndExpandAction.ID, ToggleCollapseAndExpandAction.LABEL, collapseDeepestExpandedLevelAction, expandAllAction)); } getContainer(): HTMLElement { @@ -381,6 +384,7 @@ export class SearchView extends ViewletPane { this.refreshAction.update(); this.cancelAction.update(); + this.toggleCollapseAction.update(); super.updateActions(); } @@ -690,6 +694,9 @@ export class SearchView extends ViewletPane { } })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + this._register(this.tree.onDidChangeCollapseState(() => + this.toggleCollapseStateDelayer.trigger(() => this.toggleCollapseAction.onTreeCollapseStateChange()) + )); const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true, openOnSelection: false })); this._register(Event.debounce(resourceNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => { @@ -1694,7 +1701,8 @@ export class SearchView extends ViewletPane { this.state === SearchUIState.SlowSearch ? this.cancelAction : this.refreshAction, - ...this.actions + ...this.actions, + this.toggleCollapseAction ]; } From 1487fec1e7f0bbaad3783813e5bb03f18d8b8844 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 13 Dec 2019 15:54:12 -0800 Subject: [PATCH 4/4] Use expand-all codicon --- src/vs/workbench/contrib/search/browser/searchActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 7c12c6a0ceb54..785f9f4575506 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -376,7 +376,7 @@ export class ExpandAllAction extends Action { @IViewletService private readonly viewletService: IViewletService, @IPanelService private readonly panelService: IPanelService ) { - super(id, label, 'search-action codicon-chrome-restore'); + super(id, label, 'search-action codicon-expand-all'); this.update(); }