using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using ColorCode.Common;
using ColorCode.Parsing;
using ColorCode.Styling;
using ColorCode.Wpf.Common;
#nullable enable
namespace ColorCode.Wpf
{
///
/// Creates a , for rendering Syntax Highlighted code to a RichTextBlock.
///
public class RichTextBoxFormatter : CodeColorizerBase
{
///
/// Creates a , for rendering Syntax Highlighted code to a RichTextBlock.
///
/// The Custom styles to Apply to the formatted Code.
/// The language parser that the instance will use for its lifetime.
public RichTextBoxFormatter(StyleDictionary? style = null, ILanguageParser? languageParser = null) : base(style, languageParser)
{
}
///
/// Adds Syntax Highlighted Source Code to the provided RichTextBlock.
///
/// The source code to colorize.
/// The language to use to colorize the source code.
/// The Control to add the Text to.
public void FormatRichTextBox(string sourceCode, ILanguage language, RichTextBox richText)
{
var paragraph = new Paragraph();
richText.Document.Blocks.Add(paragraph);
FormatInlines(sourceCode, language, paragraph.Inlines);
}
///
/// Adds Syntax Highlighted Source Code to the provided InlineCollection.
///
/// The source code to colorize.
/// The language to use to colorize the source code.
/// InlineCollection to add the Text to.
public void FormatInlines(string sourceCode, ILanguage language, InlineCollection inlineCollection)
{
InlineCollection = inlineCollection;
languageParser.Parse(sourceCode, language, (parsedSourceCode, captures) => Write(parsedSourceCode, captures));
}
private InlineCollection? InlineCollection { get; set; }
protected override void Write(string parsedSourceCode, IList scopes)
{
var styleInsertions = new List();
foreach (Scope scope in scopes)
GetStyleInsertionsForCapturedStyle(scope, styleInsertions);
styleInsertions.SortStable((x, y) => x.Index.CompareTo(y.Index));
int offset = 0;
Scope? previousScope = null;
foreach (var styleinsertion in styleInsertions)
{
var text = parsedSourceCode.Substring(offset, styleinsertion.Index - offset);
CreateSpan(text, previousScope);
if (!string.IsNullOrWhiteSpace(styleinsertion.Text))
{
CreateSpan(text, previousScope);
}
offset = styleinsertion.Index;
previousScope = styleinsertion.Scope;
}
var remaining = parsedSourceCode.Substring(offset);
// Ensures that those loose carriages don't run away!
if (remaining != "\r")
{
CreateSpan(remaining, null);
}
}
private void CreateSpan(string text, Scope? scope)
{
var span = new Span();
var run = new Run
{
Text = text
};
// Styles and writes the text to the span.
if (scope != null) StyleRun(run, scope);
span.Inlines.Add(run);
InlineCollection?.Add(span);
}
private void StyleRun(Run run, Scope scope)
{
string? foreground = null;
string? background = null;
bool italic = false;
bool bold = false;
if (Styles.Contains(scope.Name))
{
Styling.Style style = Styles[scope.Name];
foreground = style.Foreground;
background = style.Background;
italic = style.Italic;
bold = style.Bold;
}
if (!string.IsNullOrWhiteSpace(foreground))
run.Foreground = foreground.GetSolidColorBrush();
//Background isn't supported, but a workaround could be created.
if (italic)
run.FontStyle = FontStyles.Italic;
if (bold)
run.FontWeight = FontWeights.Bold;
}
private void GetStyleInsertionsForCapturedStyle(Scope scope, ICollection styleInsertions)
{
styleInsertions.Add(new TextInsertion
{
Index = scope.Index,
Scope = scope
});
foreach (Scope childScope in scope.Children)
GetStyleInsertionsForCapturedStyle(childScope, styleInsertions);
styleInsertions.Add(new TextInsertion
{
Index = scope.Index + scope.Length
});
}
}
}