Merge pull request #2578 from Flow-Launcher/node-plugin-processinfo

fix node plugin, job object association, v2 context menu
This commit is contained in:
Jeremy Wu 2024-03-27 13:59:23 +11:00 committed by GitHub
commit 085de915d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 68 additions and 48 deletions

View file

@ -12,15 +12,9 @@ namespace Flow.Launcher.Core.Plugin
public ExecutablePluginV2(string filename) public ExecutablePluginV2(string filename)
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo { FileName = filename };
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
} }
protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited;
} }
} }

View file

@ -15,8 +15,6 @@ namespace Flow.Launcher.Core.Plugin
{ {
internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated
{ {
public abstract string SupportedLanguage { get; set; }
public const string JsonRpc = "JsonRPC"; public const string JsonRpc = "JsonRPC";
protected abstract IDuplexPipe ClientPipe { get; set; } protected abstract IDuplexPipe ClientPipe { get; set; }
@ -41,9 +39,23 @@ namespace Flow.Launcher.Core.Plugin
} }
} }
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
public override List<Result> LoadContextMenus(Result selectedResult) public override List<Result> LoadContextMenus(Result selectedResult)
{ {
throw new NotImplementedException(); try
{
var res = JTF.Run(() => RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("context_menu",
new object[] { selectedResult.ContextData }));
var results = ParseResults(res);
return results;
}
catch
{
return new List<Result>();
}
} }
public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token) public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
@ -51,7 +63,7 @@ namespace Flow.Launcher.Core.Plugin
try try
{ {
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query", var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
new[] { query }, new object[] { query, Settings.Inner },
token); token);
var results = ParseResults(res); var results = ParseResults(res);
@ -88,12 +100,26 @@ namespace Flow.Launcher.Core.Plugin
public event ResultUpdatedEventHandler ResultsUpdated; public event ResultUpdatedEventHandler ResultsUpdated;
protected enum MessageHandlerType
{
HeaderDelimited,
LengthHeaderDelimited,
NewLineDelimited
}
protected abstract MessageHandlerType MessageHandler { get; }
private void SetupJsonRPC() private void SetupJsonRPC()
{ {
var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption }; var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption };
var handler = new NewLineDelimitedMessageHandler(ClientPipe, IJsonRpcMessageHandler handler = MessageHandler switch
formatter); {
MessageHandlerType.HeaderDelimited => new HeaderDelimitedMessageHandler(ClientPipe, formatter),
MessageHandlerType.LengthHeaderDelimited => new LengthHeaderMessageHandler(ClientPipe, formatter),
MessageHandlerType.NewLineDelimited => new NewLineDelimitedMessageHandler(ClientPipe, formatter),
_ => throw new ArgumentOutOfRangeException()
};
RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API)); RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API));

View file

@ -10,29 +10,21 @@ namespace Flow.Launcher.Core.Plugin
/// <summary> /// <summary>
/// Execution of JavaScript & TypeScript plugins /// Execution of JavaScript & TypeScript plugins
/// </summary> /// </summary>
internal class NodePluginV2 : ProcessStreamPluginV2 internal sealed class NodePluginV2 : ProcessStreamPluginV2
{ {
public NodePluginV2(string filename) public NodePluginV2(string filename)
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo { FileName = filename, };
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
} }
public override string SupportedLanguage { get; set; }
protected override ProcessStartInfo StartInfo { get; set; } protected override ProcessStartInfo StartInfo { get; set; }
public override async Task InitAsync(PluginInitContext context) public override async Task InitAsync(PluginInitContext context)
{ {
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
StartInfo.ArgumentList.Add(string.Empty);
StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
await base.InitAsync(context); await base.InitAsync(context);
} }
protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.HeaderDelimited;
} }
} }

View file

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO.Pipelines; using System.IO.Pipelines;
@ -6,6 +8,7 @@ using System.Threading.Tasks;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin; using Flow.Launcher.Plugin;
using Meziantou.Framework.Win32; using Meziantou.Framework.Win32;
using Microsoft.VisualBasic.ApplicationServices;
using Nerdbank.Streams; using Nerdbank.Streams;
namespace Flow.Launcher.Core.Plugin namespace Flow.Launcher.Core.Plugin
@ -18,18 +21,18 @@ namespace Flow.Launcher.Core.Plugin
{ {
_jobObject.SetLimits(new JobObjectLimits() _jobObject.SetLimits(new JobObjectLimits()
{ {
Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException |
JobObjectLimitFlags.SilentBreakawayOk
}); });
_jobObject.AssignProcess(Process.GetCurrentProcess()); _jobObject.AssignProcess(Process.GetCurrentProcess());
} }
public override string SupportedLanguage { get; set; } protected sealed override IDuplexPipe ClientPipe { get; set; } = null!;
protected sealed override IDuplexPipe ClientPipe { get; set; }
protected abstract ProcessStartInfo StartInfo { get; set; } protected abstract ProcessStartInfo StartInfo { get; set; }
protected Process ClientProcess { get; set; } protected Process ClientProcess { get; set; } = null!;
public override async Task InitAsync(PluginInitContext context) public override async Task InitAsync(PluginInitContext context)
{ {
@ -37,12 +40,18 @@ namespace Flow.Launcher.Core.Plugin
StartInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory; StartInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory;
StartInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory; StartInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory;
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); StartInfo.RedirectStandardError = true;
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.CreateNoWindow = true;
StartInfo.UseShellExecute = false;
StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
ClientProcess = Process.Start(StartInfo); var process = Process.Start(StartInfo);
ArgumentNullException.ThrowIfNull(ClientProcess); ArgumentNullException.ThrowIfNull(process);
ClientProcess = process;
_jobObject.AssignProcess(ClientProcess);
SetupPipe(ClientProcess); SetupPipe(ClientProcess);
ErrorStream = ClientProcess.StandardError; ErrorStream = ClientProcess.StandardError;

View file

@ -18,20 +18,11 @@ namespace Flow.Launcher.Core.Plugin
{ {
internal sealed class PythonPluginV2 : ProcessStreamPluginV2 internal sealed class PythonPluginV2 : ProcessStreamPluginV2
{ {
public override string SupportedLanguage { get; set; } = AllowedLanguage.Python;
protected override ProcessStartInfo StartInfo { get; set; } protected override ProcessStartInfo StartInfo { get; set; }
public PythonPluginV2(string filename) public PythonPluginV2(string filename)
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo { FileName = filename, };
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true
};
var path = Path.Combine(Constant.ProgramDirectory, JsonRpc); var path = Path.Combine(Constant.ProgramDirectory, JsonRpc);
StartInfo.EnvironmentVariables["PYTHONPATH"] = path; StartInfo.EnvironmentVariables["PYTHONPATH"] = path;
@ -39,5 +30,13 @@ namespace Flow.Launcher.Core.Plugin
//Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable //Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable
StartInfo.ArgumentList.Add("-B"); StartInfo.ArgumentList.Add("-B");
} }
public override async Task InitAsync(PluginInitContext context)
{
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
await base.InitAsync(context);
}
protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited;
} }
} }

View file

@ -196,7 +196,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
FileName = FullPath, FileName = FullPath,
WorkingDirectory = ParentDirectory, WorkingDirectory = ParentDirectory,
UseShellExecute = true, UseShellExecute = true,
Verb = runAsAdmin ? "runas" : "" Verb = runAsAdmin ? "runas" : "",
}; };
_ = Task.Run(() => Main.StartProcess(Process.Start, info)); _ = Task.Run(() => Main.StartProcess(Process.Start, info));