Browse Source

display IL instructions as links to MSDN and display a tooltip with information.

pull/70/head
Siegfried Pammer 15 years ago
parent
commit
e1e40816d1
  1. 2
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  2. 7
      ILSpy/MainWindow.xaml.cs
  3. 103
      ILSpy/TextView/DecompilerTextView.cs
  4. 9
      ILSpy/TextView/ReferenceElementGenerator.cs

2
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{ {
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": "); writer.Write(": ");
writer.Write(instruction.OpCode.Name); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if(null != instruction.Operand) { if(null != instruction.Operand) {
writer.Write(' '); writer.Write(' ');
WriteOperand(writer, instruction.Operand); WriteOperand(writer, instruction.Operand);

7
ILSpy/MainWindow.xaml.cs

@ -276,6 +276,13 @@ namespace ICSharpCode.ILSpy
SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve())); SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) { } else if (reference is AssemblyDefinition) {
SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference)); SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference));
} else if (reference is Mono.Cecil.Cil.OpCode) {
string link = "http://msdn.microsoft.com/library/system.reflection.emit.opcodes." + ((Mono.Cecil.Cil.OpCode)reference).Code.ToString().ToLowerInvariant() + ".aspx";
try {
Process.Start(link);
} catch {
}
} }
} }
#endregion #endregion

103
ILSpy/TextView/DecompilerTextView.cs

@ -19,8 +19,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@ -30,12 +32,13 @@ using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Threading; using System.Windows.Threading;
using System.Xml; using System.Xml;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Folding; using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd; using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.NRefactory.Documentation;
using Microsoft.Win32; using Microsoft.Win32;
using Mono.Cecil; using Mono.Cecil;
@ -69,11 +72,99 @@ namespace ICSharpCode.ILSpy.TextView
}); });
InitializeComponent(); InitializeComponent();
this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference); this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink);
textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator); textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator);
this.uiElementGenerator = new UIElementGenerator(); this.uiElementGenerator = new UIElementGenerator();
textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator); textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator);
textEditor.Options.RequireControlModifierForHyperlinkClick = false; textEditor.Options.RequireControlModifierForHyperlinkClick = false;
textEditor.TextArea.TextView.MouseHover += TextViewMouseHover;
textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped;
}
#endregion
#region Tooltip support
ToolTip tooltip;
void TextViewMouseHoverStopped(object sender, MouseEventArgs e)
{
if (tooltip != null)
tooltip.IsOpen = false;
}
void TextViewMouseHover(object sender, MouseEventArgs e)
{
TextViewPosition? position = textEditor.TextArea.TextView.GetPosition(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);
if (position == null)
return;
int offset = textEditor.Document.GetOffset(position.Value);
ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();
if (seg == null)
return;
object content = GenerateTooltip(seg);
if (tooltip != null)
tooltip.IsOpen = false;
if (content != null)
tooltip = new ToolTip() { Content = content, IsOpen = true };
}
object GenerateTooltip(ReferenceSegment segment)
{
if (segment.Reference is Mono.Cecil.Cil.OpCode) {
Mono.Cecil.Cil.OpCode code = (Mono.Cecil.Cil.OpCode)segment.Reference;
string encodedName = code.Code.ToString();
string opCodeHex = code.Size > 1 ? string.Format("0x{0:x2}{1:x2}", code.Op1, code.Op2) : string.Format("0x{0:x2}", code.Op2);
string documentationFile = FindDocumentation("mscorlib.xml");
string text = "";
if (documentationFile != null){
XmlDocumentationProvider provider = new XmlDocumentationProvider(documentationFile);
string documentation = provider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + encodedName);
if (documentation != null)
text = StripXml(documentation);
}
return string.Format("{0} ({1}): {2}", code.Name, opCodeHex, text);
}
return null;
}
string StripXml(string xml)
{
return Regex.Replace(xml, "</?.*>", "").Trim();
}
string FindDocumentation(string fileName)
{
string path = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
List<string> names = new List<string>();
EnumerateCultures(CultureInfo.CurrentCulture, names);
names.Add("en");
names.Add("en-US");
names.Add("en-GB");
foreach (string name in names) {
string location = Path.Combine(path, name, fileName);
if (File.Exists(location))
return location;
}
path = Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES(X86)") ?? Environment.GetEnvironmentVariable("PROGRAMFILES"), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0");
string loc = Path.Combine(path, fileName);
if (File.Exists(loc))
return loc;
return null;
}
void EnumerateCultures(CultureInfo info, List<string> names)
{
while (info != null) {
names.Add(info.Name);
info = info.Parent;
if (info == info.Parent)
return;
}
} }
#endregion #endregion
@ -361,6 +452,14 @@ namespace ICSharpCode.ILSpy.TextView
} }
mainWindow.JumpToReference(reference); mainWindow.JumpToReference(reference);
} }
/// <summary>
/// Filters all ReferenceSegments that are no real links.
/// </summary>
bool IsLink(ReferenceSegment referenceSegment)
{
return true;
}
#endregion #endregion
#region SaveToDisk #region SaveToDisk

9
ILSpy/TextView/ReferenceElementGenerator.cs

@ -29,17 +29,21 @@ namespace ICSharpCode.ILSpy.TextView
sealed class ReferenceElementGenerator : VisualLineElementGenerator sealed class ReferenceElementGenerator : VisualLineElementGenerator
{ {
Action<ReferenceSegment> referenceClicked; Action<ReferenceSegment> referenceClicked;
Predicate<ReferenceSegment> isLink;
/// <summary> /// <summary>
/// The collection of references (hyperlinks). /// The collection of references (hyperlinks).
/// </summary> /// </summary>
public TextSegmentCollection<ReferenceSegment> References { get; set; } public TextSegmentCollection<ReferenceSegment> References { get; set; }
public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked) public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked, Predicate<ReferenceSegment> isLink)
{ {
if (referenceClicked == null) if (referenceClicked == null)
throw new ArgumentNullException("referenceClicked"); throw new ArgumentNullException("referenceClicked");
if (isLink == null)
throw new ArgumentNullException("isLink");
this.referenceClicked = referenceClicked; this.referenceClicked = referenceClicked;
this.isLink = isLink;
} }
public override int GetFirstInterestedOffset(int startOffset) public override int GetFirstInterestedOffset(int startOffset)
@ -56,6 +60,9 @@ namespace ICSharpCode.ILSpy.TextView
if (this.References == null) if (this.References == null)
return null; return null;
foreach (var segment in this.References.FindSegmentsContaining(offset)) { foreach (var segment in this.References.FindSegmentsContaining(offset)) {
// skip all non-links
if (!isLink(segment))
continue;
// ensure that hyperlinks don't span several lines (VisualLineElements can't contain line breaks) // ensure that hyperlinks don't span several lines (VisualLineElements can't contain line breaks)
int endOffset = Math.Min(segment.EndOffset, CurrentContext.VisualLine.LastDocumentLine.EndOffset); int endOffset = Math.Min(segment.EndOffset, CurrentContext.VisualLine.LastDocumentLine.EndOffset);
// don't create hyperlinks with length 0 // don't create hyperlinks with length 0

Loading…
Cancel
Save