Browse Source

Merge pull request #3550 from icsharpcode/fix/3521

#3521: Add API to set an initially highlighted entity after navigation
pull/3589/head
Christoph Wille 3 months ago committed by GitHub
parent
commit
e214742c91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 38
      ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs
  2. 20
      ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
  3. 22
      ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
  4. 10
      ILSpy/Analyzers/AnalyzerTreeViewModel.cs
  5. 4
      ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs
  6. 18
      ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
  7. 7
      ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
  8. 9
      ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
  9. 16
      ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs
  10. 13
      ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
  11. 9
      ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
  12. 16
      ILSpy/AssemblyTree/AssemblyTreeModel.cs
  13. 7
      ILSpy/TextView/AvalonEditTextOutput.cs
  14. 49
      ILSpy/TextView/DecompilerTextView.cs
  15. 4
      ILSpy/Util/MessageBus.cs
  16. 2
      ILSpy/Views/DebugSteps.xaml.cs

38
ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs

@ -21,21 +21,21 @@ using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
#nullable disable
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
namespace ICSharpCode.ILSpyX.TreeView namespace ICSharpCode.ILSpyX.TreeView
{ {
public partial class SharpTreeNode : INotifyPropertyChanged public partial class SharpTreeNode : INotifyPropertyChanged
{ {
[AllowNull]
protected static ITreeNodeImagesProvider ImagesProvider { get; private set; } protected static ITreeNodeImagesProvider ImagesProvider { get; private set; }
public static void SetImagesProvider(ITreeNodeImagesProvider provider) => ImagesProvider = provider; public static void SetImagesProvider(ITreeNodeImagesProvider provider) => ImagesProvider = provider;
SharpTreeNodeCollection modelChildren; SharpTreeNodeCollection? modelChildren;
internal SharpTreeNode modelParent; internal SharpTreeNode? modelParent;
bool isVisible = true; bool isVisible = true;
void UpdateIsVisible(bool parentIsVisible, bool updateFlattener) void UpdateIsVisible(bool parentIsVisible, bool updateFlattener)
@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpyX.TreeView
node = node.listParent; node = node.listParent;
} }
// Remember the removed nodes: // Remember the removed nodes:
List<SharpTreeNode> removedNodes = null; List<SharpTreeNode>? removedNodes = null;
if (updateFlattener && !newIsVisible) if (updateFlattener && !newIsVisible)
{ {
removedNodes = VisibleDescendantsAndSelf().ToList(); removedNodes = VisibleDescendantsAndSelf().ToList();
@ -118,19 +118,19 @@ namespace ICSharpCode.ILSpyX.TreeView
} }
} }
public SharpTreeNode Parent { public SharpTreeNode? Parent {
get { return modelParent; } get { return modelParent; }
} }
public virtual object Text { public virtual object? Text {
get { return null; } get { return null; }
} }
public virtual object Icon { public virtual object? Icon {
get { return null; } get { return null; }
} }
public virtual object ToolTip { public virtual object? ToolTip {
get { return null; } get { return null; }
} }
@ -199,7 +199,7 @@ namespace ICSharpCode.ILSpyX.TreeView
while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0) while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0)
removeEnd = removeEnd.modelChildren.Last(); removeEnd = removeEnd.modelChildren.Last();
List<SharpTreeNode> removedNodes = null; List<SharpTreeNode>? removedNodes = null;
int visibleIndexOfRemoval = 0; int visibleIndexOfRemoval = 0;
if (node.isVisible) if (node.isVisible)
{ {
@ -221,11 +221,11 @@ namespace ICSharpCode.ILSpyX.TreeView
} }
if (e.NewItems != null) if (e.NewItems != null)
{ {
SharpTreeNode insertionPos; SharpTreeNode? insertionPos;
if (e.NewStartingIndex == 0) if (e.NewStartingIndex == 0)
insertionPos = null; insertionPos = null;
else else
insertionPos = modelChildren[e.NewStartingIndex - 1]; insertionPos = modelChildren?[e.NewStartingIndex - 1];
foreach (SharpTreeNode node in e.NewItems) foreach (SharpTreeNode node in e.NewItems)
{ {
@ -260,7 +260,7 @@ namespace ICSharpCode.ILSpyX.TreeView
#region Expanding / LazyLoading #region Expanding / LazyLoading
public virtual object ExpandedIcon { public virtual object? ExpandedIcon {
get { return Icon; } get { return Icon; }
} }
@ -371,13 +371,13 @@ namespace ICSharpCode.ILSpyX.TreeView
public IEnumerable<SharpTreeNode> Ancestors() public IEnumerable<SharpTreeNode> Ancestors()
{ {
for (SharpTreeNode n = this.Parent; n != null; n = n.Parent) for (SharpTreeNode? n = this.Parent; n != null; n = n.Parent)
yield return n; yield return n;
} }
public IEnumerable<SharpTreeNode> AncestorsAndSelf() public IEnumerable<SharpTreeNode> AncestorsAndSelf()
{ {
for (SharpTreeNode n = this; n != null; n = n.Parent) for (SharpTreeNode? n = this; n != null; n = n.Parent)
yield return n; yield return n;
} }
@ -402,7 +402,7 @@ namespace ICSharpCode.ILSpyX.TreeView
} }
} }
public virtual string LoadEditText() public virtual string? LoadEditText()
{ {
return null; return null;
} }
@ -699,7 +699,7 @@ namespace ICSharpCode.ILSpyX.TreeView
#region INotifyPropertyChanged Members #region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public void RaisePropertyChanged(string name) public void RaisePropertyChanged(string name)
{ {
@ -725,10 +725,10 @@ namespace ICSharpCode.ILSpyX.TreeView
{ {
} }
public override string ToString() public override string? ToString()
{ {
// used for keyboard navigation // used for keyboard navigation
object text = this.Text; object? text = this.Text;
return text != null ? text.ToString() : string.Empty; return text != null ? text.ToString() : string.Empty;
} }
} }

