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

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

LuaおよびBoostに依存するため、あらかじめインストールしておく。

sudo apt-get install liblua5.2 liblua5.2-dev libboost-dev

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

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

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

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

また、Ubuntu 16.04ではmakeしようとした際にLuaライブラリのパス取得で失敗したため、src/makefileの次の箇所を編集した。

src/makefileの変更
36c36
41c41
< LUA_CFLAGS=$(shell pkg-config --cflags lua) -DUSE_LUA52
---
> LUA_CFLAGS=$(shell pkg-config --cflags lua5.2) -DUSE_LUA52
44c44
< LUA_LIBS=$(shell pkg-config --libs lua)
---
> LUA_LIBS=$(shell pkg-config --libs lua5.2)

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/):

ABAP/4                        : abap4 ( abp )
ABC                           : abc
Advanced Backus-Naur Form     : abnf
ActionScript                  : actionscript ( as )
ADA95                         : ada ( a adb ads gnad )
Agda                          : agda
ALGOL 68                      : algol ( alg )
  :
  :

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

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

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

makefileに追記・修正する内容
3c3,4
< CFLAGS=-g -O2 -fPIC
---
> CFLAGS=-g -O2 -fPIC -std=c++0x
> CSC=mcs
8,9c9,10
< LUA_CFLAGS=$(shell pkg-config --cflags lua)
< LUA_LIBS=$(shell pkg-config --libs lua)
---
> LUA_CFLAGS=$(shell pkg-config --cflags lua5.2)
> LUA_LIBS=$(shell pkg-config --libs lua5.2)
33a35,40
> csharp: lib-stamp
> 	swig -c++ -csharp -o highlight_wrap.cpp -dllimport libhighlight -namespace highlight highlight.i
> 	${CXX} ${CFLAGS} -c highlight_wrap.cpp ${LUA_CFLAGS} -I${HL_INC}
> 	${CXX} -shared -s highlight_wrap.o  -L${HL_SRC} -lhighlight ${LUA_LIBS} -o libhighlight.so
> 	${CSC} /t:library /out:highlight.dll *.cs
> 
48c55
< .PHONY: python python-clean perl perl-clean php php-clean clean
---
> .PHONY: python python-clean perl perl-clean php php-clean csharp csharp-clean clean

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

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

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

$ make csharp
    :
    :
swig -c++ -csharp -o highlight_wrap.cpp -dllimport libhighlight -namespace highlight highlight.i
../../src/include/codegenerator.h:85: Warning 503: Can't wrap 'operator =' unless renamed to a valid identifier.
g++ -g -O2 -fPIC -std=c++0x -c highlight_wrap.cpp -I/usr/include/lua5.2 -I../../src/include/
g++ -shared -s highlight_wrap.o  -L../../src/ -lhighlight -llua5.2 -o libhighlight.so
mcs /t:library /out:highlight.dll *.cs

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

testmod.cs
using System;

using highlight;

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

    gen.initTheme("/usr/local/share/highlight/themes/edit-msvs2008.theme");
    gen.loadLanguage("/usr/local/share/highlight/langDefs/csharp.lang");
    gen.setIncludeStyle(true);
    gen.setEncoding("UTF-8");

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

    CodeGenerator.deleteInstance(gen);
  }
}

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

出力例
$ mcs -r:./highlight.dll testmod.cs && mono testmod.exe
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Source file</title>
<style type="text/css">
/* highlight theme: Visual Studio IDE */
body.hl	{ background-color:#ffffff; }
pre.hl	{ color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New',monospace;}
.hl.num { color:#000000; }
.hl.esc { color:#a31515; }
.hl.str { color:#a31515; }
.hl.pps { color:#0000ff; }
.hl.slc { color:#008000; }
.hl.com { color:#008000; }
.hl.ppc { color:#0000ff; }
.hl.opt { color:#000000; }
.hl.ipl { color:#a31515; }
.hl.lin { color:#2b91af; }
.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 opt">{</span>
  <span class="hl kwa">static void</span> <span class="hl kwd">Main</span><span class="hl opt">(</span><span class="hl kwa">string</span><span class="hl opt">[]</span> args<span class="hl opt">)</span>
  <span class="hl opt">{</span>
    Console<span class="hl opt">.</span><span class="hl kwd">WriteLine</span><span class="hl opt">(</span><span class="hl str">&quot;Hello, world!&quot;</span><span class="hl opt">);</span>
  <span class="hl opt">}</span>
<span class="hl opt">}</span>
</pre>
</body>
</html>
<!--HTML generated by highlight 3.34, http://www.andre-simon.de/-->