Dock's ActiveDockable setter re-runs InitActiveDockable, and with it
SetFocusedDockable, even when the value does not change, so activating
the already-active document moved the active-pane highlight to the
documents dock. Two callers guarded against that individually; the
factory override does it for every caller, and ActivateAndFocus keeps
its explicit SetFocusedDockable for the callers that do want the focus.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
The tree selection binder focused the selected row on every model-driven
change, so Back/Forward, search-result jumps, go-to-definition, tab
activation and omnibar picks all pulled the keyboard focus into the
assembly tree. WPF's SelectNodes only scrolled the row into view; the
row took the focus when the pane itself was activated. The binder now
checks where the keyboard focus sits when the selection changes and
leaves it alone unless the tree already holds it. The Analyzer pane
keeps focusing its row, as its WPF counterpart did on every selection
change, so an Analyze request still lands the user in the pane.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
The only Ctrl+R binding sat on the window and always analyzed the
assembly tree's selection, so pressing it on a result row in the
Analyzer pane promoted the wrong entity, or nothing. WPF ILSpy bound the
key on the pane as well. Avalonia walks KeyBindings from the focused
element up to the window, so a pane-level binding takes precedence while
the focus is in the pane and falls through when it has nothing to
analyze. The command takes the selection as its parameter so both
bindings share one implementation.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
The assembly tree had Thunderbird-style context targeting (right-click a
row outside the selection without moving the selection, highlight the
target, re-focus the row after a keyboard-invoked menu); the Analyzer
pane only rebuilt the menu from its selection, so a right-click on
another row opened the menu for the previous selection. Hosting the
behaviour in one controller keyed on SharpTreeView gives both panes the
same gestures and keeps the assembly pane down to its middle-click
open-in-new-tab handler.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
IsVisible required every selected node to be a member node while
IsEnabled filtered to the member subset first, so a mixed selection
reported itself enabled-but-hidden. Hidden won, so nothing was visible
to the user, but the two predicates disagreed about the same selection.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
Dynamic and token-writer reference paths can receive method-group resolve results directly. Returning the first method keeps ResolveResult.GetSymbol aligned with the existing method-group handling used by AST annotations.
Assisted-by: OpenAI:openai/gpt-5.5:OpenCode
Every other context-menu entry names its header by resource key so the
menu builder can localise it; this one shipped the raw string, which
WPF ILSpy did as well. The "Remove" string already exists in the resx.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
AvaloniaEdit marks Tab handled while AcceptsTab is on, so the keyboard
focus could never leave the read-only editor. WPF ILSpy removed the
TabForward/TabBackward editing commands for the same reason.
#4042
Assisted-by: Claude:claude-fable-5-1:Claude Code
Dynamic member tooltips should describe the visible static target, while punctuation should stay reserved for useful call targets such as constructors. This keeps dynamic object creation tooltips on the constructor parentheses but avoids noisy synthetic member references on dynamic invocation parentheses.
Assisted-by: OpenAI:openai/gpt-5.5:OpenCode
One assembly that failed to load faulted the whole sweep, which skipped
the command requery at its end. 'Remove assemblies with load errors' was
therefore not re-evaluated in the one case it exists for.
ShowAssemblyList(AssemblyList) is internal so the test can show a list
whose load it controls.
Assisted-by: Claude:claude-fable-5-1:Claude Code
The ancestor binding logged 'Ancestor not found' whenever a row's content
was evaluated outside a SharpTreeView. An inherited property needs no
lookup, and leaves the lines on where there is no tree view at all.
Assisted-by: Claude:claude-fable-5-1:Claude Code
With a DataContext binding of its own the panel first inherited the main
window's view model, and each of its compiled bindings failed the cast to
UpdatePanelViewModel and logged an error at startup. The new test boots the
window with a capturing log sink and fails on any binding error.
Assisted-by: Claude:claude-fable-5-1:Claude Code
Dock's theme binds DockCapabilityOverrides.CanDrag/CanDrop on the tool and
document docks themselves, not only on the panes inside them, and logged a
binding error per property at startup. Filling them in from InitLayout
covers layouts restored from disk, which never pass through CreateLayout.
Assisted-by: Claude:claude-fable-5-1:Claude Code
Use the PDB table row width for StateMachineMethod external MethodDef references, and include method-level custom debug information in decoded PDB output. Also report a missing Portable PDB explicitly when a debug metadata table is requested from an assembly without one.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
The stale-node guard compared selected nodes only against top-level assembly-list entries, so nodes owned by package entries looked removed even while their parent package was active. Walk the parent bundle chain before clearing the decompiler tab so restored or clicked package-entry selections still render.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
The PDB dumper opened the outer package path directly, so --bundle-entry was ignored while dumping debug information. Load package inputs through the shared entry resolver and pass Cecil the selected assembly image while keeping ordinary files on the path-based code path for adjacent PDB discovery.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Roslyn can lower mostly constant arrays with a static data blob and leave non-constant elements as explicit stores. Merge those trailing stores into the decoded initializer so the decompiler keeps a single array initializer instead of splitting the expression.
Assisted-by: OpenCode:openai/gpt-5.5:opencode
Raw metadata table readers compensate for SRM APIs that do not expose every table shape directly, so keeping them separate from general metadata helpers makes that boundary explicit.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Metadata can contain private virtual members that C# cannot declare directly. Adjusting the emitted accessibility in the decompiler keeps such members compilable without pushing the policy into the general AST builder.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
AccessedThroughProperty-backed controls need to remain addressable as fields so WinForms initialization can use compiler-generated backing storage without hiding the setter that maintains WithEvents subscriptions.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Debug and Release builds preserve different local names in the test assembly PDB. The dump tests only need to prove that named locals are emitted, so allow either stable name observed in CI instead of binding the assertion to one configuration.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Captured locals can first be named while visiting a nested local function even though their declaring ILFunction is the parent. Reserving such generated names only in the nested scope lets sibling captured locals reuse the same name and later collide when declarations are hoisted.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Release builds may optimize away an otherwise unused local even when the PDB still carries local-variable information for active test methods. Assert on a local that survives the Release test assembly so the dump coverage is not tied to optimizer behavior.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
PdbDumper reads both Portable and Windows PDBs, but only the Portable path
had tests: Mono.Cecil's native PDB writer is COM-only, so the format cannot
be produced off Windows. The fixture is compiled here by the in-box C#
compiler, which still emits it, and the test asserts the PDB really is the
native format before reading anything out of it - otherwise the suite would
silently re-cover the Portable path.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
The Pdb2Xml command that just left the app was the only way to see a PDB's
contents, and it was Windows-only. Mono.Cecil's symbol readers cover the
Portable and the Windows (native MSF) format in managed code and pick the
right one themselves, so a single dump path serves both on every OS.
Cecil's object model, rather than raw metadata, is what makes this work for
Windows PDBs at all: they have no metadata tables for --dump-table to read.
That also settles the split between the two options - --dump-table stays the
raw row view of a Portable PDB, --dump-pdb is the decoded one.
Neither symbol reader exposes the document list, so the documents are
collected from the sequence points that reference them.
Assisted-by: Claude:claude-opus-5:Claude Code
The metadata-table dumper only knew the Cor tables, so the debug metadata a
Portable PDB carries had no command-line view at all; the GUI has shown those
eight tables for years. Moving the dumper into ILSpyX puts it next to the PDB
providers it now needs and makes it available to any host, not just ilspycmd.
Two things a user would otherwise trip over: a standalone .pdb is accepted as
an input file, and asking an assembly for a debug table resolves the embedded
or adjacent PDB instead of reporting an empty table. Reading a debug table
without either would silently print nothing at all.
The GUI's StateMachineMethod reader moves to a shared extension. Its own copy
read the MethodDef column with ReadInt16 and switched to the wide form at
ushort.MaxValue rather than 1 << 16, so a large enough method table would have
produced negative RIDs.
Assisted-by: Claude:claude-opus-5:Claude Code
Microsoft.DiaSymReader.Converter.Xml was the only reason the app referenced
the DiaSymReader chain, and it dragged the NETStandard.Library 1.6.1 shim set
with it: 77 packages in ILSpy's lock file, several of them needing security
overrides to keep NuGet audit quiet. The command itself only ever ran on
Windows, because the converter reaches into native COM.
Nothing is lost that the tooling cannot do without it. ilspycmd prints the
metadata tables, and Mono.Cecil's symbol readers - already a dependency, and
already what DebugInfoUtils falls back to for a Windows PDB - read both PDB
formats in managed code on every OS.
Assisted-by: Claude:claude-opus-5:Claude Code
Null propagation must not render an extension-method conditional access when normal member lookup would bind to a non-method member, because that produces invalid C# for ambiguous Count-style cases.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
AvaloniaEdit inserts the line number margin before ILSpy's bookmark gutter and can render it before its cached font size is initialized. Keep the gutter order stable after live toggles and prepare the margin before the first render.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Recognize Roslyn's null-terminated backing-store shape for u8 literals so escaped control bytes decompile back to UTF-8 string literal syntax, fix LdStrUtf8 to infer ReadOnlySpan<byte> in both the template and generated code, and cover overload-resolution cases where the explicit span conversion must be preserved.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Match the WPF shortcut model by binding Ctrl+R at the window level while keeping the context menu as a separate surface over the same analyze logic.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Batched selection fan-out can briefly replay a selection for nodes whose assemblies were already removed. Ignore those stale selections and version decompiler-tab updates so late spinner or output continuations cannot overwrite an explicit clear.
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
A selection change reaches a full application-wide command re-query, a
session-settings write, a message-bus broadcast and a decompile of the
new selection. The model already had a flag to hold that off during a
bulk edit, but only its own SelectNodes ever set it: the binder that
mirrors the tree's selection into the model added and removed one node at
a time, so selecting every row cost one fan-out per row.
The flag becomes a scope any bulk edit can enter, and the binder uses it.
Membership now comes from a set rather than a scan of the model selection
per node, which was quadratic once a large part of the tree was selected.
DockWorkspace subscribed to both the selection collection and the
SelectedItem property, so it showed the selection twice per change and
once per node during a bulk edit; the property already fires when the
selection settles, so the collection subscription goes.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
Deleting assemblies one at a time is not equivalent to deleting them
together: each removal raises its own collection change, and the
listeners of the assembly list react to one of those by pruning
navigation history, restarting a running search, re-querying every bound
command and re-decompiling whatever is still selected. Selecting an
expanded list and pressing Delete therefore paid for all of that once per
assembly, and froze the UI.
Emptying the list still reports a Remove rather than the Reset that
Clear() raises, because consumers read Reset as "re-read everything" and
several short-circuit their per-removal cleanup on it, which would leave
tabs and navigation history pointing at unloaded assemblies.
The tree's removal is reordered for the same reason: it detached and
announced one node at a time, so a listener reacting to the first
notification still found the rest of the same removal attached and took
them for a selection the user had made.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Assisted-by: OpenCode:openai/gpt-5.5:OpenCode