20
ILSpy/Analyzers/AnalyzerEntityTreeNode.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Windows; using System.Windows;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -25,6 +26,8 @@ using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView;
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers namespace ICSharpCode.ILSpy.Analyzers
{ {
/// <summary> /// <summary>
@ -32,7 +35,9 @@ namespace ICSharpCode.ILSpy.Analyzers
/// </summary> /// </summary>
public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode
{ {
public abstract IEntity Member { get; } public abstract IEntity? Member { get; }
public IEntity? SourceMember { get; protected set; }
public override void ActivateItem(IPlatformRoutedEventArgs e) public override void ActivateItem(IPlatformRoutedEventArgs e)
{ {
@ -43,10 +48,14 @@ namespace ICSharpCode.ILSpy.Analyzers
return; return;
} }
MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(this.Member.ParentModule?.MetadataFile, this.Member.MetadataToken))); var module = this.Member.ParentModule?.MetadataFile;
Debug.Assert(module != null);
MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, this.Member.MetadataToken), this.SourceMember));
} }
public override object ToolTip => Member?.ParentModule?.MetadataFile?.FileName; public override object? ToolTip => Member?.ParentModule?.MetadataFile?.FileName;
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies) public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{ {
@ -56,13 +65,12 @@ namespace ICSharpCode.ILSpy.Analyzers
} }
foreach (LoadedAssembly asm in removedAssemblies) foreach (LoadedAssembly asm in removedAssemblies)
{ {
if (this.Member.ParentModule.MetadataFile == asm.GetMetadataFileOrNull()) if (this.Member.ParentModule!.MetadataFile == asm.GetMetadataFileOrNull())
return false; // remove this node return false; // remove this node
} }
this.Children.RemoveAll( this.Children.RemoveAll(
delegate (SharpTreeNode n) { delegate (SharpTreeNode n) {
AnalyzerTreeNode an = n as AnalyzerTreeNode; return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
}); });
return true; return true;
} }

22
ILSpy/Analyzers/AnalyzerSearchTreeNode.cs

