読者です 読者をやめる 読者になる 読者になる

反省はしても後悔はしない

Vim とか備忘録とか。それと関数型言語勉強中

Vim のカラースキームが微妙に気に食わないときの対処法

Vim

この記事は Vim Advent Calendar の 253 日目の記事です。昨日は id:tyru さんの Alt-Spaceでウインドウのメニューを表示、あとウインドウの最大化 でした。

はじめに

Vim にはたくさんのカラースキームがありますね。きっとみなさんは、たくさんあるカラースキームの中からお気に入りを見つけて設定をしていることでしょう。

しかし中には 99% 気に入って入るけれど、1% だけ微妙に気に食わないという場合もあるかと思います。今日はそんなときにカラースキームの一部だけをちょこっと改造する方法を紹介します。

:highlight を vimrc に書く (基本編)

色を変えたい部分は highlight コマンドをちょろっと vimrc に書けば OK です。

" 以下のコマンドは :colorscheme の前に設定します
" コメントを濃い緑にする
autocmd ColorScheme * highlight Comment ctermfg=22 guifg=#008800
" ...
colorscheme molokai

まず、:highlight コマンドは autocmd ColorScheme で囲むようにします。こうすることにより、気が変わって途中でカラースキームを変えたとしても再度設定し直されるようになります。

Comment というのがコメントを表しています。他にどういうのがあるかは :help group-name :help highlight-groupsして調べてみましょう。ソースコードの要素だけでなく行番号やビジュアルモードの選択範囲なども設定出来ます。

ctermfg はターミナルの、guifg は GUI 版の Vim の前景色を表します。GUI 版の場合は CSS などでお馴染みの RGB を 16 進数で記述しますが、ターミナルの場合は色を表す番号で指定します。どの番号がどの色かを調べる Perl スクリプトがあるのでそれを利用すればよいでしょう。

f:id:cohama:20130811020341p:plain

ほかにも太字にしたい場合は cterm=BOLD gui=BOLD などのように設定出来ます。

蛇足 syntax と highlight

ここからは応用編です。 Vim の色を決定しているのは主に syntax と highlight という仕組みです。syntax は構文とその構文が属するハイライトグループを決定するもので、例えば "hoge" は文字列、int はキーワードなどのように各言語ごとに用意された構文用のファイルに従って決定されます。highlight は各ハイライトグループどのように色付けするかを設定するものです。文字列は緑、キーワードは青などのように設定します。

どのハイライトグループがどのように highlight されているかは :highlight コマンドを引数なしで起動することで確認できます。

f:id:cohama:20130811020358p:plain

この出力を眺めていると、ハイライトの仕方ではなく link to となっているものがあることに気づきます。実はハイライトグループは他のハイライトグループにリンクさせることができます。

例えば、Vim script の文字列は vimString、Ruby の文字列は rubyString というハイライトグループになっていますが、どちらも String というハイライトグループにリンクされています。これにより、String というハイライトグループだけ変更すればほとんどの言語での文字列の色を帰ることができます。また、逆に特定の言語の文字列の色だけを変えるということもできます。(デフォルトでちゃんと設定されていれば)

カーソル下の syntax 情報を取得する

例として以下の Ruby ファイルを見てください。

f:id:cohama:20130811020503p:plain

これは、molokai というカラースキームで Ruby ファイルを表示したものですが、module class といったキーワードと実際のモジュール名、クラス名が同じ色になってしまっています。これを改善するために module class というキーワードの色を変えたいとして、これらがどのハイライトグループに属するかが予めわかっていないといけません。

そこで、Vim script の synID synIDattr synIDtrans という組み込みの関数を使って構文情報とそのハイライト情報を取得してみましょう。

以下の様な Vim script を書いてみます。

function! s:get_syn_id(transparent)
  let synid = synID(line("."), col("."), 1)
  if a:transparent
    return synIDtrans(synid)
  else
    return synid
  endif
endfunction
function! s:get_syn_attr(synid)
  let name = synIDattr(a:synid, "name")
  let ctermfg = synIDattr(a:synid, "fg", "cterm")
  let ctermbg = synIDattr(a:synid, "bg", "cterm")
  let guifg = synIDattr(a:synid, "fg", "gui")
  let guibg = synIDattr(a:synid, "bg", "gui")
  return {
        \ "name": name,
        \ "ctermfg": ctermfg,
        \ "ctermbg": ctermbg,
        \ "guifg": guifg,
        \ "guibg": guibg}
endfunction
function! s:get_syn_info()
  let baseSyn = s:get_syn_attr(s:get_syn_id(0))
  echo "name: " . baseSyn.name .
        \ " ctermfg: " . baseSyn.ctermfg .
        \ " ctermbg: " . baseSyn.ctermbg .
        \ " guifg: " . baseSyn.guifg .
        \ " guibg: " . baseSyn.guibg
  let linkedSyn = s:get_syn_attr(s:get_syn_id(1))
  echo "link to"
  echo "name: " . linkedSyn.name .
        \ " ctermfg: " . linkedSyn.ctermfg .
        \ " ctermbg: " . linkedSyn.ctermbg .
        \ " guifg: " . linkedSyn.guifg .
        \ " guibg: " . linkedSyn.guibg
endfunction
command! SyntaxInfo call s:get_syn_info()

これで :SyntaxInfo というコマンドによりカーソル下の構文情報(ハイライト情報)を取得できるようになりました。

f:id:cohama:20130811020537p:plain

f:id:cohama:20130811020543p:plain

調べてみると、module は rubyModule で Define というハイライトグループにリンクしており、モジュール名(Hoge) は rubyConstant でType というハイライトグループにリンクしていることが分かります。

この情報を使って色を変えてみましょう。

autocmd ColorScheme * highlight rubyModule guifg=#88ff88

f:id:cohama:20130811020555p:plain

これで Ruby の時だけ module というキーワードの色を変えることができました。

カラースキームを自作してみる

いろいろ改造していると、たくさんの :highlight コマンドが vimrc に増えてきます。そこまで来たらむしろカラースキームを作ってしまったほうが良いでしょう。

乱暴に言ってしまうとカラースキームの実態はここで説明した highlight コマンドの集合体です。なので、既存のカラースキームを改造するだけの場合は結構簡単に作れます。

カラースキームの作り方については下記の id:thinca さんの記事がとても参考になるでしょう。 *1

カラースキームを作ってみよう

まとめ

  • :highlight を vimrc に書けば色付けを変更できる
  • synID などの関数により構文情報を取得できるのでハイライトの設定に便利
  • カラースキームの自作は難しくない

明日は @ujihisa さんです。

*1:書いている途中でネタかぶりに気づきました