Browse Source

#694 Add an "Open in ILSpy" option to the context menu in the code window

https://github.com/icsharpcode/ILSpy/issues/694

Supports methods, enums, delegates, properties, interfaces, events and classes.

TODO: disambiguate overloaded methods by number and type of parameters

Changes (only) to ILSpy.AddIn project:
add: code window context menu item "Open code in ILSpy"
add: OpenCodeItemInILSpyCallback does the work, using Visual Studio code model to interrogate source at selection point
change: add additional argument support to OpenAssemblyInILSpy (to support "/navigateTo")
pull/695/head
yggy 9 years ago
parent
commit
3f22e62a88
  1. 14
      ILSpy.AddIn/ILSpyAddIn.vsct
  2. 87
      ILSpy.AddIn/ILSpyAddInPackage.cs
  3. 1
      ILSpy.AddIn/PkgCmdID.cs
  4. 4
      ILSpy.AddIn/Utils.cs

14
ILSpy.AddIn/ILSpyAddIn.vsct

@ -47,6 +47,10 @@ @@ -47,6 +47,10 @@
<Group guid="guidILSpyAddInCmdSet" id="OpenILSpyProjGroup" priority="0x0200">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
</Group>
<Group guid="guidILSpyAddInCmdSet" id="OpenILSpyCodeItemGroup" priority="0x0200">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN"/>
</Group>
</Groups>
<!--Buttons section. -->
@ -77,6 +81,14 @@ @@ -77,6 +81,14 @@
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenCodeItemInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyCodeItemGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<Strings>
<ButtonText>Open code in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyGroup" />
<Icon guid="guidImages" id="bmpLogo" />
@ -114,9 +126,11 @@ @@ -114,9 +126,11 @@
<IDSymbol name="OpenILSpyGroup" value="0x1010" />
<IDSymbol name="OpenILSpyRefGroup" value="0x1020" />
<IDSymbol name="OpenILSpyProjGroup" value="0x1030" />
<IDSymbol name="OpenILSpyCodeItemGroup" value="0x1040" />
<IDSymbol name="cmdidOpenILSpy" value="0x0100" />
<IDSymbol name="cmdidOpenReferenceInILSpy" value="0x0200" />
<IDSymbol name="cmdidOpenProjectOutputInILSpy" value="0x0300" />
<IDSymbol name="cmdidOpenCodeItemInILSpy" value="0x0400" />
</GuidSymbol>
<GuidSymbol name="guidImages" value="{2f654db9-4641-4638-9937-27c6202b2a6a}" >

87
ILSpy.AddIn/ILSpyAddInPackage.cs

