implement v2

This commit is contained in:
Hongtao Zhang 2023-06-02 23:05:09 +08:00
parent 1551567269
commit e183920b8e
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
11 changed files with 109 additions and 86 deletions

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@ -8,6 +9,15 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
internal override string Language => AllowedLanguage.PythonV2;
internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata)
{
return new PluginPair
{
Plugin = new PythonPluginV2(filePath),
Metadata = metadata
};
}
internal PythonV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
}
}

View file

@ -57,11 +57,16 @@
<PackageReference Include="FSharp.Core" Version="7.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.1" />
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
<PackageReference Include="StreamJsonRpc" Version="2.15.29" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Helper" />
</ItemGroup>
</Project>

View file

@ -28,7 +28,7 @@ namespace Flow.Launcher.Core.Plugin
public record JsonRPCResponseModel(int Id, JsonRPCErrorModel Error = default) : JsonRPCBase(Id, Error);
public record JsonRPCQueryResponseModel(int Id,
[property: JsonPropertyName("result")] List<JsonRPCResult> Result,
IReadOnlyDictionary<string, object> SettingsChange = null,
IReadOnlyDictionary<string, object> SettingsChanges = null,
string DebugMessage = "",
JsonRPCErrorModel Error = default) : JsonRPCResponseModel(Id, Error);

View file

@ -251,10 +251,18 @@ namespace Flow.Launcher.Core.Plugin
return sourceBuffer;
}
protected override async Task<List<Result>> QueryRequestAsync(JsonRPCRequestModel request, CancellationToken token)
public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
var request = new JsonRPCRequestModel(RequestId++,
"query",
new object[]
{
query.Search
},
Settings.Inner);
var output = await RequestAsync(request, token);
return await DeserializedResultAsync(output);
}
}

View file

@ -69,7 +69,6 @@ namespace Flow.Launcher.Core.Plugin
};
protected abstract Task<bool> ExecuteResultAsync(JsonRPCResult result);
protected abstract Task<List<Result>> QueryRequestAsync(JsonRPCRequestModel request, CancellationToken token);
protected PortableSettings Settings { get; set; }
@ -96,7 +95,7 @@ namespace Flow.Launcher.Core.Plugin
results.AddRange(queryResponseModel.Result);
Settings.UpdateSettings(queryResponseModel.SettingsChange);
Settings.UpdateSettings(queryResponseModel.SettingsChanges);
return results;
}
@ -123,19 +122,7 @@ namespace Flow.Launcher.Core.Plugin
}
}
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
var request = new JsonRPCRequestModel(RequestId++,
"query",
new object[]
{
query.Search
},
Settings.Inner);
return await QueryRequestAsync(request, token);
}
public abstract Task<List<Result>> QueryAsync(Query query, CancellationToken token);
private async Task InitSettingAsync()

View file

@ -7,77 +7,70 @@ using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows.Controls;
using Flow.Launcher.Core.Plugin.JsonRPCV2Models;
using Flow.Launcher.Plugin;
using StreamJsonRpc;
namespace Flow.Launcher.Core.Plugin
{
internal abstract class JsonRpcPluginV2 : JsonRPCPluginBase
{
public abstract string SupportedLanguage { get; set; }
public const string JsonRpc = "JsonRPC";
protected abstract Stream InputStream { get; set; }
protected abstract Stream OutputStream { get; set; }
protected abstract StreamReader ErrorStream { get; set; }
protected Channel<JsonRPCRequestModel> InputMessageChannel { get; set; }
protected abstract JsonRpc Rpc { get; set; }
private (Task SendTask, Task ReceiveTask) MessageTask { get; set; }
private CancellationTokenSource MessageCancellationTokenSource { get; set; }
protected StreamReader ErrorStream { get; set; }
protected int RequestId;
private ConcurrentDictionary<int, TaskCompletionSource<JsonRPCQueryResponseModel>> RequestTaskDictionary { get; } = new();
// TODO: Switch to Async Task
private async void ReceiveMessageAsync(CancellationToken token)
protected override async Task<bool> ExecuteResultAsync(JsonRPCResult result)
{
var response =
JsonSerializer.DeserializeAsyncEnumerable<JsonRPCQueryResponseModel>(OutputStream, cancellationToken: token);
ArgumentNullException.ThrowIfNull(response);
await foreach (var message in response.WithCancellation(token))
try
{
if (!RequestTaskDictionary.TryGetValue(message.Id, out var task))
{
// Either Task is already handled or it is a invalid resopnse.
continue;
}
RequestTaskDictionary.Remove(message.Id, out _);
task.TrySetResult(message);
var res = await Rpc.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method, argument: result.JsonRPCAction.Parameters);
return res.Hide;
}
catch
{
return false;
}
}
// TODO: Switch to Async Task
private async void SendMessageAsync(PluginMetadata metadata, CancellationToken token)
public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
var fullMessage = new JsonRPCRequestMessage(metadata, InputMessageChannel.Reader.ReadAllAsync(token));
await JsonSerializer.SerializeAsync(InputStream, fullMessage, cancellationToken: token);
try
{
var res = await Rpc.InvokeAsync<JsonRPCQueryResponseModel>("query", query);
var results = ParseResults(res);
return results;
}
catch
{
return new List<Result>();
}
}
protected override async Task<List<Result>> QueryRequestAsync(JsonRPCRequestModel query, CancellationToken token)
{
int currentRequestId = Interlocked.Add(ref RequestId, 1);
await InputMessageChannel.Writer.WriteAsync(query, token);
await Task.Delay(50, token);
await InputStream.FlushAsync(token);
var task = new TaskCompletionSource<JsonRPCQueryResponseModel>();
RequestTaskDictionary[currentRequestId] = task;
var result = await task.Task;
//TODO: Parse Result
return new List<Result>();
}
public override async Task InitAsync(PluginInitContext context)
{
await base.InitAsync(context);
InputMessageChannel = Channel.CreateUnbounded<JsonRPCRequestModel>();
MessageCancellationTokenSource = new CancellationTokenSource();
SendMessageAsync(context.CurrentPluginMetadata, MessageCancellationTokenSource.Token);
ReceiveMessageAsync(MessageCancellationTokenSource.Token);
// MessageTask =
// (SendMessageAsync(context.CurrentPluginMetadata, MessageCancellationTokenSource.Token),
// ReceiveMessageAsync(MessageCancellationTokenSource.Token));
_ = ReadErrorAsync();
async Task ReadErrorAsync()
{
var error = await ErrorStream.ReadToEndAsync();
if (!string.IsNullOrEmpty(error))
{
throw new Exception(error);
}
}
}
}
}

