Flow.Launcher/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs

159 lines
5.1 KiB
C#
Raw Permalink Normal View History

2022-09-01 02:34:47 +00:00
using System;
using System.Collections.Generic;
using System.IO;
2023-07-03 08:19:27 +00:00
using System.IO.Pipelines;
2022-09-01 02:34:47 +00:00
using System.Threading;
using System.Threading.Tasks;
2023-06-02 15:05:09 +00:00
using Flow.Launcher.Core.Plugin.JsonRPCV2Models;
2022-09-01 02:34:47 +00:00
using Flow.Launcher.Plugin;
using Microsoft.VisualStudio.Threading;
2023-06-02 15:05:09 +00:00
using StreamJsonRpc;
using IAsyncDisposable = System.IAsyncDisposable;
2023-06-02 15:05:09 +00:00
2022-09-01 02:34:47 +00:00
namespace Flow.Launcher.Core.Plugin
{
2023-07-03 08:19:27 +00:00
internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated
2022-09-01 02:34:47 +00:00
{
2023-06-02 15:05:09 +00:00
public const string JsonRpc = "JsonRPC";
2022-09-01 02:34:47 +00:00
2025-04-09 11:26:52 +00:00
private static readonly string ClassName = nameof(JsonRPCPluginV2);
2023-07-03 08:19:27 +00:00
protected abstract IDuplexPipe ClientPipe { get; set; }
2022-09-01 02:34:47 +00:00
2023-06-02 15:05:09 +00:00
protected StreamReader ErrorStream { get; set; }
2022-09-01 02:34:47 +00:00
2023-07-03 08:19:27 +00:00
private JsonRpc RPC { get; set; }
2023-06-02 15:05:09 +00:00
protected override async Task<bool> ExecuteResultAsync(JsonRPCResult result)
2022-09-01 02:34:47 +00:00
{
var res = await RPC.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method,
argument: result.JsonRPCAction.Parameters);
2022-09-01 02:34:47 +00:00
return res.Hide;
2022-09-01 02:34:47 +00:00
}
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
public override List<Result> LoadContextMenus(Result selectedResult)
{
var res = JTF.Run(() => RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("context_menu",
new object[] { selectedResult.ContextData }));
var results = ParseResults(res);
return results;
}
2023-06-02 15:05:09 +00:00
public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
2022-09-01 02:34:47 +00:00
{
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
new object[] { query, Settings.Inner },
token);
2022-09-01 02:34:47 +00:00
var results = ParseResults(res);
2023-06-02 15:05:09 +00:00
return results;
2022-09-01 02:34:47 +00:00
}
2023-06-02 15:05:09 +00:00
2023-03-26 19:04:06 +00:00
public override async Task InitAsync(PluginInitContext context)
2022-09-01 02:34:47 +00:00
{
2023-03-26 19:04:06 +00:00
await base.InitAsync(context);
2023-06-02 15:05:09 +00:00
2023-07-03 08:19:27 +00:00
SetupJsonRPC();
2023-06-02 15:05:09 +00:00
_ = ReadErrorAsync();
2023-06-25 04:43:47 +00:00
await RPC.InvokeAsync("initialize", context);
2023-06-02 15:05:09 +00:00
async Task ReadErrorAsync()
{
var error = await ErrorStream.ReadToEndAsync();
if (!string.IsNullOrEmpty(error))
{
throw new Exception(error);
}
}
2022-09-01 02:34:47 +00:00
}
2023-06-24 16:43:21 +00:00
2023-07-03 08:19:27 +00:00
public event ResultUpdatedEventHandler ResultsUpdated;
protected enum MessageHandlerType
{
HeaderDelimited,
LengthHeaderDelimited,
NewLineDelimited
}
protected abstract MessageHandlerType MessageHandler { get; }
2023-07-03 08:19:27 +00:00
private void SetupJsonRPC()
{
var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption };
IJsonRpcMessageHandler handler = MessageHandler switch
{
MessageHandlerType.HeaderDelimited => new HeaderDelimitedMessageHandler(ClientPipe, formatter),
MessageHandlerType.LengthHeaderDelimited => new LengthHeaderMessageHandler(ClientPipe, formatter),
MessageHandlerType.NewLineDelimited => new NewLineDelimitedMessageHandler(ClientPipe, formatter),
_ => throw new ArgumentOutOfRangeException()
};
2023-07-03 08:19:27 +00:00
RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API));
RPC.AddLocalRpcMethod("UpdateResults", new Action<string, JsonRPCQueryResponseModel>((trimmedQuery, response) =>
2023-07-03 08:19:27 +00:00
{
var results = ParseResults(response);
ResultsUpdated?.Invoke(this,
new ResultUpdatedEventArgs { Query = new Query() { TrimmedQuery = trimmedQuery }, Results = results });
2023-07-03 08:19:27 +00:00
}));
RPC.SynchronizationContext = null;
RPC.StartListening();
}
public virtual async Task ReloadDataAsync()
2023-07-03 08:19:27 +00:00
{
try
{
await RPC.InvokeAsync("reload_data", Context);
}
catch (RemoteMethodNotFoundException)
{
2025-04-09 11:43:22 +00:00
// Ignored
}
catch (ConnectionLostException)
{
2025-04-09 11:43:22 +00:00
// Ignored
}
catch (Exception e)
{
2025-04-09 11:26:52 +00:00
Context.API.LogException(ClassName, $"Failed to call reload_data for plugin {Context.CurrentPluginMetadata.Name}", e);
}
2023-07-03 08:19:27 +00:00
}
public virtual async ValueTask DisposeAsync()
2023-06-24 16:43:21 +00:00
{
2024-05-17 03:24:24 +00:00
try
{
await RPC.InvokeAsync("close");
}
catch (RemoteMethodNotFoundException)
{
2025-04-09 11:43:22 +00:00
// Ignored
}
catch (ConnectionLostException)
{
2025-04-09 11:43:22 +00:00
// Ignored
}
catch (Exception e)
2024-05-17 03:24:24 +00:00
{
2025-04-09 11:26:52 +00:00
Context.API.LogException(ClassName, $"Failed to call close for plugin {Context.CurrentPluginMetadata.Name}", e);
2024-05-17 03:24:24 +00:00
}
2024-05-27 06:15:00 +00:00
finally
{
RPC?.Dispose();
ErrorStream?.Dispose();
}
2023-06-24 16:43:21 +00:00
}
2022-09-01 02:34:47 +00:00
}
}