diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/DebuggerTextOutput.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/DebuggerTextOutput.cs index bd0a6b6b38..b13cfd3c4d 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/DebuggerTextOutput.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/DebuggerTextOutput.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using ICSharpCode.Core; using ICSharpCode.Decompiler; +using Mono.Cecil; namespace ICSharpCode.ILSpyAddIn { @@ -12,6 +14,7 @@ namespace ICSharpCode.ILSpyAddIn readonly ITextOutput output; public readonly List DebuggerMemberMappings = new List(); + public readonly Dictionary MemberLocations = new Dictionary(); public DebuggerTextOutput(ITextOutput output) { @@ -49,6 +52,9 @@ namespace ICSharpCode.ILSpyAddIn public void WriteDefinition(string text, object definition, bool isLocal) { + if (definition is MemberReference) { + MemberLocations[XmlDocKeyProvider.GetKey((MemberReference)definition)] = Location; + } output.WriteDefinition(text, definition, isLocal); } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs index 17204f1dee..2c8534419c 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs @@ -7,16 +7,17 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; - using ICSharpCode.AvalonEdit; using ICSharpCode.AvalonEdit.AddIn; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.AvalonEdit.Search; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.AvalonEdit; +using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.ILSpyAddIn.ViewContent { @@ -35,7 +36,7 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent /// /// Equivalent to AE.AddIn CodeEditor, but without editing capabilities. /// - class CodeView : Grid, IDisposable, ICodeEditor + class CodeView : Grid, IDisposable, ICodeEditor, IPositionable { public event EventHandler DocumentChanged; @@ -69,6 +70,8 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent this.adapter.TextEditor.MouseHover += TextEditorMouseHover; this.adapter.TextEditor.MouseHoverStopped += TextEditorMouseHoverStopped; this.adapter.TextEditor.MouseLeave += TextEditorMouseLeave; + + this.adapter.TextEditor.TextArea.DefaultInputHandler.NestedInputHandlers.Add(new SearchInputHandler(this.adapter.TextEditor.TextArea)); } #region Popup @@ -259,5 +262,22 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent { this.adapter.TextEditor.TextArea.TextView.Redraw(segment, priority); } + + public int Line { + get { + return this.adapter.Caret.Line; + } + } + + public int Column { + get { + return this.adapter.Caret.Column; + } + } + + public void JumpTo(int line, int column) + { + this.adapter.JumpTo(line, column); + } } } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs index 2456b3e1d5..9d4603e0ef 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -10,6 +11,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; using ICSharpCode.ILSpyAddIn.LaunchILSpy; using ICSharpCode.ILSpyAddIn.ViewContent; +using ICSharpCode.NRefactory; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Debugging; @@ -40,6 +42,7 @@ namespace ICSharpCode.ILSpyAddIn readonly CancellationTokenSource cancellation = new CancellationTokenSource(); MemberReference decompiledType; + Dictionary memberLocations; #region Constructor public DecompiledViewContent(string assemblyFile, string fullTypeName, string entityTag) @@ -125,7 +128,10 @@ namespace ICSharpCode.ILSpyAddIn this.jumpToEntityTagWhenDecompilationFinished = entityTag; return; } - // TODO: implement this + if (memberLocations != null) { + var location = memberLocations[entityTag]; + codeView.JumpTo(location.Line, location.Column); + } } #endregion @@ -134,7 +140,7 @@ namespace ICSharpCode.ILSpyAddIn { try { StringWriter writer = new StringWriter(); - RunDecompiler(assemblyFile, fullTypeName, new PlainTextOutput(writer), cancellation.Token); + RunDecompiler(assemblyFile, fullTypeName, new DebuggerTextOutput(new PlainTextOutput(writer)), cancellation.Token); if (!cancellation.IsCancellationRequested) { WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer); } @@ -155,7 +161,7 @@ namespace ICSharpCode.ILSpyAddIn } } - void RunDecompiler(string assemblyFile, string fullTypeName, ITextOutput textOutput, CancellationToken cancellationToken) + void RunDecompiler(string assemblyFile, string fullTypeName, DebuggerTextOutput textOutput, CancellationToken cancellationToken) { ReaderParameters readerParameters = new ReaderParameters(); // Use new assembly resolver instance so that the AssemblyDefinitions can be garbage-collected @@ -174,18 +180,7 @@ namespace ICSharpCode.ILSpyAddIn // save decompilation data decompiledType = typeDefinition; - - /* - int token = decompiledType.MetadataToken.ToInt32(); - var info = new DecompileInformation { - CodeMappings = astBuilder.CodeMappings, - LocalVariables = astBuilder.LocalVariables, - DecompiledMemberReferences = astBuilder.DecompiledMemberReferences - }; - - // save the data - DebuggerDecompilerService.DebugInformation.AddOrUpdate(token, info, (k, v) => info); - */ + memberLocations = textOutput.MemberLocations; } void OnDecompilationFinished(StringWriter output) diff --git a/src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 623da55c48..846edc60a2 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -121,6 +121,7 @@ + diff --git a/src/Libraries/ICSharpCode.Decompiler/XmlDocKeyProvider.cs b/src/Libraries/ICSharpCode.Decompiler/XmlDocKeyProvider.cs new file mode 100644 index 0000000000..ffc0336d04 --- /dev/null +++ b/src/Libraries/ICSharpCode.Decompiler/XmlDocKeyProvider.cs @@ -0,0 +1,244 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler +{ + /// + /// Provides XML documentation tags. + /// + public sealed class XmlDocKeyProvider + { + #region GetKey + public static string GetKey(MemberReference member) + { + StringBuilder b = new StringBuilder(); + if (member is TypeReference) { + b.Append("T:"); + AppendTypeName(b, (TypeReference)member); + } else { + if (member is FieldReference) + b.Append("F:"); + else if (member is PropertyDefinition) + b.Append("P:"); + else if (member is EventDefinition) + b.Append("E:"); + else if (member is MethodReference) + b.Append("M:"); + AppendTypeName(b, member.DeclaringType); + b.Append('.'); + b.Append(member.Name.Replace('.', '#')); + IList parameters; + TypeReference explicitReturnType = null; + if (member is PropertyDefinition) { + parameters = ((PropertyDefinition)member).Parameters; + } else if (member is MethodReference) { + MethodReference mr = (MethodReference)member; + if (mr.HasGenericParameters) { + b.Append("``"); + b.Append(mr.GenericParameters.Count); + } + parameters = mr.Parameters; + if (mr.Name == "op_Implicit" || mr.Name == "op_Explicit") { + explicitReturnType = mr.ReturnType; + } + } else { + parameters = null; + } + if (parameters != null && parameters.Count > 0) { + b.Append('('); + for (int i = 0; i < parameters.Count; i++) { + if (i > 0) b.Append(','); + AppendTypeName(b, parameters[i].ParameterType); + } + b.Append(')'); + } + if (explicitReturnType != null) { + b.Append('~'); + AppendTypeName(b, explicitReturnType); + } + } + return b.ToString(); + } + + static void AppendTypeName(StringBuilder b, TypeReference type) + { + if (type == null) { + // could happen when a TypeSpecification has no ElementType; e.g. function pointers in C++/CLI assemblies + return; + } + if (type is GenericInstanceType) { + GenericInstanceType giType = (GenericInstanceType)type; + AppendTypeNameWithArguments(b, giType.ElementType, giType.GenericArguments); + } else if (type is TypeSpecification) { + AppendTypeName(b, ((TypeSpecification)type).ElementType); + ArrayType arrayType = type as ArrayType; + if (arrayType != null) { + b.Append('['); + for (int i = 0; i < arrayType.Dimensions.Count; i++) { + if (i > 0) + b.Append(','); + ArrayDimension ad = arrayType.Dimensions[i]; + if (ad.IsSized) { + b.Append(ad.LowerBound); + b.Append(':'); + b.Append(ad.UpperBound); + } + } + b.Append(']'); + } + ByReferenceType refType = type as ByReferenceType; + if (refType != null) { + b.Append('@'); + } + PointerType ptrType = type as PointerType; + if (ptrType != null) { + b.Append('*'); + } + } else { + GenericParameter gp = type as GenericParameter; + if (gp != null) { + b.Append('`'); + if (gp.Owner.GenericParameterType == GenericParameterType.Method) { + b.Append('`'); + } + b.Append(gp.Position); + } else if (type.DeclaringType != null) { + AppendTypeName(b, type.DeclaringType); + b.Append('.'); + b.Append(type.Name); + } else { + b.Append(type.FullName); + } + } + } + + static int AppendTypeNameWithArguments(StringBuilder b, TypeReference type, IList genericArguments) + { + int outerTypeParameterCount = 0; + if (type.DeclaringType != null) { + TypeReference declType = type.DeclaringType; + outerTypeParameterCount = AppendTypeNameWithArguments(b, declType, genericArguments); + b.Append('.'); + } else if (!string.IsNullOrEmpty(type.Namespace)) { + b.Append(type.Namespace); + b.Append('.'); + } + int localTypeParameterCount = 0; + b.Append(NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out localTypeParameterCount)); + + if (localTypeParameterCount > 0) { + int totalTypeParameterCount = outerTypeParameterCount + localTypeParameterCount; + b.Append('{'); + for (int i = outerTypeParameterCount; i < totalTypeParameterCount && i < genericArguments.Count; i++) { + if (i > outerTypeParameterCount) b.Append(','); + AppendTypeName(b, genericArguments[i]); + } + b.Append('}'); + } + return outerTypeParameterCount + localTypeParameterCount; + } + #endregion + + #region FindMemberByKey + public static MemberReference FindMemberByKey(ModuleDefinition module, string key) + { + if (module == null) + throw new ArgumentNullException("module"); + if (key == null || key.Length < 2 || key[1] != ':') + return null; + switch (key[0]) { + case 'T': + return FindType(module, key.Substring(2)); + case 'F': + return FindMember(module, key, type => type.Fields); + case 'P': + return FindMember(module, key, type => type.Properties); + case 'E': + return FindMember(module, key, type => type.Events); + case 'M': + return FindMember(module, key, type => type.Methods); + default: + return null; + } + } + + static MemberReference FindMember(ModuleDefinition module, string key, Func> memberSelector) + { + Debug.WriteLine("Looking for member " + key); + int parenPos = key.IndexOf('('); + int dotPos; + if (parenPos > 0) { + dotPos = key.LastIndexOf('.', parenPos - 1, parenPos); + } else { + dotPos = key.LastIndexOf('.'); + } + if (dotPos < 0) return null; + TypeDefinition type = FindType(module, key.Substring(2, dotPos - 2)); + if (type == null) + return null; + string shortName; + if (parenPos > 0) { + shortName = key.Substring(dotPos + 1, parenPos - (dotPos + 1)); + } else { + shortName = key.Substring(dotPos + 1); + } + Debug.WriteLine("Searching in type {0} for {1}", type.FullName, shortName); + MemberReference shortNameMatch = null; + foreach (MemberReference member in memberSelector(type)) { + string memberKey = GetKey(member); + Debug.WriteLine(memberKey); + if (memberKey == key) + return member; + if (shortName == member.Name.Replace('.', '#')) + shortNameMatch = member; + } + // if there's no match by ID string (key), return the match by name. + return shortNameMatch; + } + + static TypeDefinition FindType(ModuleDefinition module, string name) + { + int pos = name.LastIndexOf('.'); + string ns; + if (pos >= 0) { + ns = name.Substring(0, pos); + name = name.Substring(pos + 1); + } else { + ns = string.Empty; + } + if (string.IsNullOrEmpty(name)) return null; + TypeDefinition type = module.GetType(ns, name); + if (type == null && ns.Length > 0) { + // try if this is a nested type + type = FindType(module, ns); + if (type != null) { + type = type.NestedTypes.FirstOrDefault(t => t.Name == name); + } + } + return type; + } + #endregion + } +}