Browse Source

add icon margin & bookmarks

pull/219/head
Eusebiu Marcu 14 years ago
parent
commit
8c37175189
  1. 20
      ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  2. 2
      ILSpy.sln
  3. 123
      ILSpy/AvalonEdit/ITextMarker.cs
  4. 83
      ILSpy/AvalonEdit/IconBarManager.cs
  5. 239
      ILSpy/AvalonEdit/IconBarMargin.cs
  6. 67
      ILSpy/AvalonEdit/TextEditorWeakEventManager.cs
  7. 292
      ILSpy/AvalonEdit/TextMarkerService.cs
  8. 97
      ILSpy/Bookmarks/BookmarkBase.cs
  9. 28
      ILSpy/Bookmarks/BookmarkEventHandler.cs
  10. 116
      ILSpy/Bookmarks/BookmarkManager.cs
  11. 69
      ILSpy/Bookmarks/IBookmark.cs
  12. 21
      ILSpy/Bookmarks/MarkerBookmark.cs
  13. 162
      ILSpy/Bookmarks/MemberBookmark.cs
  14. 8
      ILSpy/CSharpLanguage.cs
  15. 24
      ILSpy/ILLanguage.cs
  16. 16
      ILSpy/ILSpy.csproj
  17. 74
      ILSpy/Language.cs
  18. 37
      ILSpy/TextView/DecompilerTextView.cs
  19. 18
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs

20
ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -123,12 +123,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -123,12 +123,11 @@ namespace ICSharpCode.Decompiler.Ast
public void StartNode(AstNode node)
{
// code mappings
var ranges = node.Annotation<List<ILRange>>();
if (ranges != null && ranges.Count > 0)
{
if (ranges != null && ranges.Count > 0) {
// find the ancestor that has method mapping as annotation
if (node.Ancestors != null && node.Ancestors.Count() > 0)
{
if (node.Ancestors != null && node.Ancestors.Count() > 0) {
var n = node.Ancestors.FirstOrDefault(a => a.Annotation<MemberMapping>() != null);
if (n != null) {
MemberMapping mapping = n.Annotation<MemberMapping>();
@ -145,6 +144,19 @@ namespace ICSharpCode.Decompiler.Ast @@ -145,6 +144,19 @@ namespace ICSharpCode.Decompiler.Ast
}
}
// definitions of types and their members
Predicate<AstNode> predicate = n => n is TypeDeclaration || n is DelegateDeclaration ||
n is FieldDeclaration || n is PropertyDeclaration || n is EventDeclaration ||n is MethodDeclaration || n is ConstructorDeclaration ||
n is IndexerDeclaration || n is OperatorDeclaration;
if (predicate(node)) {
var n = node as AttributedNode;
int c = 0;
if (n != null)
c = n.Attributes.Count;
node.AddAnnotation(Tuple.Create(output.CurrentLine + c, 0));
}
nodeStack.Push(node);
}

2
ILSpy.sln

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.0.1.7146
# SharpDevelop 4.1.0.7384-alpha
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}"
ProjectSection(SolutionItems) = postProject
doc\Command Line.txt = doc\Command Line.txt

123
ILSpy/AvalonEdit/ITextMarker.cs

@ -0,0 +1,123 @@ @@ -0,0 +1,123 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows.Media;
using ICSharpCode.ILSpy.Bookmarks;
namespace ICSharpCode.ILSpy.AvalonEdit
{
/// <summary>
/// Represents a text marker.
/// </summary>
public interface ITextMarker
{
/// <summary>
/// Gets the start offset of the marked text region.
/// </summary>
int StartOffset { get; }
/// <summary>
/// Gets the end offset of the marked text region.
/// </summary>
int EndOffset { get; }
/// <summary>
/// Gets the length of the marked region.
/// </summary>
int Length { get; }
/// <summary>
/// Deletes the text marker.
/// </summary>
void Delete();
/// <summary>
/// Gets whether the text marker was deleted.
/// </summary>
bool IsDeleted { get; }
/// <summary>
/// Event that occurs when the text marker is deleted.
/// </summary>
event EventHandler Deleted;
/// <summary>
/// Gets/Sets the background color.
/// </summary>
Color? BackgroundColor { get; set; }
/// <summary>
/// Gets/Sets the foreground color.
/// </summary>
Color? ForegroundColor { get; set; }
/// <summary>
/// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers.
/// </summary>
TextMarkerType MarkerType { get; set; }
/// <summary>
/// Gets/Sets the color of the marker.
/// </summary>
Color MarkerColor { get; set; }
/// <summary>
/// Gets/Sets an object with additional data for this text marker.
/// </summary>
object Tag { get; set; }
/// <summary>
/// Gets/Sets an object that will be displayed as tooltip in the text editor.
/// </summary>
object ToolTip { get; set; }
/// <summary>
/// Gets or sets if the marker is visible or not.
/// </summary>
Predicate<object> IsVisible { get; set; }
/// <summary>
/// Gets or sets the bookmark.
/// </summary>
IBookmark Bookmark { get; set; }
}
public enum TextMarkerType
{
/// <summary>
/// Use no marker
/// </summary>
None,
/// <summary>
/// Use squiggly underline marker
/// </summary>
SquigglyUnderline
}
public interface ITextMarkerService
{
/// <summary>
/// Creates a new text marker. The text marker will be invisible at first,
/// you need to set one of the Color properties to make it visible.
/// </summary>
ITextMarker Create(int startOffset, int length);
/// <summary>
/// Gets the list of text markers.
/// </summary>
IEnumerable<ITextMarker> TextMarkers { get; }
/// <summary>
/// Removes the specified text marker.
/// </summary>
void Remove(ITextMarker marker);
/// <summary>
/// Removes all text markers that match the condition.
/// </summary>
void RemoveAll(Predicate<ITextMarker> predicate);
}
}

83
ILSpy/AvalonEdit/IconBarManager.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// 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.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.ILSpy.Bookmarks;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.ILSpy.AvalonEdit
{
/// <summary>
/// Stores the entries in the icon bar margin. Multiple icon bar margins
/// can use the same manager if split view is used.
/// </summary>
public class IconBarManager : IBookmarkMargin
{
ObservableCollection<IBookmark> bookmarks = new ObservableCollection<IBookmark>();
public IconBarManager()
{
bookmarks.CollectionChanged += bookmarks_CollectionChanged;
}
public IList<IBookmark> Bookmarks {
get { return bookmarks; }
}
void bookmarks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Redraw();
}
public void Redraw()
{
if (RedrawRequested != null)
RedrawRequested(this, EventArgs.Empty);
}
public event EventHandler RedrawRequested;
internal void UpdateClassMemberBookmarks(IEnumerable<AstNode> nodes)
{
this.bookmarks.Clear();
if (nodes == null || nodes.Count() == 0)
return;
foreach (var n in nodes) {
switch (n.NodeType) {
case NodeType.TypeDeclaration:
case NodeType.TypeReference:
this.bookmarks.Add(new TypeBookmark(n));
break;
case NodeType.Member:
this.bookmarks.Add(new MemberBookmark(n));
break;
default:
// do nothing
break;
}
}
}
}
}

239
ILSpy/AvalonEdit/IconBarMargin.cs

@ -0,0 +1,239 @@ @@ -0,0 +1,239 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Bookmarks;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AvalonEdit
{
public class IconBarMargin : AbstractMargin, IDisposable
{
readonly IconBarManager manager;
public IconBarMargin(IconBarManager manager)
{
BookmarkManager.Added += delegate { InvalidateVisual(); };
BookmarkManager.Removed += delegate { InvalidateVisual(); };
this.manager = manager;
}
public IList<MemberReference> DecompiledMembers { get; set; }
public virtual void Dispose()
{
this.TextView = null; // detach from TextView (will also detach from manager)
}
/// <inheritdoc/>
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
// accept clicks even when clicking on the background
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
return new Size(18, 0);
}
protected override void OnRender(DrawingContext drawingContext)
{
Size renderSize = this.RenderSize;
drawingContext.DrawRectangle(SystemColors.ControlBrush, null,
new Rect(0, 0, renderSize.Width, renderSize.Height));
drawingContext.DrawLine(new Pen(SystemColors.ControlDarkBrush, 1),
new Point(renderSize.Width - 0.5, 0),
new Point(renderSize.Width - 0.5, renderSize.Height));
ICSharpCode.AvalonEdit.Rendering.TextView textView = this.TextView;
if (textView != null && textView.VisualLinesValid) {
// create a dictionary line number => first bookmark
Dictionary<int, IBookmark> bookmarkDict = new Dictionary<int, IBookmark>();
foreach (var bm in BookmarkManager.Bookmarks) {
if (!DecompiledMembers.Contains(bm.MemberReference))
continue;
int line = bm.LineNumber;
IBookmark existingBookmark;
if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder)
bookmarkDict[line] = bm;
}
foreach (var bm in manager.Bookmarks) {
int line = bm.LineNumber;
IBookmark existingBookmark;
if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder)
bookmarkDict[line] = bm;
}
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
foreach (VisualLine line in textView.VisualLines) {
int lineNumber = line.FirstDocumentLine.LineNumber;
IBookmark bm;
if (bookmarkDict.TryGetValue(lineNumber, out bm)) {
Rect rect = new Rect(0, PixelSnapHelpers.Round(line.VisualTop - textView.VerticalOffset, pixelSize.Height), 16, 16);
if (dragDropBookmark == bm && dragStarted)
drawingContext.PushOpacity(0.5);
drawingContext.DrawImage(bm.Image, rect);
if (dragDropBookmark == bm && dragStarted)
drawingContext.Pop();
}
}
if (dragDropBookmark != null && dragStarted) {
Rect rect = new Rect(0, PixelSnapHelpers.Round(dragDropCurrentPoint - 8, pixelSize.Height), 16, 16);
drawingContext.DrawImage(dragDropBookmark.Image, rect);
}
}
}
IBookmark dragDropBookmark; // bookmark being dragged (!=null if drag'n'drop is active)
double dragDropStartPoint;
double dragDropCurrentPoint;
bool dragStarted; // whether drag'n'drop operation has started (mouse was moved minimum distance)
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
int line = GetLineFromMousePosition(e);
if (!e.Handled && line > 0) {
IBookmark bm = GetBookmarkFromLine(line);
if (bm != null) {
bm.MouseDown(e);
if (!e.Handled) {
if (e.ChangedButton == MouseButton.Left && bm.CanDragDrop && CaptureMouse()) {
StartDragDrop(bm, e);
e.Handled = true;
}
}
}
}
// don't allow selecting text through the IconBarMargin
if (e.ChangedButton == MouseButton.Left)
e.Handled = true;
}
IBookmark GetBookmarkFromLine(int line)
{
BookmarkBase result = null;
foreach (BookmarkBase bm in BookmarkManager.Bookmarks) {
if (bm.LineNumber == line &&
this.DecompiledMembers != null && this.DecompiledMembers.Contains(bm.MemberReference)) {
if (result == null || bm.ZOrder > result.ZOrder)
return result;
}
}
return manager.Bookmarks.FirstOrDefault(b => b.LineNumber == line);
}
protected override void OnLostMouseCapture(MouseEventArgs e)
{
CancelDragDrop();
base.OnLostMouseCapture(e);
}
void StartDragDrop(IBookmark bm, MouseEventArgs e)
{
dragDropBookmark = bm;
dragDropStartPoint = dragDropCurrentPoint = e.GetPosition(this).Y;
if (TextView != null) {
TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea;
if (area != null)
area.PreviewKeyDown += TextArea_PreviewKeyDown;
}
}
void CancelDragDrop()
{
if (dragDropBookmark != null) {
dragDropBookmark = null;
dragStarted = false;
if (TextView != null) {
TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea;
if (area != null)
area.PreviewKeyDown -= TextArea_PreviewKeyDown;
}
ReleaseMouseCapture();
InvalidateVisual();
}
}
void TextArea_PreviewKeyDown(object sender, KeyEventArgs e)
{
// any key press cancels drag'n'drop
CancelDragDrop();
if (e.Key == Key.Escape)
e.Handled = true;
}
int GetLineFromMousePosition(MouseEventArgs e)
{
ICSharpCode.AvalonEdit.Rendering.TextView textView = this.TextView;
if (textView == null)
return 0;
VisualLine vl = textView.GetVisualLineFromVisualTop(e.GetPosition(textView).Y + textView.ScrollOffset.Y);
if (vl == null)
return 0;
return vl.FirstDocumentLine.LineNumber;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (dragDropBookmark != null) {
dragDropCurrentPoint = e.GetPosition(this).Y;
if (Math.Abs(dragDropCurrentPoint - dragDropStartPoint) > SystemParameters.MinimumVerticalDragDistance)
dragStarted = true;
InvalidateVisual();
}
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
int line = GetLineFromMousePosition(e);
if (!e.Handled && dragDropBookmark != null) {
if (dragStarted) {
if (line != 0)
dragDropBookmark.Drop(line);
e.Handled = true;
}
CancelDragDrop();
}
if (!e.Handled && line != 0) {
var bm = GetBookmarkFromLine(line);
if (bm != null) {
bm.MouseUp(e);
if (bm is BookmarkBase) {
if ((bm as BookmarkBase).CanToggle) {
BookmarkManager.RemoveMark(bm as BookmarkBase);
InvalidateVisual();
}
}
if (e.Handled)
return;
}
if (e.ChangedButton == MouseButton.Left) {
// TODO: notify subscribers
}
InvalidateVisual();
}
}
}
}

67
ILSpy/AvalonEdit/TextEditorWeakEventManager.cs

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
// 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.Windows;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.ILSpy.AvalonEdit
{
public static class TextEditorWeakEventManager
{
public sealed class MouseHover : WeakEventManagerBase<MouseHover, TextEditor>
{
protected override void StopListening(TextEditor source)
{
source.MouseHover -= DeliverEvent;
}
protected override void StartListening(TextEditor source)
{
source.MouseHover += DeliverEvent;
}
}
public sealed class MouseHoverStopped : WeakEventManagerBase<MouseHoverStopped, TextEditor>
{
protected override void StopListening(TextEditor source)
{
source.MouseHoverStopped -= DeliverEvent;
}
protected override void StartListening(TextEditor source)
{
source.MouseHoverStopped += DeliverEvent;
}
}
public sealed class MouseDown : WeakEventManagerBase<MouseDown, TextEditor>
{
protected override void StopListening(TextEditor source)
{
source.MouseDown -= DeliverEvent;
}
protected override void StartListening(TextEditor source)
{
source.MouseDown += DeliverEvent;
}
}
}
}

292
ILSpy/AvalonEdit/TextMarkerService.cs

@ -0,0 +1,292 @@ @@ -0,0 +1,292 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.ILSpy.Bookmarks;
namespace ICSharpCode.ILSpy.AvalonEdit
{
/// <summary>
/// Handles the text markers for a code editor.
/// </summary>
public sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
{
TextEditor codeEditor;
TextSegmentCollection<TextMarker> markers = new TextSegmentCollection<TextMarker>();
public TextMarkerService()
{
}
public TextEditor CodeEditor {
get { return codeEditor; }
set { codeEditor = value; }
}
#region ITextMarkerService
public ITextMarker Create(int startOffset, int length)
{
int textLength = codeEditor.TextArea.TextView.Document.TextLength;
if (startOffset < 0 || startOffset > textLength)
throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be between 0 and " + textLength);
if (length < 0 || startOffset + length > textLength)
throw new ArgumentOutOfRangeException("length", length, "length must not be negative and startOffset+length must not be after the end of the document");
TextMarker m = new TextMarker(this, startOffset, length);
markers.Add(m);
// no need to mark segment for redraw: the text marker is invisible until a property is set
return m;
}
public IEnumerable<ITextMarker> GetMarkersAtOffset(int offset)
{
return markers.FindSegmentsContaining(offset);
}
public IEnumerable<ITextMarker> TextMarkers {
get { return markers; }
}
public void RemoveAll(Predicate<ITextMarker> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (TextMarker m in markers.ToArray()) {
if (predicate(m))
Remove(m);
}
}
public void Remove(ITextMarker marker)
{
if (marker == null)
return;
TextMarker m = marker as TextMarker;
if (markers.Remove(m)) {
Redraw(m);
m.OnDeleted();
}
}
/// <summary>
/// Redraws the specified text segment.
/// </summary>
public void Redraw(ISegment segment)
{
codeEditor.TextArea.TextView.Redraw(segment, DispatcherPriority.Normal);
}
#endregion
#region DocumentColorizingTransformer
protected override void ColorizeLine(DocumentLine line)
{
if (markers == null)
return;
int lineStart = line.Offset;
int lineEnd = lineStart + line.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) {
if (!marker.IsVisible(marker.Bookmark))
continue;
Brush foregroundBrush = null;
if (marker.ForegroundColor != null) {
foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value);
foregroundBrush.Freeze();
}
ChangeLinePart(
Math.Max(marker.StartOffset, lineStart),
Math.Min(marker.EndOffset, lineEnd),
element => {
if (foregroundBrush != null) {
element.TextRunProperties.SetForegroundBrush(foregroundBrush);
}
}
);
}
}
#endregion
#region IBackgroundRenderer
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext)
{
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (markers == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) {
if (!marker.IsVisible(marker.Bookmark))
continue;
if (marker.BackgroundColor != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.CornerRadius = 3;
geoBuilder.AddSegment(textView, marker);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
Color color = marker.BackgroundColor.Value;
SolidColorBrush brush = new SolidColorBrush(color);
brush.Freeze();
drawingContext.DrawGeometry(brush, null, geometry);
}
}
if (marker.MarkerType != TextMarkerType.None) {
foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) {
Point startPoint = r.BottomLeft;
Point endPoint = r.BottomRight;
Pen usedPen = new Pen(new SolidColorBrush(marker.MarkerColor), 1);
usedPen.Freeze();
switch (marker.MarkerType) {
case TextMarkerType.SquigglyUnderline:
double offset = 2.5;
int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4);
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open()) {
ctx.BeginFigure(startPoint, false, false);
ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false);
}
geometry.Freeze();
drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry);
break;
}
}
}
}
}
IEnumerable<Point> CreatePoints(Point start, Point end, double offset, int count)
{
for (int i = 0; i < count; i++)
yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0));
}
#endregion
}
sealed class TextMarker : TextSegment, ITextMarker
{
readonly TextMarkerService service;
public TextMarker(TextMarkerService service, int startOffset, int length)
{
if (service == null)
throw new ArgumentNullException("service");
this.service = service;
this.StartOffset = startOffset;
this.Length = length;
this.markerType = TextMarkerType.None;
}
public event EventHandler Deleted;
public bool IsDeleted {
get { return !this.IsConnectedToCollection; }
}
public void Delete()
{
service.Remove(this);
}
internal void OnDeleted()
{
if (Deleted != null)
Deleted(this, EventArgs.Empty);
}
void Redraw()
{
service.Redraw(this);
}
Color? backgroundColor;
public Color? BackgroundColor {
get { return backgroundColor; }
set {
if (backgroundColor != value) {
backgroundColor = value;
Redraw();
}
}
}
Color? foregroundColor;
public Color? ForegroundColor {
get { return foregroundColor; }
set {
if (foregroundColor != value) {
foregroundColor = value;
Redraw();
}
}
}
public object Tag { get; set; }
TextMarkerType markerType;
public TextMarkerType MarkerType {
get { return markerType; }
set {
if (markerType != value) {
markerType = value;
Redraw();
}
}
}
Color markerColor;
public Color MarkerColor {
get { return markerColor; }
set {
if (markerColor != value) {
markerColor = value;
Redraw();
}
}
}
/// <inheritdoc/>
public object ToolTip { get; set; }
/// <inheritdoc/>
public Predicate<object> IsVisible { get; set; }
/// <inheritdoc/>
public IBookmark Bookmark { get; set; }
}
}

97
ILSpy/Bookmarks/BookmarkBase.cs

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.Bookmarks
{
/// <summary>
/// A bookmark that can be attached to an AvalonEdit TextDocument.
/// </summary>
public class BookmarkBase : IBookmark
{
AstLocation location;
protected virtual void RemoveMark()
{
}
public AstLocation Location {
get { return location; }
set { location = value; }
}
public event EventHandler DocumentChanged;
protected virtual void OnDocumentChanged(EventArgs e)
{
if (DocumentChanged != null) {
DocumentChanged(this, e);
}
}
protected virtual void Redraw()
{
}
public MemberReference MemberReference { get; set; }
public int LineNumber {
get { return location.Line; }
}
public int ColumnNumber {
get { return location.Column; }
}
public virtual int ZOrder {
get { return 0; }
}
/// <summary>
/// Gets if the bookmark can be toggled off using the 'set/unset bookmark' command.
/// </summary>
public virtual bool CanToggle {
get {
return true;
}
}
public BookmarkBase(MemberReference member, AstLocation location)
{
this.MemberReference = member;
this.Location = location;
}
public virtual ImageSource Image {
get { return null; }
}
public virtual void MouseDown(MouseButtonEventArgs e)
{
}
public virtual void MouseUp(MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left && CanToggle) {
RemoveMark();
e.Handled = true;
}
}
public virtual bool CanDragDrop {
get { return false; }
}
public virtual void Drop(int lineNumber)
{
}
}
}

28
ILSpy/Bookmarks/BookmarkEventHandler.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.ILSpy.Bookmarks
{
public delegate void BookmarkEventHandler(object sender, BookmarkEventArgs e);
/// <summary>
/// Description of BookmarkEventHandler.
/// </summary>
public class BookmarkEventArgs : EventArgs
{
BookmarkBase bookmark;
public BookmarkBase Bookmark {
get {
return bookmark;
}
}
public BookmarkEventArgs(BookmarkBase bookmark)
{
this.bookmark = bookmark;
}
}
}

116
ILSpy/Bookmarks/BookmarkManager.cs

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
using Mono.CSharp;
namespace ICSharpCode.ILSpy.Bookmarks
{
/// <summary>
/// Static class that maintains the list of bookmarks and breakpoints.
/// </summary>
public static partial class BookmarkManager
{
static List<BookmarkBase> bookmarks = new List<BookmarkBase>();
public static List<BookmarkBase> Bookmarks {
get {
return bookmarks;
}
}
public static List<BookmarkBase> GetBookmarks(string typeName)
{
if (typeName == null)
throw new ArgumentNullException("typeName");
List<BookmarkBase> marks = new List<BookmarkBase>();
foreach (BookmarkBase mark in bookmarks) {
if (typeName == mark.MemberReference.FullName) {
marks.Add(mark);
}
}
return marks;
}
public static void AddMark(BookmarkBase bookmark)
{
if (bookmark == null) return;
if (bookmarks.Contains(bookmark)) return;
if (bookmarks.Exists(b => IsEqualBookmark(b, bookmark))) return;
bookmarks.Add(bookmark);
OnAdded(new BookmarkEventArgs(bookmark));
}
static bool IsEqualBookmark(BookmarkBase a, BookmarkBase b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
if (a.GetType() != b.GetType())
return false;
if (a.MemberReference.FullName != b.MemberReference.FullName)
return false;
return a.LineNumber == b.LineNumber;
}
public static void RemoveMark(BookmarkBase bookmark)
{
bookmarks.Remove(bookmark);
OnRemoved(new BookmarkEventArgs(bookmark));
}
public static void Clear()
{
while (bookmarks.Count > 0) {
var b = bookmarks[bookmarks.Count - 1];
bookmarks.RemoveAt(bookmarks.Count - 1);
OnRemoved(new BookmarkEventArgs(b));
}
}
internal static void Initialize()
{
}
static void OnRemoved(BookmarkEventArgs e)
{
if (Removed != null) {
Removed(null, e);
}
}
static void OnAdded(BookmarkEventArgs e)
{
if (Added != null) {
Added(null, e);
}
}
public static void ToggleBookmark(string typeName, int line,
Predicate<BookmarkBase> canToggle,
Func<AstLocation, BookmarkBase> bookmarkFactory)
{
foreach (BookmarkBase bookmark in GetBookmarks(typeName)) {
if (canToggle(bookmark) && bookmark.LineNumber == line) {
BookmarkManager.RemoveMark(bookmark);
return;
}
}
// no bookmark at that line: create a new bookmark
BookmarkManager.AddMark(bookmarkFactory(new AstLocation(line, 0)));
}
public static event BookmarkEventHandler Removed;
public static event BookmarkEventHandler Added;
}
}

69
ILSpy/Bookmarks/IBookmark.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Input;
using System.Windows.Media;
namespace ICSharpCode.ILSpy.Bookmarks
{
/// <summary>
/// The bookmark margin.
/// </summary>
public interface IBookmarkMargin
{
/// <summary>
/// Gets the list of bookmarks.
/// </summary>
IList<IBookmark> Bookmarks { get; }
/// <summary>
/// Redraws the bookmark margin. Bookmarks need to call this method when the Image changes.
/// </summary>
void Redraw();
}
/// <summary>
/// Represents a bookmark in the bookmark margin.
/// </summary>
public interface IBookmark
{
/// <summary>
/// Gets the line number of the bookmark.
/// </summary>
int LineNumber { get; }
/// <summary>
/// Gets the image.
/// </summary>
ImageSource Image { get; }
/// <summary>
/// Gets the Z-Order of the bookmark icon.
/// </summary>
int ZOrder { get; }
/// <summary>
/// Handles the mouse down event.
/// </summary>
void MouseDown(MouseButtonEventArgs e);
/// <summary>
/// Handles the mouse up event.
/// </summary>
void MouseUp(MouseButtonEventArgs e);
/// <summary>
/// Gets whether this bookmark can be dragged around.
/// </summary>
bool CanDragDrop { get; }
/// <summary>
/// Notifies the bookmark that it was dropped on the specified line.
/// </summary>
void Drop(int lineNumber);
}
}

21
ILSpy/Bookmarks/MarkerBookmark.cs

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.ILSpy.AvalonEdit;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.Bookmarks
{
public abstract class MarkerBookmark : BookmarkBase
{
public MarkerBookmark(MemberReference member, AstLocation location) : base(member, location)
{
}
public ITextMarker Marker { get; set; }
public abstract ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length);
}
}

162
ILSpy/Bookmarks/MemberBookmark.cs

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.Bookmarks
{
/// <summary>
/// Bookmark used to give additional operations for class members.
/// Does not derive from SDBookmark because it is not stored in the central BookmarkManager,
/// but only in the document's BookmarkManager.
/// </summary>
public class MemberBookmark : IBookmark
{
AstNode node;
public AstNode Node {
get {
return node;
}
}
public MemberBookmark(AstNode node)
{
this.node = node;
}
public virtual ImageSource Image {
get {
var attrNode = (AttributedNode)node;
if (node is FieldDeclaration)
return GetMemberOverlayedImage(attrNode, MemberIcon.Field);
if (node is PropertyDeclaration)
return GetMemberOverlayedImage(attrNode, MemberIcon.Property);
if (node is EventDeclaration)
return GetMemberOverlayedImage(attrNode, MemberIcon.Event);
if (node is IndexerDeclaration)
return GetMemberOverlayedImage(attrNode, MemberIcon.Indexer);
if (node is OperatorDeclaration)
return GetMemberOverlayedImage(attrNode, MemberIcon.Operator);
return GetMemberOverlayedImage(attrNode, MemberIcon.Method);
}
}
ImageSource GetMemberOverlayedImage(AttributedNode attrNode, MemberIcon icon)
{
switch (attrNode.Modifiers & Modifiers.VisibilityMask) {
case Modifiers.Protected:
return Images.GetIcon(icon, AccessOverlayIcon.Protected, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static);
case Modifiers.Private:
return Images.GetIcon(icon, AccessOverlayIcon.Private, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static);
case Modifiers.Internal:
return Images.GetIcon(icon, AccessOverlayIcon.Internal, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static);
}
return Images.GetIcon(icon, AccessOverlayIcon.Public, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static);
}
public int LineNumber {
get {
var t = node.Annotation<Tuple<int, int>>();
if (t != null)
return t.Item1;
return 0;
}
}
public virtual void MouseDown(MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left) {
// TODO: menu items
}
}
public virtual void MouseUp(MouseButtonEventArgs e)
{
}
int IBookmark.ZOrder {
get { return -10; }
}
bool IBookmark.CanDragDrop {
get { return false; }
}
void IBookmark.Drop(int lineNumber)
{
throw new NotSupportedException();
}
}
public class TypeBookmark : MemberBookmark
{
public TypeBookmark(AstNode node) : base (node)
{
}
public override ImageSource Image {
get {
var attrNode = (AttributedNode)Node;
if (Node is DelegateDeclaration)
return GetTypeOverlayedImage(attrNode, TypeIcon.Delegate);
if (Node is TypeDeclaration) {
var n = Node as TypeDeclaration;
switch (n.ClassType)
{
case ClassType.Delegate:
return GetTypeOverlayedImage(attrNode, TypeIcon.Delegate);
case ClassType.Enum:
return GetTypeOverlayedImage(attrNode, TypeIcon.Enum);
case ClassType.Struct:
return GetTypeOverlayedImage(attrNode, TypeIcon.Struct);
case ClassType.Interface:
return GetTypeOverlayedImage(attrNode, TypeIcon.Interface);
}
}
return GetTypeOverlayedImage(attrNode, TypeIcon.Class);
}
}
public override void MouseDown(MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left) {
// TODO: menu items
}
}
public override void MouseUp(MouseButtonEventArgs e)
{
}
ImageSource GetTypeOverlayedImage(AttributedNode attrNode, TypeIcon icon)
{
switch (attrNode.Modifiers & Modifiers.VisibilityMask) {
case Modifiers.Protected:
return Images.GetIcon(icon, AccessOverlayIcon.Protected);
case Modifiers.Private:
return Images.GetIcon(icon, AccessOverlayIcon.Private);
case Modifiers.Internal:
return Images.GetIcon(icon, AccessOverlayIcon.Internal);
}
return Images.GetIcon(icon, AccessOverlayIcon.Public);
}
}
}

8
ILSpy/CSharpLanguage.cs

@ -23,11 +23,11 @@ using System.ComponentModel.Composition; @@ -23,11 +23,11 @@ using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Resources;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xaml;
using System.Xml;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Ast.Transforms;
@ -91,6 +91,7 @@ namespace ICSharpCode.ILSpy @@ -91,6 +91,7 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType, isSingleMember: true);
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
@ -99,6 +100,7 @@ namespace ICSharpCode.ILSpy @@ -99,6 +100,7 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType, isSingleMember: true);
codeDomBuilder.AddProperty(property);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
}
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
@ -107,6 +109,7 @@ namespace ICSharpCode.ILSpy @@ -107,6 +109,7 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType, isSingleMember: true);
codeDomBuilder.AddField(field);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
@ -115,6 +118,7 @@ namespace ICSharpCode.ILSpy @@ -115,6 +118,7 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType, isSingleMember: true);
codeDomBuilder.AddEvent(ev);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
@ -122,6 +126,7 @@ namespace ICSharpCode.ILSpy @@ -122,6 +126,7 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type);
codeDomBuilder.AddType(type);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
}
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options)
@ -149,6 +154,7 @@ namespace ICSharpCode.ILSpy @@ -149,6 +154,7 @@ namespace ICSharpCode.ILSpy
codeDomBuilder.GenerateCode(output);
}
}
OnDecompilationFinished(null);
}
#region WriteProjectFile

24
ILSpy/ILLanguage.cs

@ -18,8 +18,6 @@ @@ -18,8 +18,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil;
@ -52,32 +50,43 @@ namespace ICSharpCode.ILSpy @@ -52,32 +50,43 @@ namespace ICSharpCode.ILSpy
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleMethod(method);
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleMethod(method);
NotifyDecompilationFinished(dis);
}
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleField(field);
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleField(field);
NotifyDecompilationFinished(dis);
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleProperty(property);
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleProperty(property);
NotifyDecompilationFinished(dis);
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleEvent(ev);
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleEvent(ev);
NotifyDecompilationFinished(dis);
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleType(type);
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleType(type);
NotifyDecompilationFinished(dis);
}
public override void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleNamespace(nameSpace, types);
OnDecompilationFinished(null);
}
public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
@ -86,6 +95,7 @@ namespace ICSharpCode.ILSpy @@ -86,6 +95,7 @@ namespace ICSharpCode.ILSpy
output.WriteLine();
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).WriteAssemblyHeader(assembly.AssemblyDefinition);
OnDecompilationFinished(null);
}
public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider)

16
ILSpy/ILSpy.csproj

@ -93,7 +93,18 @@ @@ -93,7 +93,18 @@
</Compile>
<Compile Include="AssemblyList.cs" />
<Compile Include="AssemblyListManager.cs" />
<Compile Include="AvalonEdit\IconBarManager.cs" />
<Compile Include="AvalonEdit\IconBarMargin.cs" />
<Compile Include="AvalonEdit\TextMarkerService.cs" />
<Compile Include="AvalonEdit\TextEditorWeakEventManager.cs" />
<Compile Include="AvalonEdit\ITextMarker.cs" />
<Compile Include="BamlDecompiler.cs" />
<Compile Include="Bookmarks\BookmarkBase.cs" />
<Compile Include="Bookmarks\BookmarkEventHandler.cs" />
<Compile Include="Bookmarks\BookmarkManager.cs" />
<Compile Include="Bookmarks\MemberBookmark.cs" />
<Compile Include="Bookmarks\IBookmark.cs" />
<Compile Include="Bookmarks\MarkerBookmark.cs" />
<Compile Include="CommandLineArguments.cs" />
<Compile Include="Commands.cs" />
<Compile Include="ConnectMethodDecompiler.cs" />
@ -304,6 +315,9 @@ @@ -304,6 +315,9 @@
<Name>ICSharpCode.TreeView</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="AvalonEdit" />
<Folder Include="Bookmarks" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

74
ILSpy/Language.cs

@ -17,20 +17,57 @@ @@ -17,20 +17,57 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Decompilation event arguments.
/// </summary>
public sealed class DecompileEventArgs : EventArgs
{
/// <summary>
/// Gets ot sets the code mappings
/// </summary>
public Dictionary<int, List<MemberMapping>> CodeMappings { get; set; }
/// <summary>
/// Gets or sets the local variables.
/// </summary>
public ConcurrentDictionary<int, IEnumerable<ILVariable>> LocalVariables { get; set; }
/// <summary>
/// Gets the list of MembeReferences that are decompiled (TypeDefinitions, MethodDefinitions, etc)
/// </summary>
public Dictionary<int, MemberReference> DecompiledMemberReferences { get; set; }
/// <summary>
/// Gets (or internal sets) the AST nodes.
/// </summary>
public IEnumerable<AstNode> AstNodes { get; internal set; }
}
/// <summary>
/// Base class for language-specific decompiler implementations.
/// </summary>
public abstract class Language
{
/// <summary>
/// Decompile finished event.
/// </summary>
public event EventHandler<DecompileEventArgs> DecompileFinished;
/// <summary>
/// Gets the name of the language (as shown in the UI)
/// </summary>
@ -82,6 +119,7 @@ namespace ICSharpCode.ILSpy @@ -82,6 +119,7 @@ namespace ICSharpCode.ILSpy
public virtual void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, nameSpace);
OnDecompilationFinished(null);
}
public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
@ -137,6 +175,40 @@ namespace ICSharpCode.ILSpy @@ -137,6 +175,40 @@ namespace ICSharpCode.ILSpy
{
return true;
}
protected virtual void OnDecompilationFinished(DecompileEventArgs e)
{
if (DecompileFinished != null) {
DecompileFinished(this, e);
}
}
protected void NotifyDecompilationFinished(BaseCodeMappings b)
{
if (b is AstBuilder) {
var builder = b as AstBuilder;
OnDecompilationFinished(new DecompileEventArgs {
CodeMappings = builder.CodeMappings,
LocalVariables = builder.LocalVariables,
DecompiledMemberReferences = builder.DecompiledMemberReferences,
AstNodes = builder.CompilationUnit.GetNodesWithLineNumbers(n =>
n is TypeDeclaration || n is DelegateDeclaration ||
n is FieldDeclaration || n is PropertyDeclaration ||
n is EventDeclaration || n is MethodDeclaration ||
n is ConstructorDeclaration ||
n is IndexerDeclaration || n is OperatorDeclaration)
});
}
if (b is ReflectionDisassembler) {
var dis = b as ReflectionDisassembler;
OnDecompilationFinished(new DecompileEventArgs {
CodeMappings = dis.CodeMappings,
DecompiledMemberReferences = dis.DecompiledMemberReferences,
AstNodes = null // TODO: how can I find the AST nodes?
});
}
}
}
public static class Languages
@ -174,4 +246,4 @@ namespace ICSharpCode.ILSpy @@ -174,4 +246,4 @@ namespace ICSharpCode.ILSpy
return AllLanguages.FirstOrDefault(l => l.Name == name) ?? AllLanguages.First();
}
}
}
}

