Browse Source

Fix #4028: derived-type entries were hidden by the filter cascade

DerivedTypesEntryNode.Filter reported Recurse, but the cascade's
Recurse handling force-loads the entry's lazy children and hides the
entry when all of them are hidden. A leaf derived type has no children,
so every entry under "Derived Types" ended up hidden, and the hiding
propagated up the whole derived chain. The WPF tree showed these
entries as matches; Match restores that and also keeps the entries'
children lazy instead of eagerly scanning the assembly list for each
level of the chain.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/4039/head
Christoph Wille 4 weeks ago
parent
commit
28a5cbb219
  1. 29
      ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs
  2. 12
      ILSpy/TreeNodes/DerivedTypesEntryNode.cs

29
ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs

@ -1539,6 +1539,35 @@ public class AssemblyTreeTests @@ -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<TypeTreeNode>(
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<DerivedTypesTreeNode>().Single();
derived.IsExpanded = true;
var entries = derived.Children.OfType<DerivedTypesEntryNode>().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()
{

12
ILSpy/TreeNodes/DerivedTypesEntryNode.cs

@ -69,16 +69,18 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -69,16 +69,18 @@ namespace ICSharpCode.ILSpy.TreeNodes
};
/// <summary>
/// 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:
/// <see cref="LanguageSettings.SearchTermMatches"/> 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: <see cref="LanguageSettings.SearchTermMatches"/> is a
/// no-op so the assembly tree stays independent of the search pane.
/// </summary>
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)

Loading…
Cancel
Save