Linux

lessでシンタックスハイライトする

More than 1 year has passed since last update.

less でシンタックスハイライトする方法があると聞いたので、GNU source-highlight を使った方法を試してみました。

より詳しい記事として以下のような記事もあるので参考のこと。

これらの記事では環境変数を弄っているのに対して、本記事では alias を使用する方法を紹介します。


GNU source-highlight のインストール

ほとんどの環境で less は最初から使用できるはずなのでインストールはスキップします。とは言うものの、もしかしたらバージョン違いで同じように設定できない恐れもあるので、その辺りは確認しておきます。

# cat /etc/centos-release

CentOS Linux release 7.2.1511 (Core)
# less --version
less 458 (POSIX regular expressions)
Copyright (C) 1984-2012 Mark Nudelman

less comes with NO WARRANTY, to the extent permitted by law.
For information about the terms of redistribution,
see the file named README in the less distribution.
Homepage: http://www.greenwoodsoftware.com/less

ではインストールします。root でない人は sudo してください。

# yum install source-highlight


インストールが完了したら大雑把な構成を把握します。

# rpm -q source-highlight

source-highlight-3.1.6-6.el7.x86_64
# rpm -ql source-highlight
/etc/bash_completion.d
/etc/bash_completion.d/source-highlight
/usr/bin/check-regexp
/usr/bin/cpp2html
/usr/bin/java2html
/usr/bin/source-highlight
/usr/bin/source-highlight-settings
/usr/bin/src-hilite-lesspipe.sh
/usr/lib64/libsource-highlight.so.4
/usr/lib64/libsource-highlight.so.4.0.0
/usr/share/doc/source-highlight-3.1.6
略(マニュアルっぽいhtmlファイルがいっぱい)
/usr/share/info/source-highlight-lib.info.gz
/usr/share/info/source-highlight.info.gz
/usr/share/man/man1/check-regexp.1.gz
/usr/share/man/man1/source-highlight-settings.1.gz
/usr/share/man/man1/source-highlight.1.gz
/usr/share/source-highlight
/usr/share/source-highlight/Hello.css
略(共通設定と思われるファイルがいっぱい)

バージョンは 3.1.6 がインストールされたようです。

/etc/ の下は bash 関連のようなので一旦無視でいいでしょう。実行ファイルは /usr/bin/ に入ったようです。その他はマニュアル類と、共通設定ファイルと思われるファイルがインストールされます。


使い方

先ほどインストールされた /usr/bin/src-hilite-lesspipe.sh を使います。ソースコードをこのスクリプトに与えると、カラーシーケンスが付与されたテキストが出力されます。色の定義は拡張子に依存するようです(後述)。これを -R オプション付きの less にパイプします。

やってみます。まずコマンドが長くなるので alias を設定します。

# which hilite

/usr/bin/which: no hilite in (/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin)
#
# alias hilite='/usr/bin/src-hilite-lesspipe.sh'
# alias less='less -R'
# alias | grep less
alias hilite='/usr/bin/src-hilite-lesspipe.sh'
alias less='less -R'

動作確認は以下のように。

# hilite hoge.sh | less


カラースキームの調整

デフォルトのカラースキームに不満がある場合は、/usr/share/source-highlight/ 配下のファイルを弄ります。

ごちゃごちゃしていますが、拡張子ごとに見ると以下のようなファイル構成です。

# ls /usr/share/source-highlight/ | sed -e 's/.*\.//' | sort | uniq -c

46 css
1 defaults
100 lang
2 map
21 outlang
6 style

lang は言語の構文定義ファイルのようです。outlang は出力方式を定義するファイルのようです。map は拡張子と構文定義ファイルの紐づけを行っている lang.map と、出力フォーマット名と outlnag ファイルを紐づけているらしい outlang.map の 2 つです。実際のカラースキームを定義しているのは、cssstyle のようです。style.defaultsstyle ファイル全体のデフォルト値だと思われます。

詳細はちょっとわからないので、hilite コマンドの中で何が行われているのか確認してみます。

# cat /usr/bin/src-hilite-lesspipe.sh

#! /bin/sh

for source in "$@"; do
case
$source in
*ChangeLog|*changelog)
source-highlight --failsafe -f esc --lang-def=changelog.lang --style-file=esc.style -i "$source" ;;
*Makefile|*makefile)
source-highlight --failsafe -f esc --lang-def=makefile.lang --style-file=esc.style -i "$source" ;;
*) source-highlight --failsafe --infer-lang -f esc --style-file=esc.style -i "$source" ;;
esac
done

思ったよりシンプルでした。--style-file=esc.style が怪しいですね。見てみましょう。

# cat /usr/share/source-highlight/esc.style

keyword blue b ;
type, classname darkgreen ;
string red ;
regexp orange ;
specialchar pink ;
comment cyan i ;
number purple ;
preproc darkblue b ;
symbol darkred ;
function black b;
cbracket red;
variable darkgreen ;

// line numbers
linenum yellow;

// other elements for ChangeLog and Log files
date blue ;
time darkblue ;
ip darkgreen ;
file darkblue ;
name darkgreen ;

// Internet related
url blue u;

// for diffs
oldfile orange;
newfile darkgreen;
difflines blue;

// for css
selector purple;
property blue;
value darkgreen i;

はい。お気づきでしょうか、この中に function black b; という項目があります。これは関数名を黒のボールドで表示せよという指定になるのですが、通常の端末は背景が黒であるため異様に見づらいです(というか見えませんでした)。

変えました。

# diff /usr/share/source-highlight/esc.style /usr/share/source-highlight/esc.style.20160625

10c10
< function yellow b;
---
> function black b;


おわりに

各記事では LESSLESSOPEN といった環境変数を変更しているのですが、この記事では環境変数に手を加えない方向でシンタックスハイライトできるようにしてみました。

なぜそのようなことをしたかというと、LESSOPEN には既に値が設定されていたからです(LESS は空でしたが)。

$ echo $LESSOPEN

|/usr/bin/lesspipe.sh %s

LESSOPEN に指定される値は入力プリプロセスと呼ばれるもので、less が実行される前に実行されるコマンドだそうです。

では、/usr/bin/lesspipe.sh は一体何をしているのかというと、

$ cat /usr/bin/lesspipe.sh

#!/bin/sh
#
# To use this filter with less, define LESSOPEN:
# export LESSOPEN="|/usr/bin/lesspipe.sh %s"
#
# The script should return zero if the output was valid and non-zero
# otherwise, so less could detect even a valid empty output
# (for example while uncompressing gzipped empty file).
# For backward-compatibility, this is not required by default. To turn
# this functionality there should be another vertical bar (|) straight
# after the first one in the LESSOPEN environment variable:
# export LESSOPEN="||/usr/bin/lesspipe.sh %s"

if [ ! -e "$1" ] ; then
exit 1
fi

if [ -d "$1" ] ; then
ls -alF -- "$1"
exit $?
fi

exec 2>/dev/null

# Allow for user defined filters
if [ -x ~/.lessfilter ]; then
~/.lessfilter "$1"
if [ $? -eq 0 ]; then
exit 0
fi
fi

case "$1" in
*.[1-9n].bz2|*.[1-9]x.bz2|*.man.bz2|*.[1-9n].[gx]z|*.[1-9]x.[gx]z|*.man.[gx]z|*.[1-9n].lzma|*.[1-9]x.lzma|*.man.lzma)
case "$1" in
*.gz) DECOMPRESSOR="gzip -dc" ;;
*.bz2) DECOMPRESSOR="bzip2 -dc" ;;
*.xz|*.lzma) DECOMPRESSOR="xz -dc" ;;
esac
if [ -n "$DECOMPRESSOR" ] && $DECOMPRESSOR -- "$1" | file - | grep -q troff; then
$DECOMPRESSOR -- "$1" | groff -Tascii -mandoc -
exit $?
fi ;;&
*.[1-9n]|*.[1-9]x|*.man)
if file "$1" | grep -q troff; then
man -l "$1" | cat -s
exit $?
fi ;;&
*.tar) tar tvvf "$1" ;;
*.tgz|*.tar.gz|*.tar.[zZ]) tar tzvvf "$1" ;;
*.tar.xz) tar Jtvvf "$1" ;;
*.xz|*.lzma) xz -dc -- "$1" ;;
*.tar.bz2|*.tbz2) bzip2 -dc -- "$1" | tar tvvf - ;;
*.[zZ]|*.gz) gzip -dc -- "$1" ;;
*.bz2) bzip2 -dc -- "$1" ;;
*.zip|*.jar|*.nbm) zipinfo -- "$1" ;;
*.rpm) rpm -qpivl --changelog -- "$1" ;;
*.cpi|*.cpio) cpio -itv < "$1" ;;
*.gpg) gpg -d "$1" ;;
*.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)
if [ -x /usr/bin/identify ]; then
identify "$1"
elif [ -x /usr/bin/gm ]; then
gm identify "$1"
else
echo "No identify available"
echo "Install ImageMagick or GraphicsMagick to browse images"
exit 1
fi ;;
*)
if [ -x /usr/bin/file ] && [ -x /usr/bin/iconv ] && [ -x /usr/bin/cut ]; then
case
`file -b "$1"` in
*UTF-16*) conv='UTF-16' ;;
*UTF-32*) conv='UTF-32' ;;
esac
if [ -n "$conv" ]; then
env=`echo $LANG | cut -d. -f2`
if [ -n "$env" -a "$conv" != "$env" ]; then
iconv -f $conv -t $env "$1"
exit $?
fi
fi
fi
exit 1
esac
exit $?

拡張子によって実行するコマンドを振り分けています。つまりどういうことかというと、less は各種バイナリファイルを開く(ファイルのサマリを表示する)のにも使えてしまうということです。

折角の便利機能なので、使えなくしてしまうことは避けたいと思った結果、上記のような方法を選択しました。

というわけで我が家の less でもシンタックスハイライトができるようになりました。