Merge pull request #626 from Flow-Launcher/dev

Release 1.8.2
This commit is contained in:
Jeremy Wu 2021-08-11 19:06:27 +10:00 committed by GitHub
commit 151c69e9b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 88 additions and 151 deletions

View file

@ -40,15 +40,7 @@ namespace Flow.Launcher.Core.Plugin
public List<Result> LoadContextMenus(Result selectedResult)
{
var output = ExecuteContextMenu(selectedResult);
try
{
return DeserializedResult(output);
}
catch (Exception e)
{
Log.Exception($"|JsonRPCPlugin.LoadContextMenus|Exception on result <{selectedResult}>", e);
return null;
}
return DeserializedResult(output);
}
private static readonly JsonSerializerOptions options = new()
@ -65,23 +57,10 @@ namespace Flow.Launcher.Core.Plugin
{
if (output == Stream.Null) return null;
try
{
var queryResponseModel =
await JsonSerializer.DeserializeAsync<JsonRPCQueryResponseModel>(output, options);
var queryResponseModel =
await JsonSerializer.DeserializeAsync<JsonRPCQueryResponseModel>(output, options);
return ParseResults(queryResponseModel);
}
catch (JsonException e)
{
Log.Exception(GetType().FullName, "Unexpected Json Input", e);
}
finally
{
await output.DisposeAsync();
}
return null;
return ParseResults(queryResponseModel);
}
private List<Result> DeserializedResult(string output)
@ -249,7 +228,7 @@ namespace Flow.Launcher.Core.Plugin
await using var source = process.StandardOutput.BaseStream;
var buffer = BufferManager.GetStream();
token.Register(() =>
{
// ReSharper disable once AccessToModifiedClosure
@ -274,6 +253,14 @@ namespace Flow.Launcher.Core.Plugin
token.ThrowIfCancellationRequested();
if (buffer.Length == 0)
{
var errorMessage = process.StandardError.EndOfStream ?
"Empty JSONRPC Response" :
await process.StandardError.ReadToEndAsync();
throw new InvalidDataException($"{context.CurrentPluginMetadata.Name}|{errorMessage}");
}
if (!process.StandardError.EndOfStream)
{
using var standardError = process.StandardError;
@ -281,23 +268,12 @@ namespace Flow.Launcher.Core.Plugin
if (!string.IsNullOrEmpty(error))
{
Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}");
return Stream.Null;
Log.Error($"|{context.CurrentPluginMetadata.Name}.{nameof(ExecuteAsync)}|{error}");
}
Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error.");
return Stream.Null;
}
return buffer;
}
catch (Exception e)
{
Log.Exception(
$"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
e);
return Stream.Null;
}
finally
{
process?.Dispose();
@ -307,20 +283,8 @@ namespace Flow.Launcher.Core.Plugin
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
try
{
var output = await ExecuteQueryAsync(query, token);
return await DeserializedResultAsync(output);
}
catch (OperationCanceledException)
{
return null;
}
catch (Exception e)
{
Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);
return null;
}
var output = await ExecuteQueryAsync(query, token);
return await DeserializedResultAsync(output);
}
public virtual Task InitAsync(PluginInitContext context)
@ -329,4 +293,4 @@ namespace Flow.Launcher.Core.Plugin
return Task.CompletedTask;
}
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
@ -26,7 +27,8 @@ namespace Flow.Launcher.Infrastructure.Image
private const int MaxCached = 50;
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
private const int permissibleFactor = 2;
private SemaphoreSlim semaphore = new(1, 1);
public void Initialization(Dictionary<string, int> usage)
{
foreach (var key in usage.Keys)
@ -60,20 +62,29 @@ namespace Flow.Launcher.Infrastructure.Image
}
);
// To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
if (Data.Count > permissibleFactor * MaxCached)
SliceExtra();
async void SliceExtra()
{
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
Data.TryRemove(key, out _);
// To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
if (Data.Count > permissibleFactor * MaxCached)
{
await semaphore.WaitAsync().ConfigureAwait(false);
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary
// Double Check to avoid concurrent remove
if (Data.Count > permissibleFactor * MaxCached)
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key).ToArray())
Data.TryRemove(key, out _);
semaphore.Release();
}
}
}
}
public bool ContainsKey(string key)
{
return Data.ContainsKey(key) && Data[key].imageSource != null;
return key is not null && Data.ContainsKey(key) && Data[key].imageSource != null;
}
public int CacheSize()

View file

