まずは記事掲載に遅れたことをお詫び申し上げます。
この記事は Vim Advent Calendar 2021 その2 の 23日目の記事です。
statusline と tabline を設定できるプラグインを作りました
→ seroqn/vim-hairline
参考:itchyny/lightline.vim:偉大なる先達。
動機
初めはタブライン tabline だけ設定するプラグインを作っていました。タブページの数が増えてくるとタブラインの表示が圧迫され、まともに表示できないことがあったのが気になったからです。私の考えでは現在表示しているタブページと、その両隣のタブページさえ示せれば実用に足ると考えています(タブにめちゃくちゃ長い文字列を表示させるなんてことをしない限りは)。
そしてそういう表示をしてくれるプラグインがなかったので、自作しました。そして tabline についてだけ作ったら、結局設定方法が似ているステータスライン statusline も設定できるようにしたい魔力に襲われたので、結局ステータスラインについても実装してしまいました。
コンセプト
- 起動時に低負荷。設定を全て
vimrc
に書けば起動時に読み込まれるのはplugin/
以下にある1ファイルだけにする1。 - 実行時もなるべく低負荷
- 高カスタマイズ性
- コンパクトな設定表記
- プレフィクスによって特別な部品やハイライト指定を通常の部品に混ぜて記述できるようにした
- 自分が使う機能以外は削いだ。他の似た既存の plugin より低機能2かもしれない。その分低負荷だと思われる3。
- 多分この低負荷さを大幅に崩すような機能は追加しないつもり。
できないこと
自分の使う機能しか実装していないのと、技術的理由、軽量化的理由から、以下の機能はありません。
- パーツとパーツの間を区切るセパレーター文字 "|" の設定
- フォントにパッチを当てて powerline や airline や lightline のように三角形▶をセパレーターとして使うこと
- リプレイスモード(ノーマルモードで
R
押打で入るモード)の認識
こんなやつです
g:hairline
という辞書変数を定義します。この変数に statusline
という辞書を定義すればステータスラインが、tabline
という辞書を定義すればタブラインが有効になります。
(どちらかを無効にしたければそれを定義しないか、statusline.disable
tabline.disable
を非0 に設定すればそれを無効にできます)
let g:hairline = {'statusline': {}, 'tabline': {}}
powerline や lightline をパクった外見を初期状態に設定させていただきました。
タブページを複数開いても現在タブとその両隣のタブしか表示しません。デフォルトではタブリストの右にタブ総数と現在のタブ番号を数字で表示しています。
タブラベルの表示領域を潤沢に使える余裕があります。
ステータスラインの設定方法
部品の指定
statusline.left
statusline.right
tabline.left
tabline.right
に部品(パート)を並べていきます。これらは文字列のリストです。
let g:hairline.statusline.left = ["bufname", "filetype"]
let g:hairline.statusline.right = ["percent"]
その部品は g:hairline.part
で設定します。これは辞書で、中身は式の文字列です。
let g:hairline.part = {
\ "bufname": "%t",
\ "filetype": "%{&ft !=# '' ? &ft : 'no ft'}",
\ "persent": "%3p%%",
\ }
部品に関数を使いたいときには g:hairline.part_func
を使います。その部品を指定するときには名前の前にプレフィクス f:
をつけます。
let g:hairline.statusline.left = ["f:mode", "bufname", "filetype"]
let g:hairline.part_func = {}
function! g:hairline.part_func.mode() abort
let mode = get(
\ {'n': 'NORMAL', 'i': 'INSERT', 'v': 'VISUAL', 'V': 'V-LINE',
\ "\<C-v>": 'V-BLOCK', 't': 'TERMINAL'}, mode(), 'NORMAL')
return mode
endfunction
文脈
hairline には文脈というものがあります。
"NC" 非カレントウィンドウ
"n" カレントウィンドウのノーマルモード、または以下にないモード
"v" カレントウィンドウのビジュアルモード・セレクトモード
"i" カレントウィンドウのインサートモード
"t" カレントウィンドウのターミナルモード
ステータスラインにおいて、left
right
の後ろにアンダースコアと文脈名をポストフィクスした名前があればその文脈ではそちらが優先されます。
let g:hairline.statusline.left = ["f:mode", "bufname", "filetype"]
let g:hairline.statusline.left_NC = ["bufname", "filetype"]
上記例では非カレントウィンドウでは "f:mode"
がない左辺が使われます。
ハイライト定義
文脈はハイライト定義にも使われます。上記文脈に加えて "TAB" と "COMMON" が使われます。"COMMON" は全ての文脈で共通して使われるハイライトです。ただし、"COMMON" ハイライトを定義する際には g:hairline.common_hl_basenames
も設定しなければ他の文脈に適用されません。"TAB" はタブラインで有効です。
ハイライトは g:hairline.highlight
関数内で定義します。
ハイライト名は Hairline_{文脈}_{任意の名前}
で設定します。
let g:hairline.common_hl_basenames = ['plain', 'bufname', 'ftype', 'percent']
function! g:hairline#default.highlight() abort "{{{
hi Hairline_COMMON_plain guifg=gray54 guibg=gray19 ctermfg=245 ctermbg=236
hi Hairline_COMMON_bufname guifg=white guibg=gray35 ctermfg=231 ctermbg=240
hi Hairline_COMMON_ftype guifg=gray38 guibg=gray82 ctermfg=241 ctermbg=252
hi Hairline_COMMON_percent guifg=gray74 guibg=gray34 ctermfg=250 ctermbg=240
hi Hairline_NC_bufname guifg=gray34 guibg=gray15 ctermfg=240 ctermbg=235
hi Hairline_NC_ftype guifg=gray15 guibg=gray38 ctermfg=235 ctermbg=241
hi Hairline_NC_percent guifg=gray34 guibg=gray15 ctermfg=240 ctermbg=235
endfunc
"}}}
これで非カレントウィンドウ以外の文脈の "bufname"、"ftype"、"percent" は共通のハイライトが使われ、別の定義がある非カレントウィンドウだけには別のハイライトが適用されます。"plain" は特別な名前で、left
と right
の間のスペースなどを埋めるときなどに使われます。
実は先の例、
let g:hairline.statusline.left = ["bufname", "filetype"]
let g:hairline.statusline.right = ["percent"]
だと、ステータスラインのハイライトには Hairline_COMMON_plain
だけしか使われません。ハイライトをパーツに適用させるには、H:
プレフィックスのパーツを使います。
H:{ハイライト名}
でその文脈のハイライトが選択されて適用されます。ここでは非カレントウィンドウにだけ別のハイライトが適用されます。H:
プレフィクスでハイライトを指定すると以後、別のハイライトを指定するまでの間はそのハイライトが使われます。ただし、left の終端から先は "plain" が使われます。right の先頭で別のハイライトを使う場合は再度設定する必要があります。
let g:hairline.statusline.left = ["H:bufname", "bufname", "H:ftype", "filetype"]
let g:hairline.statusline.right = ["H:percent", "percent"]
そのほかのプレフィクス
部品に使えるプレフィクスは他にも r:
(式を直接記述する)、I:
(このパーツを無視する)、X:
(パーツの両側にスペースを挟まない)などがあります。
とりあえずここまで
続きはヘルプをご参照ください。とりあえず言えることは、かなり軽量であろうということです。あと、部品指定の中にハイライト指定も含まれているので、もしかしたら後から設定を見返すとき理解しやすいかもしれません。
タブラインの設定も同じような感じです。
それではよいクリスマスを。