Fix package search and navigation follow-ups from #4023 - #4025
Merged
Merged
Conversation
Searching a package resolves its entries by file name against a case-insensitive cache, which is right for an assembly reference but wrong for an archive entry: two entries differing only in case are two files, and they collapsed onto one LoadedAssembly, so one was searched twice and the other never. Keying the cache by the entry itself separates them, and the entry's package-relative path becomes the assembly's file name, which is what tells the copies of one assembly in a multi-target package apart wherever a search result shows a location. Cancellation was only checked between top-level list entries, so a walk the user had already replaced by typing another character kept extracting package entries alongside the run they were waiting for. The omnibar had no way to end its run at all: its view model is per document tab and nothing cancelled it when the tab went away. Assisted-by: Claude:claude-opus-5[1m]:Claude Code
Resolving a metadata file to its assembly node learned to descend into packages, but three sibling lookups kept their own scan of the root's direct children, so a token reference, a metadata:// link and a LoadedAssembly reference still resolved to nothing inside a package. One of them sat behind a guard whose result was never used, which returned early for exactly the case it was meant to serve. Routing all of them through the one lookup fixes them together. Namespaces were matched by comparing a full name against a node label, which is only ever equal in flat mode: with nested namespace nodes the label is the last segment, and the empty-name test matched the first child rather than the global namespace node. The assembly node already indexes its namespaces by full name. The descent itself no longer sweeps the package depth-first. Expanding a folder resolves and extracts every .dll it holds, so the path is taken from the package's folder graph, which costs no tree node and reads no entry. Assisted-by: Claude:claude-opus-5[1m]:Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-ups to #4023, from a review of that PR after it merged. Two commits, one per half of the original change; no behaviour from #4023 is reverted.
Searching a package (
3dfb59ac8)PackageFolder's cache is keyed case-insensitively, which is correct for an assembly reference but not for an archive entry:lib/net10.0/a.dllandlib/net10.0/A.dllare two files and shared oneLoadedAssembly, so one was searched twice and the other never. The cache is now keyed by the entry itself;ResolveFileNamekeeps case-insensitive name lookup for reference resolution.lib/net472/X.dll,lib/net8.0/X.dllandlib/netstandard2.0/X.dllare three assemblies, and every search hit in them showedX.dllas its location. A nested assembly's file name is now the package-relative path.ShortNameand the tree label are unchanged (GetFileNameWithoutExtensionhandles the path).Reaching a package-nested node (
482c0bd81)FindTreeNode'sMetadataFilearm learned to descend into packages; itsLoadedAssemblyarm,MetadataNavigator.FindTableNode, andMetadataProtocolHandlerkept their own scan of the root's direct children, so a token reference, ametadata://link and aLoadedAssemblyreference still resolved to nothing inside a package. All now route through the one lookup.MetadataNavigator.ResolveRowToTreeNodereturned early for the case it was meant to serve. ItsGetAssemblies()guard is top-level-only and its result was never used, so double-clicking a metadata row under a package did nothing.FindNamespaceNode's namespace half compared a full name against a node label. Those are only ever equal in flat mode; with nested namespace nodes the label is the last segment, so"System.Collections.Generic" == "System"failed, and the empty-name test matched the first child rather than the global-namespace node. The assembly node already indexes its namespaces by full name..dllit holds, so reaching one assembly built the folders ordered ahead of it too. The path now comes from the package's in-memory folder graph, which costs no tree node and reads no entry.Tests
Four new tests in
AssemblyTreeModelTests, over a two-assembly zip: the walk yields both entries with their paths, a cancelled walk stops before the next entry, navigation lands on a node whose module is the nested assembly (the fixture's type handle is0x02000002and resolves in nearly every assembly, so asserting the node type alone cannot fail), and the sibling folder is left unexpanded.ILSpy.Tests1210 passed / 0 failed,ICSharpCode.ILSpyCmd.Tests22 passed / 0 failed, clean rebuild ofILSpy.Desktop.slnfandILSpy.XPlat.slnfwith no warnings.Opened by an AI agent (Claude) on Siegfried's behalf.
🤖 Generated with Claude Code