diff --git a/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs b/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs index 9db92cdf9..3119d4605 100644 --- a/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs +++ b/ILSpy.AddIn/Commands/OpenCodeItemCommand.cs @@ -3,7 +3,9 @@ using System.IO; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Text; namespace ICSharpCode.ILSpy.AddIn.Commands { @@ -18,10 +20,19 @@ namespace ICSharpCode.ILSpy.AddIn.Commands protected override void OnBeforeQueryStatus(object sender, EventArgs e) { - OleMenuCommand menuItem = sender as OleMenuCommand; - if (menuItem != null) { + if (sender is OleMenuCommand menuItem) { + menuItem.Visible = false; + + // Enable this item only if this is a .cs file! + if (Utils.GetCurrentViewHost(owner, f => f.EndsWith(".cs")) == null) + return; + + // Enable this item only if this is a Roslyn document + if (GetRoslynDocument() == null) + return; + var document = owner.DTE.ActiveDocument; - menuItem.Enabled = + menuItem.Visible = (document != null) && (document.ProjectItem != null) && (document.ProjectItem.ContainingProject != null) && @@ -30,26 +41,99 @@ namespace ICSharpCode.ILSpy.AddIn.Commands } } - protected override async void OnExecute(object sender, EventArgs e) + Document GetRoslynDocument() { var document = owner.DTE.ActiveDocument; var selection = (EnvDTE.TextPoint)((EnvDTE.TextSelection)document.Selection).ActivePoint; var id = owner.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(document.FullName).FirstOrDefault(); + if (id == null) + return null; - if (id == null) return; - var roslynDocument = owner.Workspace.CurrentSolution.GetDocument(id); + return owner.Workspace.CurrentSolution.GetDocument(id); + } + + EnvDTE.TextPoint GetEditorSelection() + { + var document = owner.DTE.ActiveDocument; + return ((EnvDTE.TextSelection)document.Selection).ActivePoint; + } + + protected override async void OnExecute(object sender, EventArgs e) + { + var textView = Utils.GetCurrentViewHost(owner)?.TextView; + if (textView == null) + return; + + SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition; + var roslynDocument = GetRoslynDocument(); var ast = await roslynDocument.GetSyntaxRootAsync().ConfigureAwait(false); var model = await roslynDocument.GetSemanticModelAsync().ConfigureAwait(false); - var node = ast.FindNode(new Microsoft.CodeAnalysis.Text.TextSpan(selection.AbsoluteCharOffset, 1)); - if (node == null) + var node = ast.FindNode(new TextSpan(caretPosition.Position, 0), false, true); + if (node == null) { + owner.ShowMessage("Can't show ILSpy for this code element!"); return; - var symbol = model.GetSymbolInfo(node).Symbol; - if (symbol == null) + } + + var symbol = GetSymbolResolvableByILSpy(model, node); + if (symbol == null) { + owner.ShowMessage("Can't show ILSpy for this code element!"); return; - var refs = GetReferences(roslynDocument.Project).Select(fn => fn.Value).Where(f => File.Exists(f)).ToArray(); + } + + + var roslynProject = roslynDocument.Project; + var refsmap = GetReferences(roslynProject); + + // Add our own project as well (not among references) + var project = owner.DTE.Solution.Projects.OfType() + .FirstOrDefault(p => p.FileName == roslynProject.FilePath); + if (project != null) { + string projectOutputPath = GetProjectOutputPath(project, roslynProject); + refsmap.Add(roslynDocument.Project.AssemblyName, projectOutputPath); + } + + var refs = refsmap.Select(fn => fn.Value).Where(f => File.Exists(f)); OpenAssembliesInILSpy(new ILSpyParameters(refs, "/navigateTo:" + symbol.GetDocumentationCommentId())); } + ISymbol GetSymbolResolvableByILSpy(SemanticModel model, SyntaxNode node) + { + var current = node; + while (current != null) { + var symbol = model.GetSymbolInfo(current).Symbol; + if (symbol == null) { + symbol = model.GetDeclaredSymbol(current); + } + + // ILSpy can only resolve some symbol types, so allow them, discard everything else + if (symbol != null) { + switch (symbol.Kind) { + case SymbolKind.ArrayType: + case SymbolKind.Event: + case SymbolKind.Field: + case SymbolKind.Method: + case SymbolKind.NamedType: + case SymbolKind.Namespace: + case SymbolKind.PointerType: + case SymbolKind.Property: + break; + default: + symbol = null; + break; + } + } + + if (symbol != null) + return symbol; + + current = current is IStructuredTriviaSyntax + ? ((IStructuredTriviaSyntax)current).ParentTrivia.Token.Parent + : current.Parent; + } + + return null; + } + internal static void Register(ILSpyAddInPackage owner) { instance = new OpenCodeItemCommand(owner); diff --git a/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs b/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs index a2f23957a..924f8deaa 100644 --- a/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs +++ b/ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace ICSharpCode.ILSpy.AddIn.Commands { @@ -13,11 +14,21 @@ namespace ICSharpCode.ILSpy.AddIn.Commands { } + protected override void OnBeforeQueryStatus(object sender, EventArgs e) + { + if (sender is OleMenuCommand menuItem) { + menuItem.Visible = false; + + var selectedItem = owner.DTE.SelectedItems.Item(1); + menuItem.Visible = (ProjectItemForILSpy.Detect(owner, selectedItem) != null); + } + } + protected override void OnExecute(object sender, EventArgs e) { if (owner.DTE.SelectedItems.Count != 1) return; - var projectItemWrapper = ProjectItemForILSpy.Detect(owner.DTE.SelectedItems.Item(1)); + var projectItemWrapper = ProjectItemForILSpy.Detect(owner, owner.DTE.SelectedItems.Item(1)); if (projectItemWrapper != null) { OpenAssembliesInILSpy(projectItemWrapper.GetILSpyParameters(owner)); } diff --git a/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs b/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs index e52cc6a25..9cc73346a 100644 --- a/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs +++ b/ILSpy.AddIn/Commands/ProjectItemForILSpy.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using EnvDTE; namespace ICSharpCode.ILSpy.AddIn.Commands @@ -14,9 +10,13 @@ namespace ICSharpCode.ILSpy.AddIn.Commands class ProjectItemForILSpy { SelectedItem item; + Project project; + Microsoft.CodeAnalysis.Project roslynProject; - ProjectItemForILSpy(SelectedItem item) + ProjectItemForILSpy(Project project, Microsoft.CodeAnalysis.Project roslynProject, SelectedItem item) { + this.project = project; + this.roslynProject = roslynProject; this.item = item; } @@ -25,30 +25,24 @@ namespace ICSharpCode.ILSpy.AddIn.Commands /// /// Selected item to check. /// instance or null, if item is not a supported project. - public static ProjectItemForILSpy Detect(SelectedItem item) + public static ProjectItemForILSpy Detect(ILSpyAddInPackage package, SelectedItem item) { - return new ProjectItemForILSpy(item); + var project = item.Project; + var roslynProject = package.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == project.FileName); + if (roslynProject == null) + return null; + + return new ProjectItemForILSpy(project, roslynProject, item); } /// /// If possible retrieves parameters to use for launching ILSpy instance. /// - /// Package instance. + /// Package instance. /// Parameters object or null, if not applicable. - public ILSpyParameters GetILSpyParameters(ILSpyAddInPackage owner) + public ILSpyParameters GetILSpyParameters(ILSpyAddInPackage package) { - 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) }); + return new ILSpyParameters(new[] { Utils.GetProjectOutputAssembly(project, roslynProject) }); } } } diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index b7e3a7aa6..26fdc6f75 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -49,8 +49,11 @@ + + + @@ -81,6 +84,7 @@ + diff --git a/ILSpy.AddIn/ILSpyAddIn.vsct b/ILSpy.AddIn/ILSpyAddIn.vsct index cd79e807b..d747baf57 100644 --- a/ILSpy.AddIn/ILSpyAddIn.vsct +++ b/ILSpy.AddIn/ILSpyAddIn.vsct @@ -82,18 +82,22 @@ - +