From ce1020c3697b28b1c7716c0c25b2e23791191f1c Mon Sep 17 00:00:00 2001
From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com>
Date: Sun, 2 Nov 2025 12:23:56 +1000
Subject: [PATCH] display installed packages v1
---
Directory.Packages.props | 1 +
.../Features/Evaluation/ProjectEvaluation.cs | 70 +++++++++++++++++++
.../Features/Nuget/NugetClientService.cs | 40 +++++++++++
.../SharpIDE.Application.csproj | 1 +
.../Features/Nuget/NugetPanel.cs | 25 ++++++-
5 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5b6b6e9..14cc957 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -44,6 +44,7 @@
+
diff --git a/src/SharpIDE.Application/Features/Evaluation/ProjectEvaluation.cs b/src/SharpIDE.Application/Features/Evaluation/ProjectEvaluation.cs
index 3693047..494291b 100644
--- a/src/SharpIDE.Application/Features/Evaluation/ProjectEvaluation.cs
+++ b/src/SharpIDE.Application/Features/Evaluation/ProjectEvaluation.cs
@@ -1,5 +1,6 @@
using Ardalis.GuardClauses;
using Microsoft.Build.Evaluation;
+using NuGet.ProjectModel;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
namespace SharpIDE.Application.Features.Evaluation;
@@ -55,4 +56,73 @@ public static class ProjectEvaluation
return Guid.Parse(userSecretsId);
}
+ public class InstalledPackage
+ {
+ public required string Name { get; set; }
+ public required string RequestedVersion { get; set; }
+ public required string? ResolvedVersion { get; set; }
+ public required string TargetFramework { get; set; }
+ public required bool IsTopLevel { get; set; }
+ public required bool IsAutoReferenced { get; set; }
+ }
+ public static async Task> GetPackageReferencesForProject(SharpIdeProjectModel projectModel, bool includeTransitive = true)
+ {
+ using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(ProjectEvaluation)}.{nameof(GetPackageReferencesForProject)}");
+ Guard.Against.Null(projectModel, nameof(projectModel));
+
+ var project = _projectCollection.GetLoadedProjects(projectModel.FilePath).Single();
+
+ var assetsPath = project.GetPropertyValue("ProjectAssetsFile");
+
+ if (File.Exists(assetsPath) is false)
+ {
+ throw new FileNotFoundException("Could not find project.assets.json file", assetsPath);
+ }
+ var lockFileFormat = new LockFileFormat();
+ var lockFile = lockFileFormat.Read(assetsPath);
+ var packages = GetPackagesFromAssetsFile(lockFile, includeTransitive);
+ return packages;
+ }
+
+ // ChatGPT Special
+ private static List GetPackagesFromAssetsFile(LockFile assetsFile, bool includeTransitive)
+ {
+ var packages = new List();
+
+ foreach (var target in assetsFile.Targets.Where(t => t.RuntimeIdentifier == null))
+ {
+ var tfm = target.TargetFramework.GetShortFolderName();
+ var tfmInfo = assetsFile.PackageSpec.TargetFrameworks
+ .FirstOrDefault(t => t.FrameworkName.Equals(target.TargetFramework));
+
+ if (tfmInfo == null) continue;
+
+ var topLevelDependencies = tfmInfo.Dependencies
+ .DistinctBy(s => s.Name)
+ .Select(s => s.Name)
+ .ToHashSet();
+
+ foreach (var library in target.Libraries.Where(l => l.Type == "package"))
+ {
+ var isTopLevel = topLevelDependencies.Contains(library.Name);
+ if (!includeTransitive && !isTopLevel) continue;
+
+ var dependency = tfmInfo.Dependencies
+ .FirstOrDefault(d => d.Name.Equals(library.Name, StringComparison.OrdinalIgnoreCase));
+
+ packages.Add(new InstalledPackage
+ {
+ Name = library.Name,
+ RequestedVersion = dependency?.LibraryRange.VersionRange?.ToString() ?? "",
+ ResolvedVersion = library.Version?.ToString(),
+ TargetFramework = tfm,
+ IsTopLevel = isTopLevel,
+ IsAutoReferenced = dependency?.AutoReferenced ?? false
+ });
+ }
+ }
+
+ return packages;
+ }
+
}
diff --git a/src/SharpIDE.Application/Features/Nuget/NugetClientService.cs b/src/SharpIDE.Application/Features/Nuget/NugetClientService.cs
index ace984f..e7ac258 100644
--- a/src/SharpIDE.Application/Features/Nuget/NugetClientService.cs
+++ b/src/SharpIDE.Application/Features/Nuget/NugetClientService.cs
@@ -3,6 +3,8 @@ using NuGet.Configuration;
using NuGet.Packaging.Core;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+using SharpIDE.Application.Features.Evaluation;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
namespace SharpIDE.Application.Features.Nuget;
@@ -106,4 +108,42 @@ public class NugetClientService
metadataList.Reverse();
return metadataList;
}
+
+ public async Task> GetPackagesForInstalledPackages(string directoryPath, List installedPackages, CancellationToken cancellationToken = default)
+ {
+ var settings = Settings.LoadDefaultSettings(root: directoryPath);
+ var packageSourceProvider = new PackageSourceProvider(settings);
+ var packageSources = packageSourceProvider.LoadPackageSources().Where(p => p.IsEnabled).ToList();
+
+ var packagesResult = new List();
+ foreach (var installedPackage in installedPackages)
+ {
+ var idePackageResult = new IdePackageResult(installedPackage.Name, []);
+ var nugetVersionString = installedPackage.ResolvedVersion ?? installedPackage.RequestedVersion;
+ var nugetVersion = NuGetVersion.Parse(nugetVersionString);
+ var packageIdentity = new PackageIdentity(installedPackage.Name, nugetVersion);
+
+
+ foreach (var source in packageSources)
+ {
+ var repository = Repository.Factory.GetCoreV3(source.Source);
+ var metadataResource = await repository.GetResourceAsync(cancellationToken).ConfigureAwait(false);
+
+ if (metadataResource == null)
+ continue;
+
+ var foundPackage = await metadataResource.GetMetadataAsync(packageIdentity, _sourceCacheContext, _nugetLogger, cancellationToken).ConfigureAwait(false);
+ if (foundPackage != null)
+ {
+ idePackageResult.PackageFromSources.Add(new IdePackageFromSourceResult(foundPackage, source));
+ }
+ }
+
+ if (idePackageResult.PackageFromSources.Count > 0)
+ {
+ packagesResult.Add(idePackageResult);
+ }
+ }
+ return packagesResult;
+ }
}
diff --git a/src/SharpIDE.Application/SharpIDE.Application.csproj b/src/SharpIDE.Application/SharpIDE.Application.csproj
index 93c437e..2e7f417 100644
--- a/src/SharpIDE.Application/SharpIDE.Application.csproj
+++ b/src/SharpIDE.Application/SharpIDE.Application.csproj
@@ -49,6 +49,7 @@
+
diff --git a/src/SharpIDE.Godot/Features/Nuget/NugetPanel.cs b/src/SharpIDE.Godot/Features/Nuget/NugetPanel.cs
index bb0813e..eef1e4a 100644
--- a/src/SharpIDE.Godot/Features/Nuget/NugetPanel.cs
+++ b/src/SharpIDE.Godot/Features/Nuget/NugetPanel.cs
@@ -1,4 +1,5 @@
using Godot;
+using SharpIDE.Application.Features.Evaluation;
using SharpIDE.Application.Features.Nuget;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
@@ -32,7 +33,29 @@ public partial class NugetPanel : Control
{
await Task.Delay(300);
var result = await _nugetClientService.GetTop100Results(Solution!.DirectoryPath);
- ;
+
+ _ = Task.GodotRun(async () =>
+ {
+ var project = Solution.AllProjects.First(s => s.Name == "ProjectB");
+ await project.MsBuildEvaluationProjectTask;
+ var installedPackages = await ProjectEvaluation.GetPackageReferencesForProject(project);
+ await this.InvokeAsync(() => _installedPackagesVboxContainer.QueueFreeChildren());
+ var idePackageResult = await _nugetClientService.GetPackagesForInstalledPackages(project.ChildNodeBasePath, installedPackages);
+ var scenes = idePackageResult.Select(s =>
+ {
+ var scene = _packageEntryScene.Instantiate();
+ scene.PackageResult = s;
+ scene.PackageSelected += OnPackageSelected;
+ return scene;
+ }).ToList();
+ await this.InvokeAsync(() =>
+ {
+ foreach (var scene in scenes)
+ {
+ _installedPackagesVboxContainer.AddChild(scene);
+ }
+ });
+ });
await this.InvokeAsync(() => _availablePackagesItemList.QueueFreeChildren());
var scenes = result.Select(s =>
{