Skip to content

Commit 5600880

Browse files
committed
Merge branch 'dev' into search-open-explorer-window-tweaks
2 parents 1606908 + 1d02c30 commit 5600880

File tree

464 files changed

+39281
-7997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

464 files changed

+39281
-7997
lines changed

.github/workflows/stale.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# For more information, see:
2+
# https://github.com/actions/stale
3+
name: Mark stale issues and pull requests
4+
5+
on:
6+
schedule:
7+
- cron: '30 1 * * *'
8+
9+
jobs:
10+
stale:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
issues: write
14+
pull-requests: write
15+
steps:
16+
- uses: actions/stale@v4
17+
with:
18+
stale-issue-message: 'This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
19+
days-before-stale: 45
20+
days-before-close: 7
21+
days-before-pr-close: -1
22+
exempt-all-milestones: true
23+
close-issue-message: 'This issue was closed because it has been stale for 7 days with no activity. If you feel this issue still needs attention please feel free to reopen.'
24+
stale-pr-label: 'no-pr-activity'
25+
exempt-issue-labels: 'keep-fresh'
26+
exempt-pr-labels: 'keep-fresh,awaiting-approval,work-in-progress'

Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Flow.Launcher.Infrastructure.Logger;
33
using System;
44
using System.Collections.Generic;
5+
using System.Net;
6+
using System.Net.Http;
57
using System.Text.Json;
68
using System.Threading;
79
using System.Threading.Tasks;
@@ -10,43 +12,43 @@ namespace Flow.Launcher.Core.ExternalPlugins
1012
{
1113
public static class PluginsManifest
1214
{
13-
static PluginsManifest()
14-
{
15-
UpdateTask = UpdateManifestAsync();
16-
}
17-
18-
public static List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
19-
20-
public static Task UpdateTask { get; private set; }
15+
private const string manifestFileUrl = "https://cdn.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json";
2116

2217
private static readonly SemaphoreSlim manifestUpdateLock = new(1);
2318

24-
public static Task UpdateManifestAsync()
25-
{
26-
if (manifestUpdateLock.CurrentCount == 0)
27-
{
28-
return UpdateTask;
29-
}
19+
private static string latestEtag = "";
3020

31-
return UpdateTask = DownloadManifestAsync();
32-
}
21+
public static List<UserPlugin> UserPlugins { get; private set; } = new List<UserPlugin>();
3322

34-
private async static Task DownloadManifestAsync()
23+
public static async Task UpdateManifestAsync(CancellationToken token = default)
3524
{
3625
try
3726
{
38-
await manifestUpdateLock.WaitAsync().ConfigureAwait(false);
27+
await manifestUpdateLock.WaitAsync(token).ConfigureAwait(false);
28+
29+
var request = new HttpRequestMessage(HttpMethod.Get, manifestFileUrl);
30+
request.Headers.Add("If-None-Match", latestEtag);
31+
32+
var response = await Http.SendAsync(request, token).ConfigureAwait(false);
3933

40-
await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json")
41-
.ConfigureAwait(false);
34+
if (response.StatusCode == HttpStatusCode.OK)
35+
{
36+
Log.Info($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Fetched plugins from manifest repo");
4237

43-
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(jsonStream).ConfigureAwait(false);
38+
var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
39+
40+
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(json, cancellationToken: token).ConfigureAwait(false);
41+
42+
latestEtag = response.Headers.ETag.Tag;
43+
}
44+
else if (response.StatusCode != HttpStatusCode.NotModified)
45+
{
46+
Log.Warn($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Http response for manifest file was {response.StatusCode}");
47+
}
4448
}
4549
catch (Exception e)
4650
{
47-
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
48-
49-
UserPlugins = new List<UserPlugin>();
51+
Log.Exception($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Http request failed", e);
5052
}
5153
finally
5254
{

Flow.Launcher.Core/Flow.Launcher.Core.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net5.0-windows</TargetFramework>
4+
<TargetFramework>net6.0-windows</TargetFramework>
55
<UseWpf>true</UseWpf>
66
<UseWindowsForms>true</UseWindowsForms>
77
<OutputType>Library</OutputType>
@@ -53,7 +53,7 @@
5353
</ItemGroup>
5454

5555
<ItemGroup>
56-
<PackageReference Include="Droplex" Version="1.4.0" />
56+
<PackageReference Include="Droplex" Version="1.4.1" />
5757
<PackageReference Include="FSharp.Core" Version="5.0.2" />
5858
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.1.3" />
5959
<PackageReference Include="squirrel.windows" Version="1.5.2" />

Flow.Launcher.Core/Plugin/ExecutablePlugin.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Diagnostics;
1+
using System.Diagnostics;
32
using System.IO;
43
using System.Threading;
54
using System.Threading.Tasks;
@@ -22,18 +21,23 @@ public ExecutablePlugin(string filename)
2221
RedirectStandardOutput = true,
2322
RedirectStandardError = true
2423
};
24+
25+
// required initialisation for below request calls
26+
_startInfo.ArgumentList.Add(string.Empty);
2527
}
2628

2729
protected override Task<Stream> RequestAsync(JsonRPCRequestModel request, CancellationToken token = default)
2830
{
29-
_startInfo.Arguments = $"\"{request}\"";
31+
// since this is not static, request strings will build up in ArgumentList if index is not specified
32+
_startInfo.ArgumentList[0] = request.ToString();
3033
return ExecuteAsync(_startInfo, token);
3134
}
3235

3336
protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
3437
{
35-
_startInfo.Arguments = $"\"{rpcRequest}\"";
38+
// since this is not static, request strings will build up in ArgumentList if index is not specified
39+
_startInfo.ArgumentList[0] = rpcRequest.ToString();
3640
return Execute(_startInfo);
3741
}
3842
}
39-
}
43+
}

Flow.Launcher.Core/Plugin/JsonPRCModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,4 @@ public class JsonRPCResult : Result
9393

9494
public Dictionary<string, object> SettingsChange { get; set; }
9595
}
96-
}
96+
}

Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
115115

