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;
|
2023-07-03 08:44:50 +00:00
|
|
|
|
using Microsoft.VisualStudio.Threading;
|
2023-06-02 15:05:09 +00:00
|
|
|
|
using StreamJsonRpc;
|
2023-07-03 08:44:50 +00:00
|
|
|
|
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
|
|
|
|
{
|
2025-01-20 15:14:28 +00:00
|
|
|
|
var res = await RPC.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method,
|
|
|
|
|
|
argument: result.JsonRPCAction.Parameters);
|
2022-09-01 02:34:47 +00:00
|
|
|
|
|
2025-01-20 15:14:28 +00:00
|
|
|
|
return res.Hide;
|
2022-09-01 02:34:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-19 17:10:05 +00:00
|
|
|
|
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
|
|
|
|
|
|
|
2023-07-03 08:44:50 +00:00
|
|
|
|
public override List<Result> LoadContextMenus(Result selectedResult)
|
|
|
|
|
|
{
|
2025-01-20 15:14:28 +00:00
|
|
|
|
var res = JTF.Run(() => RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("context_menu",
|
|
|
|
|
|
new object[] { selectedResult.ContextData }));
|
2024-02-19 17:10:05 +00:00
|
|
|
|
|
2025-01-20 15:14:28 +00:00
|
|
|
|
var results = ParseResults(res);
|
2024-02-19 17:10:05 +00:00
|
|
|
|
|
2025-01-20 15:14:28 +00:00
|
|
|
|
return results;
|
2023-07-03 08:44:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2025-01-20 15:14:28 +00:00
|
|
|
|
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
|
|
|
|
|
|
new object[] { query, Settings.Inner },
|
|
|
|
|
|
token);
|
2022-09-01 02:34:47 +00:00
|
|
|
|
|
2025-01-20 15:14:28 +00:00
|
|
|
|
var results = ParseResults(res);
|
2023-06-02 15:05:09 +00:00
|
|
|
|
|
2025-01-20 15:14:28 +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;
|
|
|
|
|
|
|
2024-02-19 05:46:24 +00:00
|
|
|
|
protected enum MessageHandlerType
|
|
|
|
|
|
{
|
|
|
|
|
|
HeaderDelimited,
|
|
|
|
|
|
LengthHeaderDelimited,
|
|
|
|
|
|
NewLineDelimited
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected abstract MessageHandlerType MessageHandler { get; }
|
|
|
|
|
|
|
2023-07-03 08:19:27 +00:00
|
|
|
|
private void SetupJsonRPC()
|
|
|
|
|
|
{
|
2023-12-10 08:26:43 +00:00
|
|
|
|
var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption };
|
2024-02-19 05:46:24 +00:00
|
|
|
|
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));
|
|
|
|
|
|
|
2025-11-26 10:15:12 +00:00
|
|
|
|
RPC.AddLocalRpcMethod("UpdateResults", new Action<string, JsonRPCQueryResponseModel>((trimmedQuery, response) =>
|
2023-07-03 08:19:27 +00:00
|
|
|
|
{
|
|
|
|
|
|
var results = ParseResults(response);
|
2023-12-10 08:26:43 +00:00
|
|
|
|
ResultsUpdated?.Invoke(this,
|
2025-11-26 10:15:12 +00:00
|
|
|
|
new ResultUpdatedEventArgs { Query = new Query() { TrimmedQuery = trimmedQuery }, Results = results });
|
2023-07-03 08:19:27 +00:00
|
|
|
|
}));
|
|
|
|
|
|
RPC.SynchronizationContext = null;
|
|
|
|
|
|
RPC.StartListening();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-18 05:59:43 +00:00
|
|
|
|
public virtual async Task ReloadDataAsync()
|
2023-07-03 08:19:27 +00:00
|
|
|
|
{
|
2024-11-18 05:59:43 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-02-19 08:03:46 +00:00
|
|
|
|
await RPC.InvokeAsync("reload_data", Context);
|
2024-11-18 05:59:43 +00:00
|
|
|
|
}
|
2025-04-09 10:09:54 +00:00
|
|
|
|
catch (RemoteMethodNotFoundException)
|
|
|
|
|
|
{
|
2025-04-09 11:43:22 +00:00
|
|
|
|
// Ignored
|
2025-04-09 10:09:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (ConnectionLostException)
|
2024-11-18 05:59:43 +00:00
|
|
|
|
{
|
2025-04-09 11:43:22 +00:00
|
|
|
|
// Ignored
|
2024-11-18 05:59:43 +00:00
|
|
|
|
}
|
2025-04-09 10:09:54 +00:00
|
|
|
|
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);
|
2025-04-09 10:09:54 +00:00
|
|
|
|
}
|
2023-07-03 08:19:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-17 03:21:35 +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");
|
|
|
|
|
|
}
|
2025-04-09 10:09:54 +00:00
|
|
|
|
catch (RemoteMethodNotFoundException)
|
|
|
|
|
|
{
|
2025-04-09 11:43:22 +00:00
|
|
|
|
// Ignored
|
2025-04-09 10:09:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (ConnectionLostException)
|
|
|
|
|
|
{
|
2025-04-09 11:43:22 +00:00
|
|
|
|
// Ignored
|
2025-04-09 10:09:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|