Merge branch 'dev' into l10n_dev

This commit is contained in:
Jeremy Wu 2024-03-27 22:19:34 +11:00 committed by GitHub
commit a0f30ea639
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 113 additions and 66 deletions

View file

@ -12,15 +12,9 @@ namespace Flow.Launcher.Core.Plugin
public ExecutablePluginV2(string filename)
{
StartInfo = new ProcessStartInfo
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
StartInfo = new ProcessStartInfo { FileName = filename };
}
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
{
public abstract string SupportedLanguage { get; set; }
public const string JsonRpc = "JsonRPC";
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)
{
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)
@ -51,7 +63,7 @@ namespace Flow.Launcher.Core.Plugin
try
{
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
new[] { query },
new object[] { query, Settings.Inner },
token);
var results = ParseResults(res);
@ -88,12 +100,26 @@ namespace Flow.Launcher.Core.Plugin
public event ResultUpdatedEventHandler ResultsUpdated;
protected enum MessageHandlerType
{
HeaderDelimited,
LengthHeaderDelimited,
NewLineDelimited
}
protected abstract MessageHandlerType MessageHandler { get; }
private void SetupJsonRPC()
{
var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption };
var handler = new NewLineDelimitedMessageHandler(ClientPipe,
formatter);
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()
};
RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API));

View file

