Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Highlight referenced element after jump from analyzer window #283

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions ILSpy/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -555,11 +555,21 @@ public ILSpyTreeNode FindTreeNode(object reference)
}
}

public void JumpToReference(object reference)
public void JumpToReference(object reference, EventHandler<DecompileFinishedEventArgs> jumpFinished = null)
{
ILSpyTreeNode treeNode = FindTreeNode(reference);
if (treeNode != null) {
if (null != jumpFinished)
{
nextDecompileAction = jumpFinished;
}
SelectNode(treeNode);
if (null != jumpFinished && null != nextDecompileAction)
{
// selection did not change so execute action immediately
nextDecompileAction = null;
jumpFinished(this, new DecompileFinishedEventArgs(this.CurrentLanguage, this.SelectedNodes));
}
} else if (reference is Mono.Cecil.Cil.OpCode) {
string link = "http://msdn.microsoft.com/library/system.reflection.emit.opcodes." + ((Mono.Cecil.Cil.OpCode)reference).Code.ToString().ToLowerInvariant() + ".aspx";
try {
Expand Down Expand Up @@ -631,9 +641,14 @@ void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
}

private bool ignoreDecompilationRequests;

private EventHandler<DecompileFinishedEventArgs> nextDecompileAction;

private void DecompileSelectedNodes(DecompilerTextViewState state = null, bool recordHistory = true)
{
EventHandler<DecompileFinishedEventArgs> decompilationAction = nextDecompileAction;
nextDecompileAction = null;

if (ignoreDecompilationRequests)
return;

Expand All @@ -649,7 +664,8 @@ private void DecompileSelectedNodes(DecompilerTextViewState state = null, bool r
if (node != null && node.View(decompilerTextView))
return;
}
decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions() { TextViewState = state });
decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes,
new DecompilationOptions() { TextViewState = state }, decompilationAction);
}

void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
Expand Down
68 changes: 54 additions & 14 deletions ILSpy/TextView/DecompilerTextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,13 @@ void ShowOutput(AvalonEditTextOutput textOutput, IHighlightingDefinition highlig
/// Starts the decompilation of the given nodes.
/// The result is displayed in the text view.
/// </summary>
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, DecompilationOptions options)
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes, DecompilationOptions options,
EventHandler<DecompileFinishedEventArgs> decompileFinished)
{
// Some actions like loading an assembly list cause several selection changes in the tree view,
// and each of those will start a decompilation action.
bool isDecompilationScheduled = this.nextDecompilationRun != null;
this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options);
this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options, decompileFinished);
if (!isDecompilationScheduled) {
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(
delegate {
Expand All @@ -418,12 +419,15 @@ sealed class DecompilationContext
public readonly ILSpy.Language Language;
public readonly ILSpyTreeNode[] TreeNodes;
public readonly DecompilationOptions Options;
public readonly EventHandler<DecompileFinishedEventArgs> DecompileFinished;

public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options)
public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options,
EventHandler<DecompileFinishedEventArgs> decompileFinished = null)
{
this.Language = language;
this.TreeNodes = treeNodes;
this.Options = options;
this.DecompileFinished = decompileFinished;
}
}

Expand Down Expand Up @@ -463,12 +467,28 @@ void DoDecompile(DecompilationContext context, int outputLengthLimit)
ShowOutput(output);
isDecompilationOk = false;
} finally {
this.UpdateDebugUI(isDecompilationOk);
this.OnDecompileFinished(isDecompilationOk, context);
}
decompiledNodes = context.TreeNodes;
});
}

public event EventHandler<DecompileFinishedEventArgs> DecompileFinished;

void OnDecompileFinished(bool isDecompilationOk, DecompilationContext context)
{
if (isDecompilationOk) {
var args = new DecompileFinishedEventArgs(context.Language, context.TreeNodes);
var handler = DecompileFinished;
if (null != handler)
handler(this, args);
if (null != context.DecompileFinished)
context.DecompileFinished(this, args);
}
// TODO: this should be handled through DecompileFinished handler on IconBarMargin
UpdateDebugUI(isDecompilationOk);
}

