Browse Source

Decompiler text view backed by AvaloniaEdit (Phase 1)

Selecting a node in the assembly tree now opens (or refocuses) a
DecompilerTabPageModel document tab and renders the C# decompilation in
an AvaloniaEdit TextEditor with XSHD-driven syntax highlighting.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
ed8ad4ee38
  1. 5
      ILSpy/App.axaml
  2. 3
      ILSpy/AssemblyTree/AssemblyListPane.axaml.cs
  3. 42
      ILSpy/DecompilationOptions.cs
  4. 68
      ILSpy/Docking/DockWorkspace.cs
  5. 6
      ILSpy/Docking/ILSpyDockFactory.cs
  6. 4
      ILSpy/ILSpy.csproj
  7. 81
      ILSpy/Languages/CSharpLanguage.cs
  8. 50
      ILSpy/Languages/Language.cs
  9. 1215
      ILSpy/TextView/Asm-Mode.xshd
  10. 106
      ILSpy/TextView/AvaloniaEditTextOutput.cs
  11. 159
      ILSpy/TextView/CSharp-Mode.xshd
  12. 125
      ILSpy/TextView/DecompilerTabPageModel.cs
  13. 16
      ILSpy/TextView/DecompilerTextView.axaml
  14. 59
      ILSpy/TextView/DecompilerTextView.axaml.cs
  15. 79
      ILSpy/TextView/HighlightingService.cs
  16. 540
      ILSpy/TextView/ILAsm-Mode.xshd
  17. 63
      ILSpy/TextView/XML-Mode.xshd
  18. 6
      ILSpy/TreeNodes/EventTreeNode.cs
  19. 6
      ILSpy/TreeNodes/FieldTreeNode.cs
  20. 11
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  21. 6
      ILSpy/TreeNodes/MethodTreeNode.cs
  22. 18
      ILSpy/TreeNodes/NamespaceTreeNode.cs
  23. 6
      ILSpy/TreeNodes/PropertyTreeNode.cs
  24. 11
      ILSpy/TreeNodes/TypeTreeNode.cs

5
ILSpy/App.axaml

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
xmlns:tree="using:ILSpy.AssemblyTree"
xmlns:search="using:ILSpy.Search"
xmlns:analyzers="using:ILSpy.Analyzers"
xmlns:textView="using:ILSpy.TextView"
xmlns:dockTheme="using:Dock.Avalonia.Themes.Simple"
xmlns:controls="using:ILSpy.Controls"
RequestedThemeVariant="Default">
@ -35,12 +36,16 @@ @@ -35,12 +36,16 @@
<DataTemplate DataType="analyzers:AnalyzerTreeViewModel">
<analyzers:AnalyzerTreeView />
</DataTemplate>
<DataTemplate DataType="textView:DecompilerTabPageModel">
<textView:DecompilerTextView />
</DataTemplate>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<SimpleTheme />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Simple.v2.xaml" />
<StyleInclude Source="avares://AvaloniaEdit/Themes/Simple/AvaloniaEdit.xaml" />
<dockTheme:DockSimpleTheme />
<!-- Classic SharpTreeView-style +/- expander, ported from ILSpy WPF

3
ILSpy/AssemblyTree/AssemblyListPane.axaml.cs