@ -76,29 +76,29 @@ namespace ICSharpCode.ILSpy.Analyzers
} }
} }
AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol symbol) AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol resultSymbol)
{ {
if (symbol == null) if (resultSymbol == null)
{ {
throw new ArgumentNullException(nameof(symbol)); throw new ArgumentNullException(nameof(resultSymbol));
} }
switch (symbol) switch (resultSymbol)
{ {
case IModule module: case IModule module:
return new AnalyzedModuleTreeNode(module) { }; return new AnalyzedModuleTreeNode(module, (IEntity)this.symbol);
case ITypeDefinition td: case ITypeDefinition td:
return new AnalyzedTypeTreeNode(td) { }; return new AnalyzedTypeTreeNode(td, (IEntity)this.symbol);
case IField fd: case IField fd:
return new AnalyzedFieldTreeNode(fd) { }; return new AnalyzedFieldTreeNode(fd, (IEntity)this.symbol);
case IMethod md: case IMethod md:
return new AnalyzedMethodTreeNode(md) { }; return new AnalyzedMethodTreeNode(md, (IEntity)this.symbol);
case IProperty pd: case IProperty pd:
return new AnalyzedPropertyTreeNode(pd) { }; return new AnalyzedPropertyTreeNode(pd, (IEntity)this.symbol);
case IEvent ed: case IEvent ed:
return new AnalyzedEventTreeNode(ed) { }; return new AnalyzedEventTreeNode(ed, (IEntity)this.symbol);
default: default:
throw new ArgumentOutOfRangeException(nameof(symbol), $"Symbol {symbol.GetType().FullName} is not supported."); throw new ArgumentOutOfRangeException(nameof(resultSymbol), $"Symbol {resultSymbol.GetType().FullName} is not supported.");
} }
} }

10
ILSpy/Analyzers/AnalyzerTreeViewModel.cs

@ -109,20 +109,20 @@ namespace ICSharpCode.ILSpy.Analyzers
switch (entity) switch (entity)
{ {
case ITypeDefinition td: case ITypeDefinition td:
AddOrSelect(new AnalyzedTypeTreeNode(td)); AddOrSelect(new AnalyzedTypeTreeNode(td, null));
break; break;
case IField fd: case IField fd:
if (!fd.IsConst) if (!fd.IsConst)
AddOrSelect(new AnalyzedFieldTreeNode(fd)); AddOrSelect(new AnalyzedFieldTreeNode(fd, null));
break; break;
case IMethod md: case IMethod md:
AddOrSelect(new AnalyzedMethodTreeNode(md)); AddOrSelect(new AnalyzedMethodTreeNode(md, null));
break; break;
case IProperty pd: case IProperty pd:
AddOrSelect(new AnalyzedPropertyTreeNode(pd)); AddOrSelect(new AnalyzedPropertyTreeNode(pd, null));
break; break;
case IEvent ed: case IEvent ed:
AddOrSelect(new AnalyzedEventTreeNode(ed)); AddOrSelect(new AnalyzedEventTreeNode(ed, null));
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported."); throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported.");

4
ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs

@ -24,8 +24,8 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
readonly string name; readonly string name;
public AnalyzedAccessorTreeNode(IMethod analyzedMethod, string name) public AnalyzedAccessorTreeNode(IMethod analyzedMethod, IEntity source, string name)
: base(analyzedMethod) : base(analyzedMethod, source)
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
{ {

18
ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs

@ -17,10 +17,14 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode
@ -28,11 +32,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
readonly IEvent analyzedEvent; readonly IEvent analyzedEvent;
readonly string prefix; readonly string prefix;
public AnalyzedEventTreeNode(IEvent analyzedEvent, string prefix = "") public AnalyzedEventTreeNode(IEvent analyzedEvent, IEntity? source, string prefix = "")
{ {
this.analyzedEvent = analyzedEvent ?? throw new ArgumentNullException(nameof(analyzedEvent)); this.analyzedEvent = analyzedEvent ?? throw new ArgumentNullException(nameof(analyzedEvent));
this.prefix = prefix; this.prefix = prefix;
this.LazyLoading = true; this.LazyLoading = true;
this.SourceMember = source;
} }
public override IEntity Member => analyzedEvent; public override IEntity Member => analyzedEvent;
@ -45,15 +50,16 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
protected override void LoadChildren() protected override void LoadChildren()
{ {
if (analyzedEvent.CanAdd) if (analyzedEvent.CanAdd)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, "add")); this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, this.SourceMember, "add"));
if (analyzedEvent.CanRemove) if (analyzedEvent.CanRemove)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, "remove")); this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, this.SourceMember, "remove"));
if (TryFindBackingField(analyzedEvent, out var backingField)) if (TryFindBackingField(analyzedEvent, out var backingField))
this.Children.Add(new AnalyzedFieldTreeNode(backingField)); this.Children.Add(new AnalyzedFieldTreeNode(backingField, this.SourceMember));
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedEvent)) if (analyzer.Show(analyzedEvent))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header));
@ -61,10 +67,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
} }
} }
bool TryFindBackingField(IEvent analyzedEvent, out IField backingField) bool TryFindBackingField(IEvent analyzedEvent, [NotNullWhen(true)] out IField? backingField)
{ {
backingField = null; backingField = null;
foreach (var field in analyzedEvent.DeclaringTypeDefinition.GetFields(options: GetMemberOptions.IgnoreInheritedMembers)) foreach (var field in analyzedEvent.DeclaringTypeDefinition?.GetFields(options: GetMemberOptions.IgnoreInheritedMembers) ?? [])
{ {
if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private) if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private)
{ {

7
ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs

@ -17,19 +17,23 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode
{ {
readonly IField analyzedField; readonly IField analyzedField;
public AnalyzedFieldTreeNode(IField analyzedField) public AnalyzedFieldTreeNode(IField analyzedField, IEntity? source)
{ {
this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField)); this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField));
this.SourceMember = source;
this.LazyLoading = true; this.LazyLoading = true;
} }
@ -42,6 +46,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedField)) if (analyzer.Show(analyzedField))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header));

