1092 lines
30 KiB
C#
1092 lines
30 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using Microsoft.Win32;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
public static class MarkdownRenderer
|
|
{
|
|
private static readonly Regex FilePathPattern = new Regex("(?<![\"'=/>])([A-Za-z]:\\\\[^\\s<>\"',;)]+|\\.{1,2}/[\\w\\-./]+(?:\\.[a-zA-Z]{1,10})?|[\\w\\-]+(?:/[\\w\\-\\.]+){1,}(?:\\.[a-zA-Z]{1,10})?|[\\w\\-]+\\.(?:cs|py|js|ts|tsx|jsx|json|xml|html|htm|css|md|txt|yml|yaml|toml|sh|bat|ps1|csproj|sln|docx|xlsx|pptx|pdf|csv|enc|skill))(?=[,\\s;)\"'<]|$)", RegexOptions.Compiled);
|
|
|
|
private static readonly Brush KeywordBrush = new SolidColorBrush(Color.FromRgb(197, 134, 192));
|
|
|
|
private static readonly Brush TypeBrush = new SolidColorBrush(Color.FromRgb(78, 201, 176));
|
|
|
|
private static readonly Brush StringBrush = new SolidColorBrush(Color.FromRgb(206, 145, 120));
|
|
|
|
private static readonly Brush CommentBrush = new SolidColorBrush(Color.FromRgb(106, 153, 85));
|
|
|
|
private static readonly Brush NumberBrush = new SolidColorBrush(Color.FromRgb(181, 206, 168));
|
|
|
|
private static readonly Brush MethodBrush = new SolidColorBrush(Color.FromRgb(220, 220, 170));
|
|
|
|
private static readonly Brush DirectiveBrush = new SolidColorBrush(Color.FromRgb(156, 220, 254));
|
|
|
|
private static readonly HashSet<string> CSharpKeywords = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"abstract", "as", "async", "await", "base", "bool", "break", "byte", "case", "catch",
|
|
"char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double",
|
|
"else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for",
|
|
"foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
|
|
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private",
|
|
"protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc",
|
|
"static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint",
|
|
"ulong", "unchecked", "unsafe", "ushort", "using", "var", "virtual", "void", "volatile", "while",
|
|
"yield", "record", "init", "required", "get", "set", "value", "where", "when", "and",
|
|
"or", "not"
|
|
};
|
|
|
|
private static readonly HashSet<string> PythonKeywords = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"False", "None", "True", "and", "as", "assert", "async", "await", "break", "class",
|
|
"continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global",
|
|
"if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise",
|
|
"return", "try", "while", "with", "yield"
|
|
};
|
|
|
|
private static readonly HashSet<string> JsKeywords = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"abstract", "arguments", "async", "await", "boolean", "break", "byte", "case", "catch", "char",
|
|
"class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum",
|
|
"eval", "export", "extends", "false", "final", "finally", "float", "for", "from", "function",
|
|
"goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long",
|
|
"native", "new", "null", "of", "package", "private", "protected", "public", "return", "short",
|
|
"static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try",
|
|
"typeof", "undefined", "var", "void", "volatile", "while", "with", "yield"
|
|
};
|
|
|
|
private static readonly HashSet<string> JavaKeywords = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const",
|
|
"continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally",
|
|
"float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long",
|
|
"native", "new", "null", "package", "private", "protected", "public", "return", "short", "static",
|
|
"strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try",
|
|
"void", "volatile", "while", "var", "record", "sealed", "permits", "yield"
|
|
};
|
|
|
|
private static readonly HashSet<string> SqlKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"SELECT", "FROM", "WHERE", "INSERT", "INTO", "UPDATE", "SET", "DELETE", "CREATE", "DROP",
|
|
"ALTER", "TABLE", "INDEX", "VIEW", "JOIN", "INNER", "LEFT", "RIGHT", "OUTER", "FULL",
|
|
"CROSS", "ON", "AND", "OR", "NOT", "IN", "IS", "NULL", "LIKE", "BETWEEN",
|
|
"EXISTS", "HAVING", "GROUP", "BY", "ORDER", "ASC", "DESC", "LIMIT", "OFFSET", "UNION",
|
|
"ALL", "DISTINCT", "AS", "CASE", "WHEN", "THEN", "ELSE", "END", "VALUES", "PRIMARY",
|
|
"KEY", "FOREIGN", "REFERENCES", "CONSTRAINT", "UNIQUE", "CHECK", "DEFAULT", "COUNT", "SUM", "AVG",
|
|
"MIN", "MAX", "CAST", "COALESCE", "IF", "BEGIN", "COMMIT", "ROLLBACK", "GRANT", "REVOKE",
|
|
"TRUNCATE", "WITH", "RECURSIVE"
|
|
};
|
|
|
|
private static readonly Regex SyntaxPattern = new Regex("(//[^\\n]*|#[^\\n]*)|(\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*')|(\\b\\d+\\.?\\d*[fFdDmMlL]?\\b)|(\\b[A-Z]\\w*(?=\\s*[\\.<\\(]))|(\\b\\w+(?=\\s*\\())|(\\b\\w+\\b)", RegexOptions.Compiled);
|
|
|
|
public static bool EnableFilePathHighlight { get; set; } = true;
|
|
|
|
public static StackPanel Render(string markdown, Brush textColor, Brush secondaryColor, Brush accentColor, Brush codeBg)
|
|
{
|
|
StackPanel stackPanel = new StackPanel();
|
|
if (string.IsNullOrEmpty(markdown))
|
|
{
|
|
return stackPanel;
|
|
}
|
|
string[] array = markdown.Replace("\r\n", "\n").Split('\n');
|
|
int i = 0;
|
|
while (i < array.Length)
|
|
{
|
|
string text = array[i];
|
|
if (text.TrimStart().StartsWith("```"))
|
|
{
|
|
object obj;
|
|
if (text.TrimStart().Length <= 3)
|
|
{
|
|
obj = "";
|
|
}
|
|
else
|
|
{
|
|
string text2 = text.TrimStart();
|
|
obj = text2.Substring(3, text2.Length - 3).Trim();
|
|
}
|
|
string lang = (string)obj;
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
for (i++; i < array.Length && !array[i].TrimStart().StartsWith("```"); i++)
|
|
{
|
|
stringBuilder.AppendLine(array[i]);
|
|
}
|
|
if (i < array.Length)
|
|
{
|
|
i++;
|
|
}
|
|
Border element = CreateCodeBlock(stringBuilder.ToString().TrimEnd(), lang, textColor, codeBg, accentColor);
|
|
stackPanel.Children.Add(element);
|
|
}
|
|
else if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
stackPanel.Children.Add(new Border
|
|
{
|
|
Height = 6.0
|
|
});
|
|
i++;
|
|
}
|
|
else if (Regex.IsMatch(text.Trim(), "^-{3,}$|^\\*{3,}$"))
|
|
{
|
|
stackPanel.Children.Add(new Border
|
|
{
|
|
Height = 1.0,
|
|
Background = secondaryColor,
|
|
Opacity = 0.3,
|
|
Margin = new Thickness(0.0, 8.0, 0.0, 8.0)
|
|
});
|
|
i++;
|
|
}
|
|
else if (text.StartsWith('#'))
|
|
{
|
|
int j;
|
|
for (j = 0; j < text.Length && text[j] == '#'; j++)
|
|
{
|
|
}
|
|
string text2 = text;
|
|
int num = j;
|
|
string text3 = text2.Substring(num, text2.Length - num).Trim();
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
double num2 = j switch
|
|
{
|
|
1 => 20.0,
|
|
2 => 17.0,
|
|
3 => 15.0,
|
|
_ => 14.0,
|
|
};
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
double fontSize = num2;
|
|
TextBlock textBlock = new TextBlock
|
|
{
|
|
FontSize = fontSize,
|
|
FontWeight = FontWeights.Bold,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Margin = new Thickness(0.0, (j == 1) ? 12 : 8, 0.0, 4.0)
|
|
};
|
|
AddInlines(textBlock.Inlines, text3, textColor, accentColor, codeBg);
|
|
stackPanel.Children.Add(textBlock);
|
|
i++;
|
|
}
|
|
else if (Regex.IsMatch(text, "^\\s*[-*]\\s") || Regex.IsMatch(text, "^\\s*\\d+\\.\\s"))
|
|
{
|
|
Match match = Regex.Match(text, "^(\\s*)([-*]|\\d+\\.)\\s(.*)");
|
|
if (match.Success)
|
|
{
|
|
int num3 = match.Groups[1].Value.Length / 2;
|
|
string value = match.Groups[2].Value;
|
|
string value2 = match.Groups[3].Value;
|
|
bool flag = ((value == "-" || value == "*") ? true : false);
|
|
string text4 = (flag ? "•" : value);
|
|
Grid grid = new Grid
|
|
{
|
|
Margin = new Thickness(12 + num3 * 16, 2.0, 0.0, 2.0)
|
|
};
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = new GridLength(18.0)
|
|
});
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = new GridLength(1.0, GridUnitType.Star)
|
|
});
|
|
TextBlock element2 = new TextBlock
|
|
{
|
|
Text = text4,
|
|
FontSize = 13.5,
|
|
Foreground = accentColor,
|
|
VerticalAlignment = VerticalAlignment.Top
|
|
};
|
|
Grid.SetColumn(element2, 0);
|
|
grid.Children.Add(element2);
|
|
TextBlock textBlock2 = new TextBlock
|
|
{
|
|
FontSize = 13.5,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
VerticalAlignment = VerticalAlignment.Top
|
|
};
|
|
Grid.SetColumn(textBlock2, 1);
|
|
AddInlines(textBlock2.Inlines, value2, textColor, accentColor, codeBg);
|
|
grid.Children.Add(textBlock2);
|
|
stackPanel.Children.Add(grid);
|
|
}
|
|
i++;
|
|
}
|
|
else if (text.TrimStart().StartsWith('>'))
|
|
{
|
|
List<string> list = new List<string>();
|
|
for (; i < array.Length && array[i].TrimStart().StartsWith('>'); i++)
|
|
{
|
|
string text5 = array[i].TrimStart();
|
|
object item;
|
|
if (text5.Length <= 1)
|
|
{
|
|
item = "";
|
|
}
|
|
else
|
|
{
|
|
string text2 = text5;
|
|
item = text2.Substring(1, text2.Length - 1).TrimStart();
|
|
}
|
|
list.Add((string)item);
|
|
}
|
|
Border border = new Border
|
|
{
|
|
BorderBrush = accentColor,
|
|
BorderThickness = new Thickness(3.0, 0.0, 0.0, 0.0),
|
|
Padding = new Thickness(12.0, 6.0, 8.0, 6.0),
|
|
Margin = new Thickness(4.0, 4.0, 0.0, 4.0),
|
|
Background = new SolidColorBrush(Color.FromArgb(16, byte.MaxValue, byte.MaxValue, byte.MaxValue))
|
|
};
|
|
TextBlock textBlock3 = new TextBlock
|
|
{
|
|
FontSize = 13.0,
|
|
FontStyle = FontStyles.Italic,
|
|
Foreground = secondaryColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
LineHeight = 20.0
|
|
};
|
|
AddInlines(textBlock3.Inlines, string.Join("\n", list), textColor, accentColor, codeBg);
|
|
border.Child = textBlock3;
|
|
stackPanel.Children.Add(border);
|
|
}
|
|
else if (text.Contains('|') && text.Trim().StartsWith('|'))
|
|
{
|
|
List<string> list2 = new List<string>();
|
|
for (; i < array.Length && array[i].Contains('|'); i++)
|
|
{
|
|
list2.Add(array[i]);
|
|
}
|
|
FrameworkElement frameworkElement = CreateMarkdownTable(list2, textColor, accentColor, codeBg);
|
|
if (frameworkElement != null)
|
|
{
|
|
stackPanel.Children.Add(frameworkElement);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TextBlock textBlock4 = new TextBlock
|
|
{
|
|
FontSize = 13.5,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
LineHeight = 22.0,
|
|
Margin = new Thickness(0.0, 2.0, 0.0, 2.0)
|
|
};
|
|
AddInlines(textBlock4.Inlines, text, textColor, accentColor, codeBg);
|
|
stackPanel.Children.Add(textBlock4);
|
|
i++;
|
|
}
|
|
}
|
|
return stackPanel;
|
|
}
|
|
|
|
private static void AddInlines(InlineCollection inlines, string text, Brush textColor, Brush accentColor, Brush codeBg)
|
|
{
|
|
string pattern = "(\\[([^\\]]+)\\]\\(([^)]+)\\)|~~(.+?)~~|\\*\\*(.+?)\\*\\*|\\*(.+?)\\*|`(.+?)`|([^*`~\\[]+))";
|
|
MatchCollection matchCollection = Regex.Matches(text, pattern);
|
|
foreach (Match item in matchCollection)
|
|
{
|
|
if (item.Groups[2].Success && item.Groups[3].Success)
|
|
{
|
|
string linkUrl = item.Groups[3].Value;
|
|
Hyperlink hyperlink = new Hyperlink(new Run(item.Groups[2].Value))
|
|
{
|
|
Foreground = accentColor,
|
|
TextDecorations = null,
|
|
Cursor = Cursors.Hand
|
|
};
|
|
hyperlink.Click += delegate
|
|
{
|
|
try
|
|
{
|
|
Process.Start(new ProcessStartInfo(linkUrl)
|
|
{
|
|
UseShellExecute = true
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
};
|
|
inlines.Add(hyperlink);
|
|
}
|
|
else if (item.Groups[4].Success)
|
|
{
|
|
inlines.Add(new Run(item.Groups[4].Value)
|
|
{
|
|
TextDecorations = TextDecorations.Strikethrough,
|
|
Foreground = new SolidColorBrush(Color.FromArgb(153, byte.MaxValue, byte.MaxValue, byte.MaxValue))
|
|
});
|
|
}
|
|
else if (item.Groups[5].Success)
|
|
{
|
|
inlines.Add(new Run(item.Groups[5].Value)
|
|
{
|
|
FontWeight = FontWeights.Bold
|
|
});
|
|
}
|
|
else if (item.Groups[6].Success)
|
|
{
|
|
inlines.Add(new Run(item.Groups[6].Value)
|
|
{
|
|
FontStyle = FontStyles.Italic
|
|
});
|
|
}
|
|
else if (item.Groups[7].Success)
|
|
{
|
|
Border childUIElement = new Border
|
|
{
|
|
Background = codeBg,
|
|
CornerRadius = new CornerRadius(4.0),
|
|
Padding = new Thickness(5.0, 1.0, 5.0, 1.0),
|
|
Margin = new Thickness(1.0, 0.0, 1.0, 0.0),
|
|
Child = new TextBlock
|
|
{
|
|
Text = item.Groups[7].Value,
|
|
FontFamily = new FontFamily("Cascadia Code, Consolas, monospace"),
|
|
FontSize = 12.5,
|
|
Foreground = accentColor
|
|
}
|
|
};
|
|
inlines.Add(new InlineUIContainer(childUIElement)
|
|
{
|
|
BaselineAlignment = BaselineAlignment.Center
|
|
});
|
|
}
|
|
else if (item.Groups[8].Success)
|
|
{
|
|
AddPlainTextWithFilePaths(inlines, item.Groups[8].Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void AddPlainTextWithFilePaths(InlineCollection inlines, string text)
|
|
{
|
|
if (!EnableFilePathHighlight)
|
|
{
|
|
inlines.Add(new Run(text));
|
|
return;
|
|
}
|
|
MatchCollection matchCollection = FilePathPattern.Matches(text);
|
|
if (matchCollection.Count == 0)
|
|
{
|
|
inlines.Add(new Run(text));
|
|
return;
|
|
}
|
|
int num = 0;
|
|
foreach (Match item in matchCollection)
|
|
{
|
|
if (item.Index > num)
|
|
{
|
|
int num2 = num;
|
|
inlines.Add(new Run(text.Substring(num2, item.Index - num2)));
|
|
}
|
|
inlines.Add(new Run(item.Value)
|
|
{
|
|
Foreground = new SolidColorBrush(Color.FromRgb(59, 130, 246)),
|
|
FontWeight = FontWeights.Medium
|
|
});
|
|
num = item.Index + item.Length;
|
|
}
|
|
if (num < text.Length)
|
|
{
|
|
int num2 = num;
|
|
inlines.Add(new Run(text.Substring(num2, text.Length - num2)));
|
|
}
|
|
}
|
|
|
|
private static FrameworkElement? CreateMarkdownTable(List<string> rows, Brush textColor, Brush accentColor, Brush codeBg)
|
|
{
|
|
if (rows.Count < 2)
|
|
{
|
|
return null;
|
|
}
|
|
string[] array = ParseRow(rows[0]);
|
|
int num = array.Length;
|
|
if (num == 0)
|
|
{
|
|
return null;
|
|
}
|
|
int num2 = 1;
|
|
if (rows.Count > 1 && Regex.IsMatch(rows[1].Trim(), "^[\\|\\s:\\-]+$"))
|
|
{
|
|
num2 = 2;
|
|
}
|
|
Grid grid = new Grid
|
|
{
|
|
Margin = new Thickness(0.0, 6.0, 0.0, 6.0)
|
|
};
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = new GridLength(1.0, GridUnitType.Star)
|
|
});
|
|
}
|
|
int num3 = 0;
|
|
grid.RowDefinitions.Add(new RowDefinition
|
|
{
|
|
Height = GridLength.Auto
|
|
});
|
|
for (int j = 0; j < num; j++)
|
|
{
|
|
Border border = new Border
|
|
{
|
|
Background = new SolidColorBrush(Color.FromArgb(32, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
BorderBrush = new SolidColorBrush(Color.FromArgb(48, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
BorderThickness = new Thickness(0.0, 0.0, (j < num - 1) ? 1 : 0, 1.0),
|
|
Padding = new Thickness(8.0, 5.0, 8.0, 5.0)
|
|
};
|
|
TextBlock child = new TextBlock
|
|
{
|
|
Text = ((j < array.Length) ? array[j] : ""),
|
|
FontSize = 12.0,
|
|
FontWeight = FontWeights.SemiBold,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap
|
|
};
|
|
border.Child = child;
|
|
Grid.SetRow(border, num3);
|
|
Grid.SetColumn(border, j);
|
|
grid.Children.Add(border);
|
|
}
|
|
num3++;
|
|
for (int k = num2; k < rows.Count; k++)
|
|
{
|
|
string[] array2 = ParseRow(rows[k]);
|
|
grid.RowDefinitions.Add(new RowDefinition
|
|
{
|
|
Height = GridLength.Auto
|
|
});
|
|
for (int l = 0; l < num; l++)
|
|
{
|
|
Border border2 = new Border
|
|
{
|
|
Background = ((k % 2 == 0) ? new SolidColorBrush(Color.FromArgb(8, byte.MaxValue, byte.MaxValue, byte.MaxValue)) : Brushes.Transparent),
|
|
BorderBrush = new SolidColorBrush(Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
BorderThickness = new Thickness(0.0, 0.0, (l < num - 1) ? 1 : 0, 1.0),
|
|
Padding = new Thickness(8.0, 4.0, 8.0, 4.0)
|
|
};
|
|
TextBlock textBlock = new TextBlock
|
|
{
|
|
FontSize = 12.0,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap
|
|
};
|
|
AddInlines(textBlock.Inlines, (l < array2.Length) ? array2[l] : "", textColor, accentColor, codeBg);
|
|
border2.Child = textBlock;
|
|
Grid.SetRow(border2, num3);
|
|
Grid.SetColumn(border2, l);
|
|
grid.Children.Add(border2);
|
|
}
|
|
num3++;
|
|
}
|
|
Border border3 = new Border
|
|
{
|
|
CornerRadius = new CornerRadius(6.0),
|
|
BorderBrush = new SolidColorBrush(Color.FromArgb(48, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
BorderThickness = new Thickness(1.0),
|
|
ClipToBounds = true,
|
|
Margin = new Thickness(0.0, 4.0, 0.0, 4.0)
|
|
};
|
|
border3.Child = grid;
|
|
return border3;
|
|
static string[] ParseRow(string row)
|
|
{
|
|
string text = row.Trim().Trim('|');
|
|
return (from c in text.Split('|')
|
|
select c.Trim()).ToArray();
|
|
}
|
|
}
|
|
|
|
private static Border CreateCodeBlock(string code, string lang, Brush textColor, Brush codeBg, Brush accentColor)
|
|
{
|
|
Border border = new Border
|
|
{
|
|
Background = codeBg,
|
|
CornerRadius = new CornerRadius(10.0),
|
|
Margin = new Thickness(0.0, 6.0, 0.0, 6.0),
|
|
Padding = new Thickness(0.0)
|
|
};
|
|
StackPanel stackPanel = new StackPanel();
|
|
Border border2 = new Border
|
|
{
|
|
Background = new SolidColorBrush(Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
CornerRadius = new CornerRadius(10.0, 10.0, 0.0, 0.0),
|
|
Padding = new Thickness(14.0, 6.0, 8.0, 6.0)
|
|
};
|
|
Grid grid = new Grid();
|
|
grid.Children.Add(new TextBlock
|
|
{
|
|
Text = (string.IsNullOrEmpty(lang) ? "code" : lang),
|
|
FontSize = 11.0,
|
|
Foreground = accentColor,
|
|
FontWeight = FontWeights.SemiBold,
|
|
VerticalAlignment = VerticalAlignment.Center
|
|
});
|
|
StackPanel stackPanel2 = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
HorizontalAlignment = HorizontalAlignment.Right,
|
|
VerticalAlignment = VerticalAlignment.Center
|
|
};
|
|
string capturedCode = code;
|
|
string capturedLang = lang;
|
|
Button button = CreateCodeHeaderButton("\ue74e", "저장", textColor);
|
|
button.Click += delegate
|
|
{
|
|
try
|
|
{
|
|
string extensionForLang = GetExtensionForLang(capturedLang);
|
|
SaveFileDialog saveFileDialog = new SaveFileDialog
|
|
{
|
|
FileName = "code" + extensionForLang,
|
|
Filter = $"코드 파일 (*{extensionForLang})|*{extensionForLang}|모든 파일 (*.*)|*.*"
|
|
};
|
|
if (saveFileDialog.ShowDialog() == true)
|
|
{
|
|
File.WriteAllText(saveFileDialog.FileName, capturedCode);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
};
|
|
stackPanel2.Children.Add(button);
|
|
Button button2 = CreateCodeHeaderButton("\ue740", "확대", textColor);
|
|
button2.Click += delegate
|
|
{
|
|
ShowCodeFullScreen(capturedCode, capturedLang, codeBg, textColor);
|
|
};
|
|
stackPanel2.Children.Add(button2);
|
|
Button button3 = CreateCodeHeaderButton("\ue8c8", "복사", textColor);
|
|
button3.Click += delegate
|
|
{
|
|
try
|
|
{
|
|
Clipboard.SetText(capturedCode);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
};
|
|
stackPanel2.Children.Add(button3);
|
|
grid.Children.Add(stackPanel2);
|
|
border2.Child = grid;
|
|
stackPanel.Children.Add(border2);
|
|
Grid grid2 = new Grid
|
|
{
|
|
Margin = new Thickness(0.0, 0.0, 0.0, 4.0)
|
|
};
|
|
grid2.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = GridLength.Auto
|
|
});
|
|
grid2.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = new GridLength(1.0, GridUnitType.Star)
|
|
});
|
|
string[] array = code.Split('\n');
|
|
TextBlock element = new TextBlock
|
|
{
|
|
FontFamily = new FontFamily("Cascadia Code, Consolas, monospace"),
|
|
FontSize = 12.5,
|
|
Foreground = new SolidColorBrush(Color.FromArgb(80, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
Padding = new Thickness(10.0, 10.0, 6.0, 14.0),
|
|
LineHeight = 20.0,
|
|
TextAlignment = TextAlignment.Right,
|
|
Text = string.Join("\n", Enumerable.Range(1, array.Length))
|
|
};
|
|
Grid.SetColumn(element, 0);
|
|
grid2.Children.Add(element);
|
|
TextBlock textBlock = new TextBlock
|
|
{
|
|
FontFamily = new FontFamily("Cascadia Code, Consolas, monospace"),
|
|
FontSize = 12.5,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Padding = new Thickness(8.0, 10.0, 14.0, 14.0),
|
|
LineHeight = 20.0
|
|
};
|
|
ApplySyntaxHighlighting(textBlock, code, lang, textColor);
|
|
Grid.SetColumn(textBlock, 1);
|
|
grid2.Children.Add(textBlock);
|
|
stackPanel.Children.Add(grid2);
|
|
border.Child = stackPanel;
|
|
return border;
|
|
}
|
|
|
|
private static Button CreateCodeHeaderButton(string mdlIcon, string label, Brush fg)
|
|
{
|
|
return new Button
|
|
{
|
|
Background = Brushes.Transparent,
|
|
BorderThickness = new Thickness(0.0),
|
|
Cursor = Cursors.Hand,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Padding = new Thickness(5.0, 2.0, 5.0, 2.0),
|
|
Margin = new Thickness(2.0, 0.0, 0.0, 0.0),
|
|
Content = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
Children =
|
|
{
|
|
(UIElement)new TextBlock
|
|
{
|
|
Text = mdlIcon,
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 10.0,
|
|
Foreground = fg,
|
|
Opacity = 0.6,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Margin = new Thickness(0.0, 0.0, 3.0, 0.0)
|
|
},
|
|
(UIElement)new TextBlock
|
|
{
|
|
Text = label,
|
|
FontSize = 10.0,
|
|
Foreground = fg,
|
|
Opacity = 0.6
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private static string GetExtensionForLang(string lang)
|
|
{
|
|
string text = (lang ?? "").ToLowerInvariant();
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
string result;
|
|
switch (text)
|
|
{
|
|
case "csharp":
|
|
case "cs":
|
|
result = ".cs";
|
|
break;
|
|
case "python":
|
|
case "py":
|
|
result = ".py";
|
|
break;
|
|
case "javascript":
|
|
case "js":
|
|
result = ".js";
|
|
break;
|
|
case "typescript":
|
|
case "ts":
|
|
result = ".ts";
|
|
break;
|
|
case "java":
|
|
result = ".java";
|
|
break;
|
|
case "html":
|
|
result = ".html";
|
|
break;
|
|
case "css":
|
|
result = ".css";
|
|
break;
|
|
case "json":
|
|
result = ".json";
|
|
break;
|
|
case "xml":
|
|
result = ".xml";
|
|
break;
|
|
case "sql":
|
|
result = ".sql";
|
|
break;
|
|
case "bash":
|
|
case "sh":
|
|
case "shell":
|
|
result = ".sh";
|
|
break;
|
|
case "powershell":
|
|
case "ps1":
|
|
result = ".ps1";
|
|
break;
|
|
case "bat":
|
|
case "cmd":
|
|
result = ".bat";
|
|
break;
|
|
case "yaml":
|
|
case "yml":
|
|
result = ".yml";
|
|
break;
|
|
case "markdown":
|
|
case "md":
|
|
result = ".md";
|
|
break;
|
|
case "cpp":
|
|
case "c++":
|
|
result = ".cpp";
|
|
break;
|
|
case "c":
|
|
result = ".c";
|
|
break;
|
|
case "go":
|
|
result = ".go";
|
|
break;
|
|
case "rust":
|
|
case "rs":
|
|
result = ".rs";
|
|
break;
|
|
default:
|
|
result = ".txt";
|
|
break;
|
|
}
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void ShowCodeFullScreen(string code, string lang, Brush codeBg, Brush textColor)
|
|
{
|
|
Window window = new Window();
|
|
window.Title = "코드 — " + (string.IsNullOrEmpty(lang) ? "code" : lang);
|
|
window.Width = 900.0;
|
|
window.Height = 650.0;
|
|
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
|
window.Background = ((codeBg is SolidColorBrush solidColorBrush) ? new SolidColorBrush(solidColorBrush.Color) : Brushes.Black);
|
|
Window window2 = window;
|
|
Grid grid = new Grid();
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = GridLength.Auto
|
|
});
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition
|
|
{
|
|
Width = new GridLength(1.0, GridUnitType.Star)
|
|
});
|
|
string[] array = code.Split('\n');
|
|
TextBlock element = new TextBlock
|
|
{
|
|
FontFamily = new FontFamily("Cascadia Code, Consolas, monospace"),
|
|
FontSize = 13.0,
|
|
LineHeight = 22.0,
|
|
Foreground = new SolidColorBrush(Color.FromArgb(80, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
|
|
Padding = new Thickness(16.0, 16.0, 8.0, 16.0),
|
|
TextAlignment = TextAlignment.Right,
|
|
Text = string.Join("\n", Enumerable.Range(1, array.Length))
|
|
};
|
|
Grid.SetColumn(element, 0);
|
|
grid.Children.Add(element);
|
|
TextBlock textBlock = new TextBlock
|
|
{
|
|
FontFamily = new FontFamily("Cascadia Code, Consolas, monospace"),
|
|
FontSize = 13.0,
|
|
LineHeight = 22.0,
|
|
Foreground = textColor,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Padding = new Thickness(8.0, 16.0, 16.0, 16.0)
|
|
};
|
|
ApplySyntaxHighlighting(textBlock, code, lang, textColor);
|
|
Grid.SetColumn(textBlock, 1);
|
|
grid.Children.Add(textBlock);
|
|
ScrollViewer content = new ScrollViewer
|
|
{
|
|
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
|
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
|
|
Content = grid
|
|
};
|
|
window2.Content = content;
|
|
window2.Show();
|
|
}
|
|
|
|
private static HashSet<string> GetKeywordsForLang(string lang)
|
|
{
|
|
string text = lang.ToLowerInvariant();
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
HashSet<string> result;
|
|
switch (text)
|
|
{
|
|
case "csharp":
|
|
case "cs":
|
|
case "c#":
|
|
result = CSharpKeywords;
|
|
break;
|
|
case "python":
|
|
case "py":
|
|
result = PythonKeywords;
|
|
break;
|
|
case "javascript":
|
|
case "js":
|
|
case "jsx":
|
|
result = JsKeywords;
|
|
break;
|
|
case "typescript":
|
|
case "ts":
|
|
case "tsx":
|
|
result = JsKeywords;
|
|
break;
|
|
case "java":
|
|
case "kotlin":
|
|
case "kt":
|
|
result = JavaKeywords;
|
|
break;
|
|
case "sql":
|
|
case "mysql":
|
|
case "postgresql":
|
|
case "sqlite":
|
|
result = SqlKeywords;
|
|
break;
|
|
case "go":
|
|
case "golang":
|
|
result = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"break", "case", "chan", "const", "continue", "default", "defer", "else", "fallthrough", "for",
|
|
"func", "go", "goto", "if", "import", "interface", "map", "package", "range", "return",
|
|
"select", "struct", "switch", "type", "var", "nil", "true", "false", "append", "len",
|
|
"cap", "make", "new", "panic", "recover"
|
|
};
|
|
break;
|
|
case "rust":
|
|
case "rs":
|
|
result = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",
|
|
"extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match",
|
|
"mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
|
|
"super", "trait", "true", "type", "unsafe", "use", "where", "while", "yield", "Box",
|
|
"Vec", "String", "Option", "Result", "Some", "None", "Ok", "Err", "println", "macro_rules"
|
|
};
|
|
break;
|
|
case "cpp":
|
|
case "c++":
|
|
case "c":
|
|
case "h":
|
|
case "hpp":
|
|
result = new HashSet<string>(StringComparer.Ordinal)
|
|
{
|
|
"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else",
|
|
"enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register",
|
|
"return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned",
|
|
"void", "volatile", "while", "class", "namespace", "using", "public", "private", "protected", "virtual",
|
|
"override", "template", "typename", "nullptr", "true", "false", "new", "delete", "throw", "try",
|
|
"catch", "const_cast", "dynamic_cast", "static_cast", "reinterpret_cast", "bool", "string", "include", "define", "ifdef",
|
|
"ifndef", "endif"
|
|
};
|
|
break;
|
|
default:
|
|
result = CSharpKeywords;
|
|
break;
|
|
}
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void ApplySyntaxHighlighting(TextBlock tb, string code, string lang, Brush defaultColor)
|
|
{
|
|
if (string.IsNullOrEmpty(lang) || string.IsNullOrEmpty(code))
|
|
{
|
|
tb.Text = code;
|
|
return;
|
|
}
|
|
HashSet<string> keywordsForLang = GetKeywordsForLang(lang);
|
|
bool flag;
|
|
switch (lang.ToLowerInvariant())
|
|
{
|
|
case "python":
|
|
case "py":
|
|
case "bash":
|
|
case "sh":
|
|
case "shell":
|
|
case "ruby":
|
|
case "rb":
|
|
case "yaml":
|
|
case "yml":
|
|
case "toml":
|
|
case "powershell":
|
|
case "ps1":
|
|
flag = true;
|
|
break;
|
|
default:
|
|
flag = false;
|
|
break;
|
|
}
|
|
bool flag2 = flag;
|
|
foreach (Match item in SyntaxPattern.Matches(code))
|
|
{
|
|
if (item.Groups[1].Success)
|
|
{
|
|
string value = item.Groups[1].Value;
|
|
if (value.StartsWith('#') && !flag2)
|
|
{
|
|
tb.Inlines.Add(new Run(value)
|
|
{
|
|
Foreground = defaultColor
|
|
});
|
|
}
|
|
else
|
|
{
|
|
tb.Inlines.Add(new Run(value)
|
|
{
|
|
Foreground = CommentBrush,
|
|
FontStyle = FontStyles.Italic
|
|
});
|
|
}
|
|
}
|
|
else if (item.Groups[2].Success)
|
|
{
|
|
tb.Inlines.Add(new Run(item.Groups[2].Value)
|
|
{
|
|
Foreground = StringBrush
|
|
});
|
|
}
|
|
else if (item.Groups[3].Success)
|
|
{
|
|
tb.Inlines.Add(new Run(item.Groups[3].Value)
|
|
{
|
|
Foreground = NumberBrush
|
|
});
|
|
}
|
|
else if (item.Groups[4].Success)
|
|
{
|
|
string value2 = item.Groups[4].Value;
|
|
tb.Inlines.Add(new Run(value2)
|
|
{
|
|
Foreground = (keywordsForLang.Contains(value2) ? KeywordBrush : TypeBrush)
|
|
});
|
|
}
|
|
else if (item.Groups[5].Success)
|
|
{
|
|
string value3 = item.Groups[5].Value;
|
|
if (keywordsForLang.Contains(value3))
|
|
{
|
|
tb.Inlines.Add(new Run(value3)
|
|
{
|
|
Foreground = KeywordBrush
|
|
});
|
|
}
|
|
else
|
|
{
|
|
tb.Inlines.Add(new Run(value3)
|
|
{
|
|
Foreground = MethodBrush
|
|
});
|
|
}
|
|
}
|
|
else if (item.Groups[6].Success)
|
|
{
|
|
string value4 = item.Groups[6].Value;
|
|
if (keywordsForLang.Contains(value4))
|
|
{
|
|
tb.Inlines.Add(new Run(value4)
|
|
{
|
|
Foreground = KeywordBrush
|
|
});
|
|
}
|
|
else
|
|
{
|
|
tb.Inlines.Add(new Run(value4)
|
|
{
|
|
Foreground = defaultColor
|
|
});
|
|
}
|
|
}
|
|
}
|
|
tb.Inlines.Clear();
|
|
int num = 0;
|
|
foreach (Match item2 in SyntaxPattern.Matches(code))
|
|
{
|
|
if (item2.Index > num)
|
|
{
|
|
InlineCollection inlines = tb.Inlines;
|
|
int num2 = num;
|
|
inlines.Add(new Run(code.Substring(num2, item2.Index - num2))
|
|
{
|
|
Foreground = defaultColor
|
|
});
|
|
}
|
|
if (item2.Groups[1].Success)
|
|
{
|
|
string value5 = item2.Groups[1].Value;
|
|
if (value5.StartsWith('#') && !flag2)
|
|
{
|
|
tb.Inlines.Add(new Run(value5)
|
|
{
|
|
Foreground = defaultColor
|
|
});
|
|
}
|
|
else
|
|
{
|
|
tb.Inlines.Add(new Run(value5)
|
|
{
|
|
Foreground = CommentBrush,
|
|
FontStyle = FontStyles.Italic
|
|
});
|
|
}
|
|
}
|
|
else if (item2.Groups[2].Success)
|
|
{
|
|
tb.Inlines.Add(new Run(item2.Groups[2].Value)
|
|
{
|
|
Foreground = StringBrush
|
|
});
|
|
}
|
|
else if (item2.Groups[3].Success)
|
|
{
|
|
tb.Inlines.Add(new Run(item2.Groups[3].Value)
|
|
{
|
|
Foreground = NumberBrush
|
|
});
|
|
}
|
|
else if (item2.Groups[4].Success)
|
|
{
|
|
string value6 = item2.Groups[4].Value;
|
|
tb.Inlines.Add(new Run(value6)
|
|
{
|
|
Foreground = (keywordsForLang.Contains(value6) ? KeywordBrush : TypeBrush)
|
|
});
|
|
}
|
|
else if (item2.Groups[5].Success)
|
|
{
|
|
string value7 = item2.Groups[5].Value;
|
|
tb.Inlines.Add(new Run(value7)
|
|
{
|
|
Foreground = (keywordsForLang.Contains(value7) ? KeywordBrush : MethodBrush)
|
|
});
|
|
}
|
|
else if (item2.Groups[6].Success)
|
|
{
|
|
string value8 = item2.Groups[6].Value;
|
|
tb.Inlines.Add(new Run(value8)
|
|
{
|
|
Foreground = (keywordsForLang.Contains(value8) ? KeywordBrush : defaultColor)
|
|
});
|
|
}
|
|
num = item2.Index + item2.Length;
|
|
}
|
|
if (num < code.Length)
|
|
{
|
|
InlineCollection inlines2 = tb.Inlines;
|
|
int num2 = num;
|
|
inlines2.Add(new Run(code.Substring(num2, code.Length - num2))
|
|
{
|
|
Foreground = defaultColor
|
|
});
|
|
}
|
|
}
|
|
}
|