ソースコードの記号をいい感じに表示する

ソースコードの ->=> などの記号を表示する際に、アスキー文字の組み合わせではなく → や ⇒ という風な本物の記号を使いたい。

目次

Unicode の記号を使う

一部の言語は、演算子に Unicode の記号を使える。

例えば、 Julia は標準で Unicode の演算子を持っている。(≠, ≤, ≥ など)

Haskell の場合は、ユーザー定義の演算子に Unicode の記号を使えるほか、 GHC 拡張を使えば関数型の -> や型クラス制約の => も Unicode の記号で書ける:

この方法の欠点は、特定の言語や処理系でしか使えないことである。

組版時に置き換える

LaTeX で組版する場合は、処理時に特定の記号列を置き換えるという手がある。例えば、 listings パッケージであれば literate オプションを使えば良い。

\documentclass{standalone}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{color}
\definecolor{hskeyword}{rgb}{0,0,0.5}
\lstdefinestyle{myhscode}{
  language=Haskell,
  basicstyle=\ttfamily,
  keywordstyle=\color{hskeyword},
  columns=fullflexible,
  otherkeywords={}, % デフォルトでは => が otherkeywords に含まれており、 literate の指定が効かない
  literate=*{..}{{.\kern-2pt.}}{2}
            {::}{{:\kern-2pt:}}{2}
            {->}{{\(\rightarrow\)}}{2}
            {=>}{{\(\Rightarrow\)}}{2},
  keepspaces
}
\begin{document}
\begin{lstlisting}[style=myhscode]
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y = True
              | otherwise = elem x ys
\end{lstlisting}
\end{document}

欠点は、見た目が良くても、コピペした結果が意味のあるソースコードにならないという点である。まあ、 PDF にした時点で折り返しの改行が入ったりするので、 PDF からコピペした結果なんて気にしても仕方がないという意見もあるかもしれない。

文字の間隔を詰める(カーニングの調整)だけなら、コピペ結果を変えずに済むかもしれない。

\documentclass{standalone}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{color}
\definecolor{hskeyword}{rgb}{0,0,0.5}
\lstdefinestyle{myhscode2}{
  language=Haskell,
  basicstyle=\ttfamily,
  keywordstyle=\color{hskeyword},
  columns=fullflexible,
  otherkeywords={}, % デフォルトでは => が otherkeywords に含まれており、 literate の指定が効かない
  literate=*{..}{{.\kern-2pt.}}{2}
            {::}{{:\kern-2pt:}}{2}
            {->}{{-\kern-2.5pt>}}{2}
            {=>}{{=\kern-2.5pt>}}{2},
  keepspaces
}
\begin{document}
\begin{lstlisting}[style=myhscode2]
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y = True
              | otherwise = elem x ys
\end{lstlisting}
\end{document}

特殊なフォントを使う

専用のフォントを使えば、リガチャー(合字)によって特定の記号列の見た目を変更できる。見た目だけをいじるだけなので、文字の情報は元のままである。

このようなリガチャーを持ったフォントには、

などがある。Fira Code の Alternatives の項が良くまとまっている。

今回は、 Fira Code を試してみる。配布物の中に WOFF 形式のファイルと CSS ファイルが含まれているので、 Web で使う際の手間が省ける。

参考:コーディング用フォント「Fira Code」を使おう – Qiita

Web で使う

配布物に含まれる fira_code.css を読み込むと  ‘Fira Code’ という名前のフォントが使えるようになる。リガチャーを有効にするためのCSSの設定は、 specimen.html のソースを参考にすれば良さそうだ。

適用例:

elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y = True
              | otherwise = elem x ys

LaTeX で使う

XeLaTeX または LuaLaTeX の場合は、 fontspec パッケージを使って Fira Code を読み込めば良い(フォントをシステムにインストールした場合はファイル名じゃなくてフォント名で十分だろう):

\documentclass{standalone}
\usepackage{luatex85} % standalone 環境が最新の LuaTeX に対応していないみたいなので…
\usepackage{fontspec}
\setmonofont{FiraCode-Regular.otf}[Contextuals={Alternate}]
\begin{document}
\texttt{elem :: (Eq a) => a -> [a] -> Bool}
\end{document}

筆者の環境では、 XeLaTeX の場合は Contextuals={Alternate} がなくてもリガチャーが有効になったが、 LuaLaTeX の場合は Contextuals={Alternate} が必須だった。

(ちなみに、 fontspec のマニュアルによると、 \setmonofont の直後にオプション引数を指定するのは古い書き方で、最新の書き方ではフォント名の直後に書くらしい)

verbatim や listings で使う場合も同様にすれば ::++ などのリガチャーが有効になるが、そのままでは =>-> でリガチャーが有効にならない。

どうやら、 LaTeX には verbatim 系環境で特定の文字に関するリガチャーを無効にする機能があって、それによって < > -  などのリガチャーが無効になっているようである。そういう文字の一覧は \verbatim@nolig@list に入っているので、自分でこのマクロを上書きしてやれば良い。(それによる副作用があるかは筆者は知らない)

\documentclass{article}
\usepackage{verbatim}
\usepackage{fontspec}
\setmonofont{FiraCode-Regular.otf}[Contextuals={Alternate}]

\makeatletter
% デフォルトでは \do\`\do\<\do\>\do,\do\'\do\- となっているので、 < > - を除く
\def\verbatim@nolig@list{\do\`\do\,\do\'}
\makeatother

\usepackage{listings}
\usepackage{color}
\definecolor{hskeyword}{rgb}{0,0,0.5}
\lstdefinestyle{myhscode}{
  language=Haskell,
  basicstyle=\ttfamily,
  keywordstyle=\color{hskeyword},
  columns=fullflexible,
  keepspaces
}

\begin{document}
With verbatim:
\begin{verbatim}
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y = True
              | otherwise = elem x ys
\end{verbatim}

With listings:
\begin{lstlisting}[style=myhscode]
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) | x == y = True
              | otherwise = elem x ys
\end{lstlisting}
\end{document}

リガチャーによる方法の欠点

欠点は、置き換えた結果がその文脈にとって不適切となる可能性があることである。具体的には、Haskell において Prelude.- と書いた場合のドット . とハイフン - は別のトークンだが、Fira Code においては .- が一つの記号 (.-) として扱われてしまう。他のプログラミング言語では(あるいは、 Haskell でもモジュールが絡まない場合は) .- を一つの記号として表示するのが適切かもしれないが、この文脈ではそうではないのである。

もちろん、 Hasklig のような特定のプログラミング言語に特化したフォントを使えば、こういう問題は軽減できる。

あるいは、シンタックスハイライターの応用で、リガチャーしてほしい部分だけそのフォントを使う(残りの部分は、リガチャーなしのフォントを使う)という手もあるかもしれない。

この他の欠点としては、字体へのこだわりがある人にとって字体の選択肢が減るというものもあるかもしれない。(こういうリガチャーを持った新しいフォントを作る手間がどのくらいなのか、筆者にはよくわからないが…。)