9
ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs

@ -17,10 +17,13 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode
@ -28,9 +31,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
readonly IMethod analyzedMethod; readonly IMethod analyzedMethod;
readonly string prefix; readonly string prefix;
public AnalyzedMethodTreeNode(IMethod analyzedMethod, string prefix = "") public AnalyzedMethodTreeNode(IMethod analyzedMethod, IEntity? source, string prefix = "")
{ {
this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod)); this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod));
this.SourceMember = source;
this.prefix = prefix; this.prefix = prefix;
this.LazyLoading = true; this.LazyLoading = true;
} }
@ -44,9 +48,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedMethod)) if (analyzer.Show(analyzedMethod))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata!.Header));
} }
} }
} }

16
ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Windows; using System.Windows;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -25,15 +26,18 @@ using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView;
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
internal class AnalyzedModuleTreeNode : AnalyzerEntityTreeNode internal class AnalyzedModuleTreeNode : AnalyzerEntityTreeNode
{ {
readonly IModule analyzedModule; readonly IModule analyzedModule;
public AnalyzedModuleTreeNode(IModule analyzedModule) public AnalyzedModuleTreeNode(IModule analyzedModule, IEntity? source)
{ {
this.analyzedModule = analyzedModule ?? throw new ArgumentNullException(nameof(analyzedModule)); this.analyzedModule = analyzedModule ?? throw new ArgumentNullException(nameof(analyzedModule));
this.SourceMember = source;
this.LazyLoading = true; this.LazyLoading = true;
} }
@ -41,16 +45,17 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
public override object Text => analyzedModule.AssemblyName; public override object Text => analyzedModule.AssemblyName;
public override object ToolTip => analyzedModule.MetadataFile?.FileName; public override object? ToolTip => analyzedModule.MetadataFile?.FileName;
protected override void LoadChildren() protected override void LoadChildren()
{ {
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedModule)) if (analyzer.Show(analyzedModule))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata!.Header));
} }
} }
} }
@ -66,7 +71,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile)); MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile));
} }
public override IEntity Member => null; public override IEntity? Member => null;
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies) public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{ {
@ -81,8 +86,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
} }
this.Children.RemoveAll( this.Children.RemoveAll(
delegate (SharpTreeNode n) { delegate (SharpTreeNode n) {
AnalyzerTreeNode an = n as AnalyzerTreeNode; return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
}); });
return true; return true;
} }

13
ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs

@ -17,10 +17,13 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
sealed class AnalyzedPropertyTreeNode : AnalyzerEntityTreeNode sealed class AnalyzedPropertyTreeNode : AnalyzerEntityTreeNode
@ -28,11 +31,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
readonly IProperty analyzedProperty; readonly IProperty analyzedProperty;
readonly string prefix; readonly string prefix;
public AnalyzedPropertyTreeNode(IProperty analyzedProperty, string prefix = "") public AnalyzedPropertyTreeNode(IProperty analyzedProperty, IEntity? source, string prefix = "")
{ {
this.analyzedProperty = analyzedProperty ?? throw new ArgumentNullException(nameof(analyzedProperty)); this.analyzedProperty = analyzedProperty ?? throw new ArgumentNullException(nameof(analyzedProperty));
this.prefix = prefix; this.prefix = prefix;
this.LazyLoading = true; this.LazyLoading = true;
this.SourceMember = source;
} }
public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty); public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty);
@ -43,16 +47,17 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
protected override void LoadChildren() protected override void LoadChildren()
{ {
if (analyzedProperty.CanGet) if (analyzedProperty.CanGet)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, "get")); this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, this.SourceMember, "get"));
if (analyzedProperty.CanSet) if (analyzedProperty.CanSet)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, "set")); this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, this.SourceMember, "set"));
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedProperty)) if (analyzer.Show(analyzedProperty))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata!.Header));
} }
} }
} }

9
ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs

@ -17,19 +17,23 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{ {
internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode
{ {
readonly ITypeDefinition analyzedType; readonly ITypeDefinition analyzedType;
public AnalyzedTypeTreeNode(ITypeDefinition analyzedType) public AnalyzedTypeTreeNode(ITypeDefinition analyzedType, IEntity? source)
{ {
this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType)); this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType));
this.SourceMember = source;
this.LazyLoading = true; this.LazyLoading = true;
} }
@ -42,9 +46,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
foreach (var lazy in Analyzers) foreach (var lazy in Analyzers)
{ {
var analyzer = lazy.Value; var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedType)) if (analyzer.Show(analyzedType))
{ {
this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata.Header)); this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata!.Header));
} }
} }
} }

16
ILSpy/AssemblyTree/AssemblyTreeModel.cs

@ -67,6 +67,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
private readonly NavigationHistory<NavigationState> history = new(); private readonly NavigationHistory<NavigationState> history = new();
private NavigationState? navigatingToState; private NavigationState? navigatingToState;
private object? sourceOfReference;
private readonly SettingsService settingsService; private readonly SettingsService settingsService;
private readonly LanguageService languageService; private readonly LanguageService languageService;
private readonly IExportProvider exportProvider; private readonly IExportProvider exportProvider;
@ -262,7 +263,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
found = true; found = true;
if (SelectedItem == initialSelection) if (SelectedItem == initialSelection)
{ {
await JumpToReferenceAsync(mr); await JumpToReferenceAsync(mr, null);
} }
} }
} }
@ -601,7 +602,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
List<string> path = new List<string>(); List<string> path = new List<string>();
while (node.Parent != null) while (node.Parent != null)
{ {
path.Add(node.ToString()); path.Add(node.ToString()!);
node = node.Parent; node = node.Parent;
} }
path.Reverse(); path.Reverse();
@ -642,7 +643,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
private void JumpToReference(object? sender, NavigateToReferenceEventArgs e) private void JumpToReference(object? sender, NavigateToReferenceEventArgs e)
{ {
JumpToReferenceAsync(e.Reference, e.InNewTabPage).HandleExceptions(); JumpToReferenceAsync(e.Reference, e.Source, e.InNewTabPage).HandleExceptions();
IsActive = true; IsActive = true;
} }
@ -653,8 +654,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// Returns a task that will signal completion when the decompilation of the jump target has finished. /// 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. /// The task will be marked as canceled if the decompilation is canceled.
/// </returns> /// </returns>
private Task JumpToReferenceAsync(object? reference, bool inNewTabPage = false) private Task JumpToReferenceAsync(object? reference, object? source, bool inNewTabPage = false)
{ {
this.sourceOfReference = source;
var decompilationTask = Task.CompletedTask; var decompilationTask = Task.CompletedTask;
switch (reference) switch (reference)
@ -804,6 +806,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree
public void DecompileSelectedNodes(ViewState? newState = null) public void DecompileSelectedNodes(ViewState? newState = null)
{ {
object? source = this.sourceOfReference;
this.sourceOfReference = null;
var activeTabPage = DockWorkspace.ActiveTabPage; var activeTabPage = DockWorkspace.ActiveTabPage;
if (activeTabPage.FrozenContent) if (activeTabPage.FrozenContent)
@ -831,7 +835,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree
var options = activeTabPage.CreateDecompilationOptions(); var options = activeTabPage.CreateDecompilationOptions();
options.TextViewState = newState as DecompilerTextViewState; options.TextViewState = newState as DecompilerTextViewState;
activeTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options)); activeTabPage.ShowTextViewAsync(textView => {
return textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, source, options);
});
} }
public void RefreshDecompiledView() public void RefreshDecompiledView()

