From 6badecb8770dd0416a9c41a5f2e0d442c2cc9994 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 May 2020 20:14:36 +1000 Subject: [PATCH] Add TopLevelDirecotry search using DirectoryInfo --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 13 +- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 4 +- .../DirectoryInfo/DirectoryInfoSearch.cs | 114 ++++++++++++++++++ .../Search/ResultManager.cs | 51 ++++++++ .../Search/SearchManager.cs | 31 +++-- 5 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 4ca665cab..283e19fdf 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -2,6 +2,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.Explorer; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; using System; using System.Collections.Generic; @@ -16,7 +17,7 @@ namespace Flow.Launcher.Test.Plugins return new List(); } - private List MethodDirectoryInfoClassSearchReturnsTwoResults(string dummyString) + private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString) { return new List { @@ -111,7 +112,8 @@ namespace Flow.Launcher.Test.Plugins MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsFalse, - "path string not used"); + new Query(), + "string not used"); // Then Assert.IsTrue(results.Count == 2, @@ -130,7 +132,8 @@ namespace Flow.Launcher.Test.Plugins MethodWindowsIndexSearchReturnsZeroResults, MethodDirectoryInfoClassSearchReturnsTwoResults, MethodIndexExistsReturnsTrue, - "path string not used"); + new Query(), + "string not used"); // Then Assert.IsTrue(results.Count == 0, @@ -149,9 +152,7 @@ namespace Flow.Launcher.Test.Plugins public void WhenGivenQuerySearchString_ThenShouldIndicateIfItIsLocationString(string querySearchString, bool expectedResult) { // When, Given - var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - - var result = searchManager.IsLocationPathString(querySearchString); + var result = FilesFolders.IsLocationPathString(querySearchString); //Then Assert.IsTrue(result == expectedResult, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e3f9e9abc..e23fbdb78 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; @@ -42,8 +42,6 @@ namespace Flow.Launcher.Plugin.Explorer return results; return new SearchManager(_settings, Context).Search(query); - - return new SearchManager(_settings, Context).Search(query.Search); } public void Save() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs new file mode 100644 index 000000000..7b55697a9 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -0,0 +1,114 @@ +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.SharedCommands; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows; + +namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo +{ + public class DirectoryInfoSearch + { + private PluginInitContext _context; + + private Settings _settings; + + public DirectoryInfoSearch(Settings settings, PluginInitContext context) + { + _context = context; + _settings = settings; + } + public List TopLevelDirectorySearch(Query query, string search) + { + var results = new List(); + //var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0; + string incompleteName = ""; + //if (hasSpecial || !Directory.Exists(search + "\\")) + if (!FilesFolders.LocationExists(search + "\\")) + { + // if folder doesn't exist, we want to take the last part and use it afterwards to help the user + // find the right folder. + int index = search.LastIndexOf('\\'); + if (index > 0 && index < (search.Length - 1)) + { + incompleteName = search.Substring(index + 1).ToLower(); + search = search.Substring(0, index + 1); + if (!FilesFolders.LocationExists(search)) + { + return results; + } + } + else + { + return results; + } + } + else + { + // folder exist, add \ at the end of doesn't exist + if (!search.EndsWith("\\")) + { + search += "\\"; + } + } + + results.Add(new ResultManager().CreateOpenCurrentFolderResult(incompleteName, search)); + + var searchOption = SearchOption.TopDirectoryOnly; + incompleteName += "*"; + + //// give the ability to search all folder when starting with > + //if (incompleteName.StartsWith(">")) + //{ + // searchOption = SearchOption.AllDirectories; + + // // match everything before and after search term using supported wildcard '*', ie. *searchterm* + // incompleteName = "*" + incompleteName.Substring(1); + //} + + var folderList = new List(); + var fileList = new List(); + + var folderSubtitleString = Constants.DefaultFolderSubtitleString; + + try + { + // search folder and add results + var directoryInfo = new System.IO.DirectoryInfo(search); + var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); + + foreach (var fileSystemInfo in fileSystemInfos) + { + if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; + + if (fileSystemInfo is System.IO.DirectoryInfo) + { + if (searchOption == SearchOption.AllDirectories) + folderSubtitleString = fileSystemInfo.FullName; + + folderList.Add(new ResultManager().CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query)); + } + else + { + fileList.Add(new ResultManager().CreateFileResult(fileSystemInfo.FullName, query)); + } + } + } + catch (Exception e) + { + if (e is UnauthorizedAccessException || e is ArgumentException) + { + results.Add(new Result { Title = e.Message, Score = 501 }); + + return results; + } + + throw; + } + + // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. + return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); //<===== MOVE ORDERING OUT + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 3ee7848d8..796650e58 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -2,6 +2,8 @@ using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Text; using System.Windows; @@ -42,6 +44,55 @@ namespace Flow.Launcher.Plugin.Explorer.Search ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path } }; } + + public Result CreateOpenCurrentFolderResult(string incompleteName, string search) + { + var firstResult = "Open current directory"; + if (incompleteName.Length > 0) + firstResult = "Open " + search; + + var folderName = search.TrimEnd('\\').Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None).Last(); + + return new Result + { + Title = firstResult, + SubTitle = $"Use > to search files and subfolders within {folderName}, " + + $"* to search for file extensions in {folderName} or both >* to combine the search", + IcoPath = search, + Score = 500, + Action = c => + { + FilesFolders.OpenPath(search); + return true; + } + }; + } + + public Result CreateFileResult(string filePath, Query query) + { + var result = new Result + { + Title = Path.GetFileName(filePath), + SubTitle = filePath, + IcoPath = filePath, + TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData, + Action = c => + { + try + { + FilesFolders.OpenPath(filePath); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Could not start " + filePath); + } + + return true; + }, + ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath } + }; + return result; + } } public class SearchResult diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 1f2327e5c..6119f635f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,6 @@ +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; @@ -18,7 +20,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search searcher = new IndexSearcher(_context); } - public List Search(string querySearchString) + internal List Search(Query query) { var querySearch = query.Search; @@ -34,33 +36,36 @@ namespace Flow.Launcher.Plugin.Explorer.Search querySearch = EnvironmentVariables.TranslateEnvironmentVariablePath(querySearch); } + if (FilesFolders.IsLocationPathString(querySearch)) + { return TopLevelFolderSearchBehaviour(WindowsIndexTopLevelFolderSearch, DirectoryInfoClassSearch, WindowsIndexExists, - querySearchString); + query, + querySearch); } - return WindowsIndexFilesAndFoldersSearch(querySearchString); + return WindowsIndexFilesAndFoldersSearch(querySearch); } - private List DirectoryInfoClassSearch(string arg) + private List DirectoryInfoClassSearch(Query query, string querySearch) { - throw new NotImplementedException(); - } + var directoryInfoSearch = new DirectoryInfoSearch(_settings, _context); - /// + return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); } public List TopLevelFolderSearchBehaviour( Func> windowsIndexSearch, - Func> directoryInfoClassSearch, + Func> directoryInfoClassSearch, Func indexExists, - string path) + Query query, + string querySearchString) { - var results = windowsIndexSearch(path); + var results = windowsIndexSearch(querySearchString); - if (results.Count == 0 && !indexExists(path)) - return directoryInfoClassSearch(path); + if (results.Count == 0 && !indexExists(querySearchString)) + return directoryInfoClassSearch(query, querySearchString); return results; } @@ -73,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders); } - + private List WindowsIndexTopLevelFolderSearch(string path) { var queryConstructor = new QueryConstructor(_settings);