void UpdateDebugUI(bool isDecompilationOk)
{
// sync bookmarks
Expand Down Expand Up @@ -606,16 +626,7 @@ internal void JumpToReference(ReferenceSegment referenceSegment)
{
object reference = referenceSegment.Reference;
if (referenceSegment.IsLocal) {
ClearLocalReferenceMarks();
if (references != null) {
foreach (var r in references) {
if (reference.Equals(r.Reference)) {
var mark = textMarkerService.Create(r.StartOffset, r.Length);
mark.BackgroundColor = r.IsLocalTarget ? Colors.LightSeaGreen : Colors.GreenYellow;
localReferenceMarks.Add(mark);
}
}
}
MarkReferences(reference);
return;
}
if (definitionLookup != null) {
Expand All @@ -634,6 +645,23 @@ internal void JumpToReference(ReferenceSegment referenceSegment)
MainWindow.Instance.JumpToReference(reference);
}

public void MarkReferences(object reference)
{
ClearLocalReferenceMarks();
if (references != null)
{
foreach (var r in references)
{
if (reference.Equals(r.Reference))
{
var mark = textMarkerService.Create(r.StartOffset, r.Length);
mark.BackgroundColor = r.IsLocalTarget ? Colors.LightSeaGreen : Colors.GreenYellow;
localReferenceMarks.Add(mark);
}
}
}
}

void ClearLocalReferenceMarks()
{
foreach (var mark in localReferenceMarks) {
Expand Down Expand Up @@ -834,4 +862,16 @@ internal void RestoreFoldings(List<NewFolding> list)
folding.DefaultClosed = !ExpandedFoldings.Any(f => f.Item1 == folding.StartOffset && f.Item2 == folding.EndOffset);
}
}

public class DecompileFinishedEventArgs : EventArgs
{
public ILSpy.Language Language { get; private set; }
public IEnumerable<ILSpyTreeNode> Decompilednodes { get; private set; }

public DecompileFinishedEventArgs(ILSpy.Language language, IEnumerable<ILSpyTreeNode> treeNodes)
{
this.Language = language;
this.Decompilednodes = treeNodes;
}
}
}
6 changes: 6 additions & 0 deletions ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@ public override object Text
{
get { return name ?? base.Text; }
}

internal override object GetMemberReference()
{
// the referenced element is the event and not add/remove methods
return ((AnalyzerTreeNode)Parent).GetMemberReference();
}
}
}
2 changes: 1 addition & 1 deletion ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedEventFiredByTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedEventFiredByTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly EventDefinition analyzedEvent;
private readonly FieldDefinition eventBackingField;
Expand Down
2 changes: 1 addition & 1 deletion ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedFieldAccessTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedFieldAccessTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly bool showWrites; // true: show writes; false: show read access
private readonly FieldDefinition analyzedField;
Expand Down
2 changes: 1 addition & 1 deletion ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedMethodUsedByTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedMethodUsedByTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly MethodDefinition analyzedMethod;
private ConcurrentDictionary<MethodDefinition, int> foundMethods;
Expand Down
8 changes: 7 additions & 1 deletion ILSpy/TreeNodes/Analyzer/AnalyzedPropertyAccessorTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@ public override object Text
{
get { return name ?? base.Text; }
}
}

internal override object GetMemberReference()
{
// the referenced element is the property and not get/set methods
return ((AnalyzerTreeNode)Parent).GetMemberReference();
}
}
}
2 changes: 1 addition & 1 deletion ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedTypeExposedByTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedTypeExposedByTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly TypeDefinition analyzedType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal class AnalyzedTypeExtensionMethodsTreeNode : AnalyzerSearchTreeNode
internal class AnalyzedTypeExtensionMethodsTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly TypeDefinition analyzedType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedTypeInstantiationsTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedTypeInstantiationsTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly TypeDefinition analyzedType;
private readonly bool isSystemObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal sealed class AnalyzedVirtualMethodUsedByTreeNode : AnalyzerSearchTreeNode
internal sealed class AnalyzedVirtualMethodUsedByTreeNode : AnalyzerReferencingSearchTreeNode
{
private readonly MethodDefinition analyzedMethod;
private ConcurrentDictionary<MethodDefinition, int> foundMethods;
Expand Down
27 changes: 24 additions & 3 deletions ILSpy/TreeNodes/Analyzer/AnalyzerEntityTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

using System;
using System.Collections.Generic;
using System.Linq;

using ICSharpCode.ILSpy.TextView;
using ICSharpCode.TreeView;
using Mono.Cecil;

Expand All @@ -26,14 +29,27 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
/// <summary>
/// Base class for entity nodes.
/// </summary>
public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode
{
public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode
{
public abstract MemberReference Member { get; }

public override void ActivateItem(System.Windows.RoutedEventArgs e)
{
e.Handled = true;
MainWindow.Instance.JumpToReference(this.Member);
// Find which member is referenced
EventHandler<DecompileFinishedEventArgs> action = null;
if (null != Parent && Parent is AnalyzerTreeNode)
{
var referencedMember = ((AnalyzerTreeNode)Parent).GetMemberReference() as MemberReference;
if (null != referencedMember)
{
action = (s, a) =>
{
MainWindow.Instance.TextView.MarkReferences(referencedMember);
};
}
}
MainWindow.Instance.JumpToReference(this.Member, action);
}

public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
Expand All @@ -49,5 +65,10 @@ public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> remov
});
return true;
}

internal override object GetMemberReference()
{
return Member;
}
}
}
11 changes: 11 additions & 0 deletions ILSpy/TreeNodes/Analyzer/AnalyzerSearchTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,15 @@ public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> remov
return true;
}
}

/// <summary>
/// base class for analyzer nodes that search for referencing elements like AnalyzedMethodUsedByTreeNode or AnalyzedTypeInstantiationsTreeNode.
/// </summary>
public abstract class AnalyzerReferencingSearchTreeNode : AnalyzerSearchTreeNode
{
internal override object GetMemberReference()
{
return ((AnalyzerTreeNode)Parent).GetMemberReference();
}
}
}
5 changes: 5 additions & 0 deletions ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
}
base.OnChildrenChanged(e);
}

internal virtual object GetMemberReference()
{
return null;
}

/// <summary>
/// Handles changes to the assembly list.
Expand Down