@ -27,6 +27,8 @@ using Avalonia.VisualTree; @@ -27,6 +27,8 @@ using Avalonia.VisualTree;
using ICSharpCode.ILSpyX.TreeView;
using ILSpy.TreeNodes;
namespace ILSpy.AssemblyTree
{
public partial class AssemblyListPane : UserControl
@ -97,6 +99,7 @@ namespace ILSpy.AssemblyTree @@ -97,6 +99,7 @@ namespace ILSpy.AssemblyTree
TreeGrid.ScrollIntoView(target, TreeGrid.Columns[0]);
}
void OnTreeGridDoubleTapped(object? sender, TappedEventArgs e)
{
if (TreeGrid.HierarchicalModel is not IHierarchicalModel model)

42
ILSpy/DecompilationOptions.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// Copyright (c) 2026 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.Threading;
using ICSharpCode.Decompiler;
namespace ILSpy
{
/// <summary>
/// Options passed to <see cref="Languages.Language"/>'s Decompile* methods.
/// Stripped-down compared to the WPF host: no project export, no display-settings plumbing,
/// no view-state restoration — just enough to drive a single decompilation into a text view.
/// </summary>
public sealed class DecompilationOptions
{
public DecompilerSettings DecompilerSettings { get; }
public CancellationToken CancellationToken { get; set; }
public DecompilationOptions(DecompilerSettings settings)
{
DecompilerSettings = settings;
}
public DecompilationOptions() : this(new DecompilerSettings()) { }
}
}

68
ILSpy/Docking/DockWorkspace.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Composition;
using Dock.Model.Controls;
@ -26,7 +27,10 @@ using Dock.Model.Core.Events; @@ -26,7 +27,10 @@ using Dock.Model.Core.Events;
using ILSpy.Analyzers;
using ILSpy.AssemblyTree;
using ILSpy.Languages;
using ILSpy.Search;
using ILSpy.TextView;
using ILSpy.TreeNodes;
using ILSpy.ViewModels;
namespace ILSpy.Docking
@ -36,6 +40,8 @@ namespace ILSpy.Docking @@ -36,6 +40,8 @@ namespace ILSpy.Docking
public class DockWorkspace
{
readonly ILSpyDockFactory factory;
readonly AssemblyTreeModel assemblyTreeModel;
readonly LanguageService languageService;
public IFactory Factory => factory;
@ -47,10 +53,18 @@ namespace ILSpy.Docking @@ -47,10 +53,18 @@ namespace ILSpy.Docking
public DockWorkspace(
AssemblyTreeModel assemblyTreeModel,
SearchPaneModel searchPaneModel,
AnalyzerTreeViewModel analyzerTreeViewModel)
AnalyzerTreeViewModel analyzerTreeViewModel,
LanguageService languageService)
{
this.assemblyTreeModel = assemblyTreeModel;
this.languageService = languageService;
factory = new ILSpyDockFactory(assemblyTreeModel, searchPaneModel, analyzerTreeViewModel);
Layout = factory.CreateLayout();
if (factory.InitialDecompilerTab is { } initialTab)
initialTab.Language = languageService.CurrentLanguage;
assemblyTreeModel.PropertyChanged += OnAssemblyTreePropertyChanged;
languageService.PropertyChanged += OnLanguagePropertyChanged;
// Layout/factory initialization (locators, parent/factory wiring) is done by
// the DockControl in MainWindow.axaml via InitializeFactory/InitializeLayout.
@ -101,6 +115,58 @@ namespace ILSpy.Docking @@ -101,6 +115,58 @@ namespace ILSpy.Docking
}
}
void OnAssemblyTreePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(AssemblyTreeModel.SelectedItem))
ShowSelectedNode();
}
void OnLanguagePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LanguageService.CurrentLanguage))
{
if (GetActiveDecompilerTab() is { } tab)
{
tab.Language = languageService.CurrentLanguage;
// Re-decompile by re-assigning the same node so the tab refreshes for the new language.
var node = tab.CurrentNode;
tab.CurrentNode = null;
tab.CurrentNode = node;
}
}
}
void ShowSelectedNode()
{
if (assemblyTreeModel.SelectedItem is not ILSpyTreeNode node)
return;
var tab = GetActiveDecompilerTab();
if (tab == null)
{
tab = factory.InitialDecompilerTab;
if (tab == null || factory.Documents == null)
return;
factory.AddDockable(factory.Documents, tab);
factory.SetActiveDockable(tab);
factory.SetFocusedDockable(factory.Documents, tab);
}
tab.Language = languageService.CurrentLanguage;
tab.CurrentNode = node;
}
DecompilerTabPageModel? GetActiveDecompilerTab()
{
if (factory.Documents?.ActiveDockable is DecompilerTabPageModel active)
return active;
if (factory.Documents?.VisibleDockables != null)
{
foreach (var d in factory.Documents.VisibleDockables)
if (d is DecompilerTabPageModel m)
return m;
}
return null;
}
public TabPageModel OpenNewTab(TabPageModel tab)
{
if (factory.Documents != null)

6
ILSpy/Docking/ILSpyDockFactory.cs

@ -24,6 +24,7 @@ using Dock.Model.Mvvm.Controls; @@ -24,6 +24,7 @@ using Dock.Model.Mvvm.Controls;
using ILSpy.Analyzers;
using ILSpy.AssemblyTree;
using ILSpy.Search;
using ILSpy.TextView;
namespace ILSpy.Docking
{
@ -35,6 +36,8 @@ namespace ILSpy.Docking @@ -35,6 +36,8 @@ namespace ILSpy.Docking
public IDocumentDock? Documents { get; private set; }
public DecompilerTabPageModel? InitialDecompilerTab { get; private set; }
public ILSpyDockFactory(
AssemblyTreeModel assemblyTreeModel,
SearchPaneModel searchPaneModel,
@ -71,6 +74,9 @@ namespace ILSpy.Docking @@ -71,6 +74,9 @@ namespace ILSpy.Docking
};
Documents = documents;
// Initial decompiler tab is added lazily on first selection (DockWorkspace.ShowSelectedNode).
InitialDecompilerTab = new DecompilerTabPageModel { Title = "(no selection)" };
var bottomToolDock = new ToolDock {
Id = "BottomTools",
Proportion = 0.2,

4
ILSpy/ILSpy.csproj

@ -26,6 +26,10 @@ @@ -26,6 +26,10 @@
<AvaloniaResource Remove="Assets\ILSpy.pdn" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TextView\*.xshd" />
</ItemGroup>
<ItemGroup>
<!-- Avalonia core -->
<PackageReference Include="Avalonia" />

81
ILSpy/Languages/CSharpLanguage.cs

@ -17,12 +17,21 @@ @@ -17,12 +17,21 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ConversionFlags = ICSharpCode.Decompiler.Output.ConversionFlags;
@ -60,5 +69,77 @@ namespace ILSpy.Languages @@ -60,5 +69,77 @@ namespace ILSpy.Languages
| ConversionFlags.ShowParameterModifiers;
return ambience.ConvertSymbol(entity);
}
public override void WriteCommentLine(ITextOutput output, string comment) => output.WriteLine("// " + comment);
public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(type.ParentModule?.MetadataFile != null);
DecompileEntities(type.ParentModule.MetadataFile, new[] { type.MetadataToken }, output, options);
}
public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(method.ParentModule?.MetadataFile != null);
DecompileEntities(method.ParentModule.MetadataFile, new[] { method.MetadataToken }, output, options);
}
public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(field.ParentModule?.MetadataFile != null);
DecompileEntities(field.ParentModule.MetadataFile, new[] { field.MetadataToken }, output, options);
}
public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(property.ParentModule?.MetadataFile != null);
DecompileEntities(property.ParentModule.MetadataFile, new[] { property.MetadataToken }, output, options);
}
public override void DecompileEvent(IEvent ev, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(ev.ParentModule?.MetadataFile != null);
DecompileEntities(ev.ParentModule.MetadataFile, new[] { ev.MetadataToken }, output, options);
}
public override void DecompileNamespace(string nameSpace, IEnumerable<ITypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
var typesByModule = types.GroupBy(t => {
Debug.Assert(t.ParentModule?.MetadataFile != null);
return t.ParentModule.MetadataFile;
});
bool first = true;
foreach (var group in typesByModule)
{
if (!first)
output.WriteLine();
first = false;
DecompileEntities(group.Key, group.Select(t => t.MetadataToken), output, options);
}
}
void DecompileEntities(MetadataFile module, IEnumerable<EntityHandle> handles, ITextOutput output, DecompilationOptions options)
{
if (module == null)
{
WriteCommentLine(output, "(metadata file unavailable)");
return;
}
var resolver = module.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences);
var decompiler = new CSharpDecompiler(module, resolver, options.DecompilerSettings) {
CancellationToken = options.CancellationToken,
DebugInfoProvider = module.GetDebugInfoOrNull(),
};
SyntaxTree syntaxTree = decompiler.Decompile(handles);
WriteCode(output, options.DecompilerSettings, syntaxTree, decompiler.TypeSystem);
}
static void WriteCode(ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem)
{
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
output.IndentationString = settings.CSharpFormattingOptions.IndentationString;
TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem);
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions));
}
}
}

50
ILSpy/Languages/Language.cs

@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.IL;
@ -26,7 +28,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -26,7 +28,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ILSpy.Languages
{
/// <summary>
/// Output language for tree-node labels and (eventually) decompiled views.
/// Output language for tree-node labels and decompiled views.
/// Subclasses override formatting for their target syntax. Implementations must be
/// thread-safe.
/// </summary>
@ -63,6 +65,52 @@ namespace ILSpy.Languages @@ -63,6 +65,52 @@ namespace ILSpy.Languages
return ambience.ConvertSymbol(entity);
}
/// <summary>
/// Writes <paramref name="comment"/> as a single-line comment in this language's syntax.
/// </summary>
public virtual void WriteCommentLine(ITextOutput output, string comment)
{
output.WriteLine("// " + comment);
}
// Default Decompile* implementations write a stub comment so we always produce *something*
// for symbols whose language doesn't have a meaningful decompilation. Real languages
// (CSharpLanguage, eventually ILLanguage) override these.
public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, TypeToString(type));
}
public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(method.DeclaringTypeDefinition != null);
WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition) + "." + method.Name);
}
public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(field.DeclaringTypeDefinition != null);
WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition) + "." + field.Name);
}
public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(property.DeclaringTypeDefinition != null);
WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition) + "." + property.Name);
}
public virtual void DecompileEvent(IEvent ev, ITextOutput output, DecompilationOptions options)
{
Debug.Assert(ev.DeclaringTypeDefinition != null);
WriteCommentLine(output, TypeToString(ev.DeclaringTypeDefinition) + "." + ev.Name);
}
public virtual void DecompileNamespace(string nameSpace, IEnumerable<ITypeDefinition> types, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, nameSpace);
}
public override string ToString() => Name;
}
}

1215
ILSpy/TextView/Asm-Mode.xshd

File diff suppressed because it is too large Load Diff

106
ILSpy/TextView/AvaloniaEditTextOutput.cs

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
// Copyright (c) 2026 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.Reflection.Metadata;
using System.Text;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ILSpy.TextView
{
/// <summary>
/// Phase-1 <see cref="ITextOutput"/> for the Avalonia text view: just accumulates text into a
/// <see cref="StringBuilder"/> with the indentation contract the decompiler expects. Reference
/// markers, fold ranges, and inline UI elements are intentionally dropped — those will land
/// when we add hyperlinks and folding support.
/// </summary>
sealed class AvaloniaEditTextOutput : ITextOutput
{
readonly StringBuilder builder = new();
int indent;
bool needsIndent;
public string IndentationString { get; set; } = "\t";
public int TextLength => builder.Length;
public string GetText() => builder.ToString();
public void Indent() => indent++;
public void Unindent()
{
if (indent > 0)
indent--;
}
void WriteIndentIfNeeded()
{
if (!needsIndent)
return;
needsIndent = false;
for (int i = 0; i < indent; i++)
builder.Append(IndentationString);
}
public void Write(char ch)
{
WriteIndentIfNeeded();
builder.Append(ch);
}
public void Write(string text)
{
WriteIndentIfNeeded();
builder.Append(text);
}
public void WriteLine()
{
builder.Append('\n');
needsIndent = true;
}
public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false)
{
Write(omitSuffix ? opCode.Name.TrimEnd('.') : opCode.Name);
}
public void WriteReference(MetadataFile metadata, Handle handle, string text, string protocol = "decompile", bool isDefinition = false)
=> Write(text);
public void WriteReference(IType type, string text, bool isDefinition = false) => Write(text);
public void WriteReference(IMember member, string text, bool isDefinition = false) => Write(text);
public void WriteLocalReference(string text, object reference, bool isDefinition = false) => Write(text);
public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false)
{
// Folding support arrives in a later phase.
}
public void MarkFoldEnd()
{
// Folding support arrives in a later phase.
}
}
}

159
ILSpy/TextView/CSharp-Mode.xshd

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
<?xml version="1.0"?>
<SyntaxDefinition name="C#" extensions=".cs" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<!-- This is a variant of the AvalonEdit C# highlighting that has several constructs disabled.
The disabled constructs (e.g. contextual keywords) are highlighted using the CSharpLanguage.HighlightingTokenWriter instead.
-->
<!-- The named colors 'Comment' and 'String' are used in SharpDevelop to detect if a line is inside a multiline string/comment -->
<Color name="Comment" foreground="Green" exampleText="// comment" />
<Color name="String" foreground="Blue" exampleText="string text = &quot;Hello, World!&quot;"/>
<Color name="StringInterpolation" foreground="Black" exampleText="string text = $&quot;Hello, {name}!&quot;"/>
<Color name="Char" foreground="Magenta" exampleText="char linefeed = '\n';"/>
<Color name="Preprocessor" foreground="Green" exampleText="#region Title" />
<Color name="Punctuation" exampleText="a(b.c);" />
<Color name="ValueTypeKeywords" fontWeight="bold" foreground="Red" exampleText="bool b = true;" />
<Color name="ReferenceTypeKeywords" foreground="Red" exampleText="object o;" />
<Color name="NumberLiteral" foreground="DarkBlue" exampleText="3.1415f"/>
<Color name="ThisOrBaseReference" fontWeight="bold" exampleText="this.Do(); base.Do();"/>
<Color name="NullOrValueKeywords" fontWeight="bold" exampleText="if (value == null)"/>
<Color name="Keywords" fontWeight="bold" foreground="Blue" exampleText="if (a) {} else {}"/>
<Color name="GotoKeywords" foreground="Navy" exampleText="continue; return null;"/>
<Color name="QueryKeywords" foreground="Navy" exampleText="from x in y select z;"/>
<Color name="ExceptionKeywords" fontWeight="bold" foreground="Teal" exampleText="try {} catch {} finally {}"/>
<Color name="CheckedKeyword" fontWeight="bold" foreground="DarkGray" exampleText="checked {}"/>
<Color name="UnsafeKeywords" foreground="Olive" exampleText="unsafe { fixed (..) {} }"/>
<Color name="OperatorKeywords" fontWeight="bold" foreground="Pink" exampleText="public static implicit operator..."/>
<Color name="ParameterModifiers" fontWeight="bold" foreground="DeepPink" exampleText="(ref int a, params int[] b)"/>
<Color name="Modifiers" foreground="Brown" exampleText="static readonly int a;"/>
<Color name="Visibility" fontWeight="bold" foreground="Blue" exampleText="public override void ToString();"/>
<Color name="NamespaceKeywords" fontWeight="bold" foreground="Green" exampleText="namespace A.B { using System; }"/>
<Color name="GetSetAddRemove" foreground="SaddleBrown" exampleText="int Prop { get; set; }"/>
<Color name="TrueFalse" fontWeight="bold" foreground="DarkCyan" exampleText="b = false; a = true;" />
<Color name="TypeKeywords" fontWeight="bold" foreground="DarkCyan" exampleText="if (x is int) { a = x as int; type = typeof(int); size = sizeof(int); c = new object(); }"/>
<Color name="AttributeKeywords" foreground="Navy" exampleText="[assembly: AssemblyVersion(&quot;1.0.0.*&quot;)]" />
<!-- Colors used for semantic highlighting -->
<Color name="ReferenceTypes" foreground="#004085" exampleText="System.#{#Uri#}# uri;"/>
<Color name="InterfaceTypes" foreground="#004085" exampleText="System.#{#IDisposable#}# obj;"/>
<Color name="TypeParameters" foreground="#004085" exampleText="class MyList&lt;#{#T#}#&gt; { }"/>
<Color name="DelegateTypes" foreground="#004085" exampleText="System.#{#Action#}#; action;"/>
<Color name="ValueTypes" fontWeight="bold" foreground="#004085" exampleText="System.#{#DateTime#}# date;"/>
<Color name="EnumTypes" fontWeight="bold" foreground="#004085" exampleText="System.#{#ConsoleKey#}# key;"/>
<Color name="MethodDeclaration" exampleText="override string #{#ToString#}#() { }"/>
<Color name="MethodCall" foreground="MidnightBlue" fontWeight="bold" exampleText="o.#{#ToString#}#();"/>
<Color name="FieldDeclaration" exampleText="private int #{#name#}#;"/>
<Color name="FieldAccess" fontStyle="italic" exampleText="return this.#{#name#}#;"/>
<Color name="PropertyDeclaration" exampleText="private int #{#name#}# { get; set; }"/>
<Color name="PropertyAccess" exampleText="return this.#{#name#}#;"/>
<Color name="EventDeclaration" exampleText="private event Action #{#name#}#;"/>
<Color name="EventAccess" exampleText="this.#{#name#}#?.Invoke();"/>
<Color name="Variable" exampleText="var #{#name#}# = 42;"/>
<Color name="Parameter" exampleText="void Method(string #{#name#}#) { }"/>
<Color name="InactiveCode" foreground="Gray" exampleText="#{#Deactivated by #if#}#"/>
<Color name="SemanticError" foreground="DarkRed" exampleText="o.#{#MissingMethod#}#()"/>
<Property name="DocCommentMarker" value="///" />
<RuleSet name="CommentMarkerSet">
<Keywords fontWeight="bold" foreground="Red">
<Word>TODO</Word>
<Word>FIXME</Word>
</Keywords>
<Keywords fontWeight="bold" foreground="#E0E000">
<Word>HACK</Word>
<Word>UNDONE</Word>
</Keywords>
</RuleSet>
<!-- This is the main ruleset. -->
<RuleSet>
<Span color="Preprocessor">
<Begin>\#</Begin>
<RuleSet name="PreprocessorSet">
<Span> <!-- preprocessor directives that allow comments -->
<Begin fontWeight="bold">
(define|undef|if|elif|else|endif|line)\b
</Begin>
<RuleSet>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>//</Begin>
</Span>
</RuleSet>
</Span>
<Span> <!-- preprocessor directives that don't allow comments -->
<Begin fontWeight="bold">
(region|endregion|error|warning|pragma)\b
</Begin>
</Span>
</RuleSet>
</Span>
<Span color="Comment">
<Begin color="XmlDoc/DocComment">///(?!/)</Begin>
<RuleSet>
<Import ruleSet="XmlDoc/DocCommentSet"/>
<Import ruleSet="CommentMarkerSet"/>
</RuleSet>
</Span>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>//</Begin>
</Span>
<Span color="Comment" ruleSet="CommentMarkerSet" multiline="true">
<Begin>/\*</Begin>
<End>\*/</End>
</Span>
<Span color="String">
<Begin>"</Begin>
<End>"</End>
<RuleSet>
<!-- span for escape sequences -->
<Span begin="\\" end="."/>
</RuleSet>
</Span>
<Span color="Char">
<Begin>'</Begin>
<End>'</End>
<RuleSet>
<!-- span for escape sequences -->
<Span begin="\\" end="."/>
</RuleSet>
</Span>
<Span color="String" multiline="true">
<Begin color="String">@"</Begin>
<End>"</End>
<RuleSet>
<!-- span for escape sequences -->
<Span begin='""' end=""/>
</RuleSet>
</Span>
<Span color="String">
<Begin>\$"</Begin>
<End>"</End>
<RuleSet>
<!-- span for escape sequences -->
<Span begin="\\" end="."/>
<Span begin="\{\{" end=""/>
<!-- string interpolation -->
<Span begin="{" end="}" color="StringInterpolation" ruleSet=""/>
</RuleSet>
</Span>
<!-- Digits -->
<Rule color="NumberLiteral">
\b0[xX][0-9a-fA-F]+ # hex number
|
( \b\d+(\.[0-9]+)? #number with optional floating point
| \.[0-9]+ #or just starting with floating point
)
([eE][+-]?[0-9]+)? # optional exponent
</Rule>
</RuleSet>
</SyntaxDefinition>

125
ILSpy/TextView/DecompilerTabPageModel.cs

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
// Copyright (c) 2026 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.Threading;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using ICSharpCode.Decompiler;
using ILSpy.Languages;
using ILSpy.TreeNodes;
using ILSpy.ViewModels;
namespace ILSpy.TextView
{
/// <summary>
/// A document tab that hosts decompiled output for a single tree node. Re-decompiles when
/// <see cref="CurrentNode"/> changes; previous in-flight decompilations are cancelled so a
/// rapid tree-selection sweep doesn't pile up background work.
/// </summary>
public sealed partial class DecompilerTabPageModel : TabPageModel
{
CancellationTokenSource? activeCts;
[ObservableProperty]
private string text = string.Empty;
/// <summary>
/// File extension driving syntax highlighting (e.g. ".cs"). Updated alongside <see cref="Text"/>.
/// </summary>
[ObservableProperty]
private string syntaxExtension = ".cs";
ILSpyTreeNode? currentNode;
public ILSpyTreeNode? CurrentNode {
get => currentNode;
set {
if (currentNode == value)
return;
currentNode = value;
_ = DecompileAsync();
}
}
public DecompilerTabPageModel()
{
Title = "Empty";
}
public Language Language { get; set; } = null!;
async Task DecompileAsync()
{
activeCts?.Cancel();
var cts = activeCts = new CancellationTokenSource();
var node = currentNode;
var language = Language;
if (node == null || language == null)
{
Text = string.Empty;
return;
}
Title = node.Text?.ToString() ?? "(unnamed)";
Text = "Decompiling…";
SyntaxExtension = language.FileExtension;
try
{
var (output, _) = await Task.Run(() => {
var output = new AvaloniaEditTextOutput();
var options = new DecompilationOptions { CancellationToken = cts.Token };
try
{
node.Decompile(language, output, options);
}
catch (OperationCanceledException)
{
// expected on cancel — just return whatever we got
}
catch (Exception ex)
{
output.WriteLine();
output.WriteLine("/* Decompilation failed:");
output.WriteLine(ex.ToString());
output.WriteLine("*/");
}
return (output, cts.Token);
}, cts.Token).ConfigureAwait(true);
if (cts.Token.IsCancellationRequested)
return;
var rendered = output.GetText();
await Dispatcher.UIThread.InvokeAsync(() => {
Text = rendered;
});
}
catch (OperationCanceledException)
{
// stale request — drop silently
}
}
}
}

16
ILSpy/TextView/DecompilerTextView.axaml

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ae="using:AvaloniaEdit"
xmlns:textView="using:ILSpy.TextView"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="400"
x:Class="ILSpy.TextView.DecompilerTextView"
x:DataType="textView:DecompilerTabPageModel">
<ae:TextEditor Name="Editor"
IsReadOnly="True"
ShowLineNumbers="True"
FontFamily="Consolas, Menlo, Monospace"
FontSize="13" />
</UserControl>

59
ILSpy/TextView/DecompilerTextView.axaml.cs

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
// Copyright (c) 2026 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.ComponentModel;
using Avalonia.Controls;
namespace ILSpy.TextView
{
public partial class DecompilerTextView : UserControl
{
public DecompilerTextView()
{
InitializeComponent();
}
protected override void OnDataContextChanged(System.EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is DecompilerTabPageModel model)
{
model.PropertyChanged += OnModelPropertyChanged;
ApplyDocument(model);
}
}
void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is DecompilerTabPageModel model
&& (e.PropertyName == nameof(DecompilerTabPageModel.Text)
|| e.PropertyName == nameof(DecompilerTabPageModel.SyntaxExtension)))
{
ApplyDocument(model);
}
}
void ApplyDocument(DecompilerTabPageModel model)
{
Editor.SyntaxHighlighting = HighlightingService.GetByExtension(model.SyntaxExtension);
Editor.Document.Text = model.Text;
Editor.ScrollToHome();
}
}
}

79
ILSpy/TextView/HighlightingService.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
// Copyright (c) 2026 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.IO;
using System.Xml;
using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Highlighting.Xshd;
namespace ILSpy.TextView
{
/// <summary>
/// Loads our XSHD highlighting definitions (CSharp / IL / Asm / XML) from embedded resources
/// once and registers them with AvaloniaEdit's <see cref="HighlightingManager"/>. Lookup by
/// file extension (".cs" / ".il" / ".asm" / ".xml") matches what <see cref="Languages.Language.FileExtension"/>
/// returns.
/// </summary>
static class HighlightingService
{
static bool registered;
static readonly object gate = new();
// Embedded resource names follow the csproj RootNamespace ("ICSharpCode.ILSpy"),
// not the file's C# namespace ("ILSpy").
const string ResourcePrefix = "ICSharpCode.ILSpy.TextView.";
public static void EnsureRegistered()
{
if (registered)
return;
lock (gate)
{
if (registered)
return;
Register("C#", new[] { ".cs" }, "CSharp-Mode.xshd");
Register("IL", new[] { ".il" }, "ILAsm-Mode.xshd");
Register("Asm", new[] { ".asm" }, "Asm-Mode.xshd");
Register("XML", new[] { ".xml", ".xaml" }, "XML-Mode.xshd");
registered = true;
}
}
public static IHighlightingDefinition? GetByExtension(string fileExtension)
{
EnsureRegistered();
return HighlightingManager.Instance.GetDefinitionByExtension(fileExtension);
}
static void Register(string name, string[] extensions, string resourceName)
{
HighlightingManager.Instance.RegisterHighlighting(name, extensions, () => Load(resourceName));
}
static IHighlightingDefinition Load(string resourceName)
{
using var stream = typeof(HighlightingService).Assembly
.GetManifestResourceStream(ResourcePrefix + resourceName)
?? throw new InvalidOperationException($"Highlighting resource not found: {resourceName}");
using var reader = XmlReader.Create(stream);
return HighlightingLoader.Load(reader, HighlightingManager.Instance);
}
}
}

540
ILSpy/TextView/ILAsm-Mode.xshd

@ -0,0 +1,540 @@ @@ -0,0 +1,540 @@
<SyntaxDefinition name="ILAsm" extensions=".il" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<Color name="Comment" foreground="Green" exampleText="// comment" />
<Color name="String" foreground="Magenta" exampleText="&quot;Hello, World!&quot;" />
<Color name="Instructions" foreground="Blue" exampleText="nop;" />
<Color name="Keywords" foreground="Blue" fontWeight="bold" exampleText="true" />
<Color name="Directives" foreground="Green" fontWeight="bold" exampleText=".class" />
<Color name="Security" foreground="Red" exampleText="request" />
<Color name="Label" exampleText="IL_0000:" />
<RuleSet ignoreCase="false">
<Keywords color="Instructions">
<Word>nop</Word>
<Word>break</Word>
<Word>ldarg.0</Word>
<Word>ldarg.1</Word>
<Word>ldarg.2</Word>
<Word>ldarg.3</Word>
<Word>ldloc.0</Word>
<Word>ldloc.1</Word>
<Word>ldloc.2</Word>
<Word>ldloc.3</Word>
<Word>stloc.0</Word>
<Word>stloc.1</Word>
<Word>stloc.2</Word>
<Word>stloc.3</Word>
<Word>ldarg.s</Word>
<Word>ldarga.s</Word>
<Word>starg.s</Word>
<Word>ldloc.s</Word>
<Word>ldloca.s</Word>
<Word>stloc.s</Word>
<Word>ldnull</Word>
<Word>ldc.i4.m1</Word>
<Word>ldc.i4.0</Word>
<Word>ldc.i4.1</Word>
<Word>ldc.i4.2</Word>
<Word>ldc.i4.3</Word>
<Word>ldc.i4.4</Word>
<Word>ldc.i4.5</Word>
<Word>ldc.i4.6</Word>
<Word>ldc.i4.7</Word>
<Word>ldc.i4.8</Word>
<Word>ldc.i4.s</Word>
<Word>ldc.i4</Word>
<Word>ldc.i8</Word>
<Word>ldc.r4</Word>
<Word>ldc.r8</Word>
<Word>dup</Word>
<Word>pop</Word>
<Word>jmp</Word>
<Word>call</Word>
<Word>calli</Word>
<Word>ret</Word>
<Word>br.s</Word>
<Word>brfalse.s</Word>
<Word>brtrue.s</Word>
<Word>beq.s</Word>
<Word>bge.s</Word>
<Word>bgt.s</Word>
<Word>ble.s</Word>
<Word>blt.s</Word>
<Word>bne.un.s</Word>
<Word>bge.un.s</Word>
<Word>bgt.un.s</Word>
<Word>ble.un.s</Word>
<Word>blt.un.s</Word>
<Word>br</Word>
<Word>brfalse</Word>
<Word>brtrue</Word>
<Word>beq</Word>
<Word>bge</Word>
<Word>bgt</Word>
<Word>ble</Word>
<Word>blt</Word>
<Word>bne.un</Word>
<Word>bge.un</Word>
<Word>bgt.un</Word>
<Word>ble.un</Word>
<Word>blt.un</Word>
<Word>switch</Word>
<Word>ldind.i1</Word>
<Word>ldind.u1</Word>
<Word>ldind.i2</Word>
<Word>ldind.u2</Word>
<Word>ldind.i4</Word>
<Word>ldind.u4</Word>
<Word>ldind.i8</Word>
<Word>ldind.i</Word>
<Word>ldind.r4</Word>
<Word>ldind.r8</Word>
<Word>ldind.ref</Word>
<Word>stind.ref</Word>
<Word>stind.i1</Word>
<Word>stind.i2</Word>
<Word>stind.i4</Word>
<Word>stind.i8</Word>
<Word>stind.r4</Word>
<Word>stind.r8</Word>
<Word>add</Word>
<Word>sub</Word>
<Word>mul</Word>
<Word>div</Word>
<Word>div.un</Word>
<Word>rem</Word>
<Word>rem.un</Word>
<Word>and</Word>
<Word>or</Word>
<Word>xor</Word>
<Word>shl</Word>
<Word>shr</Word>
<Word>shr.un</Word>
<Word>neg</Word>
<Word>not</Word>
<Word>conv.i1</Word>
<Word>conv.i2</Word>
<Word>conv.i4</Word>
<Word>conv.i8</Word>
<Word>conv.r4</Word>
<Word>conv.r8</Word>
<Word>conv.u4</Word>
<Word>conv.u8</Word>
<Word>callvirt</Word>
<Word>cpobj</Word>
<Word>ldobj</Word>
<Word>ldstr</Word>
<Word>newobj</Word>
<Word>castclass</Word>
<Word>isinst</Word>
<Word>conv.r.un</Word>
<Word>unbox</Word>
<Word>throw</Word>
<Word>ldfld</Word>
<Word>ldflda</Word>
<Word>stfld</Word>
<Word>ldsfld</Word>
<Word>ldsflda</Word>
<Word>stsfld</Word>
<Word>stobj</Word>
<Word>conv.ovf.i1.un</Word>
<Word>conv.ovf.i2.un</Word>
<Word>conv.ovf.i4.un</Word>
<Word>conv.ovf.i8.un</Word>
<Word>conv.ovf.u1.un</Word>
<Word>conv.ovf.u2.un</Word>
<Word>conv.ovf.u4.un</Word>
<Word>conv.ovf.u8.un</Word>
<Word>conv.ovf.i.un</Word>
<Word>conv.ovf.u.un</Word>
<Word>box</Word>
<Word>newarr</Word>
<Word>ldlen</Word>
<Word>ldelema</Word>
<Word>ldelem</Word>
<Word>ldelem.i1</Word>
<Word>ldelem.u1</Word>
<Word>ldelem.i2</Word>
<Word>ldelem.u2</Word>
<Word>ldelem.i4</Word>
<Word>ldelem.u4</Word>
<Word>ldelem.i8</Word>
<Word>ldelem.i</Word>
<Word>ldelem.r4</Word>
<Word>ldelem.r8</Word>
<Word>ldelem.ref</Word>
<Word>stelem</Word>
<Word>stelem.i</Word>
<Word>stelem.i1</Word>
<Word>stelem.i2</Word>
<Word>stelem.i4</Word>
<Word>stelem.i8</Word>
<Word>stelem.r4</Word>
<Word>stelem.r8</Word>
<Word>stelem.ref</Word>
<Word>conv.ovf.i1</Word>
<Word>conv.ovf.u1</Word>
<Word>conv.ovf.i2</Word>
<Word>conv.ovf.u2</Word>
<Word>conv.ovf.i4</Word>
<Word>conv.ovf.u4</Word>
<Word>conv.ovf.i8</Word>
<Word>conv.ovf.u8</Word>
<Word>refanyval</Word>
<Word>ckfinite</Word>
<Word>mkrefany</Word>
<Word>ldtoken</Word>
<Word>conv.u2</Word>
<Word>conv.u1</Word>
<Word>conv.i</Word>
<Word>conv.ovf.i</Word>
<Word>conv.ovf.u</Word>
<Word>add.ovf</Word>
<Word>add.ovf.un</Word>
<Word>mul.ovf</Word>
<Word>mul.ovf.un</Word>
<Word>sub.ovf</Word>
<Word>sub.ovf.un</Word>
<Word>endfinally</Word>
<Word>leave</Word>
<Word>leave.s</Word>
<Word>stind.i</Word>
<Word>conv.u</Word>
<Word>prefix7</Word>
<Word>prefix6</Word>
<Word>prefix5</Word>
<Word>prefix4</Word>
<Word>prefix3</Word>
<Word>prefix2</Word>
<Word>prefix1</Word>
<Word>prefixref</Word>
<Word>arglist</Word>
<Word>ceq</Word>
<Word>cgt</Word>
<Word>cgt.un</Word>
<Word>clt</Word>
<Word>clt.un</Word>
<Word>ldftn</Word>
<Word>ldvirtftn</Word>
<Word>ldarg</Word>
<Word>ldarga</Word>
<Word>starg</Word>
<Word>ldloc</Word>
<Word>ldloca</Word>
<Word>stloc</Word>
<Word>localloc</Word>
<Word>endfilter</Word>
<Word>unaligned.</Word>
<Word>volatile.</Word>
<Word>tail.</Word>
<Word>initobj</Word>
<Word>cpblk</Word>
<Word>initblk</Word>
<Word>rethrow</Word>
<Word>sizeof</Word>
<Word>refanytype</Word>
<Word>illegal</Word>
<Word>endmac</Word>
<Word>brnull</Word>
<Word>brnull.s</Word>
<Word>brzero</Word>
<Word>brzero.s</Word>
<Word>brinst</Word>
<Word>brinst.s</Word>
<Word>ldind.u8</Word>
<Word>ldelem.u8</Word>
<Word>ldc.i4.M1</Word>
<Word>endfault</Word>
</Keywords>
<Keywords color="Keywords">
<Word>void</Word>
<Word>bool</Word>
<Word>char</Word>
<Word>wchar</Word>
<Word>int</Word>
<Word>int8</Word>
<Word>int16</Word>
<Word>int32</Word>
<Word>int64</Word>
<Word>uint8</Word>
<Word>uint16</Word>
<Word>uint32</Word>
<Word>uint64</Word>
<Word>float</Word>
<Word>float32</Word>
<Word>float64</Word>
<Word>refany</Word>
<Word>typedref</Word>
<Word>object</Word>
<Word>string</Word>
<Word>native</Word>
<Word>unsigned</Word>
<Word>value</Word>
<Word>valuetype</Word>
<Word>class</Word>
<Word>const</Word>
<Word>vararg</Word>
<Word>default</Word>
<Word>stdcall</Word>
<Word>thiscall</Word>
<Word>fastcall</Word>
<Word>unmanaged</Word>
<Word>not_in_gc_heap</Word>
<Word>beforefieldinit</Word>
<Word>instance</Word>
<Word>filter</Word>
<Word>catch</Word>
<Word>static</Word>
<Word>public</Word>
<Word>private</Word>
<Word>synchronized</Word>
<Word>interface</Word>
<Word>extends</Word>
<Word>implements</Word>
<Word>handler</Word>
<Word>finally</Word>
<Word>fault</Word>
<Word>to</Word>
<Word>abstract</Word>
<Word>auto</Word>
<Word>sequential</Word>
<Word>explicit</Word>
<Word>wrapper</Word>
<Word>ansi</Word>
<Word>unicode</Word>
<Word>autochar</Word>
<Word>import</Word>
<Word>enum</Word>
<Word>virtual</Word>
<Word>notremotable</Word>
<Word>special</Word>
<Word>il</Word>
<Word>cil</Word>
<Word>optil</Word>
<Word>managed</Word>
<Word>preservesig</Word>
<Word>runtime</Word>
<Word>method</Word>
<Word>field</Word>
<Word>bytearray</Word>
<Word>final</Word>
<Word>sealed</Word>
<Word>specialname</Word>
<Word>family</Word>
<Word>assembly</Word>
<Word>famandassem</Word>
<Word>famorassem</Word>
<Word>privatescope</Word>
<Word>nested</Word>
<Word>hidebysig</Word>
<Word>newslot</Word>
<Word>rtspecialname</Word>
<Word>pinvokeimpl</Word>
<Word>unmanagedexp</Word>
<Word>reqsecobj</Word>
<Word>.ctor</Word>
<Word>.cctor</Word>
<Word>initonly</Word>
<Word>literal</Word>
<Word>notserialized</Word>
<Word>forwardref</Word>
<Word>internalcall</Word>
<Word>noinlining</Word>
<Word>aggressiveinlining</Word>
<Word>nomangle</Word>
<Word>lasterr</Word>
<Word>winapi</Word>
<Word>cdecl</Word>
<Word>stdcall</Word>
<Word>thiscall</Word>
<Word>fastcall</Word>
<Word>as</Word>
<Word>pinned</Word>
<Word>modreq</Word>
<Word>modopt</Word>
<Word>serializable</Word>
<Word>at</Word>
<Word>tls</Word>
<Word>true</Word>
<Word>false</Word>
<Word>strict</Word>
<Word>type</Word>
</Keywords>
<Keywords color="Directives">
<Word>.class</Word>
<Word>.namespace</Word>
<Word>.method</Word>
<Word>.field</Word>
<Word>.emitbyte</Word>
<Word>.try</Word>
<Word>.maxstack</Word>
<Word>.locals</Word>
<Word>.entrypoint</Word>
<Word>.zeroinit</Word>
<Word>.pdirect</Word>
<Word>.data</Word>
<Word>.event</Word>
<Word>.addon</Word>
<Word>.removeon</Word>
<Word>.fire</Word>
<Word>.other</Word>
<Word>protected</Word>
<Word>.property</Word>
<Word>.set</Word>
<Word>.get</Word>
<Word>default</Word>
<Word>.import</Word>
<Word>.permission</Word>
<Word>.permissionset</Word>
<Word>.line</Word>
<Word>.language</Word>
<Word>.interfaceimpl</Word>
<Word>#line</Word>
</Keywords>
<Keywords color="Security">
<Word>request</Word>
<Word>demand</Word>
<Word>assert</Word>
<Word>deny</Word>
<Word>permitonly</Word>
<Word>linkcheck</Word>
<Word>inheritcheck</Word>
<Word>reqmin</Word>
<Word>reqopt</Word>
<Word>reqrefuse</Word>
<Word>prejitgrant</Word>
<Word>prejitdeny</Word>
<Word>noncasdemand</Word>
<Word>noncaslinkdemand</Word>
<Word>noncasinheritance</Word>
</Keywords>
<Keywords color="Directives">
<!-- custom value specifier -->
<Word>.custom</Word>
<!-- IL method attribute -->
<Word>init</Word>
<!-- Class layout directives -->
<Word>.size</Word>
<Word>.pack</Word>
<!-- Manifest-related keywords -->
<Word>.file</Word>
<Word>nometadata</Word>
<Word>.hash</Word>
<Word>.assembly</Word>
<Word>implicitcom</Word>
<Word>noappdomain</Word>
<Word>noprocess</Word>
<Word>nomachine</Word>
<Word>.publickey</Word>
<Word>.publickeytoken</Word>
<Word>algorithm</Word>
<Word>.ver</Word>
<Word>.locale</Word>
<Word>extern</Word>
<Word>.export</Word>
<Word>.manifestres</Word>
<Word>.mresource</Word>
<Word>.localized</Word>
<!-- Field marshaling keywords -->
<Word>.module</Word>
<Word>marshal</Word>
<Word>custom</Word>
<Word>sysstring</Word>
<Word>fixed</Word>
<Word>variant</Word>
<Word>currency</Word>
<Word>syschar</Word>
<Word>decimal</Word>
<Word>date</Word>
<Word>bstr</Word>
<Word>tbstr</Word>
<Word>lpstr</Word>
<Word>lpwstr</Word>
<Word>lptstr</Word>
<Word>objectref</Word>
<Word>iunknown</Word>
<Word>idispatch</Word>
<Word>struct</Word>
<Word>safearray</Word>
<Word>byvalstr</Word>
<Word>lpvoid</Word>
<Word>any</Word>
<Word>array</Word>
<Word>lpstruct</Word>
<!-- VTable fixup keywords -->
<Word>.vtfixup</Word>
<Word>fromunmanaged</Word>
<Word>callmostderived</Word>
<Word>.vtentry</Word>
<!-- Parameter attributes -->
<Word>in</Word>
<Word>out</Word>
<Word>opt</Word>
<Word>lcid</Word>
<Word>retval</Word>
<Word>.param</Word>
<!-- Method implementations -->
<Word>.override</Word>
<Word>with</Word>
<!-- VariantType keywords -->
<Word>null</Word>
<Word>error</Word>
<Word>hresult</Word>
<Word>carray</Word>
<Word>userdefined</Word>
<Word>record</Word>
<Word>filetime</Word>
<Word>blob</Word>
<Word>stream</Word>
<Word>storage</Word>
<Word>streamed_object</Word>
<Word>stored_object</Word>
<Word>blob_object</Word>
<Word>cf</Word>
<Word>clsid</Word>
<Word>vector</Word>
<!-- Null reference keyword for InitOpt -->
<Word>nullref</Word>
<!-- Header flags keywords -->
<Word>.subsystem</Word>
<Word>.corflags</Word>
<Word>.stackreserve</Word>
<Word>alignment</Word>
<Word>.imagebase</Word>
</Keywords>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>//</Begin>
</Span>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>/\*</Begin>
<End>\*/</End>
</Span>
<Span color="String">
<Begin>"</Begin>
<End>"</End>
</Span>
<Span>
<Begin>'</Begin>
<End>'</End>
</Span>
<Rule color="Label">
^ \s* \w+ :
</Rule>
</RuleSet>
<RuleSet name="CommentMarkerSet" ignoreCase="false">
<Keywords foreground="#FFFF0000" fontWeight="bold">
<Word>TODO</Word>
<Word>FIXME</Word>
</Keywords>
<Keywords foreground="#EEE0E000" fontWeight="bold">
<Word>HACK</Word>
<Word>UNDONE</Word>
</Keywords>
</RuleSet>
</SyntaxDefinition>

