Browse Source

Semantic syntax highlighting via the WPF host's token writer

Wires up CSharpHighlightingTokenWriter so the decompiler view paints
keywords / types / members / parameters in distinct colours, identical
to the WPF host. Supporting changes:

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
4f1c13c86e
  1. 577
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  2. 5
      ILSpy/Languages/CSharpLanguage.cs
  3. 34
      ILSpy/TextView/AvaloniaEditTextOutput.cs
  4. 11
      ILSpy/TextView/DecompilerTabPageModel.cs
  5. 22
      ILSpy/TextView/DecompilerTextView.axaml.cs
  6. 39
      ILSpy/TextView/ISmartTextOutput.cs

577
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -0,0 +1,577 @@ @@ -0,0 +1,577 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using AvaloniaEdit.Highlighting;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX.Extensions;
using ILSpy.TextView;
namespace ILSpy.Languages
{
class CSharpHighlightingTokenWriter : DecoratingTokenWriter
{
HighlightingColor visibilityKeywordsColor;
HighlightingColor namespaceKeywordsColor;
HighlightingColor structureKeywordsColor;
HighlightingColor gotoKeywordsColor;
HighlightingColor queryKeywordsColor;
HighlightingColor exceptionKeywordsColor;
HighlightingColor checkedKeywordColor;
HighlightingColor unsafeKeywordsColor;
HighlightingColor valueTypeKeywordsColor;
HighlightingColor referenceTypeKeywordsColor;
HighlightingColor operatorKeywordsColor;
HighlightingColor parameterModifierColor;
HighlightingColor modifiersColor;
HighlightingColor accessorKeywordsColor;
HighlightingColor attributeKeywordsColor;
HighlightingColor referenceTypeColor;
HighlightingColor valueTypeColor;
HighlightingColor interfaceTypeColor;
HighlightingColor enumerationTypeColor;
HighlightingColor typeParameterTypeColor;
HighlightingColor delegateTypeColor;
HighlightingColor methodCallColor;
HighlightingColor methodDeclarationColor;
HighlightingColor fieldDeclarationColor;
HighlightingColor fieldAccessColor;
HighlightingColor propertyDeclarationColor;
HighlightingColor propertyAccessColor;
HighlightingColor eventDeclarationColor;
HighlightingColor eventAccessColor;
HighlightingColor variableColor;
HighlightingColor parameterColor;
HighlightingColor valueKeywordColor;
HighlightingColor thisKeywordColor;
HighlightingColor trueKeywordColor;
HighlightingColor typeKeywordsColor;
public RichTextModel HighlightingModel { get; } = new RichTextModel();
public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutput? textOutput = null, ILocatable? locatable = null)
: base(decoratedWriter)
{
var highlighting = HighlightingManager.Instance.GetDefinition("C#");
this.locatable = locatable;
this.textOutput = textOutput;
this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility");
this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords");
this.structureKeywordsColor = highlighting.GetNamedColor("Keywords");
this.gotoKeywordsColor = highlighting.GetNamedColor("GotoKeywords");
this.queryKeywordsColor = highlighting.GetNamedColor("QueryKeywords");
this.exceptionKeywordsColor = highlighting.GetNamedColor("ExceptionKeywords");
this.checkedKeywordColor = highlighting.GetNamedColor("CheckedKeyword");
this.unsafeKeywordsColor = highlighting.GetNamedColor("UnsafeKeywords");
this.valueTypeKeywordsColor = highlighting.GetNamedColor("ValueTypeKeywords");
this.referenceTypeKeywordsColor = highlighting.GetNamedColor("ReferenceTypeKeywords");
this.operatorKeywordsColor = highlighting.GetNamedColor("OperatorKeywords");
this.parameterModifierColor = highlighting.GetNamedColor("ParameterModifiers");
this.modifiersColor = highlighting.GetNamedColor("Modifiers");
this.accessorKeywordsColor = highlighting.GetNamedColor("GetSetAddRemove");
this.referenceTypeColor = highlighting.GetNamedColor("ReferenceTypes");
this.valueTypeColor = highlighting.GetNamedColor("ValueTypes");
this.interfaceTypeColor = highlighting.GetNamedColor("InterfaceTypes");
this.enumerationTypeColor = highlighting.GetNamedColor("EnumTypes");
this.typeParameterTypeColor = highlighting.GetNamedColor("TypeParameters");
this.delegateTypeColor = highlighting.GetNamedColor("DelegateTypes");
this.methodDeclarationColor = highlighting.GetNamedColor("MethodDeclaration");
this.methodCallColor = highlighting.GetNamedColor("MethodCall");
this.fieldDeclarationColor = highlighting.GetNamedColor("FieldDeclaration");
this.fieldAccessColor = highlighting.GetNamedColor("FieldAccess");
this.propertyDeclarationColor = highlighting.GetNamedColor("PropertyDeclaration");
this.propertyAccessColor = highlighting.GetNamedColor("PropertyAccess");
this.eventDeclarationColor = highlighting.GetNamedColor("EventDeclaration");
this.eventAccessColor = highlighting.GetNamedColor("EventAccess");
this.variableColor = highlighting.GetNamedColor("Variable");
this.parameterColor = highlighting.GetNamedColor("Parameter");
this.valueKeywordColor = highlighting.GetNamedColor("NullOrValueKeywords");
this.thisKeywordColor = highlighting.GetNamedColor("ThisOrBaseReference");
this.trueKeywordColor = highlighting.GetNamedColor("TrueFalse");
this.typeKeywordsColor = highlighting.GetNamedColor("TypeKeywords");
this.attributeKeywordsColor = highlighting.GetNamedColor("AttributeKeywords");
//this.externAliasKeywordColor = ...;
}
public override void WriteKeyword(Role role, string keyword)
{
HighlightingColor? color = null;
switch (keyword)
{
case "namespace":
case "using":
if (role == UsingStatement.UsingKeywordRole)
color = structureKeywordsColor;
else
color = namespaceKeywordsColor;
break;
case "this":
case "base":
color = thisKeywordColor;
break;
case "true":
case "false":
color = trueKeywordColor;
break;
case "public":
case "internal":
case "protected":
case "private":
color = visibilityKeywordsColor;
break;
case "if":
case "else":
case "switch":
case "case":
case "default":
case "while":
case "do":
case "for":
case "foreach":
case "lock":
case "await":
color = structureKeywordsColor;
break;
case "where":
if (nodeStack.PeekOrDefault() is QueryClause)
color = queryKeywordsColor;
else
color = structureKeywordsColor;
break;
case "in":
if (nodeStack.PeekOrDefault() is ForeachStatement)
color = structureKeywordsColor;
else if (nodeStack.PeekOrDefault() is QueryClause)
color = queryKeywordsColor;
else
color = parameterModifierColor;
break;
case "as":
case "is":
case "new":
case "sizeof":
case "typeof":
case "nameof":
case "stackalloc":
color = typeKeywordsColor;
break;
case "with":
if (role == WithInitializerExpression.WithKeywordRole)
color = typeKeywordsColor;
break;
case "try":
case "throw":
case "catch":
case "finally":
color = exceptionKeywordsColor;
break;
case "when":
if (role == CatchClause.WhenKeywordRole)
color = exceptionKeywordsColor;
break;
case "get":
case "set":
case "add":
case "remove":
case "init":
if (role == PropertyDeclaration.GetKeywordRole ||
role == PropertyDeclaration.SetKeywordRole ||
role == PropertyDeclaration.InitKeywordRole ||
role == CustomEventDeclaration.AddKeywordRole ||
role == CustomEventDeclaration.RemoveKeywordRole)
color = accessorKeywordsColor;
break;
case "abstract":
case "const":
case "event":
case "extern":
case "override":
case "sealed":
case "static":
case "virtual":
case "volatile":
case "async":
case "partial":
color = modifiersColor;
break;
case "readonly":
if (role == ComposedType.ReadonlyRole)
color = parameterModifierColor;
else
color = modifiersColor;
break;
case "checked":
case "unchecked":
color = checkedKeywordColor;
break;
case "fixed":
case "unsafe":
color = unsafeKeywordsColor;
break;
case "enum":
case "struct":
color = valueTypeKeywordsColor;
break;
case "class":
case "interface":
case "delegate":
case "extension":
color = referenceTypeKeywordsColor;
break;
case "record":
color = role == Roles.RecordKeyword ? referenceTypeKeywordsColor : valueTypeKeywordsColor;
break;
case "select":
case "group":
case "by":
case "into":
case "from":
case "orderby":
case "let":
case "join":
case "on":
case "equals":
if (nodeStack.PeekOrDefault() is QueryClause)
color = queryKeywordsColor;
break;
case "ascending":
case "descending":
if (nodeStack.PeekOrDefault() is QueryOrdering)
color = queryKeywordsColor;
break;
case "explicit":
case "implicit":
case "operator":
color = operatorKeywordsColor;
break;
case "params":
case "ref":
case "out":
case "scoped":
color = parameterModifierColor;
break;
case "break":
case "continue":
case "goto":
case "yield":
case "return":
color = gotoKeywordsColor;
break;
}
if (nodeStack.PeekOrDefault() is AttributeSection)
color = attributeKeywordsColor;
if (color != null)
{
BeginSpan(color);
}
base.WriteKeyword(role, keyword);
if (color != null)
{
EndSpan();
}
}
public override void WritePrimitiveType(string type)
{
HighlightingColor? color = null;
switch (type)
{
case "new":
case "notnull":
// Not sure if reference type or value type
color = referenceTypeKeywordsColor;
break;
case "bool":
case "byte":
case "char":
case "decimal":
case "double":
case "enum":
case "float":
case "int":
case "long":
case "sbyte":
case "short":
case "struct":
case "uint":
case "ushort":
case "ulong":
case "unmanaged":
case "nint":
case "nuint":
color = valueTypeKeywordsColor;
break;
case "class":
case "object":
case "string":
case "void":
case "dynamic":
color = referenceTypeKeywordsColor;
break;
}
if (color != null)
{
BeginSpan(color);
}
base.WritePrimitiveType(type);
if (color != null)
{
EndSpan();
}
}
public override void WriteIdentifier(Identifier identifier)
{
HighlightingColor? color = null;
if (identifier.Parent?.GetResolveResult() is ILVariableResolveResult rr)
{
if (rr.Variable.Kind == VariableKind.Parameter)
{
if (identifier.Name == "value"
&& identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is { } accessor
&& accessor.Role != PropertyDeclaration.GetterRole)
{
color = valueKeywordColor;
}
else
{
color = parameterColor;
}
}
else
{
color = variableColor;
}
}
if (identifier.Parent is AstType)
{
switch (identifier.Name)
{
case "var":
color = queryKeywordsColor;
break;
case "global":
color = structureKeywordsColor;
break;
}
}
switch (GetCurrentDefinition())
{
case ITypeDefinition t:
ApplyTypeColor(t, ref color);
break;
case IMethod:
color = methodDeclarationColor;
break;
case IField:
color = fieldDeclarationColor;
break;
case IProperty:
color = propertyDeclarationColor;
break;
case IEvent:
color = eventDeclarationColor;
break;
}
switch (GetCurrentMemberReference())
{
case IType t:
ApplyTypeColor(t, ref color);
break;
case IMethod m:
color = methodCallColor;
if (m.IsConstructor)
ApplyTypeColor(m.DeclaringType, ref color);
break;
case IField:
color = fieldAccessColor;
break;
case IProperty:
color = propertyAccessColor;
break;
case IEvent:
color = eventAccessColor;
break;
}
if (color != null)
{
BeginSpan(color);
}
base.WriteIdentifier(identifier);
if (color != null)
{
EndSpan();
}
}
void ApplyTypeColor(IType type, ref HighlightingColor? color)
{
switch (type?.Kind)
{
case TypeKind.Delegate:
color = delegateTypeColor;
break;
case TypeKind.Class:
color = referenceTypeColor;
break;
case TypeKind.Interface:
color = interfaceTypeColor;
break;
case TypeKind.Enum:
color = enumerationTypeColor;
break;
case TypeKind.Struct:
color = valueTypeColor;
break;
}
}
public override void WritePrimitiveValue(object value, ICSharpCode.Decompiler.CSharp.Syntax.LiteralFormat format)
{
HighlightingColor? color = null;
if (value is null)
{
color = valueKeywordColor;
}
if (value is true || value is false)
{
color = trueKeywordColor;
}
if (color != null)
{
BeginSpan(color);
}
base.WritePrimitiveValue(value, format);
if (color != null)
{
EndSpan();
}
}
ISymbol? GetCurrentDefinition()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
var node = nodeStack.Peek();
if (node is Identifier)
node = node.Parent;
if (ICSharpCode.Decompiler.TextTokenWriter.IsDefinition(ref node))
return node.GetSymbol();
return null;
}
ISymbol? GetCurrentMemberReference()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
AstNode node = nodeStack.Peek();
var symbol = node.GetSymbol();
if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression)
{
symbol = node.Parent.GetSymbol();
}
if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression)
{
var ctorSymbol = node.Parent.GetSymbol();
if (ctorSymbol != null)
symbol = ctorSymbol;
}
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member)
{
var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
return null;
}
return symbol;
}
readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
public override void StartNode(AstNode node)
{
nodeStack.Push(node);
base.StartNode(node);
}
public override void EndNode(AstNode node)
{
base.EndNode(node);
nodeStack.Pop();
}
readonly Stack<HighlightingColor> colorStack = new Stack<HighlightingColor>();
HighlightingColor currentColor = new HighlightingColor();
int currentColorBegin = -1;
readonly ILocatable? locatable;
readonly ISmartTextOutput? textOutput;
private void BeginSpan(HighlightingColor highlightingColor)
{
if (textOutput != null)
{
textOutput.BeginSpan(highlightingColor);
return;
}
// At least one of textOutput / locatable is supplied to the constructor;
// the early-return above handles the textOutput path, so locatable is non-null here.
Debug.Assert(locatable != null);
if (currentColorBegin > -1)
HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
colorStack.Push(currentColor);
currentColor = currentColor.Clone();
currentColorBegin = locatable.Length;
currentColor.MergeWith(highlightingColor);
currentColor.Freeze();
}
private void EndSpan()
{
if (textOutput != null)
{
textOutput.EndSpan();
return;
}
// See BeginSpan: locatable is the non-null branch when textOutput is unset.
Debug.Assert(locatable != null);
HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
currentColor = colorStack.Pop();
currentColorBegin = locatable.Length;
}
}
}

5
ILSpy/Languages/CSharpLanguage.cs

@ -139,6 +139,11 @@ namespace ILSpy.Languages @@ -139,6 +139,11 @@ namespace ILSpy.Languages
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
output.IndentationString = settings.CSharpFormattingOptions.IndentationString;
TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem);
// Wrap with semantic highlighting when the output supports it. The writer emits
// BeginSpan/EndSpan around tokens, which AvaloniaEditTextOutput records into a
// RichTextModel that the view's RichTextColorizer paints.
if (output is TextView.ISmartTextOutput smartOutput)
tokenWriter = new CSharpHighlightingTokenWriter(tokenWriter, smartOutput);
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions));
}
}

34
ILSpy/TextView/AvaloniaEditTextOutput.cs

@ -16,9 +16,12 @@ @@ -16,9 +16,12 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Text;
using AvaloniaEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
@ -27,17 +30,23 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -27,17 +30,23 @@ 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.
/// Avalonia-side <see cref="ITextOutput"/> + <see cref="ISmartTextOutput"/>: accumulates text
/// into a <see cref="StringBuilder"/> and tracks <c>BeginSpan</c>/<c>EndSpan</c> pairs into a
/// <see cref="RichTextModel"/> that the text view renders via <c>RichTextColorizer</c>.
/// Reference markers, fold ranges, and inline UI elements are intentionally dropped here —
/// those land when we add hyperlinks and folding support.
/// </summary>
sealed class AvaloniaEditTextOutput : ITextOutput
sealed class AvaloniaEditTextOutput : ISmartTextOutput
{
readonly StringBuilder builder = new();
readonly Stack<(int Offset, HighlightingColor Color)> openSpans = new();
int indent;
bool needsIndent;
public RichTextModel HighlightingModel { get; } = new RichTextModel();
public string Title { get; set; } = string.Empty;
public string IndentationString { get; set; } = "\t";
public int TextLength => builder.Length;
@ -102,5 +111,20 @@ namespace ILSpy.TextView @@ -102,5 +111,20 @@ namespace ILSpy.TextView
{
// Folding support arrives in a later phase.
}
public void BeginSpan(HighlightingColor highlightingColor)
{
openSpans.Push((builder.Length, highlightingColor));
}
public void EndSpan()
{
if (openSpans.Count == 0)
return;
var (start, color) = openSpans.Pop();
var length = builder.Length - start;
if (length > 0)
HighlightingModel.SetHighlighting(start, length, color);
}
}
}

