diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs
index 1a1b17539..273698b86 100644
--- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs
+++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs
@@ -54,4 +54,4 @@ namespace Flow.Launcher.Core.Plugin
return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null;
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs
index 5a5ba1971..26167e945 100644
--- a/Flow.Launcher.Core/Plugin/PluginManager.cs
+++ b/Flow.Launcher.Core/Plugin/PluginManager.cs
@@ -320,4 +320,4 @@ namespace Flow.Launcher.Core.Plugin
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs
index b18c07e3c..fcf178445 100644
--- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs
+++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs
@@ -183,4 +183,4 @@ namespace Flow.Launcher.Core.Plugin
});
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs
index b203967de..44c34968c 100644
--- a/Flow.Launcher.Core/Updater.cs
+++ b/Flow.Launcher.Core/Updater.cs
@@ -35,7 +35,8 @@ namespace Flow.Launcher.Core
UpdateInfo newUpdateInfo;
if (!silentUpdate)
- api.ShowMsg("Please wait...", "Checking for new update");
+ api.ShowMsg(api.GetTranslation("pleaseWait"),
+ api.GetTranslation("update_flowlauncher_update_check"));
using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
@@ -51,12 +52,13 @@ namespace Flow.Launcher.Core
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
- MessageBox.Show("You already have the latest Flow Launcher version");
+ MessageBox.Show(api.GetTranslation("update_flowlauncher_already_on_latest"));
return;
}
if (!silentUpdate)
- api.ShowMsg("Update found", "Updating...");
+ api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"),
+ api.GetTranslation("update_flowlauncher_updating"));
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
@@ -67,8 +69,9 @@ namespace Flow.Launcher.Core
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
- MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
- $"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
+ MessageBox.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
+ DataLocation.PortableDataPath,
+ targetDestination));
}
else
{
@@ -79,7 +82,7 @@ namespace Flow.Launcher.Core
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
- if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ if (MessageBox.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
@@ -87,7 +90,8 @@ namespace Flow.Launcher.Core
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
- api.ShowMsg("Update Failed", "Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.");
+ api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
+ api.GetTranslation("update_flowlauncher_check_connection"));
return;
}
}
diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
index 73f565a33..ac333d567 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
@@ -39,7 +39,6 @@ namespace Flow.Launcher.Infrastructure.Image
var usage = LoadStorageToConcurrentDictionary();
-
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
{
ImageSource img = new BitmapImage(new Uri(icon));
diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
index 268dc20b8..f0e4a79fc 100644
--- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
@@ -9,7 +9,7 @@ namespace Flow.Launcher.Infrastructure.Storage
///
/// Serialize object using json format.
///
- public class JsonStrorage
+ public class JsonStrorage where T : new()
{
private readonly JsonSerializerOptions _serializerSettings;
private T _data;
@@ -76,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Storage
BackupOriginFile();
}
- _data = JsonSerializer.Deserialize("{}", _serializerSettings);
+ _data = new T();
Save();
}
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 432235fa7..830106d32 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -14,10 +14,10 @@
- 1.3.1
- 1.3.1
- 1.3.1
- 1.3.1
+ 1.4.0
+ 1.4.0
+ 1.4.0
+ 1.4.0
Flow.Launcher.Plugin
Flow-Launcher
MIT
@@ -64,4 +64,4 @@
-
\ No newline at end of file
+
diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs
index 36f098e7d..b0b41cc22 100644
--- a/Flow.Launcher.Plugin/IAsyncPlugin.cs
+++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs
@@ -4,9 +4,28 @@ using System.Threading.Tasks;
namespace Flow.Launcher.Plugin
{
+ ///
+ /// Asynchronous Plugin Model for Flow Launcher
+ ///
public interface IAsyncPlugin
{
+ ///
+ /// Asynchronous Querying
+ ///
+ ///
+ /// If the Querying or Init method requires high IO transmission
+ /// or performing CPU intense jobs (performing better with cancellation), please use this IAsyncPlugin interface
+ ///
+ /// Query to search
+ /// Cancel when querying job is obsolete
+ ///
Task> QueryAsync(Query query, CancellationToken token);
+
+ ///
+ /// Initialize plugin asynchrously (will still wait finish to continue)
+ ///
+ ///
+ ///
Task InitAsync(PluginInitContext context);
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs
index 4cc6d8d40..203dc9af7 100644
--- a/Flow.Launcher.Plugin/IPlugin.cs
+++ b/Flow.Launcher.Plugin/IPlugin.cs
@@ -2,10 +2,30 @@
namespace Flow.Launcher.Plugin
{
+ ///
+ /// Synchronous Plugin Model for Flow Launcher
+ ///
+ /// If the Querying or Init method requires high IO transmission
+ /// or performaing CPU intense jobs (performing better with cancellation), please try the IAsyncPlugin interface
+ ///
+ ///
public interface IPlugin
{
+ ///
+ /// Querying when user's search changes
+ ///
+ /// This method will be called within a Task.Run,
+ /// so please avoid synchrously wait for long.
+ ///
+ ///
+ /// Query to search
+ ///
List Query(Query query);
-
+
+ ///
+ /// Initialize plugin
+ ///
+ ///
void Init(PluginInitContext context);
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
index 9c922f667..fc4ac4715 100644
--- a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
+++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
@@ -2,8 +2,19 @@
namespace Flow.Launcher.Plugin
{
+ ///
+ /// This interface is to indicate and allow plugins to asyncronously reload their
+ /// in memory data cache or other mediums when user makes a new change
+ /// that is not immediately captured. For example, for BrowserBookmark and Program
+ /// plugin does not automatically detect when a user added a new bookmark or program,
+ /// so this interface's function is exposed to allow user manually do the reloading after
+ /// those new additions.
+ ///
+ /// The command that allows user to manual reload is exposed via Plugin.Sys, and
+ /// it will call the plugins that have implemented this interface.
+ ///
public interface IAsyncReloadable
{
Task ReloadDataAsync();
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs
index 29b3c15c9..31611519c 100644
--- a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs
+++ b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs
@@ -1,7 +1,7 @@
namespace Flow.Launcher.Plugin
{
///
- /// This interface is to indicate and allow plugins to reload their
+ /// This interface is to indicate and allow plugins to synchronously reload their
/// in memory data cache or other mediums when user makes a new change
/// that is not immediately captured. For example, for BrowserBookmark and Program
/// plugin does not automatically detect when a user added a new bookmark or program,
@@ -10,6 +10,10 @@
///
/// The command that allows user to manual reload is exposed via Plugin.Sys, and
/// it will call the plugins that have implemented this interface.
+ ///
+ ///
+ /// If requiring reloading data asynchronously, please use the IAsyncReloadable interface
+ ///
///
public interface IReloadable
{
diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
index 27cd1a558..37222a1d0 100644
--- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
@@ -146,31 +146,23 @@ namespace Flow.Launcher.Plugin.SharedCommands
/// This checks whether a given string is a directory path or network location string.
/// It does not check if location actually exists.
///
- public static bool IsLocationPathString(string querySearchString)
+ public static bool IsLocationPathString(this string querySearchString)
{
- if (string.IsNullOrEmpty(querySearchString))
+ if (string.IsNullOrEmpty(querySearchString) || querySearchString.Length < 3)
return false;
// // shared folder location, and not \\\location\
- if (querySearchString.Length >= 3
- && querySearchString.StartsWith(@"\\")
- && char.IsLetter(querySearchString[2]))
+ if (querySearchString.StartsWith(@"\\")
+ && querySearchString[2] != '\\')
return true;
// c:\
- if (querySearchString.Length == 3
- && char.IsLetter(querySearchString[0])
+ if (char.IsLetter(querySearchString[0])
&& querySearchString[1] == ':'
&& querySearchString[2] == '\\')
- return true;
-
- // c:\\
- if (querySearchString.Length >= 4
- && char.IsLetter(querySearchString[0])
- && querySearchString[1] == ':'
- && querySearchString[2] == '\\'
- && char.IsLetter(querySearchString[3]))
- return true;
+ {
+ return querySearchString.Length == 3 || querySearchString[3] != '\\';
+ }
return false;
}
diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs
index 468b94457..8925ae708 100644
--- a/Flow.Launcher.Test/FuzzyMatcherTest.cs
+++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs
@@ -40,7 +40,7 @@ namespace Flow.Launcher.Test
Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore))
.Cast()
.ToList()
- .ForEach(x => listToReturn.Add((int)x));
+ .ForEach(x => listToReturn.Add((int) x));
return listToReturn;
}
@@ -92,7 +92,8 @@ namespace Flow.Launcher.Test
[TestCase("cand")]
[TestCase("cpywa")]
[TestCase("ccs")]
- public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm)
+ public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreaterThanPrecisionScoreResults(
+ string searchTerm)
{
var results = new List();
var matcher = new StringMatcher();
@@ -108,9 +109,9 @@ namespace Flow.Launcher.Test
foreach (var precisionScore in GetPrecisionScores())
{
var filteredResult = results.Where(result => result.Score >= precisionScore)
- .Select(result => result)
- .OrderByDescending(x => x.Score)
- .ToList();
+ .Select(result => result)
+ .OrderByDescending(x => x.Score)
+ .ToList();
Debug.WriteLine("");
Debug.WriteLine("###############################################");
@@ -119,6 +120,7 @@ namespace Flow.Launcher.Test
{
Debug.WriteLine("SCORE: " + item.Score.ToString() + ", FoundString: " + item.Title);
}
+
Debug.WriteLine("###############################################");
Debug.WriteLine("");
@@ -128,11 +130,11 @@ namespace Flow.Launcher.Test
[TestCase(Chrome, Chrome, 157)]
[TestCase(Chrome, LastIsChrome, 147)]
- [TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 25)]
+ [TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 90)]
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
- [TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
- [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)]//double spacing intended
+ [TestCase("sql", MicrosoftSqlServerManagementStudio, 90)]
+ [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
string queryString, string compareString, int expectedScore)
{
@@ -141,20 +143,20 @@ namespace Flow.Launcher.Test
var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore;
// Should
- Assert.AreEqual(expectedScore, rawScore,
+ Assert.AreEqual(expectedScore, rawScore,
$"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}");
}
[TestCase("goo", "Google Chrome", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("chr", "Google Chrome", StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("chr", "Chrome", StringMatcher.SearchPrecisionScore.Regular, true)]
- [TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.None, true)]
- [TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Low, true)]
- [TestCase("cand", "Candy Crush Saga from King",StringMatcher.SearchPrecisionScore.Regular, true)]
- [TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)]
+ [TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("cand", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular,
+ false)]
public void WhenGivenDesiredPrecision_ThenShouldReturn_AllResultsGreaterOrEqual(
string queryString,
string compareString,
@@ -170,7 +172,8 @@ namespace Flow.Launcher.Test
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
- Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})");
+ Debug.WriteLine(
+ $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
Debug.WriteLine("###############################################");
Debug.WriteLine("");
@@ -179,13 +182,15 @@ namespace Flow.Launcher.Test
$"Query:{queryString}{Environment.NewLine} " +
$"Compare:{compareString}{Environment.NewLine}" +
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
- $"Precision Score: {(int)expectedPrecisionScore}");
+ $"Precision Score: {(int) expectedPrecisionScore}");
}
[TestCase("exce", "OverLeaf-Latex: An online LaTeX editor", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("term", "Windows Terminal (Preview)", StringMatcher.SearchPrecisionScore.Regular, true)]
- [TestCase("sql s managa", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
- [TestCase("sql' s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
+ [TestCase("sql s managa", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular,
+ false)]
+ [TestCase("sql' s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular,
+ false)]
[TestCase("sql s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
@@ -195,15 +200,21 @@ namespace Flow.Launcher.Test
[TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("mssms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("msms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("chr", "Shutdown", StringMatcher.SearchPrecisionScore.Regular, false)]
- [TestCase("mssms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
- [TestCase("chr", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, false)]
- [TestCase("ch r", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("chr", "Change settings for text-to-speech and for speech recognition (if installed).",
+ StringMatcher.SearchPrecisionScore.Regular, false)]
+ [TestCase("ch r", "Change settings for text-to-speech and for speech recognition (if installed).",
+ StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("a test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("cod", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("code", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("codes", "Visual Studio Codes", StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("vsc", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("vs", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
+ [TestCase("vc", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)]
public void WhenGivenQuery_ShouldReturnResults_ContainingAllQuerySubstrings(
string queryString,
string compareString,
@@ -211,7 +222,7 @@ namespace Flow.Launcher.Test
bool expectedPrecisionResult)
{
// When
- var matcher = new StringMatcher { UserSettingSearchPrecision = expectedPrecisionScore };
+ var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
// Given
var matchResult = matcher.FuzzyMatch(queryString, compareString);
@@ -219,7 +230,8 @@ namespace Flow.Launcher.Test
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
- Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})");
+ Debug.WriteLine(
+ $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
Debug.WriteLine("###############################################");
Debug.WriteLine("");
@@ -228,7 +240,7 @@ namespace Flow.Launcher.Test
$"Query:{queryString}{Environment.NewLine} " +
$"Compare:{compareString}{Environment.NewLine}" +
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
- $"Precision Score: {(int)expectedPrecisionScore}");
+ $"Precision Score: {(int) expectedPrecisionScore}");
}
[TestCase("man", "Task Manager", "eManual")]
@@ -238,7 +250,7 @@ namespace Flow.Launcher.Test
string queryString, string compareString1, string compareString2)
{
// When
- var matcher = new StringMatcher { UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular };
+ var matcher = new StringMatcher {UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular};
// Given
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
@@ -247,8 +259,10 @@ namespace Flow.Launcher.Test
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
- Debug.WriteLine($"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
- Debug.WriteLine($"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
+ Debug.WriteLine(
+ $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
+ Debug.WriteLine(
+ $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
Debug.WriteLine("###############################################");
Debug.WriteLine("");
@@ -256,13 +270,13 @@ namespace Flow.Launcher.Test
Assert.True(compareString1Result.Score > compareString2Result.Score,
$"Query: \"{queryString}\"{Environment.NewLine} " +
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
- $"Should be greater than{ Environment.NewLine}" +
+ $"Should be greater than{Environment.NewLine}" +
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
}
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]
public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore(
- string queryString, string firstName, string firstDescription, string firstExecutableName,
+ string queryString, string firstName, string firstDescription, string firstExecutableName,
string secondName, string secondDescription, string secondExecutableName)
{
// Act
@@ -275,15 +289,36 @@ namespace Flow.Launcher.Test
var secondDescriptionMatch = matcher.FuzzyMatch(queryString, secondDescription).RawScore;
var secondExecutableNameMatch = matcher.FuzzyMatch(queryString, secondExecutableName).RawScore;
- var firstScore = new[] { firstNameMatch, firstDescriptionMatch, firstExecutableNameMatch }.Max();
- var secondScore = new[] { secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch }.Max();
+ var firstScore = new[] {firstNameMatch, firstDescriptionMatch, firstExecutableNameMatch}.Max();
+ var secondScore = new[] {secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch}.Max();
// Assert
Assert.IsTrue(firstScore > secondScore,
$"Query: \"{queryString}\"{Environment.NewLine} " +
$"Name of first: \"{firstName}\", Final Score: {firstScore}{Environment.NewLine}" +
- $"Should be greater than{ Environment.NewLine}" +
+ $"Should be greater than{Environment.NewLine}" +
$"Name of second: \"{secondName}\", Final Score: {secondScore}{Environment.NewLine}");
}
+
+ [TestCase("vsc","Visual Studio Code", 100)]
+ [TestCase("jbr","JetBrain Rider",100)]
+ [TestCase("jr","JetBrain Rider",90)]
+ [TestCase("vs","Visual Studio",100)]
+ [TestCase("vs","Visual Studio Preview",100)]
+ [TestCase("vsp","Visual Studio Preview",100)]
+ [TestCase("vsp","Visual Studio",0)]
+ [TestCase("pc","Postman Canary",100)]
+
+ public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString,
+ int desiredScore)
+ {
+ var matcher = new StringMatcher();
+ var score = matcher.FuzzyMatch(queryString, compareString).Score;
+ Assert.IsTrue(score == desiredScore,
+ $@"Query: ""{queryString}""
+ CompareString: ""{compareString}""
+ Score: {score}
+ Desired Score: {desiredScore}");
+ }
}
}
\ No newline at end of file
diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs
index 09c7d9a30..0710babe3 100644
--- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs
+++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs
@@ -24,7 +24,7 @@ namespace Flow.Launcher.Test.Plugins
return new List();
}
- private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString)
+ private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString, CancellationToken token)
{
return new List
{
@@ -60,8 +60,8 @@ namespace Flow.Launcher.Test.Plugins
$"Actual: {result}{Environment.NewLine}");
}
- [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")]
- [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")]
+ [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
+ [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
{
// Given
@@ -79,7 +79,7 @@ namespace Flow.Launcher.Test.Plugins
[TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
"FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" +
- " AND directory='file:C:\\SomeFolder'")]
+ " AND directory='file:C:\\SomeFolder' ORDER BY System.FileName")]
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
@@ -116,11 +116,8 @@ namespace Flow.Launcher.Test.Plugins
[TestCase("scope='file:'")]
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
{
- // Given
- var queryConstructor = new QueryConstructor(new Settings());
-
//When
- var resultString = queryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch();
+ var resultString = QueryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch;
// Then
Assert.IsTrue(resultString == expectedString,
@@ -130,7 +127,7 @@ namespace Flow.Launcher.Test.Plugins
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
- "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")]
+ "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
@@ -205,7 +202,7 @@ namespace Flow.Launcher.Test.Plugins
}
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
- "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:'")]
+ "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
@@ -243,6 +240,9 @@ namespace Flow.Launcher.Test.Plugins
[TestCase(@"cc:\", false)]
[TestCase(@"\\\SomeNetworkLocation\", false)]
[TestCase("RandomFile", false)]
+ [TestCase(@"c:\>*", true)]
+ [TestCase(@"c:\>", true)]
+ [TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)]
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
{
// When, Given
@@ -295,9 +295,9 @@ namespace Flow.Launcher.Test.Plugins
}
[TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")]
- [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " +
- "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " +
- "scope='file:c:\\SomeFolder'")]
+ [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' "
+ + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND "
+ + "scope='file:c:\\SomeFolder'")]
public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
{
// Given
diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml
index 5f4cdff19..a97f90733 100644
--- a/Flow.Launcher/CustomQueryHotkeySetting.xaml
+++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml
@@ -5,7 +5,7 @@
Icon="Images\app.png"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
- Title="Custom Plugin Hotkey" Height="200" Width="674.766">
+ Title="{DynamicResource customeQueryHotkeyTitle}" Height="200" Width="674.766">
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 1e0e6a7e0..b6bf76b7f 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -17,6 +17,7 @@
Flow Launcher Settings
General
+ Portable Mode
Start Flow Launcher on system startup
Hide Flow Launcher when focus is lost
Do not show new version notifications
@@ -39,12 +40,13 @@
Plugin
Find more plugins
+ Enable
Disable
Action keyword:
Current action keyword:
New action keyword:
- Current Priority:
- New Priority:
+ Current Priority:
+ New Priority:
Plugin Directory
Author
Init time:
@@ -109,7 +111,7 @@
Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number
Please provide an valid integer for Priority!
-
+
Old Action Keyword
New Action Keyword
@@ -122,6 +124,7 @@
Use * if you don't want to specify an action keyword
+ Custom Plugin Hotkey
Preview
Hotkey is unavailable, please select a new hotkey
Invalid plugin hotkey
@@ -146,11 +149,23 @@
Failed to send report
Flow Launcher got an error
+
+ Please wait...
+
+ Checking for new update
+ You already have the latest Flow Launcher version
+ Update found
+ Updating...
+ Flow Launcher was not able to move your user profile data to the new update version.
+ Please manually move your profile data folder from {0} to {1}
+ New Update
New Flow Launcher release {0} is now available
An error occurred while trying to install software updates
Update
Cancel
+ Update Failed
+ Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.
This upgrade will restart Flow Launcher
Following files will be updated
Update files
diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml
index a2cfe569d..4cc0b4428 100644
--- a/Flow.Launcher/MainWindow.xaml
+++ b/Flow.Launcher/MainWindow.xaml
@@ -97,7 +97,7 @@
Background="Transparent"/>
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 3812b4e1f..04a1063f8 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -26,6 +26,7 @@ namespace Flow.Launcher
#region Private Fields
private readonly Storyboard _progressBarStoryboard = new Storyboard();
+ private bool isProgressBarStoryboardPaused;
private Settings _settings;
private NotifyIcon _notifyIcon;
private MainViewModel _viewModel;
@@ -52,7 +53,7 @@ namespace Flow.Launcher
private void OnInitialized(object sender, EventArgs e)
{
-
+
}
private void OnLoaded(object sender, RoutedEventArgs _)
@@ -73,7 +74,7 @@ namespace Flow.Launcher
{
if (e.PropertyName == nameof(MainViewModel.MainWindowVisibility))
{
- if (Visibility == Visibility.Visible)
+ if (_viewModel.MainWindowVisibility == Visibility.Visible)
{
Activate();
QueryTextBox.Focus();
@@ -84,7 +85,34 @@ namespace Flow.Launcher
QueryTextBox.SelectAll();
_viewModel.LastQuerySelected = true;
}
+
+ if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused)
+ {
+ _progressBarStoryboard.Resume();
+ isProgressBarStoryboardPaused = false;
+ }
}
+ else if (!isProgressBarStoryboardPaused)
+ {
+ _progressBarStoryboard.Pause();
+ isProgressBarStoryboardPaused = true;
+ }
+ }
+ else if (e.PropertyName == nameof(MainViewModel.ProgressBarVisibility))
+ {
+ Dispatcher.Invoke(() =>
+ {
+ if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused)
+ {
+ _progressBarStoryboard.Pause();
+ isProgressBarStoryboardPaused = true;
+ }
+ else if (_viewModel.MainWindowVisibility == Visibility.Visible && isProgressBarStoryboardPaused)
+ {
+ _progressBarStoryboard.Resume();
+ isProgressBarStoryboardPaused = false;
+ }
+ }, System.Windows.Threading.DispatcherPriority.Render);
}
};
_settings.PropertyChanged += (o, e) =>
@@ -170,6 +198,7 @@ namespace Flow.Launcher
_progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever;
ProgressBar.BeginStoryboard(_progressBarStoryboard);
_viewModel.ProgressBarVisibility = Visibility.Hidden;
+ isProgressBarStoryboardPaused = true;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml
index 21d7e88f7..4c7eac114 100644
--- a/Flow.Launcher/SettingWindow.xaml
+++ b/Flow.Launcher/SettingWindow.xaml
@@ -37,7 +37,7 @@
-
+
@@ -166,7 +166,7 @@
-
diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs
index 09e69f6d8..c92ef4956 100644
--- a/Flow.Launcher/Storage/TopMostRecord.cs
+++ b/Flow.Launcher/Storage/TopMostRecord.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
+using System.Text.Json.Serialization;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Storage
@@ -8,20 +9,24 @@ namespace Flow.Launcher.Storage
// todo this class is not thread safe.... but used from multiple threads.
public class TopMostRecord
{
- private Dictionary records = new Dictionary();
+ ///
+ /// You should not directly access this field
+ ///
+ /// It is public due to System.Text.Json limitation in version 3.1
+ ///
+ ///
+ /// TODO: Set it to private
+ public Dictionary records { get; set; } = new Dictionary();
internal bool IsTopMost(Result result)
{
- if (records.Count == 0)
+ if (records.Count == 0 || !records.ContainsKey(result.OriginQuery.RawQuery))
{
return false;
}
- // since this dictionary should be very small (or empty) going over it should be pretty fast.
- return records.Any(o => o.Value.Title == result.Title
- && o.Value.SubTitle == result.SubTitle
- && o.Value.PluginID == result.PluginID
- && o.Key == result.OriginQuery.RawQuery);
+ // since this dictionary should be very small (or empty) going over it should be pretty fast.
+ return records[result.OriginQuery.RawQuery].Equals(result);
}
internal void Remove(Result result)
@@ -53,5 +58,12 @@ namespace Flow.Launcher.Storage
public string Title { get; set; }
public string SubTitle { get; set; }
public string PluginID { get; set; }
+
+ public bool Equals(Result r)
+ {
+ return Title == r.Title
+ && SubTitle == r.SubTitle
+ && PluginID == r.PluginID;
+ }
}
}
diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs
index c7ffe1a1d..bc7a2da73 100644
--- a/Flow.Launcher/Storage/UserSelectedRecord.cs
+++ b/Flow.Launcher/Storage/UserSelectedRecord.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin;
@@ -6,14 +7,27 @@ namespace Flow.Launcher.Storage
{
public class UserSelectedRecord
{
- private Dictionary records = new Dictionary();
+ ///
+ /// You should not directly access this field
+ ///
+ /// It is public due to System.Text.Json limitation in version 3.1
+ ///
+ ///
+ /// TODO: Set it to private
+ [JsonPropertyName("records")]
+ public Dictionary records { get; set; }
+
+ public UserSelectedRecord()
+ {
+ records = new Dictionary();
+ }
public void Add(Result result)
{
var key = result.ToString();
- if (records.TryGetValue(key, out int value))
+ if (records.ContainsKey(key))
{
- records[key] = value + 1;
+ records[key]++;
}
else
{
diff --git a/Flow.Launcher/Themes/BlurBlack Darker.xaml b/Flow.Launcher/Themes/BlurBlack Darker.xaml
new file mode 100644
index 000000000..5c615d500
--- /dev/null
+++ b/Flow.Launcher/Themes/BlurBlack Darker.xaml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #356ef3
+
+
+
+
+
+
diff --git a/Flow.Launcher/Themes/BlurWhite.xaml b/Flow.Launcher/Themes/BlurWhite.xaml
index 1c1f2f9ec..6a130bb39 100644
--- a/Flow.Launcher/Themes/BlurWhite.xaml
+++ b/Flow.Launcher/Themes/BlurWhite.xaml
@@ -17,7 +17,7 @@
@@ -26,7 +26,7 @@
-
+
diff --git a/Flow.Launcher/Themes/Gray.xaml b/Flow.Launcher/Themes/Gray.xaml
index 16a1db274..1fbaa959a 100644
--- a/Flow.Launcher/Themes/Gray.xaml
+++ b/Flow.Launcher/Themes/Gray.xaml
@@ -4,21 +4,19 @@
@@ -31,15 +29,15 @@
- #00AAF6
+ #787878
@@ -38,7 +38,7 @@
- #3875D7
+ #909090
+
+
+
+
+
+
+
+
+
+
+
+ #5e81ac
+
+
+
+
diff --git a/Flow.Launcher/Themes/Nord.xaml b/Flow.Launcher/Themes/Nord.xaml
new file mode 100644
index 000000000..2253b3410
--- /dev/null
+++ b/Flow.Launcher/Themes/Nord.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #5e81ac
+
+
+
+
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index ff09f364d..efa707850 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -20,9 +18,8 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Storage;
-using System.Windows.Media;
-using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
+using System.Threading.Tasks.Dataflow;
namespace Flow.Launcher.ViewModel
{
@@ -96,11 +93,18 @@ namespace Flow.Launcher.ViewModel
async Task updateAction()
{
+ var queue = new Dictionary();
while (await _resultsUpdateQueue.OutputAvailableAsync())
{
+ queue.Clear();
await Task.Delay(20);
- _resultsUpdateQueue.TryReceiveAll(out var queue);
- UpdateResultView(queue.Where(r => !r.Token.IsCancellationRequested));
+ while (_resultsUpdateQueue.TryReceive(out var item))
+ {
+ if (!item.Token.IsCancellationRequested)
+ queue[item.ID] = item;
+ }
+
+ UpdateResultView(queue.Values);
}
}
@@ -236,7 +240,7 @@ namespace Flow.Launcher.ViewModel
public string QueryText
{
- get { return _queryText; }
+ get => _queryText;
set
{
_queryText = value;
@@ -351,9 +355,20 @@ namespace Flow.Launcher.ViewModel
{
var filtered = results.Where
(
- r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet()
- || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
- ).ToList();
+ r =>
+ {
+ var match = StringMatcher.FuzzySearch(query, r.Title);
+ if (!match.IsSearchPrecisionScoreMet())
+ {
+ match = StringMatcher.FuzzySearch(query, r.SubTitle);
+ }
+
+ if (!match.IsSearchPrecisionScoreMet()) return false;
+
+ r.Score = match.Score;
+ return true;
+
+ }).ToList();
ContextMenu.AddResults(filtered, id);
}
else
@@ -407,105 +422,111 @@ namespace Flow.Launcher.ViewModel
private void QueryResults()
{
- if (!string.IsNullOrEmpty(QueryText))
+ _updateSource?.Cancel();
+
+ if (string.IsNullOrWhiteSpace(QueryText))
{
- _updateSource?.Cancel();
- var currentUpdateSource = new CancellationTokenSource();
- _updateSource = currentUpdateSource;
- var currentCancellationToken = _updateSource.Token;
- _updateToken = currentCancellationToken;
-
- ProgressBarVisibility = Visibility.Hidden;
- _isQueryRunning = true;
- var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins);
- if (query != null)
- {
- // handle the exclusiveness of plugin using action keyword
- RemoveOldQueryResults(query);
-
- _lastQuery = query;
-
- var plugins = PluginManager.ValidPluginsForQuery(query);
- Task.Run(async () =>
- {
- if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
- {
- // Wait 45 millisecond for query change in global query
- // if query changes, return so that it won't be calculated
- await Task.Delay(45, currentCancellationToken);
- if (currentCancellationToken.IsCancellationRequested)
- return;
- }
-
- _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
- {
- // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
- if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
- {
- ProgressBarVisibility = Visibility.Visible;
- }
- }, currentCancellationToken);
-
- // so looping will stop once it was cancelled
-
- Task[] tasks = new Task[plugins.Count];
-
- try
- {
- for (var i = 0; i < plugins.Count; i++)
- {
- if (!plugins[i].Metadata.Disabled)
- tasks[i] = QueryTask(plugins[i], query, currentCancellationToken);
- else tasks[i] = Task.CompletedTask; // Avoid Null
- }
-
- // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
- await Task.WhenAll(tasks);
- }
- catch (OperationCanceledException)
- {
- // nothing to do here
- }
-
- if (currentCancellationToken.IsCancellationRequested)
- return;
-
- // this should happen once after all queries are done so progress bar should continue
- // until the end of all querying
- _isQueryRunning = false;
- if (!currentCancellationToken.IsCancellationRequested)
- {
- // update to hidden if this is still the current query
- ProgressBarVisibility = Visibility.Hidden;
- }
-
- // Local Function
- async Task QueryTask(PluginPair plugin, Query query, CancellationToken token)
- {
- // Since it is wrapped within a Task.Run, the synchronous context is null
- // Task.Yield will force it to run in ThreadPool
- await Task.Yield();
-
- var results = await PluginManager.QueryForPlugin(plugin, query, token);
- if (!currentCancellationToken.IsCancellationRequested)
- _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query, token));
- }
- }, currentCancellationToken).ContinueWith(
- t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception),
- TaskContinuationOptions.OnlyOnFaulted);
- }
- }
- else
- {
- _updateSource?.Cancel();
Results.Clear();
Results.Visbility = Visibility.Collapsed;
+ return;
}
+
+ _updateSource?.Dispose();
+
+ var currentUpdateSource = new CancellationTokenSource();
+ _updateSource = currentUpdateSource;
+ var currentCancellationToken = _updateSource.Token;
+ _updateToken = currentCancellationToken;
+
+ ProgressBarVisibility = Visibility.Hidden;
+ _isQueryRunning = true;
+
+ var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins);
+
+ // handle the exclusiveness of plugin using action keyword
+ RemoveOldQueryResults(query);
+
+ _lastQuery = query;
+
+ var plugins = PluginManager.ValidPluginsForQuery(query);
+
+ Task.Run(async () =>
+ {
+ if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
+ {
+ // Wait 45 millisecond for query change in global query
+ // if query changes, return so that it won't be calculated
+ await Task.Delay(45, currentCancellationToken);
+ if (currentCancellationToken.IsCancellationRequested)
+ return;
+ }
+
+ _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
+ {
+ // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
+ if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
+ {
+ ProgressBarVisibility = Visibility.Visible;
+ }
+ }, currentCancellationToken);
+
+ Task[] tasks = new Task[plugins.Count];
+ try
+ {
+ for (var i = 0; i < plugins.Count; i++)
+ {
+ if (!plugins[i].Metadata.Disabled)
+ {
+ tasks[i] = QueryTask(plugins[i]);
+ }
+ else
+ {
+ tasks[i] = Task.CompletedTask; // Avoid Null
+ }
+ }
+
+ // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
+ await Task.WhenAll(tasks);
+ }
+ catch (OperationCanceledException)
+ {
+ // nothing to do here
+ }
+
+ if (currentCancellationToken.IsCancellationRequested)
+ return;
+
+ // this should happen once after all queries are done so progress bar should continue
+ // until the end of all querying
+ _isQueryRunning = false;
+ if (!currentCancellationToken.IsCancellationRequested)
+ {
+ // update to hidden if this is still the current query
+ ProgressBarVisibility = Visibility.Hidden;
+ }
+
+ // Local function
+ async Task QueryTask(PluginPair plugin)
+ {
+ // Since it is wrapped within a Task.Run, the synchronous context is null
+ // Task.Yield will force it to run in ThreadPool
+ await Task.Yield();
+
+ var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken);
+ if (!currentCancellationToken.IsCancellationRequested)
+ _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query,
+ currentCancellationToken));
+ }
+ }, currentCancellationToken)
+ .ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception),
+ TaskContinuationOptions.OnlyOnFaulted);
}
+
private void RemoveOldQueryResults(Query query)
{
string lastKeyword = _lastQuery.ActionKeyword;
+
string keyword = query.ActionKeyword;
if (string.IsNullOrEmpty(lastKeyword))
{
@@ -600,7 +621,6 @@ namespace Flow.Launcher.ViewModel
return selected;
}
-
private bool HistorySelected()
{
var selected = SelectedResults == History;
@@ -753,45 +773,25 @@ namespace Flow.Launcher.ViewModel
#endif
- foreach (var result in resultsForUpdates.SelectMany(u => u.Results))
+ foreach (var metaResults in resultsForUpdates)
{
- if (_topMostRecord.IsTopMost(result))
+ foreach (var result in metaResults.Results)
{
- result.Score = int.MaxValue;
- }
- else
- {
- result.Score += _userSelectedRecord.GetSelectedCount(result) * 5;
+ if (_topMostRecord.IsTopMost(result))
+ {
+ result.Score = int.MaxValue;
+ }
+ else
+ {
+ var priorityScore = metaResults.Metadata.Priority * 150;
+ result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore;
+ }
}
}
Results.AddResults(resultsForUpdates, token);
}
- /// U
- /// To avoid deadlock, this method should not called from main thread
- ///
- public void UpdateResultView(List list, PluginMetadata metadata, Query originQuery)
- {
- foreach (var result in list)
- {
- if (_topMostRecord.IsTopMost(result))
- {
- result.Score = int.MaxValue;
- }
- else
- {
- var priorityScore = metadata.Priority * 150;
- result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore;
- }
- }
-
- if (originQuery.RawQuery == _lastQuery.RawQuery)
- {
- Results.AddResults(list, metadata.ID);
- }
- }
-
#endregion
}
}
\ No newline at end of file
diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs
index fa0c51a65..c91bbb107 100644
--- a/Flow.Launcher/ViewModel/ResultViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultViewModel.cs
@@ -1,14 +1,11 @@
using System;
-using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
-using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
-using Microsoft.FSharp.Core;
namespace Flow.Launcher.ViewModel
{
@@ -128,7 +125,6 @@ namespace Flow.Launcher.ViewModel
}
}
-
public override int GetHashCode()
{
return Result.GetHashCode();
diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs
index 2257d35b0..be48f53c1 100644
--- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs
+++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs
@@ -23,7 +23,6 @@ namespace Flow.Launcher.ViewModel
Token = token;
}
-
public ResultsForUpdate(List results, PluginMetadata metadata, Query query, CancellationToken token)
{
Results = results;
diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs
index fc77327cc..feab3a751 100644
--- a/Flow.Launcher/ViewModel/ResultsViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs
@@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Configuration;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -11,7 +9,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
-using System.Windows.Forms;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@@ -137,46 +134,14 @@ namespace Flow.Launcher.ViewModel
Results.Update(Results.Where(r => r.Result.PluginID != metadata.ID).ToList());
}
-
///
/// To avoid deadlock, this method should not called from main thread
///
public void AddResults(List newRawResults, string resultId)
{
+ var newResults = NewResults(newRawResults, resultId);
- lock (_collectionLock)
- {
- var newResults = NewResults(newRawResults, resultId);
-
- // https://social.msdn.microsoft.com/Forums/vstudio/en-US/5ff71969-f183-4744-909d-50f7cd414954/binding-a-tabcontrols-selectedindex-not-working?forum=wpf
- // fix selected index flow
- var updateTask = Task.Run(() =>
- {
- // update UI in one run, so it can avoid UI flickering
-
- Results.Update(newResults);
- if (Results.Any())
- SelectedItem = Results[0];
- });
- if (!updateTask.Wait(300))
- {
- updateTask.Dispose();
- throw new TimeoutException("Update result use too much time.");
- }
-
- }
-
- if (Visbility != Visibility.Visible && Results.Count > 0)
- {
- Margin = new Thickness { Top = 8 };
- SelectedIndex = 0;
- Visbility = Visibility.Visible;
- }
- else
- {
- Margin = new Thickness { Top = 0 };
- Visbility = Visibility.Collapsed;
- }
+ UpdateResults(newResults);
}
///
/// To avoid deadlock, this method should not called from main thread
@@ -184,17 +149,21 @@ namespace Flow.Launcher.ViewModel
public void AddResults(IEnumerable resultsForUpdates, CancellationToken token)
{
var newResults = NewResults(resultsForUpdates);
+
if (token.IsCancellationRequested)
return;
+
+ UpdateResults(newResults, token);
+ }
+
+ private void UpdateResults(List newResults, CancellationToken token = default)
+ {
lock (_collectionLock)
{
// update UI in one run, so it can avoid UI flickering
-
Results.Update(newResults, token);
if (Results.Any())
SelectedItem = Results[0];
-
-
}
switch (Visbility)
@@ -209,10 +178,8 @@ namespace Flow.Launcher.ViewModel
Visbility = Visibility.Collapsed;
break;
}
-
}
-
private List NewResults(List newRawResults, string resultId)
{
if (newRawResults.Count == 0)
@@ -220,12 +187,10 @@ namespace Flow.Launcher.ViewModel
var results = Results as IEnumerable;
- var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)).ToList();
-
-
+ var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings));
return results.Where(r => r.Result.PluginID != resultId)
- .Concat(results.Intersect(newResults).Union(newResults))
+ .Concat(newResults)
.OrderByDescending(r => r.Result.Score)
.ToList();
}
@@ -238,14 +203,12 @@ namespace Flow.Launcher.ViewModel
var results = Results as IEnumerable;
return results.Where(r => r != null && !resultsForUpdates.Any(u => u.Metadata.ID == r.Result.PluginID))
- .Concat(
- resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
+ .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
.OrderByDescending(rv => rv.Result.Score)
.ToList();
}
#endregion
-
#region FormattedText Dependency Property
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
@@ -277,55 +240,52 @@ namespace Flow.Launcher.ViewModel
}
#endregion
- public class ResultCollection : ObservableCollection
+ public class ResultCollection : List, INotifyCollectionChanged
{
-
private long editTime = 0;
- private bool _suppressNotifying = false;
-
private CancellationToken _token;
- protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+
+ protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
- if (!_suppressNotifying)
- {
- base.OnCollectionChanged(e);
- }
+ CollectionChanged?.Invoke(this, e);
}
- public void BulkAddRange(IEnumerable resultViews)
+ public void BulkAddAll(List resultViews)
{
- // suppress notifying before adding all element
- _suppressNotifying = true;
- foreach (var item in resultViews)
- {
- Add(item);
- }
- _suppressNotifying = false;
- // manually update event
- // wpf use directx / double buffered already, so just reset all won't cause ui flickering
+ AddRange(resultViews);
+
+ // can return because the list will be cleared next time updated, which include a reset event
if (_token.IsCancellationRequested)
return;
+
+ // manually update event
+ // wpf use directx / double buffered already, so just reset all won't cause ui flickering
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
- public void AddRange(IEnumerable Items)
+ private void AddAll(List Items)
{
- foreach (var item in Items)
+ for (int i = 0; i < Items.Count; i++)
{
+ var item = Items[i];
if (_token.IsCancellationRequested)
return;
Add(item);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i));
}
}
- public void RemoveAll()
+ public void RemoveAll(int Capacity = 512)
{
- ClearItems();
+ Clear();
+ if (this.Capacity > 8000 && Capacity < this.Capacity)
+ this.Capacity = Capacity;
+
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
-
-
-
///
/// Update the results collection with new results, try to keep identical results
///
@@ -336,17 +296,21 @@ namespace Flow.Launcher.ViewModel
if (Count == 0 && newItems.Count == 0 || _token.IsCancellationRequested)
return;
- if (editTime < 5 || newItems.Count < 30)
+ if (editTime < 10 || newItems.Count < 30)
{
- if (Count != 0) ClearItems();
- AddRange(newItems);
+ if (Count != 0) RemoveAll(newItems.Count);
+ AddAll(newItems);
editTime++;
return;
}
else
{
Clear();
- BulkAddRange(newItems);
+ BulkAddAll(newItems);
+ if (Capacity > 8000 && newItems.Count < 3000)
+ {
+ Capacity = newItems.Count;
+ }
editTime++;
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
index 3253b7a7b..5124f6fb3 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
@@ -4,7 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Windows;
+using System.Threading;
namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
@@ -17,14 +17,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
resultManager = new ResultManager(context);
}
- internal List TopLevelDirectorySearch(Query query, string search)
+ internal List TopLevelDirectorySearch(Query query, string search, CancellationToken token)
{
var criteria = ConstructSearchCriteria(search);
- if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator))
- return DirectorySearch(SearchOption.AllDirectories, query, search, criteria);
+ if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) >
+ search.LastIndexOf(Constants.DirectorySeperator))
+ return DirectorySearch(new EnumerationOptions
+ {
+ RecurseSubdirectories = true
+ }, query, search, criteria, token);
- return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria);
+ return DirectorySearch(new EnumerationOptions(), query, search, criteria, token); // null will be passed as default
}
public string ConstructSearchCriteria(string search)
@@ -46,7 +50,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
return incompleteName;
}
- private List DirectorySearch(SearchOption searchOption, Query query, string search, string searchCriteria)
+ private List DirectorySearch(EnumerationOptions enumerationOption, Query query, string search,
+ string searchCriteria, CancellationToken token)
{
var results = new List();
@@ -59,38 +64,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
var directoryInfo = new System.IO.DirectoryInfo(path);
- foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, searchOption))
+ foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption))
{
- if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
-
if (fileSystemInfo is System.IO.DirectoryInfo)
{
- folderList.Add(resultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query, true, false));
+ folderList.Add(resultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName,
+ fileSystemInfo.FullName, query, true, false));
}
else
{
fileList.Add(resultManager.CreateFileResult(fileSystemInfo.FullName, query, true, false));
}
+
+ token.ThrowIfCancellationRequested();
}
}
catch (Exception e)
{
- if (e is UnauthorizedAccessException || e is ArgumentException)
- {
- results.Add(new Result { Title = e.Message, Score = 501 });
+ if (!(e is ArgumentException))
+ throw e;
+
+ results.Add(new Result {Title = e.Message, Score = 501});
- return results;
- }
+ return results;
#if DEBUG // Please investigate and handle error from DirectoryInfo search
- throw e;
#else
Log.Exception($"|Flow.Launcher.Plugin.Explorer.DirectoryInfoSearch|Error from performing DirectoryInfoSearch", e);
-#endif
+#endif
}
- // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
+ // Initial 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();
}
}
-}
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs
index 8bd19956e..6f0020ac9 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs
@@ -6,25 +6,31 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks
{
public class QuickFolderAccess
{
- internal List FolderListMatched(Query query, List folderLinks, PluginInitContext context)
+ private readonly ResultManager resultManager;
+
+ public QuickFolderAccess(PluginInitContext context)
+ {
+ resultManager = new ResultManager(context);
+ }
+
+ internal List FolderListMatched(Query query, List folderLinks)
{
if (string.IsNullOrEmpty(query.Search))
return new List();
string search = query.Search.ToLower();
-
- var queriedFolderLinks = folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
+
+ var queriedFolderLinks =
+ folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
return queriedFolderLinks.Select(item =>
- new ResultManager(context)
- .CreateFolderResult(item.Nickname, item.Path, item.Path, query))
- .ToList();
+ resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query))
+ .ToList();
}
- internal List FolderListAll(Query query, List folderLinks, PluginInitContext context)
+ internal List FolderListAll(Query query, List folderLinks)
=> folderLinks
- .Select(item =>
- new ResultManager(context).CreateFolderResult(item.Nickname, item.Path, item.Path, query))
+ .Select(item => resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query))
.ToList();
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
index 6b3a96912..14aefeb19 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
@@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
private readonly IndexSearch indexSearch;
- private readonly QuickFolderAccess quickFolderAccess = new QuickFolderAccess();
+ private readonly QuickFolderAccess quickFolderAccess;
private readonly ResultManager resultManager;
@@ -28,6 +28,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
indexSearch = new IndexSearch(context);
resultManager = new ResultManager(context);
this.settings = settings;
+ quickFolderAccess = new QuickFolderAccess(context);
}
internal async Task> SearchAsync(Query query, CancellationToken token)
@@ -40,12 +41,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return await WindowsIndexFileContentSearchAsync(query, querySearch, token).ConfigureAwait(false);
// This allows the user to type the assigned action keyword and only see the list of quick folder links
- if (settings.QuickFolderAccessLinks.Count > 0
- && query.ActionKeyword == settings.SearchActionKeyword
- && string.IsNullOrEmpty(query.Search))
- return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context);
+ if (string.IsNullOrEmpty(query.Search))
+ return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks);
- var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context);
+ var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks);
if (quickFolderLinks.Count > 0)
results.AddRange(quickFolderLinks);
@@ -58,7 +57,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
// Query is a location path with a full environment variable, eg. %appdata%\somefolder\
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
- if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath)
+ if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath)
{
results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false));
@@ -70,6 +69,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
if (isEnvironmentVariablePath)
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
+ // Check that actual location exists, otherwise directory search will throw directory not found exception
if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath)))
return results;
@@ -77,15 +77,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search
results.Add(resultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch));
- if (token.IsCancellationRequested)
- return null;
+ token.ThrowIfCancellationRequested();
- results.AddRange(await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
- DirectoryInfoClassSearch,
- useIndexSearch,
- query,
- locationPath,
- token).ConfigureAwait(false));
+ var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
+ DirectoryInfoClassSearch,
+ useIndexSearch,
+ query,
+ locationPath,
+ token).ConfigureAwait(false);
+
+ token.ThrowIfCancellationRequested();
+
+ results.AddRange(directoryResult);
return results;
}
@@ -109,23 +112,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return actionKeyword == settings.FileContentSearchActionKeyword;
}
- private List DirectoryInfoClassSearch(Query query, string querySearch)
+ private List DirectoryInfoClassSearch(Query query, string querySearch, CancellationToken token)
{
var directoryInfoSearch = new DirectoryInfoSearch(context);
- return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch);
+ return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch, token);
}
public async Task> TopLevelDirectorySearchBehaviourAsync(
Func>> windowsIndexSearch,
- Func> directoryInfoClassSearch,
+ Func> directoryInfoClassSearch,
bool useIndexSearch,
Query query,
string querySearchString,
CancellationToken token)
{
if (!useIndexSearch)
- return directoryInfoClassSearch(query, querySearchString);
+ return directoryInfoClassSearch(query, querySearchString, token);
return await windowsIndexSearch(query, querySearchString, token);
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs
index 5b1d47ef8..8480e6328 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs
@@ -1,4 +1,4 @@
-using Flow.Launcher.Infrastructure.Logger;
+using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Search.Interop;
using System;
using System.Collections.Generic;
@@ -15,7 +15,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
private readonly ResultManager resultManager;
// Reserved keywords in oleDB
- private readonly string reservedStringPattern = @"^[\/\\\$\%_]+$";
+ private readonly string reservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_]+$";
internal IndexSearch(PluginInitContext context)
{
@@ -24,9 +24,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
internal async Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
{
- var folderResults = new List();
- var fileResults = new List();
var results = new List();
+ var fileResults = new List();
try
{
@@ -55,7 +54,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
if (dataReaderResults.GetString(2) == "Directory")
{
- folderResults.Add(resultManager.CreateFolderResult(
+ results.Add(resultManager.CreateFolderResult(
dataReaderResults.GetString(0),
path,
path,
@@ -83,8 +82,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
LogException("General error from performing index search", e);
}
+ results.AddRange(fileResults);
+
// Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
- return results.Concat(folderResults.OrderBy(x => x.Title)).Concat(fileResults.OrderBy(x => x.Title)).ToList(); ;
+ return results;
}
internal async Task> WindowsIndexSearchAsync(string searchString, string connectionString,
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
index 5718fdb0a..20e85bbb5 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs
@@ -42,7 +42,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
// Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer
var queryHelper = catalogManager.GetQueryHelper();
-
+
return queryHelper;
}
@@ -81,11 +81,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var previousLevelDirectory = path.Substring(0, indexOfSeparator);
if (string.IsNullOrEmpty(itemName))
- return searchDepth + $"{previousLevelDirectory}'";
+ return $"{searchDepth}{previousLevelDirectory}'";
- return $"(System.FileName LIKE '{itemName}%' " +
- $"OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND " +
- searchDepth + $"{previousLevelDirectory}'";
+ return $"(System.FileName LIKE '{itemName}%' OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND {searchDepth}{previousLevelDirectory}'";
}
///
@@ -96,9 +94,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE ";
if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator))
- return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path);
+ return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path) + QueryOrderByFileNameRestriction;
- return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path);
+ return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path) + QueryOrderByFileNameRestriction;
}
///
@@ -107,16 +105,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
public string QueryForAllFilesAndFolders(string userSearchString)
{
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
- return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch();
+ return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch
+ + QueryOrderByFileNameRestriction;
}
///
/// Set the required WHERE clause restriction to search for all files and folders.
///
- public string QueryWhereRestrictionsForAllFilesAndFoldersSearch()
- {
- return $"scope='file:'";
- }
+ public const string QueryWhereRestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
+
+ public const string QueryOrderByFileNameRestriction = " ORDER BY System.FileName";
+
///
/// Search will be performed on all indexed file contents for the specified search keywords.
@@ -125,7 +124,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE ";
- return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch();
+ return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch
+ + QueryOrderByFileNameRestriction;
}
///
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
index e62ea93fc..e62637896 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
@@ -1,7 +1,6 @@
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
using System.Collections.Generic;
-using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin.Explorer
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
index aa44c4413..1e92d2254 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
@@ -7,7 +7,7 @@
"Name": "Explorer",
"Description": "Search and manage files and folders. Explorer utilises Windows Index Search",
"Author": "Jeremy Wu",
- "Version": "1.2.6",
+ "Version": "1.5.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
index 40579e6e5..66bfd2ab5 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
@@ -30,15 +30,29 @@ namespace Flow.Launcher.Plugin.PluginsManager
return new PluginsManagerSettings(viewModel);
}
- public async Task InitAsync(PluginInitContext context)
+ public Task InitAsync(PluginInitContext context)
{
Context = context;
viewModel = new SettingsViewModel(context);
Settings = viewModel.Settings;
contextMenu = new ContextMenu(Context);
pluginManager = new PluginsManager(Context, Settings);
- await pluginManager.UpdateManifest();
- lastUpdateTime = DateTime.Now;
+ var updateManifestTask = pluginManager.UpdateManifest();
+ _ = updateManifestTask.ContinueWith(t =>
+ {
+ if (t.IsCompletedSuccessfully)
+ {
+ lastUpdateTime = DateTime.Now;
+ }
+ else
+ {
+ context.API.ShowMsg("Plugin Manifest Download Fail.",
+ "Please check if you can connect to github.com. " +
+ "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false);
+ }
+ });
+
+ return Task.CompletedTask;
}
public List LoadContextMenus(Result selectedResult)
@@ -61,7 +75,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
return search switch
{
- var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s),
+ var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s, token),
var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s),
var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s),
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>
@@ -93,4 +107,4 @@ namespace Flow.Launcher.Plugin.PluginsManager
lastUpdateTime = DateTime.Now;
}
}
-}
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
index 68df5bc1f..0f5e6d9e8 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -36,7 +37,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
}
}
- private readonly string icoPath = "Images\\pluginsmanager.png";
+ internal readonly string icoPath = "Images\\pluginsmanager.png";
internal PluginsManager(PluginInitContext context, Settings settings)
{
@@ -64,27 +65,27 @@ namespace Flow.Launcher.Plugin.PluginsManager
return false;
}
},
- new Result()
+ new Result()
+ {
+ Title = Settings.HotkeyUninstall,
+ IcoPath = icoPath,
+ Action = _ =>
{
- Title = Settings.HotkeyUninstall,
- IcoPath = icoPath,
- Action = _ =>
- {
- Context.API.ChangeQuery("pm uninstall ");
- return false;
- }
- },
- new Result()
- {
- Title = Settings.HotkeyUpdate,
- IcoPath = icoPath,
- Action = _ =>
- {
- Context.API.ChangeQuery("pm update ");
- return false;
- }
+ Context.API.ChangeQuery("pm uninstall ");
+ return false;
}
- };
+ },
+ new Result()
+ {
+ Title = Settings.HotkeyUpdate,
+ IcoPath = icoPath,
+ Action = _ =>
+ {
+ Context.API.ChangeQuery("pm update ");
+ return false;
+ }
+ }
+ };
}
internal async Task InstallOrUpdate(UserPlugin plugin)
@@ -137,7 +138,8 @@ namespace Flow.Launcher.Plugin.PluginsManager
catch (Exception e)
{
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
- string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), plugin.Name));
+ string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
+ plugin.Name));
Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "InstallOrUpdate");
@@ -164,7 +166,8 @@ namespace Flow.Launcher.Plugin.PluginsManager
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
- where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version
+ where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) <
+ 0 // if current version precedes manifest version
select
new
{
@@ -214,22 +217,29 @@ namespace Flow.Launcher.Plugin.PluginsManager
Task.Run(async delegate
{
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
- Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
+ Context.API.ShowMsg(
+ Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
+ Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
- await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false);
+ await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
+ .ConfigureAwait(false);
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
- Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
+ Context.API.ShowMsg(
+ Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
+ Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
Install(x.PluginNewUserPlugin, downloadToFilePath);
Context.API.RestartApp();
}).ContinueWith(t =>
{
- Log.Exception("PluginsManager", $"Update failed for {x.Name}", t.Exception.InnerException, "RequestUpdate");
- Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
- string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), x.Name));
+ Log.Exception("PluginsManager", $"Update failed for {x.Name}",
+ t.Exception.InnerException, "RequestUpdate");
+ Context.API.ShowMsg(
+ Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
+ string.Format(
+ Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
+ x.Name));
}, TaskContinuationOptions.OnlyOnFaulted);
return true;
@@ -264,8 +274,21 @@ namespace Flow.Launcher.Plugin.PluginsManager
.ToList();
}
- internal List RequestInstallOrUpdate(string searchName)
+ private Task _downloadManifestTask = Task.CompletedTask;
+
+ internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token)
{
+ if (!pluginsManifest.UserPlugins.Any() &&
+ _downloadManifestTask.Status != TaskStatus.Running)
+ {
+ _downloadManifestTask = pluginsManifest.DownloadManifest();
+ }
+
+ await _downloadManifestTask;
+
+ if (token.IsCancellationRequested)
+ return null;
+
var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim();
var results =
@@ -406,4 +429,4 @@ namespace Flow.Launcher.Plugin.PluginsManager
return new List();
}
}
-}
+}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
index ef2c1255a..75d6038d4 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
@@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
- "Version": "1.4.1",
+ "Version": "1.6.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
index 954c238a9..22f4aea59 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
@@ -41,6 +41,9 @@ namespace Flow.Launcher.Plugin.Program
public async Task> QueryAsync(Query query, CancellationToken token)
{
+ if (IsStartupIndexProgramsRequired)
+ _ = IndexPrograms();
+
Win32[] win32;
UWP.Application[] uwps;
@@ -97,22 +100,31 @@ namespace Flow.Launcher.Plugin.Program
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
});
+ bool indexedWinApps = false;
+ bool indexedUWPApps = false;
var a = Task.Run(() =>
{
if (IsStartupIndexProgramsRequired || !_win32s.Any())
+ {
Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs);
+ indexedWinApps = true;
+ }
});
var b = Task.Run(() =>
{
if (IsStartupIndexProgramsRequired || !_uwps.Any())
+ {
Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms);
+ indexedUWPApps = true;
+ }
});
await Task.WhenAll(a, b);
- _settings.LastIndexTime = DateTime.Today;
+ if (indexedWinApps && indexedUWPApps)
+ _settings.LastIndexTime = DateTime.Today;
}
public static void IndexWin32Programs()
@@ -138,7 +150,7 @@ namespace Flow.Launcher.Plugin.Program
var t2 = Task.Run(IndexUwpPrograms);
- await Task.WhenAll(t1, t2);
+ await Task.WhenAll(t1, t2).ConfigureAwait(false);
_settings.LastIndexTime = DateTime.Today;
}
@@ -193,12 +205,14 @@ namespace Flow.Launcher.Plugin.Program
return;
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
- _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled =
- false;
+ _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
+ .FirstOrDefault()
+ .Enabled = false;
if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
- _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled =
- false;
+ _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)
+ .FirstOrDefault()
+ .Enabled = false;
_settings.DisabledProgramSources
.Add(
@@ -232,4 +246,4 @@ namespace Flow.Launcher.Plugin.Program
await IndexPrograms();
}
}
-}
\ No newline at end of file
+}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
index e4e92b9bc..d66ca345e 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs
@@ -51,7 +51,7 @@ namespace Flow.Launcher.Plugin.Program.Views
private void ViewRefresh()
{
- if(programSourceView.Items.Count == 0
+ if (programSourceView.Items.Count == 0
&& btnProgramSourceStatus.Visibility == Visibility.Visible
&& btnEditProgramSource.Visibility == Visibility.Visible)
{
@@ -70,21 +70,19 @@ namespace Flow.Launcher.Plugin.Program.Views
programSourceView.Items.Refresh();
}
- private void ReIndexing()
+ private async void ReIndexing()
{
ViewRefresh();
- Task.Run(() =>
- {
- Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Visible; });
- Main.IndexPrograms();
- Dispatcher.Invoke(() => { indexingPanel.Visibility = Visibility.Hidden; });
- });
+
+ indexingPanel.Visibility = Visibility.Visible;
+ await Main.IndexPrograms();
+ indexingPanel.Visibility = Visibility.Hidden;
}
private void btnAddProgramSource_OnClick(object sender, RoutedEventArgs e)
{
var add = new AddProgramSource(context, _settings);
- if(add.ShowDialog() ?? false)
+ if (add.ShowDialog() ?? false)
{
ReIndexing();
}
@@ -165,14 +163,14 @@ namespace Flow.Launcher.Plugin.Program.Views
UniqueIdentifier = directory
};
- directoriesToAdd.Add(source);
+ directoriesToAdd.Add(source);
}
}
if (directoriesToAdd.Count() > 0)
{
directoriesToAdd.ForEach(x => _settings.ProgramSources.Add(x));
- directoriesToAdd.ForEach(x => ProgramSettingDisplayList.Add(x));
+ directoriesToAdd.ForEach(x => ProgramSettingDisplayList.Add(x));
ViewRefresh();
ReIndexing();
@@ -238,8 +236,8 @@ namespace Flow.Launcher.Plugin.Program.Views
ProgramSettingDisplayList.SetProgramSourcesStatus(selectedItems, true);
ProgramSettingDisplayList.RemoveDisabledFromSettings();
- }
-
+ }
+
if (selectedItems.IsReindexRequired())
ReIndexing();
@@ -282,7 +280,7 @@ namespace Flow.Launcher.Plugin.Program.Views
var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string;
Sort(sortBy, direction);
-
+
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
index 6c2c18e47..1dedd9909 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
@@ -4,7 +4,7 @@
"Name": "Program",
"Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng",
- "Version": "1.2.3",
+ "Version": "1.3.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
index 7ebab91a1..624fe05bc 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
@@ -170,8 +170,7 @@ namespace Flow.Launcher.Plugin.Sys
// http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html
// FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED))
// 0 for nothing
- var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle,
- 0);
+ var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0);
if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF)
{
MessageBox.Show($"Error emptying recycle bin, error code: {result}\n" +
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
index cf8ed6041..75d7073b8 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
@@ -4,7 +4,7 @@
"Name": "System Commands",
"Description": "Provide System related commands. e.g. shutdown,lock,setting etc.",
"Author": "qianlifeng",
- "Version": "1.1.2",
+ "Version": "1.2.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml
index 452be00ee..eff1ac263 100644
--- a/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Url/Languages/en.xaml
@@ -2,6 +2,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
+ Open search in:
+ New Window
+ New Tab
+
Open url:{0}
Can't open url:{0}
diff --git a/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
index f54aea878..9219a0009 100644
--- a/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Url/SettingsControl.xaml
@@ -10,11 +10,11 @@
-
+
+ Content="{DynamicResource flowlauncher_plugin_new_window}" Click="OnNewBrowserWindowClick" />
+ Content="{DynamicResource flowlauncher_plugin_new_tab}" Click="OnNewTabClick" />
-
-
+
-
+
-
+
+ Click="OnChooseClick" FontSize="13" Content="{DynamicResource flowlauncher_plugin_websearch_choose}" Width="80"/>
(); // this handles the cancellation
+
+ if (resultStream.Length == 0)
+ return new List(); // this handles the cancellation
+
json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS");
}
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
index b150d26c1..c33ebd7e1 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs
@@ -21,8 +21,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
try
{
const string api = "https://www.google.com/complete/search?output=chrome&q=";
+
using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
- if (resultStream.Length == 0) return new List();
+
+ if (resultStream.Length == 0)
+ return new List();
+
json = await JsonDocument.ParseAsync(resultStream);
}
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs
index bf444a2f7..c58e61141 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs
@@ -8,4 +8,4 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public abstract Task> Suggestions(string query, CancellationToken token);
}
-}
\ No newline at end of file
+}
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
index c036fbf8b..aba7989be 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json
@@ -25,7 +25,7 @@
"Name": "Web Searches",
"Description": "Provide the web search ability",
"Author": "qianlifeng",
- "Version": "1.2.1",
+ "Version": "1.3.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll",