C#のソースコードを、Visual Studioのエディタのように色付けしてHTMLで表示したいときがあります。
ソースコードを開くと、正規表現を使って字句解析し、C#の言語仕様で定義されたキーワードとリテラル文字列に色付けし、変換したHTMLをテキストボックスに出力します。
簡素化のため、ここでは次のルールに従ってソースコードが記述されているものとします。
- トークンは、区切り文字列、リテラル文字列、識別子のいずれか。
- 区切り文字列は、区切り記号、演算子、キーワード、改行、空白のいずれか。
- リテラル文字列は、「"」または「'」で囲まれた文字列で、文字列中の「"」「'」は「\」でエスケープされている。
- 区切り文字列でもリテラル文字列でもないものは識別子。
リテラル文字列の色は「
#990000」、キーワードの色は「
blue」にしています。
このアプリケーション自身のコード(MainWindow.xaml.cs)を開いてみる
生成されたHTMLをブラウザで開いてみる
MainWindow.xaml
<Window x:Class="CsHighlighter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="OpenCommandHandler"/>
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Command="Open" />
</Menu>
<TextBox Grid.Row="1" Name="textBox1"
AcceptsReturn="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" />
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Win32;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Windows;
using System.Windows.Input;
namespace CsHighlighter
{
public partial class MainWindow :
Window
{
private static readonly string[] keywords = {
"abstract",
"as",
"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",
"virtual",
"void",
"volatile",
"while"};
private static readonly string[] marks = {
"<<=",
">>=",
"??",
"::",
"++",
"--",
"&&",
"||",
"->",
"==",
"!=",
"<="
,
">=",
"+=",
"-=",
"*=",
"/=",
"%=",
"&=",
"|=",
"^=",
"<<",
">>",
"=>",
"{",
"}",
"[",
"]",
"(",
")",
".",
",",
":",
";",
"+",
"-",
"*",
"/",
"%",
"&"
,
"|",
"^",
"!",
"~",
"=",
"<",
">",
"?"};
private static readonly string[] markChars = {
"{",
"}",
"[",
"]",
"(",
")",
".",
",",
":",
";",
"+",
"-",
"*",
"/",
"%",
"&"
,
"|",
"^",
"!",
"~",
"=",
"<",
">",
"?"};
public MainWindow()
{
InitializeComponent();
}
private void OpenCommandHandler(
object sender,
ExecutedRoutedEventArgs e)
{
OpenFileDialog ofd =
new OpenFileDialog();
if (ofd.ShowDialog() ==
true)
{
string content;
using (
FileStream fs =
new FileStream(ofd.FileName,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
using (
StreamReader sr =
new StreamReader(fs))
{
content = sr.ReadToEnd();
}
List<
string> tokens = GetTokens(content);
string htmlCode = GetHtmlCode(tokens);
this.textBox1.Text =
"<!DOCTYPE html><html><head><title></title></head><body>" + htmlCode +
"</body></html>";
}
}
private static string GetHtmlCode(List<
string> tokens)
{
HashSet<
string> keywordSet =
new HashSet<
string>(keywords);
string htmlCode =
"";
foreach (
string token
in tokens)
{
if (token ==
"\r\n" || token ==
"\n")
{
htmlCode +=
"<br/>";
}
else if (keywordSet.Contains(token))
{
htmlCode +=
"<span style=\"color:blue;\">" + token +
"</span>";
}
else
{
string tempHtmlCode =
HttpUtility.HtmlEncode(token);
tempHtmlCode = tempHtmlCode.Replace(
" ",
" ");
if (token.StartsWith(
"\"") || token.StartsWith(
"'"))
{
tempHtmlCode =
"<span style=\"color:#990000;\">" + tempHtmlCode +
"</span>";
}
htmlCode += tempHtmlCode;
}
}
return htmlCode;
}
private static List<
string> GetTokens(
string content)
{
string marksRegex =
"";
foreach (
string mark
in marks)
{
foreach (
char c
in mark)
{
marksRegex +=
"\\" + c;
}
marksRegex +=
"|";
}
string markCharsRegex =
"";
foreach (
string c
in markChars)
{
markCharsRegex +=
"\\" + c;
}
Regex regex =
new Regex(
"(\"(?:[^\"\\\\]|\\\\.)*\")|('(?:[^\'\\\\]|\\\\.)*')|\\r?\\n|\\s+|" +
marksRegex +
"[^\\s\"\'\\r\\n" + markCharsRegex +
"]+");
List<
string> tokens =
new List<
string>();
MatchCollection mc = regex.Matches(content);
foreach (
Match m
in mc)
{
tokens.Add(m.Value);
}
return tokens;
}
}
}