11
ILSpy/TextView/DecompilerTabPageModel.cs

@ -22,6 +22,8 @@ using System.Threading.Tasks; @@ -22,6 +22,8 @@ using System.Threading.Tasks;
using Avalonia.Threading;
using AvaloniaEdit.Highlighting;
using CommunityToolkit.Mvvm.ComponentModel;
using ICSharpCode.Decompiler;
@ -50,6 +52,13 @@ namespace ILSpy.TextView @@ -50,6 +52,13 @@ namespace ILSpy.TextView
[ObservableProperty]
private string syntaxExtension = ".cs";
/// <summary>
/// Semantic-highlighting spans collected during decompilation; the view feeds this into
/// AvaloniaEdit's <c>RichTextColorizer</c>.
/// </summary>
[ObservableProperty]
private RichTextModel? highlightingModel;
ILSpyTreeNode? currentNode;
public ILSpyTreeNode? CurrentNode {
@ -112,7 +121,9 @@ namespace ILSpy.TextView @@ -112,7 +121,9 @@ namespace ILSpy.TextView
return;
var rendered = output.GetText();
var model = output.HighlightingModel;
await Dispatcher.UIThread.InvokeAsync(() => {
HighlightingModel = model;
Text = rendered;
});
}

22
ILSpy/TextView/DecompilerTextView.axaml.cs

@ -20,10 +20,14 @@ using System.ComponentModel; @@ -20,10 +20,14 @@ using System.ComponentModel;
using Avalonia.Controls;
using AvaloniaEdit.Highlighting;
namespace ILSpy.TextView
{
public partial class DecompilerTextView : UserControl
{
RichTextColorizer? activeColorizer;
public DecompilerTextView()
{
InitializeComponent();
@ -43,7 +47,8 @@ namespace ILSpy.TextView @@ -43,7 +47,8 @@ namespace ILSpy.TextView
{
if (sender is DecompilerTabPageModel model
&& (e.PropertyName == nameof(DecompilerTabPageModel.Text)
|| e.PropertyName == nameof(DecompilerTabPageModel.SyntaxExtension)))
|| e.PropertyName == nameof(DecompilerTabPageModel.SyntaxExtension)
|| e.PropertyName == nameof(DecompilerTabPageModel.HighlightingModel)))
{
ApplyDocument(model);
}
@ -53,6 +58,21 @@ namespace ILSpy.TextView @@ -53,6 +58,21 @@ namespace ILSpy.TextView
{
Editor.SyntaxHighlighting = HighlightingService.GetByExtension(model.SyntaxExtension);
Editor.Document.Text = model.Text;
// Swap the semantic-highlighting colorizer. AvaloniaEdit only exposes Add/Remove on
// LineTransformers, so we keep a reference to the previous one.
var transformers = Editor.TextArea.TextView.LineTransformers;
if (activeColorizer != null)
{
transformers.Remove(activeColorizer);
activeColorizer = null;
}
if (model.HighlightingModel is { } richModel)
{
activeColorizer = new RichTextColorizer(richModel);
transformers.Add(activeColorizer);
}
Editor.ScrollToHome();
}
}

39
ILSpy/TextView/ISmartTextOutput.cs

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
// 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 AvaloniaEdit.Highlighting;
using ICSharpCode.Decompiler;
namespace ILSpy.TextView
{
/// <summary>
/// Adds Avalonia-specific output features to <see cref="ITextOutput"/> — currently just
/// span-based highlighting. Inline UI elements are deferred to a later phase.
/// </summary>
public interface ISmartTextOutput : ITextOutput
{
void BeginSpan(HighlightingColor highlightingColor);
void EndSpan();
/// <summary>
/// Title displayed in the document tab's header.
/// </summary>
string Title { get; set; }
}
}
Loading…
Cancel
Save