やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

highlight シンタックス・ハイライト用ツールを使ってみた

 超便利。ファイルマネージャrangerでも使われている。HTMLやANSIエスケープシーケンス等で出力できる。

対象環境

  • Raspbierry pi 3 Model B+
  • Raspbian stretch 9.0 2018-11-13
  • bash 4.4.12

公式

手順

  1. インストール
  2. バージョン
  3. ヘルプ
  4. 最新の設定ファイル取得
  5. 使ってみる

 面倒なら以下でインストール・最新設定ファイル入手できる。

install_highlight.sh

#!/bin/bash
set -Ceu
#-----------------------------------------------------------------------------
# install_highlight.sh
# 作成日時: 2019-03-17 17:00:54
#-----------------------------------------------------------------------------
Run() {
    sudo apt install -y highlight
    # 最新は3.49。だがaptは古く3.18しかない。この場合markdown,json,yaml用シンタックス定義ファイルがないため、それらの言語がハイライトされない。そこでgithubから最新定義ファイル一式をダウンロードする。
    mkdir -p /tmp/work
    cd /tmp/work
    git clone https://github.com/andre-simon/highlight
    cd highlight
    sudo cp -Ra /tmp/work/highlight/langDefs/* /usr/share/highlight/langDefs/
    sudo cp -Ra /tmp/work/highlight/themes/* /usr/share/highlight/themes/
    rm -Rf /tmp/work/highlight
#  以下コマンドで定義ファイルが存在することを確認する
#  highlight --list-langs | grep 'md'
#  highlight --list-langs | grep 'json'
#  highlight --list-langs | grep 'yaml'
}
Run

1. インストール

sudo apt install -y highlight

2. バージョン

$ highlight --version

 highlight version 3.18
 Copyright (C) 2002-2013 Andre Simon <andre.simon1 at gmx.de>

 Argparser class
 Copyright (C) 2006-2008 Antonio Diaz Diaz <ant_diaz at teleline.es>

 Artistic Style Classes (2.04)
 Copyright (C) 2006-2013 by Jim Pattee <jimp03 at email.com>
 Copyright (C) 1998-2002 by Tal Davidson

 Diluculum Lua wrapper (1.0)
 Copyright (C) 2005-2013 by Leandro Motta Barros

 xterm 256 color matching functions
 Copyright (C) 2006 Wolfgang Frisch <wf at frexx.de>

 This software is released under the terms of the GNU General Public License.
 For more information about these matters, see the file named COPYING.

3. ヘルプ

$ highlight --help
USAGE: highlight [OPTIONS]... [FILES]...

General options:

 -B, --batch-recursive=<wc>     convert all matching files, searches subdirs
                                  (Example: -B '*.cpp')
 -D, --data-dir=<directory>     set path to data directory (deprecated)
     --config-file=<file>       set path to a lang or theme file
 -d, --outdir=<directory>       name of output directory
 -h, --help                     print this help
 -i, --input=<file>             name of single input file
 -o, --output=<file>            name of single output file
 -p, --list-langs               list installed language definitions (deprecated)
 -P, --progress                 print progress bar in batch mode
 -q, --quiet                    supress progress info in batch mode
 -S, --syntax=<type>            specify type of source code
 -v, --verbose                  print debug info
 -w, --list-themes              list installed colour themes (deprecated)
     --force                    generate output if input syntax is unknown
     --list-scripts=<type>      list installed scripts
                                  <type>=[langs, themes, plugins]
     --plug-in=<script>         execute Lua plug-in script; repeat option to
                                  execute multiple plug-ins
     --plug-in-read=<path>      set input file for a plug-in (e.g. "tags")
     --print-config             print path configuration
     --print-style              print stylesheet only (see --style-outfile)
     --skip=<list>              ignore listed unknown file types
                                  (Example: --skip='bak;c~;h~')
     --start-nested=<lang>      define nested language which starts input
                                  without opening delimiter
     --validate-input           test if input is text, remove Unicode BOM
     --version                  print version and copyright information


Output formatting options:

 -O, --out-format=<format>      output file in given format
                                  <format>=[html, xhtml, latex, tex,
                                  odt, rtf, ansi, xterm256, bbcode, svg]
 -c, --style-outfile=<file>     name of style file or print to stdout, if
                                  'stdout' is given as file argument
 -e, --style-infile=<file>      file to be included in style-outfile
 -f, --fragment                 omit document header and footer
 -F, --reformat=<style>         reformats and indents output in given style
                                  <style>=[allman, banner, gnu,
                                  horstmann, java, kr, linux, otbs,
                                  stroustrup, whitesmith, google, pico, lisp]
 -I, --include-style            include style definition
 -J, --line-length=<num>        line length before wrapping (see -W, -V)
 -j, --line-number-length=<num> line number width incl. left padding
 -k, --font=<font>              set font (specific to output format)
 -K, --font-size=<num?>         set font size (specific to output format)
 -l, --line-numbers             print line numbers in output file
 -m, --line-number-start=<cnt>  start line numbering with cnt (assumes -l)
 -s, --style=<style>            set colour style (see -w)
 -t, --replace-tabs=<num>       replace tabs by <num> spaces
 -T, --doc-title=<title>        document title
 -u, --encoding=<enc>           set output encoding which matches input file
                                  encoding; omit encoding info if set to NONE
 -V, --wrap-simple              wrap long lines without indenting function
                                  parameters and statements
 -W, --wrap                     wrap long lines
     --wrap-no-numbers          omit line numbers of wrapped lines
                                  (assumes -l)
 -z, --zeroes                   pad line numbers with 0's
     --kw-case=<case>           change case of case insensitive keywords
                                  <case> =  [upper, lower, capitalize]
     --delim-cr                 set CR as end-of-line delimiter (MacOS 9)
     --no-trailing-nl           omit trailing newline


(X)HTML output options:

 -a, --anchors                  attach anchor to line numbers
 -y, --anchor-prefix=<str>      set anchor name prefix
 -N, --anchor-filename          use input file name as anchor prefix
 -C, --print-index              print index with hyperlinks to output files
 -n, --ordered-list             print lines as ordered list items
     --class-name=<name>        set CSS class name prefix;
                                  omit class name if set to NONE
     --inline-css               output CSS within each tag (verbose output)
     --enclose-pre              enclose fragmented output with pre tag 
                                  (assumes -f)


LaTeX output options:

 -b, --babel                    disable Babel package shorthands
 -r, --replace-quotes           replace double quotes by \dq{}
     --pretty-symbols           improve appearance of brackets and other symbols


RTF output options:

 -x, --page-size=<ps>           set page size 
                                  <ps> = [a3, a4, a5, b4, b5, b6, letter]
     --char-styles              include character stylesheets


SVG output options:

     --height                   set image height (units allowed)
     --width                    set image width (see --height)


GNU source-highlight compatibility options:

     --doc                      create stand alone document
     --no-doc                   cancel the --doc option
     --css=filename             the external style sheet filename
     --src-lang=STRING          source language
 -t, --tab=INT                  specify tab length
 -n, --line-number[=0]          number all output lines, optional padding
     --line-number-ref[=p]      number all output lines and generate an anchor,
                                  made of the specified prefix p + the line
                                  number  (default='line')
     --output-dir=path          output directory
     --failsafe                 if no language definition is found for the
                                  input, it is simply copied to the output


If no in- or output files are specified, stdin and stdout will be used.
HTML will be generated unless an other output format is given. Style definitions
are stored in highlight.css (HTML, XHTML, SVG) or highlight.sty (LaTeX, TeX)
if neither -c nor -I is given.
Reformatting code (-F) will only work with C, C++, C# and Java input files.
Wrapping lines with -V or -W will cause faulty highlighting of long single
line comments and directives. Use with caution.
See README how to install own scripts in the home directory.

Updates and information: http://www.andre-simon.de/

4. 最新の設定ファイル取得

 githubから最新の設定ファイルを取得する。ターミナルを起動して以下コマンドを実行すればいい。

mkdir -p /tmp/work
cd /tmp/work
git clone https://github.com/andre-simon/highlight
cd highlight
sudo cp -Ra /tmp/work/highlight/langDefs/* /usr/share/highlight/langDefs/
sudo cp -Ra /tmp/work/highlight/themes/* /usr/share/highlight/themes/
rm -Rf /tmp/work/highlight
highlight --list-langs | grep 'md'

 必要性については以下で語る。一言でいうとMarkdownのハイライトに対応するため。

 まずhighlightでシンタックス・ハイライトできる言語を以下コマンドで一覧する。

highlight --list-langs

 残念なことにMarkdownがない。最新版3.49なら対応しているようだが、sudo apt install -y highlightでインストールしたバージョン3.18は古くて未対応。(highlight --list-langsGitHub Flavored Markdown : md ( markdown )がない)

 でも安心して欲しい。Markdown用の設定ファイルさえ追加すればいい。ネットから落として追加する。

 まず設定ファイルを保存するパスを探した。

$ which highlight
/usr/bin/highlight
$ cd /usr/share/highlight/langDefs

 GitHubには最新版highlightの言語定義ファイルがある。この中にあるmd.langMarkdown用の設定ファイル。

 これを/usr/share/highlight/langDefsに配置すればいい。

sudo cd /usr/share/highlight/langDefs
sudo wget https://raw.githubusercontent.com/andre-simon/highlight/master/langDefs/md.lang

 追加されたことを確認。

$ highlight --list-langs
...
GitHub Flavored Markdown      : md
...

 じつは他にも未対応の言語がある。json.lang, yaml.langがなかった。他にもあるかもしれない。

 いっそgithubからすべてダウンロードしたい。

 設定ファイルの配置は/usr/shareのみ有効。システム全体でなくユーザ固有設定にしたくて~/.configにダウンロードしたが参照されなかった。

 wget -Aディレクトリコピーしようとしてもダメだった。GitHubから一部ディレクトリだけを取得するにはsvn exportコマンドを使うらしい。わざわざsvnコマンドを入れるのも嫌なのでgit cloneでやる。

mkdir -p /tmp/work
mkdir -p ~/.config/highlight/langDefs
mkdir -p ~/.config/highlight/themes
cd ~/.config/highlight/langDefs
wget -A *.lang https://raw.githubusercontent.com/andre-simon/highlight/master/langDefs
cd ~/.config/highlight/themes
wget -A *.theme https://raw.githubusercontent.com/andre-simon/highlight/master/themes

 ~/.configにダウンロードしても参照されず。

mkdir -p /tmp/work
mkdir -p ~/.config/highlight/langDefs
mkdir -p ~/.config/highlight/themes
cd /tmp/work
git clone https://github.com/andre-simon/highlight
cd highlight
cp -Ra /tmp/work/highlight/langDefs/* ~/.config/highlight/langDefs/
cp -Ra /tmp/work/highlight/themes/* ~/.config/highlight/themes/
rm -Rf /tmp/work/highlight
highlight --list-langs | grep 'json'
highlight --list-langs | grep 'json'
highlight --list-langs | grep 'yaml'

 /usr/sharegithubの最新設定ファイルでコピーすると成功。

mkdir -p /tmp/work
cd /tmp/work
git clone https://github.com/andre-simon/highlight
cd highlight
sudo cp -Ra /tmp/work/highlight/langDefs/* /usr/share/highlight/langDefs/
sudo cp -Ra /tmp/work/highlight/themes/* /usr/share/highlight/themes/
rm -Rf /tmp/work/highlight
highlight --list-langs | grep 'md'
highlight --list-langs | grep 'json'
highlight --list-langs | grep 'yaml'

5. 使ってみる

5-1. 出力形式

 -Oフラグ。html, xhtml, latex, tex, odt, rtf, ansi, xterm256, bbcode, svgに対応。デフォルトはhtml

5-1-1. ASCIIエスケープ

 ASCIIエスケープ形式で出力する。ターミナル上でハイライトされる。8色のみ。

highlight -O ansi /home/pi/.config/ranger/scope.sh

 256色に対応するターミナルアプリなら以下。Raspbianのlxterminalなら可能。

export TERM=xterm-256color
highlight -O xterm256 /home/pi/.config/ranger/scope.sh

 less -Rで表示。

echo -e 'echo '\''Hello world!!'\' | highlight -S bash -O xterm256 -t 4 | less -R

 ファイルを表示。

cd /tmp/work
echo -e 'echo '\''Hello world!!'\' > a.sh
highlight -S bash -O xterm256 -t 4 a.sh | less -R

5-1-2. HTML

highlight /home/pi/.config/ranger/scope.sh

 HTMLを標準出力に垂れ流す。何も嬉しくない。そこでHTMLプレビューすべくzenityコマンドに渡す。

highlight --inline-css /home/pi/.config/ranger/scope.sh > /tmp/work/a.sh.html; zenity --text-info --html --filename /tmp/work/a.sh.html

f:id:ytyaru:20190317171647p:plain

 ソースコードがハイライトされた。

 --inline-cssによりHTML内にCSSを埋め込む。これによりzenityでCSSが反映される。デフォルトではCSSが外部ファイル化されるが、そのときzenityではCSSファイル参照されずハイライトされない。よってzenityで表示するときは--inline-cssを付与すること。

 デフォルトのcharsetがISO-8859-1なのでUTF-8にする。<meta charset="UTF-8"><meta charset="UTF-8">になる。

highlight /home/pi/.config/ranger/scope.sh --encoding UTF-8

 また、<html>,<head>,<body>などのタグを排除し、コード部分だけを<pre>で囲ったコードだけ欲しいときは以下。

$ highlight -f --enclose-pre  /home/pi/.config/ranger/scope.sh
フラグ 意味
-f <html>等を削除しコード部分のみ出力する
--enclose-pre <pre>で囲む

 これができればソースコード部分だけを他のHTMLに挿入するなど応用できる。

 ただ、ふつうは<pre><code>で囲うもの。これが出来ないのが残念。もっとも、-fを使って以下のようにすれば解決する。

code=$(highlight -f /home/pi/.config/ranger/scope.sh)
#echo -e `<pre><code>\n${code}\n</code></pre>`
echo "<pre><code>"
echo "${code}"
echo "</code></pre>"

 でもそれを言ったら--enclose-preフラグは不要なわけで……。--enclose-pre-codeフラグがあれば良かったのに。なんとも中途半端。

 echo -eにしなかったのはコード改変防止のため。もしソースコードの中に\nという文字列があれば改行コードになってしまう。正規表現のときなどは改行コードを表すときに\nという文字列を使う。これが改行コードに変換されると困る。よってecho -e "${code}"したらダメ。

5-2. 入力形式

 ファイルから。

highlight -O xterm256 /home/pi/.config/ranger/scope.sh

 標準入力から。

cat /home/pi/.config/ranger/scope.sh | highlight --syntax bash -O xterm256 

 標準入力の場合は拡張子がないため言語を特定できない。そこで--syntaxで言語を指定する。

 これで動的なテキストに対してハイライトできる。

5-3. 行数表示

 左端に行数を表示する。

highlight -O xterm256 --line-numbers /home/pi/.config/ranger/scope.sh

 ファイル末尾から3行分だけ表示する。

cat /home/pi/.config/ranger/scope.sh | tail -n -3 | highlight --syntax bash -O xterm256 --line-numbers --line-number-start $(( $(cat /home/pi/.config/ranger/scope.sh | wc -l) + 1 - 3 ))

 開始行数の計算が面倒。

/tmp/cutlines.sh

#!/bin/bash
set -Ceu
#-----------------------------------------------------------------------------
# cutlines.sh
# 作成日時: 2019-03-17 15:43:06
#-----------------------------------------------------------------------------
# 指定範囲行のテキストを取得する。
# $1: length   require 1〜
# $2: start    option  1〜
# length:
#   * テキスト最大行を超えていたら全文対象
#   * 負数ならその数だけ末尾行を除いたテキストを返す
# start:
#   * テキスト最大行を超えていたら何も返さない。
#   * -2以下の負数ならエラー(tail: 無効な行数です: `+-1')
CutLines() {
    local length=$1
    [[ $# -lt 2 ]] && local start=0 || local start=$2
#  echo "$(cat -)" | tail -n +$(($start+1)) | head -n $length
    echo "$(cat -)" | tail -n +$start | head -n $length
}

 上記のスクリプト定義をロードする。

. /tmp/cutlines.sh

 先頭から3行抽出する。

$ echo -e "1\n2\n3\n4\n5" | CutLines 3
1
2
3

 先頭から1行飛ばして3行抽出する。

$ echo -e "1\n2\n3\n4\n5" | CutLines 3 1
2
3
4

 100行目から5行分表示する。

cat /home/pi/.config/ranger/scope.sh | CutLines 5 100 | highlight --syntax bash -O xterm256 --line-numbers --line-number-start 100

 わかりやすくなった。CutLinesの開始行と--line-number-startの値が同じにできた。どちらも100行目を表す。

 以下のようにすればスクリプトファイルを作成せずに済む。

cat /home/pi/.config/ranger/scope.sh| tail -n +100 | head -n 5 | highlight --syntax bash -O xterm256 --line-numbers --line-number-start 100

f:id:ytyaru:20190317172202p:plain

5-4. 対応言語

 ハイライトできる言語を調べる。

highlight --list-langs

 特定の言語を調べたいときはgrepすると絞りこめる。

$ highlight --list-langs | grep 'csharp'

C#                            : csharp ( cs )

 対応言語の数は以下。

$ echo $(highlight --list-langs | grep ': ' | wc -l)
221

 取得できた言語は--syntaxの設定値になる。たとえばC#としてハイライトするなら以下。

cat /tmp/a.cs | highlight --syntax csharp

5-4. スタイル(CSS)

highlight --list-themes

 88種類ある。

$ highlight --list-themes | head -n -3 | tail -n +4 | wc -l
88

 取得できたスタイル(CSS)は--styleの設定値になる。たとえばacidCSSを使うなら以下。

highlight --style acid -O xterm256 /home/pi/.config/ranger/scope.sh

5-5. フォント

 --styleで出力されるCSSのフォントを指定する。

$ highlight --help
...
 -k, --font=<font>              set font (specific to output format)
 -K, --font-size=<num?>         set font size (specific to output format)
highlight --inline-css --style acid -O html --font "VL ゴシック" --font-size 20 /home/pi/.config/ranger/scope.sh

 ちなみにターミナルにしても反映されない。ターミナルアプリのフォントサイズで表示される。ANSIエスケープシーケンスではフォント変更できない。

highlight --inline-css --style acid -O xterm256 --font "VL ゴシック" --font-size 20 /home/pi/.config/ranger/scope.sh

5-5. plugin

 プラグインを自作できるらしい。Lua言語で。

 bash_functions.luaBashの関数名をキーワードリストに追加するpluginらしい。

 ヘルプをみてみるとLua言語で書いたプラグインを参照する引数がある。

$ highlight --help
...
     --list-scripts=<type>      list installed scripts
                                  <type>=[langs, themes, plugins]
     --plug-in=<script>         execute Lua plug-in script; repeat option to
                                  execute multiple plug-ins
     --plug-in-read=<path>      set input file for a plug-in (e.g. "tags")

 そもそもLua言語がわからない。だがじつはLuaのREPLが標準インストールされていた。

 恒例のHelloWorldを書いてみた。

$ lua
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> print("Hello world !!")
Hello world !!

 いまいち有用さがわからないが拡張できるというだけで素晴らしい。

所感

 静的サイトジェネレータの一部機能として使えそう。Markdownプレビューアを作れそう。

 他にもパラメータがあるので詳しくはヘルプ参照。