From 20188bda9b4e3e5b2ebfdb90f3b6d2462f61771f Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Fri, 24 Oct 2025 19:16:21 -0500 Subject: [PATCH] File search now has filters (#42141) Closes #39260 Search for all files & folders, folders only, or files only. Enjoy. https://github.com/user-attachments/assets/43ba93f5-dfc5-4e73-8414-547cf99dcfcf --- .../ext/Microsoft.CmdPal.Ext.Indexer/Icons.cs | 6 +- .../Indexer/SearchFilters.cs | 27 +++++++++ .../Pages/IndexerPage.cs | 55 ++++++++++++++++--- .../Properties/Resources.Designer.cs | 27 +++++++++ .../Properties/Resources.resx | 9 +++ 5 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchFilters.cs diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Icons.cs index f57b8a2d07..bd9759514c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Icons.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Icons.cs @@ -6,7 +6,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit; namespace Microsoft.CmdPal.Ext.Indexer; -internal sealed class Icons +internal static class Icons { internal static IconInfo FileExplorerSegoeIcon { get; } = new("\uEC50"); @@ -19,4 +19,8 @@ internal sealed class Icons internal static IconInfo DocumentIcon { get; } = new("\uE8A5"); // Document internal static IconInfo FolderOpenIcon { get; } = new("\uE838"); // FolderOpen + + internal static IconInfo FilesIcon { get; } = new("\uF571"); // PrintAllPages + + internal static IconInfo FilterIcon { get; } = new("\uE71C"); // Filter } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchFilters.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchFilters.cs new file mode 100644 index 0000000000..bf2e5dc451 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchFilters.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.Indexer.Properties; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer; + +internal sealed partial class SearchFilters : Filters +{ + public SearchFilters() + { + CurrentFilterId = "all"; + } + + public override IFilterItem[] GetFilters() + { + return [ + new Filter() { Id = "all", Name = Resources.Indexer_Filter_All, Icon = Icons.FilterIcon }, + new Separator(), + new Filter() { Id = "folders", Name = Resources.Indexer_Filter_Folders_Only, Icon = Icons.FolderOpenIcon }, + new Filter() { Id = "files", Name = Resources.Indexer_Filter_Files_Only, Icon = Icons.FilesIcon }, + ]; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs index 3b09fcf149..0ed9458398 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Microsoft.CmdPal.Ext.Indexer.Indexer; using Microsoft.CmdPal.Ext.Indexer.Properties; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; @@ -36,6 +37,11 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable PlaceholderText = Resources.Indexer_PlaceholderText; _searchEngine = new(); _queryCookie = 10; + + var filters = new SearchFilters(); + filters.PropChanged += Filters_PropChanged; + Filters = filters; + CreateEmptyContent(); } @@ -49,6 +55,11 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable initialQuery = query; SearchText = query; disposeSearchEngine = false; + + var filters = new SearchFilters(); + filters.PropChanged += Filters_PropChanged; + Filters = filters; + CreateEmptyContent(); } @@ -79,30 +90,56 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable { // {20D04FE0-3AEA-1069-A2D8-08002B30309D} is CLSID for "This PC" const string template = "search-ms:query={0}&crumb=location:::{{20D04FE0-3AEA-1069-A2D8-08002B30309D}}"; - var encodedSearchText = UrlEncoder.Default.Encode(SearchText); + var fullSearchText = FullSearchString(SearchText); + var encodedSearchText = UrlEncoder.Default.Encode(fullSearchText); var command = string.Format(CultureInfo.CurrentCulture, template, encodedSearchText); ShellHelpers.OpenInShell(command); } public override ICommandItem EmptyContent => _isEmptyQuery ? _noSearchEmptyContent : _nothingFoundEmptyContent; + private void Filters_PropChanged(object sender, IPropChangedEventArgs args) + { + PerformSearch(SearchText); + } + public override void UpdateSearchText(string oldSearch, string newSearch) { if (oldSearch != newSearch && newSearch != initialQuery) { - _ = Task.Run(() => - { - _isEmptyQuery = string.IsNullOrWhiteSpace(newSearch); - Query(newSearch); - LoadMore(); - OnPropertyChanged(nameof(EmptyContent)); - initialQuery = null; - }); + PerformSearch(newSearch); } } + private void PerformSearch(string newSearch) + { + var actualSearch = FullSearchString(newSearch); + _ = Task.Run(() => + { + _isEmptyQuery = string.IsNullOrWhiteSpace(actualSearch); + Query(actualSearch); + LoadMore(); + OnPropertyChanged(nameof(EmptyContent)); + initialQuery = null; + }); + } + public override IListItem[] GetItems() => [.. _indexerListItems]; + private string FullSearchString(string query) + { + switch (Filters.CurrentFilterId) + { + case "folders": + return $"{query} kind:folders"; + case "files": + return $"{query} kind:NOT folders"; + case "all": + default: + return query; + } + } + public override void LoadMore() { IsLoading = true; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs index 44b87b05e2..d4d8f9cd0f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs @@ -177,6 +177,33 @@ namespace Microsoft.CmdPal.Ext.Indexer.Properties { } } + /// + /// Looks up a localized string similar to Files and folders. + /// + internal static string Indexer_Filter_All { + get { + return ResourceManager.GetString("Indexer_Filter_All", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Files. + /// + internal static string Indexer_Filter_Files_Only { + get { + return ResourceManager.GetString("Indexer_Filter_Files_Only", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folders. + /// + internal static string Indexer_Filter_Folders_Only { + get { + return ResourceManager.GetString("Indexer_Filter_Folders_Only", resourceCulture); + } + } + /// /// Looks up a localized string similar to Find file from path. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx index 66504abed1..8e67eae4e9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx @@ -196,4 +196,13 @@ You can try searching all files on this PC or adjust your indexing settings. Search all files + + Files and folders + + + Folders + + + Files + \ No newline at end of file