様々な言語に対応したシンタックスハイライトツールHighlightについて。

ソースからのビルド・インストール

ソースのダウンロード、展開。

wget http://www.andre-simon.de/zip/highlight-2.16.tar.bz2
tar -xvf highlight-2.16.tar.bz2
cd highlight-2.16/

パッケージにはconfigureは含まれておらず、標準では/usrにインストールされる。 /usr以外にインストールする場合は、以下のようにmakefileを直接編集する。

プレフィックスの変更
11c11
< PREFIX = /usr
---
> PREFIX = /usr/local
 PREFIX = /usr/local]]>

make, make installする。

make
sudo make install

ここまでの操作ではコマンドラインインターフェイスのみしかビルドされない。 GUIが必要な場合は、以下のコマンドでビルドする。

make gui
sudo make install-gui

インストールできたら、確認をかねてhighlight -pでハイライトできる言語の一覧を表示してみる。

$ highlight -p

Installed language definitions (located in /usr/local/share/highlight/langDefs/):

Informix            : 4gl
ASCEND              : a4c
Advanced Backus-Naur Form: abnf
ABAP/4              : abp
  :
  :

SWIGでC#用バインディングを作成する

examples/swig/highlight.iにSWIGのインターフェイス定義ファイルが用意されているので、これを使ってC#用バインディングを作成する。 なお、ここで使用したSWIGのバージョンは1.3.36である。

用意されているmakefileはPython, Perl用のバインディングを作成するようにしか書かれていないので、これに追記してC#用バインディングを作成するmakefileを書く。 追記する内容は以下の通り。

makefileに追記する内容
2a3
> CSC=gmcs
25a27,33
> csharp:
> 	${MAKE} -C ../../ -f ./makefile
> 	swig -c++ -csharp -o highlight_wrap.cpp -dllimport libhighlight -namespace highlight highlight.i
> 	${CXX} -c highlight_wrap.cpp -I${HL_SRC}
> 	${CXX} -shared -s highlight_wrap.o  -L${HL_SRC} -lhighlight -o libhighlight.so
> 	patch -d . -p0 < highlight.patch
> 	${CSC} /t:library /out:highlight.dll *.cs
34c42
< .PHONY: python python-clean perl perl-clean clean
---
> .PHONY: python python-clean perl perl-clean clean csharp csharp-clean
 CSC=gmcs
25a27,33
> csharp:
> 	${MAKE} -C ../../ -f ./makefile
> 	swig -c++ -csharp -o highlight_wrap.cpp -dllimport libhighlight -namespace highlight highlight.i
> 	${CXX} -c highlight_wrap.cpp -I${HL_SRC}
> 	${CXX} -shared -s highlight_wrap.o  -L${HL_SRC} -lhighlight -o libhighlight.so
> 	patch -d . -p0 < highlight.patch
> 	${CSC} /t:library /out:highlight.dll *.cs
34c42
< .PHONY: python python-clean perl perl-clean clean
---
> .PHONY: python python-clean perl perl-clean clean csharp csharp-clean]]>

ここでswigに渡しているオプションの詳細は次の通り。

-dllimport libhighlight
参照するライブラリのファイル名(DllImport属性で指定される)をlibhighlightに指定
-namespace highlight
生成されるアセンブリのルート名前空間をhighlightに指定

また、swigが生成するRegexDef.cs, ReGroup.cs, RegexElement.csをそのままコンパイルするとエラーとなってしまうので、修正する必要がある。 上記のmakefile内で自動的にソースを修正するようにしてあるので、以下の内容でパッチを作成しておく。

highlight.patch
Index: RegexDef.cs
===================================================================
--- RegexDef.cs.org	2009-11-01 08:43:58.000000000 +0900
+++ RegexDef.cs	2009-11-01 08:44:51.000000000 +0900
@@ -48,9 +48,7 @@
       if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
     } 
     get {
-      SWIGTYPE_p_string ret = new SWIGTYPE_p_string(highlightPINVOKE.RegexDef_reString_get(swigCPtr), true);
-      if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
-      return ret;
+      return highlightPINVOKE.RegexDef_reString_get(swigCPtr);
     } 
   }

Index: ReGroup.cs
===================================================================
--- ReGroup.cs.org	2009-11-01 08:43:58.000000000 +0900
+++ ReGroup.cs	2009-11-01 08:44:51.000000000 +0900
@@ -86,9 +86,7 @@
       if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
     } 
     get {
-      SWIGTYPE_p_string ret = new SWIGTYPE_p_string(highlightPINVOKE.ReGroup_name_get(swigCPtr), true);
-      if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
-      return ret;
+      return highlightPINVOKE.ReGroup_name_get(swigCPtr);
     } 
   }

Index: RegexElement.cs
===================================================================
--- RegexElement.cs.org	2009-11-01 08:43:58.000000000 +0900
+++ RegexElement.cs	2009-11-01 08:44:51.000000000 +0900
@@ -112,9 +112,7 @@
       if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
     } 
     get {
-      SWIGTYPE_p_string ret = new SWIGTYPE_p_string(highlightPINVOKE.RegexElement_langName_get(swigCPtr), true);
-      if (highlightPINVOKE.SWIGPendingException.Pending) throw highlightPINVOKE.SWIGPendingException.Retrieve();
-      return ret;
+      return highlightPINVOKE.RegexElement_langName_get(swigCPtr);
     } 
   }

makefileへの追記が済んだらmake csharpでmakeする。

$ make csharp
make -C ../../ -f ./makefile
make[1]: ディレクトリ `/srv/files/build/highlight/highlight-2.16' に入ります
make -C ./src -f ./makefile HL_DATA_DIR=/usr/local/share/highlight/ HL_CONFIG_DIR=/etc/highlight/
make[2]: ディレクトリ `/srv/files/build/highlight/highlight-2.16/src' に入ります
c++  -o highlight arg_parser.o cmdlineoptions.o main.o help.o -L. -lhighlight
make[2]: ディレクトリ `/srv/files/build/highlight/highlight-2.16/src' から出ます
make[1]: ディレクトリ `/srv/files/build/highlight/highlight-2.16' から出ます
swig -c++ -csharp -o highlight_wrap.cpp -dllimport libhighlight -namespace highlight highlight.i
../../src/core/codegenerator.h:86: Warning(503): Can't wrap 'operator =' unless renamed to a valid identifier.
g++ -c highlight_wrap.cpp -I../../src/
g++ -shared -s highlight_wrap.o  -L../../src/ -lhighlight -o libhighlight.so
patch -d . -p0 < highlight.patch
patching file RegexDef.cs
patching file ReGroup.cs
patching file RegexElement.cs
gmcs /t:library /out:highlight.dll *.cs

これでライブラリhighlight.soおよびラッパhighlight.dllが作成される。 作成したラッパが動作するかテストコードを作ってテストしてみる。

testmod.cs
using System;

using highlight;

class testmod {
  public static void Main(string[] args)
  {
    var gen = CodeGenerator.getInstance(OutputType.HTML);
    var input =
@"public class Test {
  public static void Main(string[] args)
  {
    Console.WriteLine(""Hello, world!"");
  }
}";

    gen.initTheme("/usr/local/share/highlight/themes/ide-msvs2008.style");
    gen.initLanguage("/usr/local/share/highlight/langDefs/cs.lang");

    gen.setIncludeStyle(true);
    gen.setEncoding("UTF-8");

    Console.WriteLine(gen.generateString(input));

    CodeGenerator.deleteInstance(gen);
  }
}

上記のコードをコンパイル・実行すると、HTMLでマークアップされたハイライト済みのテキストが出力される。

出力例
$ gmcs -r:./highlight.dll testmod.cs && mono testmod.exe
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Source file</title>
<style type="text/css">
<!--
body.hl	{ background-color:#ffffff; }
pre.hl	{ color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';}
.hl.num { color:#000000; }
.hl.esc { color:#a31515; }
.hl.str { color:#a31515; }
.hl.dstr { color:#0000ff; }
.hl.slc { color:#008000; }
.hl.com { color:#008000; }
.hl.dir { color:#0000ff; }
.hl.sym { color:#000000; }
.hl.line { color:#2b91af; }
.hl.mark	{ background-color:#ffffbb;}
.hl.kwa { color:#0000ff; }
.hl.kwb { color:#0000ff; }
.hl.kwc { color:#2b91af; }
.hl.kwd { color:#000000; }
//-->
</style>
</head>
<body class="hl">
<pre class="hl"><span class="hl kwa">public class</span> Test <span class="hl sym">{</span>
  <span class="hl kwa">public static</span> <span class="hl kwb">void</span> <span class="hl kwd">Main</span><span class="hl sym">(</span><span class="hl kwb">string</span><span class="hl sym">[]</span> args<span class="hl sym">)</span>
  <span class="hl sym">{</span>
    Console<span class="hl sym">.</span><span class="hl kwd">WriteLine</span><span class="hl sym">(</span><span class="hl str">&quot;Hello, world!&quot;</span><span class="hl sym">);</span>
  <span class="hl sym">}</span>
<span class="hl sym">}</span>
</pre>
</body>
</html>
<!--HTML generated by highlight 2.13, http://www.andre-simon.de/-->