diff --git a/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs b/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs index 7312dcf92..7c8daeb90 100644 --- a/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs +++ b/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs @@ -1539,6 +1539,35 @@ public class AssemblyTreeTests "the loaded assembly list contains several Exception subclasses (e.g. SystemException, ArgumentException)"); } + [AvaloniaTest] + public async Task Derived_Type_Entries_Stay_Visible_When_The_DerivedTypes_Node_Is_Expanded() + { + // The filter cascade runs for children added under a visible parent. A derived-type + // entry must report FilterResult.Match there: the Recurse handling force-loads the + // entry's own (lazy) children and hides the entry when all of them are hidden -- a + // leaf derived type has none, so every entry under "Derived Types" ended up hidden. + + var (_, vm) = await TestHarness.BootAsync(3); + + var coreLibName = typeof(object).Assembly.GetName().Name!; + var typeNode = vm.AssemblyTreeModel.FindNode( + coreLibName, "System", "System.Exception"); + // Expand the full ancestor chain so the type node is IsVisible -- the cascade only + // fires for children of visible parents, which is the state the real tree is in. + foreach (var ancestor in typeNode.Ancestors()) + ancestor.IsExpanded = true; + typeNode.IsExpanded = true; + + var derived = typeNode.Children.OfType().Single(); + derived.IsExpanded = true; + + var entries = derived.Children.OfType().ToList(); + entries.Should().NotBeEmpty( + "the loaded assembly list contains several Exception subclasses"); + entries.Should().OnlyContain(e => e.IsVisible, + "public derived-type entries must show under the expanded Derived Types node"); + } + [AvaloniaTest] public async Task Sealed_Class_Has_No_DerivedTypes_Node() { diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 08eda9a4f..01ce0cdb5 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -69,16 +69,18 @@ namespace ICSharpCode.ILSpy.TreeNodes }; /// - /// Drops non-public entries under PublicOnly visibility, otherwise recurses so the user - /// can drill into derived chains. The active search term is deliberately not consulted: - /// is a no-op so the assembly tree stays - /// independent of the search pane. + /// Drops non-public entries under PublicOnly visibility, otherwise reports a match. It must + /// not report Recurse: the filter cascade's Recurse handling force-loads this node's lazy + /// children and hides the node when all of them are hidden, so a leaf derived type (no + /// further subclasses, hence no children) would vanish from the tree. The active search term + /// is deliberately not consulted: is a + /// no-op so the assembly tree stays independent of the search pane. /// public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; - return FilterResult.Recurse; + return FilterResult.Match; } public override void ActivateItem(IPlatformRoutedEventArgs e)