Splits Windows-only tests out of the cross-platform ILSpy.Avalonia.Tests
project so the Pdb2Xml / OpenFromGac code paths (which compile only on
Windows) can be exercised without conditional `#if WINDOWS` blocks inside
the main test suite.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Lets non-decompile-protocol EntityReference hyperlinks route through plug-in
resolvers instead of always falling into FindTreeNode. The dispatcher in
DockWorkspace.OnNavigateRequested checks `entity.Protocol != "decompile"`,
walks every IProtocolHandler MEF export, and lands the user on the first
non-null tree node returned.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stub in OpenFromGacCommand with a real
dialog. CanExecute gates on OperatingSystem.IsWindows() since the Windows
Global Assembly Cache doesn't exist on Linux/macOS (matches WPF's
AppEnvironment.IsWindows gate). The actual GAC enumeration uses the shared
UniversalAssemblyResolver.EnumerateGac() + GetAssemblyInGac() pair from
ICSharpCode.Decompiler — no platform-specific code in the Avalonia tree.
Assisted-by: Claude:claude-opus-4-7:Claude Code
WPF ships Pdb2XmlCommand behind `#if DEBUG && WINDOWS` because
Microsoft.DiaSymReader uses native COM interop. Avalonia parity uses the
same gate, with the package references conditional on the build host being
Windows + Debug, and the consuming command file `#if DEBUG && WINDOWS`.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stub in FileCommands.cs's
ManageAssemblyListsCommand with a real File → Manage Assembly Lists dialog.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click an assembly → Select PDB. File-pick a portable / Windows PDB,
call LoadedAssembly.LoadDebugInfo, and refresh the active tree selection
so the new debug symbols feed back into the decompiler output.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds BindTree_With_Small_AssemblyList_Settles_In_Reasonable_Time alongside
the existing 200-assembly [Explicit] benchmark. Not [Explicit] so it runs
in the regular suite; 8 copies of CoreLib with a 15s threshold catches
order-of-magnitude regressions in the open+settle pipeline without the
30s+ cost of the full benchmark.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Single assertion guarding the existing IDeferredContentPresentation.DeferContentPresentation
override that keeps tool panes (search, analyzers, debug-steps, …) hot at
startup. Without the opt-out, headless tests can't reach pane descendants
until focus-activation, and the search pane's startup tasks (assembly index
scan etc.) wouldn't kick off until first user interaction.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Same shape as DecompileAllCommand but routes through ILLanguage. Writes to
c:\temp\disassembled\<safe-name>.il in parallel, reports per-assembly time
and exception type, with CanExecute gating on the output dir existing.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stubs in FileCommands.cs with real
implementations. Both are DEBUG-only File-menu entries for decompiler dev:
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click one or more assemblies with CodeView debug-directory entries →
Generate Portable PDB. Folder picker for output, then PortablePdbWriter
runs per assembly under DockWorkspace.RunWithCancellation; the report tab
lists per-file success / fail / unsupported plus total elapsed time + an
Open Explorer button.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Ctrl+M toggles the innermost fold containing the caret. Ctrl+Shift+M collapses
all when any fold is open and expands all otherwise (parity-based toggle).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Standard popup convention; the chips/pills inside don't override KeyDown so
the Tunnel-routed handler on the popup root catches the key before any
inner control. Arrow-key navigation between chips is a bigger lift (chip
groups don't share a focus container) — leaving as a follow-up.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The funnel glyph is a hand-drawn 10×9 trapezoid with no associated text, so
its purpose isn't obvious to a user who hasn't seen the active state. Adding
a tooltip on the funnel's hit-area Border surfaces the column name when the
user hovers (Avalonia 11+ ToolTip.SetTip).
Assisted-by: Claude:claude-opus-4-7:Claude Code
When a tree node becomes visible for the first time, its already-realised
children might carry stale IsHidden state — OnChildrenChanged only fires
for newly-added children, and only when the parent was visible at the time.
The visible result: a property's accessor children read as visible the first
time the user expands the property even under ShowApiLevel=PublicOnly, until
something else (refresh / language flip) forces a cascade.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Pair to ea56a80cd. When DecompilerSettings.ExtensionMembers is on and the
active language is C#, methods that the decompiler identifies as implementation
methods for an extension block are filtered out of the outer static class —
they surface as ExtensionTreeNode children of the containing static class
instead. Without this gate the user would see each extension member twice:
once as a regular method on the static container, once under the extension
block. Mirrors WPF MethodTreeNode.Filter lines 95-104 verbatim.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Each `extension<T>(ReceiverType source) { ... }` block inside a static class
now appears as its own tree node sibling to the regular methods/properties.
Expanding the node lists the block's declared methods + properties as
PropertyTreeNode / MethodTreeNode children sorted by name.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors WPF's `ShowCFGContextMenuEntry` and `GraphVizGraph` utility behind
`#if DEBUG`. Right-click a `BlockContainer` reference in the IL output
("DEBUG -- Show CFG") → builds the ControlFlowGraph for the method, writes
a `.gv` file in the temp dir, shells out to `dot` (Graphviz) to render PNG,
opens the result with the OS default image viewer.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click → "Extract Package Entry" on items inside a NuGet zip or .NET
single-file bundle. Single-item selection (an AssemblyTreeNode or a
ResourceTreeNode) pops a SaveFileDialog; multi-item or PackageFolderTreeNode
selection pops a folder-picker. Runs the extraction on a background thread
through the new DockWorkspace.RunWithCancellation infrastructure, then
ShowText reports per-entry write paths + total elapsed time + an "Open
Explorer" button that selects the result in the OS file manager.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click an assembly tree node → Create Diagram. Prompts for an output
folder via the new FilePickers.PickFolderAsync helper, then runs the shared
ICSharpCode.ILSpyX.MermaidDiagrammer.GenerateHtmlDiagrammer engine on a
background thread under DockWorkspace.RunWithCancellation (the new wait UI
with a custom "Creating diagram…" title). On completion, pushes a report
into the active tab via ShowText — elapsed time, learn-more link, and an
"Open Explorer" button that selects the generated index.html in the OS file
manager (xdg-open / open -R / explorer.exe per platform).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the WPF-equivalent "run a long-running task with the wait UI showing a
custom title, then display the resulting text in the editor" primitives. Both
methods live on DecompilerTabPageModel (the natural owner — it's where the
wait adorner and editor state already live); DockWorkspace exposes thin
delegates that forward to the active tab.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors WPF's ZoomScrollViewer behaviour without templating AvaloniaEdit's
TextEditor. The Avalonia path scales the editor's FontSize (which feeds
DisplaySettings.SelectedFontSize) on Ctrl+Wheel; AvaloniaEdit lays out at the
new size on the next render. WPF's full-content-tree LayoutTransform approach
doesn't fit AvaloniaEdit's architecture, but the font-scale result is what
users actually expect from "editor zoom".
Assisted-by: Claude:claude-opus-4-7:Claude Code
Foldings persistence rounds out the view-state work that landed in 185e0551d
(caret + scroll). Mirrors WPF's DecompilerTextViewState.SaveFoldingsState /
RestoreFoldings semantics verbatim — including the layout-checksum gate that
refuses restoration when the new document's foldings don't match the captured
layout, so a stale snapshot can't accidentally expand random regions of a
shifted document.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds ICSharpCode.BamlDecompiler as a ProjectReference, ports IResourceFileHandler +
ResourceFileHandlerContext from WPF, and plumbs both through:
Assisted-by: Claude:claude-opus-4-7:Claude Code
Closes two tracker items in one commit:
- `BrowseForwardCommand` was already wired end-to-end (command, toolbar
button, Alt+Right key binding). New test pins the wiring so a future
regression to any of the three surfaces is caught.
- `ViewState (caret + scroll persistence across navigation)` was missing.
Navigating back to a previously-visited node landed the caret at offset
0 / scroll at top instead of where the user was looking before they
moved away.
Promotes the modern .NET XML-doc lookup from the Avalonia port into the
shared ICSharpCode.Decompiler library so every host (WPF, Avalonia, any
third-party consumer of XmlDocLoader) gets hover/tooltip documentation
for system entities without per-host fallback wiring.
The decompiler-view hover tooltip's XML-documentation lookup goes through
the shared ICSharpCode.Decompiler.Documentation.XmlDocLoader. That helper
only knows two layouts: .xml beside the .dll, and the .NET Framework
reference-assemblies paths (Program Files (x86)\Reference Assemblies\
Microsoft\Framework\v1.0 … v4.8.1). Modern .NET runtime DLLs
(System.Private.CoreLib.dll, System.Linq.dll, …) ship without any .xml
beside them — the XML files live in the parallel reference pack at
Closes the third logged analyzer follow-up. When the user double-clicks an
analyzer result (e.g. one of the methods listed under "Used By" on Foo.Bar),
the decompiled-output view now paints local-reference marks on every
mention of the originally-analysed entity inside the navigated-to body —
so the user can spot the exact call site the result row corresponds to.
Two of the three analyzer follow-ups I'd logged from earlier live-testing.
Both are tiny, both close gaps that made the analyzer feature feel
half-finished:
Brings the assembly-comparison feature across. Right-click two valid
managed assemblies in the tree → "Compare..." → fresh tab opens with a
diff tree: identical entries hidden by default, additions tinted green,
removals pink, signature-matched-but-different rows blue. Identical
entries surface via the "Show identical entries" toolbar checkbox.
Brings the assembly-to-project export across. Right-click an assembly in
the tree (or use the existing File → Save Code / Ctrl+S that calls into
`node.Save()`) and choose between two filters in the save picker:
Brings the WPF decompiler-pipeline-stepper feature across. Available only
in Debug builds — the entire feature set is gated behind `#if DEBUG`, so
Release users see neither the pane nor the ILAst languages in the picker.
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.
Refactor of the MessageBus port: CurrentAssemblyListChangedEventArgs,
TabPagesCollectionChangedEventArgs, and SettingsChangedEventArgs now
share a generic base — abstract WrappedEventArgs<T>(T inner) : EventArgs
exposing an explicit `Inner` property. The three subclasses collapse to
one-liners with no extra body, and consumers read .Inner uniformly
across all three. No implicit conversion operator — the unwrap is
always visible at the read site.
Brings WPF's MessageBus pattern to the Avalonia port so future features
(updater banner, navigate-to-reference plumbing, cross-pane settings
notifications) can fan out without each subscriber importing every
publisher's class. Infrastructure only — no existing Avalonia code wires
the bus yet; this commit makes it available.
The pane's watermark advertises a prefix DSL that the parser didn't
implement — only inassembly: and innamespace: were honoured. Typing
"t:String" treated the whole thing as a literal keyword and matched
nothing useful.
The PE TypeDef table includes a <Module> pseudo-type plus any user-declared
top-level types in the empty namespace. Until now AssemblyTreeNode skipped
the empty namespace when building NamespaceTreeNodes and instead hung the
global types directly off the assembly node, breaking the long-standing
tree shape (every type lives under a namespace folder).
MemberSearchStrategy.Search uses language.GetEntityName(module, handle, ...)
as a cheap pre-filter: read the entity name straight from metadata, run the
caller's match (Contains / Equals / Regex), and ONLY when it matches do the
expensive typeSystem.MainModule.GetDefinition(handle) resolve. The contract
is "this must be cheap because it's called for every type / method / field /
property / event handle in the module."
Search felt much slower than the WPF baseline despite Parallel.ForEachAsync
across producers. The slowness wasn't in the producer — it was in the drain:
a 50ms Task.Delay polling loop that posted batches to the UI thread via
Dispatcher.UIThread.Post, with no per-tick time budget and append-order
insertion. That meant up-to-50ms of latency before each visible batch, an
extra thread-hop per batch, and the most relevant hit sitting buried behind
arrival-order results until the run finished.