116116
foreach (var result in queryResponseModel.Result)
117117
{
118-
result.Action = c =>
118+
result.AsyncAction = async c =>
119119
{
120120
UpdateSettings(result.SettingsChange);
121121

@@ -133,15 +133,15 @@ private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
133133
}
134134
else
135135
{
136-
var actionResponse = Request(result.JsonRPCAction);
136+
var actionResponse = await RequestAsync(result.JsonRPCAction);
137137

138-
if (string.IsNullOrEmpty(actionResponse))
138+
if (actionResponse.Length == 0)
139139
{
140140
return !result.JsonRPCAction.DontHideAfterAction;
141141
}
142142

143-
var jsonRpcRequestModel =
144-
JsonSerializer.Deserialize<JsonRPCRequestModel>(actionResponse, options);
143+
var jsonRpcRequestModel = await
144+
JsonSerializer.DeserializeAsync<JsonRPCRequestModel>(actionResponse, options);
145145

146146
if (jsonRpcRequestModel?.Method?.StartsWith("Flow.Launcher.") ?? false)
147147
{
@@ -166,19 +166,20 @@ private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
166166
private void ExecuteFlowLauncherAPI(string method, object[] parameters)
167167
{
168168
var parametersTypeArray = parameters.Select(param => param.GetType()).ToArray();
169-
MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method, parametersTypeArray);
170-
if (methodInfo != null)
169+
var methodInfo = typeof(IPublicAPI).GetMethod(method, parametersTypeArray);
170+
if (methodInfo == null)
171+
{
172+
return;
173+
}
174+
try
175+
{
176+
methodInfo.Invoke(PluginManager.API, parameters);
177+
}
178+
catch (Exception)
171179
{
172-
try
173-
{
174-
methodInfo.Invoke(PluginManager.API, parameters);
175-
}
176-
catch (Exception)
177-
{
178180
#if (DEBUG)
179-
throw;
181+
throw;
180182
#endif
181-
}
182183
}
183184
}
184185

@@ -241,7 +242,7 @@ protected string Execute(ProcessStartInfo startInfo)
241242
protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default)
242243
{
243244
Process process = null;
244-
bool disposed = false;
245+
using var exitTokenSource = new CancellationTokenSource();
245246
try
246247
{
247248
process = Process.Start(startInfo);
@@ -251,6 +252,7 @@ protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, Cancellati
251252
return Stream.Null;
252253
}
253254

255+
254256
await using var source = process.StandardOutput.BaseStream;
255257

256258
var buffer = BufferManager.GetStream();
@@ -259,7 +261,7 @@ protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, Cancellati
259261
{
260262
// ReSharper disable once AccessToModifiedClosure
261263
// Manually Check whether disposed
262-
if (!disposed && !process.HasExited)
264+
if (!exitTokenSource.IsCancellationRequested && !process.HasExited)
263265
process.Kill();
264266
});
265267

@@ -302,8 +304,8 @@ protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, Cancellati
302304
}
303305
finally
304306
{
307+
exitTokenSource.Cancel();
305308
process?.Dispose();
306-
disposed = true;
307309
}
308310
}
309311