@ -18,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
// TODO: Remove. This is backwards compatibility for 1.8.0 release.
// Introduced two new action keywords in Explorer, so need to update plugin setting in the UserData folder.
if (metadata.ID == "572be03c74c642baae319fc283e561a8" && metadata.ActionKeywords.Count != settings.ActionKeywords.Count)
if (metadata.ID == "572be03c74c642baae319fc283e561a8" && metadata.ActionKeywords.Count > settings.ActionKeywords.Count)
{
settings.ActionKeywords.Add(Query.GlobalPluginWildcardSign); // for index search
settings.ActionKeywords.Add(Query.GlobalPluginWildcardSign); // for path search

View file

@ -39,7 +39,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
/// </summary>
public bool ShouldUsePinyin { get; set; } = false;
internal SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular;
[JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))]
public SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular;
[JsonIgnore]
public string QuerySearchPrecisionString

View file

@ -42,7 +42,7 @@
<ColumnDefinition Width="0" />
</Grid.ColumnDefinitions>
<Image x:Name="ImageIcon" Width="32" Height="32" HorizontalAlignment="Left"
Source="{Binding Image.Value}" />
Source="{Binding Image}" />
<Grid Margin="5 0 5 0" Grid.Column="1" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />

View file

@ -108,9 +108,7 @@ namespace Flow.Launcher.ViewModel
}
Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends");
}
;
};
void continueAction(Task t)
{

View file

@ -11,62 +11,12 @@ namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
public class LazyAsync<T> : Lazy<ValueTask<T>>
{
private readonly T defaultValue;
private readonly Action _updateCallback;
public new T Value
{
get
{
if (!IsValueCreated)
{
_ = Exercute(); // manually use callback strategy
return defaultValue;
}
if (!base.Value.IsCompletedSuccessfully)
return defaultValue;
return base.Value.Result;
// If none of the variables captured by the local function are captured by other lambdas,
// the compiler can avoid heap allocations.
async ValueTask Exercute()
{
await base.Value.ConfigureAwait(false);
_updateCallback();
}
}
}
public LazyAsync(Func<ValueTask<T>> factory, T defaultValue, Action updateCallback) : base(factory)
{
if (defaultValue != null)
{
this.defaultValue = defaultValue;
}
_updateCallback = updateCallback;
}
}
public ResultViewModel(Result result, Settings settings)
{
if (result != null)
{
Result = result;
Image = new LazyAsync<ImageSource>(
SetImage,
ImageLoader.DefaultImage,
() =>
{
OnPropertyChanged(nameof(Image));
});
}
}
Settings = settings;
}
@ -85,44 +35,55 @@ namespace Flow.Launcher.ViewModel
? Result.SubTitle
: Result.SubTitleToolTip;
public LazyAsync<ImageSource> Image { get; set; }
private volatile bool ImageLoaded;
private async ValueTask<ImageSource> SetImage()
private ImageSource image = ImageLoader.DefaultImage;
public ImageSource Image
{
get
{
if (!ImageLoaded)
{
ImageLoaded = true;
_ = LoadImageAsync();
}
return image;
}
private set => image = value;
}
private async ValueTask LoadImageAsync()
{
var imagePath = Result.IcoPath;
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
{
try
{
return Result.Icon();
image = Result.Icon();
return;
}
catch (Exception e)
{
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
return ImageLoader.DefaultImage;
}
}
if (ImageLoader.CacheContainImage(imagePath))
{
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
return ImageLoader.Load(imagePath);
image = ImageLoader.Load(imagePath);
return;
}
return await Task.Run(() => ImageLoader.Load(imagePath));
// We need to modify the property not field here to trigger the OnPropertyChanged event
Image = await Task.Run(() => ImageLoader.Load(imagePath)).ConfigureAwait(false);
}
public Result Result { get; }
public override bool Equals(object obj)
{
var r = obj as ResultViewModel;
if (r != null)
{
return Result.Equals(r.Result);
}
else
{
return false;
}
return obj is ResultViewModel r && Result.Equals(r.Result);
}
public override int GetHashCode()

View file

