[[📒Articles]] > [[📒2025 Articles]]
![[2025-04-04.webp|cover-picture]]
[[Neovim]]起動時のダッシュボードを[[snacks.dashboard]]でつくってみました。これまではいくつかの問題があり、導入を見送っていましたが、今回はそれらすべての問題を解決しつつ、さらに改善することもできました。
> [!info]
> - この記事は [[Vim駅伝]] #2025/04/04 の 参加記事です
## はじめに
ダッシュボードをつくってみた経緯をお話しします。
### snack.nvimを使い始めた
[[ファジーファインダー]]として長く使っていた[[telescope.nvim]]を[[snacks.picker]]に変えました。そのために[[snacks.nvim]]をインストールしました。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
snacks.nvim/docs/picker.md at main · folke/snacks.nvim
</div>
<div class="link-card-v2-content">
🍿 A collection of QoL plugins for Neovim. Contribute to folke/snacks.nvim development by creating an account on ...
</div>
<img class="link-card-v2-image" src="https://repository-images.githubusercontent.com/882482569/62265501-363d-4cf4-9ded-55d42b2d37bf" />
<a href="https://github.com/folke/snacks.nvim/blob/main/docs/picker.md"></a>
</div>
[[snacks.nvim]]は沢山のプラグイン機能を同梱しているので、他にも多くの機能を利用できます。そのうちの1つである[[snacks.dashboard]]にも興味が湧いてきました。
### GPT-4oの画像生成が超パワーアップした
ちょうどその頃、[[ChatGPT]]で[[GPT-4o]]の画像生成機能が大幅にパワーアップしました。今までほぼ不可能だった **既存の画像キャラを使った別画像の作成** ができるようになりました。たとえば、以前に書いた以下記事のカバー画像について
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" />
<span class="link-card-v2-site-name">Minerva</span>
</div>
<div class="link-card-v2-title">
📘Neovimを使ったことがなかったころの君へ
</div>
<div class="link-card-v2-content">2023年のおわり、1年のふりかえり記事を書こうとネタを考えながら帰路につき、いつもは見ないポストを開けるとそこには見慣れない1通の手紙が...。</div>
<img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/%F0%9F%93%98Articles/attachments/2023-12-29.jpg" />
<a data-href="📘Neovimを使ったことがなかったころの君へ" class="internal-link"></a>
</div>
%%[[📘Neovimを使ったことがなかったころの君へ]]%%
全身が入る立ち絵をわずかなプロンプトだけで生成できるようになりました。本記事のカバー画像も同様です。
![[neovim-standup.png|frame-verticle]]
当時、先の記事のカバー画像をつくるために数十回と試行を繰り返しました。記事自体もかなり気合を入れて書いたのもあり、このキャラ(以下、Neovimちゃん)には愛着を持っています。『[[Neovim]]起動時にNeovimちゃんが表示されたら良いな』と思いました。
### ダッシュボードを導入しなかった理由
一方で、[[Neovim]]へのダッシュボード導入は長らく否定的でした。主な理由は以下3点です。
- 起動速度が犠牲になるくらいなら導入しなくてもいいと思っていた
- 画像を表示しても見栄えが良くない
- 他プラグインとの相性を考えるのも大変そう
結論から言うと、これらの問題はすべて解決されました。細かな検討記録はADRに記載しています。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" />
<span class="link-card-v2-site-name">Minerva</span>
</div>
<div class="link-card-v2-title">
💿VIM-0001 snacks.nvimのダッシュボード機能を使う
</div>
<div class="link-card-v2-content">懸念事項がすべて解決したどころか、むしろメリットが出てきたため。</div>
<img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/vim-adr.webp" />
<a data-href="💿VIM-0001 snacks.nvimのダッシュボード機能を使う" class="internal-link"></a>
</div>
%%[[💿VIM-0001 snacks.nvimのダッシュボード機能を使う]]%%
## つくったモノ
実際に作成したダッシュボード、[[Neovim]]起動直後の画像です。
![[Pasted image 20250402212916.png|frame]]
### 機能
ダッシュボード画面では以下の機能を設定しています。ほとんどが[[snacks.picker]]の機能です。
| キーバインド | 機能 |
| ------------ | -------------------------------- |
| f | プロジェクトファイル検索 |
| e | スマート検索 |
| r | 最近開いたファイル検索 |
| t | エクスプローラーツリー検索 |
| p | 最近開いたプロジェクト検索 |
| g | grep検索 |
| i | [[ノーマルモード]]で編集を始める |
| l | [[lazy.nvim]]を開く |
| q | 終了 |
### 効果
#### 起動直後に必要なキー入力が半減
今までは `vim` で立ち上げてから `<C-j>f` を押して[[snacks.picker]]を起動していましたが、`vim` で立ち上げて `f` と押すだけでよくなりました。
#### 起動速度の高速化
もともと30ms弱かかっていた起動時間が20ms弱になりました。[[no-neck-pain.nvim]]や[[barbar.nvim]]の読み込みタイミングが、起動直後ではなく[[snacks.picker]]立ち上げ後になったのが大きいです。
#### 起動時の画面チラつき軽減
[[no-neck-pain.nvim]]や[[barbar.nvim]]が起動すると、[[ウィンドウ (Vim)|ウィンドウ]]サイズに変更が加わるため一瞬チラつきが発生していました。今回、ロードタイミングが変わったことで、読み込み時のチラつきが目立たなくなりました。
一方で、[[snacks.dashboard]]自体にもチラつきが発生する要因がありました。この回避方法については本記事後半で説明します。
#### Neovimちゃんが起動時に表示される
私のターミナル環境は[[kitty (プロトコル)|kitty]]に対応していないため、綺麗な画像は表示できません...が、[[chafa]]を使うことで十分な品質になったと思いました。少し粗いくらいの方が味もありますしね😊
## 環境
| 対象 | バージョン |
| --------------------- | ----------------------------- |
| [[Ubuntu]] | 24.04.1 LTS |
| [[Neovim]] | 0.10.3 |
| [[snacks.nvim]] | `bc0630e` |
| [[no-neck-pain.nvim]] | `53c8ef0` |
| [[barbar.nvim]] | `807bede` |
| [[chafa]] | 1.14.0 |
| [[Windows Terminal]] | 1.22.250204002 |
| [[Zsh]] | 5.9 (x86_64-ubuntu-linux-gnu) |
## つくりかた
### snacks.nvimのインストール
[[snacks.nvim]]のREADMEに従います。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
GitHub - folke/snacks.nvim: 🍿 A collection of QoL plugins for Neovim
</div>
<div class="link-card-v2-content">
🍿 A collection of QoL plugins for Neovim. Contribute to folke/snacks.nvim development by creating an account on ...
</div>
<img class="link-card-v2-image" src="https://repository-images.githubusercontent.com/882482569/62265501-363d-4cf4-9ded-55d42b2d37bf" />
<a href="https://github.com/folke/snacks.nvim"></a>
</div>
```lua
return {
"folke/snacks.nvim",
priority = 1000,
lazy = false,
}
```
### chafaで画像をつくる
画像を[[Sixel]]や[[kitty (プロトコル)|kitty]]なしで表示するため[[chafa]]を使います。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
GitHub - hpjansson/chafa: 📺🗿 Terminal graphics for the 21st century.
</div>
<div class="link-card-v2-content">
📺🗿 Terminal graphics for the 21st century. Contribute to hpjansson/chafa development by creating an account on ...
</div>
<img class="link-card-v2-image" src="https://repository-images.githubusercontent.com/130906897/fbe05080-f81e-11e9-8d23-93857fde0390" />
<a href="https://github.com/hpjansson/chafa"></a>
</div>
インストールします。
```console
sudo apt install -y chafa
```
今回はこちらの画像を使っています。
![[dashboard.png|frame]]
*Neovimちゃん*
画像をダウンロードして[[chafa]]で表示させてみましょう。
```bash
wget "https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/dashboard.png"
chafa dashboard.png --size 48 --symbols vhalf
```
うまくいくとこんな感じになります。
![[Pasted image 20250403210315.png|frame-verticle]]
*chafaでNeovimちゃんをターミナルに表示*
### ダッシュボードの設定をつくる
あとは[[snacks.dashboard]]の設定を追加するだけです。
```lua
-- 画像ファイルのパスがある場所を設定してください
local dashboardImagePath = vim.fn.stdpath("config") .. "/lua/snacks/dashboard.png"
-- dashboard で picker を開いて移動する際に発生するチラつきを防止する
local preventFlicker = function(handler)
vim.schedule(function()
Snacks.bufdelete()
end)
vim.schedule(function()
-- ここの順番が逆だとno-neck-painがエラーになる
vim.cmd([[:NoNeckPain]])
vim.cmd([[:BarbarEnable]])
end)
vim.schedule(function()
handler()
end)
end
return {
"folke/snacks.nvim",
lazy = false,
keys = {
--- 省略
},
---@type snacks.Config
opts = {
dashboard = {
row = 10,
preset = {
keys = {
{
icon = " ",
key = "f",
desc = "files",
action = function()
preventFlicker(Snacks.picker.files)
end,
},
{
icon = " ",
key = "e",
desc = "smart",
action = function()
preventFlicker(Snacks.picker.smart)
end,
},
{
icon = " ",
key = "r",
desc = "recent",
action = function()
preventFlicker(Snacks.picker.recent)
end,
},
{
icon = " ",
key = "t",
desc = "explorer",
action = function()
preventFlicker(Snacks.picker.explorer)
end,
},
{
icon = " ",
key = "p",
desc = "project",
action = function()
preventFlicker(Snacks.picker.projects)
end,
},
{
icon = " ",
key = "g",
desc = "grep",
action = function()
preventFlicker(Snacks.picker.grep)
end,
},
{
icon = " ",
key = "i",
desc = "edit",
action = function()
preventFlicker(function()
vim.cmd([[:startinsert]])
vim.cmd([[:stopinsert]])
end)
end,
},
{ icon = " ", key = "l", desc = "Lazy", action = ":Lazy", enabled = package.loaded.lazy ~= nil },
{ icon = " ", key = "q", desc = "quit", action = ":qa" },
},
},
sections = {
{
section = "terminal",
cmd = "chafa " .. dashboardImagePath .. " --size 48 --symbols vhalf; sleep .1",
height = 30,
padding = 0,
},
{
pane = 2,
{ section = "header" },
{ section = "keys", gap = 1, padding = 1 },
{ section = "startup" },
},
},
},
picker = {
--- 今回のメインではないので省略
},
},
}
```
設定自体は一般的ですが、1点だけこだわりがあります。`preventFlicker` 関数です。詳細は以下のページをご覧ください。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" />
<span class="link-card-v2-site-name">Minerva</span>
</div>
<div class="link-card-v2-title">
📝snacksのdashboardでkeysに指定したpickerコマンドからファイルを選択すると一瞬だけdashboardの画面がちらつく
</div>
<div class="link-card-v2-content">snacks.dashboardからsnacks.pickerを起動したあとに、ダッシュボードのバッファを削除する処理を入れる。これなら元のダッシュボードバッファを描画する処理が走らないため、一瞬チラつく現象は起こらない。</div>
<img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/troubleshooting.webp" />
<a data-href="📝snacksのdashboardでkeysに指定したpickerコマンドからファイルを選択すると一瞬だけdashboardの画面がちらつく" class="internal-link"></a>
</div>
%%[[📝snacksのdashboardでkeysに指定したpickerコマンドからファイルを選択すると一瞬だけdashboardの画面がちらつく]]%%
## very lazyな方へ
[[#起動直後に必要なキー入力が半減]] にて以下のように説明しました。
> 今までは `vim` で立ち上げてから `<C-j>f` を押して[[snacks.picker]]を起動していましたが、`vim` で立ち上げて `f` と押すだけでよくなりました。
しかし、ストイックな方の中には、こう思う人もいるかもしれません。
**『5つもキーを押さなければいけないのか...?』**
安心してください。もっと楽になります。[[Neovim]]起動後と同様に、ターミナルでも `<C-j>f` を押すだけで[[snacks.nvim]]が起動されるようにしてみましょう。[[ZLE]]を使って[[コマンド実行をキーバインド (Zsh)|コマンド実行をキーバインド]]できるように `.zshrc` に以下を追加します。
```zsh
function run_vim() {
LBUFFER="vim" # コマンドラインに「vim」をセット
zle accept-line # Enterキー相当の動作を実行(コマンド実行)
}
zle -N run_vim # ウィジェット
bindkey '^J' run_vim # キーバインド
```
[[ターミナル]]から一瞬で[[Neovim]]を起動し、[[snacks.dashboard]]表示し、[[snacks.picker]]を起動してファイルを開いてみます。
![[2025-04-03-21-35-39.webm|frame]]
*~~ダッシュボード見えないから意味ないのでは...~~*
## まとめ
[[chafa]]で作成したNeovimちゃんの画像を使って[[snacks.dashboard]]のダッシュボードを作成する方法を紹介しました。見栄えだけでなく、実用面でもメリットがあるので非常にオススメです😊