Merge branch 'dev' into DotNet5Upgrade
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 663 B After Width: | Height: | Size: 663 B |
|
Before Width: | Height: | Size: 1,019 B After Width: | Height: | Size: 1,019 B |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Output\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
|
|
@ -21,17 +24,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
};
|
||||
}
|
||||
|
||||
protected override string ExecuteQuery(Query query)
|
||||
protected override Task<Stream> ExecuteQueryAsync(Query query, CancellationToken token)
|
||||
{
|
||||
JsonRPCServerRequestModel request = new JsonRPCServerRequestModel
|
||||
{
|
||||
Method = "query",
|
||||
Parameters = new object[] { query.Search },
|
||||
Parameters = new object[] {query.Search},
|
||||
};
|
||||
|
||||
_startInfo.Arguments = $"\"{request}\"";
|
||||
|
||||
return Execute(_startInfo);
|
||||
return ExecuteAsync(_startInfo, token);
|
||||
}
|
||||
|
||||
protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest)
|
||||
|
|
@ -40,10 +43,12 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return Execute(_startInfo);
|
||||
}
|
||||
|
||||
protected override string ExecuteContextMenu(Result selectedResult) {
|
||||
JsonRPCServerRequestModel request = new JsonRPCServerRequestModel {
|
||||
protected override string ExecuteContextMenu(Result selectedResult)
|
||||
{
|
||||
JsonRPCServerRequestModel request = new JsonRPCServerRequestModel
|
||||
{
|
||||
Method = "contextmenu",
|
||||
Parameters = new object[] { selectedResult.ContextData },
|
||||
Parameters = new object[] {selectedResult.ContextData},
|
||||
};
|
||||
|
||||
_startInfo.Arguments = $"\"{request}\"";
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
[JsonPropertyName("result")]
|
||||
public new List<JsonRPCResult> Result { get; set; }
|
||||
|
||||
public string DebugMessage { get; set; }
|
||||
}
|
||||
|
||||
public class JsonRPCRequestModel : JsonRPCModelBase
|
||||
|
|
@ -58,13 +60,12 @@ namespace Flow.Launcher.Core.Plugin
|
|||
string rpc = string.Empty;
|
||||
if (Parameters != null && Parameters.Length > 0)
|
||||
{
|
||||
string parameters = Parameters.Aggregate("[", (current, o) => current + (GetParameterByType(o) + ","));
|
||||
parameters = parameters.Substring(0, parameters.Length - 1) + "]";
|
||||
rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":{1}", Method, parameters);
|
||||
string parameters = $"[{string.Join(',', Parameters.Select(GetParameterByType))}]";
|
||||
rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":{parameters}";
|
||||
}
|
||||
else
|
||||
{
|
||||
rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":[]", Method);
|
||||
rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":[]";
|
||||
}
|
||||
|
||||
return rpc;
|
||||
|
|
@ -72,26 +73,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
|
||||
private string GetParameterByType(object parameter)
|
||||
=> parameter switch
|
||||
{
|
||||
if (parameter == null) {
|
||||
return "null";
|
||||
}
|
||||
if (parameter is string)
|
||||
{
|
||||
return string.Format(@"\""{0}\""", ReplaceEscapes(parameter.ToString()));
|
||||
}
|
||||
if (parameter is int || parameter is float || parameter is double)
|
||||
{
|
||||
return string.Format(@"{0}", parameter);
|
||||
}
|
||||
if (parameter is bool)
|
||||
{
|
||||
return string.Format(@"{0}", parameter.ToString().ToLower());
|
||||
}
|
||||
return parameter.ToString();
|
||||
}
|
||||
null => "null",
|
||||
string _ => $@"\""{ReplaceEscapes(parameter.ToString())}\""",
|
||||
bool _ => $@"{parameter.ToString().ToLower()}",
|
||||
_ => parameter.ToString()
|
||||
};
|
||||
|
||||
private string ReplaceEscapes(string str)
|
||||
|
||||
private string ReplaceEscapes(string str)
|
||||
{
|
||||
return str.Replace(@"\", @"\\") //Escapes in ProcessStartInfo
|
||||
.Replace(@"\", @"\\") //Escapes itself when passed to client
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ using System.Text.Json;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// Represent the plugin that using JsonPRC
|
||||
/// every JsonRPC plugin should has its own plugin instance
|
||||
/// </summary>
|
||||
internal abstract class JsonRPCPlugin : IPlugin, IContextMenu
|
||||
internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu
|
||||
{
|
||||
protected PluginInitContext context;
|
||||
public const string JsonRPC = "JsonRPC";
|
||||
|
|
@ -27,24 +27,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
public abstract string SupportedLanguage { get; set; }
|
||||
|
||||
protected abstract string ExecuteQuery(Query query);
|
||||
protected abstract Task<Stream> ExecuteQueryAsync(Query query, CancellationToken token);
|
||||
protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest);
|
||||
protected abstract string ExecuteContextMenu(Result selectedResult);
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
{
|
||||
string output = ExecuteQuery(query);
|
||||
try
|
||||
{
|
||||
return DeserializedResult(output);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
string output = ExecuteContextMenu(selectedResult);
|
||||
|
|
@ -59,50 +45,71 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<List<Result>> DeserializedResultAsync(Stream output)
|
||||
{
|
||||
if (output == Stream.Null) return null;
|
||||
|
||||
JsonRPCQueryResponseModel queryResponseModel = await
|
||||
JsonSerializer.DeserializeAsync<JsonRPCQueryResponseModel>(output);
|
||||
|
||||
return ParseResults(queryResponseModel);
|
||||
}
|
||||
|
||||
private List<Result> DeserializedResult(string output)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(output))
|
||||
if (string.IsNullOrEmpty(output)) return null;
|
||||
|
||||
JsonRPCQueryResponseModel queryResponseModel =
|
||||
JsonSerializer.Deserialize<JsonRPCQueryResponseModel>(output);
|
||||
return ParseResults(queryResponseModel);
|
||||
}
|
||||
|
||||
private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
if (queryResponseModel.Result == null) return null;
|
||||
|
||||
if(!string.IsNullOrEmpty(queryResponseModel.DebugMessage))
|
||||
{
|
||||
List<Result> results = new List<Result>();
|
||||
context.API.ShowMsg(queryResponseModel.DebugMessage);
|
||||
}
|
||||
|
||||
JsonRPCQueryResponseModel queryResponseModel = JsonSerializer.Deserialize<JsonRPCQueryResponseModel>(output);
|
||||
if (queryResponseModel.Result == null) return null;
|
||||
|
||||
foreach (JsonRPCResult result in queryResponseModel.Result)
|
||||
foreach (JsonRPCResult result in queryResponseModel.Result)
|
||||
{
|
||||
result.Action = c =>
|
||||
{
|
||||
JsonRPCResult result1 = result;
|
||||
result.Action = c =>
|
||||
{
|
||||
if (result1.JsonRPCAction == null) return false;
|
||||
if (result.JsonRPCAction == null) return false;
|
||||
|
||||
if (!String.IsNullOrEmpty(result1.JsonRPCAction.Method))
|
||||
if (!string.IsNullOrEmpty(result.JsonRPCAction.Method))
|
||||
{
|
||||
if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher."))
|
||||
{
|
||||
if (result1.JsonRPCAction.Method.StartsWith("Flow.Launcher."))
|
||||
ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4),
|
||||
result.JsonRPCAction.Parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
string actionReponse = ExecuteCallback(result.JsonRPCAction);
|
||||
JsonRPCRequestModel jsonRpcRequestModel =
|
||||
JsonSerializer.Deserialize<JsonRPCRequestModel>(actionReponse);
|
||||
if (jsonRpcRequestModel != null
|
||||
&& !string.IsNullOrEmpty(jsonRpcRequestModel.Method)
|
||||
&& jsonRpcRequestModel.Method.StartsWith("Flow.Launcher."))
|
||||
{
|
||||
ExecuteFlowLauncherAPI(result1.JsonRPCAction.Method.Substring(4), result1.JsonRPCAction.Parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
string actionReponse = ExecuteCallback(result1.JsonRPCAction);
|
||||
JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize<JsonRPCRequestModel>(actionReponse);
|
||||
if (jsonRpcRequestModel != null
|
||||
&& !String.IsNullOrEmpty(jsonRpcRequestModel.Method)
|
||||
&& jsonRpcRequestModel.Method.StartsWith("Flow.Launcher."))
|
||||
{
|
||||
ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), jsonRpcRequestModel.Parameters);
|
||||
}
|
||||
ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4),
|
||||
jsonRpcRequestModel.Parameters);
|
||||
}
|
||||
}
|
||||
return !result1.JsonRPCAction.DontHideAfterAction;
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return !result.JsonRPCAction.DontHideAfterAction;
|
||||
};
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private void ExecuteFlowLauncherAPI(string method, object[] parameters)
|
||||
|
|
@ -117,9 +124,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
catch (Exception)
|
||||
{
|
||||
#if (DEBUG)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -130,17 +135,20 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="arguments"></param>
|
||||
/// <param name="token">Cancellation Token</param>
|
||||
/// <returns></returns>
|
||||
protected string Execute(string fileName, string arguments)
|
||||
protected Task<Stream> ExecuteAsync(string fileName, string arguments, CancellationToken token = default)
|
||||
{
|
||||
ProcessStartInfo start = new ProcessStartInfo();
|
||||
start.FileName = fileName;
|
||||
start.Arguments = arguments;
|
||||
start.UseShellExecute = false;
|
||||
start.CreateNoWindow = true;
|
||||
start.RedirectStandardOutput = true;
|
||||
start.RedirectStandardError = true;
|
||||
return Execute(start);
|
||||
ProcessStartInfo start = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = arguments,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
return ExecuteAsync(start, token);
|
||||
}
|
||||
|
||||
protected string Execute(ProcessStartInfo startInfo)
|
||||
|
|
@ -148,52 +156,102 @@ namespace Flow.Launcher.Core.Plugin
|
|||
try
|
||||
{
|
||||
using var process = Process.Start(startInfo);
|
||||
if (process == null)
|
||||
{
|
||||
Log.Error("|JsonRPCPlugin.Execute|Can't start new process");
|
||||
return string.Empty;
|
||||
}
|
||||
if (process == null) return string.Empty;
|
||||
|
||||
using var standardOutput = process.StandardOutput;
|
||||
var result = standardOutput.ReadToEnd();
|
||||
|
||||
if (string.IsNullOrEmpty(result))
|
||||
{
|
||||
using (var standardError = process.StandardError)
|
||||
using var standardError = process.StandardError;
|
||||
var error = standardError.ReadToEnd();
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
var error = standardError.ReadToEnd();
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
Log.Error($"|JsonRPCPlugin.Execute|{error}");
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error.");
|
||||
return string.Empty;
|
||||
}
|
||||
Log.Error($"|JsonRPCPlugin.Execute|{error}");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error.");
|
||||
return string.Empty;
|
||||
}
|
||||
else if (result.StartsWith("DEBUG:"))
|
||||
|
||||
if (result.StartsWith("DEBUG:"))
|
||||
{
|
||||
MessageBox.Show(new Form { TopMost = true }, result.Substring(6));
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e);
|
||||
Log.Exception(
|
||||
$"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
||||
e);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(PluginInitContext ctx)
|
||||
protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default)
|
||||
{
|
||||
context = ctx;
|
||||
try
|
||||
{
|
||||
using var process = Process.Start(startInfo);
|
||||
if (process == null)
|
||||
{
|
||||
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
|
||||
return Stream.Null;
|
||||
}
|
||||
|
||||
var result = process.StandardOutput.BaseStream;
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (!process.StandardError.EndOfStream)
|
||||
{
|
||||
using var standardError = process.StandardError;
|
||||
var error = await standardError.ReadToEndAsync();
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}");
|
||||
return Stream.Null;
|
||||
}
|
||||
|
||||
Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error.");
|
||||
return Stream.Null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(
|
||||
$"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
||||
e);
|
||||
return Stream.Null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
|
||||
{
|
||||
var output = await ExecuteQueryAsync(query, token);
|
||||
try
|
||||
{
|
||||
return await DeserializedResultAsync(output);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
this.context = context;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,11 +195,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
catch (OperationCanceledException)
|
||||
{
|
||||
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
|
||||
return results = null;
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
}
|
||||
|
||||
return results;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -28,7 +30,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
}
|
||||
|
||||
protected override string ExecuteQuery(Query query)
|
||||
protected override Task<Stream> ExecuteQueryAsync(Query query, CancellationToken token)
|
||||
{
|
||||
JsonRPCServerRequestModel request = new JsonRPCServerRequestModel
|
||||
{
|
||||
|
|
@ -40,13 +42,14 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// todo happlebao why context can't be used in constructor
|
||||
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
|
||||
|
||||
return Execute(_startInfo);
|
||||
return ExecuteAsync(_startInfo, token);
|
||||
}
|
||||
|
||||
protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest)
|
||||
{
|
||||
_startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\"";
|
||||
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
|
||||
// TODO: Async Action
|
||||
return Execute(_startInfo);
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +61,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
_startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\"";
|
||||
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
|
||||
|
||||
// TODO: Async Action
|
||||
return Execute(_startInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ namespace Flow.Launcher.Core
|
|||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
|
||||
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e is TaskCanceledException)
|
||||
{
|
||||
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
|
||||
api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Output\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Windows.Interop;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Http
|
||||
{
|
||||
|
|
@ -18,6 +20,8 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
|
||||
private static HttpClient client = new HttpClient();
|
||||
|
||||
public static IPublicAPI API { get; set; }
|
||||
|
||||
static Http()
|
||||
{
|
||||
// need to be added so it would work on a win10 machine
|
||||
|
|
@ -50,25 +54,36 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
/// </summary>
|
||||
public static void UpdateProxy(ProxyProperty property)
|
||||
{
|
||||
(WebProxy.Address, WebProxy.Credentials) = property switch
|
||||
if (string.IsNullOrEmpty(Proxy.Server))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
ProxyProperty.Enabled => Proxy.Enabled switch
|
||||
(WebProxy.Address, WebProxy.Credentials) = property switch
|
||||
{
|
||||
true => Proxy.UserName switch
|
||||
ProxyProperty.Enabled => Proxy.Enabled switch
|
||||
{
|
||||
var userName when !string.IsNullOrEmpty(userName) =>
|
||||
(new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
|
||||
_ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
|
||||
new NetworkCredential(Proxy.UserName, Proxy.Password))
|
||||
true when !string.IsNullOrEmpty(Proxy.Server) => Proxy.UserName switch
|
||||
{
|
||||
var userName when string.IsNullOrEmpty(userName) =>
|
||||
(new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
|
||||
_ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
|
||||
new NetworkCredential(Proxy.UserName, Proxy.Password))
|
||||
},
|
||||
_ => (null, null)
|
||||
},
|
||||
false => (null, null)
|
||||
},
|
||||
ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
catch(UriFormatException e)
|
||||
{
|
||||
API.ShowMsg("Please try again", "Unable to parse Http Proxy");
|
||||
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Output\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
33
Flow.Launcher.Test/HttpTest.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
{
|
||||
[TestFixture]
|
||||
class HttpTest
|
||||
{
|
||||
[Test]
|
||||
public void GivenHttpProxy_WhenUpdated_ThenWebProxyShouldAlsoBeUpdatedToTheSame()
|
||||
{
|
||||
HttpProxy proxy = new HttpProxy();
|
||||
Http.Proxy = proxy;
|
||||
|
||||
proxy.Enabled = true;
|
||||
proxy.Server = "127.0.0.1";
|
||||
Assert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}"));
|
||||
Assert.IsNull(Http.WebProxy.Credentials);
|
||||
|
||||
proxy.UserName = "test";
|
||||
Assert.NotNull(Http.WebProxy.Credentials);
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName);
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, "");
|
||||
|
||||
proxy.Password = "test password";
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,6 @@ namespace Flow.Launcher
|
|||
_settingsVM = new SettingWindowViewModel(_updater, _portable);
|
||||
_settings = _settingsVM.Settings;
|
||||
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
_alphabet.Initialize(_settings);
|
||||
_stringMatcher = new StringMatcher(_alphabet);
|
||||
|
|
@ -71,6 +70,10 @@ namespace Flow.Launcher
|
|||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
|
||||
|
||||
Http.API = API;
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
await PluginManager.InitializePlugins(API);
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Output\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
using System;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.BrowserBookmark\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Caculator\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.ControlPanel\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
this.settings = settings;
|
||||
}
|
||||
|
||||
private class PathEqualityComparator : IEqualityComparer<Result>
|
||||
{
|
||||
private static PathEqualityComparator instance;
|
||||
public static PathEqualityComparator Instance => instance ??= new PathEqualityComparator();
|
||||
public bool Equals(Result x, Result y)
|
||||
{
|
||||
return x.SubTitle == y.SubTitle;
|
||||
}
|
||||
|
||||
public int GetHashCode(Result obj)
|
||||
{
|
||||
return obj.SubTitle.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<List<Result>> SearchAsync(Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var results = new HashSet<Result>(PathEqualityComparator.Instance);
|
||||
|
||||
var querySearch = query.Search;
|
||||
|
||||
|
|
@ -38,7 +53,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
var quickaccessLinks = QuickAccess.AccessLinkListMatched(query, settings.QuickAccessLinks);
|
||||
|
||||
if (quickaccessLinks.Count > 0)
|
||||
results.AddRange(quickaccessLinks);
|
||||
results.UnionWith(quickaccessLinks);
|
||||
|
||||
var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch);
|
||||
|
||||
|
|
@ -50,9 +65,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath)
|
||||
{
|
||||
results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false));
|
||||
results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false));
|
||||
|
||||
return results;
|
||||
return results.ToList();
|
||||
}
|
||||
|
||||
var locationPath = querySearch;
|
||||
|
|
@ -62,7 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
// Check that actual location exists, otherwise directory search will throw directory not found exception
|
||||
if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath)))
|
||||
return results;
|
||||
return results.ToList();
|
||||
|
||||
var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
|
||||
|
||||
|
|
@ -79,9 +94,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
results.AddRange(directoryResult);
|
||||
results.UnionWith(directoryResult);
|
||||
|
||||
return results;
|
||||
return results.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<Result>> WindowsIndexFileContentSearchAsync(Query query, string querySearchString, CancellationToken token)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"Name": "Explorer",
|
||||
"Description": "Search and manage files and folders. Explorer utilises Windows Index Search",
|
||||
"Author": "Jeremy Wu",
|
||||
"Version": "1.7.0",
|
||||
"Version": "1.7.1",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginIndicator\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
Uninstall(x.PluginExistingMetadata);
|
||||
Uninstall(x.PluginExistingMetadata, false);
|
||||
|
||||
var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory,
|
||||
$"{x.Name}-{x.NewVersion}.zip");
|
||||
|
|
@ -414,10 +414,13 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
return Search(results, uninstallSearch);
|
||||
}
|
||||
|
||||
private void Uninstall(PluginMetadata plugin)
|
||||
private void Uninstall(PluginMetadata plugin, bool removedSetting = true)
|
||||
{
|
||||
PluginManager.Settings.Plugins.Remove(plugin.ID);
|
||||
PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||
if (removedSetting)
|
||||
{
|
||||
PluginManager.Settings.Plugins.Remove(plugin.ID);
|
||||
PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||
}
|
||||
|
||||
// Marked for deletion. Will be deleted on next start up
|
||||
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"Name": "Plugins Manager",
|
||||
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
|
||||
"Author": "Jeremy Wu",
|
||||
"Version": "1.7.0",
|
||||
"Version": "1.7.1",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.ProcessKiller\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Program\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ using Flow.Launcher.Plugin.SharedModels;
|
|||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using System.Diagnostics;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.Programs
|
||||
{
|
||||
[Serializable]
|
||||
public class Win32 : IProgram
|
||||
public class Win32 : IProgram, IEquatable<Win32>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string UniqueIdentifier { get; set; }
|
||||
|
|
@ -35,6 +36,20 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
private const string ShortcutExtension = "lnk";
|
||||
private const string ExeExtension = "exe";
|
||||
|
||||
private static readonly Win32 Default = new Win32()
|
||||
{
|
||||
Name = string.Empty,
|
||||
Description = string.Empty,
|
||||
IcoPath = string.Empty,
|
||||
FullPath = string.Empty,
|
||||
LnkResolvedPath = null,
|
||||
ParentDirectory = string.Empty,
|
||||
ExecutableName = null,
|
||||
UniqueIdentifier = string.Empty,
|
||||
Valid = false,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
|
||||
public Result Result(string query, IPublicAPI api)
|
||||
{
|
||||
|
|
@ -423,12 +438,12 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
private static Win32 GetProgramFromPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
return Default;
|
||||
|
||||
path = Environment.ExpandEnvironmentVariables(path);
|
||||
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
return Default;
|
||||
|
||||
var entry = Win32Program(path);
|
||||
|
||||
|
|
@ -470,14 +485,15 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
}
|
||||
|
||||
private static Win32[] ProgramsHasher(IEnumerable<Win32> programs)
|
||||
private static IEnumerable<Win32> ProgramsHasher(IEnumerable<Win32> programs)
|
||||
{
|
||||
return programs.GroupBy(p => p.FullPath.ToLower())
|
||||
.SelectMany(g =>
|
||||
{
|
||||
if (g.Count() > 1)
|
||||
return DistinctBy(g.Where(p => !string.IsNullOrEmpty(p.Description)), x => x.Description);
|
||||
return g;
|
||||
var temp = g.Where(g => !string.IsNullOrEmpty(g.Description)).ToList();
|
||||
if (temp.Any())
|
||||
return DistinctBy(temp, x => x.Description);
|
||||
return g.Take(1);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
|
|
@ -489,22 +505,26 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
var programs = Enumerable.Empty<Win32>();
|
||||
|
||||
var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
|
||||
|
||||
programs = programs.Concat(unregistered);
|
||||
|
||||
var autoIndexPrograms = Enumerable.Empty<Win32>();
|
||||
|
||||
if (settings.EnableRegistrySource)
|
||||
{
|
||||
var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(appPaths);
|
||||
autoIndexPrograms = autoIndexPrograms.Concat(appPaths);
|
||||
}
|
||||
|
||||
if (settings.EnableStartMenuSource)
|
||||
{
|
||||
var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(startMenu);
|
||||
autoIndexPrograms = autoIndexPrograms.Concat(startMenu);
|
||||
}
|
||||
|
||||
autoIndexPrograms = ProgramsHasher(autoIndexPrograms);
|
||||
|
||||
return ProgramsHasher(programs.Where(p => p != null));
|
||||
return programs.Concat(autoIndexPrograms).Distinct().ToArray();
|
||||
}
|
||||
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
|
||||
catch (Exception e)
|
||||
|
|
@ -522,5 +542,18 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return UniqueIdentifier.GetHashCode();
|
||||
}
|
||||
|
||||
public bool Equals([AllowNull] Win32 other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return UniqueIdentifier == other.UniqueIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Program",
|
||||
"Description": "Search programs in Flow.Launcher",
|
||||
"Author": "qianlifeng",
|
||||
"Version": "1.4.2",
|
||||
"Version": "1.4.3",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Shell\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Sys\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Url\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.WebSearch\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
|
|
|
|||
|
|
@ -123,12 +123,12 @@ namespace Flow.Launcher.Plugin.WebSearch
|
|||
var source = _settings.SelectedSuggestion;
|
||||
if (source != null)
|
||||
{
|
||||
var suggestions = await source.Suggestions(keyword, token);
|
||||
var suggestions = await source.Suggestions(keyword, token).ConfigureAwait(false);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
return null;
|
||||
|
||||
var resultsFromSuggestion = suggestions.Select(o => new Result
|
||||
var resultsFromSuggestion = suggestions?.Select(o => new Result
|
||||
{
|
||||
Title = o,
|
||||
SubTitle = subtitle,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
|
|||
catch (HttpRequestException e)
|
||||
{
|
||||
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
|
||||
return new List<string>();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(result)) return new List<string>();
|
||||
|
|
|
|||
|
|
@ -24,16 +24,13 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
|
|||
const string api = "https://api.bing.com/qsonhs.aspx?q=";
|
||||
|
||||
using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false);
|
||||
|
||||
if (resultStream.Length == 0)
|
||||
return new List<string>(); // this handles the cancellation
|
||||
|
||||
|
||||
json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS");
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return null;
|
||||
return new List<string>();
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,15 +24,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
|
|||
|
||||
using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false);
|
||||
|
||||
if (resultStream.Length == 0)
|
||||
return new List<string>();
|
||||
|
||||
json = await JsonDocument.ParseAsync(resultStream);
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return null;
|
||||
return new List<string>();
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
|
|
|
|||
24
README.md
|
|
@ -4,11 +4,12 @@
|
|||
</a>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||
[](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev)
|
||||
[](https://github.com/Flow-Launcher/Flow.Launcher/releases)
|
||||
[](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)
|
||||

|
||||
[](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest)
|
||||
[](https://flow-launcher.github.io/docs)
|
||||
[](https://discord.gg/AvgAQgh)
|
||||
|
||||
Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at being more than an app launcher, it searches, integrates and expands on functionalities. Flow will continue to evolve, designed to be open and built with the community at heart.
|
||||
|
|
@ -16,6 +17,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
|
|||
<sub>Remember to star it, flow will love you more :)</sub>
|
||||
|
||||
## Features
|
||||
|
||||

|
||||
|
||||
- Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.
|
||||
|
|
@ -37,27 +39,27 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
|
|||
|
||||
Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up.
|
||||
|
||||
**Integrations**
|
||||
- Python plugins:
|
||||
- Once a Python plugin has been detected at start up, you will be prompted to either select the location or allow Python 3.9.1 to automatic download and install.
|
||||
### Integrations
|
||||
- If you use Python plugins:
|
||||
- Install [Python3](https://www.python.org/downloads/), download `.exe` installer.
|
||||
- Add Python to `%PATH%` or set it in flow's settings.
|
||||
- Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`.
|
||||
- The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`.
|
||||
- Start to launch your Python plugins.
|
||||
- Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest).
|
||||
|
||||
**Usage**
|
||||
### Usage
|
||||
- Open flow's search window: <kbd>Alt</kbd>+<kbd>Space</kbd> is the default hotkey.
|
||||
- Search programs, bookmarks and control panel items with acronyms eg. `gk` or `gp` for GitKraken Preview; fuzzy search eg. `code` or `vis` for Visual Studio Code.
|
||||
- Open context menu: on the selected result, press <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
|
||||
- Cancel/Return to previous screen: <kbd>Esc</kbd>.
|
||||
- Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name.
|
||||
- Press `F5` while in the query window to reload all plugin data.
|
||||
- Install/Uninstall/Update plugins: in the search window, type `pm` `install`/`uninstall`/`update` + the plugin name.
|
||||
- Saved user settings are located:
|
||||
- If using roaming: `%APPDATA%\FlowLauncher`
|
||||
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData`
|
||||
- Logs are saved along with your user settings folder.
|
||||
|
||||
[More tips](https://flow-launcher.github.io/docs/#/usage-tips)
|
||||
|
||||
## Status
|
||||
|
||||
Flow is under heavy development, but the code base is stable, so contributions are very welcome. If you would like to help maintain it, please do not hesistate to get in touch.
|
||||
|
|
@ -70,7 +72,7 @@ You will find the main goals of flow placed under Projects board, so feel free t
|
|||
|
||||
Get in touch if you like to join the Flow-Launcher Team and help build this great tool.
|
||||
|
||||
**Question/Suggestion**
|
||||
## Question/Suggestion
|
||||
|
||||
Yes please, submit an issue to let us know.
|
||||
|
||||
|
|
@ -86,4 +88,4 @@ Install .Net Core 5 SDK via Visual Studio installer or manually from [here](http
|
|||
|
||||
## Documentation
|
||||
|
||||
[Wiki](https://github.com/Flow-Launcher/Flow.Launcher/wiki)
|
||||
Visit [here](https://flow-launcher.github.io/docs) for more information on usage, development and design documentations
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.7.1")]
|
||||
[assembly: AssemblyFileVersion("1.7.1")]
|
||||
[assembly: AssemblyInformationalVersion("1.7.1")]
|
||||
[assembly: AssemblyVersion("1.7.2")]
|
||||
[assembly: AssemblyFileVersion("1.7.2")]
|
||||
[assembly: AssemblyInformationalVersion("1.7.2")]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
version: '1.7.1.{build}'
|
||||
version: '1.7.2.{build}'
|
||||
|
||||
init:
|
||||
- ps: |
|
||||
|
|
|
|||