diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin index 486a48a727..19a2adb926 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin @@ -12,7 +12,11 @@ - + + + + + diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj index 51a0e4026e..e2d4ae0e46 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj @@ -40,17 +40,30 @@ + + 3.0 + + + 3.0 + 3.5 + + 4.0 + + + 3.0 + Properties\GlobalAssemblyInfo.cs + @@ -58,6 +71,8 @@ SetILSpyPathDialog.cs + + SetILSpyPathDialog.cs @@ -66,6 +81,11 @@ + + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} + ICSharpCode.AvalonEdit + False + {984CC812-9470-4A13-AFF9-CC44068D666C} ICSharpCode.Decompiler @@ -94,8 +114,14 @@ ICSharpCode.SharpDevelop.Dom False + + {0162E499-42D0-409B-AA25-EED21F75336B} + AvalonEdit.AddIn + False + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs index 50e3ee303f..7bc3e20acb 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs @@ -32,12 +32,7 @@ namespace ICSharpCode.ILSpyAddIn ReflectionProjectContent rpc = pc as ReflectionProjectContent; string assemblyLocation = null; if (rpc != null) { - // prefer GAC assemblies over reference assemblies: - assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName)); - if (string.IsNullOrEmpty(assemblyLocation)) { - // use file only if assembly isn't in GAC: - assemblyLocation = rpc.AssemblyLocation; - } + assemblyLocation = GetAssemblyLocation(rpc); } else { IProject project = pc.Project as IProject; if (project != null) { @@ -59,6 +54,19 @@ namespace ICSharpCode.ILSpyAddIn Process.Start(ilspyPath, commandLine); } + public static string GetAssemblyLocation(ReflectionProjectContent rpc) + { + if (rpc == null) + throw new ArgumentNullException("rpc"); + // prefer GAC assemblies over reference assemblies: + string assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName)); + if (string.IsNullOrEmpty(assemblyLocation)) { + // use file only if assembly isn't in GAC: + assemblyLocation = rpc.AssemblyLocation; + } + return assemblyLocation; + } + #region Find ILSpy internal const string ILSpyExePathPropertyName = "ILSpyAddIn.ILSpyExePath"; diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs new file mode 100644 index 0000000000..e4a0f88884 --- /dev/null +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs @@ -0,0 +1,54 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Linq; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.ILSpyAddIn +{ + public class NavigateToDecompiledEntityService : INavigateToEntityService + { + public bool NavigateTo(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + + // Get the underlying entity for generic instance members + while ((entity is IMember) && ((IMember)entity).GenericMember != null) + entity = ((IMember)entity).GenericMember; + + IClass declaringType = (entity as IClass) ?? entity.DeclaringType; + if (declaringType == null) + return false; + // get the top-level type + while (declaringType.DeclaringType != null) + declaringType = declaringType.DeclaringType; + + ReflectionProjectContent rpc = entity.ProjectContent as ReflectionProjectContent; + if (rpc != null) { + string assemblyLocation = ILSpyController.GetAssemblyLocation(rpc); + if (!string.IsNullOrEmpty(assemblyLocation) && File.Exists(assemblyLocation)) { + NavigateTo(assemblyLocation, declaringType.FullyQualifiedName, ((AbstractEntity)entity).DocumentationTag); + return true; + } + } + return false; + } + + public static void NavigateTo(string assemblyFile, string typeName, string entityTag) + { + foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType()) { + if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) { + vc.WorkbenchWindow.SelectWindow(); + vc.JumpToEntity(entityTag); + return; + } + } + WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag)); + } + } +} diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs index b804da8885..9d32b6fb8a 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs @@ -20,5 +20,3 @@ using System.Reflection; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] - -[assembly: System.CLSCompliant(true)] diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs new file mode 100644 index 0000000000..1ff758072a --- /dev/null +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs @@ -0,0 +1,34 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Windows.Controls; +using ICSharpCode.AvalonEdit.AddIn; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Highlighting; + +namespace ICSharpCode.ILSpyAddIn.ViewContent +{ + /// + /// Equivalent to AE.AddIn CodeEditor, but without editing capabilities. + /// + public class CodeView : Grid, IDisposable + { + readonly SharpDevelopTextEditor textEditor = new SharpDevelopTextEditor(); + + public CodeView() + { + this.Children.Add(textEditor); + textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#"); + } + + public TextDocument Document { + get { return textEditor.Document; } + set { textEditor.Document = value; } + } + + public void Dispose() + { + } + } +} diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs new file mode 100644 index 0000000000..304bffa655 --- /dev/null +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs @@ -0,0 +1,156 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Threading; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.Core; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.ILSpyAddIn.ViewContent; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop.Gui; +using Mono.Cecil; + +namespace ICSharpCode.ILSpyAddIn +{ + /// + /// Hosts a decompiled type. + /// + public class DecompiledViewContent : AbstractViewContentWithoutFile + { + readonly string assemblyFile; + readonly string fullTypeName; + + /// + /// Entity to jump to once decompilation has finished. + /// + string jumpToEntityTagWhenDecompilationFinished; + + bool decompilationFinished; + + readonly CodeView codeView = new CodeView(); + readonly CancellationTokenSource cancellation = new CancellationTokenSource(); + + #region Constructor + public DecompiledViewContent(string assemblyFile, string fullTypeName, string entityTag) + { + this.assemblyFile = assemblyFile; + this.fullTypeName = fullTypeName; + this.jumpToEntityTagWhenDecompilationFinished = entityTag; + + string shortTypeName = fullTypeName.Substring(fullTypeName.LastIndexOf('.') + 1); + this.TitleName = "[" + ReflectionHelper.SplitTypeParameterCountFromReflectionName(shortTypeName) + "]"; + + Thread thread = new Thread(DecompilationThread); + thread.Name = "Decompiler (" + shortTypeName + ")"; + thread.Start(); + } + #endregion + + #region Properties + public string AssemblyFile { + get { return assemblyFile; } + } + + public string FullTypeName { + get { return fullTypeName; } + } + + public override object Control { + get { return codeView; } + } + #endregion + + #region Dispose + public override void Dispose() + { + cancellation.Cancel(); + codeView.Dispose(); + base.Dispose(); + } + #endregion + + #region Load/Save + public override void Load() + { + // nothing to do... + } + + public override void Save() + { + if (!decompilationFinished) + return; + // TODO: show Save As dialog to allow the user to save the decompiled file + } + #endregion + + #region JumpToEntity + public void JumpToEntity(string entityTag) + { + if (!decompilationFinished) { + this.jumpToEntityTagWhenDecompilationFinished = entityTag; + return; + } + // TODO: implement this + } + #endregion + + #region Decompilation + void DecompilationThread() + { + try { + StringWriter writer = new StringWriter(); + RunDecompiler(assemblyFile, fullTypeName, new PlainTextOutput(writer), cancellation.Token); + if (!cancellation.IsCancellationRequested) { + WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer); + } + } catch (OperationCanceledException) { + // ignore cancellation + } catch (Exception ex) { + if (cancellation.IsCancellationRequested) { + MessageService.ShowException(ex); + return; + } + AnalyticsMonitorService.TrackException(ex); + + StringWriter writer = new StringWriter(); + writer.WriteLine("Exception while decompiling " + fullTypeName); + writer.WriteLine(); + writer.WriteLine(ex.ToString()); + WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer); + } + } + + static void RunDecompiler(string assemblyFile, string fullTypeName, ITextOutput textOutput, CancellationToken cancellationToken) + { + ReaderParameters readerParameters = new ReaderParameters(); + // Use new assembly resolver instance so that the AssemblyDefinitions can be garbage-collected + // once the code is decompiled. + readerParameters.AssemblyResolver = new DefaultAssemblyResolver(); + + ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFile, readerParameters); + TypeDefinition typeDefinition = module.GetType(fullTypeName); + if (typeDefinition == null) + throw new InvalidOperationException("Could not find type"); + DecompilerContext context = new DecompilerContext(module); + context.CancellationToken = cancellationToken; + AstBuilder astBuilder = new AstBuilder(context); + astBuilder.AddType(typeDefinition); + astBuilder.GenerateCode(textOutput); + } + + void OnDecompilationFinished(StringWriter output) + { + if (cancellation.IsCancellationRequested) + return; + codeView.Document.Text = output.ToString(); + codeView.Document.UndoStack.ClearAll(); + + this.decompilationFinished = true; + JumpToEntity(this.jumpToEntityTagWhenDecompilationFinished); + } + #endregion + } +} diff --git a/src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs b/src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs index 8f7794f90c..7a2649e14d 100644 --- a/src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs +++ b/src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs @@ -15,11 +15,19 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands { protected override void RunImpl(ITextEditor editor, int offset, ResolveResult symbol) { - if (symbol == null) - return; FilePosition pos = symbol.GetDefinitionPosition(); if (pos.IsEmpty) { - //new GoToDecompiledDefinition().Run(symbol); + IEntity entity; + if (symbol is MemberResolveResult) { + entity = ((MemberResolveResult)symbol).ResolvedMember; + } else if (symbol is TypeResolveResult) { + entity = ((TypeResolveResult)symbol).ResolvedClass; + } else { + entity = null; + } + if (entity != null) { + NavigationService.NavigateTo(entity); + } } else { try { if (pos.Position.IsEmpty) diff --git a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs index 3a8b3d5743..52031f035c 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs @@ -55,9 +55,7 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser public override void ActivateItem() { - if (c.CompilationUnit != null) { - FileService.JumpToFilePosition(c.CompilationUnit.FileName, c.Region.BeginLine, c.Region.BeginColumn); - } + NavigationService.NavigateTo(c); } protected override void Initialize() diff --git a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs index a1b18e8d34..632133607b 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs @@ -8,19 +8,9 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser { public class MemberNode : ExtTreeNode { - int line; - int column; ModifierEnum modifiers; IClass declaringType; - string FileName { - get { - if (declaringType == null || declaringType.CompilationUnit == null) { - return null; - } - return declaringType.CompilationUnit.FileName; - } - } public override bool Visible { get { ClassBrowserFilter filter = ClassBrowserPad.Instance.Filter; @@ -51,8 +41,6 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser this.ContextmenuAddinTreePath = "/SharpDevelop/Pads/ClassBrowser/MemberContextMenu"; declaringType = member.DeclaringType; modifiers = member.Modifiers; - line = member.Region.BeginLine; - column = member.Region.BeginColumn; } public static string GetText(IMember member) @@ -123,9 +111,7 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser public override void ActivateItem() { - if (FileName != null) { - FileService.JumpToFilePosition(FileName, line, column); - } + NavigationService.NavigateTo(member); } } } diff --git a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs index 6043ba911b..91ad361845 100644 --- a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs +++ b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs @@ -445,5 +445,42 @@ namespace ICSharpCode.SharpDevelop } } #endregion + + #region Navigate To Entity + public static bool NavigateTo(Dom.IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + var cu = entity.CompilationUnit; + Dom.DomRegion region; + if (entity is Dom.IClass) + region = ((Dom.IClass)entity).Region; + else if (entity is Dom.IMember) + region = ((Dom.IMember)entity).Region; + else + region = Dom.DomRegion.Empty; + + if (cu == null || string.IsNullOrEmpty(cu.FileName) || region.IsEmpty) { + foreach (var item in AddInTree.BuildItems("/SharpDevelop/Services/NavigateToEntityService", null, false)) { + if (item.NavigateTo(entity)) + return true; + } + return false; + } else { + return FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn) != null; + } + } + #endregion + } + + /// + /// Called by when the entity is not defined in source code. + /// + /// + /// Loaded from addin tree path "/SharpDevelop/Services/NavigateToEntityService" + /// + public interface INavigateToEntityService + { + bool NavigateTo(Dom.IEntity entity); } } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs b/src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs index afda99db15..176261b3a0 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs @@ -26,11 +26,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring public void Execute() { - var cu = this.Class.CompilationUnit; - var region = this.Class.Region; - if (cu == null || cu.FileName == null || region == null || region.IsEmpty) - return; - FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn); + NavigationService.NavigateTo(this.Class); } } } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs b/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs index c2ba735569..51168ff6fc 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs @@ -26,11 +26,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring public void Execute() { - var cu = this.Member.CompilationUnit; - var region = this.Member.Region; - if (cu == null || cu.FileName == null || region == null || region.IsEmpty) - return; - FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn); + NavigationService.NavigateTo(this.Member); } } }