@@ -354,7 +356,9 @@ public virtual async Task InitAsync(PluginInitContext context)
354356
this.context = context;
355357
await InitSettingAsync();
356358
}
357-
private static readonly Thickness settingControlMargin = new(10);
359+
private static readonly Thickness settingControlMargin = new(10, 4, 10, 4);
360+
private static readonly Thickness settingPanelMargin = new(15, 20, 15, 20);
361+
private static readonly Thickness settingTextBlockMargin = new(10, 4, 10, 4);
358362
private JsonRpcConfigurationModel _settingsTemplate;
359363
public Control CreateSettingPanel()
360364
{
@@ -363,22 +367,23 @@ public Control CreateSettingPanel()
363367
var settingWindow = new UserControl();
364368
var mainPanel = new StackPanel
365369
{
366-
Margin = settingControlMargin,
367-
Orientation = Orientation.Vertical
370+
Margin = settingPanelMargin, Orientation = Orientation.Vertical
368371
};
369372
settingWindow.Content = mainPanel;
370373

371374
foreach (var (type, attribute) in _settingsTemplate.Body)
372375
{
373376
var panel = new StackPanel
374377
{
375-
Orientation = Orientation.Horizontal,
376-
Margin = settingControlMargin
378+
Orientation = Orientation.Horizontal, Margin = settingControlMargin
377379
};
378-
var name = new Label()
380+
var name = new TextBlock()
379381
{
380-
Content = attribute.Label,
381-
Margin = settingControlMargin
382+
Text = attribute.Label,
383+
Width = 120,
384+
VerticalAlignment = VerticalAlignment.Center,
385+
Margin = settingControlMargin,
386+
TextWrapping = TextWrapping.WrapWithOverflow
382387
};
383388

384389
FrameworkElement contentControl;
@@ -390,8 +395,8 @@ public Control CreateSettingPanel()
390395
contentControl = new TextBlock
391396
{
392397
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
393-
Margin = settingControlMargin,
394-
MaxWidth = 400,
398+
Margin = settingTextBlockMargin,
399+
MaxWidth = 500,
395400
TextWrapping = TextWrapping.WrapWithOverflow
396401
};
397402
break;

Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,6 @@ internal class PluginAssemblyLoader : AssemblyLoadContext
1515

1616
private readonly AssemblyName assemblyName;
1717

18-
private static readonly ConcurrentDictionary<string, byte> loadedAssembly;
19-
20-
static PluginAssemblyLoader()
21-
{
22-
var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
23-
loadedAssembly = new ConcurrentDictionary<string, byte>(
24-
currentAssemblies.Select(x => new KeyValuePair<string, byte>(x.FullName, default)));
25-
26-
AppDomain.CurrentDomain.AssemblyLoad += (sender, args) =>
27-
{
28-
loadedAssembly[args.LoadedAssembly.FullName] = default;
29-
};
30-
}
31-
3218
internal PluginAssemblyLoader(string assemblyFilePath)
3319
{
3420
dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath);
@@ -47,21 +33,15 @@ protected override Assembly Load(AssemblyName assemblyName)
4733
// When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher
4834
// Otherwise duplicate assembly will be loaded and some weird behavior will occur, such as WinRT.Runtime.dll
4935
// will fail due to loading multiple versions in process, each with their own static instance of registration state
50-
if (assemblyPath == null || ExistsInReferencedPackage(assemblyName))
51-
return null;
36+
var existAssembly = Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
5237

53-
return LoadFromAssemblyPath(assemblyPath);
38+
return existAssembly ?? (assemblyPath == null ? null : LoadFromAssemblyPath(assemblyPath));
5439
}
5540

5641
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type)
5742
{
5843
var allTypes = assembly.ExportedTypes;
5944
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Any(t => t == type));
6045
}
61-
62-
internal bool ExistsInReferencedPackage(AssemblyName assemblyName)
63-
{
64-
return loadedAssembly.ContainsKey(assemblyName.FullName);
65-
}
6646
}
6747
}

Flow.Launcher.Core/Plugin/QueryBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalP
1616
return null;
1717
}
1818

19-
var rawQuery = string.Join(Query.TermSeparator, terms);
19+
var rawQuery = text;
2020
string actionKeyword, search;
2121
string possibleActionKeyword = terms[0];
2222
string[] searchTerms;
2323

2424
if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled)
2525
{ // use non global plugin for query
2626
actionKeyword = possibleActionKeyword;
27-
search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..] : string.Empty;
27+
search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty;
2828
searchTerms = terms[1..];
2929
}
3030
else
3131
{ // non action keyword
3232
actionKeyword = string.Empty;
33-
search = rawQuery;
33+
search = rawQuery.TrimStart();
3434
searchTerms = terms;
3535
}
3636

0 commit comments

Comments
 (0)