View file

@ -0,0 +1,4 @@
namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
{
public record JsonRPCExecuteResponse(bool Hide = true);
}

View file

@ -0,0 +1,9 @@
using System.Collections.Generic;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
{
public record JsonRPCQueryRequest(
List<JsonRPCResult> Results
);
}

View file

@ -2,10 +2,14 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin;
using Microsoft.VisualStudio.Threading;
using StreamJsonRpc;
namespace Flow.Launcher.Core.Plugin
{
@ -13,11 +17,11 @@ namespace Flow.Launcher.Core.Plugin
{
private readonly ProcessStartInfo _startInfo;
private Process _process;
public override string SupportedLanguage { get; set; } = AllowedLanguage.Python;
protected override Stream InputStream { get; set; }
protected override Stream OutputStream { get; set; }
protected override StreamReader ErrorStream { get; set; }
protected override JsonRpc Rpc { get; set; }
public PythonPluginV2(string filename)
{
@ -49,23 +53,26 @@ namespace Flow.Launcher.Core.Plugin
{
throw new NotImplementedException();
}
protected override Task<bool> ExecuteResultAsync(JsonRPCResult result)
{
throw new NotImplementedException();
}
public override async Task InitAsync(PluginInitContext context)
{
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
_process = Process.Start(_startInfo);
ArgumentNullException.ThrowIfNull(_process);
InputStream = _process.StandardInput.BaseStream;
OutputStream = _process.StandardOutput.BaseStream;
ErrorStream = _process.StandardError;
var formatter = new JsonMessageFormatter();
var handler = new NewLineDelimitedMessageHandler(_process.StandardInput.BaseStream,
_process.StandardOutput.BaseStream,
formatter);
Rpc = new JsonRpc(handler, context.API);
Rpc.StartListening();
_ = _process.StandardError.ReadToEndAsync().ContinueWith(e => throw new Exception(e.Result));
await base.InitAsync(context);
}
}

View file

@ -90,7 +90,7 @@
</PackageReference>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="ModernWpfUI" Version="0.9.6" />
<PackageReference Include="ModernWpfUI" Version="0.9.5" />
<PackageReference Include="NHotkey.Wpf" Version="2.1.0" />
<PackageReference Include="NuGet.CommandLine" Version="6.3.1">
<PrivateAssets>all</PrivateAssets>

View file

@ -31,12 +31,12 @@ namespace Flow.Launcher
var Icon = !File.Exists(iconPath)
? Path.Combine(Constant.ProgramDirectory, "Images\\app.png")
: iconPath;
new ToastContentBuilder()
.AddText(title, hintMaxLines: 1)
.AddText(subTitle)
.AddAppLogoOverride(new Uri(Icon))
.Show();
// new ToastContentBuilder()
// .AddText(title, hintMaxLines: 1)
// .AddText(subTitle)
// .AddAppLogoOverride(new Uri(Icon))
// .Show();
}
private static void LegacyShow(string title, string subTitle, string iconPath)