diff --git a/ILSpy.AddIn/Commands/AssemblyReferenceForILSpy.cs b/ILSpy.AddIn/Commands/AssemblyReferenceForILSpy.cs new file mode 100644 index 000000000..cde4f679a --- /dev/null +++ b/ILSpy.AddIn/Commands/AssemblyReferenceForILSpy.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VSLangProj; + +namespace ICSharpCode.ILSpy.AddIn.Commands +{ + /// + /// Represents an assembly reference item in Solution Explorer, which can be opened in ILSpy. + /// + class AssemblyReferenceForILSpy + { + Reference reference; + + AssemblyReferenceForILSpy(Reference reference) + { + this.reference = reference; + } + + /// + /// Detects whether the given selected item represents a supported project. + /// + /// Data object of selected item to check. + /// instance or null, if item is not a supported project. + public static AssemblyReferenceForILSpy Detect(object itemData) + { + return (itemData is Reference reference) ? new AssemblyReferenceForILSpy(reference) : null; + } + + /// + /// If possible retrieves parameters to use for launching ILSpy instance. + /// + /// List of current project's references. + /// Parameters object or null, if not applicable. + public ILSpyParameters GetILSpyParameters(Dictionary projectReferences) + { + if (projectReferences.TryGetValue(reference.Name, out var path)) + return new ILSpyParameters(new[] { path }); + + return null; + } + } +} diff --git a/ILSpy.AddIn/Commands/NuGetReferenceForILSpy.cs b/ILSpy.AddIn/Commands/NuGetReferenceForILSpy.cs new file mode 100644 index 000000000..bcbaa82a3 --- /dev/null +++ b/ILSpy.AddIn/Commands/NuGetReferenceForILSpy.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EnvDTE; + +namespace ICSharpCode.ILSpy.AddIn.Commands +{ + /// + /// Represents a NuGet package item in Solution Explorer, which can be opened in ILSpy. + /// + class NuGetReferenceForILSpy + { + ProjectItem projectItem; + + NuGetReferenceForILSpy(ProjectItem projectItem) + { + this.projectItem = projectItem; + } + + /// + /// Detects whether the given selected item represents a supported project. + /// + /// Data object of selected item to check. + /// instance or null, if item is not a supported project. + public static NuGetReferenceForILSpy Detect(object itemData) + { + if (itemData is ProjectItem projectItem) { + var properties = Utils.GetProperties(projectItem.Properties, "Type"); + if ((properties[0] as string) == "Package") { + return new NuGetReferenceForILSpy(projectItem); + } + } + + return null; + } + + /// + /// If possible retrieves parameters to use for launching ILSpy instance. + /// + /// Parameters object or null, if not applicable. + public ILSpyParameters GetILSpyParameters() + { + var properties = Utils.GetProperties(projectItem.Properties, "Name", "Version", "Path"); + if (properties[0] != null && properties[1] != null && properties[2] != null) { + return new ILSpyParameters(new[] { $"{properties[2]}\\{properties[0]}.{properties[1]}.nupkg" }); + } + + return null; + } + } +} diff --git a/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs b/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs index 30bca416e..9db92cdf9 100644 --- a/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs +++ b/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs @@ -47,7 +47,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands if (symbol == null) return; var refs = GetReferences(roslynDocument.Project).Select(fn => fn.Value).Where(f => File.Exists(f)).ToArray(); - OpenAssembliesInILSpy(refs, "/navigateTo:" + symbol.GetDocumentationCommentId()); + OpenAssembliesInILSpy(new ILSpyParameters(refs, "/navigateTo:" + symbol.GetDocumentationCommentId())); } internal static void Register(ILSpyAddInPackage owner) diff --git a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs index 9d44d1b7d..d3e1254f1 100644 --- a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs @@ -11,6 +11,18 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.AddIn.Commands { + public class ILSpyParameters + { + public ILSpyParameters(IEnumerable assemblyFileNames, params string[] arguments) + { + this.AssemblyFileNames = assemblyFileNames; + this.Arguments = arguments; + } + + public IEnumerable AssemblyFileNames { get; private set; } + public string[] Arguments { get; private set; } + } + abstract class ILSpyCommand { protected ILSpyAddInPackage owner; @@ -36,18 +48,21 @@ namespace ICSharpCode.ILSpy.AddIn.Commands return Path.Combine(basePath, "ILSpy.exe"); } - protected void OpenAssembliesInILSpy(IEnumerable assemblyFileNames, params string[] arguments) + protected void OpenAssembliesInILSpy(ILSpyParameters parameters) { - foreach (string assemblyFileName in assemblyFileNames) { + if (parameters == null) + return; + + foreach (string assemblyFileName in parameters.AssemblyFileNames) { if (!File.Exists(assemblyFileName)) { owner.ShowMessage("Could not find assembly '{0}', please ensure the project and all references were built correctly!", assemblyFileName); return; } } - string commandLineArguments = Utils.ArgumentArrayToCommandLine(assemblyFileNames.ToArray()); - if (arguments != null) { - commandLineArguments = string.Concat(commandLineArguments, " ", Utils.ArgumentArrayToCommandLine(arguments)); + string commandLineArguments = Utils.ArgumentArrayToCommandLine(parameters.AssemblyFileNames.ToArray()); + if (parameters.Arguments != null) { + commandLineArguments = string.Concat(commandLineArguments, " ", Utils.ArgumentArrayToCommandLine(parameters.Arguments)); } System.Diagnostics.Process.Start(GetILSpyPath(), commandLineArguments); diff --git a/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs b/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs index 699b54d7e..a2f23957a 100644 --- a/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs +++ b/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs @@ -15,16 +15,12 @@ namespace ICSharpCode.ILSpy.AddIn.Commands protected override void OnExecute(object sender, EventArgs e) { - if (owner.DTE.SelectedItems.Count != 1) return; - var project = owner.DTE.SelectedItems.Item(1).Project; - var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == project.FileName); - string outputFileName = Path.GetFileName(roslynProject.OutputFilePath); - //get the directory path based on the project file. - string projectPath = Path.GetDirectoryName(project.FullName); - //get the output path based on the active configuration - string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString(); - //combine the project path and output path to get the bin path - OpenAssembliesInILSpy(new[] { Path.Combine(projectPath, projectOutputPath, outputFileName) }); + if (owner.DTE.SelectedItems.Count != 1) + return; + var projectItemWrapper = ProjectItemForILSpy.Detect(owner.DTE.SelectedItems.Item(1)); + if (projectItemWrapper != null) { + OpenAssembliesInILSpy(projectItemWrapper.GetILSpyParameters(owner)); + } } internal static void Register(ILSpyAddInPackage owner) diff --git a/ILSpy.AddIn/Commands/OpenReferenceCommand.cs b/ILSpy.AddIn/Commands/OpenReferenceCommand.cs index d9cb95981..f3c2fcc00 100644 --- a/ILSpy.AddIn/Commands/OpenReferenceCommand.cs +++ b/ILSpy.AddIn/Commands/OpenReferenceCommand.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using EnvDTE; using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Shell; using Mono.Cecil; using VSLangProj; @@ -17,109 +18,70 @@ namespace ICSharpCode.ILSpy.AddIn.Commands { } + protected override void OnBeforeQueryStatus(object sender, EventArgs e) + { + if (sender is OleMenuCommand menuItem) { + menuItem.Visible = false; + + var selectedItemData = owner.GetSelectedItemsData().FirstOrDefault(); + if (selectedItemData == null) + return; + + /* + * Assure that we only show the context menu item on items we intend: + * - Project references + * - NuGet package references + */ + if ((AssemblyReferenceForILSpy.Detect(selectedItemData) != null) + || (ProjectReferenceForILSpy.Detect(selectedItemData) != null) + || (NuGetReferenceForILSpy.Detect(selectedItemData) != null)) { + menuItem.Visible = true; + } + } + } + protected override void OnExecute(object sender, EventArgs e) { - var explorer = owner.DTE.ToolWindows.SolutionExplorer; - var item = ((object[])explorer.SelectedItems).FirstOrDefault() as UIHierarchyItem; + var itemObject = owner.GetSelectedItemsData().FirstOrDefault(); + if (itemObject == null) + return; - if (item == null) return; - if (item.Object is Reference reference) { + var referenceItem = AssemblyReferenceForILSpy.Detect(itemObject); + if (referenceItem != null) { + Reference reference = itemObject as Reference; var project = reference.ContainingProject; var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == project.FileName); var references = GetReferences(roslynProject); + var parameters = referenceItem.GetILSpyParameters(references); if (references.TryGetValue(reference.Name, out var path)) - OpenAssembliesInILSpy(new[] { path }); + OpenAssembliesInILSpy(parameters); else owner.ShowMessage("Could not find reference '{0}', please ensure the project and all references were built correctly!", reference.Name); - } else { - dynamic referenceObject = item.Object; - if (TryGetProjectFileName(referenceObject, out string fileName)) { - var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == fileName); - var references = GetReferences(roslynProject); - if (references.TryGetValue(referenceObject.Name, out string path)) { - OpenAssembliesInILSpy(new[] { path }); - return; - } - } else { - var values = GetProperties(referenceObject.Properties, "Type", "FusionName", "ResolvedPath"); - if (values[0] == "Package") { - values = GetProperties(referenceObject.Properties, "Name", "Version", "Path"); - if (values[0] != null && values[1] != null && values[2] != null) { - OpenAssembliesInILSpy(new[] { $"{values[2]}\\{values[0]}.{values[1]}.nupkg" }); - return; - } - } else if (values[2] != null) { - OpenAssembliesInILSpy(new[] { $"{values[2]}" }); - return; - } else if (!string.IsNullOrWhiteSpace(values[1])) { - OpenAssembliesInILSpy(new string[] { GacInterop.FindAssemblyInNetGac(AssemblyNameReference.Parse(values[1])) }); - return; - } - } - owner.ShowMessage("Could not find reference '{0}', please ensure the project and all references were built correctly!", referenceObject.Name); } - } - private bool TryGetProjectFileName(dynamic referenceObject, out string fileName) - { - try { - fileName = referenceObject.Project.FileName; - return true; - } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) { - fileName = null; - return false; + // Handle NuGet references + var nugetRefItem = NuGetReferenceForILSpy.Detect(itemObject); + if (nugetRefItem != null) { + OpenAssembliesInILSpy(nugetRefItem.GetILSpyParameters()); } - } - private string[] GetProperties(Properties properties, params string[] names) - { - string[] values = new string[names.Length]; - foreach (dynamic p in properties) { - try { - for (int i = 0; i < names.Length; i++) { - if (names[i] == p.Name) { - values[i] = p.Value; - break; - } - } - } catch { - continue; - } - } - return values; - } - - private object GetPropertyObject(EnvDTE.Properties properties, string name) - { - foreach (dynamic p in properties) { - try { - if (name == p.Name) { - return p.Object; + // Handle project references + var projectRefItem = ProjectReferenceForILSpy.Detect(itemObject); + if (projectRefItem != null) { + var projectItem = itemObject as ProjectItem; + string fileName = projectItem.ContainingProject?.FileName; + if (!string.IsNullOrEmpty(fileName)) { + var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == fileName); + var references = GetReferences(roslynProject); + if (references.TryGetValue(projectItem.Name, out string path)) { + OpenAssembliesInILSpy(projectRefItem.GetILSpyParameters(references)); + return; } - } catch { - continue; } - } - return null; - } - - private bool HasProperties(EnvDTE.Properties properties, params string[] names) - { - return properties.Count > 0 && names.Any(n => HasProperty(properties, n)); - } - private bool HasProperty(EnvDTE.Properties properties, string name) - { - foreach (dynamic p in properties) { - try { - if (name == p.Name) { - return true; - } - } catch { - continue; - } + OpenAssembliesInILSpy(projectRefItem.GetILSpyParameters()); + return; } - return false; } internal static void Register(ILSpyAddInPackage owner) @@ -127,4 +89,5 @@ namespace ICSharpCode.ILSpy.AddIn.Commands instance = new OpenReferenceCommand(owner); } } + } diff --git a/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs b/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs new file mode 100644 index 000000000..e52cc6a25 --- /dev/null +++ b/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EnvDTE; + +namespace ICSharpCode.ILSpy.AddIn.Commands +{ + /// + /// Represents a project item in Solution Explorer, which can be opened in ILSpy. + /// + class ProjectItemForILSpy + { + SelectedItem item; + + ProjectItemForILSpy(SelectedItem item) + { + this.item = item; + } + + /// + /// Detects whether the given represents a supported project. + /// + /// Selected item to check. + /// instance or null, if item is not a supported project. + public static ProjectItemForILSpy Detect(SelectedItem item) + { + return new ProjectItemForILSpy(item); + } + + /// + /// If possible retrieves parameters to use for launching ILSpy instance. + /// + /// Package instance. + /// Parameters object or null, if not applicable. + public ILSpyParameters GetILSpyParameters(ILSpyAddInPackage owner) + { + var project = item.Project; + var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == project.FileName); + string outputFileName = Path.GetFileName(roslynProject.OutputFilePath); + + // Get the directory path based on the project file. + string projectPath = Path.GetDirectoryName(project.FullName); + + // Get the output path based on the active configuration + string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString(); + + // Combine the project path and output path to get the bin path + return new ILSpyParameters(new[] { Path.Combine(projectPath, projectOutputPath, outputFileName) }); + } + } +} diff --git a/ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs b/ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs new file mode 100644 index 000000000..1927e9852 --- /dev/null +++ b/ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EnvDTE; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.AddIn.Commands +{ + /// + /// Represents a project reference item in Solution Explorer, which can be opened in ILSpy. + /// + class ProjectReferenceForILSpy + { + ProjectItem projectItem; + string fusionName; + string resolvedPath; + + ProjectReferenceForILSpy(ProjectItem projectItem, string fusionName, string resolvedPath) + { + this.projectItem = projectItem; + this.fusionName = fusionName; + this.resolvedPath = resolvedPath; + } + + /// + /// Detects whether the given selected item represents a supported project. + /// + /// Data object of selected item to check. + /// instance or null, if item is not a supported project. + public static ProjectReferenceForILSpy Detect(object itemData) + { + if (itemData is ProjectItem projectItem) { + var properties = Utils.GetProperties(projectItem.Properties, "FusionName", "ResolvedPath"); + string fusionName = properties[0] as string; + string resolvedPath = properties[1] as string; + if ((fusionName != null) || (resolvedPath != null)) { + return new ProjectReferenceForILSpy(projectItem, fusionName, resolvedPath); + } + } + + return null; + } + + /// + /// If possible retrieves parameters to use for launching ILSpy instance. + /// + /// List of current project's references. + /// Parameters object or null, if not applicable. + public ILSpyParameters GetILSpyParameters(Dictionary projectReferences) + { + string fileName = projectItem.ContainingProject?.FileName; + if (!string.IsNullOrEmpty(fileName)) { + if (projectReferences.TryGetValue(projectItem.Name, out string path)) { + return new ILSpyParameters(new[] { path }); + } + } + + return null; + } + + /// + /// If possible retrieves parameters to use for launching ILSpy instance. + /// + /// Parameters object or null, if not applicable. + public ILSpyParameters GetILSpyParameters() + { + if (resolvedPath != null) { + return new ILSpyParameters(new[] { $"{resolvedPath}" }); + } else if (!string.IsNullOrWhiteSpace(fusionName)) { + return new ILSpyParameters(new string[] { GacInterop.FindAssemblyInNetGac(AssemblyNameReference.Parse(fusionName)) }); + } + + return null; + } + } +} diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index af43a6069..14acc8975 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -63,6 +63,10 @@ + + + + @@ -100,6 +104,7 @@ + Designer diff --git a/ILSpy.AddIn/ILSpyAddIn.vsct b/ILSpy.AddIn/ILSpyAddIn.vsct index 716d8a431..cd79e807b 100644 --- a/ILSpy.AddIn/ILSpyAddIn.vsct +++ b/ILSpy.AddIn/ILSpyAddIn.vsct @@ -51,6 +51,10 @@ + + + + @@ -83,13 +87,13 @@ -