Vim のターミナルウィンドウの文字色を設定する

はじめに

Vim 8.1 がリリースされましたね。目玉機能の一つとしてターミナルウィンドウが挙げられています。 Vim 8.0 から存在していましたが、ついに実験的機能ではなくなった、ということでしょうか。

f:id:machakann:20180519224618j:plain

実はこのターミナルウィンドウの文字色は、設定によって変更できます。しかし、これは :highlight コマンドとは異なる方法で管理され、しかも、適切に扱うにはある程度 Vim script の知識が必要になります。なので、ここに簡単にまとめておこうと思います。

ただし、注意する点として以下の機能を使うためには gvim を使用しているか、 'termguicolors' オプションが使用可能でオンになっている必要があります。

ユーザーによる設定

各文字色の指定には g:terminal_ansi_colors 変数を使います。これは十六要素のリストで、各要素は色指定のための文字列です。基本的には #RRGGBB 形式の七文字で指定します。

let g:terminal_ansi_colors = [
  \ "#0c0c0c", "#c50f1f", "#13a10e", "#c19c00",
  \ "#0037da", "#881798", "#3a96dd", "#cccccc",
  \ "#767676", "#e74856", "#16c60c", "#eadf84",
  \ "#3b78ff", "#b4009e", "#61d6d6", "#e8e8e8"
  \ ]

各要素は次のように各文字色に対応しています。

インデックス
0 black
1 dark red
2 dark green
3 brown
4 dark blue
5 dark magenta
6 dark cyan
7 light grey
8 dark grey
9 red
10 green
11 yellow
12 blue
13 magenta
14 cyan
15 white

つまり、このような対応になります。

let g:terminal_ansi_colors = [
  \ "black",     "dark red",     "dark green", "brown",
  \ "dark blue", "dark magenta", "dark cyan",  "light grey",
  \ "dark grey", "red",          "green",      "yellow",
  \ "blue",      "magenta",      "cyan",       "white"
  \ ]

理想的には、ユーザーは何も設定の必要がなく、各カラースキームが適切な色を設定するのが一番良いと思います。しかし、実際にはまだ新しい機能なので、まだすべてのカラースキームファイルが対応しているわけではありません。ひとまず使っているカラースキームで問題がある場合に g:terminal_ansi_colors を設定しましょう。

g:terminal_ansi_colors は vimrc で設定してもカラースキームが上書きしたり、削除したりします。なので、デフォルト設定として次のように設定すると安心です。

if has('terminal')
  function! s:set_default_ansi_colors() abort
    if exists('g:terminal_ansi_colors')
      return
    endif

    let g:terminal_ansi_colors = [
      \ "#0c0c0c", "#c50f1f", "#13a10e", "#c19c00",
      \ "#0037da", "#881798", "#3a96dd", "#cccccc",
      \ "#767676", "#e74856", "#16c60c", "#eadf84",
      \ "#3b78ff", "#b4009e", "#61d6d6", "#e8e8e8"
      \ ]
  endfunction
  call s:set_default_ansi_colors()

  augroup vimrc
    autocmd ColorScheme * call s:set_default_ansi_colors()
  augroup END
endif

自動コマンドグループ名 (:help :augroup) は各自適切に設定してください。以下のリンクが参考になります。

vimrcアンチパターン · vim-jp/reading-vimrc Wiki · GitHub

おさらい autocmd/augroup - Qiita

【一人 vimrc Advent Calendar 2017】vimrc で安全に autocmd を設定する【3日目】 - Secret Garden(Instrumental)

カラースキームファイルによる設定

カラースキームを作ったら、その背景色にあう文字色を設定しましょう。

カラースキームの背景が黒に近い場合、デフォルトの色も見やすいと思います。これで十分なら、設定をクリアするだけで簡単です。

unlet! g:terminal_ansi_colors

もっとカラースキームにあった色を設定したい場合は、 g:terminal_ansi_colors を使って色を設定します。ただし、その色設定はカラースキームを後で変更した場合に邪魔かもしれない、という点に注意しましょう。ほかのカラースキームへ変更する時に設定をクリアするようにしてください。

if has('terminal') && exists('##ColorSchemePre')
  let g:terminal_ansi_colors = [
    \ "#0c0c0c", "#c50f1f", "#0f8d64", "#814d25",
    \ "#2353b1", "#881798", "#359fcf", "#cccccc",
    \ "#767676", "#e74856", "#88b422", "#cfa42b",
    \ "#6290ea", "#c800b2", "#44bfe0", "#e8e8e8"
    \ ]

  augroup mycolorscheme
    autocmd!
    autocmd ColorSchemePre * unlet! g:terminal_ansi_colors
    autocmd ColorSchemePre * autocmd! mycolorscheme
  augroup END
endif

自動コマンドグループ名 (:help :augroup) は各自適切に設定してください。

個人的には背景が暗い場合はデフォルトでも十分、背景が明るい場合はデフォルトだと厳しいなと感じます。

関連パッチ

patch 8.0.1685: can't set ANSI colors of a terminal window

patch 8.0.1777: cannot cleanup before loading another colorscheme

Vim の Julialang シンタックスハイライトについて

Vim の julialang 用シンタックスハイライト はなかなか控えめで渋くて好きです。 (Vim script 用のなんかは書いていると、ちょっと色が多すぎるな…と思ったりします。) ただ、括弧がハイライトされると視覚的に便利かなー、と思うので改造しようかと思っていたら、実はすでに構文グループが存在していた、ということを最近知りました。

github.com

