From a576d20cfb68092817353d13067a788b0e84185e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 23 May 2026 08:24:43 +0200 Subject: [PATCH] CompareView "Show Identical" now actually filters rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ShowIdentical toggle was wired model-side (CompareTabPageModel.OnShowIdenticalChanged → RootEntry.EnsureChildrenFiltered) and view-side (Rebind on PropertyChanged), and the cascade was correctly setting IsHidden via ComparisonEntryTreeNode.Filter. But the HierarchicalModel's ChildrenSelector and SetRoots passed node.Children through unfiltered — so IsHidden flags had no effect on what the DataGrid actually rendered. Toggling the checkbox looked like a no-op. Assisted-by: Claude:claude-opus-4-7:Claude Code --- ILSpy.Tests/Compare/CompareViewRenderTests.cs | 52 +++++++++++++++++++ ILSpy/Views/CompareView.axaml.cs | 14 ++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/ILSpy.Tests/Compare/CompareViewRenderTests.cs b/ILSpy.Tests/Compare/CompareViewRenderTests.cs index 0fd3e06ea..0d6db9c6c 100644 --- a/ILSpy.Tests/Compare/CompareViewRenderTests.cs +++ b/ILSpy.Tests/Compare/CompareViewRenderTests.cs @@ -106,4 +106,56 @@ public class CompareViewRenderTests () => grid.GetVisualDescendants().OfType().Any(), description: "the diff grid must render at least one row once the merged tree is bound"); } + + [AvaloniaTest] + public async Task CompareView_ShowIdentical_Toggle_Changes_Visible_Row_Set() + { + // Comparing an assembly with itself yields only identical rows. With ShowIdentical=false + // (default) every row should be hidden; flipping ShowIdentical=true must surface them. + // Regression: the bug was that the HierarchicalModel's ChildrenSelector returned + // node.Children unfiltered, so IsHidden flags set by ApplyFilterToChild had no effect + // on what the DataGrid actually rendered. + var window = AppComposition.Current.GetExport(); + window.Show(); + var vm = (MainWindowViewModel)window.DataContext!; + await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); + + var assembly = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() + .First(a => a.IsLoadedAsValidAssembly); + + // Construct the compare model directly with the same assembly on both sides so every + // entry collapses to DiffKind.None. The View renders via the standard OpenNewTab path. + var page = new CompareTabPageModel(assembly, assembly); + vm.DockWorkspace.OpenNewTab(page); + + var view = await window.WaitForComponent(); + var grid = await view.WaitForComponent(); + await Waiters.WaitForAsync(() => grid.HierarchicalModel != null); + global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); + + // Baseline: ShowIdentical=false. All rows are identical-only, so the visible row count + // must be zero (every entry filters to Hidden). + page.ShowIdentical.Should().BeFalse("baseline state"); + var rowsBeforeToggle = grid.GetVisualDescendants().OfType().Count(); + rowsBeforeToggle.Should().Be(0, + "with ShowIdentical=false and only-identical content, no rows must be visible"); + + // Flip ShowIdentical=true and re-evaluate. + page.ShowIdentical = true; + global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); + await Waiters.WaitForAsync( + () => grid.GetVisualDescendants().OfType().Any(), + description: "flipping ShowIdentical=true must surface previously-hidden identical rows"); + + var rowsAfterToggle = grid.GetVisualDescendants().OfType().Count(); + rowsAfterToggle.Should().BeGreaterThan(0, + "with ShowIdentical=true, identical rows must surface in the grid"); + + // Flip back and verify it returns to the hidden state. + page.ShowIdentical = false; + global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); + await Waiters.WaitForAsync( + () => grid.GetVisualDescendants().OfType().Count() == 0, + description: "flipping ShowIdentical=false again must re-hide all identical rows"); + } } diff --git a/ILSpy/Views/CompareView.axaml.cs b/ILSpy/Views/CompareView.axaml.cs index e292c5a6f..0205aed18 100644 --- a/ILSpy/Views/CompareView.axaml.cs +++ b/ILSpy/Views/CompareView.axaml.cs @@ -18,6 +18,7 @@ using System; using System.ComponentModel; +using System.Linq; using Avalonia.Controls; using Avalonia.Controls.DataGridHierarchical; @@ -80,15 +81,24 @@ namespace ILSpy.Compare ilspy.EnsureChildrenFiltered(); else node.EnsureLazyChildren(); - return node.Children; + return FilterHidden(node.Children); }, IsLeafSelector = node => !node.ShowExpander, VirtualizeChildren = false, IsExpandedPropertyPath = nameof(SharpTreeNode.IsExpanded), }; var model = new HierarchicalModel(options); - model.SetRoots(root.Children); + model.SetRoots(FilterHidden(root.Children)); DiffGrid.HierarchicalModel = model; } + + // ShowIdentical=false hides identical rows by setting IsHidden=true via + // ComparisonEntryTreeNode.Filter, but the DataGrid's HierarchicalModel only sees + // what ChildrenSelector / SetRoots feed it — IsHidden alone has no effect on the + // rendered row set. Filter the children here so the cascade-set IsHidden flags + // actually reach the grid. Same pattern AssemblyListPane.FilterChildren uses for + // the assembly tree's ShowApiLevel cascade. + static System.Collections.Generic.IEnumerable FilterHidden(System.Collections.Generic.IEnumerable children) + => children.Where(c => c is not TreeNodes.ILSpyTreeNode it || !it.IsHidden).ToList(); } }