diff --git a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs
index eceee8719..fd3d75ff0 100644
--- a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs
+++ b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs
@@ -179,12 +179,12 @@ namespace ICSharpCode.ILSpy.Debugger.Commands
}
}
- [ExportContextMenuEntry(Header = "_Debug Assembly", Icon = "Images/application-x-executable.png")]
+ [ExportContextMenuEntryAttribute(Header = "_Debug Assembly", Icon = "Images/application-x-executable.png")]
internal sealed class DebugExecutableNodeCommand : DebuggerCommand, IContextMenuEntry
{
- public bool IsVisible(SharpTreeNode[] selectedNodes)
+ public bool IsVisible(TextViewContext context)
{
- return selectedNodes.All(
+ return context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(
delegate (SharpTreeNode n) {
AssemblyTreeNode a = n as AssemblyTreeNode;
if (a == null)
@@ -194,15 +194,17 @@ namespace ICSharpCode.ILSpy.Debugger.Commands
});
}
- public bool IsEnabled(SharpTreeNode[] selectedNodes)
+ public bool IsEnabled(TextViewContext context)
{
- return selectedNodes.Length == 1;
+ return context.SelectedTreeNodes != null && context.SelectedTreeNodes.Length == 1;
}
- public void Execute(SharpTreeNode[] selectedNodes)
+ public void Execute(TextViewContext context)
{
+ if (context.SelectedTreeNodes == null)
+ return;
if (!CurrentDebugger.IsDebugging) {
- AssemblyTreeNode n = selectedNodes[0] as AssemblyTreeNode;
+ AssemblyTreeNode n = context.SelectedTreeNodes[0] as AssemblyTreeNode;
if (DebuggerSettings.Instance.AskForArguments) {
var window = new ExecuteProcessWindow { Owner = MainWindow.Instance,
diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs
index e4ae3210e..26e104a96 100644
--- a/ILSpy/ContextMenuEntry.cs
+++ b/ILSpy/ContextMenuEntry.cs
@@ -19,17 +19,49 @@
using System;
using System.ComponentModel.Composition;
using System.Linq;
+using System.Windows;
using System.Windows.Controls;
-
+using ICSharpCode.ILSpy.TextView;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy
{
public interface IContextMenuEntry
{
- bool IsVisible(SharpTreeNode[] selectedNodes);
- bool IsEnabled(SharpTreeNode[] selectedNodes);
- void Execute(SharpTreeNode[] selectedNodes);
+ bool IsVisible(TextViewContext context);
+ bool IsEnabled(TextViewContext context);
+ void Execute(TextViewContext context);
+ }
+
+ public class TextViewContext
+ {
+ ///
+ /// Returns the selected nodes in the tree view.
+ /// Returns null, if context menu does not belong to a tree view.
+ ///
+ public SharpTreeNode[] SelectedTreeNodes { get; private set; }
+
+ ///
+ /// Returns the text view the context menu is assigned to.
+ /// Returns null, if context menu is not assigned to a text view.
+ ///
+ public DecompilerTextView TextView { get; private set; }
+
+ ///
+ /// Returns the reference the mouse cursor is currently hovering above.
+ /// Returns null, if there was no reference found.
+ ///
+ public ReferenceSegment Reference { get; private set; }
+
+ public static TextViewContext Create(SharpTreeNode[] selectedTreeNodes = null, DecompilerTextView textView = null)
+ {
+ var reference = textView != null ? textView.GetReferenceSegmentAtMousePosition() : null;
+ return new TextViewContext {
+ SelectedTreeNodes = selectedTreeNodes,
+ TextView = textView,
+ Reference = reference
+ };
+ }
}
public interface IContextMenuEntryMetadata
@@ -61,23 +93,31 @@ namespace ICSharpCode.ILSpy
///
/// Enables extensible context menu support for the specified tree view.
///
- public static void Add(SharpTreeView treeView)
+ public static void Add(SharpTreeView treeView, DecompilerTextView textView = null)
{
- var provider = new ContextMenuProvider(treeView);
+ var provider = new ContextMenuProvider(treeView, textView);
treeView.ContextMenuOpening += provider.treeView_ContextMenuOpening;
// Context menu is shown only when the ContextMenu property is not null before the
// ContextMenuOpening event handler is called.
treeView.ContextMenu = new ContextMenu();
+ if (textView != null) {
+ textView.ContextMenuOpening += provider.textView_ContextMenuOpening;
+ // Context menu is shown only when the ContextMenu property is not null before the
+ // ContextMenuOpening event handler is called.
+ textView.ContextMenu = new ContextMenu();
+ }
}
readonly SharpTreeView treeView;
+ readonly DecompilerTextView textView;
[ImportMany(typeof(IContextMenuEntry))]
Lazy[] entries = null;
- private ContextMenuProvider(SharpTreeView treeView)
+ ContextMenuProvider(SharpTreeView treeView, DecompilerTextView textView = null)
{
this.treeView = treeView;
+ this.textView = textView;
App.CompositionContainer.ComposeParts(this);
}
@@ -88,12 +128,34 @@ namespace ICSharpCode.ILSpy
e.Handled = true; // don't show the menu
return;
}
- ContextMenu menu = new ContextMenu();
+ TextViewContext context = TextViewContext.Create(selectedNodes);
+ ContextMenu menu;
+ if (ShowContextMenu(context, out menu))
+ treeView.ContextMenu = menu;
+ else
+ // hide the context menu.
+ e.Handled = true;
+ }
+
+ void textView_ContextMenuOpening(object sender, ContextMenuEventArgs e)
+ {
+ TextViewContext context = TextViewContext.Create(textView: textView);
+ ContextMenu menu;
+ if (ShowContextMenu(context, out menu))
+ textView.ContextMenu = menu;
+ else
+ // hide the context menu.
+ e.Handled = true;
+ }
+
+ bool ShowContextMenu(TextViewContext context, out ContextMenu menu)
+ {
+ menu = new ContextMenu();
foreach (var category in entries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Category)) {
bool needSeparatorForCategory = true;
foreach (var entryPair in category) {
IContextMenuEntry entry = entryPair.Value;
- if (entry.IsVisible(selectedNodes)) {
+ if (entry.IsVisible(context)) {
if (needSeparatorForCategory && menu.Items.Count > 0) {
menu.Items.Add(new Separator());
needSeparatorForCategory = false;
@@ -107,22 +169,15 @@ namespace ICSharpCode.ILSpy
Source = Images.LoadImage(entry, entryPair.Metadata.Icon)
};
}
- if (entryPair.Value.IsEnabled(selectedNodes)) {
- menuItem.Click += delegate
- {
- entry.Execute(selectedNodes);
- };
+ if (entryPair.Value.IsEnabled(context)) {
+ menuItem.Click += delegate { entry.Execute(context); };
} else
menuItem.IsEnabled = false;
menu.Items.Add(menuItem);
}
}
}
- if (menu.Items.Count > 0)
- treeView.ContextMenu = menu;
- else
- // hide the context menu.
- e.Handled = true;
+ return menu.Items.Count > 0;
}
}
}
diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs
index 0e161e1ed..e484e7c4c 100644
--- a/ILSpy/Languages/CSharpLanguage.cs
+++ b/ILSpy/Languages/CSharpLanguage.cs
@@ -230,7 +230,6 @@ namespace ICSharpCode.ILSpy
return "x86";
else
return "AnyCPU (64-bit preferred)";
- break;
case TargetArchitecture.AMD64:
return "x64";
case TargetArchitecture.IA64:
@@ -250,7 +249,6 @@ namespace ICSharpCode.ILSpy
return "x86";
else
return "AnyCPU";
- break;
case TargetArchitecture.AMD64:
return "x64";
case TargetArchitecture.IA64:
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index c11516d48..ac1ae1675 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -90,7 +90,7 @@ namespace ICSharpCode.ILSpy
InitMainMenu();
InitToolbar();
- ContextMenuProvider.Add(treeView);
+ ContextMenuProvider.Add(treeView, decompilerTextView);
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs
index ce2debeeb..41d260027 100644
--- a/ILSpy/TextView/AvalonEditTextOutput.cs
+++ b/ILSpy/TextView/AvalonEditTextOutput.cs
@@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy.TextView
///
/// A text segment that references some object. Used for hyperlinks in the editor.
///
- sealed class ReferenceSegment : TextSegment
+ public sealed class ReferenceSegment : TextSegment
{
public object Reference;
public bool IsLocal;
diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs
index 4e5498343..05eefd431 100644
--- a/ILSpy/TextView/DecompilerTextView.cs
+++ b/ILSpy/TextView/DecompilerTextView.cs
@@ -182,7 +182,7 @@ namespace ICSharpCode.ILSpy.TextView
TextViewPosition? position = textEditor.TextArea.TextView.GetPosition(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);
if (position == null)
return;
- int offset = textEditor.Document.GetOffset(position.Value);
+ int offset = textEditor.Document.GetOffset(position.Value.Location);
ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();
if (seg == null)
return;
@@ -761,6 +761,15 @@ namespace ICSharpCode.ILSpy.TextView
}
#endregion
+ internal ReferenceSegment GetReferenceSegmentAtMousePosition()
+ {
+ TextViewPosition? position = textEditor.TextArea.TextView.GetPosition(Mouse.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);
+ if (position == null)
+ return null;
+ int offset = textEditor.Document.GetOffset(position.Value.Location);
+ return referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();
+ }
+
public DecompilerTextViewState GetState()
{
if (decompiledNodes == null)
diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs
index cb66683e1..fc5a62117 100644
--- a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs
+++ b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs
@@ -18,22 +18,27 @@
using System;
using System.Linq;
+using System.Windows;
using ICSharpCode.TreeView;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
- [ExportContextMenuEntry(Header = "Analyze", Icon = "images/Search.png")]
+ [ExportContextMenuEntryAttribute(Header = "Analyze", Icon = "images/Search.png")]
internal sealed class AnalyzeContextMenuEntry : IContextMenuEntry
{
- public bool IsVisible(SharpTreeNode[] selectedNodes)
+ public bool IsVisible(TextViewContext context)
{
- return selectedNodes.All(n => n is IMemberTreeNode);
+ if (context.SelectedTreeNodes == null)
+ return context.Reference != null && context.Reference.Reference is MemberReference;
+ return context.SelectedTreeNodes.All(n => n is IMemberTreeNode);
}
- public bool IsEnabled(SharpTreeNode[] selectedNodes)
+ public bool IsEnabled(TextViewContext context)
{
- foreach (IMemberTreeNode node in selectedNodes) {
+ if (context.SelectedTreeNodes == null)
+ return context.Reference != null && context.Reference.Reference is MemberReference;
+ foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
if (!(node.Member is TypeDefinition
|| node.Member is FieldDefinition
|| node.Member is MethodDefinition
@@ -45,20 +50,29 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
return true;
}
- public void Execute(SharpTreeNode[] selectedNodes)
+ public void Execute(TextViewContext context)
{
- // TODO: figure out when equivalent nodes are already present
- // and focus those instead.
- foreach (IMemberTreeNode node in selectedNodes) {
- Analyze(node.Member);
+ var segment = context.Reference;
+ if (context.SelectedTreeNodes != null) {
+ // TODO: figure out when equivalent nodes are already present
+ // and focus those instead.
+ foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
+ Analyze(node.Member);
+ }
+ } else if (segment != null && segment.Reference is MemberReference) {
+ if (segment.Reference is MemberReference)
+ Analyze((MemberReference)segment.Reference);
+ // TODO: implement support for other references: ParameterReference, etc.
}
}
public static void Analyze(MemberReference member)
{
- TypeDefinition type = member as TypeDefinition;
+ TypeDefinition type = null;
+ if (member is TypeReference)
+ type = ((TypeReference)member).Resolve();
if (type != null)
- AnalyzerTreeView.Instance.Show(new AnalyzedTypeTreeNode(type));
+ AnalyzerTreeView.Instance.Show(new AnalyzedTypeTreeNode(type.Resolve()));
FieldDefinition field = member as FieldDefinition;
if (field != null)
AnalyzerTreeView.Instance.Show(new AnalyzedFieldTreeNode(field));
diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs
index ee11b7b93..ea9ac9af9 100644
--- a/ILSpy/TreeNodes/AssemblyTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs
@@ -255,22 +255,26 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
- [ExportContextMenuEntry(Header = "_Remove", Icon = "images/Delete.png")]
+ [ExportContextMenuEntryAttribute(Header = "_Remove", Icon = "images/Delete.png")]
sealed class RemoveAssembly : IContextMenuEntry
{
- public bool IsVisible(SharpTreeNode[] selectedNodes)
+ public bool IsVisible(TextViewContext context)
{
- return selectedNodes.All(n => n is AssemblyTreeNode);
+ if (context.SelectedTreeNodes == null)
+ return false;
+ return context.SelectedTreeNodes.All(n => n is AssemblyTreeNode);
}
- public bool IsEnabled(SharpTreeNode[] selectedNodes)
+ public bool IsEnabled(TextViewContext context)
{
return true;
}
- public void Execute(SharpTreeNode[] selectedNodes)
+ public void Execute(TextViewContext context)
{
- foreach (var node in selectedNodes) {
+ if (context.SelectedTreeNodes == null)
+ return;
+ foreach (var node in context.SelectedTreeNodes) {
node.Delete();
}
}
diff --git a/TestPlugin/ContextMenuCommand.cs b/TestPlugin/ContextMenuCommand.cs
index dc0f4c576..f19000aed 100644
--- a/TestPlugin/ContextMenuCommand.cs
+++ b/TestPlugin/ContextMenuCommand.cs
@@ -11,22 +11,24 @@ using Mono.Cecil;
namespace TestPlugin
{
- [ExportContextMenuEntry(Header = "_Save Assembly")]
+ [ExportContextMenuEntryAttribute(Header = "_Save Assembly")]
public class SaveAssembly : IContextMenuEntry
{
- public bool IsVisible(SharpTreeNode[] selectedNodes)
+ public bool IsVisible(TextViewContext context)
{
- return selectedNodes.All(n => n is AssemblyTreeNode);
+ return context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AssemblyTreeNode);
}
- public bool IsEnabled(SharpTreeNode[] selectedNodes)
+ public bool IsEnabled(TextViewContext context)
{
- return selectedNodes.Length == 1;
+ return context.SelectedTreeNodes != null && context.SelectedTreeNodes.Length == 1;
}
- public void Execute(SharpTreeNode[] selectedNodes)
+ public void Execute(TextViewContext context)
{
- AssemblyTreeNode node = (AssemblyTreeNode)selectedNodes[0];
+ if (context.SelectedTreeNodes == null)
+ return;
+ AssemblyTreeNode node = (AssemblyTreeNode)context.SelectedTreeNodes[0];
AssemblyDefinition asm = node.LoadedAssembly.AssemblyDefinition as AssemblyDefinition;
if (asm != null) {
SaveFileDialog dlg = new SaveFileDialog();