37
ILSpy/TextView/DecompilerTextView.cs

@ -34,12 +34,14 @@ using System.Windows.Media; @@ -34,12 +34,14 @@ 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.AvalonEdit.Rendering;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.AvalonEdit;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.XmlDoc;
using ICSharpCode.NRefactory.Documentation;
@ -64,6 +66,10 @@ namespace ICSharpCode.ILSpy.TextView @@ -64,6 +66,10 @@ namespace ICSharpCode.ILSpy.TextView
DefinitionLookup definitionLookup;
CancellationTokenSource currentCancellationTokenSource;
readonly IconBarManager manager;
readonly IconBarMargin iconMargin;
readonly TextMarkerService textMarkerService;
#region Constructor
public DecompilerTextView()
{
@ -85,6 +91,16 @@ namespace ICSharpCode.ILSpy.TextView @@ -85,6 +91,16 @@ namespace ICSharpCode.ILSpy.TextView
textEditor.Options.RequireControlModifierForHyperlinkClick = false;
textEditor.TextArea.TextView.MouseHover += TextViewMouseHover;
textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped;
// add marker service & margin
iconMargin = new IconBarMargin((manager = new IconBarManager()));
textMarkerService = new TextMarkerService();
textMarkerService.CodeEditor = textEditor;
textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
textEditor.TextArea.LeftMargins.Add(iconMargin);
textEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); };
}
#endregion
@ -339,11 +355,13 @@ namespace ICSharpCode.ILSpy.TextView @@ -339,11 +355,13 @@ namespace ICSharpCode.ILSpy.TextView
output.WriteLine(ex.ToString());
}
ShowOutput(output);
} finally {
iconMargin.InvalidateVisual();
}
});
}
static Task<AvalonEditTextOutput> DecompileAsync(DecompilationContext context, int outputLengthLimit)
Task<AvalonEditTextOutput> DecompileAsync(DecompilationContext context, int outputLengthLimit)
{
Debug.WriteLine("Start decompilation of {0} tree nodes", context.TreeNodes.Length);
@ -388,9 +406,10 @@ namespace ICSharpCode.ILSpy.TextView @@ -388,9 +406,10 @@ namespace ICSharpCode.ILSpy.TextView
return tcs.Task;
}
static void DecompileNodes(DecompilationContext context, ITextOutput textOutput)
void DecompileNodes(DecompilationContext context, ITextOutput textOutput)
{
var nodes = context.TreeNodes;
context.Language.DecompileFinished += Language_DecompileFinished;
for (int i = 0; i < nodes.Length; i++) {
if (i > 0)
textOutput.WriteLine();
@ -398,6 +417,20 @@ namespace ICSharpCode.ILSpy.TextView @@ -398,6 +417,20 @@ namespace ICSharpCode.ILSpy.TextView
context.Options.CancellationToken.ThrowIfCancellationRequested();
nodes[i].Decompile(context.Language, textOutput, context.Options);
}
context.Language.DecompileFinished -= Language_DecompileFinished;
}
void Language_DecompileFinished(object sender, DecompileEventArgs e)
{
if (e != null) {
manager.UpdateClassMemberBookmarks(e.AstNodes);
if (iconMargin.DecompiledMembers == null) {
iconMargin.DecompiledMembers = new List<MemberReference>();
}
iconMargin.DecompiledMembers.AddRange(e.DecompiledMemberReferences.Values.AsEnumerable());
} else {
manager.UpdateClassMemberBookmarks(null);
}
}
#endregion

18
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs

@ -25,6 +25,9 @@ @@ -25,6 +25,9 @@
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp
{
@ -101,6 +104,21 @@ namespace ICSharpCode.NRefactory.CSharp @@ -101,6 +104,21 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
/// <summary>
/// Gets all nodes in that are satisfing a predicate.
/// </summary>
/// <param name="predicate">Predicate to filter the nodes.</param>
/// <returns></returns>
public IEnumerable<AstNode> GetNodesWithLineNumbers(Predicate<AstNode> predicate)
{
if (predicate == null)
throw new ArgumentNullException("predicate");
return TreeTraversal
.PreOrder((AstNode)this, n => n.Children)
.Where(n => predicate(n) && n.Annotation<Tuple<int, int>>() != null);
}
public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitCompilationUnit (this, data);

Loading…
Cancel
Save