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 => {