242 lines
8.4 KiB
C#
242 lines
8.4 KiB
C#
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
|
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
|
// All Rights Reserved.
|
|
|
|
// TODO: This class is work in progress.
|
|
//
|
|
using System;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Media;
|
|
|
|
using WPFluent.Appearance;
|
|
|
|
|
|
namespace WPFluent.Controls;
|
|
|
|
/// <summary>
|
|
/// Formats a string of code into <see cref="System.Windows.Controls.TextBox"/> control.
|
|
/// <para>Implementation and regex patterns inspired by <see href="https://github.com/antoniandre/simple-syntax-highlighter"/>.</para>
|
|
/// </summary>
|
|
internal static class Highlighter
|
|
{
|
|
private const string EndlinePattern = /* language=regex */
|
|
"(\n)";
|
|
|
|
private const string TabPattern = /* language=regex */
|
|
"(\t)";
|
|
|
|
private const string QuotePattern = /* language=regex */
|
|
"(\"(?:\\\"|[^\"])*\")|('(?:\\'|[^'])*')";
|
|
|
|
private const string CommentPattern = /* language=regex */
|
|
@"(\/\/.*?(?:\n|$)|\/\*.*?\*\/)";
|
|
|
|
private const string TagPattern = /* language=regex */
|
|
@"(<\/?)([a-zA-Z\-:]+)(.*?)(\/?>)";
|
|
|
|
private const string EntityPattern = /* language=regex */
|
|
@"(&[a-zA-Z0-9#]+;)";
|
|
|
|
// private const string PunctuationPattern = /* language=regex */
|
|
// @"(!==?|(?:[[\\] ()\{\}.:;,+\\-?=!]|<|>)+|&&|\\|\\|)";
|
|
|
|
// private const string NumberPattern = /* language=regex */
|
|
// @"(-? (?:\.\d+|\d+(?:\.\d+)?))";
|
|
|
|
// private const string BooleanPattern = /* language=regex */
|
|
// "\b(true|false)\b";
|
|
|
|
// private const string AttributePattern = /* language=regex */
|
|
// "(\\s*)([a-zA-Z\\d\\-:]+)=(\" | ')(.*?)\\3";
|
|
////
|
|
|
|
public static Paragraph FormatAsParagraph(
|
|
string code,
|
|
SyntaxLanguage language = SyntaxLanguage.Autodetect
|
|
)
|
|
{
|
|
var paragraph = new Paragraph();
|
|
Regex rgx = new(GetPattern(language, code));
|
|
|
|
bool lightTheme = IsLightTheme();
|
|
|
|
foreach (Match match in rgx.Matches(code).Cast<Match>())
|
|
{
|
|
foreach (object group in match.Groups)
|
|
{
|
|
// Remove whole matches
|
|
if (group is Match)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Cast to group
|
|
Group codeMatched = (Group)group;
|
|
|
|
// Remove empty groups
|
|
if (string.IsNullOrEmpty(codeMatched.Value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (codeMatched.Value.Contains('\t'))
|
|
{
|
|
paragraph.Inlines.Add(Line(" ", Brushes.Transparent));
|
|
}
|
|
else if (codeMatched.Value.Contains("/*") || codeMatched.Value.Contains("//"))
|
|
{
|
|
paragraph.Inlines.Add(Line(codeMatched.Value, Brushes.Orange));
|
|
}
|
|
else if (codeMatched.Value.Contains('<') || codeMatched.Value.Contains('>'))
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(codeMatched.Value, lightTheme ? Brushes.DarkCyan : Brushes.CornflowerBlue)
|
|
);
|
|
}
|
|
else if (codeMatched.Value.Contains('"'))
|
|
{
|
|
string[] attributeArray = codeMatched.Value.Split('"');
|
|
attributeArray = attributeArray.Where(x => !string.IsNullOrEmpty(x.Trim())).ToArray();
|
|
|
|
if (attributeArray.Length % 2 == 0)
|
|
{
|
|
for (int i = 0; i < attributeArray.Length; i += 2)
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(
|
|
attributeArray[i],
|
|
lightTheme ? Brushes.DarkSlateGray : Brushes.WhiteSmoke
|
|
)
|
|
);
|
|
paragraph.Inlines.Add(
|
|
Line("\"", lightTheme ? Brushes.DarkCyan : Brushes.CornflowerBlue)
|
|
);
|
|
paragraph.Inlines.Add(Line(attributeArray[i + 1], Brushes.Coral));
|
|
paragraph.Inlines.Add(
|
|
Line("\"", lightTheme ? Brushes.DarkCyan : Brushes.CornflowerBlue)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(codeMatched.Value, lightTheme ? Brushes.DarkSlateGray : Brushes.WhiteSmoke)
|
|
);
|
|
}
|
|
}
|
|
else if (codeMatched.Value.Contains('\''))
|
|
{
|
|
string[] attributeArray = codeMatched.Value.Split('\'');
|
|
attributeArray = attributeArray.Where(x => !string.IsNullOrEmpty(x.Trim())).ToArray();
|
|
|
|
if (attributeArray.Length % 2 == 0)
|
|
{
|
|
for (int i = 0; i < attributeArray.Length; i += 2)
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(
|
|
attributeArray[i],
|
|
lightTheme ? Brushes.DarkSlateGray : Brushes.WhiteSmoke
|
|
)
|
|
);
|
|
paragraph.Inlines.Add(
|
|
Line("'", lightTheme ? Brushes.DarkCyan : Brushes.CornflowerBlue)
|
|
);
|
|
paragraph.Inlines.Add(Line(attributeArray[i + 1], Brushes.Coral));
|
|
paragraph.Inlines.Add(
|
|
Line("'", lightTheme ? Brushes.DarkCyan : Brushes.CornflowerBlue)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(codeMatched.Value, lightTheme ? Brushes.DarkSlateGray : Brushes.WhiteSmoke)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
paragraph.Inlines.Add(
|
|
Line(codeMatched.Value, lightTheme ? Brushes.CornflowerBlue : Brushes.Aqua)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return paragraph;
|
|
}
|
|
|
|
public static string Clean(string code)
|
|
{
|
|
code = code.Replace(@"\n", "\n");
|
|
code = code.Replace(@"\t", "\t");
|
|
code = code.Replace("<", "<");
|
|
code = code.Replace(">", ">");
|
|
code = code.Replace("&", "&");
|
|
code = code.Replace(""", "\"");
|
|
code = code.Replace("'", "'");
|
|
|
|
return code;
|
|
}
|
|
|
|
private static Run Line(string line, SolidColorBrush brush)
|
|
{
|
|
return new Run(line) { Foreground = brush };
|
|
}
|
|
|
|
private static bool IsLightTheme()
|
|
{
|
|
return Appearance.ThemeManager.GetAppTheme() == ThemeType.Light;
|
|
}
|
|
|
|
/*
|
|
private static string GetPattern(SyntaxLanguage language)
|
|
{
|
|
return GetPattern(language, string.Empty);
|
|
}
|
|
*/
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
|
"Style",
|
|
"IDE0060:Remove unused parameter",
|
|
Justification = "WIP"
|
|
)]
|
|
private static string GetPattern(SyntaxLanguage language, string code)
|
|
{
|
|
var pattern = string.Empty;
|
|
|
|
// TODO: Auto detected
|
|
if (language == SyntaxLanguage.Autodetect)
|
|
{
|
|
language = SyntaxLanguage.XAML;
|
|
}
|
|
|
|
switch (language)
|
|
{
|
|
case SyntaxLanguage.CSHARP:
|
|
pattern += EndlinePattern;
|
|
pattern += "|" + TabPattern;
|
|
pattern += "|" + QuotePattern;
|
|
pattern += "|" + CommentPattern;
|
|
pattern += "|" + EntityPattern;
|
|
pattern += "|" + TagPattern;
|
|
break;
|
|
|
|
case SyntaxLanguage.XAML:
|
|
pattern += EndlinePattern;
|
|
pattern += "|" + TabPattern;
|
|
pattern += "|" + QuotePattern;
|
|
pattern += "|" + CommentPattern;
|
|
pattern += "|" + EntityPattern;
|
|
pattern += "|" + TagPattern;
|
|
break;
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
}
|