From 0da21b1ed75e05627622ea73269c8aad662a2392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 14:37:37 +0800 Subject: [PATCH 1/8] JSONRPC Async Model --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 20 +-- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 120 +++++++++--------- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 12 +- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index 6a96a94b754..cbf048f1696 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Plugin @@ -21,34 +23,36 @@ public ExecutablePlugin(string filename) }; } - protected override string ExecuteQuery(Query query) + protected override Task 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) { _startInfo.Arguments = $"\"{rpcRequest}\""; - return Execute(_startInfo); + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } - 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}\""; - return Execute(_startInfo); + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index c7ad70391b7..626013cf0f2 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Reflection; 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 +16,7 @@ namespace Flow.Launcher.Core.Plugin /// Represent the plugin that using JsonPRC /// every JsonRPC plugin should has its own plugin instance /// - internal abstract class JsonRPCPlugin : IPlugin, IContextMenu + internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu { protected PluginInitContext context; public const string JsonRPC = "JsonRPC"; @@ -27,23 +26,10 @@ internal abstract class JsonRPCPlugin : IPlugin, IContextMenu /// public abstract string SupportedLanguage { get; set; } - protected abstract string ExecuteQuery(Query query); + protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); - public List 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 LoadContextMenus(Result selectedResult) { @@ -65,44 +51,47 @@ private List DeserializedResult(string output) { List results = new List(); - JsonRPCQueryResponseModel queryResponseModel = JsonSerializer.Deserialize(output); + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); if (queryResponseModel.Result == null) return null; foreach (JsonRPCResult result in queryResponseModel.Result) { - 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 (result1.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(result1.JsonRPCAction.Method.Substring(4), result1.JsonRPCAction.Parameters); + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), + result.JsonRPCAction.Parameters); } else { - string actionReponse = ExecuteCallback(result1.JsonRPCAction); - JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize(actionReponse); + string actionReponse = ExecuteCallback(result.JsonRPCAction); + JsonRPCRequestModel jsonRpcRequestModel = + JsonSerializer.Deserialize(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; + + return !result.JsonRPCAction.DontHideAfterAction; }; results.Add(result); } + return results; } - else - { - return null; - } + + return null; } private void ExecuteFlowLauncherAPI(string method, object[] parameters) @@ -117,9 +106,7 @@ private void ExecuteFlowLauncherAPI(string method, object[] parameters) catch (Exception) { #if (DEBUG) - { - throw; - } + throw; #endif } } @@ -130,8 +117,9 @@ private void ExecuteFlowLauncherAPI(string method, object[] parameters) /// /// /// + /// Cancellation Token /// - protected string Execute(string fileName, string arguments) + protected async Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) { ProcessStartInfo start = new ProcessStartInfo(); start.FileName = fileName; @@ -140,10 +128,10 @@ protected string Execute(string fileName, string arguments) start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; - return Execute(start); + return await ExecuteAsync(start, token); } - protected string Execute(ProcessStartInfo startInfo) + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { try { @@ -155,45 +143,59 @@ protected string Execute(ProcessStartInfo startInfo) } using var standardOutput = process.StandardOutput; - var result = standardOutput.ReadToEnd(); + var result = await standardOutput.ReadToEndAsync(); + if (token.IsCancellationRequested) + return string.Empty; + if (string.IsNullOrEmpty(result)) { - using (var standardError = process.StandardError) + using var standardError = process.StandardError; + var error = await standardError.ReadToEndAsync(); + 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; } - } - else if (result.StartsWith("DEBUG:")) - { - MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); + + Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); return string.Empty; } - else + + if (result.StartsWith("DEBUG:")) { - return result; + MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + return string.Empty; } + 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) + public async Task> QueryAsync(Query query, CancellationToken token) + { + string output = await ExecuteQueryAsync(query, token); + try + { + return DeserializedResult(output); + } + catch (Exception e) + { + Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e); + return null; + } + } + + public Task InitAsync(PluginInitContext context) { - context = ctx; + this.context = context; + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 3c5a3a699a2..3147267358e 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -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 @@ public PythonPlugin(string filename) } - protected override string ExecuteQuery(Query query) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { @@ -40,14 +42,15 @@ protected override string ExecuteQuery(Query query) // 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; - return Execute(_startInfo); + // TODO: Async Action + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } protected override string ExecuteContextMenu(Result selectedResult) { @@ -58,7 +61,8 @@ protected override string ExecuteContextMenu(Result selectedResult) { _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - return Execute(_startInfo); + // TODO: Async Action + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } } } \ No newline at end of file From b15319ea4da793d8401a97b0c782fd3164860097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 14:43:17 +0800 Subject: [PATCH 2/8] Keep the Sync version for ContextMenu and Callback --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 4 +- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 50 +++++++++++++++++-- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 4 +- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index cbf048f1696..d0d47aa58ce 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -39,7 +39,7 @@ protected override Task ExecuteQueryAsync(Query query, CancellationToken protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { _startInfo.Arguments = $"\"{rpcRequest}\""; - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } protected override string ExecuteContextMenu(Result selectedResult) @@ -52,7 +52,7 @@ protected override string ExecuteContextMenu(Result selectedResult) _startInfo.Arguments = $"\"{request}\""; - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 626013cf0f2..7d221b74d09 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -131,6 +131,48 @@ protected async Task ExecuteAsync(string fileName, string arguments, Can return await ExecuteAsync(start, token); } + protected string Execute(ProcessStartInfo startInfo) + { + try + { + using var process = Process.Start(startInfo); + if (process == null) return string.Empty; + + using var standardOutput = process.StandardOutput; + var result = standardOutput.ReadToEnd(); + + if (string.IsNullOrEmpty(result)) + { + using var standardError = process.StandardError; + var error = standardError.ReadToEnd(); + if (!string.IsNullOrEmpty(error)) + { + Log.Error($"|JsonRPCPlugin.Execute|{error}"); + return string.Empty; + } + + Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); + return string.Empty; + } + + if (result.StartsWith("DEBUG:")) + { + MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + return string.Empty; + } + + return result; + + } + catch (Exception e) + { + Log.Exception( + $"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", + e); + return string.Empty; + } + } + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { try @@ -138,7 +180,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati using var process = Process.Start(startInfo); if (process == null) { - Log.Error("|JsonRPCPlugin.Execute|Can't start new process"); + Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); return string.Empty; } @@ -153,11 +195,11 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati var error = await standardError.ReadToEndAsync(); if (!string.IsNullOrEmpty(error)) { - Log.Error($"|JsonRPCPlugin.Execute|{error}"); + Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); return string.Empty; } - Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); + Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error."); return string.Empty; } @@ -172,7 +214,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati catch (Exception e) { Log.Exception( - $"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", + $"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); return string.Empty; } diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 3147267358e..750684954ee 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -50,7 +50,7 @@ protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } protected override string ExecuteContextMenu(Result selectedResult) { @@ -62,7 +62,7 @@ protected override string ExecuteContextMenu(Result selectedResult) { _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } } } \ No newline at end of file From f93dcaef73a9478d0c5920c9eca41ff2e91ff3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 9 Feb 2021 14:03:22 +0800 Subject: [PATCH 3/8] use throw instead of return empty string --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 7d221b74d09..5b56f934727 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -185,14 +185,18 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati } using var standardOutput = process.StandardOutput; + + token.ThrowIfCancellationRequested(); + var result = await standardOutput.ReadToEndAsync(); - if (token.IsCancellationRequested) - return string.Empty; + token.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(result)) { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); + token.ThrowIfCancellationRequested(); + if (!string.IsNullOrEmpty(error)) { Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); From aabb820f6f7e017bedff097ab6a03ba03433721f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 12:02:59 +0800 Subject: [PATCH 4/8] Optimize ToString() method override --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 33 ++++++++-------------- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 4 +-- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index 247fe1889d6..a0731f8fb38 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -58,13 +58,12 @@ public override string ToString() 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 +71,16 @@ public override string ToString() } 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 diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 5b56f934727..a37b2b68bdd 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -119,7 +119,7 @@ private void ExecuteFlowLauncherAPI(string method, object[] parameters) /// /// Cancellation Token /// - protected async Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) + protected Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) { ProcessStartInfo start = new ProcessStartInfo(); start.FileName = fileName; @@ -128,7 +128,7 @@ protected async Task ExecuteAsync(string fileName, string arguments, Can start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; - return await ExecuteAsync(start, token); + return ExecuteAsync(start, token); } protected string Execute(ProcessStartInfo startInfo) From 34a7a1522642f4252e6106511cb8538508bfdfa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 13:56:27 +0800 Subject: [PATCH 5/8] Use Stream model --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 3 +- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 141 ++++++++++-------- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 2 +- 3 files changed, 78 insertions(+), 68 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index d0d47aa58ce..13b6ac968da 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin; @@ -23,7 +24,7 @@ public ExecutablePlugin(string filename) }; } - protected override Task ExecuteQueryAsync(Query query, CancellationToken token) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index a37b2b68bdd..a5633b1187c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Text.Json; using System.Threading; @@ -26,7 +27,7 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu /// public abstract string SupportedLanguage { get; set; } - protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); + protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); @@ -45,53 +46,66 @@ public List LoadContextMenus(Result selectedResult) } } - private List DeserializedResult(string output) - { - if (!String.IsNullOrEmpty(output)) - { - List results = new List(); - JsonRPCQueryResponseModel queryResponseModel = - JsonSerializer.Deserialize(output); - if (queryResponseModel.Result == null) return null; - foreach (JsonRPCResult result in queryResponseModel.Result) + private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) + { + var results = new List(); + if (queryResponseModel.Result == null) return null; + + foreach (JsonRPCResult result in queryResponseModel.Result) + { + result.Action = c => { - result.Action = c => - { - if (result.JsonRPCAction == null) return false; + if (result.JsonRPCAction == null) return false; - if (!String.IsNullOrEmpty(result.JsonRPCAction.Method)) + if (!string.IsNullOrEmpty(result.JsonRPCAction.Method)) + { + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) - { - ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), - result.JsonRPCAction.Parameters); - } - else + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), + result.JsonRPCAction.Parameters); + } + else + { + string actionReponse = ExecuteCallback(result.JsonRPCAction); + JsonRPCRequestModel jsonRpcRequestModel = + JsonSerializer.Deserialize(actionReponse); + if (jsonRpcRequestModel != null + && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) + && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) { - string actionReponse = ExecuteCallback(result.JsonRPCAction); - JsonRPCRequestModel jsonRpcRequestModel = - JsonSerializer.Deserialize(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 !result.JsonRPCAction.DontHideAfterAction; - }; - results.Add(result); - } - - return results; + return !result.JsonRPCAction.DontHideAfterAction; + }; + results.Add(result); } - return null; + return results; + } + + private async Task> DeserializedResultAsync(Stream output) + { + if (output == Stream.Null) return null; + + JsonRPCQueryResponseModel queryResponseModel = await + JsonSerializer.DeserializeAsync(output); + + return ParseResults(queryResponseModel); + } + + private List DeserializedResult(string output) + { + if (string.IsNullOrEmpty(output)) return null; + + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); + return ParseResults(queryResponseModel); } private void ExecuteFlowLauncherAPI(string method, object[] parameters) @@ -119,15 +133,17 @@ private void ExecuteFlowLauncherAPI(string method, object[] parameters) /// /// Cancellation Token /// - protected Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) + protected Task 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; + ProcessStartInfo start = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; return ExecuteAsync(start, token); } @@ -137,7 +153,7 @@ protected string Execute(ProcessStartInfo startInfo) { using var process = Process.Start(startInfo); if (process == null) return string.Empty; - + using var standardOutput = process.StandardOutput; var result = standardOutput.ReadToEnd(); @@ -157,12 +173,12 @@ protected string Execute(ProcessStartInfo startInfo) if (result.StartsWith("DEBUG:")) { - MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); return string.Empty; } return result; - + } catch (Exception e) { @@ -173,7 +189,7 @@ protected string Execute(ProcessStartInfo startInfo) } } - protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { try { @@ -181,17 +197,14 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati if (process == null) { Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); - return string.Empty; + return Stream.Null; } - using var standardOutput = process.StandardOutput; + var result = process.StandardOutput.BaseStream; token.ThrowIfCancellationRequested(); - var result = await standardOutput.ReadToEndAsync(); - token.ThrowIfCancellationRequested(); - - if (string.IsNullOrEmpty(result)) + if (!process.StandardError.EndOfStream) { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); @@ -200,17 +213,11 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati if (!string.IsNullOrEmpty(error)) { Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); - return string.Empty; + return Stream.Null; } Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error."); - return string.Empty; - } - - if (result.StartsWith("DEBUG:")) - { - MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); - return string.Empty; + return Stream.Null; } return result; @@ -220,16 +227,18 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati Log.Exception( $"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); - return string.Empty; + return Stream.Null; } } + + public async Task> QueryAsync(Query query, CancellationToken token) { - string output = await ExecuteQueryAsync(query, token); + var output = await ExecuteQueryAsync(query, token); try { - return DeserializedResult(output); + return await DeserializedResultAsync(output); } catch (Exception e) { diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 750684954ee..356a68a81b1 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -30,7 +30,7 @@ public PythonPlugin(string filename) } - protected override Task ExecuteQueryAsync(Query query, CancellationToken token) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { From 824ea07ab2ca46c6563563708371f1af2d2a7f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 13:59:59 +0800 Subject: [PATCH 6/8] add Debug message field for debugging --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 2 + Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 43 ++++++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index a0731f8fb38..b1c8ff6ea94 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -45,6 +45,8 @@ public class JsonRPCQueryResponseModel : JsonRPCResponseModel { [JsonPropertyName("result")] public new List Result { get; set; } + + public string DebugMessage { get; set; } } public class JsonRPCRequestModel : JsonRPCModelBase diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index a5633b1187c..e342a5f944c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -48,11 +48,35 @@ public List LoadContextMenus(Result selectedResult) + private async Task> DeserializedResultAsync(Stream output) + { + if (output == Stream.Null) return null; + + JsonRPCQueryResponseModel queryResponseModel = await + JsonSerializer.DeserializeAsync(output); + + return ParseResults(queryResponseModel); + } + + private List DeserializedResult(string output) + { + if (string.IsNullOrEmpty(output)) return null; + + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); + return ParseResults(queryResponseModel); + } + private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) { var results = new List(); if (queryResponseModel.Result == null) return null; + if(!string.IsNullOrEmpty(queryResponseModel.DebugMessage)) + { + context.API.ShowMsg(queryResponseModel.DebugMessage); + } + foreach (JsonRPCResult result in queryResponseModel.Result) { result.Action = c => @@ -89,25 +113,6 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) return results; } - private async Task> DeserializedResultAsync(Stream output) - { - if (output == Stream.Null) return null; - - JsonRPCQueryResponseModel queryResponseModel = await - JsonSerializer.DeserializeAsync(output); - - return ParseResults(queryResponseModel); - } - - private List DeserializedResult(string output) - { - if (string.IsNullOrEmpty(output)) return null; - - JsonRPCQueryResponseModel queryResponseModel = - JsonSerializer.Deserialize(output); - return ParseResults(queryResponseModel); - } - private void ExecuteFlowLauncherAPI(string method, object[] parameters) { MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method); From 9ebebfd0ef9fcaaada22c096adb55f093969e08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:27:02 +0800 Subject: [PATCH 7/8] No need to cancel error reporting --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index e342a5f944c..9caac1a4723 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -213,7 +213,6 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); - token.ThrowIfCancellationRequested(); if (!string.IsNullOrEmpty(error)) { From abd721a06cff43e1490843b8f9b995e064cc1c62 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 16 Feb 2021 06:14:13 +1100 Subject: [PATCH 8/8] fix formatting --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 9caac1a4723..7a088bd091d 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -31,7 +31,6 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); - public List LoadContextMenus(Result selectedResult) { string output = ExecuteContextMenu(selectedResult); @@ -235,8 +234,6 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati } } - - public async Task> QueryAsync(Query query, CancellationToken token) { var output = await ExecuteQueryAsync(query, token); @@ -257,4 +254,4 @@ public Task InitAsync(PluginInitContext context) return Task.CompletedTask; } } -} \ No newline at end of file +}