@ -10,29 +10,21 @@ namespace Flow.Launcher.Core.Plugin
/// <summary>
/// Execution of JavaScript & TypeScript plugins
/// </summary>
internal class NodePluginV2 : ProcessStreamPluginV2
internal sealed class NodePluginV2 : ProcessStreamPluginV2
{
public NodePluginV2(string filename)
{
StartInfo = new ProcessStartInfo
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
StartInfo = new ProcessStartInfo { FileName = filename, };
}
public override string SupportedLanguage { get; set; }
protected override ProcessStartInfo StartInfo { get; set; }
public override async Task InitAsync(PluginInitContext context)
{
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
StartInfo.ArgumentList.Add(string.Empty);
StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
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.Diagnostics;
using System.IO.Pipelines;
@ -6,6 +8,7 @@ using System.Threading.Tasks;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin;
using Meziantou.Framework.Win32;
using Microsoft.VisualBasic.ApplicationServices;
using Nerdbank.Streams;
namespace Flow.Launcher.Core.Plugin
@ -18,18 +21,18 @@ namespace Flow.Launcher.Core.Plugin
{
_jobObject.SetLimits(new JobObjectLimits()
{
Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException
Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException |
JobObjectLimitFlags.SilentBreakawayOk
});
_jobObject.AssignProcess(Process.GetCurrentProcess());
}
public override string SupportedLanguage { get; set; }
protected sealed override IDuplexPipe ClientPipe { get; set; }
protected sealed override IDuplexPipe ClientPipe { get; set; } = null!;
protected abstract ProcessStartInfo StartInfo { get; set; }
protected Process ClientProcess { get; set; }
protected Process ClientProcess { get; set; } = null!;
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_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;
ClientProcess = Process.Start(StartInfo);
ArgumentNullException.ThrowIfNull(ClientProcess);
var process = Process.Start(StartInfo);
ArgumentNullException.ThrowIfNull(process);
ClientProcess = process;
_jobObject.AssignProcess(ClientProcess);
SetupPipe(ClientProcess);
ErrorStream = ClientProcess.StandardError;

View file

@ -18,20 +18,11 @@ namespace Flow.Launcher.Core.Plugin
{
internal sealed class PythonPluginV2 : ProcessStreamPluginV2
{
public override string SupportedLanguage { get; set; } = AllowedLanguage.Python;
protected override ProcessStartInfo StartInfo { get; set; }
public PythonPluginV2(string filename)
{
StartInfo = new ProcessStartInfo
{
FileName = filename,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true
};
StartInfo = new ProcessStartInfo { FileName = filename, };
var path = Path.Combine(Constant.ProgramDirectory, JsonRpc);
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
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

@ -186,6 +186,10 @@
Key="F12"
Command="{Binding ToggleGameModeCommand}"
Modifiers="Ctrl" />
<KeyBinding
Key="C"
Modifiers="Ctrl+Shift"
Command="{Binding CopyAlternativeCommand}" />
<KeyBinding
Key="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
Command="{Binding TogglePreviewCommand}"

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
@ -137,6 +137,8 @@ namespace Flow.Launcher
isDragging = false;
App.API.HideMainWindow();
var data = new DataObject(DataFormats.FileDrop, new[]
{
path

View file

@ -176,7 +176,8 @@ namespace Flow.Launcher.ViewModel
var token = e.Token == default ? _updateToken : e.Token;
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token)))
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query,
token)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
}
@ -190,7 +191,8 @@ namespace Flow.Launcher.ViewModel
Hide();
await PluginManager.ReloadDataAsync().ConfigureAwait(false);
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
}
[RelayCommand]
@ -265,6 +267,7 @@ namespace Flow.Launcher.ViewModel
{
autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}";
}
autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText;
}
@ -286,11 +289,13 @@ namespace Flow.Launcher.ViewModel
{
results.SelectedIndex = int.Parse(index);
}
var result = results.SelectedItem?.Result;
if (result == null)
{
return;
}
var hideWindow = await result.ExecuteAsync(new ActionContext
{
// not null means pressing modifier key + number, should ignore the modifier key
@ -371,6 +376,17 @@ namespace Flow.Launcher.ViewModel
{
GameModeStatus = !GameModeStatus;
}
[RelayCommand]
public void CopyAlternative()
{
var result = Results.SelectedItem?.Result?.CopyText;
if (result != null)
{
App.API.CopyToClipboard(result, directCopy: false);
}
}
#endregion
@ -403,6 +419,7 @@ namespace Flow.Launcher.ViewModel
public bool GameModeStatus { get; set; } = false;
private string _queryText;
public string QueryText
{
get => _queryText;
@ -426,6 +443,7 @@ namespace Flow.Launcher.ViewModel
Settings.WindowSize += 100;
Settings.WindowLeft -= 50;
}
OnPropertyChanged();
}
@ -441,6 +459,7 @@ namespace Flow.Launcher.ViewModel
Settings.WindowLeft += 50;
Settings.WindowSize -= 100;
}
OnPropertyChanged();
}
@ -520,18 +539,17 @@ namespace Flow.Launcher.ViewModel
{
if (QueryText != queryText)
{
// re-query is done in QueryText's setter method
QueryText = queryText;
// set to false so the subsequent set true triggers
// PropertyChanged and MoveQueryTextToEnd is called
QueryTextCursorMovedToEnd = false;
}
else if (isReQuery)
{
Query(isReQuery: true);
}
QueryTextCursorMovedToEnd = true;
});
}
@ -601,8 +619,8 @@ namespace Flow.Launcher.ViewModel
public string OpenResultCommandModifiers => Settings.OpenResultModifiers;
public string PreviewHotkey
{
public string PreviewHotkey
{
get
{
// TODO try to patch issue #1755
@ -616,6 +634,7 @@ namespace Flow.Launcher.ViewModel
{
Settings.PreviewHotkey = "F1";
}
return Settings.PreviewHotkey;
}
}
@ -684,7 +703,6 @@ namespace Flow.Launcher.ViewModel
results.Add(ContextMenuTopMost(selected));
results.Add(ContextMenuPluginInfo(selected.PluginID));
}
if (!string.IsNullOrEmpty(query))
@ -703,7 +721,6 @@ namespace Flow.Launcher.ViewModel
r.Score = match.Score;
return true;
}).ToList();
ContextMenu.AddResults(filtered, id);
}
@ -730,10 +747,7 @@ namespace Flow.Launcher.ViewModel
Title = string.Format(title, h.Query),
SubTitle = string.Format(time, h.ExecutedDateTime),
IcoPath = "Images\\history.png",
OriginQuery = new Query
{
RawQuery = h.Query
},
OriginQuery = new Query { RawQuery = h.Query },
Action = _ =>
{
SelectedResults = Results;
@ -870,20 +884,23 @@ namespace Flow.Launcher.ViewModel
// Task.Yield will force it to run in ThreadPool
await Task.Yield();
IReadOnlyList<Result> results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
IReadOnlyList<Result> results =
await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
currentCancellationToken.ThrowIfCancellationRequested();
results ??= _emptyResult;
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken)))
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query,
currentCancellationToken)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
}
}
}
private Query ConstructQuery(string queryText, IEnumerable<CustomShortcutModel> customShortcuts, IEnumerable<BuiltinShortcutModel> builtInShortcuts)
private Query ConstructQuery(string queryText, IEnumerable<CustomShortcutModel> customShortcuts,
IEnumerable<BuiltinShortcutModel> builtInShortcuts)
{
if (string.IsNullOrWhiteSpace(queryText))
{
@ -893,7 +910,8 @@ namespace Flow.Launcher.ViewModel
StringBuilder queryBuilder = new(queryText);
StringBuilder queryBuilderTmp = new(queryText);
foreach (var shortcut in customShortcuts)
// Sorting order is important here, the reason is for matching longest shortcut by default
foreach (var shortcut in customShortcuts.OrderByDescending(x => x.Key.Length))
{
if (queryBuilder.Equals(shortcut.Key))
{
@ -920,7 +938,9 @@ namespace Flow.Launcher.ViewModel
}
catch (Exception e)
{
Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e);
Log.Exception(
$"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}",
e);
}
}
});
@ -1065,6 +1085,7 @@ namespace Flow.Launcher.ViewModel
{
SelectedResults = Results;
}
switch (Settings.LastQueryMode)
{
case LastQueryMode.Empty:

View file

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