Linux の定番シンタックスハイライトツール Source-highlight を覚える


最近少し調べていた、Source-highlight についてまとめました。
less のシンタックスハイライトでお馴染みのツールですが、調べてみるとなかなか汎用性のある面白い設計で、それだけのツールとして済ませるというのは余りにもったいないと思い筆をとりました。
特に新しい話題でもないですが、「枯れた技術は神」ということで、この記事を通して推してみたいと思います。

Source-highlight とは?

GNU プロジェクトの一部で、正式名称は GNU Source-highlight。
Lorenzo Bettini 氏によって開発されたツールで、2001 年より提供されています。
最新バージョンは 3.1.8 で、現在も継続してメンテナンスされています。
なお、本稿はこのバージョンのドキュメントを元に執筆したものです。

Source-highlight は、フォーマット非依存のシンタックスハイライトツールです。
入力として受け取ったソースコードやログファイルを、シンタックスハイライトを施して出力します。

主要なプログラミング言語はもちろんのこと、CoffeeScript や Go といった新し目の言語にも対応しています。
また、HTML, CSS といったマークアップ言語の他に、JSON, XML, ini といった設定ファイル形式にも対応しています。
出力形式のデフォルトは HTML ですが、LATEX や MediaWiki など別の形式も用意されています。

主要なパッケージマネージャではカバーされているので、インストールは簡単です。
CentOS 7 環境で Yum, macOS 環境で Homebrew によるインストールを確認しました。

Source-highlight のココがイケてる

1つ目のポイントは、ANSI エスケープシーケンスとしての出力に対応していることです。
ANSI エスケープシーケンスとは、端末エミュレータに対してメタ的な表現を指示する文字のことです。
Source-highlight はシンタックスハイライトとして、テキスト色変更のエスケープシーケンスを挿入します。
得られた出力は、端末エミュレータ上でカラーリングされた状態で表示されます。

Source-highlight は、これの有効な使い道として less コマンドとの連携を提案しています。
less は非常に利便性の高いコマンドですが、シンタックスハイライトに対応していません。
そのため、view コマンドなどと比較したときに、視認性に欠ける点がデメリットになります。
ただし、Source-highlight を使うことで、less コマンドでもシンタックスハイライトが実現できます。
具体的には、入力を一旦 Source-highlight に渡して、出力を less コマンドで受けます。
シンタックスに応じたエスケープシーケンスが含まれているので、less のインタフェイス上でもカラーリングされて表示されます。

less コマンドとの連携は、環境変数の設定のみで簡単に自動化できます。
これは、Source-highlight のドキュメント上でも案内されている、オフィシャルな方法です。
/path/to/src-hilite-lesspipe.sh の部分は、システムに合わせて適宜変更してください。

export LESSOPEN="| /path/to/src-hilite-lesspipe.sh %s"
export LESS=' -R '

2つ目のポイントは、ツールとしての拡張性の高さです。
シンタックスの解析は、フォーマットごとの「言語定義ファイル」に基づいています。
多くの言語定義ファイルがデフォルトで用意されていますが、自作したものを追加することも可能です。
扱いたい言語がデフォルトでサポートされていなかったり、新しい言語が将来現れても、
言語定義ファイルを自作すれば、基本的にはあらゆるフォーマットへ対応可能です。

入力形式だけでなく、出力形式も言語定義ファイルの自作によって追加が可能です。
デフォルトでサポートされないような限られた用途であっても、それに応じた任意の出力を実現できます。
また、どのシンタックスにどの色を割り当てるかも、変更が可能です。
スタイルファイルというものを自作して、実行時に指定します。

Source-highlight を使ってみる

デモとして、Go 言語のソースファイルを Source-highlight を通して、HTML 形式で出力してみます。
ソースファイルのサンプルとして、A Tour of Go に登場する basic-types.go プログラムを拝借します。

$ vi basic-types.go
package main

import (
    "fmt"
    "math/cmplx"
)

var (
    ToBe   bool       = false
    MaxInt uint64     = 1<<64 - 1
    z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
    const f = "%T(%v)\n"
    fmt.Printf(f, ToBe, ToBe)
    fmt.Printf(f, MaxInt, MaxInt)
    fmt.Printf(f, z, z)
}

早速、実行してみましょう。
コマンド名は、そのまま source-highlight です。
第一引数に入力とするファイルのパスを渡します。
入力形式は、実行時にファイル名の拡張子から自動的に判別されので、明示的に指定する必要はありません。
また、出力形式ですが、HTML はデフォルトの指定値ですので、こちらも明示的に指定する必要はありません。

$ source-highlight basic-types.go
Processing basic-types.go ... created basic-types.go.html

無事、html ファイルが生成されました。

それでは、出力結果を確認します。
pre タグで囲まれた HTML 形式の出力であることがわかります。
また、シンタックスに合わせた font color タグが、随所に挿入されています。

$ cat basic-types.go.html
<!-- Generator: GNU source-highlight 3.1.8
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><b><font color="#0000FF">package</font></b> main

<b><font color="#0000FF">import</font></b> <font color="#990000">(</font>
    <font color="#FF0000">"fmt"</font>
    <font color="#FF0000">"math/cmplx"</font>
<font color="#990000">)</font>
...
(省略)

最後にブラウザで開いてみましょう。
ここでは、キャプチャ画像を掲載します。
シンタックスハイライトが、正しくなされていることがわかります。

サードパーティ製の言語定義ファイルを適用してみる

近年人気を集めている Rust は、デフォルトでサポートされていない入力形式です。
しかし、有志によって lang ファイルが公開されています。
こういった、ユーザコミュニティによる協力が得られやすい点においても、拡張性の高さは正義と言えます。
2つ目のデモとして、この lang ファイルを Source-highlight に適用して、Rust のソースコードを処理してみましょう。

まず、lang ファイルをダウンロードします。
lang ファイルは、Espians 社の tav さんが Gist 上で公開されているものです。
これをダウンロードして、インストールパスの share/source-highlight 以下に置きます。
ちなみに、macOS で Homebrew インストールした場合は、/usr/local/ がインストールパスになります。

$ wget https://gist.githubusercontent.com/tav/3846383/raw/6fb082af6ee79c4f4722946f92e314f49ee8ea9c/rust.lang
... ‘rust.lang’ saved [556/556]
$ mv rust.lang /usr/local/share/source-highlight

次に、Source-highlight の言語対応ファイルを編集します。
言語対応ファイルとは、ファイルの拡張子と言語定義ファイルの対応付けをしている設定ファイルのことです。
ここに、rust 拡張子と rust.lang ファイルの対応を追加します。
言語対応ファイルは、インストールパスの share/source-highlight/lang.map にあります。

$ echo "rust = rust.lang" >> /usr/local/share/source-highlight/lang.map

これで、準備は完了しました。
最後に、Rust のソースファイルのサンプルを用意します。
ここでは、Rust by Example から loop のサンプルコードを拝借します。

$ vi loop.rust
fn main() {
    let mut count = 0u32;

    println!("Let's count until infinity!");

    // Infinite loop
    loop {
        count += 1;

        if count == 3 {
            println!("three");

            // Skip the rest of this iteration
            continue;
        }

        println!("{}", count);

        if count == 5 {
            println!("OK, that's enough");

            // Exit this loop
            break;
        }
    }
}

それでは、Source-highlight を実行してみましょう。
source-highlight には、各行の先頭に行番号を付与するオプションがあります。
今回はこの機能も使ってみようと思うので、--line-number パラメータを付与します。

$ source-highlight --line-number loop.rust
Processing loop.rust ... created loop.rust.html

こちらも、問題なく生成されました。

ブラウザで開いて、実行結果を確認しましょう。
Rust のシンタックスに合わせて、正しくカラーリングされていることがわかります。
また、パラメータ指定した行番号ですが、こちらも正しく付与されています。

まとめ

Source-highlight は、フォーマット非依存のシンタックスハイライトツールです。
多くのプログラミング言語、マークアップ言語、設定ファイル形式に対応しています。
ANSI エスケープシーケンスの出力に対応しているので、less のカラーリングに重宝します。
また、言語定義ファイルやスタイルといった仕様を通して、高い拡張性を提供します。

最後まで読んで頂いて、有難うございました。

出典