Skip to content

Commit 25bb575

Browse files
authored
add restrict search to folder (microsoft#163597)
1 parent ff80ce6 commit 25bb575

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

src/vs/workbench/contrib/search/browser/search.contribution.ts

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contri
3838
import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files';
3939
import { AnythingQuickAccessProvider } from 'vs/workbench/contrib/search/browser/anythingQuickAccess';
4040
import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions';
41-
import { cancelSearch, clearHistoryCommand, clearSearchResults, CloseReplaceAction, collapseDeepestExpandedLevel, copyAllCommand, copyMatchCommand, copyPathCommand, expandAll, FindInFilesCommand, findOrReplaceInFiles, FocusNextInputAction, focusNextSearchResult, FocusPreviousInputAction, focusPreviousSearchResult, focusSearchListCommand, getSearchView, openSearchView, refreshSearch, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions';
41+
import { cancelSearch, clearHistoryCommand, clearSearchResults, CloseReplaceAction, collapseDeepestExpandedLevel, copyAllCommand, copyMatchCommand, copyPathCommand, expandAll, FindInFilesCommand, findOrReplaceInFiles, FocusNextInputAction, focusNextSearchResult, FocusPreviousInputAction, focusPreviousSearchResult, focusSearchListCommand, getMultiSelectedSearchResources, getSearchView, openSearchView, refreshSearch, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/contrib/search/browser/searchActions';
4242
import { searchClearIcon, searchCollapseAllIcon, searchExpandAllIcon, searchRefreshIcon, searchStopIcon, searchShowAsTree, searchViewIcon, searchShowAsList } from 'vs/workbench/contrib/search/browser/searchIcons';
4343
import { SearchView } from 'vs/workbench/contrib/search/browser/searchView';
4444
import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget';
@@ -47,7 +47,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants';
4747
import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder';
4848
import { getWorkspaceSymbols, IWorkspaceSymbol, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search';
4949
import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService';
50-
import { FileMatch, FileMatchOrMatch, FolderMatch, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel';
50+
import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel';
5151
import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants';
5252
import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor';
5353
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -228,6 +228,19 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
228228
}
229229
});
230230

231+
const restrictSearchToFolderFromSearch: ICommandHandler = async (accessor, folderMatch?: FolderMatchWithResource) => {
232+
return searchInFolderCommand(accessor, false, undefined, folderMatch);
233+
};
234+
235+
const RESTRICT_SEARCH_TO_FOLDER_ID = 'search.restrictSearchToFolder';
236+
KeybindingsRegistry.registerCommandAndKeybindingRule({
237+
id: RESTRICT_SEARCH_TO_FOLDER_ID,
238+
weight: KeybindingWeight.WorkbenchContrib,
239+
when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ResourceFolderFocusKey),
240+
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KeyF,
241+
handler: restrictSearchToFolderFromSearch
242+
});
243+
231244
MenuRegistry.appendMenuItem(MenuId.SearchContext, {
232245
command: {
233246
id: Constants.ReplaceActionId,
@@ -268,6 +281,16 @@ MenuRegistry.appendMenuItem(MenuId.SearchContext, {
268281
order: 2
269282
});
270283

284+
MenuRegistry.appendMenuItem(MenuId.SearchContext, {
285+
group: 'search',
286+
order: 3,
287+
command: {
288+
id: RESTRICT_SEARCH_TO_FOLDER_ID,
289+
title: nls.localize('restrictResultsToFolder', "Restrict Search to Folder...")
290+
},
291+
when: ContextKeyExpr.and(Constants.ResourceFolderFocusKey)
292+
});
293+
271294
KeybindingsRegistry.registerCommandAndKeybindingRule({
272295
id: Constants.CopyMatchCommandId,
273296
weight: KeybindingWeight.WorkbenchContrib,
@@ -583,17 +606,31 @@ const FocusSearchListCommand: ICommandAction = {
583606
};
584607
MenuRegistry.addCommand(FocusSearchListCommand);
585608

609+
const searchInFolderFromExplorer: ICommandHandler = async (accessor, resource?: URI) => {
610+
return searchInFolderCommand(accessor, true, resource);
611+
};
586612

587-
const searchInFolderCommand: ICommandHandler = async (accessor, resource?: URI) => {
613+
const searchInFolderCommand: ICommandHandler = async (accessor, isFromExplorer: boolean, resource?: URI, folderMatch?: FolderMatchWithResource) => {
588614
const listService = accessor.get(IListService);
589615
const fileService = accessor.get(IFileService);
590616
const viewsService = accessor.get(IViewsService);
591617
const contextService = accessor.get(IWorkspaceContextService);
592618
const commandService = accessor.get(ICommandService);
593-
const resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService), accessor.get(IExplorerService));
594619
const searchConfig = accessor.get(IConfigurationService).getValue<ISearchConfiguration>().search;
595620
const mode = searchConfig.mode;
596621

622+
let resources: URI[];
623+
624+
if (isFromExplorer) {
625+
resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService), accessor.get(IExplorerService));
626+
} else {
627+
const searchView = getSearchView(accessor.get(IViewsService));
628+
if (!searchView) {
629+
return;
630+
}
631+
resources = getMultiSelectedSearchResources(searchView.getControl(), folderMatch, searchConfig);
632+
}
633+
597634
const resolvedResources = fileService.resolveAll(resources.map(resource => ({ resource }))).then(results => {
598635
const folders: URI[] = [];
599636
results.forEach(result => {
@@ -619,13 +656,13 @@ const searchInFolderCommand: ICommandHandler = async (accessor, resource?: URI)
619656
}
620657
};
621658

622-
const FIND_IN_FOLDER_ID = 'filesExplorer.findInFolder';
659+
const FIND_IN_FOLDER_EXPLORER_ID = 'filesExplorer.findInFolder';
623660
KeybindingsRegistry.registerCommandAndKeybindingRule({
624-
id: FIND_IN_FOLDER_ID,
661+
id: FIND_IN_FOLDER_EXPLORER_ID,
625662
weight: KeybindingWeight.WorkbenchContrib,
626663
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext),
627664
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KeyF,
628-
handler: searchInFolderCommand
665+
handler: searchInFolderFromExplorer
629666
});
630667

631668
const FIND_IN_WORKSPACE_ID = 'filesExplorer.findInWorkspace';
@@ -652,7 +689,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
652689
group: '4_search',
653690
order: 10,
654691
command: {
655-
id: FIND_IN_FOLDER_ID,
692+
id: FIND_IN_FOLDER_EXPLORER_ID,
656693
title: nls.localize('findInFolder', "Find in Folder...")
657694
},
658695
when: ContextKeyExpr.and(ExplorerFolderContext)

src/vs/workbench/contrib/search/browser/searchActions.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/sea
3131
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
3232
import { ISearchConfiguration, ISearchConfigurationProperties, VIEW_ID } from 'vs/workbench/services/search/common/search';
3333
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
34+
import { URI } from 'vs/base/common/uri';
3435

3536
export function isSearchViewFocused(viewsService: IViewsService): boolean {
3637
const searchView = getSearchView(viewsService);
@@ -683,13 +684,19 @@ export const focusSearchListCommand: ICommandHandler = accessor => {
683684
});
684685
};
685686

686-
function getElementsToOperateOnInfo(viewer: WorkbenchCompressibleObjectTree<RenderableMatch, void>, currElement: RenderableMatch, sortConfig: ISearchConfigurationProperties): { elements: RenderableMatch[]; mustReselect: boolean } {
687+
export function getMultiSelectedSearchResources(viewer: WorkbenchCompressibleObjectTree<RenderableMatch, void>, currElement: RenderableMatch | undefined, sortConfig: ISearchConfigurationProperties): URI[] {
688+
return getElementsToOperateOnInfo(viewer, currElement, sortConfig).elements
689+
.map((renderableMatch) => ((renderableMatch instanceof Match) ? null : renderableMatch.resource))
690+
.filter((renderableMatch): renderableMatch is URI => (renderableMatch !== null));
691+
}
692+
693+
function getElementsToOperateOnInfo(viewer: WorkbenchCompressibleObjectTree<RenderableMatch, void>, currElement: RenderableMatch | undefined, sortConfig: ISearchConfigurationProperties): { elements: RenderableMatch[]; mustReselect: boolean } {
687694
let elements: RenderableMatch[] = viewer.getSelection().filter((x): x is RenderableMatch => x !== null).sort((a, b) => searchComparer(a, b, sortConfig.sortOrder));
688695

689-
const mustReselect = elements.includes(currElement); // this indicates whether we need to re-focus/re-select on a remove.
696+
const mustReselect = !currElement || elements.includes(currElement); // this indicates whether we need to re-focus/re-select on a remove.
690697

691698
// if selection doesn't include multiple elements, just return current focus element.
692-
if (!(elements.length > 1 && elements.includes(currElement))) {
699+
if (currElement && !(elements.length > 1 && elements.includes(currElement))) {
693700
elements = [currElement];
694701
}
695702

src/vs/workbench/contrib/search/browser/searchView.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export class SearchView extends ViewPane {
110110
private fileMatchOrFolderMatchWithResourceFocus: IContextKey<boolean>;
111111
private fileMatchFocused: IContextKey<boolean>;
112112
private folderMatchFocused: IContextKey<boolean>;
113+
private folderMatchWithResourceFocused: IContextKey<boolean>;
113114
private matchFocused: IContextKey<boolean>;
114115
private hasSearchResultsKey: IContextKey<boolean>;
115116
private lastFocusState: 'input' | 'tree' = 'input';
@@ -197,6 +198,7 @@ export class SearchView extends ViewPane {
197198
this.fileMatchOrFolderMatchWithResourceFocus = Constants.FileMatchOrFolderMatchWithResourceFocusKey.bindTo(this.contextKeyService);
198199
this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService);
199200
this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService);
201+
this.folderMatchWithResourceFocused = Constants.ResourceFolderFocusKey.bindTo(this.contextKeyService);
200202
this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService);
201203
this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService);
202204
this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService);
@@ -806,6 +808,7 @@ export class SearchView extends ViewPane {
806808
this.matchFocused.set(focus instanceof Match);
807809
this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch);
808810
this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource);
811+
this.folderMatchWithResourceFocused.set(focus instanceof FolderMatchWithResource);
809812
this.lastFocusState = 'tree';
810813
}
811814
}));
@@ -818,6 +821,7 @@ export class SearchView extends ViewPane {
818821
this.matchFocused.reset();
819822
this.fileMatchOrFolderMatchFocus.reset();
820823
this.fileMatchOrFolderMatchWithResourceFocus.reset();
824+
this.folderMatchWithResourceFocused.reset();
821825
}));
822826
}
823827

src/vs/workbench/contrib/search/common/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const FileMatchOrFolderMatchFocusKey = new RawContextKey<boolean>('fileMa
5757
export const FileMatchOrFolderMatchWithResourceFocusKey = new RawContextKey<boolean>('fileMatchOrFolderMatchWithResourceFocus', false); // Excludes "Other files"
5858
export const FileFocusKey = new RawContextKey<boolean>('fileMatchFocus', false);
5959
export const FolderFocusKey = new RawContextKey<boolean>('folderMatchFocus', false);
60+
export const ResourceFolderFocusKey = new RawContextKey<boolean>('folderMatchWithResourceFocus', false);
6061
export const MatchFocusKey = new RawContextKey<boolean>('matchFocus', false);
6162
export const ViewHasSearchPatternKey = new RawContextKey<boolean>('viewHasSearchPattern', false);
6263
export const ViewHasReplacePatternKey = new RawContextKey<boolean>('viewHasReplacePattern', false);

0 commit comments

Comments
 (0)