7
ILSpy/TextView/AvalonEditTextOutput.cs

@ -312,6 +312,13 @@ namespace ICSharpCode.ILSpy.TextView
references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = true, IsDefinition = isDefinition }); references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = true, IsDefinition = isDefinition });
} }
internal object InitialHighlightReference = null;
public void SetInitialHighlight(object reference)
{
this.InitialHighlightReference = reference;
}
public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false) public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false)
{ {
WriteIndent(); WriteIndent();

49
ILSpy/TextView/DecompilerTextView.cs

@ -802,6 +802,7 @@ namespace ICSharpCode.ILSpy.TextView
currentAddress = textOutput.Address; currentAddress = textOutput.Address;
currentTitle = textOutput.Title; currentTitle = textOutput.Title;
expandMemberDefinitions = settingsService.DisplaySettings.ExpandMemberDefinitions; expandMemberDefinitions = settingsService.DisplaySettings.ExpandMemberDefinitions;
SetLocalReferenceMarks(textOutput.InitialHighlightReference);
} }
#endregion #endregion
@ -817,7 +818,7 @@ namespace ICSharpCode.ILSpy.TextView
[Obsolete("Use DecompileAsync() instead")] [Obsolete("Use DecompileAsync() instead")]
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, DecompilationOptions options) public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, DecompilationOptions options)
{ {
DecompileAsync(language, treeNodes, options).HandleExceptions(); DecompileAsync(language, treeNodes, null, options).HandleExceptions();
} }
/// <summary> /// <summary>
@ -826,7 +827,7 @@ namespace ICSharpCode.ILSpy.TextView
/// If any errors occur, the error message is displayed in the text view, and the task returned by this method completes successfully. /// If any errors occur, the error message is displayed in the text view, and the task returned by this method completes successfully.
/// If the operation is cancelled (by starting another decompilation action); the returned task is marked as cancelled. /// If the operation is cancelled (by starting another decompilation action); the returned task is marked as cancelled.
/// </summary> /// </summary>
public Task DecompileAsync(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, DecompilationOptions options) public Task DecompileAsync(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, object? source, DecompilationOptions options)
{ {
// Some actions like loading an assembly list cause several selection changes in the tree view, // Some actions like loading an assembly list cause several selection changes in the tree view,
// and each of those will start a decompilation action. // and each of those will start a decompilation action.
@ -834,7 +835,7 @@ namespace ICSharpCode.ILSpy.TextView
bool isDecompilationScheduled = this.nextDecompilationRun != null; bool isDecompilationScheduled = this.nextDecompilationRun != null;
if (this.nextDecompilationRun != null) if (this.nextDecompilationRun != null)
this.nextDecompilationRun.TaskCompletionSource.TrySetCanceled(); this.nextDecompilationRun.TaskCompletionSource.TrySetCanceled();
this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options); this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options, source);
var task = this.nextDecompilationRun.TaskCompletionSource.Task; var task = this.nextDecompilationRun.TaskCompletionSource.Task;
if (!isDecompilationScheduled) if (!isDecompilationScheduled)
{ {
@ -857,12 +858,14 @@ namespace ICSharpCode.ILSpy.TextView
public readonly ILSpyTreeNode[] TreeNodes; public readonly ILSpyTreeNode[] TreeNodes;
public readonly DecompilationOptions Options; public readonly DecompilationOptions Options;
public readonly TaskCompletionSource<object?> TaskCompletionSource = new TaskCompletionSource<object?>(); public readonly TaskCompletionSource<object?> TaskCompletionSource = new TaskCompletionSource<object?>();
public readonly object? Source;
public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options) public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options, object? source = null)
{ {
this.Language = language; this.Language = language;
this.TreeNodes = treeNodes; this.TreeNodes = treeNodes;
this.Options = options; this.Options = options;
this.Source = source;
} }
} }
@ -914,6 +917,7 @@ namespace ICSharpCode.ILSpy.TextView
{ {
AvalonEditTextOutput textOutput = new AvalonEditTextOutput(); AvalonEditTextOutput textOutput = new AvalonEditTextOutput();
textOutput.LengthLimit = outputLengthLimit; textOutput.LengthLimit = outputLengthLimit;
textOutput.SetInitialHighlight(context.Source);
DecompileNodes(context, textOutput); DecompileNodes(context, textOutput);
textOutput.PrepareDocument(); textOutput.PrepareDocument();
tcs.SetResult(textOutput); tcs.SetResult(textOutput);
@ -994,19 +998,7 @@ namespace ICSharpCode.ILSpy.TextView
if (referenceSegment.IsLocal) if (referenceSegment.IsLocal)
{ {
ClearLocalReferenceMarks(); ClearLocalReferenceMarks();
if (references != null) SetLocalReferenceMarks(reference);
{
foreach (var r in references)
{
if (reference.Equals(r.Reference))
{
var mark = textMarkerService.Create(r.StartOffset, r.Length);
mark.BackgroundColor = (Color)(r.IsDefinition ? FindResource(ResourceKeys.TextMarkerDefinitionBackgroundColor) : FindResource(ResourceKeys.TextMarkerBackgroundColor));
localReferenceMarks.Add(mark);
}
}
}
return; return;
} }
if (definitionLookup != null) if (definitionLookup != null)
@ -1027,6 +1019,23 @@ namespace ICSharpCode.ILSpy.TextView
MessageBus.Send(this, new NavigateToReferenceEventArgs(reference, openInNewTab)); MessageBus.Send(this, new NavigateToReferenceEventArgs(reference, openInNewTab));
} }
private void SetLocalReferenceMarks(object reference)
{
if (references == null || reference == null)
{
return;
}
foreach (var r in references)
{
if (reference.Equals(r.Reference))
{
var mark = textMarkerService.Create(r.StartOffset, r.Length);
mark.BackgroundColor = (Color)(r.IsDefinition ? FindResource(ResourceKeys.TextMarkerDefinitionBackgroundColor) : FindResource(ResourceKeys.TextMarkerBackgroundColor));
localReferenceMarks.Add(mark);
}
}
}
Point? mouseDownPos; Point? mouseDownPos;
void TextAreaMouseDown(object sender, MouseButtonEventArgs e) void TextAreaMouseDown(object sender, MouseButtonEventArgs e)
@ -1091,7 +1100,11 @@ namespace ICSharpCode.ILSpy.TextView
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = language.FileExtension; dlg.DefaultExt = language.FileExtension;
dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles; dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles;
dlg.FileName = WholeProjectDecompiler.CleanUpFileName(treeNodes.First().Text.ToString(), language.FileExtension); string? nodeText = treeNodes.First().Text?.ToString();
if (!string.IsNullOrWhiteSpace(nodeText))
{
dlg.FileName = WholeProjectDecompiler.CleanUpFileName(nodeText, language.FileExtension);
}
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName); SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName);

