From 4c347ef6b99d3f050df6da0d2a4eb7ee009c2b3b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 10 May 2026 20:17:15 +0200 Subject: [PATCH] Refactor RefreshInternal: HandleExceptions, FindAssembly, race-safe selection. Three small follow-ups to #3705's lazy-resource fix: * Replace `_ = RefreshInternalAsync()` with `RefreshInternalAsync().HandleExceptions()`. The previous fire-and- forget discard silently swallowed any exception thrown outside the explicit Catch helper on GetMetadataFileAsync; HandleExceptions is the codebase's idiomatic helper that surfaces the failure into the decompile text view. * Look up the path-root assembly via AssemblyList.FindAssembly (an O(1) hash lookup keyed by OrdinalIgnoreCase, with Path.GetFullPath canonicalisation) instead of an O(n) foreach with case-sensitive string equality. Same shape used by the rest of the codebase. * Detect the user navigating during the GetMetadataFileAsync await (which can take ~2s for a sizeable WPF assembly) and bail out of the path-restore. Without this, clicking another tree node mid- refresh would be silently overwritten when the deferred SelectNode jumped back to the captured pre-refresh path. No behavior change for the .baml refresh fix itself. Co-Authored-By: Claude Opus 4.7 (1M context) --- ILSpy/AssemblyTree/AssemblyTreeModel.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ILSpy/AssemblyTree/AssemblyTreeModel.cs b/ILSpy/AssemblyTree/AssemblyTreeModel.cs index c2de7768e..cdcb8dac7 100644 --- a/ILSpy/AssemblyTree/AssemblyTreeModel.cs +++ b/ILSpy/AssemblyTree/AssemblyTreeModel.cs @@ -947,7 +947,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree private void RefreshInternal() { - _ = RefreshInternalAsync(); + RefreshInternalAsync().HandleExceptions(); } private async Task RefreshInternalAsync() @@ -958,15 +958,25 @@ namespace ICSharpCode.ILSpy.AssemblyTree ShowAssemblyList(settingsService.AssemblyListManager.LoadList(AssemblyList.ListName)); - // Ensure assembly loaded before FindNodeByPath to allow lazy-loaded resource nodes to be found + // Ensure the assembly is loaded before FindNodeByPath, so lazy-loaded + // resource nodes (e.g. .baml entries) are present in the tree. if (path?.Length > 0) { - foreach (var asm in AssemblyList.GetAssemblies()) + var rootAssembly = AssemblyList.FindAssembly(path[0]); + if (rootAssembly != null) { - if (asm.FileName == path[0]) + // FindNodeByPath() blocks the UI if the assembly is not yet loaded, + // so use an async wait instead. + var preAwaitSelection = SelectedItem; + await rootAssembly.GetMetadataFileAsync().Catch(_ => { }); + + // If the user navigated to a different node while the assembly + // was loading, respect that — don't restore the pre-refresh path. + // A change to null counts too (e.g. user cleared the selection). + if (!ReferenceEquals(SelectedItem, preAwaitSelection)) { - await asm.GetMetadataFileAsync().Catch(_ => { }); - break; + RefreshDecompiledView(); + return; } } }