@ -77,9 +77,14 @@ namespace ICSharpCode.ILSpy.AddIn @@ -77,9 +77,14 @@ namespace ICSharpCode.ILSpy.AddIn
mcs.AddCommand(menuItem2);
// Create the command for the menu item.
CommandID menuCommandID3 = new CommandID(GuidList.guidILSpyAddInCmdSet, (int)PkgCmdIDList.cmdidOpenILSpy);
MenuCommand menuItem3 = new MenuCommand(OpenILSpyCallback, menuCommandID3);
CommandID menuCommandID3 = new CommandID(GuidList.guidILSpyAddInCmdSet, (int)PkgCmdIDList.cmdidOpenCodeItemInILSpy);
MenuCommand menuItem3 = new MenuCommand(OpenCodeItemInILSpyCallback, menuCommandID3);
mcs.AddCommand(menuItem3);
// Create the command for the menu item.
CommandID menuCommandID4 = new CommandID(GuidList.guidILSpyAddInCmdSet, (int)PkgCmdIDList.cmdidOpenILSpy);
MenuCommand menuItem4 = new MenuCommand(OpenILSpyCallback, menuCommandID4);
mcs.AddCommand(menuItem4);
}
}
#endregion
@ -122,6 +127,74 @@ namespace ICSharpCode.ILSpy.AddIn @@ -122,6 +127,74 @@ namespace ICSharpCode.ILSpy.AddIn
}
}
private void OpenCodeItemInILSpyCallback(object sender, EventArgs e)
{
var document = (EnvDTE.Document)(((EnvDTE80.DTE2)GetGlobalService(typeof(EnvDTE.DTE))).ActiveDocument);
var selection = (EnvDTE.TextPoint)((EnvDTE.TextSelection)document.Selection).ActivePoint;
var projectItem = document.ProjectItem;
string navigateTo = null;
// Find the full name of the method or class enclosing the current selection.
// Note that order of testing is important, need to get the narrowest, most
// internal element first.
//
// Add a prefix to match the ILSpy command line (see doc\Command Line.txt).
// The prefix characters are documented in Appendix A of the C# specification:
// E Event
// F Field
// M Method (including constructors, destructors, and operators)
// N Namespace
// P Property (including indexers)
// T Type (such as class, delegate, enum, interface, and struct)
navigateTo = GetCodeElementFullName("/navigateTo:M:", projectItem, selection, EnvDTE.vsCMElement.vsCMElementFunction);
if (navigateTo == null) {
navigateTo = GetCodeElementFullName("/navigateTo:E:", projectItem, selection, EnvDTE.vsCMElement.vsCMElementEvent);
}
if (navigateTo == null) {
navigateTo = GetCodeElementFullName("/navigateTo:P:", projectItem, selection, EnvDTE.vsCMElement.vsCMElementProperty);
}
if (navigateTo == null) {
navigateTo = GetCodeElementFullName("/navigateTo:T:", projectItem, selection,
EnvDTE.vsCMElement.vsCMElementDelegate,
EnvDTE.vsCMElement.vsCMElementEnum,
EnvDTE.vsCMElement.vsCMElementInterface,
EnvDTE.vsCMElement.vsCMElementStruct,
EnvDTE.vsCMElement.vsCMElementClass);
}
EnvDTE.Project project = projectItem.ContainingProject;
EnvDTE.Configuration config = project.ConfigurationManager.ActiveConfiguration;
string projectPath = Path.GetDirectoryName(project.FileName);
string outputPath = config.Properties.Item("OutputPath").Value.ToString();
string assemblyFileName = project.Properties.Item("OutputFileName").Value.ToString();
// Note that if navigateTo is still null this will just open ILSpy on the assembly.
OpenAssemblyInILSpy(Path.Combine(projectPath, outputPath, assemblyFileName), navigateTo);
}
private string GetCodeElementFullName(string prefix, EnvDTE.ProjectItem file, EnvDTE.TextPoint selection, params EnvDTE.vsCMElement[] elementTypes)
{
foreach (var elementType in elementTypes)
{
try
{
var codeElement = file.FileCodeModel.CodeElementFromPoint(selection, elementType);
if (elementType == EnvDTE.vsCMElement.vsCMElementFunction)
{
// TODO: use codeElement.Parameters to disambiguate overloaded methods
}
return prefix + codeElement.FullName;
}
catch (COMException)
{
//Don’t do anything – this is expected if there is no such code element at specified point.
}
}
return null;
}
private void OpenILSpyCallback(object sender, EventArgs e)
{
Process.Start(GetILSpyPath());
@ -133,13 +206,19 @@ namespace ICSharpCode.ILSpy.AddIn @@ -133,13 +206,19 @@ namespace ICSharpCode.ILSpy.AddIn
return Path.Combine(basePath, "ILSpy.exe");
}
private void OpenAssemblyInILSpy(string assemblyFileName)
private void OpenAssemblyInILSpy(string assemblyFileName, params string[] arguments)
{
if (!File.Exists(assemblyFileName)) {
ShowMessage("Could not find assembly '{0}', please ensure the project and all references were built correctly!", assemblyFileName);
return;
}
Process.Start(GetILSpyPath(), Utils.ArgumentArrayToCommandLine(assemblyFileName));
string commandLineArguments = Utils.ArgumentArrayToCommandLine(assemblyFileName);
if (arguments != null) {
commandLineArguments = string.Concat(commandLineArguments, " ", Utils.ArgumentArrayToCommandLine(arguments));
}
Process.Start(GetILSpyPath(), commandLineArguments);
}
private void ShowMessage(string format, params object[] items)

1
ILSpy.AddIn/PkgCmdID.cs

@ -9,5 +9,6 @@ namespace ICSharpCode.ILSpy.AddIn @@ -9,5 +9,6 @@ namespace ICSharpCode.ILSpy.AddIn
public const uint cmdidOpenILSpy = 0x100;
public const uint cmdidOpenReferenceInILSpy = 0x200;
public const uint cmdidOpenProjectOutputInILSpy = 0x300;
public const uint cmdidOpenCodeItemInILSpy = 0x0400;
};
}

4
ILSpy.AddIn/Utils.cs

@ -74,6 +74,10 @@ namespace ICSharpCode.ILSpy.AddIn @@ -74,6 +74,10 @@ namespace ICSharpCode.ILSpy.AddIn
static void AppendArgument(StringBuilder b, string arg)
{
if (arg == null) {
return;
}
if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) {
b.Append(arg);
} else {

Loading…
Cancel
Save