なので括弧に色を付けたい場合は、~/.vim/after/syntax/julia.vim あたりに追記すればいいということですね。

highlight link juliaParDelim Delimiter

ハイライトグループ Delimiter は普通 Special にリンクされてると思います。 :help group-name

さらにコンマも色を付けたい場合は

highlight link juliaComma Delimiter

なんですけど、画面に色がつきすぎる感じもする。

julia-vim に Pull request を送りました

Julia も Vim で書いてるんですけど、結構な頻度でドキュメントを見たくなります。 Vim script を書く時の :help コマンドに慣れすぎているのでこのくらいの気軽さで Julia のドキュメントをひければいいのに、と思っていました。ので、書きました。

github.com

K キーでカーソル下のキーワードのドキュメントを開きます。また、 <Plug>(julia-doc-prompt)? あたりにマップしておけばREPLのそれのように使えます。

まだ、 merge されるかわかりませんが、されてほしいです。:terminal コマンドあるじゃん、と言われれば確かにそうなんですけど、:terminal 叩いてREPL立ち上げてって結構面倒じゃないですかね?改善案とかあれば件の issue にお願いします。


ここから、余談。本当は keywordprg オプションの設定だけで済ませたかったんだけど、 Julia の場合 @macrofuncname! が有効なキーワードなので iskeyword オプションも変更しなければならなかった。これだけならまだ提案してたかもしれないけど、 !bool と書くとこんどは真偽値の反転なので !iskeyword に含めるのはちょっと... ということで K キーにマッピングする形になってしまった。その点指摘されてるけど、いかんともしがたい。いい方法ないかな...

※ 2018/4/4 追記 紆余曲折の末、何とか 'keywordprg' オプションを使う形に持っていけました。

vim-multiselect を書きました

Vim に複数選択があったら、という感じの実験、コンセプトモデルのようなものを書きました。正直、基本的に :substitute とか :global みたいなのをつかってしまうのでいまいち使えてないんですけど、なんか面白い使い方でも見つからないかなー、と考えています。

  • 選択機能を提供

github.com

  • 編集機能を提供

github.com

肝心の大量処理はだいぶもっさりなのが残念なんですけど、 broadcasting textobjects って呼んでいる仕組みがあってこれはなかなか便利な感じがします。矩形選択の延長のような使い心地。デモです。

demo:broadcasting textobjects

masquerade demo

Vim の TextYankPost イベント

Vim 8.0.1394 にてオートコマンドイベント TextYankPost が実装されました。

github.com

ヤンクコマンドなどでレジスタが更新された際に処理をフックすることができます。名前に Yank とありますが、 d コマンドや c などもレジスタを更新するので同様に TextYankPost を呼びます。このパッチはイベントとともに v:event という変数を提供しており、これを通して関連する情報を取得できます。

" TextYankPost を発火したオペレータコマンド
autocmd TextYankPost * echomsg v:event.operator

" ヤンクされたテキスト (行ごとに分割された文字列のリスト)
autocmd TextYankPost * echomsg string(v:event.regcontents)

" 更新されたレジスタ
autocmd TextYankPost * echomsg v:event.regname

" レジスタの種類 (:help getregtype())
autocmd TextYankPost * echomsg v:event.regtype

この機能の原型は実はかなり昔に提案されたものだったんですが、ついに取り込まれました。

個人的な所感というか予想ですが、あまりにも限られたプラグイン、いわゆる Yankring 的なもののための機能!という感じで、そういう点で後に回されていたのかもしれません。とはいえ、かくいう私ものどからゲロでるほど欲しかったのでうれしいです。早速ビルドしました。近々vim-highlightedyankもアップデートするので Vim をビルドしましょう。(古い Vim で使えなくなるわけではないです)

追記:
あと、いじっていて気が付きましたが、何もレジスタを指定しないと v:event.regname が空なので、ユーザがコマンドにレジスタを明示的に指定したかしていないかを判別できますね。つまり ""yyyy を見分けられます。これ、 v:register では実現できないのでユーザ定義オペレータを作るときに困っていたのですが、 TextYankPost ごしなら限定的に可能になりました。使い道は…ないかな。

アンドゥした位置をハイライトする Vim プラグインを書きました

Atom text editor の Vim エミュレータ、 vim-mode-plus に関する発表 を見ました。以前からアイデアの宝庫だと感心していましたが、もはや単なるエミュレータの域を超えているように思います。

スライドを見ていて、アンドゥした位置がハイライトされているのが面白かったので早速パクりました。

vim-highlightedundo

シェルコマンド diff を使って変更を検出してハイライトします。 diff コマンドの出力は行単位なのですが、もうひと頑張りしています。正直しないほうが良かったかも、と後悔しています。コードを書くのは楽しかったんですけど。

vim-highlighteundo-demo-gif

正直、ちょっともっさりしていて常用には向かないかも。 どんな編集をしたかは Vim のアンドゥツリーに残っていないので必ずしもハイライト位置は編集位置と一致しません。これは今のところ Vim script に提供されているインターフェースではどうにもならないと思います。

最初は削除される文字列と挿入される文字列の両方をハイライトしてたんですけど、邪魔だなぁ、という感じだったので止めました。一応、試すことはまだできます。

let g:highlightedundo#highlight_mode = 2

何回アンドゥ・リドゥできるのかを取得するのが一番難しかったです。

UD デジタル教科書体

Windows update done. 楽しみにしていた UD デジタル教科書体に等幅があると知ったので喜び勇んで Vim の 'guifontwide' オプションに設定してみました。いい…。

f:id:machakann:20171031010251p:plain