4
ILSpy/Util/MessageBus.cs

@ -79,10 +79,10 @@ namespace ICSharpCode.ILSpy.Util
public class SettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs<PropertyChangedEventArgs>(e); public class SettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs<PropertyChangedEventArgs>(e);
public class NavigateToReferenceEventArgs(object reference, bool inNewTabPage = false) : EventArgs public class NavigateToReferenceEventArgs(object reference, object? source = null, bool inNewTabPage = false) : EventArgs
{ {
public object Reference { get; } = reference; public object Reference { get; } = reference;
public object? Source { get; } = source;
public bool InNewTabPage { get; } = inNewTabPage; public bool InNewTabPage { get; } = inNewTabPage;
} }

2
ILSpy/Views/DebugSteps.xaml.cs

@ -140,7 +140,7 @@ namespace ICSharpCode.ILSpy
} }
var state = dockWorkspace.ActiveTabPage.GetState(); var state = dockWorkspace.ActiveTabPage.GetState();
dockWorkspace.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(assemblyTreeModel.CurrentLanguage, assemblyTreeModel.SelectedNodes, dockWorkspace.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(assemblyTreeModel.CurrentLanguage, assemblyTreeModel.SelectedNodes, null,
new DecompilationOptions(assemblyTreeModel.CurrentLanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) { new DecompilationOptions(assemblyTreeModel.CurrentLanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) {
StepLimit = step, StepLimit = step,
IsDebug = isDebug, IsDebug = isDebug,

Loading…
Cancel
Save