mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #1817 from VictoriousRaptor/FixExplorer
Fix bugs in explorer plugin
This commit is contained in:
commit
db2b856a66
10 changed files with 265 additions and 122 deletions
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
#pragma warning disable IDE0005
|
||||
using System.Windows;
|
||||
#pragma warning restore IDE0005
|
||||
|
||||
namespace Flow.Launcher.Plugin.SharedCommands
|
||||
{
|
||||
|
|
@ -206,22 +208,16 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
///</summary>
|
||||
public static string GetPreviousExistingDirectory(Func<string, bool> locationExists, string path)
|
||||
{
|
||||
var previousDirectoryPath = "";
|
||||
var index = path.LastIndexOf('\\');
|
||||
if (index > 0 && index < (path.Length - 1))
|
||||
{
|
||||
previousDirectoryPath = path.Substring(0, index + 1);
|
||||
if (!locationExists(previousDirectoryPath))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
string previousDirectoryPath = path.Substring(0, index + 1);
|
||||
return locationExists(previousDirectoryPath) ? previousDirectoryPath : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return previousDirectoryPath;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
@ -241,5 +237,33 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if <paramref name="parentPath"/> contains <paramref name="subPath"/>.
|
||||
/// From https://stackoverflow.com/a/66877016
|
||||
/// </summary>
|
||||
/// <param name="parentPath">Parent path</param>
|
||||
/// <param name="subPath">Sub path</param>
|
||||
/// <param name="allowEqual">If <see langword="true"/>, when <paramref name="parentPath"/> and <paramref name="subPath"/> are equal, returns <see langword="true"/></param>
|
||||
/// <returns></returns>
|
||||
public static bool PathContains(string parentPath, string subPath, bool allowEqual = false)
|
||||
{
|
||||
var rel = Path.GetRelativePath(parentPath.EnsureTrailingSlash(), subPath);
|
||||
return (rel != "." || allowEqual)
|
||||
&& rel != ".."
|
||||
&& !rel.StartsWith("../")
|
||||
&& !rel.StartsWith(@"..\")
|
||||
&& !Path.IsPathRooted(rel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns path ended with "\"
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static string EnsureTrailingSlash(this string path)
|
||||
{
|
||||
return path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
53
Flow.Launcher.Test/FilesFoldersTest.cs
Normal file
53
Flow.Launcher.Test/FilesFoldersTest.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class FilesFoldersTest
|
||||
{
|
||||
// Testcases from https://stackoverflow.com/a/31941905/20703207
|
||||
// Disk
|
||||
[TestCase(@"c:", @"c:\foo", true)]
|
||||
[TestCase(@"c:\", @"c:\foo", true)]
|
||||
// Slash
|
||||
[TestCase(@"c:\foo\bar\", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo\bar", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\bar", true)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo\bar", true)]
|
||||
// File
|
||||
[TestCase(@"c:\foo", @"c:\foo\a.txt", true)]
|
||||
[TestCase(@"c:\foo", @"c:/foo/a.txt", true)]
|
||||
[TestCase(@"c:\FOO\a.txt", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo\a.txt", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foobar\a.txt", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foobar\a.txt", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo.txt", false)]
|
||||
// Prefix
|
||||
[TestCase(@"c:\foo", @"c:\foobar", false)]
|
||||
[TestCase(@"C:\Program", @"C:\Program Files\", false)]
|
||||
[TestCase(@"c:\foobar", @"c:\foo\a.txt", false)]
|
||||
[TestCase(@"c:\foobar\", @"c:\foo\a.txt", false)]
|
||||
// Edge case
|
||||
[TestCase(@"c:\foo", @"c:\foo\..\bar\baz", false)]
|
||||
[TestCase(@"c:\bar", @"c:\foo\..\bar\baz", true)]
|
||||
[TestCase(@"c:\barr", @"c:\foo\..\bar\baz", false)]
|
||||
// Equality
|
||||
[TestCase(@"c:\foo", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", false)]
|
||||
public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo", @"c:\foo", true)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo", true)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", true)]
|
||||
public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeTrue(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,11 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Flow.Launcher.Plugin.Explorer.Search.SearchManager;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
|
|
@ -176,7 +178,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
// When
|
||||
var result = SearchManager.IsFileContentSearch(query.ActionKeyword);
|
||||
var result = searchManager.IsFileContentSearch(query.ActionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(result,
|
||||
|
|
@ -193,6 +195,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase(@"c:\>*", true)]
|
||||
[TestCase(@"c:\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation", true)]
|
||||
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
|
||||
{
|
||||
// When, Given
|
||||
|
|
@ -393,5 +396,68 @@ namespace Flow.Launcher.Test.Plugins
|
|||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo", @"c:\foo", true)]
|
||||
[TestCase(@"C:\Foo\", @"c:\foo\", true)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", false)]
|
||||
public void GivenTwoPaths_WhenCompared_ThenShouldBeExpectedSameOrDifferent(string path1, string path2, bool expectedResult)
|
||||
{
|
||||
// Given
|
||||
var comparator = PathEqualityComparator.Instance;
|
||||
var result1 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path1),
|
||||
SubTitle = path1
|
||||
};
|
||||
var result2 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path2),
|
||||
SubTitle = path2
|
||||
};
|
||||
|
||||
// When, Then
|
||||
Assert.AreEqual(expectedResult, comparator.Equals(result1, result2));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo\", @"c:\foo\")]
|
||||
[TestCase(@"C:\Foo\", @"c:\foo\")]
|
||||
public void GivenTwoPaths_WhenComparedHasCode_ThenShouldBeSame(string path1, string path2)
|
||||
{
|
||||
// Given
|
||||
var comparator = PathEqualityComparator.Instance;
|
||||
var result1 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path1),
|
||||
SubTitle = path1
|
||||
};
|
||||
var result2 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path2),
|
||||
SubTitle = path2
|
||||
};
|
||||
|
||||
var hash1 = comparator.GetHashCode(result1);
|
||||
var hash2 = comparator.GetHashCode(result2);
|
||||
|
||||
// When, Then
|
||||
Assert.IsTrue(hash1 == hash2);
|
||||
}
|
||||
|
||||
[TestCase(@"%appdata%", true)]
|
||||
[TestCase(@"%appdata%\123", true)]
|
||||
[TestCase(@"c:\foo %appdata%\", false)]
|
||||
[TestCase(@"c:\users\%USERNAME%\downloads", true)]
|
||||
[TestCase(@"c:\downloads", false)]
|
||||
[TestCase(@"%", false)]
|
||||
[TestCase(@"%%", false)]
|
||||
[TestCase(@"%bla%blabla%", false)]
|
||||
public void GivenPath_WhenHavingEnvironmentVariableOrNot_ThenShouldBeExpected(string path, bool expectedResult)
|
||||
{
|
||||
// When
|
||||
var result = EnvironmentVariables.HasEnvironmentVar(path);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Exceptions;
|
||||
|
||||
|
|
@ -20,7 +18,7 @@ public class EngineNotAvailableException : Exception
|
|||
string engineName,
|
||||
string resolution,
|
||||
string message,
|
||||
Func<ActionContext, ValueTask<bool>> action = null) : base(message)
|
||||
Func<ActionContext, ValueTask<bool>>? action = null) : base(message)
|
||||
{
|
||||
EngineName = engineName;
|
||||
Resolution = resolution;
|
||||
|
|
@ -40,6 +38,23 @@ public class EngineNotAvailableException : Exception
|
|||
EngineName = engineName;
|
||||
Resolution = resolution;
|
||||
}
|
||||
|
||||
public EngineNotAvailableException(
|
||||
string engineName,
|
||||
string resolution,
|
||||
string message,
|
||||
string errorIconPath,
|
||||
Func<ActionContext, ValueTask<bool>>? action = null) : base(message)
|
||||
{
|
||||
EngineName = engineName;
|
||||
Resolution = resolution;
|
||||
ErrorIcon = errorIconPath;
|
||||
Action = action ?? (_ =>
|
||||
{
|
||||
Clipboard.SetDataObject(this.ToString());
|
||||
return ValueTask.FromResult(true);
|
||||
});
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,70 +1,75 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
{
|
||||
public static class EnvironmentVariables
|
||||
{
|
||||
internal static bool IsEnvironmentVariableSearch(string search)
|
||||
private static Dictionary<string, string> _envStringPaths = null;
|
||||
private static Dictionary<string, string> EnvStringPaths
|
||||
{
|
||||
return search.StartsWith("%")
|
||||
&& search != "%%"
|
||||
&& !search.Contains("\\") &&
|
||||
LoadEnvironmentStringPaths().Count > 0;
|
||||
get
|
||||
{
|
||||
if (_envStringPaths == null)
|
||||
{
|
||||
LoadEnvironmentStringPaths();
|
||||
}
|
||||
return _envStringPaths;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<string, string> LoadEnvironmentStringPaths()
|
||||
internal static bool IsEnvironmentVariableSearch(string search)
|
||||
{
|
||||
var envStringPaths = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
return search.StartsWith("%")
|
||||
&& search != "%%"
|
||||
&& !search.Contains('\\')
|
||||
&& EnvStringPaths.Count > 0;
|
||||
}
|
||||
|
||||
public static bool HasEnvironmentVar(string search)
|
||||
{
|
||||
// "c:\foo %appdata%\" returns false
|
||||
var splited = search.Split(Path.DirectorySeparatorChar);
|
||||
return splited.Any(dir => dir.StartsWith('%') &&
|
||||
dir.EndsWith('%') &&
|
||||
dir.Length > 2 &&
|
||||
dir.Split('%').Length == 3);
|
||||
}
|
||||
|
||||
private static void LoadEnvironmentStringPaths()
|
||||
{
|
||||
_envStringPaths = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
var homedrive = Environment.GetEnvironmentVariable("HOMEDRIVE")?.EnsureTrailingSlash() ?? "C:\\";
|
||||
|
||||
foreach (DictionaryEntry special in Environment.GetEnvironmentVariables())
|
||||
{
|
||||
var path = special.Value.ToString();
|
||||
// we add a trailing slash to the path to make sure drive paths become valid absolute paths.
|
||||
// for example, if %systemdrive% is C: we turn it to C:\
|
||||
path = path.EnsureTrailingSlash();
|
||||
|
||||
// if we don't have an absolute path, we use Path.GetFullPath to get one.
|
||||
// for example, if %homepath% is \Users\John we turn it to C:\Users\John
|
||||
// Add basepath for GetFullPath() to parse %HOMEPATH% correctly
|
||||
path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path, homedrive);
|
||||
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
// we add a trailing slash to the path to make sure drive paths become valid absolute paths.
|
||||
// for example, if %systemdrive% is C: we turn it to C:\
|
||||
path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
|
||||
// if we don't have an absolute path, we use Path.GetFullPath to get one.
|
||||
// for example, if %homepath% is \Users\John we turn it to C:\Users\John
|
||||
path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path);
|
||||
|
||||
// Variables are returned with a mixture of all upper/lower case.
|
||||
// Call ToLower() to make the results look consistent
|
||||
envStringPaths.Add(special.Key.ToString().ToLower(), path);
|
||||
// Call ToUpper() to make the results look consistent
|
||||
_envStringPaths.Add(special.Key.ToString().ToUpper(), path);
|
||||
}
|
||||
}
|
||||
|
||||
return envStringPaths;
|
||||
}
|
||||
|
||||
internal static string TranslateEnvironmentVariablePath(string environmentVariablePath)
|
||||
{
|
||||
var envStringPaths = LoadEnvironmentStringPaths();
|
||||
var splitSearch = environmentVariablePath.Substring(1).Split("%");
|
||||
var exactEnvStringPath = splitSearch[0];
|
||||
|
||||
// if there are more than 2 % characters in the query, don't bother
|
||||
if (splitSearch.Length == 2 && envStringPaths.ContainsKey(exactEnvStringPath))
|
||||
{
|
||||
var queryPartToReplace = $"%{exactEnvStringPath}%";
|
||||
var expandedPath = envStringPaths[exactEnvStringPath];
|
||||
// replace the %envstring% part of the query with its expanded equivalent
|
||||
return environmentVariablePath.Replace(queryPartToReplace, expandedPath);
|
||||
}
|
||||
|
||||
return environmentVariablePath;
|
||||
}
|
||||
|
||||
internal static List<Result> GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
|
||||
var environmentVariables = LoadEnvironmentStringPaths();
|
||||
var search = querySearch;
|
||||
|
||||
if (querySearch.EndsWith("%") && search.Length > 1)
|
||||
|
|
@ -72,12 +77,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
// query starts and ends with a %, find an exact match from env-string paths
|
||||
search = querySearch.Substring(1, search.Length - 2);
|
||||
|
||||
if (environmentVariables.ContainsKey(search))
|
||||
if (EnvStringPaths.ContainsKey(search))
|
||||
{
|
||||
var expandedPath = environmentVariables[search];
|
||||
|
||||
var expandedPath = EnvStringPaths[search];
|
||||
|
||||
results.Add(ResultManager.CreateFolderResult($"%{search}%", expandedPath, expandedPath, query));
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
|
@ -90,8 +95,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
{
|
||||
search = search.Substring(1);
|
||||
}
|
||||
|
||||
foreach (var p in environmentVariables)
|
||||
|
||||
foreach (var p in EnvStringPaths)
|
||||
{
|
||||
if (p.Key.StartsWith(search, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,20 +27,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_click_to_launch_or_install"),
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_is_not_running"),
|
||||
ClickToInstallEverythingAsync)
|
||||
{
|
||||
ErrorIcon = Constants.EverythingErrorImagePath
|
||||
};
|
||||
Constants.EverythingErrorImagePath,
|
||||
ClickToInstallEverythingAsync);
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
throw new EngineNotAvailableException(
|
||||
Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
|
||||
"Please check whether your system is x86 or x64",
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue"))
|
||||
{
|
||||
ErrorIcon = Constants.GeneralSearchErrorImagePath
|
||||
};
|
||||
Constants.GeneralSearchErrorImagePath,
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue"));
|
||||
}
|
||||
}
|
||||
private async ValueTask<bool> ClickToInstallEverythingAsync(ActionContext _)
|
||||
|
|
@ -72,16 +68,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
if (!Settings.EnableEverythingContentSearch)
|
||||
{
|
||||
throw new EngineNotAvailableException(Enum.GetName(Settings.IndexSearchEngineOption.Everything)!,
|
||||
"Click to Enable Everything Content Search (only applicable to Everything 1.5+ with indexed content)",
|
||||
"Everything Content Search is not enabled.",
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search"),
|
||||
Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search_tips"),
|
||||
Constants.EverythingErrorImagePath,
|
||||
_ =>
|
||||
{
|
||||
Settings.EnableEverythingContentSearch = true;
|
||||
return ValueTask.FromResult(true);
|
||||
})
|
||||
{
|
||||
ErrorIcon = Constants.EverythingErrorImagePath
|
||||
};
|
||||
});
|
||||
}
|
||||
if (token.IsCancellationRequested)
|
||||
yield break;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
{
|
||||
Title = title,
|
||||
IcoPath = path,
|
||||
SubTitle = Path.GetDirectoryName(path),
|
||||
SubTitle = subtitle,
|
||||
AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder),
|
||||
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
|
||||
CopyText = path,
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
{
|
||||
public class SearchManager
|
||||
{
|
||||
internal static PluginInitContext Context;
|
||||
internal PluginInitContext Context;
|
||||
|
||||
internal static Settings Settings;
|
||||
internal Settings Settings;
|
||||
|
||||
public SearchManager(Settings settings, PluginInitContext context)
|
||||
{
|
||||
|
|
@ -23,19 +23,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
Settings = settings;
|
||||
}
|
||||
|
||||
private class PathEqualityComparator : IEqualityComparer<Result>
|
||||
/// <summary>
|
||||
/// Note: A path that ends with "\" and one that doesn't will not be regarded as equal.
|
||||
/// </summary>
|
||||
public class PathEqualityComparator : IEqualityComparer<Result>
|
||||
{
|
||||
private static PathEqualityComparator instance;
|
||||
public static PathEqualityComparator Instance => instance ??= new PathEqualityComparator();
|
||||
|
||||
public bool Equals(Result x, Result y)
|
||||
{
|
||||
return x.Title == y.Title && x.SubTitle == y.SubTitle;
|
||||
return x.Title.Equals(y.Title, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(x.SubTitle, y.SubTitle, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public int GetHashCode(Result obj)
|
||||
{
|
||||
return HashCode.Combine(obj.Title.GetHashCode(), obj.SubTitle?.GetHashCode() ?? 0);
|
||||
return HashCode.Combine(obj.Title.ToLowerInvariant(), obj.SubTitle?.ToLowerInvariant() ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,19 +109,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false))
|
||||
results.Add(ResultManager.CreateResult(query, search));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return new List<Result>();
|
||||
}
|
||||
catch (EngineNotAvailableException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is OperationCanceledException)
|
||||
return results.ToList();
|
||||
|
||||
if (e is EngineNotAvailableException)
|
||||
throw;
|
||||
|
||||
throw new SearchException(engineName, e.Message, e);
|
||||
}
|
||||
|
||||
|
||||
results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any(
|
||||
excludedPath => r.SubTitle.StartsWith(excludedPath.Path, StringComparison.OrdinalIgnoreCase)));
|
||||
excludedPath => FilesFolders.PathContains(excludedPath.Path, r.SubTitle)));
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
|
|
@ -142,7 +148,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
};
|
||||
}
|
||||
|
||||
private static List<Result> EverythingContentSearchResult(Query query)
|
||||
private List<Result> EverythingContentSearchResult(Query query)
|
||||
{
|
||||
return new List<Result>()
|
||||
{
|
||||
|
|
@ -167,18 +173,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
var results = new HashSet<Result>(PathEqualityComparator.Instance);
|
||||
|
||||
var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch);
|
||||
|
||||
if (isEnvironmentVariable)
|
||||
if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch))
|
||||
return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, Context);
|
||||
|
||||
// Query is a location path with a full environment variable, eg. %appdata%\somefolder\
|
||||
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
|
||||
|
||||
var locationPath = querySearch;
|
||||
|
||||
if (isEnvironmentVariablePath)
|
||||
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
|
||||
// Query is a location path with a full environment variable, eg. %appdata%\somefolder\, c:\users\%USERNAME%\downloads
|
||||
var needToExpand = EnvironmentVariables.HasEnvironmentVar(querySearch);
|
||||
var locationPath = needToExpand ? Environment.ExpandEnvironmentVariables(querySearch) : querySearch;
|
||||
|
||||
// Check that actual location exists, otherwise directory search will throw directory not found exception
|
||||
if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists())
|
||||
|
|
@ -234,7 +234,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return results.ToList();
|
||||
}
|
||||
|
||||
public static bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword;
|
||||
public bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword;
|
||||
|
||||
|
||||
private bool UseWindowsIndexForDirectorySearch(string locationPath)
|
||||
|
|
@ -245,10 +245,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(pathToDirectory).StartsWith(x.Path, StringComparison.OrdinalIgnoreCase))
|
||||
&& WindowsIndex.WindowsIndex.PathIsIndexed(pathToDirectory);
|
||||
}
|
||||
|
||||
|
||||
internal static bool IsEnvironmentVariableSearch(string search)
|
||||
{
|
||||
return search.StartsWith("%")
|
||||
return search.StartsWith("%")
|
||||
&& search != "%%"
|
||||
&& !search.Contains('\\');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,27 +97,25 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
private IAsyncEnumerable<SearchResult> HandledEngineNotAvailableExceptionAsync()
|
||||
{
|
||||
if (!SearchManager.Settings.WarnWindowsSearchServiceOff)
|
||||
if (!Settings.WarnWindowsSearchServiceOff)
|
||||
return AsyncEnumerable.Empty<SearchResult>();
|
||||
|
||||
var api = SearchManager.Context.API;
|
||||
var api = Main.Context.API;
|
||||
|
||||
throw new EngineNotAvailableException(
|
||||
"Windows Index",
|
||||
api.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
|
||||
api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
|
||||
Constants.WindowsIndexErrorImagePath,
|
||||
c =>
|
||||
{
|
||||
SearchManager.Settings.WarnWindowsSearchServiceOff = false;
|
||||
Settings.WarnWindowsSearchServiceOff = false;
|
||||
|
||||
// Clears the warning message so user is not mistaken that it has not worked
|
||||
api.ChangeQuery(string.Empty);
|
||||
|
||||
return ValueTask.FromResult(false);
|
||||
})
|
||||
{
|
||||
ErrorIcon = Constants.WindowsIndexErrorImagePath
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,8 +470,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
|
||||
var paths = pathEnv.Split(";", StringSplitOptions.RemoveEmptyEntries).DistinctBy(p => p.ToLowerInvariant());
|
||||
|
||||
var toFilter = paths.Where(x => commonParents.All(parent => !IsSubPathOf(x, parent)))
|
||||
|
||||
var toFilter = paths.Where(x => commonParents.All(parent => !FilesFolders.PathContains(parent, x)))
|
||||
.AsParallel()
|
||||
.SelectMany(p => EnumerateProgramsInDir(p, suffixes, recursive: false));
|
||||
|
||||
|
|
@ -763,17 +763,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/66877016
|
||||
private static bool IsSubPathOf(string subPath, string basePath)
|
||||
{
|
||||
var rel = Path.GetRelativePath(basePath, subPath);
|
||||
return rel != "."
|
||||
&& rel != ".."
|
||||
&& !rel.StartsWith("../")
|
||||
&& !rel.StartsWith(@"..\")
|
||||
&& !Path.IsPathRooted(rel);
|
||||
}
|
||||
|
||||
private static List<string> GetCommonParents(IEnumerable<ProgramSource> programSources)
|
||||
{
|
||||
// To avoid unnecessary io
|
||||
|
|
@ -785,8 +774,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
HashSet<ProgramSource> parents = group.ToHashSet();
|
||||
foreach (var source in group)
|
||||
{
|
||||
if (parents.Any(p => IsSubPathOf(source.Location, p.Location) &&
|
||||
source != p))
|
||||
if (parents.Any(p => FilesFolders.PathContains(p.Location, source.Location)))
|
||||
{
|
||||
parents.Remove(source);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue