Browse Source

Add length limit to AvalonEdit output; added support for decompiling directly to disk.

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
614bce445a
  1. 4
      ILSpy/CSharpLanguage.cs
  2. 4
      ILSpy/ILLanguage.cs
  3. 7
      ILSpy/ILSpy.csproj
  4. 3
      ILSpy/Images/Images.cs
  5. BIN
      ILSpy/Images/SaveFile.png
  6. BIN
      ILSpy/Images/ViewCode.png
  7. 3
      ILSpy/Language.cs
  8. 6
      ILSpy/MainWindow.xaml
  9. 11
      ILSpy/MainWindow.xaml.cs
  10. 238
      ILSpy/TextView/DecompilerTextView.cs
  11. 46
      ILSpy/TextView/OutputLengthExceededException.cs
  12. 25
      ILSpy/TextView/SmartTextOutput.cs
  13. 65
      ILSpy/TextView/UIElementGenerator.cs

4
ILSpy/CSharpLanguage.cs

@ -31,6 +31,10 @@ namespace ICSharpCode.ILSpy @@ -31,6 +31,10 @@ namespace ICSharpCode.ILSpy
get { return "C#"; }
}
public override string FileExtension {
get { return ".cs"; }
}
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
{
throw new NotImplementedException();

4
ILSpy/ILLanguage.cs

@ -39,8 +39,8 @@ namespace ICSharpCode.ILSpy @@ -39,8 +39,8 @@ namespace ICSharpCode.ILSpy
get { return detectControlStructure ? "IL (structured)" : "IL"; }
}
public override ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting {
get { return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinition("ILAsm"); }
public override string FileExtension {
get { return ".il"; }
}
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)

7
ILSpy/ILSpy.csproj

@ -101,6 +101,8 @@ @@ -101,6 +101,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Resource Include="Images\AssemblyList.png" />
<Resource Include="Images\AssemblyWarning.png" />
<Resource Include="Images\ViewCode.png" />
<Resource Include="Images\SaveFile.png" />
<None Include="Properties\AssemblyInfo.template.cs" />
<Compile Include="Properties\WPFAssemblyInfo.cs" />
<Compile Include="MainWindow.xaml.cs">
@ -111,8 +113,10 @@ @@ -111,8 +113,10 @@
<Compile Include="SortableGridViewColumn.cs" />
<Compile Include="TextView\CaretHighlightAdorner.cs" />
<Compile Include="TextView\DecompilerTextView.cs" />
<Compile Include="TextView\OutputLengthExceededException.cs" />
<Compile Include="TextView\ReferenceElementGenerator.cs" />
<Compile Include="TextView\SmartTextOutput.cs" />
<Compile Include="TextView\UIElementGenerator.cs" />
<Compile Include="TreeNodes\AssemblyListTreeNode.cs" />
<Compile Include="TreeNodes\AssemblyReferenceTreeNode.cs" />
<Compile Include="TreeNodes\AssemblyTreeNode.cs" />
@ -204,8 +208,5 @@ @@ -204,8 +208,5 @@
<Folder Include="TreeNodes" />
<Folder Include="TextView" />
</ItemGroup>
<ItemGroup>
<Content Include="ProfilingSessions\Session20110206_031358.sdps" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

3
ILSpy/Images/Images.cs

@ -15,6 +15,9 @@ namespace ICSharpCode.ILSpy @@ -15,6 +15,9 @@ namespace ICSharpCode.ILSpy
return image;
}
public static readonly BitmapImage ViewCode = LoadBitmap("ViewCode");
public static readonly BitmapImage Save = LoadBitmap("SaveFile");
public static readonly BitmapImage Assembly = LoadBitmap("Assembly");
public static readonly BitmapImage AssemblyWarning = LoadBitmap("AssemblyWarning");
public static readonly BitmapImage Library = LoadBitmap("Library");

BIN
ILSpy/Images/SaveFile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
ILSpy/Images/ViewCode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

3
ILSpy/Language.cs

@ -30,9 +30,10 @@ namespace ICSharpCode.ILSpy @@ -30,9 +30,10 @@ namespace ICSharpCode.ILSpy
public abstract class Language
{
public abstract string Name { get; }
public abstract string FileExtension { get; }
public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting {
get { return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinition(this.Name); }
get { return ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(this.FileExtension); }
}
public virtual void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)

6
ILSpy/MainWindow.xaml

@ -25,6 +25,12 @@ @@ -25,6 +25,12 @@
</MenuItem>
<MenuItem Header="Open from _GAC" Click="OpenFromGac_Click" />
<Separator />
<MenuItem Header="_Save Code" Click="saveCode_Click">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/SaveFile.png" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="E_xit" Click="ExitClick" />
</MenuItem>
<MenuItem Header="_View">

11
ILSpy/MainWindow.xaml.cs

@ -280,7 +280,16 @@ namespace ICSharpCode.ILSpy @@ -280,7 +280,16 @@ namespace ICSharpCode.ILSpy
void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
decompilerTextView.Decompile(sessionSettings.FilterSettings.Language, treeView.SelectedItems.OfType<ILSpyTreeNodeBase>());
decompilerTextView.Decompile(sessionSettings.FilterSettings.Language,
treeView.GetTopLevelSelection().OfType<ILSpyTreeNodeBase>(),
new DecompilationOptions());
}
void saveCode_Click(object sender, RoutedEventArgs e)
{
decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language,
treeView.GetTopLevelSelection().OfType<ILSpyTreeNodeBase>(),
new DecompilationOptions());
}
protected override void OnStateChanged(EventArgs e)

238
ILSpy/TextView/DecompilerTextView.cs

@ -18,20 +18,25 @@ @@ -18,20 +18,25 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Xml;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TextView
@ -42,12 +47,14 @@ namespace ICSharpCode.ILSpy.TextView @@ -42,12 +47,14 @@ namespace ICSharpCode.ILSpy.TextView
sealed partial class DecompilerTextView : UserControl
{
readonly ReferenceElementGenerator referenceElementGenerator;
readonly UIElementGenerator uiElementGenerator;
readonly FoldingManager foldingManager;
internal MainWindow mainWindow;
DefinitionLookup definitionLookup;
CancellationTokenSource currentCancellationTokenSource;
#region Constructor
public DecompilerTextView()
{
HighlightingManager.Instance.RegisterHighlighting(
@ -63,11 +70,15 @@ namespace ICSharpCode.ILSpy.TextView @@ -63,11 +70,15 @@ namespace ICSharpCode.ILSpy.TextView
InitializeComponent();
this.referenceElementGenerator = new ReferenceElementGenerator(this);
textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator);
this.uiElementGenerator = new UIElementGenerator();
textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator);
textEditor.Text = "Welcome to ILSpy!";
foldingManager = FoldingManager.Install(textEditor.TextArea);
}
#endregion
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes)
#region RunWithCancellation
void RunWithCancellation<T>(Func<CancellationToken, Task<T>> taskCreation, Action<Task<T>> taskCompleted)
{
if (waitAdorner.Visibility != Visibility.Visible) {
waitAdorner.Visibility = Visibility.Visible;
@ -80,51 +91,92 @@ namespace ICSharpCode.ILSpy.TextView @@ -80,51 +91,92 @@ namespace ICSharpCode.ILSpy.TextView
if (previousCancellationTokenSource != null)
previousCancellationTokenSource.Cancel();
DecompilationOptions options = new DecompilationOptions();
options.CancellationToken = myCancellationTokenSource.Token;
var task = RunDecompiler(language, treeNodes.ToArray(), options);
var task = taskCreation(myCancellationTokenSource.Token);
Action continuation = delegate {
try {
if (currentCancellationTokenSource == myCancellationTokenSource) {
currentCancellationTokenSource = null;
waitAdorner.Visibility = Visibility.Collapsed;
taskCompleted(task);
} else {
try {
task.Wait();
} catch (AggregateException) {
// observe the exception (otherwise the task's finalizer will shut down the AppDomain)
}
}
} finally {
myCancellationTokenSource.Dispose();
}
};
task.ContinueWith(delegate { Dispatcher.BeginInvoke(DispatcherPriority.Normal, continuation); });
}
void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (currentCancellationTokenSource != null)
currentCancellationTokenSource.Cancel();
}
#endregion
#region ShowOutput
void ShowOutput(SmartTextOutput textOutput, ILSpy.Language language = null)
{
textEditor.ScrollToHome();
foldingManager.Clear();
try {
SmartTextOutput textOutput = task.Result;
uiElementGenerator.UIElements = textOutput.UIElements;
referenceElementGenerator.References = textOutput.References;
definitionLookup = textOutput.DefinitionLookup;
textEditor.SyntaxHighlighting = language.SyntaxHighlighting;
textEditor.SyntaxHighlighting = language != null ? language.SyntaxHighlighting : null;
textEditor.Text = textOutput.ToString();
foldingManager.UpdateFoldings(textOutput.Foldings.OrderBy(f => f.StartOffset), -1);
}
#endregion
#region Decompile (for display)
const int defaultOutputLengthLimit = 5000000; // more than 5M characters is too slow to output (when user browses treeview)
const int extendedOutputLengthLimit = 75000000; // more than 75M characters can get us into trouble with memory usage
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes, DecompilationOptions options)
{
Decompile(language, treeNodes.ToArray(), defaultOutputLengthLimit, options);
}
void Decompile(ILSpy.Language language, ILSpyTreeNodeBase[] treeNodes, int outputLengthLimit, DecompilationOptions options)
{
RunWithCancellation(
delegate (CancellationToken ct) { // creation of the background task
options.CancellationToken = ct;
return RunDecompiler(language, treeNodes, options, outputLengthLimit);
},
delegate (Task<SmartTextOutput> task) { // handling the result
try {
SmartTextOutput textOutput = task.Result;
Debug.WriteLine("Decompiler finished; output size = {0} characters", textOutput.TextLength);
ShowOutput(textOutput, language);
} catch (AggregateException aggregateException) {
textEditor.SyntaxHighlighting = null;
referenceElementGenerator.References = null;
definitionLookup = null;
Debug.WriteLine("Decompiler crashed: " + aggregateException.ToString());
// Unpack aggregate exceptions as long as there's only a single exception:
// (assembly load errors might produce nested aggregate exceptions)
Exception ex = aggregateException;
while (ex is AggregateException && (ex as AggregateException).InnerExceptions.Count == 1)
ex = ex.InnerException;
textEditor.Text = ex.ToString();
}
if (ex is OutputLengthExceededException) {
ShowOutputLengthExceededMessage(language, treeNodes, options, outputLengthLimit == defaultOutputLengthLimit);
} else {
try {
task.Wait();
} catch (AggregateException) {
// observe the exception (otherwise the task's finalizer will shut down the AppDomain)
SmartTextOutput output = new SmartTextOutput();
output.WriteLine(ex.ToString());
ShowOutput(output);
}
}
} finally {
myCancellationTokenSource.Dispose();
}
};
task.ContinueWith(delegate { Dispatcher.BeginInvoke(DispatcherPriority.Normal, continuation); });
});
}
static Task<SmartTextOutput> RunDecompiler(ILSpy.Language language, ILSpyTreeNodeBase[] nodes, DecompilationOptions options)
static Task<SmartTextOutput> RunDecompiler(ILSpy.Language language, ILSpyTreeNodeBase[] nodes, DecompilationOptions options, int outputLengthLimit)
{
Debug.WriteLine("Start decompilation of {0} nodes", nodes.Length);
if (nodes.Length == 0) {
// If there's nothing to be decompiled, don't bother starting up a thread.
// (Improves perf in some cases since we don't have to wait for the thread-pool to accept our task)
@ -136,16 +188,77 @@ namespace ICSharpCode.ILSpy.TextView @@ -136,16 +188,77 @@ namespace ICSharpCode.ILSpy.TextView
return Task.Factory.StartNew(
delegate {
SmartTextOutput textOutput = new SmartTextOutput();
textOutput.LengthLimit = outputLengthLimit;
DecompileNodes(language, nodes, options, textOutput);
return textOutput;
});
}
static void DecompileNodes(ILSpy.Language language, ILSpyTreeNodeBase[] nodes, DecompilationOptions options, ITextOutput textOutput)
{
bool first = true;
foreach (var node in nodes) {
if (first) first = false; else textOutput.WriteLine();
options.CancellationToken.ThrowIfCancellationRequested();
node.Decompile(language, textOutput, options);
}
return textOutput;
});
}
#endregion
#region ShowOutputLengthExceededMessage
void ShowOutputLengthExceededMessage(ILSpy.Language language, ILSpyTreeNodeBase[] treeNodes, DecompilationOptions options, bool wasNormalLimit)
{
SmartTextOutput output = new SmartTextOutput();
if (wasNormalLimit) {
output.WriteLine("You have selected too much code for it to be displayed automatically.");
} else {
output.WriteLine("You have selected too much code; it cannot be displayed here.");
}
output.WriteLine();
Button button;
if (wasNormalLimit) {
output.AddUIElement(MakeButton(
Images.ViewCode, "Display Code",
delegate {
Decompile(language, treeNodes, extendedOutputLengthLimit, options);
}));
output.WriteLine();
}
output.AddUIElement(MakeButton(
Images.Save, "Save Code",
delegate {
SaveToDisk(language, treeNodes, options);
}));
output.WriteLine();
ShowOutput(output);
}
Func<Button> MakeButton(ImageSource icon, string text, RoutedEventHandler click)
{
return () => {
Button button = new Button();
button.Cursor = Cursors.Arrow;
button.Margin = new Thickness(2);
if (icon != null) {
button.Content = new StackPanel {
Orientation = Orientation.Horizontal,
Children = {
new Image { Width = 16, Height = 16, Source = icon, Margin = new Thickness(0, 0, 4, 0) },
new TextBlock { Text = text }
}
};
} else {
button.Content = text;
}
button.Click += click;
return button;
};
}
#endregion
#region JumpToReference
internal void JumpToReference(ReferenceSegment referenceSegment)
{
object reference = referenceSegment.Reference;
@ -177,11 +290,80 @@ namespace ICSharpCode.ILSpy.TextView @@ -177,11 +290,80 @@ namespace ICSharpCode.ILSpy.TextView
mainWindow.SelectNode(assemblyList.Assemblies.FirstOrDefault(node => node.AssemblyDefinition == reference));
}
}
#endregion
void cancelButton_Click(object sender, RoutedEventArgs e)
#region SaveToDisk
public void SaveToDisk(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes, DecompilationOptions options)
{
if (currentCancellationTokenSource != null)
currentCancellationTokenSource.Cancel();
if (!treeNodes.Any())
return;
SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = language.FileExtension;
dlg.Filter = language.Name + "|*" + language.FileExtension + "|All Files|*.*";
dlg.FileName = CleanUpName(treeNodes.First().ToString()) + language.FileExtension;
if (dlg.ShowDialog() == true) {
SaveToDisk(language, treeNodes.ToArray(), options, dlg.FileName);
}
}
void SaveToDisk(ILSpy.Language language, ILSpyTreeNodeBase[] nodes, DecompilationOptions options, string fileName)
{
RunWithCancellation(
delegate (CancellationToken ct) {
options.CancellationToken = ct;
return Task.Factory.StartNew(
delegate {
using (StreamWriter w = new StreamWriter(fileName)) {
try {
DecompileNodes(language, nodes, options, new PlainTextOutput(w));
} catch (OperationCanceledException) {
w.WriteLine();
w.WriteLine("Decompiled was cancelled.");
throw;
}
}
SmartTextOutput output = new SmartTextOutput();
output.WriteLine("Decompilation complete.");
output.WriteLine();
output.AddUIElement(MakeButton(
null, "Open Explorer",
delegate {
Process.Start("explorer", "/select,\"" + fileName + "\"");
}
));
output.WriteLine();
return output;
});
},
delegate (Task<SmartTextOutput> task) {
try {
ShowOutput(task.Result);
} catch (AggregateException aggregateException) {
textEditor.SyntaxHighlighting = null;
Debug.WriteLine("Decompiler crashed: " + aggregateException.ToString());
// Unpack aggregate exceptions as long as there's only a single exception:
// (assembly load errors might produce nested aggregate exceptions)
Exception ex = aggregateException;
while (ex is AggregateException && (ex as AggregateException).InnerExceptions.Count == 1)
ex = ex.InnerException;
SmartTextOutput output = new SmartTextOutput();
output.WriteLine(ex.ToString());
ShowOutput(output);
}
});
}
string CleanUpName(string text)
{
int pos = text.IndexOf(':');
if (pos > 0)
text = text.Substring(0, pos);
text = text.Trim();
foreach (char c in Path.GetInvalidFileNameChars())
text = text.Replace(c, '-');
return text;
}
#endregion
}
}

