Browse Source

Revert search-pane→tree-filter bleed-through

The user reported that expanding an enum (System.DayOfWeek) with a search
term active showed only Base/Derived nodes — every FieldTreeNode child
hidden. This is NOT a recent regression. It traces back to commit
45461ddde, which made LanguageSettings.SearchTermMatches honour the
SearchTerm and wired SearchPaneModel to push its term into
LanguageSettings on every keystroke.
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
2a962ff306
  1. 37
      ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs
  2. 43
      ILSpy.Tests/Search/SearchTermFilterTests.cs
  3. 19
      ILSpy/LanguageSettings.cs
  4. 8
      ILSpy/Search/SearchPaneModel.cs

37
ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs

@ -41,6 +41,7 @@ using ILSpy.Commands; @@ -41,6 +41,7 @@ using ILSpy.Commands;
using ILSpy.Docking;
using ILSpy.Metadata;
using ILSpy.Metadata.CorTables;
using ILSpy.Search;
using ILSpy.TreeNodes;
using ILSpy.ViewModels;
using ILSpy.Views;
@ -1483,6 +1484,42 @@ public class AssemblyTreeTests @@ -1483,6 +1484,42 @@ public class AssemblyTreeTests
"Execute under headless must be a no-op rather than crash");
}
[AvaloniaTest]
public async Task Active_Search_Term_Does_Not_Hide_Member_Tree_Nodes()
{
// Pre-existing port misstep: commit 45461ddde wired the search-pane's term into
// LanguageSettings.SearchTerm and made SearchTermMatches gate visibility on it.
// WPF intentionally makes SearchTermMatches a no-op (returns true) so the assembly
// tree stays independent of the search pane. After fixing parity, FieldTreeNode.Filter
// must NOT return Hidden purely because the field's name doesn't contain the active
// SearchTerm — only ShowApiLevel + ShowMember remain valid hiding criteria.
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var settings = AppComposition.Current.GetExport<SettingsService>()
.SessionSettings.LanguageSettings;
settings.SearchTerm = "ZZZ_DefinitelyNotAnEnumName";
var coreLibName = typeof(object).Assembly.GetName().Name!;
var dayOfWeek = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLibName, "System", "System.DayOfWeek");
Assert.That(dayOfWeek, Is.Not.Null, "System.DayOfWeek must be discoverable in CoreLib");
dayOfWeek!.EnsureLazyChildren();
var monday = dayOfWeek.Children.OfType<FieldTreeNode>()
.FirstOrDefault(f => f.FieldDefinition.Name == "Monday");
Assert.That(monday, Is.Not.Null,
"Monday must be present as a FieldTreeNode child of System.DayOfWeek");
// Direct Filter() invocation bypasses the IsVisible gate that the implicit cascade
// honours, so the assertion exercises the policy independent of view-tree state.
monday!.Filter(settings).Should().NotBe(FilterResult.Hidden,
"an active search term must not hide member tree nodes — WPF's SearchTermMatches is a no-op");
}
[AvaloniaTest]
public async Task Global_Namespace_Types_Appear_Under_Dash_Node()
{

43
ILSpy.Tests/Search/SearchTermFilterTests.cs

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Headless.NUnit;
@ -35,28 +34,30 @@ namespace ICSharpCode.ILSpy.Tests.Search; @@ -35,28 +34,30 @@ namespace ICSharpCode.ILSpy.Tests.Search;
public class SearchTermFilterTests
{
[AvaloniaTest]
public Task SearchTermMatches_Empty_Term_Matches_Anything()
public Task SearchTermMatches_Is_A_No_Op_That_Always_Returns_True()
{
// Pins the WPF-parity contract: the search pane drives its own results via the
// ILSpyX search strategies; LanguageSettings.SearchTermMatches deliberately ignores
// SearchTerm so the assembly-tree filter cascade stays independent of the search
// pane. Without this, typing a term into the search box would hide tree rows whose
// names don't match it — including member rows under a type whose own name DOES
// match — because the cascade only resets the match bit one level deep.
var settings = AppComposition.Current.GetExport<SettingsService>().SessionSettings.LanguageSettings;
try
{
settings.SearchTerm = string.Empty;
settings.SearchTermMatches("Object").Should().BeTrue();
settings.SearchTermMatches("IEnumerable").Should().BeTrue();
settings.SearchTermMatches(string.Empty).Should().BeTrue();
return Task.CompletedTask;
}
[AvaloniaTest]
public Task SearchTermMatches_Is_Case_Insensitive_Substring()
{
var settings = AppComposition.Current.GetExport<SettingsService>().SessionSettings.LanguageSettings;
settings.SearchTerm = "enum";
try
{
settings.SearchTermMatches("IEnumerable").Should().BeTrue(
"contains check is case-insensitive — IEnumerable contains 'enum'");
settings.SearchTermMatches("Enumerator").Should().BeTrue();
settings.SearchTermMatches("Object").Should().BeFalse(
"Object does not contain 'enum'");
settings.SearchTermMatches("IEnumerable").Should().BeTrue();
settings.SearchTermMatches("Object").Should().BeTrue(
"WPF parity: SearchTermMatches must NOT honour the SearchTerm — it's a no-op shim");
settings.SearchTerm = "ZZZ_NoMatchAnywhere";
settings.SearchTermMatches("Anything").Should().BeTrue();
}
finally
{
@ -66,8 +67,14 @@ public class SearchTermFilterTests @@ -66,8 +67,14 @@ public class SearchTermFilterTests
}
[AvaloniaTest]
public Task Typing_In_The_Search_Pane_Pushes_The_Term_Into_LanguageSettings()
public Task Typing_In_The_Search_Pane_Does_Not_Bleed_Into_LanguageSettings_SearchTerm()
{
// The search pane is decoupled from the assembly-tree filter cascade. Earlier port
// commits pushed SearchPaneModel.SearchTerm into LanguageSettings.SearchTerm to drive
// a tree-filter cascade — that path hid member rows users expected to see (e.g. enum
// literals under their matched-by-name enum type). Reverted to WPF parity: typing in
// the search pane drives the orchestrator only.
var search = AppComposition.Current.GetExport<SearchPaneModel>();
var settings = AppComposition.Current.GetExport<SettingsService>().SessionSettings.LanguageSettings;
settings.SearchTerm.Should().Be(string.Empty,
@ -76,8 +83,8 @@ public class SearchTermFilterTests @@ -76,8 +83,8 @@ public class SearchTermFilterTests
search.SearchTerm = "Enumerable";
try
{
settings.SearchTerm.Should().Be("Enumerable",
"SearchPaneModel must push the term to LanguageSettings so the assembly-tree filter cascade activates");
settings.SearchTerm.Should().Be(string.Empty,
"SearchPaneModel must not bleed its term into LanguageSettings; the cascade stays independent");
}
finally
{

19
ILSpy/LanguageSettings.cs

@ -96,16 +96,15 @@ namespace ILSpy @@ -96,16 +96,15 @@ namespace ILSpy
public partial string SearchTerm { get; set; } = string.Empty;
/// <summary>
/// Case-insensitive substring match against the active <see cref="SearchTerm"/>.
/// Empty term matches anything so the assembly tree shows its full contents when
/// no search is active.
/// Tree-filter no-op. The search pane runs against the loaded assemblies directly
/// (see <see cref="ICSharpCode.ILSpyX.Search.MemberSearchStrategy"/> + friends);
/// piping its term through the assembly-tree filter cascade would hide member rows
/// whose names don't contain the term — even when their parent type *does* match —
/// because the cascade only resets the "match" bit one level deep. The matcher
/// stays as a parameter-taking shim so existing <c>FieldTreeNode.Filter</c> /
/// <c>MethodTreeNode.Filter</c> / etc. overrides keep their structure (the
/// <c>ShowApiLevel</c> + <c>ShowMember</c> checks they wrap are still meaningful).
/// </summary>
public bool SearchTermMatches(string value)
{
var term = SearchTerm;
if (string.IsNullOrEmpty(term))
return true;
return value.Contains(term, System.StringComparison.OrdinalIgnoreCase);
}
public bool SearchTermMatches(string value) => true;
}
}

8
ILSpy/Search/SearchPaneModel.cs

@ -162,14 +162,6 @@ namespace ILSpy.Search @@ -162,14 +162,6 @@ namespace ILSpy.Search
IsSearching = false;
var term = SearchTerm ?? string.Empty;
// Push the term into LanguageSettings so the assembly-tree filter cascade
// (every Filter override calls SearchTermMatches) hides non-matching rows
// while a search is active. Clearing the term restores the full tree.
var settings = TryGetSettings()?.SessionSettings?.LanguageSettings;
if (settings != null)
settings.SearchTerm = term;
if (string.IsNullOrWhiteSpace(term))
return;

Loading…
Cancel
Save