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

7
ILSpy/MainWindow.xaml.cs

@ -276,6 +276,13 @@ namespace ICSharpCode.ILSpy @@ -276,6 +276,13 @@ namespace ICSharpCode.ILSpy
SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) {
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

103
ILSpy/TextView/DecompilerTextView.cs

@ -19,8 +19,10 @@ @@ -19,8 +19,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@ -30,12 +32,13 @@ using System.Windows.Media; @@ -30,12 +32,13 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Xml;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.NRefactory.Documentation;
using Microsoft.Win32;
using Mono.Cecil;
@ -69,11 +72,99 @@ namespace ICSharpCode.ILSpy.TextView @@ -69,11 +72,99 @@ namespace ICSharpCode.ILSpy.TextView
});
InitializeComponent();
this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference);
this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink);
textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator);
this.uiElementGenerator = new UIElementGenerator();
textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator);
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
@ -361,6 +452,14 @@ namespace ICSharpCode.ILSpy.TextView @@ -361,6 +452,14 @@ namespace ICSharpCode.ILSpy.TextView
}
mainWindow.JumpToReference(reference);
}
/// <summary>
/// Filters all ReferenceSegments that are no real links.
/// </summary>
bool IsLink(ReferenceSegment referenceSegment)
{
return true;
}
#endregion
#region SaveToDisk

9
ILSpy/TextView/ReferenceElementGenerator.cs

@ -29,17 +29,21 @@ namespace ICSharpCode.ILSpy.TextView @@ -29,17 +29,21 @@ namespace ICSharpCode.ILSpy.TextView
sealed class ReferenceElementGenerator : VisualLineElementGenerator
{
Action<ReferenceSegment> referenceClicked;
Predicate<ReferenceSegment> isLink;
/// <summary>
/// The collection of references (hyperlinks).
/// </summary>
public TextSegmentCollection<ReferenceSegment> References { get; set; }
public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked)
public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked, Predicate<ReferenceSegment> isLink)
{
if (referenceClicked == null)
throw new ArgumentNullException("referenceClicked");
if (isLink == null)
throw new ArgumentNullException("isLink");
this.referenceClicked = referenceClicked;
this.isLink = isLink;
}
public override int GetFirstInterestedOffset(int startOffset)
@ -56,6 +60,9 @@ namespace ICSharpCode.ILSpy.TextView @@ -56,6 +60,9 @@ namespace ICSharpCode.ILSpy.TextView
if (this.References == null)
return null;
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)
int endOffset = Math.Min(segment.EndOffset, CurrentContext.VisualLine.LastDocumentLine.EndOffset);
// don't create hyperlinks with length 0

Loading…
Cancel
Save