diff --git a/ILSpy.Tests/MainWindow/MainWindowTests.cs b/ILSpy.Tests/MainWindow/MainWindowTests.cs index 3fdbf5804..f2fffdb3d 100644 --- a/ILSpy.Tests/MainWindow/MainWindowTests.cs +++ b/ILSpy.Tests/MainWindow/MainWindowTests.cs @@ -118,6 +118,25 @@ public class MainWindowTests firstMethod.Should().Be().CenteredInView(); } + [AvaloniaTest] + public async Task Assembly_Node_Has_References_Folder_Child() + { + var window = AppComposition.Current.GetExport(); + window.Show(); + var vm = (MainWindowViewModel)window.DataContext!; + await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); + + var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); + assemblyNode.EnsureLazyChildren(); + + var refFolder = assemblyNode.Children.OfType().Single(); + refFolder.Text.Should().Be(Resources.References); + + refFolder.EnsureLazyChildren(); + refFolder.Children.Should().NotBeEmpty(); + refFolder.Children.Should().AllBeAssignableTo(); + } + [AvaloniaTest] public async Task Selecting_Type_Node_Decompiles_Type_Definition() { diff --git a/ILSpy/Assets/Icons/ReferenceFolder.svg b/ILSpy/Assets/Icons/ReferenceFolder.svg new file mode 100644 index 000000000..469268e6c --- /dev/null +++ b/ILSpy/Assets/Icons/ReferenceFolder.svg @@ -0,0 +1,90 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/Images.cs b/ILSpy/Images.cs index 391baab1e..456439375 100644 --- a/ILSpy/Images.cs +++ b/ILSpy/Images.cs @@ -75,6 +75,7 @@ namespace ILSpy.Images // Containers public static readonly IImage Namespace = LoadSvg(nameof(Namespace)); + public static readonly IImage ReferenceFolder = LoadSvg(nameof(ReferenceFolder)); // Types public static readonly IImage Class = LoadSvg(nameof(Class)); diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs new file mode 100644 index 000000000..d89731948 --- /dev/null +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Metadata; + +namespace ILSpy.TreeNodes +{ + /// + /// Single entry in the references folder. Minimal port: shows the reference name + /// using ILAmbience escaping plus a generic assembly icon. Children (the + /// per-reference type list and transitive references) are not yet implemented. + /// + sealed class AssemblyReferenceTreeNode : ILSpyTreeNode + { + readonly AssemblyReference reference; + readonly AssemblyTreeNode parentAssembly; + + public AssemblyReferenceTreeNode(AssemblyReference reference, AssemblyTreeNode parentAssembly) + { + this.reference = reference ?? throw new ArgumentNullException(nameof(reference)); + this.parentAssembly = parentAssembly ?? throw new ArgumentNullException(nameof(parentAssembly)); + } + + public AssemblyReference AssemblyReference => reference; + + public override object Text => ILAmbience.EscapeName(reference.Name); + + public override object Icon => Images.Images.Assembly; + } +} diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 652e47b94..7fe361b71 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -173,6 +173,8 @@ namespace ILSpy.TreeNodes return; } + Children.Add(new ReferenceFolderTreeNode(module, this)); + var metadata = module.Metadata; var namespaces = metadata.TypeDefinitions .Where(t => metadata.GetTypeDefinition(t).GetDeclaringType().IsNil) diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs new file mode 100644 index 000000000..527fc81a4 --- /dev/null +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Linq; + +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Properties; + +namespace ILSpy.TreeNodes +{ + /// + /// References folder under an assembly node. Children are one + /// per metadata AssemblyRef. + /// + sealed class ReferenceFolderTreeNode : ILSpyTreeNode + { + readonly MetadataFile module; + readonly AssemblyTreeNode parentAssembly; + + public ReferenceFolderTreeNode(MetadataFile module, AssemblyTreeNode parentAssembly) + { + this.module = module; + this.parentAssembly = parentAssembly; + LazyLoading = true; + } + + public override object Text => Resources.References; + + public override object Icon => Images.Images.ReferenceFolder; + + protected override void LoadChildren() + { + foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name)) + Children.Add(new AssemblyReferenceTreeNode(r, parentAssembly)); + } + } +}