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

'tabline' を活用しよう

vim event

Vim Advent Calendar 2011 の 4 日目の記事です。

タブページ

Vim 7 から追加されたタブページ(いわゆるタブ機能)、使っていますか?
これによって複数の画面を扱うことが容易になりましたが、タブページの恩恵はこれだけではありません。それが今回紹介する 'tabline' です。
Vim の最上部にタブの情報を表示するための領域がありますが、実はこの部分は任意に設定することが可能です。これを利用すれば、最上部に任意の情報を表示することが可能になります。

始めの一歩 - タブページを表示する

まずはタブページを表示する必要があります。デフォルトでは、タブページは複数のタブがあるときのみ表示するようになっています。追加の情報を表示するのなら、常に表示されていた方が良いでしょう。
また、gVimの場合はデフォルトだと GUI でタブが表示されます。これだと 'tabline' オプションが使用されず、任意の情報を表示できません。
以下のように設定します。

" タブページを常に表示
set showtabline=2
" gVimでもテキストベースのタブページを使う
set guioptions-=e

タブページのラベルを生成するための関数を用意する

タブページのラベル生成用の専用の関数を用意してしまうと楽です。ここでは仮に MakeTabLine() とします。

function! MakeTabLine()
endfunction

これを以下のようにして 'tabline' に設定します。

set tabline=%!MakeTabLine()

この %! を使うことで、この関数の結果が 'tabline' の設定値になります。
ちなみに %{} と言う記法もありますが、これはこの場合は使えません。なぜなら、MakeTabLine() 自体が % による記法を含む文字列を返すからです。%{} を使うとそこが解釈されずにそのまま表示されてしまいます。
ちなみにこの段階では MakeTabLine() は 0 を返すだけなので、tabline は最後に設定します。

タブページのラベルを生成する

いよいよ MakeTabLine() の中身を作成します。
以下に雛形の例を示します。

function! s:tabpage_label(n)
  " n 番目のタブのラベルを返す
endfunction

function! MakeTabLine()
  let titles = map(range(1, tabpagenr('$')), 's:tabpage_label(v:val)')
  let sep = ' | '  " タブ間の区切り
  let tabpages = join(titles, sep) . sep . '%#TabLineFill#%T'
  let info = ''  " 好きな情報を入れる
  return tabpages . '%=' . info  " タブリストを左に、情報を右に表示
endfunction

おおむねこのようにするといいでしょう。

タブのラベルを表示する

今回の例では、s:tabpage_label() で各タブのラベルを生成します。いくつかポイントがあります。

便利な関数
  • tabpagenr()
    • 現在のタブページを取得します。これで、今生成しようとしているラベルがカレントかどうか判定できます。
  • tabpagebuflist([{tabnr}])
    • タブページ内の各ウィンドウに表示されているバッファのリストを取得します。
  • tabpagewinnr({tabnr}[, {last}])
    • 現在選択されているウィンドウ番号を取得します。tabpagebuflist() と組み合わせてタブ毎のカレントバッファがわかります。
  • pathshorten({path})
    • パス中のディレクトリを短くします。
    • pathshorten('~/.vim/autoload/myfile.vim') => '~/.v/a/myfile.vim'
  • gettabvar({tabnr}, {varname})
    • タブページローカルな変数を取得します。
  • getbufvar({bufnr}, {varname})
タブの情報を埋め込む

マウスでクリックされた時のために、特別な書式で情報を埋め込むことができます。これにより、マウスでタブを切り替えたりタブを閉じたりできます。

  • %NT
    • タブページ N のラベルの開始 (Nは任意の数字)
  • %T
    • タブページのラベルの終了
  • %NX
    • タブページ N を閉じるラベルの開始 (Nは任意の数字)
  • %X
    • タブページを閉じるラベルの終了
  • %#HighlightName#
    • 強調グループの設定
    • この印以降のハイライトが変更される
    • 以下、'tabline' でよく使うハイライト
      • TabLine アクティブでないタブページのラベル
      • TabLineSel アクティブなタブページのラベル
      • TabLineFill ラベルがない部分

他にも 'statusline' で使える % 記法は全て使えます。が、カレントバッファの情報に関するものがほとんどで、'tabline' で使うことはほとんどないでしょう。

実際に書く

例えば以下のような感じです。

" 各タブページのカレントバッファ名+αを表示
function! s:tabpage_label(n)
  " t:title と言う変数があったらそれを使う
  let title = gettabvar(a:n, 'title')
  if title !=# ''
    return title
  endif

  " タブページ内のバッファのリスト
  let bufnrs = tabpagebuflist(a:n)

  " カレントタブページかどうかでハイライトを切り替える
  let hi = a:n is tabpagenr() ? '%#TabLineSel#' : '%#TabLine#'

  " バッファが複数あったらバッファ数を表示
  let no = len(bufnrs)
  if no is 1
    let no = ''
  endif
  " タブページ内に変更ありのバッファがあったら '+' を付ける
  let mod = len(filter(copy(bufnrs), 'getbufvar(v:val, "&modified")')) ? '+' : ''
  let sp = (no . mod) ==# '' ? '' : ' '  " 隙間空ける

  " カレントバッファ
  let curbufnr = bufnrs[tabpagewinnr(a:n) - 1]  " tabpagewinnr() は 1 origin
  let fname = pathshorten(bufname(curbufnr))

  let label = no . mod . sp . fname

  return '%' . a:n . 'T' . hi . label . '%T%#TabLineFill#'
endfunction

好きな情報を表示する

お好きな情報を入れればいいかと存じます。
以下に例を挙げます。

カレントディレクトリ
let info .= fnamemodify(getcwd(), ":~") . ' '
プラグインが提供する情報
" current function info (cfi.vim)
let info .= cfi#format('%s()' . sep, '')
" Vim の稼動時間 (uptime.vim)
let info .= Uptime(2)
" hahhah.vim
let info .= g:HahHah()

実際に複数追加する際は間にセパレータを追加した方が見やすいかと思います。

スクリーンショット

今回の例で作った 'tabline' にカレントディレクトリを表示するようにすると、以下のようになります。

まとめ

常に表示して欲しい情報と言うのは割とあるもので、それも 'statusline' だとウィンドウを分割した際に複数表示されて無駄だったり、縦分割した際に幅が狭くなって読めなくなったりします。
'tabline' を使うことで+αの情報が表示できるので、是非活用しましょう。
また、今回は例、と言うことで、あまり複雑にしすぎないようにしてみました。実際に使ってみると不満も出てくると思います。その時は是非改良してみましょう。Vim scripter への1歩はこうして始まるのです。



明日は id:h1mesuke さんです。