63
ILSpy/TextView/XML-Mode.xshd

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
<SyntaxDefinition name="XML" extensions=".xml;.xsl;.xslt;.xsd;.manifest;.config;.addin;.xshd;.wxs;.wxi;.wxl;.proj;.csproj;.vbproj;.ilproj;.booproj;.build;.xfrm;.targets;.xaml;.xpt;.xft;.map;.wsdl;.disco;.ps1xml;.nuspec" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<Color foreground="Green" name="Comment" exampleText="&lt;!-- comment --&gt;" />
<Color foreground="Blue" name="CData" exampleText="&lt;![CDATA[data]]&gt;" />
<Color foreground="Blue" name="DocType" exampleText="&lt;!DOCTYPE rootElement&gt;" />
<Color foreground="Blue" name="XmlDeclaration" exampleText='&lt;?xml version="1.0"?&gt;' />
<Color foreground="DarkMagenta" name="XmlTag" exampleText='&lt;tag attribute="value" /&gt;' />
<Color foreground="Red" name="AttributeName" exampleText='&lt;tag attribute="value" /&gt;' />
<Color foreground="Blue" name="AttributeValue" exampleText='&lt;tag attribute="value" /&gt;' />
<Color foreground="Teal" name="Entity" exampleText="index.aspx?a=1&amp;amp;b=2" />
<Color foreground="Olive" name="BrokenEntity" exampleText="index.aspx?a=1&amp;b=2" />
<RuleSet>
<Span color="Comment" multiline="true">
<Begin>&lt;!--</Begin>
<End>--&gt;</End>
</Span>
<Span color="CData" multiline="true">
<Begin>&lt;!\[CDATA\[</Begin>
<End>]]&gt;</End>
</Span>
<Span color="DocType" multiline="true">
<Begin>&lt;!DOCTYPE</Begin>
<End>&gt;</End>
</Span>
<Span color="XmlDeclaration" multiline="true">
<Begin>&lt;\?</Begin>
<End>\?&gt;</End>
</Span>
<Span color="XmlTag" multiline="true">
<Begin>&lt;</Begin>
<End>&gt;</End>
<RuleSet>
<!-- Treat the position before '<' as end, as that's not a valid character
in attribute names and indicates the user forgot a closing quote. -->
<Span color="AttributeValue" multiline="true" ruleSet="EntitySet">
<Begin>"</Begin>
<End>"|(?=&lt;)</End>
</Span>
<Span color="AttributeValue" multiline="true" ruleSet="EntitySet">
<Begin>'</Begin>
<End>'|(?=&lt;)</End>
</Span>
<Rule color="AttributeName">[\d\w_\-\.]+(?=(\s*=))</Rule>
<Rule color="AttributeValue">=</Rule>
</RuleSet>
</Span>
<Import ruleSet="EntitySet"/>
</RuleSet>
<RuleSet name="EntitySet">
<Rule color="Entity">
&amp;
[\w\d\#]+
;
</Rule>
<Rule color="BrokenEntity">
&amp;
[\w\d\#]*
#missing ;
</Rule>
</RuleSet>
</SyntaxDefinition>

6
ILSpy/TreeNodes/EventTreeNode.cs

@ -18,9 +18,12 @@ @@ -18,9 +18,12 @@
using System;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
sealed class EventTreeNode : ILSpyTreeNode
@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes
Images.Images.GetOverlay(EventDefinition.Accessibility), EventDefinition.IsStatic);
public override bool ShowExpander => false;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
=> language.DecompileEvent(EventDefinition, output, options);
public override string ToString() => "Event " + EventDefinition.Name;
}
}

6
ILSpy/TreeNodes/FieldTreeNode.cs

@ -18,9 +18,12 @@ @@ -18,9 +18,12 @@
using System;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
sealed class FieldTreeNode : ILSpyTreeNode
@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes
Images.Images.GetOverlay(FieldDefinition.Accessibility), FieldDefinition.IsStatic);
public override bool ShowExpander => false;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
=> language.DecompileField(FieldDefinition, output, options);
public override string ToString() => "Field " + FieldDefinition.Name;
}
}

11
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpyX.TreeView;
using ILSpy.AppEnv;
@ -35,5 +36,15 @@ namespace ILSpy.TreeNodes @@ -35,5 +36,15 @@ namespace ILSpy.TreeNodes
=> cachedLanguageService ??= AppComposition.Current.GetExport<LanguageService>();
public Language Language => LanguageService.CurrentLanguage;
/// <summary>
/// Renders this node's decompiled representation to <paramref name="output"/> using
/// <paramref name="language"/>. Default writes a stub comment so any node we forgot to
/// override still produces *something*.
/// </summary>
public virtual void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, Text?.ToString() ?? GetType().Name);
}
}
}

6
ILSpy/TreeNodes/MethodTreeNode.cs

@ -18,9 +18,12 @@ @@ -18,9 +18,12 @@
using System;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
sealed class MethodTreeNode : ILSpyTreeNode
@ -48,6 +51,9 @@ namespace ILSpy.TreeNodes @@ -48,6 +51,9 @@ namespace ILSpy.TreeNodes
public override bool ShowExpander => false;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
=> language.DecompileMethod(MethodDefinition, output, options);
// Stable identity for SessionSettings.ActiveTreeViewPath; matches the WPF host's format.
public override string ToString()
=> "Method " + new ICSharpCode.Decompiler.IL.ILAmbience {

18
ILSpy/TreeNodes/NamespaceTreeNode.cs

@ -19,7 +19,12 @@ @@ -19,7 +19,12 @@
using System;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
@ -55,5 +60,18 @@ namespace ILSpy.TreeNodes @@ -55,5 +60,18 @@ namespace ILSpy.TreeNodes
foreach (var t in types)
Children.Add(new TypeTreeNode(t, module));
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var typeSystem = module.GetTypeSystemOrNull();
if (typeSystem == null)
{
language.WriteCommentLine(output, "(type system unavailable)");
return;
}
var types = typeSystem.MainModule.TypeDefinitions
.Where(t => t.Namespace == name && t.DeclaringTypeDefinition == null);
language.DecompileNamespace(name, types, output, options);
}
}
}

6
ILSpy/TreeNodes/PropertyTreeNode.cs

@ -18,9 +18,12 @@ @@ -18,9 +18,12 @@
using System;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
sealed class PropertyTreeNode : ILSpyTreeNode
@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes
Images.Images.GetOverlay(PropertyDefinition.Accessibility), PropertyDefinition.IsStatic);
public override bool ShowExpander => false;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
=> language.DecompileProperty(PropertyDefinition, output, options);
public override string ToString()
=> "Property " + new ICSharpCode.Decompiler.IL.ILAmbience {
ConversionFlags = ConversionFlags.ShowTypeParameterList

11
ILSpy/TreeNodes/TypeTreeNode.cs

@ -26,6 +26,8 @@ using ICSharpCode.Decompiler.Output; @@ -26,6 +26,8 @@ using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ILSpy.Languages;
namespace ILSpy.TreeNodes
{
sealed class TypeTreeNode : ILSpyTreeNode
@ -71,6 +73,15 @@ namespace ILSpy.TreeNodes @@ -71,6 +73,15 @@ namespace ILSpy.TreeNodes
public override bool CanExpandRecursively => true;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var typeDef = ResolveTypeDefinition();
if (typeDef != null)
language.DecompileType(typeDef, output, options);
else
language.WriteCommentLine(output, "(could not resolve type)");
}
// Stable identity for SessionSettings.ActiveTreeViewPath. ReflectionName is
// language-independent.
public override string ToString()

Loading…
Cancel
Save