.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

247 lines
7.1 KiB

// 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 System.Windows;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy.TextView
{
/// <summary>
/// A text segment that references some object. Used for hyperlinks in the editor.
/// </summary>
sealed class ReferenceSegment : TextSegment
{
public object Reference;
}
/// <summary>
/// Stores the positions of the definitions that were written to the text output.
/// </summary>
sealed class DefinitionLookup
{
Dictionary<object, int> definitions = new Dictionary<object, int>();
public int GetDefinitionPosition(object definition)
{
int val;
if (definitions.TryGetValue(definition, out val))
return val;
else
return -1;
}
public void AddDefinition(object definition, int offset)
{
definitions[definition] = offset;
}
}
/// <summary>
/// Text output implementation for AvalonEdit.
/// </summary>
public sealed class AvalonEditTextOutput : ISmartTextOutput
{
TextOutputLocation location = new TextOutputLocation { Line = 1, Column = 1 };
int lastLineStart = 0;
readonly StringBuilder b = new StringBuilder();
/// <summary>Current indentation level</summary>
int indent;
/// <summary>Whether indentation should be inserted on the next write</summary>
bool needsIndent;
internal readonly List<VisualLineElementGenerator> elementGenerators = new List<VisualLineElementGenerator>();
/// <summary>List of all references that were written to the output</summary>
TextSegmentCollection<ReferenceSegment> references = new TextSegmentCollection<ReferenceSegment>();
/// <summary>Stack of the fold markers that are open but not closed yet</summary>
Stack<NewFolding> openFoldings = new Stack<NewFolding>();
/// <summary>List of all foldings that were written to the output</summary>
internal readonly List<NewFolding> Foldings = new List<NewFolding>();
internal readonly DefinitionLookup DefinitionLookup = new DefinitionLookup();
/// <summary>Embedded UIElements, see <see cref="UIElementGenerator"/>.</summary>
internal readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>();
public AvalonEditTextOutput()
{
}
/// <summary>
/// Gets the list of references (hyperlinks).
/// </summary>
internal TextSegmentCollection<ReferenceSegment> References {
get { return references; }
}
public void AddVisualLineElementGenerator(VisualLineElementGenerator elementGenerator)
{
elementGenerators.Add(elementGenerator);
}
/// <summary>
/// Controls the maximum length of the text.
/// When this length is exceeded, an <see cref="OutputLengthExceededException"/> will be thrown,
/// thus aborting the decompilation.
/// </summary>
public int LengthLimit = int.MaxValue;
public int TextLength {
get { return b.Length; }
}
public TextOutputLocation Location {
get {
location.Column = b.Length - lastLineStart + 1;
return location;
}
}
#region Text Document
TextDocument textDocument;
/// <summary>
/// Prepares the TextDocument.
/// This method may be called by the background thread writing to the output.
/// Once the document is prepared, it can no longer be written to.
/// </summary>
/// <remarks>
/// Calling this method on the background thread ensures the TextDocument's line tokenization
/// runs in the background and does not block the GUI.
/// </remarks>
public void PrepareDocument()
{
if (textDocument == null) {
textDocument = new TextDocument(b.ToString());
textDocument.SetOwnerThread(null); // release ownership
}
}
/// <summary>
/// Retrieves the TextDocument.
/// Once the document is retrieved, it can no longer be written to.
/// </summary>
public TextDocument GetDocument()
{
PrepareDocument();
textDocument.SetOwnerThread(System.Threading.Thread.CurrentThread); // acquire ownership
return textDocument;
}
#endregion
public void Indent()
{
indent++;
}
public void Unindent()
{
indent--;
}
void WriteIndent()
{
Debug.Assert(textDocument == null);
if (needsIndent) {
needsIndent = false;
for (int i = 0; i < indent; i++) {
b.Append('\t');
}
}
}
public void Write(char ch)
{
WriteIndent();
b.Append(ch);
}
public void Write(string text)
{
WriteIndent();
b.Append(text);
}
public void WriteLine()
{
Debug.Assert(textDocument == null);
b.AppendLine();
needsIndent = true;
lastLineStart = b.Length;
location.Line++;
if (this.TextLength > LengthLimit) {
throw new OutputLengthExceededException();
}
}
public void WriteDefinition(string text, object definition)
{
WriteIndent();
b.Append(text);
this.DefinitionLookup.AddDefinition(definition, this.TextLength);
}
public void WriteReference(string text, object reference)
{
WriteIndent();
int start = this.TextLength;
b.Append(text);
int end = this.TextLength;
references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference });
}
public void MarkFoldStart(string collapsedText, bool defaultCollapsed)
{
WriteIndent();
openFoldings.Push(
new NewFolding {
StartOffset = this.TextLength,
Name = collapsedText,
DefaultClosed = defaultCollapsed
});
}
public void MarkFoldEnd()
{
NewFolding f = openFoldings.Pop();
f.EndOffset = this.TextLength;
this.Foldings.Add(f);
}
public void AddUIElement(Func<UIElement> element)
{
if (element != null) {
if (this.UIElements.Count > 0 && this.UIElements.Last().Key == this.TextLength)
throw new InvalidOperationException("Only one UIElement is allowed for each position in the document");
this.UIElements.Add(new KeyValuePair<int, Lazy<UIElement>>(this.TextLength, new Lazy<UIElement>(element)));
}
}
}
}