Browse Source

Open the real page for "Decompile to new tab" on custom-content nodes

The command always built a decompiler tab and force-decompiled the
node, so a metadata-table (or resource) node opened an empty code tab
instead of its grid. Route a single node through OpenNodeInNewTab, which
honours the node's CreateTab() custom content and falls back to a
decompiler tab for ordinary nodes; multi-node selections keep the
decompile-the-union behaviour.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3755/head
Siegfried Pammer 1 month ago
parent
commit
109dec52cb
  1. 36
      ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs
  2. 18
      ILSpy/Commands/DecompileInNewViewCommand.cs

36
ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs

@ -485,6 +485,42 @@ public class DecompileInNewViewTests @@ -485,6 +485,42 @@ public class DecompileInNewViewTests
"the grid must visually highlight every restored node");
}
[AvaloniaTest]
public async Task DecompileInNewView_On_A_Metadata_Table_Opens_Its_Grid_Not_A_Code_Tab()
{
// "Decompile to new tab" on a node with custom content (a metadata table) must open that
// node's own page-type — the metadata grid — not force-decompile it into an empty code tab.
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var pane = await window.WaitForComponent<AssemblyListPane>();
var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>();
var tableNode = vm.AssemblyTreeModel.FindCoreLib()
.GetChild<global::ILSpy.Metadata.MetadataTreeNode>()
.GetChild<global::ILSpy.Metadata.MetadataTablesTreeNode>()
.GetChild<global::ILSpy.Metadata.CorTables.TypeDefTableTreeNode>();
var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!;
int before = documents.VisibleDockables?.OfType<ContentTabPage>().Count() ?? 0;
var menu = pane.BuildContextMenuForCurrentState(registry.Entries, rightClickedNode: tableNode);
menu.Should().NotBeNull();
menu!.ClickItem(Resources.DecompileToNewPanel);
await Waiters.WaitForAsync(
() => documents.VisibleDockables!.OfType<ContentTabPage>()
.Any(t => t.Content is global::ILSpy.ViewModels.MetadataTablePageModel));
documents.VisibleDockables!.OfType<ContentTabPage>().Count()
.Should().BeGreaterThan(before, "a new tab must open");
var newTab = documents.VisibleDockables!.OfType<ContentTabPage>()
.First(t => t.Content is global::ILSpy.ViewModels.MetadataTablePageModel);
newTab.Content.Should().BeOfType<global::ILSpy.ViewModels.MetadataTablePageModel>(
"the metadata-table node must open its grid page, not a decompiler tab");
}
static bool RowNodeEquals(global::ILSpy.Controls.TreeView.SharpTreeViewItem row, SharpTreeNode node)
=> ReferenceEquals(row.DataContext, node);

18
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -61,13 +61,21 @@ namespace ILSpy.Commands @@ -61,13 +61,21 @@ namespace ILSpy.Commands
public void Execute(TextViewContext context)
{
var nodes = SelectedNodes(context).ToArray();
if (nodes.Length > 0)
if (nodes.Length == 1)
{
// Route a single node through OpenNodeInNewTab so nodes with custom content
// (metadata tables, resource viewers) open their own page-type instead of being
// force-decompiled into an empty code tab. Plain nodes still get a decompiler tab,
// sourced from the node so the tab/tree stay in lockstep when flipping tabs.
dockWorkspace.OpenNodeInNewTab(nodes[0]);
return;
}
if (nodes.Length > 1)
{
// Multi-node selections leave SourceNode null — no single tree row represents the
// union — and always decompile (there is no custom-content union page).
var content = new DecompilerTabPageModel { Language = languageService.CurrentLanguage };
// Single-node selection carries a SourceNode so the tab/tree stay in lockstep
// when the user flips tabs. Multi-node selections leave SourceNode null —
// no single tree row represents the union.
dockWorkspace.OpenNewTab(content, sourceNode: nodes.Length == 1 ? nodes[0] : null);
dockWorkspace.OpenNewTab(content, sourceNode: null);
content.CurrentNodes = nodes;
return;
}

Loading…
Cancel
Save