@ -52,7 +52,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
},
Score = score,
TitleToolTip = Constants.ToolTipOpenDirectory,
SubTitleToolTip = Constants.ToolTipOpenDirectory,
SubTitleToolTip = path,
ContextData = new SearchResult
{
Type = ResultType.Folder,
@ -144,7 +144,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return true;
},
TitleToolTip = Constants.ToolTipOpenContainingFolder,
SubTitleToolTip = Constants.ToolTipOpenContainingFolder,
SubTitleToolTip = filePath,
ContextData = new SearchResult
{
Type = ResultType.File,

View file

@ -143,7 +143,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return await IndexSearch.WindowsIndexSearchAsync(
querySearchString,
queryConstructor.CreateQueryHelper(),
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
@ -181,7 +181,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return await IndexSearch.WindowsIndexSearchAsync(
querySearchString,
queryConstructor.CreateQueryHelper(),
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
@ -195,7 +195,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return await IndexSearch.WindowsIndexSearchAsync(
path,
queryConstructor.CreateQueryHelper(),
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,

View file

@ -88,7 +88,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
internal async static Task<List<Result>> WindowsIndexSearchAsync(
string searchString,
CSearchQueryHelper queryHelper,
Func<CSearchQueryHelper> createQueryHelper,
Func<string, string> constructQuery,
List<AccessLink> exclusionList,
Query query,
@ -104,7 +104,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var constructedQuery = constructQuery(searchString);
return RemoveResultsInExclusionList(
await ExecuteWindowsIndexSearchAsync(constructedQuery, queryHelper.ConnectionString, query, token).ConfigureAwait(false),
await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, query, token).ConfigureAwait(false),
exclusionList,
token);
}

View file

@ -9,7 +9,7 @@
"Name": "Explorer",
"Description": "Search and manage files and folders. Explorer utilises Windows Index Search",
"Author": "Jeremy Wu",
"Version": "1.8.2",
"Version": "1.8.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",

View file

@ -120,7 +120,10 @@ namespace Flow.Launcher.Plugin.PluginsManager
.ChangeQuery(
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.HotkeyUpdate} {plugin.Name}");
Application.Current.MainWindow.Show();
var mainWindow = Application.Current.MainWindow;
mainWindow.Visibility = Visibility.Visible;
mainWindow.Focus();
shouldHideWindow = false;
return;
@ -185,7 +188,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty).TrimStart();
var resultsForUpdate =
from existingPlugin in Context.API.GetAllPlugins()
join pluginFromManifest in pluginsManifest.UserPlugins

View file

@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
"Version": "1.8.4",
"Version": "1.8.5",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",

View file

@ -49,7 +49,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
### Installation
| [Windows 7 and up installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | [Portable](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip) | `WinGet install "Flow Launcher"` |
| [Windows 7+ installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Setup.exe) | [Portable](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip) | `WinGet install "Flow Launcher"` |
| --------------------------------- | --------------------------------- | --------------------------------- |
Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up.
@ -59,10 +59,10 @@ Windows may complain about security due to code not being signed, this will be c
- Open context menu: on the selected result, press <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
- Cancel/Return to previous screen: <kbd>Esc</kbd>.
- Install/Uninstall/Update plugins: in the search window, type `pm` `install`/`uninstall`/`update` + the plugin name.
- Saved user settings are located:
- Type `flow user data` to open your saved user settings folder. They are located at:
- If using roaming: `%APPDATA%\FlowLauncher`
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData`
- Logs are saved along with your user settings folder.
- Type `open log location` to open your logs folder, they are saved along with your user settings folder.
[More tips](https://flow-launcher.github.io/docs/#/usage-tips)
@ -74,7 +74,7 @@ If you are using Python plugins, flow will prompt to either select the location
Vist [here](https://flow-launcher.github.io/docs/#/plugins) for our plugin portfolio.
If you are keen to write your own plugin for flow, please take a look at our plugin development documentation for [C#](https://flow-launcher.github.io/docs/#/develop-csharp-plugins) or [Python](https://flow-launcher.github.io/docs/#/develop-py-plugins)
If you are keen to write your own plugin for flow, please take a look at our plugin development documentation for [C#](https://flow-launcher.github.io/docs/#/develop-dotnet-plugins) or [Python](https://flow-launcher.github.io/docs/#/develop-py-plugins)
## Questions/Suggestions

View file

@ -89,7 +89,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) {
Move-Item $temp\* $output -Force
Remove-Item $temp
$file = "$output\Flow-Launcher-v$version.exe"
$file = "$output\Flow-Launcher-Setup.exe"
Write-Host "Filename: $file"
Move-Item "$output\Setup.exe" $file -Force
@ -109,7 +109,7 @@ function Publish-Self-Contained ($p) {
function Publish-Portable ($outputLocation, $version) {
& $outputLocation\Flow-Launcher-v$v.exe --silent | Out-Null
& $outputLocation\Flow-Launcher-Setup.exe --silent | Out-Null
mkdir "$env:LocalAppData\FlowLauncher\app-$version\UserData"
Compress-Archive -Path $env:LocalAppData\FlowLauncher -DestinationPath $outputLocation\Flow-Launcher-Portable.zip
}

View file

@ -1,4 +1,4 @@
version: '1.8.1.{build}'
version: '1.8.2.{build}'
init:
- ps: |