Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,10 @@ partial void OnGlobalQueryTextChanged(string value)
{
MegaQueryText = "";
MegaQueryVisible = true;
Loader?.ClearPackages(emitFinishSignal: false);
if (Loader is DiscoverablePackagesLoader discoverLoader)
discoverLoader.ResetSearch();
else
Loader?.ClearPackages(emitFinishSignal: false);
}
else
{
Expand Down Expand Up @@ -677,7 +680,7 @@ private Func<PackageWrapper, bool> BuildPackageFilter(out string query)
foreach (var f in filters) normalizedQuery = f(normalizedQuery);
query = normalizedQuery;

Func<IPackage, bool> matchFunc = SearchMode switch
Func<IPackage, bool> matchFunc = normalizedQuery.Length == 0 ? _ => true : SearchMode switch
{
SearchMode.Name => pkg => FilterHelpers.NameContains(pkg, normalizedQuery, filters),
SearchMode.Id => pkg => FilterHelpers.IdContains(pkg, normalizedQuery, filters),
Expand Down Expand Up @@ -719,7 +722,7 @@ private void UpdateFilteredState(string query)
{
bool noQuery = string.IsNullOrWhiteSpace(query);
BackgroundText = noQuery ? NoPackagesText : NoMatchesText;
BackgroundTextVisible = !MegaQueryBoxEnabled || !noQuery;
BackgroundTextVisible = !MegaQueryBoxEnabled || !noQuery || !MegaQueryVisible;
Comment thread
GabrielDuf marked this conversation as resolved.
LoadingImageVisible = false;
NoPackagesImageVisible = noQuery && BackgroundTextVisible && NoPackagesImage is not null;
}
Expand Down
5 changes: 5 additions & 0 deletions src/UniGetUI.PackageEngine.Enums/Enums.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
namespace UniGetUI.PackageEngine.Enums
{
public static class SearchLimits
{
public const int MaxEmptyQueryResultsPerSource = 100;
}

/// <summary>
/// Represents the installation scope of a package
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public struct ManagerCapabilities
public bool SupportsCustomScopesOnUpdate = true;
public bool SupportsCustomScopesOnUninstall = true;
public bool SupportsPreRelease = false;
public bool SupportsEmptySearchQuery = false;
public bool SerializesOperations = false;
public bool SupportsCustomLocations = false;
public bool SupportsCustomSources = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
List<Package> packages = [];
INativeTaskLogger logger = Manager.TaskLogger.CreateNew(LoggableTaskType.FindPackages);
Dictionary<
(PackageCatalogReference, PackageMatchField),
(PackageCatalogReference, PackageMatchField?),
Task<FindPackagesResult>
> FindPackageTasks = [];

Expand All @@ -203,24 +203,27 @@
ConnectResult result = CatalogReference.Connect();
if (result.Status == ConnectResultStatus.Ok)
{
foreach (
var filter_type in new[]
{
PackageMatchField.Name,
PackageMatchField.Id,
PackageMatchField.Moniker,
}
)
PackageMatchField?[] filterTypes = query.Length == 0
? [null]
: [PackageMatchField.Name, PackageMatchField.Id, PackageMatchField.Moniker];
foreach (PackageMatchField? filter_type in filterTypes)
{
FindPackagesOptions PackageFilters = Factory.CreateFindPackagesOptions();

logger.Log("Generating filters...");
// Name filter
PackageMatchFilter FilterName = Factory.CreatePackageMatchFilter();
FilterName.Field = filter_type;
FilterName.Value = query;
FilterName.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
PackageFilters.Filters.Add(FilterName);
if (filter_type is { } field)
{
logger.Log("Generating filters...");
// Name filter
PackageMatchFilter FilterName = Factory.CreatePackageMatchFilter();
FilterName.Field = field;
FilterName.Value = query;
FilterName.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
PackageFilters.Filters.Add(FilterName);
}
else
{
PackageFilters.ResultLimit = (uint)SearchLimits.MaxEmptyQueryResultsPerSource;
}

try
{
Expand Down Expand Up @@ -349,7 +352,7 @@
nativePackage.DefaultInstallVersion.PackageCatalog.Info.Name
);

string? reportedVersion = nativePackage.InstalledVersion.Version;

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.

Check warning on line 355 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.
string version = WinGetPkgOperationHelper.ResolveReportedInstalledVersion(
nativePackage.Id,
reportedVersion
Expand Down Expand Up @@ -429,7 +432,7 @@
source = Manager.GetLocalSource(nativePackage.Id);
}

string? reportedVersion = nativePackage.InstalledVersion.Version;

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.

Check warning on line 435 in src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Dereference of a possibly null reference.
string version = WinGetPkgOperationHelper.ResolveReportedInstalledVersion(
nativePackage.Id,
reportedVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,35 @@ public IReadOnlyList<Package> GetInstalledPackages_UnSafe()

public IReadOnlyList<Package> FindPackages_UnSafe(string query)
{
SearchResponse result = RunJson<SearchResponse>(
LoggableTaskType.FindPackages,
$"search {Quote(query)} --output json"
);
if (query.Length == 0)
{
return Manager
.SourcesHelper.GetSources()
.SelectMany(SearchSourceForBrowse)
.ToArray();
}

return SearchPackages($"search {Quote(query)} --output json");
}

private Package[] SearchSourceForBrowse(IManagerSource source)
{
try
{
return SearchPackages(
$"search {Quote(string.Empty)} --source {Quote(source.Name)} --count {SearchLimits.MaxEmptyQueryResultsPerSource} --output json"
);
}
catch (Exception ex)
{
Logger.Warn($"Could not list the packages of the WinGet source {source.Name}: {ex.Message}");
return [];
}
}

private Package[] SearchPackages(string arguments)
{
SearchResponse result = RunJson<SearchResponse>(LoggableTaskType.FindPackages, arguments);

return result
.Matches.Select(match =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using UniGetUI.Core.Logging;
using UniGetUI.Core.Tools;
Expand All @@ -11,7 +13,7 @@

namespace UniGetUI.PackageEngine.Managers.WingetManager;

internal sealed class WinGetCliHelper : IWinGetManagerHelper
internal sealed partial class WinGetCliHelper : IWinGetManagerHelper
{
// "winget search a" returns ~12k results; cap to the most relevant to avoid the freeze/RAM spike.
private const int MAX_SEARCH_RESULTS = 100;
Expand Down Expand Up @@ -270,6 +272,80 @@ IEnumerable<string> outputLines
}

public IReadOnlyList<Package> FindPackages_UnSafe(string query)
{
if (query.Length == 0)
{
return GetSourcesForBrowse()
.SelectMany(source => SearchPackages(query, source, SearchLimits.MaxEmptyQueryResultsPerSource))
.ToArray();
}

return SearchPackages(query, null, MAX_SEARCH_RESULTS);
}

private List<IManagerSource> GetSourcesForBrowse()
{
using var _cliLock = WinGet.AcquireCliLock();
List<IManagerSource> sources = [];
using Process p = new()
{
StartInfo = new()
{
FileName = _cliExecutablePath,
Arguments = Manager.Status.ExecutableCallArgs + " source export",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
},
};

IProcessTaskLogger logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListSources, p);

if (CoreTools.IsAdministrator())
{
string WinGetTemp = AppPaths.RedirectTempToElevatedDirectory(p.StartInfo);
logger.AddToStdErr(
$"[WARN] Redirecting %TEMP% folder to {WinGetTemp}, since UniGetUI was run as admin"
);
}

p.Start();

foreach (string name in ParseExportedSourceNames(ReadOutputLines(p, logger)))
{
sources.Add(Manager.SourcesHelper.Factory.GetSourceOrDefault(name));
}

logger.AddToStdErr(p.StandardError.ReadToEnd());
p.WaitForExit();
logger.Close(p.ExitCode);
return sources;
}

internal static IEnumerable<string> ParseExportedSourceNames(IEnumerable<string> outputLines)
{
foreach (string line in outputLines)
{
string? name;
try
{
name = JsonSerializer.Deserialize(line, WinGetCliJsonContext.Default.WinGetExportedSource)?.Name;
}
catch (JsonException)
{
continue;
}

if (!string.IsNullOrWhiteSpace(name))
{
yield return name;
}
}
}

private List<Package> SearchPackages(string query, IManagerSource? source, int count)
{
using var _cliLock = WinGet.AcquireCliLock();
List<Package> Packages = [];
Expand All @@ -282,8 +358,10 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)
Manager.Status.ExecutableCallArgs
+ " search \""
+ query
+ "\" --count "
+ MAX_SEARCH_RESULTS
+ "\""
+ (source is null ? "" : " --source \"" + source.Name + "\"")
+ " --count "
+ count
+ " --accept-source-agreements "
+ WinGet.GetProxyArgument(),
RedirectStandardOutput = true,
Expand All @@ -306,7 +384,7 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)

p.Start();

Packages.AddRange(ParseFoundPackages(Manager, ReadOutputLines(p, logger)));
Packages.AddRange(ParseFoundPackages(Manager, ReadOutputLines(p, logger), source));

logger.AddToStdErr(p.StandardError.ReadToEnd());
p.WaitForExit();
Expand All @@ -317,7 +395,8 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)

internal static IReadOnlyList<Package> ParseFoundPackages(
WinGet manager,
IEnumerable<string> outputLines
IEnumerable<string> outputLines,
IManagerSource? queriedSource = null
)
{
List<Package> packages = [];
Expand All @@ -338,7 +417,7 @@ IEnumerable<string> outputLines

IManagerSource source =
sourceName.Length == 0
? manager.DefaultSource
? queriedSource ?? manager.DefaultSource
: manager.SourcesHelper.Factory.GetSourceOrDefault(sourceName);

packages.Add(new Package(name, id, version, source, manager));
Expand Down Expand Up @@ -512,4 +591,9 @@ public IReadOnlyList<IManagerSource> GetSources_UnSafe()
logger.Close(p.ExitCode);
return sources;
}

internal sealed record WinGetExportedSource(string? Name);

[JsonSerializable(typeof(WinGetExportedSource))]
internal sealed partial class WinGetCliJsonContext : JsonSerializerContext { }
}
1 change: 1 addition & 0 deletions src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public WinGet()
SupportsCustomSources = true,
SupportsCustomPackageIcons = true,
SupportsCustomPackageScreenshots = true,
SupportsEmptySearchQuery = true,
Sources = new SourceCapabilities
{
KnowsPackageCount = false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using UniGetUI.Core.SettingsEngine;
using UniGetUI.Core.Tools;
using UniGetUI.Interface.Enums;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;

namespace UniGetUI.PackageEngine.PackageLoader
Expand All @@ -13,7 +14,7 @@ public class DiscoverablePackagesLoader : AbstractPackageLoader
// tags and renders thousands of packages, freezing the UI and spiking RAM.
private const int MAX_RESULTS_PER_MANAGER = 100;

private string QUERY_TEXT = string.Empty;
private string? QUERY_TEXT;
private volatile bool _pendingReload;

public DiscoverablePackagesLoader(IReadOnlyList<IPackageManager> managers)
Expand Down Expand Up @@ -45,9 +46,15 @@ public async Task ReloadPackages(string query)
await ReloadPackages();
}

public void ResetSearch()
{
QUERY_TEXT = null;
ClearPackages(emitFinishSignal: false);
}

public override async Task ReloadPackages()
{
if (QUERY_TEXT == "")
if (QUERY_TEXT is null)
{
return;
}
Expand Down Expand Up @@ -76,23 +83,38 @@ public void SetManagerSearched(IPackageManager manager, bool searched)
=> Settings.SetDictionaryItem(Settings.K.ExcludedSearchManagers, manager.Name, !searched);

protected override bool WillQueryAnyManager()
=> Managers.Any(manager => manager.IsReady() && IsManagerSearched(manager));
{
string? query = QUERY_TEXT;
return Managers.Any(manager =>
manager.IsReady() && IsManagerSearched(manager) && CanSearch(manager, query));
}

private static bool CanSearch(IPackageManager manager, string? query)
=> query != string.Empty || manager.Capabilities.SupportsEmptySearchQuery;

protected override IReadOnlyList<IPackage> LoadPackagesFromManager(IPackageManager manager)
{
if (!IsManagerSearched(manager))
string? query = QUERY_TEXT;
if (query is null || !IsManagerSearched(manager) || !CanSearch(manager, query))
{
return [];
}

string text = QUERY_TEXT;
text = CoreTools.EnsureSafeQueryString(text);
if (text == string.Empty)
string text = CoreTools.EnsureSafeQueryString(query);
if (text.Length == 0 && query.Length > 0)
{
return [];
}

IReadOnlyList<IPackage> found = manager.FindPackages(text);
if (text.Length == 0)
{
return found
.GroupBy(package => package.Source.Name)
.SelectMany(group => group.Take(SearchLimits.MaxEmptyQueryResultsPerSource))
.ToArray();
}

return found.Count > MAX_RESULTS_PER_MANAGER
? found.Take(MAX_RESULTS_PER_MANAGER).ToArray()
: found;
Expand Down
Loading
Loading