Browse Source

Keep decompiler tab title in sync with selected tree node text

Assisted-by: Claude:claude-opus-4-7:Claude Code

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
0cb6968ce4
  1. 35
      ILSpy.Tests/Editor/DecompilerViewTests.cs
  2. 20
      ILSpy/TextView/DecompilerTabPageModel.cs

35
ILSpy.Tests/Editor/DecompilerViewTests.cs

@ -23,7 +23,10 @@ using Avalonia.Headless.NUnit; @@ -23,7 +23,10 @@ using Avalonia.Headless.NUnit;
using AwesomeAssertions;
using ICSharpCode.ILSpy.Properties;
using ILSpy.AppEnv;
using ILSpy.Commands;
using ILSpy.TreeNodes;
using ILSpy.ViewModels;
using ILSpy.Views;
@ -185,6 +188,38 @@ public class DecompilerViewTests @@ -185,6 +188,38 @@ public class DecompilerViewTests
tab.Text.Should().Contain("SupportedOSPlatformAttribute");
}
[AvaloniaTest]
public async Task Decompiler_Tab_Title_Tracks_Tree_Node_Text_When_Node_Loads_Late()
{
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
// Open a fresh assembly via the command and immediately let the OpenCommand select it,
// before LoadedAssembly.Text has the rich "(version, tfm)" suffix wired up. This is the
// timing window where the tab title used to lock in the bare ShortName.
var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location;
var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>();
var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value;
openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() =>
vm.AssemblyTreeModel.SelectedItem is AssemblyTreeNode n
&& string.Equals(n.LoadedAssembly.FileName, newAsmPath, System.StringComparison.OrdinalIgnoreCase));
var node = (AssemblyTreeNode)vm.AssemblyTreeModel.SelectedItem!;
await node.LoadedAssembly.GetLoadResultAsync();
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
await Waiters.WaitForAsync(() => string.Equals(tab.Title, node.Text?.ToString(), System.StringComparison.Ordinal));
node.Text!.ToString().Should().NotBe(node.LoadedAssembly.ShortName,
"the rich form (with version + tfm) must be available, otherwise the test isn't exercising the late-update path");
tab.Title.Should().Be(node.Text!.ToString());
}
[AvaloniaTest]
public async Task Selecting_Assembly_Node_Emits_Header_And_Assembly_Attributes()
{

20
ILSpy/TextView/DecompilerTabPageModel.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
@ -120,11 +121,24 @@ namespace ILSpy.TextView @@ -120,11 +121,24 @@ namespace ILSpy.TextView
set {
if (currentNode == value)
return;
if (currentNode != null)
currentNode.PropertyChanged -= OnCurrentNodePropertyChanged;
currentNode = value;
if (currentNode != null)
currentNode.PropertyChanged += OnCurrentNodePropertyChanged;
_ = DecompileAsync();
}
}
void OnCurrentNodePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
// Tree-node Text can change after we capture the title (e.g. AssemblyTreeNode swaps
// from ShortName to "ShortName (version, tfm)" once the assembly finishes loading).
// Re-read it whenever the node fires Text so the tab header stays in sync.
if (e.PropertyName == nameof(ILSpyTreeNode.Text) && sender is ILSpyTreeNode node)
Title = node.Text?.ToString() ?? "(unnamed)";
}
public DecompilerTabPageModel()
{
Title = "Empty";
@ -145,7 +159,6 @@ namespace ILSpy.TextView @@ -145,7 +159,6 @@ namespace ILSpy.TextView
return;
}
var nodeTitle = node.Text?.ToString() ?? "(unnamed)";
var newSyntaxExtension = language.FileExtension;
IsDecompiling = true;
@ -182,7 +195,10 @@ namespace ILSpy.TextView @@ -182,7 +195,10 @@ namespace ILSpy.TextView
var collectedLookup = output.DefinitionLookup;
var collectedUIElements = output.UIElements;
await Dispatcher.UIThread.InvokeAsync(() => {
Title = nodeTitle;
// Re-read Text now (instead of capturing it before decompile started) — for
// freshly-opened assemblies, Text only has the rich "(version, tfm)" form
// after the load completes during decompile.
Title = currentNode?.Text?.ToString() ?? "(unnamed)";
SyntaxExtension = newSyntaxExtension;
HighlightingModel = model;
Foldings = collectedFoldings;

Loading…
Cancel
Save