mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
implement v2
This commit is contained in:
parent
1551567269
commit
e183920b8e
11 changed files with 109 additions and 86 deletions
|
|
@ -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) { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
||||
{
|
||||
public record JsonRPCExecuteResponse(bool Hide = true);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
||||
{
|
||||
public record JsonRPCQueryRequest(
|
||||
List<JsonRPCResult> Results
|
||||
);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue