Edited at
EmacsDay 19

カラースキームスキームとしてのカラースキーム

これは Emacs アドベントカレンダー 19 日目の記事です。

この記事では、カラースキーム設計についての、新しいアイデアを提案します。


カラースキームの二つの機能

私は、カラースキームには二つの機能があると思っています:


  1. カラーパレットを決める機能


  2. それぞれの face について、カラーパレット上の色をひとつづつ割り当てる機能


具体例を挙げると、たとえば


  • fontlock-warning-face

  • show-paren-mismatch

  • flymake-errline

などは、どれも「プログラム中にあるエラー」という点で共通していて、したがって似たようなスタイル(たいていは、赤系の色)が割り当てられていてほしい気がします。これが 2. の機能と呼んでいるものです。

一方で、では「プログラム中にあるエラー」を具体的にどんな色で表示するか、たとえば落ち着いたトーンの赤にしたいとか、バッキバキのショッキングピンクにしたいとか、はまた別問題のように思えます。

これらの二つの関心ごとをうまく分離できれば、たとえば 2. の機能だけをうまくこなすパッケージがあれば、たんにカラーパレットを差し替えるだけでカラースキームを簡単に模様替えできるよね、というのがこの記事での提案です。


カラースキームジェネレータ

ではどのようにこれを実装するかなのですが、似たような試みは以前にもありました。

base16 というカラースキームフレームワークだったり、あるいは私も過去には、 solarized の内部実装を利用して同じようなことをやっていました。

これらの実装方法はおおむね共通です:カラーパレットを保持するための変数に適当な色のリストをバインドして、その状態でカラースキームを生成する関数を呼ぶと、カラースキームができる。

しかしこれらには、私には物足りないなと感じていた点がありました:


  • 色以外のスタイルを指定しづらい(たとえば、「前景色ではなく背景色をハイライトしたい」とか、「Word よろしく波線を引きたい」とか)


  • 動的に一部のスタイルを変更するのが大変


そこで私は、新しい方法で上のようなアイデアを実装する、 elemental-theme という新しいカラースキームを書いて、使っています。まだ詰め切れてない部分が多い (と感じる) のできちっと公開はしていませんが。。

https://github.com/zk-phi/elemental-theme


elemental-theme.el

elemental-theme の実装についてざっくり説明します。

まず、 elemental-theme には、基本となる (elemental な!) elemental-*-face が 16 個組み込まれています。



  • グレースケール


    • elemental-bright-bg-face

    • elemental-brighter-bg-face

    • elemental-bright-fg-face

    • elemental-dark-fg-face

    • elemental-darker-fg-face

    • elemental-hidden-fg-face




  • アクセント色


    • elemental-highlight-bg-1-face

    • elemental-highlight-bg-2-face

    • elemental-accent-fg-1-face

    • elemental-accent-fg-2-face

    • elemental-accent-fg-3-face

    • elemental-accent-fg-4-face




  • 特定の意味を持つアクセント色


    • elemental-red-face

    • elemental-blue-face

    • elemental-green-face

    • elemental-orange-face



命名についてですが、なんとなく次のようなポリシーを持っています:



  • -fg--bg- は、同じテキストに対してこれらの二つのスタイルが同時に適用される可能性があることを示唆する



    • -fg- の方が -bg- よりも出現頻度が高い




  • elemental-<色名>-face は、特定の色相が一般的に期待されていることを示唆する


    • たとえば、 diff で削除された行は red 、追加された行は green

    • たとえば、エラーは red

    • たとえば、警告はエラーよりも少し落ち着いた、 orange



ただ、命名や、 elemental なスタイルのセットについては、まだ詰める余地があるなと思っているので今後変わる可能性があります。

さて、肝心の elemental-theme は、これらの elemental なスタイルを inherit を使って片っ端から色々な face に割り当てていきます。ときには、一つの face に複数のスタイルを継承させる場合もあります。

これによって、対応するほとんどすべての face が elemental なスタイルを継承する状態になるので、ユーザーはたんにこれらのスタイルを適宜変更するだけで模様替えすることができます。

(set-face-foreground 'elemental-accent-fg-1-face "#c4cbee")

ただし default, cursor については、どうやら inherit を設定しても正しく反映されないようだったので、これらは現状では別途設定することになります (これらも elemental なスタイルの一つなんだと思うことにしました)。

また、未対応の face についても、たんに elemental なスタイルのいずれかを選んで適宜適用してやれば、テーマの設定を一貫することができます。

(set-face-attribute 'highlight-indent-guides-top-character-face nil

:foreground 'unspecified
:inherit 'elemental-darker-fg-face)

一般的なカラースキームで未対応の face に対応しようとした場合、そのカラースキームのコードからそれらしいカラーコードを抜き出して直接指定するとか、

(set-face-foreground 'highlight-indent-guides-top-character-face "#aaaaaa")

あるいは同じ色で装飾されるとわかっている face を適当に一つ選んで、次のように指定することになると思います。

(set-face-foreground 'highlight-indent-guides-top-character-face

(face-foreground 'indent-guide-face))

この方法だと、たとえば元のカラースキームに修正が入った場合や、あるいは別のカラースキームに移行しようと思ったとき、これらも同時に修正してやる必要が出てくるので面倒だし、うっかり忘れてしまいそうです。

elemental-theme では、個々の具体的な face へのスタイルの割り当てとカラーパレットの管理とが完全に分離されているため、このような問題が起こりづらくなっています。


elemental-theme を使った配色例

最後に、 elemental-theme を利用して実際に配色した例を紹介します。


  • solarized 風


    • #b58900, #cb4b16, #dc322f, #d33682, #6c71c4, #268bd2, #2aa198, #859900




  • emacs-tron-theme 風


    • #74abbe, orange, red, magenta, violet, #ec9346, #e8b778, #a4cee5




  • planet 風


    • #e9b96e, #ff8683, #fe5450, #a6a1ea, SlateBlue, #729fcf, #649d8a, #c4dde8




  • monoplanet 風 (planet から色数を減らしたテーマ)


    • #e0b776, #729fcf, #ff8683, #c0c0c0, #c0c0c0, #c0c0c0, #649d8a, #9e9e9e




まとめ

こんな感じで責務を分離すると、カラースキームの管理も簡単になるんじゃないか、というアイデアの共有でした!

明日は hyakt さんによる dashboard.el の紹介です。