heirline.nvimでNeovimのStatusLineをカスタマイズしてみる
この記事はVim駅伝の2023-03-10
枠への参加記事として書きました。
対象読者
- StatusLineのカスタマイズを色々やってきた
- heirline.nvimがどんなものか気になる
背景
これまで、StatusLineのカスタマイズにはfeline.nvimを使ってきましたが、どうやら作者の方がメンテナンスを維持できなくなってしまったようで、乗り換える必要が生じました。
- 他のプラグインへの依存が少ない
- カスタマイズ性が高い
- デフォルトの機能が少ない
これらを条件に乗り換え先を探していたところ、Redditにてheirline.nvimが良い、との投稿を見かけたため、
試しに使ってみることにしました。
この記事では、他にも検討しているかたの一助になれば、と思い、
- heirline.nvimの仕様
- 設定してみた感想
についてまとめました。
はじめに:出典
何につけても、公式ドキュメントにまさるものはありません。
heirline.nvimは非常に細かいCOOKBOOKが用意されています。
私の拙い理解力では、誤解があるかもしれないので、もし妙だと思った場合は必ず公式ドキュメントをあたるようにしてください。
heirline.nvimの設定仕様
heirline.nvimの設定方法を理解するうえで、特に覚えておくと話が早くすすみそうな仕様をいくつか紹介します。
デフォルトでは何もしない
heirline.nvimは未設定の状態では何も表示されません。
ほぼゼロから自分で組み立てる必要があります。
ComponentTreeで表現する
heirline.nvimはHTMLのようなツリー構造を模している、と理解するのが手っとり早いように思います。
ツリー構造はluaのtableの入れ子で表現され、各ノードの属性はtableのFieldで指定します。
HTMLで例えれば次のような構造を
<StatusLine>
<VimMode style="color: blue; background-color: bright-blue">
<ModeValue>
{mode()}
</ModeValue>
<Separator style="color: bright-blue; background-color: gray">

</Separator>
</VimMode>
<FileName style="color: black; background-color: gray">
{bufname()}
</FileName>
</StatusLine>
lua tableで次のように表現します。
{
{ -- VimMode
hl = { fg="blue", bg="bright-blue" },
{ -- ModeValue
provider = function()
return vim.fn.mode()
end,
},
{ -- Separator
hl = { fg = "bright-blue", bg = "gray" }
provider = "\u{E0BC}",
},
},
{ -- FileName
hl = { fg="black", bg="gray" },
provider = function()
return vim.fn.bufname()
end,
},
}
入れ子の各TableはComponentと呼ばれています。
Componentの振る舞い
Componentはいわゆるクラスオブジェクト的な振る舞いをします。
各Fieldに指定した関数はいわゆるメソッドのように、 self.xxx
でコンポーネント自体の状態にアクセスできます。
hl
や provider
で同じ値に依拠したい場合など、初期化処理としてinit
を指定することもできます。
親で用意した値は、子に引き継がれます。
例:
{
init = function(self)
self.mode = vim.fn.mode(0) -- 親の`init`でVimの入力状態を取得しておく
end,
{
provider = function(self)
return self.mode -- 親で仕込んだ値を使用する
end,
hl = function(self)
if self.mode == "n" then
return { bg = "red", fg = "white" }
else
return { bg = "gray", fg = "white" }
end
end,
},
}
Componentの予約されたField
Componentには、子要素とともにFieldでさまざまな属性を指定します。
全て列挙するのは本家のCOOKBOOKに任せるとして、ここでは私が使用した主要なものを紹介します。
provider
コンポーネントで表示する文字列を指定する時に使用するFieldです。
例:
{ provider = "foo" }
{
provider = function(self)
if self.state == "done" then
return "foo"
else
return "bar"
end
end,
}
hl
Highlightを指定する際に使用するFieldです。
vim.api.nvim_set_hl
で指定するのと同じようにfg
, bg
, bold
などを指定します。
例:
{ hl = { fg = "black", bg = "blue", bold = true } }
{
hl = function(self)
return { fg = self.warning_color, bg = "blue", bold = true }
end,
}
condition
Componentを表示するかどうかを示すBooleanを表すFieldです。
表示しない場合はFalsyの値を指定します。
例:
{
condition = function(self)
if self.mode == "Normal" then
return false
end
return true
end,
}
update
Componentの更新契機を指定するFieldです。
例:
{ update = { "CursorHold", "CursorHoldI", "DirChanged" } }
{
update = function(self)
return self.prev_update + 3000 < self.now
end,
}
Componentのライフサイクル
コンポーネントのライフサイクルには生成と評価の2つのフェーズがあります。
- 作成:tableをインスタンス化する
- 評価
- 実際に表示される内容を決める
- 表示が更新される度に呼び出される
-
condition
,update
,init
,hl
,on_click
,provider
,pick_child
の順に評価する
Theme管理ができる
hl
に指定する色は、各Componentで即値を指定することもできますが、
全体でStyleの一貫性を持たせるために、テーマとしての色セットを用意して間接的に指定できます。
例:
require("heirline").load_colors({ -- 色の名前と即値の組み合わせを用意する
white = "#f0f0f0",
dark_powered = "#9f6f3f",
})
{ hl = { fg = "white", bg = "dark_powered" } } -- 用意した名前を使用する
実際に書いた設定
heirline.nvimで実際にStatusLineを設定した内容は私のDotfilesリポジトリに置いてあります。
-
'laststatus' = 3
で使用している - 見ているgitのstatusは、bufferの親ディレクトリではなく、NeovimのCWD
という前提があるため、そのまま使用できるとは限りませんが、一つの参考になれば幸いです。
感想
最後に、実際に仕様を理解し、設定を行った結果として気に入った部分を語ってみます。
邪魔な挙動がない
デフォルトでは何も設定しませんが、逆に言えば、余計なことをしないため、不必要な挙動に悩まされることが少なく済みます。
典型的な設定についてはCOOKBOOKに紹介されているので、コピペしつつ自分なりにカスタマイズして使用できます。
ツリーにより設定するという構造は分かりやすい
HTMLがそうであるように、見た目の構造は入れ子で表現するのが直観的で分かりやすいです。
リスト構造を使ったり、固定の変数(xxx_a
, xxx_b
, xxx_left
...)で指定させたりするよりも、素直に表現できました。
モジュール化しやすい
パーツを一つのComponentとして完結できるため、モジュールとして再利用可能な形で分離しやすくなっています。
設定が1ファイルの中で肥大化することが防げる他、WinBarとStatusLineで同じ情報を表示する際には便利ですし、
或いはモジュールとして配布したりすることもできるかもしれません。
ちょっと別の話ですが、 nvim-navicというpluginが便利でした。
heirline.nvimでも利用しやすく提供されているので、小さい設定コードで取り込めています。
懸念:既に結構複雑
luaのtableとmetatableをかなり良く活かしたプラグインですが、大局については直観で理解しやすい一方、
- 作者のクセが強め
- 世界観の説明が難しい
- 細かいことをしようと思うと若干複雑さに向き合わされる
といった部分が少し目立ち、作者の熱量が多いうちは良いものの、孤立して力尽きてしまわないか心配になる部分はありますね。
Discussion
私もfeline.nvim使っていたので参考になります...!!
ありがとうございます!!