46
ILSpy/TextView/OutputLengthExceededException.cs

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
// 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.Runtime.Serialization;
namespace ICSharpCode.ILSpy.TextView
{
/// <summary>
/// This exception gets used when the text output is longer than the specified limit.
/// </summary>
class OutputLengthExceededException : Exception, ISerializable
{
public OutputLengthExceededException()
{
}
public OutputLengthExceededException(string message) : base(message)
{
}
public OutputLengthExceededException(string message, Exception innerException) : base(message, innerException)
{
}
// This constructor is needed for serialization.
protected OutputLengthExceededException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

25
ILSpy/TextView/SmartTextOutput.cs

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.Decompiler;
@ -60,11 +60,18 @@ namespace ICSharpCode.ILSpy.TextView @@ -60,11 +60,18 @@ namespace ICSharpCode.ILSpy.TextView
public readonly List<NewFolding> Foldings = new List<NewFolding>();
public readonly DefinitionLookup DefinitionLookup = new DefinitionLookup();
public readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>();
public TextSegmentCollection<ReferenceSegment> References {
get { return references; }
}
public int LengthLimit = int.MaxValue;
public int TextLength {
get { return b.Length; }
}
public override string ToString()
{
return b.ToString();
@ -102,17 +109,13 @@ namespace ICSharpCode.ILSpy.TextView @@ -102,17 +109,13 @@ namespace ICSharpCode.ILSpy.TextView
b.Append(text);
}
public void WriteCommentLine(string comment)
{
WriteIndent();
b.AppendLine(comment);
needsIndent = true;
}
public void WriteLine()
{
b.AppendLine();
needsIndent = true;
if (b.Length > LengthLimit) {
throw new OutputLengthExceededException();
}
}
public void WriteDefinition(string text, object definition)
@ -143,5 +146,11 @@ namespace ICSharpCode.ILSpy.TextView @@ -143,5 +146,11 @@ namespace ICSharpCode.ILSpy.TextView
f.EndOffset = b.Length;
this.Foldings.Add(f);
}
public void AddUIElement(Func<UIElement> element)
{
if (element != null)
this.UIElements.Add(new KeyValuePair<int, Lazy<UIElement>>(b.Length, new Lazy<UIElement>(element)));
}
}
}

65
ILSpy/TextView/UIElementGenerator.cs

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
// 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.Windows;
using ICSharpCode.AvalonEdit.Rendering;
namespace ICSharpCode.ILSpy.TextView
{
using Pair = KeyValuePair<int, Lazy<UIElement>>;
/// <summary>
/// Embeds UIElements in the text output.
/// </summary>
public class UIElementGenerator : VisualLineElementGenerator, IComparer<Pair>
{
public List<Pair> UIElements;
public override int GetFirstInterestedOffset(int startOffset)
{
if (this.UIElements == null)
return -1;
int r = this.UIElements.BinarySearch(new Pair(startOffset, null), this);
if (r < 0)
r = ~r;
if (r < this.UIElements.Count)
return this.UIElements[r].Key;
else
return -1;
}
public override VisualLineElement ConstructElement(int offset)
{
if (this.UIElements == null)
return null;
int r = UIElements.BinarySearch(new Pair(offset, null), this);
if (r >= 0)
return new InlineObjectElement(0, this.UIElements[r].Value.Value);
else
return null;
}
int IComparer<Pair>.Compare(Pair x, Pair y)
{
return x.Key.CompareTo(y.Key);
}
}
}
Loading…
Cancel
Save