diff --git a/ICSharpCode.ILSpyX/AssemblyList.cs b/ICSharpCode.ILSpyX/AssemblyList.cs index b52e79117..ec10a4e92 100644 --- a/ICSharpCode.ILSpyX/AssemblyList.cs +++ b/ICSharpCode.ILSpyX/AssemblyList.cs @@ -281,8 +281,9 @@ namespace ICSharpCode.ILSpyX { file = Path.GetFullPath(file); return OpenAssembly(file, () => { - var newAsm = new LoadedAssembly(this, file, fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols); - newAsm.IsAutoLoaded = isAutoLoaded; + var newAsm = new LoadedAssembly(this, file, fileLoaders: manager?.LoaderRegistry, applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols) { + IsAutoLoaded = isAutoLoaded + }; return newAsm; }); } diff --git a/ILSpy/Analyzers/AnalyzeCommand.cs b/ILSpy/Analyzers/AnalyzeCommand.cs index 889e6afa0..9a24f29b2 100644 --- a/ILSpy/Analyzers/AnalyzeCommand.cs +++ b/ILSpy/Analyzers/AnalyzeCommand.cs @@ -78,12 +78,12 @@ namespace ICSharpCode.ILSpy.Analyzers public override bool CanExecute(object parameter) { - return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); + return MainWindow.Instance.AssemblyTreeModel.SelectedNodes.All(n => n is IMemberTreeNode); } public override void Execute(object parameter) { - foreach (var node in MainWindow.Instance.SelectedNodes.OfType()) + foreach (var node in MainWindow.Instance.AssemblyTreeModel.SelectedNodes.OfType()) { AnalyzerTreeView.Analyze(node.Member); } diff --git a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs index 213624ee1..4217e411c 100644 --- a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs @@ -42,7 +42,8 @@ namespace ICSharpCode.ILSpy.Analyzers MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); return; } - MainWindow.Instance.JumpToReference(new EntityReference(this.Member.ParentModule.MetadataFile, this.Member.MetadataToken)); + + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(this.Member.ParentModule?.MetadataFile, this.Member.MetadataToken))); } public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) diff --git a/ILSpy/Analyzers/AnalyzerRootNode.cs b/ILSpy/Analyzers/AnalyzerRootNode.cs index 17709687f..e1ce03438 100644 --- a/ILSpy/Analyzers/AnalyzerRootNode.cs +++ b/ILSpy/Analyzers/AnalyzerRootNode.cs @@ -2,7 +2,6 @@ using System.Collections.Specialized; using System.Linq; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; diff --git a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs index 0a7bbb95c..e0ade8666 100644 --- a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.Analyzers var context = new AnalyzerContext() { CancellationToken = ct, Language = Language, - AssemblyList = MainWindow.Instance.CurrentAssemblyList + AssemblyList = MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList }; var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory); if (context.SortResults) diff --git a/ILSpy/Analyzers/AnalyzerTreeNode.cs b/ILSpy/Analyzers/AnalyzerTreeNode.cs index 8972926a7..fb052303e 100644 --- a/ILSpy/Analyzers/AnalyzerTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerTreeNode.cs @@ -18,7 +18,6 @@ using System.Collections.Generic; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs index e7f83f638..7f343fb78 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); return; } - MainWindow.Instance.JumpToReference(analyzedModule.MetadataFile); + MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile)); } public override IEntity Member => null; diff --git a/ILSpy/AssemblyTree/AssemblyListPane.xaml b/ILSpy/AssemblyTree/AssemblyListPane.xaml new file mode 100644 index 000000000..8739412a8 --- /dev/null +++ b/ILSpy/AssemblyTree/AssemblyListPane.xaml @@ -0,0 +1,54 @@ + + + + + diff --git a/ILSpy/AssemblyTree/AssemblyListPane.xaml.cs b/ILSpy/AssemblyTree/AssemblyListPane.xaml.cs new file mode 100644 index 000000000..4e4ad8888 --- /dev/null +++ b/ILSpy/AssemblyTree/AssemblyListPane.xaml.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.Composition; +using System.Windows; + +using ICSharpCode.ILSpyX.TreeView; + +using TomsToolbox.Wpf.Composition.Mef; + +namespace ICSharpCode.ILSpy.AssemblyTree +{ + /// + /// Interaction logic for AssemblyListPane.xaml + /// + [DataTemplate(typeof(AssemblyListPaneModel))] + [PartCreationPolicy(CreationPolicy.NonShared)] + public partial class AssemblyListPane + { + public AssemblyListPane() + { + InitializeComponent(); + + ContextMenuProvider.Add(this); + } + + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (e.Property == DataContextProperty) + { + if (e.NewValue is not AssemblyListPaneModel model) + return; + + model.SetActiveView(this); + } + else if (e.Property == SelectedItemProperty) + { + if (e.NewValue is not SharpTreeNode treeNode) + return; + + FocusNode(treeNode); + } + } + } +} diff --git a/ILSpy/AssemblyTree/AssemblyListPaneModel.cs b/ILSpy/AssemblyTree/AssemblyListPaneModel.cs new file mode 100644 index 000000000..cda948a4c --- /dev/null +++ b/ILSpy/AssemblyTree/AssemblyListPaneModel.cs @@ -0,0 +1,896 @@ +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition; +using System.IO; +using System.Threading.Tasks; +using System.Windows.Input; +using System.Windows.Threading; + +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Docking; +using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.ViewModels; +using ICSharpCode.ILSpyX; +using ICSharpCode.ILSpyX.Settings; +using ICSharpCode.ILSpyX.TreeView; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.Metadata; + +using System.Reflection.Metadata.Ecma335; +using System.Windows; +using ICSharpCode.Decompiler.Documentation; +using ICSharpCode.Decompiler.TypeSystem.Implementation; +using System.Reflection.Metadata; + +using ICSharpCode.ILSpyX.Extensions; +using ICSharpCode.ILSpy.AppEnv; +using ICSharpCode.ILSpy.Search; +using ICSharpCode.Decompiler; +using System.Text; + +using TomsToolbox.Wpf; + +namespace ICSharpCode.ILSpy.AssemblyTree +{ + [ExportToolPane] + [PartCreationPolicy(CreationPolicy.Shared)] + [Export] + public class AssemblyListPaneModel : ToolPaneModel + { + public const string PaneContentId = "assemblyListPane"; + + AssemblyListPane activeView; + AssemblyList assemblyList; + AssemblyListTreeNode assemblyListTreeNode; + + bool refreshInProgress; + bool changingActiveTab; + + readonly NavigationHistoryService history = NavigationHistoryService.Instance; + + public AssemblyListPaneModel() + { + Title = Resources.Assemblies; + ContentId = PaneContentId; + IsCloseable = false; + ShortcutKey = new KeyGesture(Key.F6); + + MessageBus.Subscribers += JumpToReference; + MessageBus.Subscribers += (sender, e) => SessionSettings_PropertyChanged(sender, e); + MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); + + var selectionChangeThrottle = new DispatcherThrottle(DispatcherPriority.Background, TreeView_SelectionChanged); + SelectedItems.CollectionChanged += (_, _) => selectionChangeThrottle.Tick(); + } + + private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + var sessionSettings = SettingsService.Instance.SessionSettings; + + switch (e.PropertyName) + { + case nameof(SessionSettings.ActiveAssemblyList): + ShowAssemblyList(sessionSettings.ActiveAssemblyList); + break; + case nameof(SessionSettings.Theme): + // update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change) + DecompilerTextView.RegisterHighlighting(); + DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState); + break; + case nameof(SessionSettings.CurrentCulture): + MessageBox.Show(Properties.Resources.SettingsChangeRestartRequired, "ILSpy"); + break; + } + } + + public AssemblyList CurrentAssemblyList { + get { return assemblyList; } + } + + private SharpTreeNode root; + public SharpTreeNode Root { + get => root; + set => SetProperty(ref root, value); + } + + private SharpTreeNode selectedItem; + public SharpTreeNode SelectedItem { + get => selectedItem; + set => SetProperty(ref selectedItem, value); + } + + public ObservableCollection SelectedItems { get; } = []; + + public string[] SelectedPath => GetPathForNode(SelectedItem); + + readonly List commandLineLoadedAssemblies = []; + + public bool HandleCommandLineArguments(CommandLineArguments args) + { + LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false); + if (args.Language != null) + SettingsService.Instance.SessionSettings.LanguageSettings.Language = Languages.GetLanguage(args.Language); + return true; + } + + /// + /// Called on startup or when passed arguments via WndProc from a second instance. + /// In the format case, spySettings is non-null; in the latter it is null. + /// + public void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ILSpySettings spySettings = null) + { + var sessionSettings = SettingsService.Instance.SessionSettings; + + var relevantAssemblies = commandLineLoadedAssemblies.ToList(); + commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore + NavigateOnLaunch(args.NavigateTo, sessionSettings.ActiveTreeViewPath, spySettings, relevantAssemblies); + if (args.Search != null) + { + var searchPane = App.ExportProvider.GetExportedValue(); + + searchPane.SearchTerm = args.Search; + searchPane.Show(); + } + } + + public async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSpySettings spySettings, List relevantAssemblies) + { + var initialSelection = SelectedItem; + if (navigateTo != null) + { + bool found = false; + if (navigateTo.StartsWith("N:", StringComparison.Ordinal)) + { + string namespaceName = navigateTo.Substring(2); + foreach (LoadedAssembly asm in relevantAssemblies) + { + AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(asm); + if (asmNode != null) + { + // FindNamespaceNode() blocks the UI if the assembly is not yet loaded, + // so use an async wait instead. + await asm.GetMetadataFileAsync().Catch(ex => { }); + NamespaceTreeNode nsNode = asmNode.FindNamespaceNode(namespaceName); + if (nsNode != null) + { + found = true; + if (SelectedItem == initialSelection) + { + SelectNode(nsNode); + } + break; + } + } + } + } + else if (navigateTo == "none") + { + // Don't navigate anywhere; start empty. + // Used by ILSpy VS addin, it'll send us the real location to navigate to via IPC. + found = true; + } + else + { + IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); + // Make sure we wait for assemblies being loaded... + // BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal + await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal); + if (mr != null && mr.ParentModule?.MetadataFile != null) + { + found = true; + if (SelectedItem == initialSelection) + { + await JumpToReferenceAsync(mr); + } + } + } + if (!found && SelectedItem == initialSelection) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + output.Write($"Cannot find '{navigateTo}' in command line specified assemblies."); + DockWorkspace.Instance.ShowText(output); + } + } + else if (relevantAssemblies.Count == 1) + { + // NavigateTo == null and an assembly was given on the command-line: + // Select the newly loaded assembly + AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(relevantAssemblies[0]); + if (asmNode != null && SelectedItem == initialSelection) + { + SelectNode(asmNode); + } + } + else if (spySettings != null) + { + SharpTreeNode node = null; + if (activeTreeViewPath?.Length > 0) + { + foreach (var asm in CurrentAssemblyList.GetAssemblies()) + { + if (asm.FileName == activeTreeViewPath[0]) + { + // FindNodeByPath() blocks the UI if the assembly is not yet loaded, + // so use an async wait instead. + await asm.GetMetadataFileAsync().Catch(ex => { }); + } + } + node = FindNodeByPath(activeTreeViewPath, true); + } + if (SelectedItem == initialSelection) + { + if (node != null) + { + SelectNode(node); + + // only if not showing the about page, perform the update check: + await MainWindow.Instance.ShowMessageIfUpdatesAvailableAsync(spySettings); + } + else + { + DockWorkspace.Instance.ActiveTabPage.ShowTextView(AboutPage.Display); + } + } + } + } + + public static IEntity FindEntityInRelevantAssemblies(string navigateTo, IEnumerable relevantAssemblies) + { + ITypeReference typeRef; + IMemberReference memberRef = null; + if (navigateTo.StartsWith("T:", StringComparison.Ordinal)) + { + typeRef = IdStringProvider.ParseTypeName(navigateTo); + } + else + { + memberRef = IdStringProvider.ParseMemberIdString(navigateTo); + typeRef = memberRef.DeclaringTypeReference; + } + foreach (LoadedAssembly asm in relevantAssemblies.ToList()) + { + var module = asm.GetMetadataFileOrNull(); + if (CanResolveTypeInPEFile(module, typeRef, out var typeHandle)) + { + ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType + ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) + : new SimpleCompilation((PEFile)module, MinimalCorlib.Instance); + return memberRef == null + ? typeRef.Resolve(new SimpleTypeResolveContext(compilation)) as ITypeDefinition + : memberRef.Resolve(new SimpleTypeResolveContext(compilation)); + } + } + return null; + } + + static bool CanResolveTypeInPEFile(MetadataFile module, ITypeReference typeRef, out EntityHandle typeHandle) + { + // We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition. + if (module.IsReferenceAssembly()) + { + typeHandle = default; + return false; + } + + switch (typeRef) + { + case GetPotentiallyNestedClassTypeReference topLevelType: + typeHandle = topLevelType.ResolveInPEFile(module); + return !typeHandle.IsNil; + case NestedTypeReference nestedType: + if (!CanResolveTypeInPEFile(module, nestedType.DeclaringTypeReference, out typeHandle)) + return false; + if (typeHandle.Kind == HandleKind.ExportedType) + return true; + var typeDef = module.Metadata.GetTypeDefinition((TypeDefinitionHandle)typeHandle); + typeHandle = typeDef.GetNestedTypes().FirstOrDefault(t => { + var td = module.Metadata.GetTypeDefinition(t); + var typeName = ReflectionHelper.SplitTypeParameterCountFromReflectionName(module.Metadata.GetString(td.Name), out int typeParameterCount); + return nestedType.AdditionalTypeParameterCount == typeParameterCount && nestedType.Name == typeName; + }); + return !typeHandle.IsNil; + default: + typeHandle = default; + return false; + } + } + + public void Initialize() + { + var loadPreviousAssemblies = Options.MiscSettingsPanel.CurrentMiscSettings.LoadPreviousAssemblies; + + var sessionSettings = SettingsService.Instance.SessionSettings; + + if (loadPreviousAssemblies) + { + this.assemblyList = SettingsService.Instance.AssemblyListManager.LoadList(sessionSettings.ActiveAssemblyList); + } + else + { + SettingsService.Instance.AssemblyListManager.ClearAll(); + this.assemblyList = SettingsService.Instance.AssemblyListManager.CreateList(AssemblyListManager.DefaultListName); + } + + HandleCommandLineArguments(App.CommandLineArguments); + + if (assemblyList.GetAssemblies().Length == 0 + && assemblyList.ListName == AssemblyListManager.DefaultListName + && loadPreviousAssemblies) + { + LoadInitialAssemblies(); + } + + ShowAssemblyList(this.assemblyList); + + if (sessionSettings.ActiveAutoLoadedAssembly != null + && File.Exists(sessionSettings.ActiveAutoLoadedAssembly)) + { + this.assemblyList.Open(sessionSettings.ActiveAutoLoadedAssembly, true); + } + + Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(SettingsService.Instance.SpySettings))); + } + + void OpenAssemblies(ILSpySettings spySettings) + { + HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments, spySettings); + + AvalonEditTextOutput output = new(); + if (FormatExceptions(App.StartupExceptions.ToArray(), output)) + DockWorkspace.Instance.ShowText(output); + } + + static bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) + { + var stringBuilder = new StringBuilder(); + var result = exceptions.FormatExceptions(stringBuilder); + if (result) + { + output.Write(stringBuilder.ToString()); + } + return result; + } + + public void ShowAssemblyList(string name) + { + AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name); + //Only load a new list when it is a different one + if (list.ListName != CurrentAssemblyList.ListName) + { + ShowAssemblyList(list); + SelectNode(Root); + } + } + + void ShowAssemblyList(AssemblyList assemblyList) + { + history.Clear(); + if (this.assemblyList != null) + { + this.assemblyList.CollectionChanged -= assemblyList_CollectionChanged; + } + + this.assemblyList = assemblyList; + + assemblyList.CollectionChanged += assemblyList_CollectionChanged; + + assemblyListTreeNode = new(assemblyList) { + Select = x => SelectNode(x) + }; + + Root = assemblyListTreeNode; + + if (assemblyList.ListName == AssemblyListManager.DefaultListName) +#if DEBUG + this.Title = $"ILSpy {DecompilerVersionInfo.FullVersion}"; +#else + this.Title = "ILSpy"; +#endif + else +#if DEBUG + this.Title = $"ILSpy {DecompilerVersionInfo.FullVersion} - " + assemblyList.ListName; +#else + this.Title = "ILSpy - " + assemblyList.ListName; +#endif + } + + void assemblyList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Reset) + { + history.RemoveAll(_ => true); + } + if (e.OldItems != null) + { + var oldAssemblies = new HashSet(e.OldItems.Cast()); + history.RemoveAll(n => n.TreeNodes.Any( + nd => nd.AncestorsAndSelf().OfType().Any( + a => oldAssemblies.Contains(a.LoadedAssembly)))); + } + + MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(e)); + } + + void LoadInitialAssemblies() + { + // Called when loading an empty assembly list; so that + // the user can see something initially. + System.Reflection.Assembly[] initialAssemblies = { + typeof(object).Assembly, + typeof(Uri).Assembly, + typeof(System.Linq.Enumerable).Assembly, + typeof(System.Xml.XmlDocument).Assembly, + typeof(System.Windows.Markup.MarkupExtension).Assembly, + typeof(System.Windows.Rect).Assembly, + typeof(System.Windows.UIElement).Assembly, + typeof(System.Windows.FrameworkElement).Assembly + }; + foreach (System.Reflection.Assembly asm in initialAssemblies) + assemblyList.OpenAssembly(asm.Location); + } + + void LanguageSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Language" || e.PropertyName == "LanguageVersion") + { + DecompileSelectedNodes(recordHistory: false); + } + } + + public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm) + { + return assemblyListTreeNode.FindAssemblyNode(asm); + } + + #region Node Selection + + public void SelectNode(SharpTreeNode node, bool inNewTabPage = false) + { + if (node == null) + return; + + if (node.AncestorsAndSelf().Any(item => item.IsHidden)) + { + MessageBox.Show(Properties.Resources.NavigationFailed, "ILSpy", MessageBoxButton.OK, MessageBoxImage.Exclamation); + return; + } + + if (inNewTabPage) + { + DockWorkspace.Instance.TabPages.Add(); + SelectedItem = null; + } + + if (SelectedItem == node) + { + Dispatcher.BeginInvoke(RefreshDecompiledView); + } + else + { + activeView?.ScrollIntoView(node); + SelectedItem = node; + } + } + + internal void SelectNodes(IEnumerable nodes, bool inNewTabPage = false, bool setFocus = true, bool changingTab = false, bool ignoreCompilationRequests = false) + { + this.ignoreDecompilationRequests = ignoreCompilationRequests; + + try + { + + if (inNewTabPage) + { + DockWorkspace.Instance.TabPages.Add(); + } + + // Ensure nodes exist + var nodesList = nodes.Select(n => FindNodeByPath(GetPathForNode(n), true)) + .Where(n => n != null) + .ToArray(); + + if (!nodesList.Any() || nodesList.Any(n => n.AncestorsAndSelf().Any(a => a.IsHidden))) + { + return; + } + + this.changingActiveTab = changingTab || inNewTabPage; + + var currentFocused = Keyboard.FocusedElement; + + try + { + if (SelectedItems.SequenceEqual(nodesList)) + { + Dispatcher.BeginInvoke(RefreshDecompiledView); + return; + } + + this.SelectedItems.Clear(); + this.SelectedItems.AddRange(nodesList); + } + finally + { + if (!setFocus) + currentFocused.Focus(); + + this.changingActiveTab = false; + } + } + finally + { + this.ignoreDecompilationRequests = false; + } + + } + + /// + /// Retrieves a node using the .ToString() representations of its ancestors. + /// + public SharpTreeNode FindNodeByPath(string[] path, bool returnBestMatch) + { + if (path == null) + return null; + SharpTreeNode node = Root; + SharpTreeNode bestMatch = node; + foreach (var element in path) + { + if (node == null) + break; + bestMatch = node; + node.EnsureLazyChildren(); + if (node is ILSpyTreeNode ilSpyTreeNode) + ilSpyTreeNode.EnsureChildrenFiltered(); + node = node.Children.FirstOrDefault(c => c.ToString() == element); + } + if (returnBestMatch) + return node ?? bestMatch; + else + return node; + } + + /// + /// Gets the .ToString() representation of the node's ancestors. + /// + public static string[] GetPathForNode(SharpTreeNode node) + { + if (node == null) + return null; + List path = new List(); + while (node.Parent != null) + { + path.Add(node.ToString()); + node = node.Parent; + } + path.Reverse(); + return path.ToArray(); + } + + public ILSpyTreeNode FindTreeNode(object reference) + { + switch (reference) + { + case LoadedAssembly lasm: + return assemblyListTreeNode.FindAssemblyNode(lasm); + case MetadataFile asm: + return assemblyListTreeNode.FindAssemblyNode(asm); + case Resource res: + return assemblyListTreeNode.FindResourceNode(res); + case ValueTuple resName: + return assemblyListTreeNode.FindResourceNode(resName.Item1, resName.Item2); + case ITypeDefinition type: + return assemblyListTreeNode.FindTypeNode(type); + case IField fd: + return assemblyListTreeNode.FindFieldNode(fd); + case IMethod md: + return assemblyListTreeNode.FindMethodNode(md); + case IProperty pd: + return assemblyListTreeNode.FindPropertyNode(pd); + case IEvent ed: + return assemblyListTreeNode.FindEventNode(ed); + case INamespace nd: + return assemblyListTreeNode.FindNamespaceNode(nd); + default: + return null; + } + } + + private void JumpToReference(object sender, NavigateToReferenceEventArgs e) + { + JumpToReferenceAsync(e.Reference, e.InNewTabPage).HandleExceptions(); + } + + /// + /// Jumps to the specified reference. + /// + /// + /// Returns a task that will signal completion when the decompilation of the jump target has finished. + /// The task will be marked as canceled if the decompilation is canceled. + /// + private Task JumpToReferenceAsync(object reference, bool inNewTabPage = false) + { + var decompilationTask = Task.CompletedTask; + + switch (reference) + { + case Decompiler.Disassembler.OpCodeInfo opCode: + MainWindow.OpenLink(opCode.Link); + break; + case EntityReference unresolvedEntity: + string protocol = unresolvedEntity.Protocol; + var file = unresolvedEntity.ResolveAssembly(assemblyList); + if (file == null) + { + break; + } + if (protocol != "decompile") + { + var protocolHandlers = App.ExportProvider.GetExportedValues(); + foreach (var handler in protocolHandlers) + { + var node = handler.Resolve(protocol, file, unresolvedEntity.Handle, out bool newTabPage); + if (node != null) + { + SelectNode(node, newTabPage || inNewTabPage); + return decompilationTask; + } + } + } + var possibleToken = MetadataTokenHelpers.TryAsEntityHandle(MetadataTokens.GetToken(unresolvedEntity.Handle)); + if (possibleToken != null) + { + var typeSystem = new DecompilerTypeSystem(file, file.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached); + reference = typeSystem.MainModule.ResolveEntity(possibleToken.Value); + goto default; + } + break; + default: + ILSpyTreeNode treeNode = FindTreeNode(reference); + if (treeNode != null) + SelectNode(treeNode, inNewTabPage); + break; + } + return decompilationTask; + } + + #endregion + + public void LoadAssemblies(IEnumerable fileNames, List loadedAssemblies = null, bool focusNode = true) + { + var currentFocus = Keyboard.FocusedElement; + AssemblyTreeNode lastNode = null; + + foreach (string file in fileNames) + { + var assembly = assemblyList.OpenAssembly(file); + + if (loadedAssemblies != null) + { + loadedAssemblies.Add(assembly); + } + else + { + var node = assemblyListTreeNode.FindAssemblyNode(assembly); + if (node != null && focusNode) + { + lastNode = node; + SelectedItems.Add(node); + } + } + } + + if (!focusNode) + { + currentFocus?.Focus(); + } + else if (lastNode != null) + { + activeView?.FocusNode(lastNode); + } + } + + #region Decompile (TreeView_SelectionChanged) + + void TreeView_SelectionChanged() + { + DecompilerTextViewState state = null; + + // These are probably no longer needed and can be removed! + Debug.Assert(!refreshInProgress); + Debug.Assert(!changingActiveTab); + + if (refreshInProgress || changingActiveTab) + { + state = DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState; + } + + var delayDecompilationRequestDueToContextMenu = Mouse.RightButton == MouseButtonState.Pressed; + + if (!changingActiveTab && !delayDecompilationRequestDueToContextMenu) + { + DecompileSelectedNodes(state); + } + else + { + ContextMenuProvider.ContextMenuClosed += ContextMenuClosed; + } + + MessageBus.Send(this, new AssemblyTreeSelectionChangedEventArgs()); + + return; + + void ContextMenuClosed(object sender, EventArgs e) + { + ContextMenuProvider.ContextMenuClosed -= ContextMenuClosed; + + Dispatcher.BeginInvoke(DispatcherPriority.Background, () => { + if (Mouse.RightButton != MouseButtonState.Pressed) + { + DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState); + } + }); + } + } + + private bool ignoreDecompilationRequests; + + public void DecompileSelectedNodes(DecompilerTextViewState newState = null, bool recordHistory = true) + { + if (ignoreDecompilationRequests) + return; + + if (SelectedItems.Count == 0 && refreshInProgress) + return; + + if (recordHistory) + { + var tabPage = DockWorkspace.Instance.ActiveTabPage; + var currentState = tabPage.GetState(); + if (currentState != null) + history.UpdateCurrent(new NavigationState(tabPage, currentState)); + history.Record(new NavigationState(tabPage, SelectedItems)); + } + + DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching = true; + + if (SelectedItems.Count == 1) + { + if (SelectedItem is ILSpyTreeNode node && node.View(DockWorkspace.Instance.ActiveTabPage)) + return; + } + if (newState?.ViewedUri != null) + { + MainWindow.Instance.NavigateTo(new(newState.ViewedUri, null), recordHistory: false); + return; + } + var options = MainWindow.Instance.CreateDecompilationOptions(); + options.TextViewState = newState; + DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options)); + } + + public void RefreshDecompiledView() + { + try + { + refreshInProgress = true; + DecompileSelectedNodes(); + } + finally + { + refreshInProgress = false; + } + } + + public Language CurrentLanguage => SettingsService.Instance.SessionSettings.LanguageSettings.Language; + + public LanguageVersion CurrentLanguageVersion => SettingsService.Instance.SessionSettings.LanguageSettings.LanguageVersion; + + public IEnumerable SelectedNodes { + get { + return GetTopLevelSelection().OfType(); + } + } + + #endregion + + + public void Refresh() + { + var currentFocus = Keyboard.FocusedElement; + + try + { + refreshInProgress = true; + var path = GetPathForNode(SelectedItem); + ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(assemblyList.ListName)); + SelectNode(FindNodeByPath(path, true), inNewTabPage: false); + } + finally + { + refreshInProgress = false; + currentFocus?.Focus(); + } + } + + public void UnselectAll(bool ignoreCompilationRequests = false) + { + this.ignoreDecompilationRequests = ignoreCompilationRequests; + SelectedItems.Clear(); + this.ignoreDecompilationRequests = false; + } + + public IEnumerable GetTopLevelSelection() + { + var selection = this.SelectedItems; + var selectionHash = new HashSet(selection); + + return selection.Where(item => item.Ancestors().All(a => !selectionHash.Contains(a))); + } + + public void SetActiveView(AssemblyListPane activeView) + { + this.activeView = activeView; + } + + public void SortAssemblyList() + { + using (activeView?.LockUpdates()) + { + CurrentAssemblyList.Sort(AssemblyComparer.Instance); + } + } + + private class AssemblyComparer : IComparer + { + public static readonly AssemblyComparer Instance = new(); + int IComparer.Compare(LoadedAssembly x, LoadedAssembly y) + { + return string.Compare(x?.ShortName, y?.ShortName, StringComparison.CurrentCulture); + } + } + + public void CollapseAll() + { + using (activeView.LockUpdates()) + { + CollapseChildren(Root); + } + } + + static void CollapseChildren(SharpTreeNode node) + { + foreach (var child in node.Children) + { + if (!child.IsExpanded) + continue; + + CollapseChildren(child); + child.IsExpanded = false; + } + } + } +} diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs index c905c9a87..3a4148058 100644 --- a/ILSpy/Commands/DecompileAllCommand.cs +++ b/ILSpy/Commands/DecompileAllCommand.cs @@ -28,7 +28,6 @@ using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using TomsToolbox.Essentials; @@ -49,7 +48,7 @@ namespace ICSharpCode.ILSpy Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Parallel.ForEach( - Partitioner.Create(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true), + Partitioner.Create(MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies(), loadBalance: true), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate (LoadedAssembly asm) { if (!asm.HasLoadError) @@ -96,7 +95,7 @@ namespace ICSharpCode.ILSpy { const int numRuns = 100; var language = SettingsService.Instance.SessionSettings.LanguageSettings.Language; - var nodes = MainWindow.Instance.SelectedNodes.ToArray(); + var nodes = MainWindow.Instance.AssemblyTreeModel.SelectedNodes.ToArray(); var options = MainWindow.Instance.CreateDecompilationOptions(); Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { options.CancellationToken = ct; diff --git a/ILSpy/Commands/DecompileCommand.cs b/ILSpy/Commands/DecompileCommand.cs index 0a7d1a385..ec35a0cbe 100644 --- a/ILSpy/Commands/DecompileCommand.cs +++ b/ILSpy/Commands/DecompileCommand.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.ILSpy.Commands selection = entity; } if (selection != null) - MainWindow.Instance.JumpToReference(selection); + MessageBus.Send(this, new NavigateToReferenceEventArgs(selection)); } } } diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs index 07e9bebcb..0ecc50bf8 100644 --- a/ILSpy/Commands/DecompileInNewViewCommand.cs +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -23,11 +23,11 @@ using System.Linq; using System.Windows.Threading; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Properties; -using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; +using TomsToolbox.Essentials; + namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), InputGestureText = "MMB", Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] @@ -53,18 +53,18 @@ namespace ICSharpCode.ILSpy.Commands { if (context.SelectedTreeNodes != null) { - if (context.TreeView != MainWindow.Instance.AssemblyTreeView) + if (context.TreeView.DataContext != MainWindow.Instance.AssemblyTreeModel) { - return context.SelectedTreeNodes.OfType().Select(FindTreeNode).Where(n => n != null); + return context.SelectedTreeNodes.OfType().Select(FindTreeNode).ExceptNullItems(); } else { - return context.SelectedTreeNodes.OfType().Where(n => n != null); + return context.SelectedTreeNodes.OfType(); } } else if (context.Reference?.Reference is IEntity entity) { - if (MainWindow.Instance.FindTreeNode(entity) is ILSpyTreeNode node) + if (MainWindow.Instance.AssemblyTreeModel.FindTreeNode(entity) is { } node) { return new[] { node }; } @@ -75,7 +75,7 @@ namespace ICSharpCode.ILSpy.Commands { if (node is ILSpyTreeNode ilspyNode) return ilspyNode; - return MainWindow.Instance.FindTreeNode(node.Member); + return MainWindow.Instance.AssemblyTreeModel.FindTreeNode(node.Member); } } @@ -84,8 +84,7 @@ namespace ICSharpCode.ILSpy.Commands if (nodes.Length == 0) return; - MainWindow.Instance.SelectNodes(nodes, inNewTabPage: true); - MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); + MainWindow.Instance.AssemblyTreeModel.SelectNodes(nodes, inNewTabPage: true); } } } diff --git a/ILSpy/Commands/DisassembleAllCommand.cs b/ILSpy/Commands/DisassembleAllCommand.cs index fc44d405a..aa81bd855 100644 --- a/ILSpy/Commands/DisassembleAllCommand.cs +++ b/ILSpy/Commands/DisassembleAllCommand.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.ILSpy Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Parallel.ForEach( - Partitioner.Create(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true), + Partitioner.Create(MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies(), loadBalance: true), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate (LoadedAssembly asm) { if (!asm.HasLoadError) diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index b7c3997d0..fff3a9fb3 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -110,14 +110,14 @@ namespace ICSharpCode.ILSpy { public override bool CanExecute(object parameter) { - return MainWindow.Instance.SelectedNodes?.Count() == 1 - && MainWindow.Instance.SelectedNodes?.FirstOrDefault() is AssemblyTreeNode tn + return MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.Count() == 1 + && MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.FirstOrDefault() is AssemblyTreeNode tn && !tn.LoadedAssembly.HasLoadError; } public override void Execute(object parameter) { - var assembly = (MainWindow.Instance.SelectedNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; + var assembly = (MainWindow.Instance.AssemblyTreeModel.SelectedNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; if (assembly == null) return; GeneratePdbContextMenuEntry.GeneratePdbForAssembly(assembly); diff --git a/ILSpy/Commands/Pdb2XmlCommand.cs b/ILSpy/Commands/Pdb2XmlCommand.cs index c69777370..b624124cb 100644 --- a/ILSpy/Commands/Pdb2XmlCommand.cs +++ b/ILSpy/Commands/Pdb2XmlCommand.cs @@ -40,14 +40,14 @@ namespace ICSharpCode.ILSpy { public override bool CanExecute(object parameter) { - var selectedNodes = MainWindow.Instance.SelectedNodes; + var selectedNodes = MainWindow.Instance.AssemblyTreeModel.SelectedNodes; return selectedNodes?.Any() == true && selectedNodes.All(n => n is AssemblyTreeNode asm && !asm.LoadedAssembly.HasLoadError); } public override void Execute(object parameter) { - Execute(MainWindow.Instance.SelectedNodes.OfType()); + Execute(MainWindow.Instance.AssemblyTreeModel.SelectedNodes.OfType()); } internal static void Execute(IEnumerable nodes) diff --git a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs index c8696104b..73d9769a7 100644 --- a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs +++ b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs @@ -29,16 +29,16 @@ namespace ICSharpCode.ILSpy { public override bool CanExecute(object parameter) { - return MainWindow.Instance.CurrentAssemblyList?.GetAssemblies().Any(l => l.HasLoadError) == true; + return MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList?.GetAssemblies().Any(l => l.HasLoadError) == true; } public override void Execute(object parameter) { - foreach (var asm in MainWindow.Instance.CurrentAssemblyList.GetAssemblies()) + foreach (var asm in MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies()) { if (!asm.HasLoadError) continue; - var node = MainWindow.Instance.AssemblyListTreeNode.FindAssemblyNode(asm); + var node = MainWindow.Instance.AssemblyTreeModel.FindAssemblyNode(asm); if (node != null && node.CanDelete()) node.Delete(); } @@ -51,12 +51,12 @@ namespace ICSharpCode.ILSpy { public override bool CanExecute(object parameter) { - return MainWindow.Instance.CurrentAssemblyList?.Count > 0; + return MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList?.Count > 0; } public override void Execute(object parameter) { - MainWindow.Instance.CurrentAssemblyList?.Clear(); + MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList?.Clear(); } } } diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index d7e38ada3..9b013daad 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -32,7 +32,6 @@ using Microsoft.Win32; using ICSharpCode.ILSpyX.TreeView; using System.ComponentModel.Composition; -using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.TextView { diff --git a/ILSpy/Commands/SelectPdbContextMenuEntry.cs b/ILSpy/Commands/SelectPdbContextMenuEntry.cs index 061ff7e6b..db4fa7373 100644 --- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs +++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs @@ -48,10 +48,10 @@ namespace ICSharpCode.ILSpy await assembly.LoadDebugInfo(dlg.FileName); } - var node = (AssemblyTreeNode)MainWindow.Instance.FindNodeByPath(new[] { assembly.FileName }, true); + var node = (AssemblyTreeNode)MainWindow.Instance.AssemblyTreeModel.FindNodeByPath(new[] { assembly.FileName }, true); node.UpdateToolTip(); - MainWindow.Instance.SelectNode(node); - MainWindow.Instance.RefreshDecompiledView(); + MainWindow.Instance.AssemblyTreeModel.SelectNode(node); + MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView(); } public bool IsEnabled(TextViewContext context) => true; diff --git a/ILSpy/Commands/SetThemeCommand.cs b/ILSpy/Commands/SetThemeCommand.cs index 2d1a0b32a..7f10c21f7 100644 --- a/ILSpy/Commands/SetThemeCommand.cs +++ b/ILSpy/Commands/SetThemeCommand.cs @@ -1,5 +1,4 @@ -using ICSharpCode.ILSpy.Util; - + namespace ICSharpCode.ILSpy.Commands { public class SetThemeCommand : SimpleCommand diff --git a/ILSpy/Commands/ShowCFGContextMenuEntry.cs b/ILSpy/Commands/ShowCFGContextMenuEntry.cs index b4e8be003..2067752cc 100644 --- a/ILSpy/Commands/ShowCFGContextMenuEntry.cs +++ b/ILSpy/Commands/ShowCFGContextMenuEntry.cs @@ -6,7 +6,6 @@ using System.Windows; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; -using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.Commands { diff --git a/ILSpy/Commands/SortAssemblyListCommand.cs b/ILSpy/Commands/SortAssemblyListCommand.cs index 7621778a7..5b7dc67e1 100644 --- a/ILSpy/Commands/SortAssemblyListCommand.cs +++ b/ILSpy/Commands/SortAssemblyListCommand.cs @@ -29,17 +29,11 @@ namespace ICSharpCode.ILSpy [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources.SortAssembly_listName), MenuIcon = "Images/Sort", MenuCategory = nameof(Resources.View))] [ExportToolbarCommand(ToolTip = nameof(Resources.SortAssemblyListName), ToolbarIcon = "Images/Sort", ToolbarCategory = nameof(Resources.View))] [PartCreationPolicy(CreationPolicy.Shared)] - sealed class SortAssemblyListCommand : SimpleCommand, IComparer + sealed class SortAssemblyListCommand : SimpleCommand { public override void Execute(object parameter) { - using (MainWindow.Instance.AssemblyTreeView.LockUpdates()) - MainWindow.Instance.CurrentAssemblyList.Sort(this); - } - - int IComparer.Compare(LoadedAssembly x, LoadedAssembly y) - { - return string.Compare(x.ShortName, y.ShortName, StringComparison.CurrentCulture); + MainWindow.Instance.AssemblyTreeModel.SortAssemblyList(); } } @@ -50,19 +44,8 @@ namespace ICSharpCode.ILSpy { public override void Execute(object parameter) { - using (MainWindow.Instance.AssemblyTreeView.LockUpdates()) - CollapseChildren(MainWindow.Instance.AssemblyTreeView.Root); + MainWindow.Instance.AssemblyTreeModel.CollapseAll(); - void CollapseChildren(SharpTreeNode node) - { - foreach (var child in node.Children) - { - if (!child.IsExpanded) - continue; - CollapseChildren(child); - child.IsExpanded = false; - } - } } } } diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs index fcbdb1bae..9a50cb649 100644 --- a/ILSpy/ContextMenuEntry.cs +++ b/ILSpy/ContextMenuEntry.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -32,6 +33,7 @@ using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Composition; +using TomsToolbox.Essentials; namespace ICSharpCode.ILSpy { @@ -168,6 +170,13 @@ namespace ICSharpCode.ILSpy internal class ContextMenuProvider { + private static readonly WeakEventSource ContextMenuClosedEventSource = new(); + + public static event EventHandler ContextMenuClosed { + add => ContextMenuClosedEventSource.Subscribe(value); + remove => ContextMenuClosedEventSource.Unsubscribe(value); + } + /// /// Enables extensible context menu support for the specified tree view. /// @@ -203,37 +212,40 @@ namespace ICSharpCode.ILSpy dataGrid.ContextMenu = new ContextMenu(); } + readonly Control control; readonly SharpTreeView treeView; readonly DecompilerTextView textView; readonly ListBox listBox; readonly DataGrid dataGrid; readonly IExport[] entries; - private ContextMenuProvider() + private ContextMenuProvider(Control control) { entries = App.ExportProvider.GetExports().ToArray(); + + this.control = control; } ContextMenuProvider(DecompilerTextView textView) - : this() + : this((Control)textView) { this.textView = textView ?? throw new ArgumentNullException(nameof(textView)); } ContextMenuProvider(SharpTreeView treeView) - : this() + : this((Control)treeView) { this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView)); } ContextMenuProvider(ListBox listBox) - : this() + : this((Control)listBox) { this.listBox = listBox ?? throw new ArgumentNullException(nameof(listBox)); } ContextMenuProvider(DataGrid dataGrid) - : this() + : this((Control)dataGrid) { this.dataGrid = dataGrid ?? throw new ArgumentNullException(nameof(dataGrid)); } @@ -289,7 +301,18 @@ namespace ICSharpCode.ILSpy bool ShowContextMenu(TextViewContext context, out ContextMenu menu) { + // Closing event is raised on the control where mouse is clicked, not on the control that opened the menu, so we hook on the global window event. + var window = Window.GetWindow(control)!; + window.ContextMenuClosing += ContextMenu_Closing; + + void ContextMenu_Closing(object sender, EventArgs e) + { + window.ContextMenuClosing -= ContextMenu_Closing; + ContextMenuClosedEventSource.Raise(this, EventArgs.Empty); + } + menu = new ContextMenu(); + var menuGroups = new Dictionary[]>(); IExport[] topLevelGroup = null; foreach (var group in entries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.ParentMenuID)) diff --git a/ILSpy/Controls/TreeView/SharpTreeView.cs b/ILSpy/Controls/TreeView/SharpTreeView.cs index faa7cb2f0..c60cd5ad7 100644 --- a/ILSpy/Controls/TreeView/SharpTreeView.cs +++ b/ILSpy/Controls/TreeView/SharpTreeView.cs @@ -158,6 +158,7 @@ namespace ICSharpCode.ILSpy.Controls.TreeView if (flattener != null) { flattener.Stop(); + flattener.CollectionChanged -= flattener_CollectionChanged; } if (Root != null) { diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index a7b4c7102..c0a2b4081 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -33,7 +33,6 @@ using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.ILSpy.Analyzers; using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf; @@ -136,8 +135,7 @@ namespace ICSharpCode.ILSpy.Docking { if (state.DecompiledNodes != null) { - MainWindow.Instance.SelectNodes(state.DecompiledNodes, - inNewTabPage: false, setFocus: true, changingActiveTab: true); + MainWindow.Instance.AssemblyTreeModel.SelectNodes(state.DecompiledNodes, inNewTabPage: false, setFocus: true, changingTab: true); } else { @@ -216,7 +214,7 @@ namespace ICSharpCode.ILSpy.Docking CloseAllTabs(); SessionSettings.DockLayout.Reset(); InitializeLayout(MainWindow.Instance.dockManager); - MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); + MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView); } static readonly PropertyInfo previousContainerProperty = typeof(LayoutContent).GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance); diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs index 374ad026e..39595224f 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -27,7 +27,6 @@ using System.Windows.Media; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy diff --git a/ILSpy/GlobalUsings.cs b/ILSpy/GlobalUsings.cs new file mode 100644 index 000000000..12310c612 --- /dev/null +++ b/ILSpy/GlobalUsings.cs @@ -0,0 +1 @@ +global using ICSharpCode.ILSpy.Util; \ No newline at end of file diff --git a/ILSpy/LanguageSettings.cs b/ILSpy/LanguageSettings.cs index ef7613ccf..82245963c 100644 --- a/ILSpy/LanguageSettings.cs +++ b/ILSpy/LanguageSettings.cs @@ -23,7 +23,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Xml.Linq; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs index 4f7ca57cc..4d3436d23 100644 --- a/ILSpy/Languages/CSharpILMixedLanguage.cs +++ b/ILSpy/Languages/CSharpILMixedLanguage.cs @@ -34,7 +34,6 @@ using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index df801511c..b570ba12e 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -41,7 +41,6 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using LanguageVersion = ICSharpCode.ILSpyX.LanguageVersion; @@ -359,15 +358,15 @@ namespace ICSharpCode.ILSpy void AddReferenceWarningMessage(MetadataFile module, ITextOutput output) { - var loadedAssembly = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().FirstOrDefault(la => la.GetMetadataFileOrNull() == module); + var loadedAssembly = MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies().FirstOrDefault(la => la.GetMetadataFileOrNull() == module); if (loadedAssembly == null || !loadedAssembly.LoadedAssemblyReferencesInfo.HasErrors) return; string line1 = Properties.Resources.WarningSomeAssemblyReference; string line2 = Properties.Resources.PropertyManuallyMissingReferencesListLoadedAssemblies; AddWarningMessage(module, output, line1, line2, Properties.Resources.ShowAssemblyLoad, Images.ViewCode, delegate { - ILSpyTreeNode assemblyNode = MainWindow.Instance.FindTreeNode(module); + ILSpyTreeNode assemblyNode = MainWindow.Instance.AssemblyTreeModel.FindTreeNode(module); assemblyNode.EnsureLazyChildren(); - MainWindow.Instance.SelectNode(assemblyNode.Children.OfType().Single()); + MainWindow.Instance.AssemblyTreeModel.SelectNode(assemblyNode.Children.OfType().Single()); }); } diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index 22d060b37..f2638bcab 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -30,7 +30,6 @@ using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 39407e92e..62c51508c 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -21,52 +21,8 @@ xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" xmlns:toms="urn:TomsToolbox" xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels" - xmlns:treeNodes="clr-namespace:ICSharpCode.ILSpy.TreeNodes" - xmlns:util="clr-namespace:ICSharpCode.ILSpy.Util" d:DataContext="{d:DesignInstance local:MainWindowViewModel}"> - - - - - - - - - @@ -223,7 +179,7 @@ - + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 235594f7a..5a4d6ab6c 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -21,11 +21,10 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -33,30 +32,22 @@ using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Navigation; -using System.Windows.Threading; using AvalonDock.Layout.Serialization; using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.Documentation; -using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.ILSpy.AppEnv; +using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Docking; -using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.Updates; using ICSharpCode.ILSpy.ViewModels; -using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.Settings; -using ICSharpCode.ILSpy.Controls.TreeView; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Extensions; using Microsoft.Win32; @@ -71,11 +62,7 @@ namespace ICSharpCode.ILSpy /// partial class MainWindow : Window { - bool refreshInProgress, changingActiveTab; - readonly NavigationHistory history = new NavigationHistory(); - - AssemblyList assemblyList; - AssemblyListTreeNode assemblyListTreeNode; + readonly NavigationHistoryService history = NavigationHistoryService.Instance; static MainWindow instance; @@ -83,16 +70,17 @@ namespace ICSharpCode.ILSpy get { return instance; } } - public SharpTreeView AssemblyTreeView { + public AssemblyListPaneModel AssemblyTreeModel { get { - return FindResource("AssemblyTreeView") as SharpTreeView; + return App.ExportProvider.GetExportedValue(); } } public DecompilationOptions CreateDecompilationOptions() { var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress; - return new DecompilationOptions(CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { Progress = decompilerView }; + + return new(AssemblyTreeModel.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { Progress = decompilerView }; } public MainWindow() @@ -113,21 +101,18 @@ namespace ICSharpCode.ILSpy SettingsService.Instance.AssemblyListManager.CreateDefaultAssemblyLists(); if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture)) { - System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sessionSettings.CurrentCulture); + Thread.CurrentThread.CurrentUICulture = new CultureInfo(sessionSettings.CurrentCulture); } InitializeComponent(); InitToolPanes(); DockWorkspace.Instance.InitializeLayout(dockManager); - MessageBus.Subscribers += (sender, e) => SessionSettings_PropertyChanged(sender, e); - MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; InitMainMenu(); InitWindowMenu(); InitToolbar(); InitFileLoaders(); - ContextMenuProvider.Add(AssemblyTreeView); this.Loaded += MainWindow_Loaded; } @@ -146,26 +131,6 @@ namespace ICSharpCode.ILSpy } } - private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - var sessionSettings = SettingsService.Instance.SessionSettings; - - switch (e.PropertyName) - { - case nameof(SessionSettings.ActiveAssemblyList): - ShowAssemblyList(sessionSettings.ActiveAssemblyList); - break; - case nameof(SessionSettings.Theme): - // update syntax highlighting and force reload (AvalonEdit does not automatically refresh on highlighting change) - DecompilerTextView.RegisterHighlighting(); - DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState); - break; - case nameof(SessionSettings.CurrentCulture): - MessageBox.Show(Properties.Resources.SettingsChangeRestartRequired, "ILSpy"); - break; - } - } - void SetWindowBounds(Rect bounds) { this.Left = bounds.Left; @@ -217,7 +182,7 @@ namespace ICSharpCode.ILSpy Command = CommandWrapper.Unwrap(command.Value), ToolTip = Properties.Resources.ResourceManager.GetString(command.Metadata.ToolTip), Tag = command.Metadata.Tag, - Content = new Image { + Content = new System.Windows.Controls.Image { Width = 16, Height = 16, Source = Images.Load(command.Value, command.Metadata.ToolbarIcon) @@ -242,7 +207,7 @@ namespace ICSharpCode.ILSpy { if (parentMenuItem.Items.Count > 0) { - parentMenuItem.Items.Add(new Separator() { Tag = category.Key }); + parentMenuItem.Items.Add(new Separator { Tag = category.Key }); } foreach (var entry in category) { @@ -414,7 +379,7 @@ namespace ICSharpCode.ILSpy if (shortcutKey != null) { InputBindings.Add(new InputBinding(menuItem.Command, shortcutKey)); - menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(System.Globalization.CultureInfo.CurrentUICulture); + menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture); } if (!string.IsNullOrEmpty(pane.Icon)) { @@ -599,275 +564,26 @@ namespace ICSharpCode.ILSpy } } - public AssemblyList CurrentAssemblyList { - get { return assemblyList; } - } - - List commandLineLoadedAssemblies = new List(); - internal async Task HandleSingleInstanceCommandLineArguments(string[] args) { var cmdArgs = CommandLineArguments.Create(args); await Dispatcher.InvokeAsync(() => { - if (HandleCommandLineArguments(cmdArgs)) + if (AssemblyTreeModel.HandleCommandLineArguments(cmdArgs)) { if (!cmdArgs.NoActivate && WindowState == WindowState.Minimized) WindowState = WindowState.Normal; - HandleCommandLineArgumentsAfterShowList(cmdArgs); + AssemblyTreeModel.HandleCommandLineArgumentsAfterShowList(cmdArgs); } }); } - bool HandleCommandLineArguments(CommandLineArguments args) - { - LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false); - if (args.Language != null) - SettingsService.Instance.SessionSettings.LanguageSettings.Language = Languages.GetLanguage(args.Language); - return true; - } - - /// - /// Called on startup or when passed arguments via WndProc from a second instance. - /// In the format case, spySettings is non-null; in the latter it is null. - /// - void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ILSpySettings spySettings = null) - { - var sessionSettings = SettingsService.Instance.SessionSettings; - - var relevantAssemblies = commandLineLoadedAssemblies.ToList(); - commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore - NavigateOnLaunch(args.NavigateTo, sessionSettings.ActiveTreeViewPath, spySettings, relevantAssemblies); - if (args.Search != null) - { - var searchPane = App.ExportProvider.GetExportedValue(); - - searchPane.SearchTerm = args.Search; - searchPane.Show(); - } - } - - async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSpySettings spySettings, List relevantAssemblies) - { - var initialSelection = AssemblyTreeView.SelectedItem; - if (navigateTo != null) - { - bool found = false; - if (navigateTo.StartsWith("N:", StringComparison.Ordinal)) - { - string namespaceName = navigateTo.Substring(2); - foreach (LoadedAssembly asm in relevantAssemblies) - { - AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(asm); - if (asmNode != null) - { - // FindNamespaceNode() blocks the UI if the assembly is not yet loaded, - // so use an async wait instead. - await asm.GetMetadataFileAsync().Catch(ex => { }); - NamespaceTreeNode nsNode = asmNode.FindNamespaceNode(namespaceName); - if (nsNode != null) - { - found = true; - if (AssemblyTreeView.SelectedItem == initialSelection) - { - SelectNode(nsNode); - } - break; - } - } - } - } - else if (navigateTo == "none") - { - // Don't navigate anywhere; start empty. - // Used by ILSpy VS addin, it'll send us the real location to navigate to via IPC. - found = true; - } - else - { - IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); - // Make sure we wait for assemblies being loaded... - // BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal - await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal); - if (mr != null && mr.ParentModule.MetadataFile != null) - { - found = true; - if (AssemblyTreeView.SelectedItem == initialSelection) - { - JumpToReference(mr); - } - } - } - if (!found && AssemblyTreeView.SelectedItem == initialSelection) - { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - output.Write(string.Format("Cannot find '{0}' in command line specified assemblies.", navigateTo)); - DockWorkspace.Instance.ShowText(output); - } - } - else if (relevantAssemblies.Count == 1) - { - // NavigateTo == null and an assembly was given on the command-line: - // Select the newly loaded assembly - AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(relevantAssemblies[0]); - if (asmNode != null && AssemblyTreeView.SelectedItem == initialSelection) - { - SelectNode(asmNode); - } - } - else if (spySettings != null) - { - SharpTreeNode node = null; - if (activeTreeViewPath?.Length > 0) - { - foreach (var asm in CurrentAssemblyList.GetAssemblies()) - { - if (asm.FileName == activeTreeViewPath[0]) - { - // FindNodeByPath() blocks the UI if the assembly is not yet loaded, - // so use an async wait instead. - await asm.GetMetadataFileAsync().Catch(ex => { }); - } - } - node = FindNodeByPath(activeTreeViewPath, true); - } - if (AssemblyTreeView.SelectedItem == initialSelection) - { - if (node != null) - { - SelectNode(node); - - // only if not showing the about page, perform the update check: - await ShowMessageIfUpdatesAvailableAsync(spySettings); - } - else - { - DockWorkspace.Instance.ActiveTabPage.ShowTextView(AboutPage.Display); - } - } - } - } - - internal static IEntity FindEntityInRelevantAssemblies(string navigateTo, IEnumerable relevantAssemblies) - { - ITypeReference typeRef = null; - IMemberReference memberRef = null; - if (navigateTo.StartsWith("T:", StringComparison.Ordinal)) - { - typeRef = IdStringProvider.ParseTypeName(navigateTo); - } - else - { - memberRef = IdStringProvider.ParseMemberIdString(navigateTo); - typeRef = memberRef.DeclaringTypeReference; - } - foreach (LoadedAssembly asm in relevantAssemblies.ToList()) - { - var module = asm.GetMetadataFileOrNull(); - if (CanResolveTypeInPEFile(module, typeRef, out var typeHandle)) - { - ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType - ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) - : new SimpleCompilation((PEFile)module, MinimalCorlib.Instance); - return memberRef == null - ? typeRef.Resolve(new SimpleTypeResolveContext(compilation)) as ITypeDefinition - : (IEntity)memberRef.Resolve(new SimpleTypeResolveContext(compilation)); - } - } - return null; - } - - static bool CanResolveTypeInPEFile(MetadataFile module, ITypeReference typeRef, out EntityHandle typeHandle) - { - // We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition. - if (module.IsReferenceAssembly()) - { - typeHandle = default; - return false; - } - - switch (typeRef) - { - case GetPotentiallyNestedClassTypeReference topLevelType: - typeHandle = topLevelType.ResolveInPEFile(module); - return !typeHandle.IsNil; - case NestedTypeReference nestedType: - if (!CanResolveTypeInPEFile(module, nestedType.DeclaringTypeReference, out typeHandle)) - return false; - if (typeHandle.Kind == HandleKind.ExportedType) - return true; - var typeDef = module.Metadata.GetTypeDefinition((TypeDefinitionHandle)typeHandle); - typeHandle = typeDef.GetNestedTypes().FirstOrDefault(t => { - var td = module.Metadata.GetTypeDefinition(t); - var typeName = ReflectionHelper.SplitTypeParameterCountFromReflectionName(module.Metadata.GetString(td.Name), out int typeParameterCount); - return nestedType.AdditionalTypeParameterCount == typeParameterCount && nestedType.Name == typeName; - }); - return !typeHandle.IsNil; - default: - typeHandle = default; - return false; - } - } - void MainWindow_Loaded(object sender, RoutedEventArgs e) { DockWorkspace.Instance.TabPages.Add(); - var loadPreviousAssemblies = Options.MiscSettingsPanel.CurrentMiscSettings.LoadPreviousAssemblies; - - var sessionSettings = SettingsService.Instance.SessionSettings; - - if (loadPreviousAssemblies) - { - // Load AssemblyList only in Loaded event so that WPF is initialized before we start the CPU-heavy stuff. - // This makes the UI come up a bit faster. - this.assemblyList = SettingsService.Instance.AssemblyListManager.LoadList(sessionSettings.ActiveAssemblyList); - } - else - { - SettingsService.Instance.AssemblyListManager.ClearAll(); - this.assemblyList = SettingsService.Instance.AssemblyListManager.CreateList(AssemblyListManager.DefaultListName); - } - - HandleCommandLineArguments(App.CommandLineArguments); - - if (assemblyList.GetAssemblies().Length == 0 - && assemblyList.ListName == AssemblyListManager.DefaultListName - && loadPreviousAssemblies) - { - LoadInitialAssemblies(); - } - - ShowAssemblyList(this.assemblyList); - - if (sessionSettings.ActiveAutoLoadedAssembly != null - && File.Exists(sessionSettings.ActiveAutoLoadedAssembly)) - { - this.assemblyList.Open(sessionSettings.ActiveAutoLoadedAssembly, true); - } - - Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(SettingsService.Instance.SpySettings))); - } - - void OpenAssemblies(ILSpySettings spySettings) - { - HandleCommandLineArgumentsAfterShowList(App.CommandLineArguments, spySettings); - - AvalonEditTextOutput output = new AvalonEditTextOutput(); - if (FormatExceptions(App.StartupExceptions.ToArray(), output)) - DockWorkspace.Instance.ShowText(output); - } - - static bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) - { - var stringBuilder = new StringBuilder(); - var result = exceptions.FormatExceptions(stringBuilder); - if (result) - { - output.Write(stringBuilder.ToString()); - } - return result; + AssemblyTreeModel.Initialize(); } #region Update Check @@ -889,7 +605,7 @@ namespace ICSharpCode.ILSpy AdjustUpdateUIAfterCheck(downloadUrl, forceCheck); } - void updatePanelCloseButtonClick(object sender, RoutedEventArgs e) + void UpdatePanelCloseButtonClick(object sender, RoutedEventArgs e) { updatePanel.Visibility = Visibility.Collapsed; } @@ -898,7 +614,7 @@ namespace ICSharpCode.ILSpy { if (updateAvailableDownloadUrl != null) { - MainWindow.OpenLink(updateAvailableDownloadUrl); + OpenLink(updateAvailableDownloadUrl); } else { @@ -925,326 +641,6 @@ namespace ICSharpCode.ILSpy } #endregion - public void ShowAssemblyList(string name) - { - AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name); - //Only load a new list when it is a different one - if (list.ListName != CurrentAssemblyList.ListName) - { - ShowAssemblyList(list); - SelectNode(AssemblyTreeView.Root); - } - } - - void ShowAssemblyList(AssemblyList assemblyList) - { - history.Clear(); - if (this.assemblyList != null) - { - this.assemblyList.CollectionChanged -= assemblyList_Assemblies_CollectionChanged; - } - - this.assemblyList = assemblyList; - assemblyList.CollectionChanged += assemblyList_Assemblies_CollectionChanged; - - assemblyListTreeNode = new AssemblyListTreeNode(assemblyList); - assemblyListTreeNode.Select = x => SelectNode(x, inNewTabPage: false); - AssemblyTreeView.Root = assemblyListTreeNode; - - if (assemblyList.ListName == AssemblyListManager.DefaultListName) -#if DEBUG - this.Title = $"ILSpy {DecompilerVersionInfo.FullVersion}"; -#else - this.Title = "ILSpy"; -#endif - else -#if DEBUG - this.Title = $"ILSpy {DecompilerVersionInfo.FullVersion} - " + assemblyList.ListName; -#else - this.Title = "ILSpy - " + assemblyList.ListName; -#endif - } - - void assemblyList_Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action == NotifyCollectionChangedAction.Reset) - { - history.RemoveAll(_ => true); - } - if (e.OldItems != null) - { - var oldAssemblies = new HashSet(e.OldItems.Cast()); - history.RemoveAll(n => n.TreeNodes.Any( - nd => nd.AncestorsAndSelf().OfType().Any( - a => oldAssemblies.Contains(a.LoadedAssembly)))); - } - - MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(e)); - } - - void LoadInitialAssemblies() - { - // Called when loading an empty assembly list; so that - // the user can see something initially. - System.Reflection.Assembly[] initialAssemblies = { - typeof(object).Assembly, - typeof(Uri).Assembly, - typeof(System.Linq.Enumerable).Assembly, - typeof(System.Xml.XmlDocument).Assembly, - typeof(System.Windows.Markup.MarkupExtension).Assembly, - typeof(System.Windows.Rect).Assembly, - typeof(System.Windows.UIElement).Assembly, - typeof(System.Windows.FrameworkElement).Assembly - }; - foreach (System.Reflection.Assembly asm in initialAssemblies) - assemblyList.OpenAssembly(asm.Location); - } - - void LanguageSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Language" || e.PropertyName == "LanguageVersion") - { - DecompileSelectedNodes(recordHistory: false); - } - } - - internal AssemblyListTreeNode AssemblyListTreeNode { - get { return assemblyListTreeNode; } - } - - #region Node Selection - - public void SelectNode(SharpTreeNode obj) - { - SelectNode(obj, false); - } - - public void SelectNode(SharpTreeNode obj, bool inNewTabPage) - { - SelectNode(obj, inNewTabPage, true); - } - - public void SelectNode(SharpTreeNode obj, bool inNewTabPage, bool setFocus) - { - if (obj != null) - { - if (!obj.AncestorsAndSelf().Any(node => node.IsHidden)) - { - if (inNewTabPage) - { - DockWorkspace.Instance.TabPages.Add(); - AssemblyTreeView.SelectedItem = null; - } - - // Set both the selection and focus to ensure that keyboard navigation works as expected. - if (setFocus) - { - AssemblyTreeView.FocusNode(obj); - } - else - { - AssemblyTreeView.ScrollIntoView(obj); - } - AssemblyTreeView.SelectedItem = obj; - } - else - { - MessageBox.Show(Properties.Resources.NavigationFailed, "ILSpy", MessageBoxButton.OK, MessageBoxImage.Exclamation); - } - } - } - - public void SelectNodes(IEnumerable nodes) - { - SelectNodes(nodes, false); - } - - public void SelectNodes(IEnumerable nodes, bool inNewTabPage) - { - SelectNodes(nodes, inNewTabPage, true); - } - - public void SelectNodes(IEnumerable nodes, bool inNewTabPage, bool setFocus) - { - SelectNodes(nodes, inNewTabPage, setFocus, false); - } - - internal void SelectNodes(IEnumerable nodes, bool inNewTabPage, - bool setFocus, bool changingActiveTab) - { - if (inNewTabPage) - { - DockWorkspace.Instance.TabPages.Add(); - } - - // Ensure nodes exist - var nodesList = nodes.Select(n => FindNodeByPath(GetPathForNode(n), true)) - .Where(n => n != null).ToArray(); - - if (!nodesList.Any() || !nodesList.All(n => !n.AncestorsAndSelf().Any(a => a.IsHidden))) - { - return; - } - - this.changingActiveTab = changingActiveTab || inNewTabPage; - try - { - if (setFocus) - { - AssemblyTreeView.FocusNode(nodesList[0]); - } - else - { - AssemblyTreeView.ScrollIntoView(nodesList[0]); - } - - AssemblyTreeView.SetSelectedNodes(nodesList); - } - finally - { - this.changingActiveTab = false; - } - } - - /// - /// Retrieves a node using the .ToString() representations of its ancestors. - /// - public SharpTreeNode FindNodeByPath(string[] path, bool returnBestMatch) - { - if (path == null) - return null; - SharpTreeNode node = AssemblyTreeView.Root; - SharpTreeNode bestMatch = node; - foreach (var element in path) - { - if (node == null) - break; - bestMatch = node; - node.EnsureLazyChildren(); - var ilSpyTreeNode = node as ILSpyTreeNode; - if (ilSpyTreeNode != null) - ilSpyTreeNode.EnsureChildrenFiltered(); - node = node.Children.FirstOrDefault(c => c.ToString() == element); - } - if (returnBestMatch) - return node ?? bestMatch; - else - return node; - } - - /// - /// Gets the .ToString() representation of the node's ancestors. - /// - public static string[] GetPathForNode(SharpTreeNode node) - { - if (node == null) - return null; - List path = new List(); - while (node.Parent != null) - { - path.Add(node.ToString()); - node = node.Parent; - } - path.Reverse(); - return path.ToArray(); - } - - public ILSpyTreeNode FindTreeNode(object reference) - { - switch (reference) - { - case LoadedAssembly lasm: - return assemblyListTreeNode.FindAssemblyNode(lasm); - case MetadataFile asm: - return assemblyListTreeNode.FindAssemblyNode(asm); - case Resource res: - return assemblyListTreeNode.FindResourceNode(res); - case ValueTuple resName: - return assemblyListTreeNode.FindResourceNode(resName.Item1, resName.Item2); - case ITypeDefinition type: - return assemblyListTreeNode.FindTypeNode(type); - case IField fd: - return assemblyListTreeNode.FindFieldNode(fd); - case IMethod md: - return assemblyListTreeNode.FindMethodNode(md); - case IProperty pd: - return assemblyListTreeNode.FindPropertyNode(pd); - case IEvent ed: - return assemblyListTreeNode.FindEventNode(ed); - case INamespace nd: - return AssemblyListTreeNode.FindNamespaceNode(nd); - default: - return null; - } - } - - public void JumpToReference(object reference) - { - JumpToReference(reference, inNewTabPage: false); - } - - public void JumpToReference(object reference, bool inNewTabPage) - { - JumpToReferenceAsync(reference, inNewTabPage).HandleExceptions(); - } - - /// - /// Jumps to the specified reference. - /// - /// - /// Returns a task that will signal completion when the decompilation of the jump target has finished. - /// The task will be marked as canceled if the decompilation is canceled. - /// - public Task JumpToReferenceAsync(object reference) - { - return JumpToReferenceAsync(reference, inNewTabPage: false); - } - - public Task JumpToReferenceAsync(object reference, bool inNewTabPage) - { - decompilationTask = TaskHelper.CompletedTask; - switch (reference) - { - case Decompiler.Disassembler.OpCodeInfo opCode: - OpenLink(opCode.Link); - break; - case EntityReference unresolvedEntity: - string protocol = unresolvedEntity.Protocol ?? "decompile"; - var file = unresolvedEntity.ResolveAssembly(assemblyList); - if (file == null) - { - break; - } - if (protocol != "decompile") - { - var protocolHandlers = App.ExportProvider.GetExports(); - foreach (var handler in protocolHandlers) - { - var node = handler.Value.Resolve(protocol, file, unresolvedEntity.Handle, out bool newTabPage); - if (node != null) - { - SelectNode(node, newTabPage || inNewTabPage); - return decompilationTask; - } - } - } - var possibleToken = MetadataTokenHelpers.TryAsEntityHandle(MetadataTokens.GetToken(unresolvedEntity.Handle)); - if (possibleToken != null) - { - var typeSystem = new DecompilerTypeSystem(file, file.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached); - reference = typeSystem.MainModule.ResolveEntity(possibleToken.Value); - goto default; - } - break; - default: - ILSpyTreeNode treeNode = FindTreeNode(reference); - if (treeNode != null) - SelectNode(treeNode, inNewTabPage); - break; - } - return decompilationTask; - } - public static void OpenLink(string link) { try @@ -1274,7 +670,6 @@ namespace ICSharpCode.ILSpy // just ignore all of them. } } - #endregion #region Open/Refresh void OpenCommandExecuted(object sender, ExecutedRoutedEventArgs e) @@ -1296,52 +691,14 @@ namespace ICSharpCode.ILSpy throw new ArgumentNullException(nameof(fileNames)); if (focusNode) - AssemblyTreeView.UnselectAll(); + AssemblyTreeModel.UnselectAll(); - LoadAssemblies(fileNames, focusNode: focusNode); - } - - void LoadAssemblies(IEnumerable fileNames, List loadedAssemblies = null, bool focusNode = true) - { - SharpTreeNode lastNode = null; - foreach (string file in fileNames) - { - var asm = assemblyList.OpenAssembly(file); - if (asm != null) - { - if (loadedAssemblies != null) - { - loadedAssemblies.Add(asm); - } - else - { - var node = assemblyListTreeNode.FindAssemblyNode(asm); - if (node != null && focusNode) - { - AssemblyTreeView.SelectedItems.Add(node); - lastNode = node; - } - } - } - - if (lastNode != null && focusNode) - AssemblyTreeView.FocusNode(lastNode); - } + AssemblyTreeModel.LoadAssemblies(fileNames, focusNode: focusNode); } void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - try - { - refreshInProgress = true; - var path = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); - ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(assemblyList.ListName)); - SelectNode(FindNodeByPath(path, true), inNewTabPage: false, AssemblyTreeView.IsFocused); - } - finally - { - refreshInProgress = false; - } + AssemblyTreeModel.Refresh(); } void SearchCommandExecuted(object sender, ExecutedRoutedEventArgs e) @@ -1350,114 +707,16 @@ namespace ICSharpCode.ILSpy } #endregion - #region Decompile (TreeView_SelectionChanged) - bool delayDecompilationRequestDueToContextMenu; - - protected override void OnContextMenuClosing(ContextMenuEventArgs e) - { - base.OnContextMenuClosing(e); - - if (delayDecompilationRequestDueToContextMenu) - { - delayDecompilationRequestDueToContextMenu = false; - var state = DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState; - DecompileSelectedNodes(state); - } - } - - void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - DecompilerTextViewState state = null; - if (refreshInProgress || changingActiveTab) - { - state = DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState; - } - - this.delayDecompilationRequestDueToContextMenu = Mouse.RightButton == MouseButtonState.Pressed; - if (!changingActiveTab && !delayDecompilationRequestDueToContextMenu) - { - DecompileSelectedNodes(state); - } - - SelectionChanged?.Invoke(sender, e); - } - - Task decompilationTask; - bool ignoreDecompilationRequests; - - void DecompileSelectedNodes(DecompilerTextViewState newState = null, bool recordHistory = true) - { - if (ignoreDecompilationRequests) - return; - - if (AssemblyTreeView.SelectedItems.Count == 0 && refreshInProgress) - return; - - if (recordHistory) - { - var tabPage = DockWorkspace.Instance.ActiveTabPage; - var currentState = tabPage.GetState(); - if (currentState != null) - history.UpdateCurrent(new NavigationState(tabPage, currentState)); - history.Record(new NavigationState(tabPage, AssemblyTreeView.SelectedItems.OfType())); - } - - DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching = true; - - if (AssemblyTreeView.SelectedItems.Count == 1) - { - ILSpyTreeNode node = AssemblyTreeView.SelectedItem as ILSpyTreeNode; - if (node != null && node.View(DockWorkspace.Instance.ActiveTabPage)) - return; - } - if (newState?.ViewedUri != null) - { - NavigateTo(new RequestNavigateEventArgs(newState.ViewedUri, null), recordHistory: false); - return; - } - var options = MainWindow.Instance.CreateDecompilationOptions(); - options.TextViewState = newState; - decompilationTask = DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync( - textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options) - ); - } - void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.Handled = true; - e.CanExecute = SaveCodeContextMenuEntry.CanExecute(SelectedNodes.ToList()); + e.CanExecute = SaveCodeContextMenuEntry.CanExecute(AssemblyTreeModel.SelectedNodes.ToList()); } void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - SaveCodeContextMenuEntry.Execute(SelectedNodes.ToList()); - } - - public void RefreshDecompiledView() - { - try - { - refreshInProgress = true; - DecompileSelectedNodes(); - } - finally - { - refreshInProgress = false; - } - } - - public Language CurrentLanguage => SettingsService.Instance.SessionSettings.LanguageSettings.Language; - - public LanguageVersion CurrentLanguageVersion => SettingsService.Instance.SessionSettings.LanguageSettings.LanguageVersion; - - public event SelectionChangedEventHandler SelectionChanged; - - public IEnumerable SelectedNodes { - get { - return AssemblyTreeView.GetTopLevelSelection().OfType(); - } + SaveCodeContextMenuEntry.Execute(AssemblyTreeModel.SelectedNodes.ToList()); } - #endregion #region Back/Forward navigation void BackCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) @@ -1498,17 +757,10 @@ namespace ICSharpCode.ILSpy history.UpdateCurrent(new NavigationState(tabPage, state)); var newState = forward ? history.GoForward() : history.GoBack(); - ignoreDecompilationRequests = true; - AssemblyTreeView.SelectedItems.Clear(); DockWorkspace.Instance.ActiveTabPage = newState.TabPage; - foreach (var node in newState.TreeNodes) - { - AssemblyTreeView.SelectedItems.Add(node); - } - if (newState.TreeNodes.Any()) - AssemblyTreeView.FocusNode(newState.TreeNodes.First()); - ignoreDecompilationRequests = false; - DecompileSelectedNodes(newState.ViewState as DecompilerTextViewState, false); + + AssemblyTreeModel.SelectNodes(newState.TreeNodes, ignoreCompilationRequests: true); + AssemblyTreeModel.DecompileSelectedNodes(newState.ViewState as DecompilerTextViewState, false); } #endregion @@ -1558,9 +810,8 @@ namespace ICSharpCode.ILSpy var currentState = tabPage.GetState(); if (currentState != null) history.UpdateCurrent(new NavigationState(tabPage, currentState)); - ignoreDecompilationRequests = true; - UnselectAll(); - ignoreDecompilationRequests = false; + + AssemblyTreeModel.UnselectAll(ignoreCompilationRequests: true); history.Record(new NavigationState(tabPage, new ViewState { ViewedUri = e.Uri })); } } @@ -1569,7 +820,7 @@ namespace ICSharpCode.ILSpy { base.OnStateChanged(e); // store window state in settings only if it's not minimized - if (this.WindowState != System.Windows.WindowState.Minimized) + if (this.WindowState != WindowState.Minimized) SettingsService.Instance.SessionSettings.WindowState = this.WindowState; } @@ -1578,9 +829,9 @@ namespace ICSharpCode.ILSpy base.OnClosing(e); var sessionSettings = SettingsService.Instance.SessionSettings; - sessionSettings.ActiveAssemblyList = assemblyList.ListName; - sessionSettings.ActiveTreeViewPath = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); - sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeView.SelectedItem as SharpTreeNode); + sessionSettings.ActiveAssemblyList = AssemblyTreeModel.CurrentAssemblyList.ListName; + sessionSettings.ActiveTreeViewPath = AssemblyTreeModel.SelectedPath; + sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeModel.SelectedItem); sessionSettings.WindowBounds = this.RestoreBounds; sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(dockManager)); sessionSettings.Save(); @@ -1590,12 +841,12 @@ namespace ICSharpCode.ILSpy { if (node == null) return null; - while (!(node is TreeNodes.AssemblyTreeNode) && node.Parent != null) + while (!(node is AssemblyTreeNode) && node.Parent != null) { node = node.Parent; } //this should be an assembly node - var assyNode = node as TreeNodes.AssemblyTreeNode; + var assyNode = node as AssemblyTreeNode; var loadedAssy = assyNode.LoadedAssembly; if (!(loadedAssy.IsLoaded && loadedAssy.IsAutoLoaded)) return null; @@ -1603,11 +854,6 @@ namespace ICSharpCode.ILSpy return loadedAssy.FileName; } - public void UnselectAll() - { - AssemblyTreeView.UnselectAll(); - } - public void SetStatus(string status, Brush foreground) { if (this.statusBar.Visibility == Visibility.Collapsed) diff --git a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs index f36164b16..4d654da91 100644 --- a/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ClassLayoutTableTreeNode.cs @@ -97,7 +97,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference("metadata", classLayout.Parent)); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference("metadata", classLayout.Parent))); } string parentTooltip; diff --git a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs index e6c41e5aa..2b2ea571f 100644 --- a/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ConstantTableTreeNode.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, constant.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, constant.Parent, protocol: "metadata"))); } string parentTooltip; diff --git a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs index bae1ceffd..370532d14 100644 --- a/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/CustomAttributeTableTreeNode.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, customAttr.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, customAttr.Parent, protocol: "metadata"))); } string parentTooltip; @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnConstructorClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, customAttr.Constructor, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, customAttr.Constructor, protocol: "metadata"))); } string constructorTooltip; diff --git a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs index b719f678c..f37bdb87d 100644 --- a/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/DeclSecurityTableTreeNode.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, declSecAttr.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, declSecAttr.Parent, protocol: "metadata"))); } string parentTooltip; diff --git a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs index 44ea708f1..3018d8970 100644 --- a/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventMapTableTreeNode.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, eventMap.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventMap.Parent, protocol: "metadata"))); } string parentTooltip; @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnEventListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, eventMap.EventList, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventMap.EventList, protocol: "metadata"))); } string eventListTooltip; diff --git a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs index 7e3c95ca5..14dabb3e0 100644 --- a/ILSpy/Metadata/CorTables/EventTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/EventTableTreeNode.cs @@ -103,7 +103,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnTypeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, eventDef.Type, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, eventDef.Type, protocol: "metadata"))); } string typeTooltip; diff --git a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs index dc88622c6..f1075c16b 100644 --- a/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ExportedTypeTableTreeNode.cs @@ -107,7 +107,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnImplementationClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, type.Implementation, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, type.Implementation, protocol: "metadata"))); } string implementationTooltip; diff --git a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs index ba014ccc0..6fe92c597 100644 --- a/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldLayoutTableTreeNode.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnFieldClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, fieldLayout.Field, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldLayout.Field, protocol: "metadata"))); } string fieldTooltip; diff --git a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs index 7bb2c1da2..befd90a8c 100644 --- a/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldMarshalTableTreeNode.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, fieldMarshal.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldMarshal.Parent, protocol: "metadata"))); } string parentTooltip; diff --git a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs index f95f21a29..959225bdb 100644 --- a/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/FieldRVATableTreeNode.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnFieldClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, fieldRVA.Field, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, fieldRVA.Field, protocol: "metadata"))); } string fieldTooltip; diff --git a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs index d7af52a19..0e2180210 100644 --- a/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamConstraintTableTreeNode.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnOwnerClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, genericParamConstraint.Parameter, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParamConstraint.Parameter, protocol: "metadata"))); } string ownerTooltip; @@ -108,7 +108,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnTypeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, genericParamConstraint.Type, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParamConstraint.Type, protocol: "metadata"))); } string typeTooltip; diff --git a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs index 1ebc8f106..29509426b 100644 --- a/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/GenericParamTableTreeNode.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnOwnerClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, genericParam.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, genericParam.Parent, protocol: "metadata"))); } string ownerTooltip; diff --git a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs index e47456fb5..efe6e6fa1 100644 --- a/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ImplMapTableTreeNode.cs @@ -113,7 +113,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMemberForwardedClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, implMap.MemberForwarded, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, implMap.MemberForwarded, protocol: "metadata"))); } string memberForwardedTooltip; @@ -124,7 +124,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnImportScopeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, implMap.ImportScope, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, implMap.ImportScope, protocol: "metadata"))); } string importScopeTooltip; diff --git a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs index 72e2a6d8b..04b87015d 100644 --- a/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/InterfaceImplTableTreeNode.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnClassClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, interfaceImpl.Class, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, interfaceImpl.Class, protocol: "metadata"))); } string classTooltip; @@ -107,7 +107,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnInterfaceClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, interfaceImpl.Interface, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, interfaceImpl.Interface, protocol: "metadata"))); } string interfaceTooltip; diff --git a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs index fca47ac3d..5bd75eb91 100644 --- a/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/ManifestResourceTableTreeNode.cs @@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnImplementationClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, manifestResource.Implementation, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, manifestResource.Implementation, protocol: "metadata"))); } string implementationTooltip; diff --git a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs index fc2d01865..9f858434d 100644 --- a/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MemberRefTableTreeNode.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, memberRef.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, memberRef.Parent, protocol: "metadata"))); } string parentTooltip; diff --git a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs index 2edd46feb..2344f6ff1 100644 --- a/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodImplTableTreeNode.cs @@ -82,7 +82,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodDeclarationClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, methodImpl.MethodDeclaration, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.MethodDeclaration, protocol: "metadata"))); } string methodDeclarationTooltip; @@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodBodyClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, methodImpl.MethodBody, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.MethodBody, protocol: "metadata"))); } string methodBodyTooltip; @@ -104,7 +104,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnTypeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, methodImpl.Type, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodImpl.Type, protocol: "metadata"))); } string typeTooltip; diff --git a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs index 62f22c8a9..49cb863f0 100644 --- a/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSemanticsTableTreeNode.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, method, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, method, protocol: "metadata"))); } string methodTooltip; @@ -101,7 +101,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnAssociationClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, association, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, association, protocol: "metadata"))); } string associationTooltip; diff --git a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs index 7e8acf6f5..d25a71dd3 100644 --- a/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodSpecTableTreeNode.cs @@ -85,7 +85,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, methodSpec.Method, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodSpec.Method, protocol: "metadata"))); } string methodTooltip; diff --git a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs index b6f9ac6b4..cfea5f2ae 100644 --- a/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/MethodTableTreeNode.cs @@ -118,7 +118,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParamListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, methodDef.GetParameters().FirstOrDefault(), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, methodDef.GetParameters().FirstOrDefault(), protocol: "metadata"))); } string paramListTooltip; diff --git a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs index 02fcd5233..e95619be7 100644 --- a/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/NestedClassTableTreeNode.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnNestedClassClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, nestedClass.Nested, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, nestedClass.Nested, protocol: "metadata"))); } string nestedClassTooltip; @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnEnclosingClassClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, nestedClass.Enclosing, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, nestedClass.Enclosing, protocol: "metadata"))); } string enclosingClassTooltip; diff --git a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs index 7bbfcf09b..6071961f3 100644 --- a/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/PropertyMapTableTreeNode.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, propertyMap.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, propertyMap.Parent, protocol: "metadata"))); } string parentTooltip; @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnPropertyListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, propertyMap.PropertyList, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, propertyMap.PropertyList, protocol: "metadata"))); } string propertyListTooltip; diff --git a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs index 8f9ac73fb..129f76907 100644 --- a/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeDefTableTreeNode.cs @@ -111,7 +111,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnBaseTypeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, typeDef.BaseType, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.BaseType, protocol: "metadata"))); } public string BaseTypeTooltip { @@ -142,7 +142,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnFieldListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, typeDef.GetFields().FirstOrDefault(), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.GetFields().FirstOrDefault(), protocol: "metadata"))); } string fieldListTooltip; @@ -160,7 +160,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, typeDef.GetMethods().FirstOrDefault(), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeDef.GetMethods().FirstOrDefault(), protocol: "metadata"))); } string methodListTooltip; diff --git a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs index aeaead608..6b086fea1 100644 --- a/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs +++ b/ILSpy/Metadata/CorTables/TypeRefTableTreeNode.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnResolutionScopeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, typeRef.ResolutionScope, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, typeRef.ResolutionScope, protocol: "metadata"))); } string resolutionScopeTooltip; diff --git a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs index 9ec211751..d3067d713 100644 --- a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs +++ b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs @@ -21,7 +21,6 @@ using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs index f9ef9f369..b020f5a97 100644 --- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs @@ -188,7 +188,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, debugInfo.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.Parent, protocol: "metadata"))); } string parentTooltip; diff --git a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs index 750a18ae7..65639ad1f 100644 --- a/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/ImportScopeTableTreeNode.cs @@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnParentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, localScope.Parent, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.Parent, protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)] diff --git a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs index 8b0f1e888..f25412f98 100644 --- a/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/LocalScopeTableTreeNode.cs @@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMethodClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, localScope.Method, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.Method, protocol: "metadata"))); } string methodTooltip; @@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnImportScopeClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, localScope.ImportScope, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.ImportScope, protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.Token)] @@ -100,7 +100,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnVariableListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, localScope.GetLocalVariables().FirstOrDefault(), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.GetLocalVariables().FirstOrDefault(), protocol: "metadata"))); } [ColumnInfo("X8", Kind = ColumnKind.Token)] @@ -108,7 +108,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnConstantListClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, localScope.GetLocalConstants().FirstOrDefault(), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, localScope.GetLocalConstants().FirstOrDefault(), protocol: "metadata"))); } public int StartOffset => localScope.StartOffset; diff --git a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs index 80aede7fc..5453ce6f7 100644 --- a/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/MethodDebugInformationTableTreeNode.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnDocumentClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, debugInfo.Document, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.Document, protocol: "metadata"))); } public string DocumentTooltip { @@ -116,7 +116,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnLocalSignatureClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, debugInfo.LocalSignature, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, debugInfo.LocalSignature, protocol: "metadata"))); } public string LocalSignatureTooltip { diff --git a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs index 9341cad78..d4f778ed2 100644 --- a/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs +++ b/ILSpy/Metadata/DebugTables/StateMachineMethodTableTreeNode.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnMoveNextMethodClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, moveNextMethod, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, moveNextMethod, protocol: "metadata"))); } string moveNextMethodTooltip; @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.Metadata public void OnKickofMethodClick() { - MainWindow.Instance.JumpToReference(new EntityReference(metadataFile, kickoffMethod, protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(metadataFile, kickoffMethod, protocol: "metadata"))); } string kickoffMethodTooltip; diff --git a/ILSpy/Metadata/GoToTokenCommand.cs b/ILSpy/Metadata/GoToTokenCommand.cs index 1c0caca2b..d451f27ab 100644 --- a/ILSpy/Metadata/GoToTokenCommand.cs +++ b/ILSpy/Metadata/GoToTokenCommand.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy.Commands public void Execute(TextViewContext context) { int token = GetSelectedToken(context.DataGrid, out MetadataFile module).Value; - MainWindow.Instance.JumpToReference(new EntityReference(module, MetadataTokens.Handle(token), protocol: "metadata")); + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, MetadataTokens.Handle(token), protocol: "metadata"))); } public bool IsEnabled(TextViewContext context) diff --git a/ILSpy/Metadata/MetadataProtocolHandler.cs b/ILSpy/Metadata/MetadataProtocolHandler.cs index c49e300d7..8a50cac36 100644 --- a/ILSpy/Metadata/MetadataProtocolHandler.cs +++ b/ILSpy/Metadata/MetadataProtocolHandler.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.ILSpy.Metadata newTabPage = true; if (protocol != "metadata") return null; - var assemblyTreeNode = MainWindow.Instance.FindTreeNode(module) as AssemblyTreeNode; + var assemblyTreeNode = MainWindow.Instance.AssemblyTreeModel.FindTreeNode(module) as AssemblyTreeNode; if (assemblyTreeNode == null) return null; var mxNode = assemblyTreeNode.Children.OfType().FirstOrDefault(); diff --git a/ILSpy/Metadata/MetadataTablesTreeNode.cs b/ILSpy/Metadata/MetadataTablesTreeNode.cs index 3724ba590..1fb55be81 100644 --- a/ILSpy/Metadata/MetadataTablesTreeNode.cs +++ b/ILSpy/Metadata/MetadataTablesTreeNode.cs @@ -23,7 +23,6 @@ using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index d530079dc..892c8ea4f 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -19,7 +19,6 @@ using System.ComponentModel.Composition; using System.Xml.Linq; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.Options diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 9645dacc7..95b56b4ee 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -27,7 +27,6 @@ using System.Windows.Media; using System.Windows.Threading; using System.Xml.Linq; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.Options diff --git a/ILSpy/Search/SearchPane.xaml.cs b/ILSpy/Search/SearchPane.xaml.cs index a3a7584a2..2a5a104d6 100644 --- a/ILSpy/Search/SearchPane.xaml.cs +++ b/ILSpy/Search/SearchPane.xaml.cs @@ -32,7 +32,6 @@ using System.Windows.Media; using System.Windows.Threading; using ICSharpCode.ILSpy.AppEnv; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; @@ -250,8 +249,8 @@ namespace ICSharpCode.ILSpy.Search { searchProgressBar.IsIndeterminate = true; - startedSearch = new(await mainWindow.CurrentAssemblyList.GetAllAssemblies(), searchTerm, - (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage, + startedSearch = new(await mainWindow.AssemblyTreeModel.CurrentAssemblyList.GetAllAssemblies(), searchTerm, + (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.AssemblyTreeModel.CurrentLanguage, SettingsService.Instance.SessionSettings.LanguageSettings.ShowApiLevel); currentSearch = startedSearch; @@ -269,7 +268,7 @@ namespace ICSharpCode.ILSpy.Search { if (listBox.SelectedItem is SearchResult result) { - MainWindow.Instance.JumpToReference(result.Reference); + MessageBus.Send(this, new NavigateToReferenceEventArgs(result.Reference)); } } diff --git a/ILSpy/Search/SearchPaneModel.cs b/ILSpy/Search/SearchPaneModel.cs index 6ddcd9f04..b3bc30c40 100644 --- a/ILSpy/Search/SearchPaneModel.cs +++ b/ILSpy/Search/SearchPaneModel.cs @@ -20,7 +20,6 @@ using System.ComponentModel.Composition; using System.Windows.Input; using System.Windows.Media; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX.Search; diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs index ad58d121f..414d644cb 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -28,7 +28,6 @@ using System.Xml.Linq; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Themes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Search; using ICSharpCode.ILSpyX.Settings; diff --git a/ILSpy/TaskHelper.cs b/ILSpy/TaskHelper.cs index e26eaf062..5b33bff3c 100644 --- a/ILSpy/TaskHelper.cs +++ b/ILSpy/TaskHelper.cs @@ -198,7 +198,7 @@ namespace ICSharpCode.ILSpy public static void HandleExceptions(this Task task) { task.Catch(exception => MainWindow.Instance.Dispatcher.BeginInvoke(new Action(delegate { - AvalonEditTextOutput output = new AvalonEditTextOutput(); + AvalonEditTextOutput output = new(); output.Write(exception.ToString()); Docking.DockWorkspace.Instance.ShowText(output); }))).IgnoreExceptions(); diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 1124a73e5..842fc860f 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -52,11 +52,11 @@ using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.TreeNodes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; @@ -414,7 +414,7 @@ namespace ICSharpCode.ILSpy.TextView } else if (segment.Reference is EntityReference unresolvedEntity) { - var module = unresolvedEntity.ResolveAssembly(MainWindow.Instance.CurrentAssemblyList); + var module = unresolvedEntity.ResolveAssembly(MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList); if (module == null) return null; var typeSystem = new DecompilerTypeSystem(module, @@ -474,7 +474,7 @@ namespace ICSharpCode.ILSpy.TextView IEntity? ResolveReference(string idString) { - return MainWindow.FindEntityInRelevantAssemblies(idString, MainWindow.Instance.CurrentAssemblyList.GetAssemblies()); + return AssemblyListPaneModel.FindEntityInRelevantAssemblies(idString, MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies()); } } @@ -1001,7 +1001,7 @@ namespace ICSharpCode.ILSpy.TextView return; } } - MainWindow.Instance.JumpToReference(reference, openInNewTab); + MessageBus.Send(this, new NavigateToReferenceEventArgs(reference, openInNewTab)); } Point? mouseDownPos; diff --git a/ILSpy/TextView/DocumentationUIBuilder.cs b/ILSpy/TextView/DocumentationUIBuilder.cs index bc7a9a409..4d0c5eb78 100644 --- a/ILSpy/TextView/DocumentationUIBuilder.cs +++ b/ILSpy/TextView/DocumentationUIBuilder.cs @@ -37,7 +37,6 @@ using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Options; -using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.TextView { @@ -339,7 +338,7 @@ namespace ICSharpCode.ILSpy.TextView { var h = new Hyperlink(new Run(ambience.ConvertSymbol(referencedEntity))); h.Click += (sender, e) => { - MainWindow.Instance.JumpToReference(referencedEntity); + MessageBus.Send(this, new NavigateToReferenceEventArgs(referencedEntity)); }; return h; } @@ -382,7 +381,7 @@ namespace ICSharpCode.ILSpy.TextView { Hyperlink link = new Hyperlink(); link.Click += (sender, e) => { - MainWindow.Instance.JumpToReference(referencedEntity); + MessageBus.Send(this, new NavigateToReferenceEventArgs(referencedEntity)); }; AddSpan(link, element.Children); } diff --git a/ILSpy/Themes/WindowStyleManagerBehavior.cs b/ILSpy/Themes/WindowStyleManagerBehavior.cs index 60e771f1a..681fbf97e 100644 --- a/ILSpy/Themes/WindowStyleManagerBehavior.cs +++ b/ILSpy/Themes/WindowStyleManagerBehavior.cs @@ -24,7 +24,6 @@ using System.Windows.Interop; using System.Windows.Media; using ICSharpCode.ILSpy.Options; -using ICSharpCode.ILSpy.Util; using TomsToolbox.Essentials; using TomsToolbox.Wpf; diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index 9808ddcfd..c7668c1c5 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -24,10 +24,9 @@ using System.Windows; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpyX; -using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView; +using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.TreeNodes { @@ -87,13 +86,10 @@ namespace ICSharpCode.ILSpy.TreeNodes e.Effects = XPlatDragDropEffects.Move | XPlatDragDropEffects.Copy | XPlatDragDropEffects.Link; if (e.Data.GetDataPresent(AssemblyTreeNode.DataFormat)) return true; - else if (e.Data.GetDataPresent(DataFormats.FileDrop)) + if (e.Data.GetDataPresent(DataFormats.FileDrop)) return true; - else - { - e.Effects = XPlatDragDropEffects.None; - return false; - } + e.Effects = XPlatDragDropEffects.None; + return false; } public override void Drop(IPlatformDragEventArgs e, int index) @@ -110,8 +106,8 @@ namespace ICSharpCode.ILSpy.TreeNodes .Distinct() .ToArray(); assemblyList.Move(assemblies, index); - var nodes = assemblies.SelectArray(MainWindow.Instance.FindTreeNode); - MainWindow.Instance.SelectNodes(nodes); + var nodes = assemblies.SelectArray(MainWindow.Instance.AssemblyTreeModel.FindTreeNode); + MainWindow.Instance.AssemblyTreeModel.SelectNodes(nodes); } } @@ -187,7 +183,7 @@ namespace ICSharpCode.ILSpy.TreeNodes if (bundle == null) return null; bundle.EnsureLazyChildren(); - foreach (var node in ILSpyX.TreeView.TreeTraversal.PreOrder(bundle.Children, ExpandAndGetChildren).OfType()) + foreach (var node in TreeTraversal.PreOrder(bundle.Children, ExpandAndGetChildren).OfType()) { if (node.LoadedAssembly == asm) return node; diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 25602d875..57a326353 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -26,7 +26,6 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Themes; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.TreeNodes diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index d2f102ef2..acbe3fcf5 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -30,10 +30,10 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpy.Metadata; using ICSharpCode.ILSpy.Properties; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; @@ -630,13 +630,13 @@ namespace ICSharpCode.ILSpy.TreeNodes { foreach (var node in context.SelectedTreeNodes) { - paths.Add(MainWindow.GetPathForNode(node)); + paths.Add(AssemblyListPaneModel.GetPathForNode(node)); var la = ((AssemblyTreeNode)node).LoadedAssembly; la.AssemblyList.ReloadAssembly(la.FileName); } } - MainWindow.Instance.SelectNodes(paths.Select(p => MainWindow.Instance.FindNodeByPath(p, true)).ToArray()); - MainWindow.Instance.RefreshDecompiledView(); + MainWindow.Instance.AssemblyTreeModel.SelectNodes(paths.Select(p => MainWindow.Instance.AssemblyTreeModel.FindNodeByPath(p, true)).ToArray()); + MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView(); } } @@ -676,7 +676,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } await Task.WhenAll(tasks); - MainWindow.Instance.RefreshDecompiledView(); + MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView(); } } @@ -711,7 +711,7 @@ namespace ICSharpCode.ILSpy.TreeNodes node.RaisePropertyChanged(nameof(ILSpyTreeNode.IsAutoLoaded)); } } - MainWindow.Instance.CurrentAssemblyList.RefreshSave(); + MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.RefreshSave(); } } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index e3958a1bb..3c319ab54 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -27,7 +27,6 @@ using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Abstractions; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView; @@ -72,8 +71,8 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void ActivateItemSecondary(IPlatformRoutedEventArgs e) { - MainWindow.Instance.SelectNode(this, inNewTabPage: true); - MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); + MainWindow.Instance.AssemblyTreeModel.SelectNode(this, inNewTabPage: true); + MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.AssemblyTreeModel.RefreshDecompiledView); } /// diff --git a/ILSpy/Util/MessageBus.cs b/ILSpy/Util/MessageBus.cs index 7573cb33c..40d8743b8 100644 --- a/ILSpy/Util/MessageBus.cs +++ b/ILSpy/Util/MessageBus.cs @@ -57,4 +57,13 @@ namespace ICSharpCode.ILSpy.Util public class SessionSettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs(e); public class DockWorkspaceActiveTabPageChangedEventArgs : EventArgs; + + public class NavigateToReferenceEventArgs(object reference, bool inNewTabPage = false) : EventArgs + { + public object Reference { get; } = reference; + + public bool InNewTabPage { get; } = inNewTabPage; + } + + public class AssemblyTreeSelectionChangedEventArgs() : EventArgs; } \ No newline at end of file diff --git a/ILSpy/Util/NavigationHistoryService.cs b/ILSpy/Util/NavigationHistoryService.cs new file mode 100644 index 000000000..a13304663 --- /dev/null +++ b/ILSpy/Util/NavigationHistoryService.cs @@ -0,0 +1,48 @@ +using System; + +namespace ICSharpCode.ILSpy.Util +{ + public class NavigationHistoryService + { + readonly NavigationHistory history = new(); + + public static NavigationHistoryService Instance { get; } = new(); + + public bool CanNavigateBack => history.CanNavigateBack; + public bool CanNavigateForward => history.CanNavigateForward; + + private NavigationHistoryService() + { + } + + public void UpdateCurrent(NavigationState navigationState) + { + history.UpdateCurrent(navigationState); + } + + public void Record(NavigationState navigationState) + { + history.Record(navigationState); + } + + public NavigationState GoForward() + { + return history.GoForward(); + } + + public NavigationState GoBack() + { + return history.GoBack(); + } + + public void Clear() + { + history.Clear(); + } + + public void RemoveAll(Predicate predicate) + { + history.RemoveAll(predicate); + } + } +} diff --git a/ILSpy/ViewModels/AssemblyListPaneModel.cs b/ILSpy/ViewModels/AssemblyListPaneModel.cs deleted file mode 100644 index d6156cf8f..000000000 --- a/ILSpy/ViewModels/AssemblyListPaneModel.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System.ComponentModel.Composition; -using System.Windows; -using System.Windows.Input; - -using ICSharpCode.ILSpy.Properties; - -namespace ICSharpCode.ILSpy.ViewModels -{ - [ExportToolPane] - [PartCreationPolicy(CreationPolicy.Shared)] - public class AssemblyListPaneModel : ToolPaneModel - { - public const string PaneContentId = "assemblyListPane"; - - public AssemblyListPaneModel() - { - Title = Resources.Assemblies; - ContentId = PaneContentId; - IsCloseable = false; - ShortcutKey = new KeyGesture(Key.F6); - } - } -} diff --git a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs index ec039552f..76ab7aedb 100644 --- a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs +++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs @@ -27,7 +27,6 @@ using System.Windows.Input; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Properties; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy.ViewModels diff --git a/ILSpy/ViewModels/PaneModel.cs b/ILSpy/ViewModels/PaneModel.cs index 8663688e4..f9ef1b90e 100644 --- a/ILSpy/ViewModels/PaneModel.cs +++ b/ILSpy/ViewModels/PaneModel.cs @@ -21,7 +21,7 @@ using System.ComponentModel; using System.Windows; using System.Windows.Input; -using ICSharpCode.ILSpy.Docking; +using ICSharpCode.Decompiler.IL; using TomsToolbox.Wpf; @@ -29,6 +29,13 @@ namespace ICSharpCode.ILSpy.ViewModels { public abstract class PaneModel : ObservableObject { + private Throttle titleChangeThrottle; + + protected PaneModel() + { + titleChangeThrottle = new Throttle(() => OnPropertyChanged(nameof(Title))); + } + class CloseCommandImpl : ICommand { readonly PaneModel model; @@ -107,7 +114,10 @@ namespace ICSharpCode.ILSpy.ViewModels public string Title { get => title; - set => SetProperty(ref title, value); + set { + title = value; + titleChangeThrottle.Tick(); + } } } diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs index 10c28d47c..abcaa4666 100644 --- a/ILSpy/ViewModels/TabPageModel.cs +++ b/ILSpy/ViewModels/TabPageModel.cs @@ -20,8 +20,6 @@ using System; using System.Threading.Tasks; using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.Util; -using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy.ViewModels { diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index b619cc6b7..ff82daeba 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -8,7 +8,6 @@ using System.Windows.Input; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.ILSpy.Docking; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf.Composition.Mef; @@ -35,8 +34,8 @@ namespace ICSharpCode.ILSpy #if DEBUG MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); + MessageBus.Subscribers += SelectionChanged; - MainWindow.Instance.SelectionChanged += SelectionChanged; writingOptions.PropertyChanged += WritingOptions_PropertyChanged; if (SettingsService.Instance.SessionSettings.LanguageSettings.Language is ILAstLanguage l) @@ -53,7 +52,7 @@ namespace ICSharpCode.ILSpy DecompileAsync(lastSelectedStep); } - private void SelectionChanged(object sender, SelectionChangedEventArgs e) + private void SelectionChanged(object sender, EventArgs e) { Dispatcher.Invoke(() => { tree.ItemsSource = null; @@ -123,8 +122,8 @@ namespace ICSharpCode.ILSpy lastSelectedStep = step; var window = MainWindow.Instance; var state = DockWorkspace.Instance.ActiveTabPage.GetState(); - DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(window.CurrentLanguage, window.SelectedNodes, - new DecompilationOptions(window.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { + DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(window.AssemblyTreeModel.CurrentLanguage, window.AssemblyTreeModel.SelectedNodes, + new DecompilationOptions(window.AssemblyTreeModel.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { StepLimit = step, IsDebug = isDebug, TextViewState = state as TextView.DecompilerTextViewState diff --git a/TestPlugin/MainMenuCommand.cs b/TestPlugin/MainMenuCommand.cs index bb4a041f8..f2909e15b 100644 --- a/TestPlugin/MainMenuCommand.cs +++ b/TestPlugin/MainMenuCommand.cs @@ -23,7 +23,7 @@ namespace TestPlugin { public override void Execute(object parameter) { - foreach (var loadedAssembly in MainWindow.Instance.CurrentAssemblyList.GetAssemblies()) + foreach (var loadedAssembly in MainWindow.Instance.AssemblyTreeModel.CurrentAssemblyList.GetAssemblies()) { loadedAssembly.AssemblyList.Unload(loadedAssembly); }