2024-03-18

Neovim から tig を起動する with toggleterm.nvim

Git の操作をするときは、基本的に tig を使っています。

これまでは、ターミナル上で tmux を起動して tig 専用のペインを用意し、 commit や push をしたくなったら tig のペインに移動するという作業フローを取っていました。

この作業フローでも特別に困ったことはなかったのですが、Neovim の Terminal とうまく連携できればより実装の集中力を切らさない作業フローになるのではないかと思い、試してみることにしました。

きっかけ

そもそも試してみようと思うきっかけとなったのは、【解説】開発ライブ実況 #1 (Vim / Go) 編 by メルペイ Architect チーム Backend エンジニア という記事と Youtube の実況動画でした。

2020年の記事なので結構前なのですが、この動画の中で Vim 使いの方が Vim から tig の操作をして commit を積んでおり、その開発の様子がとてもスムーズに見えました。

ライブコーディング動画、発見があってとても良いですね。 自分のこれまでのエンジニア人生を振り返っても、ペアプロやライブコーディングは熟練者の作業を実際に見る機会になって、変化につながる気づきを与えてくれているなと感じます。

どうやるか

上記の記事には Neovim から tig を開く方法についても記載されており、neovim-remote というツールを使っているようでした。

全く使ったことのないツールでしたので軽く調べてみたのですが、個人的な感触としてはやりたいことに対してやや複雑な構成になってしまうと感じたのと、前述のとおり2020年の記事であって3-4年経過した今となっては別の手段も多くありそうだなと思い、色々と調べてみることにしました。

その結果、最終的に toggleterm.nvim という Neovim から Terminal をいい感じに管理できるプラグインを使うことにしました。

toggleterm.nvim と tig の連携

toggleterm.nvim を使い、以下のように Neovim から tig を起動できるようになりました。

tig_on_nvim_terminal_demo

;t で tig を起動し、同じく ;t で閉じます。

tig in Neovim ではパッと開いてパッと commit してパッと閉じる、というフローが最も多いと思うので Terminal の表示方法としても一番マッチしそうな floating window を使うことにしました。このあたりを選択できるのも toggleterm.nvim の良いところです。

気をつけた点としては、tig プロセスを都度起動しないようにしたことです。 短い gif 動画なのでわかりづらいですが、一度 tig を閉じて、再度起動したときに最後の状態で開かれるようになっています。裏では Terminal が閉じても tig プロセスは生きています。

Terminal 上では tig のキーバインドになるので、qQ で tig が終了すると Terminal も終了します。操作感としては問題ないのですが、これだと都度 tig プロセスを起動することになってしまいます。 これを防ぐために ;t を Terminal の close に割り当てつつ hidden = true をセットして破棄されないようにしました。

以下が設定の抜粋です。本家の README.md に lazygit との連携例 があり、そちらがかなり参考になりました。

local Terminal = require("toggleterm.terminal").Terminal
local tigTerminal = Terminal:new({
  cmd = "tig status",
  dir = "git_dir",
  direction = "float",
  hidden = true,
  on_open = function(term)
    vim.api.nvim_buf_set_keymap(term.bufnr, "t", ";t", "<CMD>close<CR>", { noremap = true, silent = true })
  end,
})

function ToggleTigTerminal()
  tigTerminal:toggle()
end

vim.api.nvim_set_keymap("n", ";t", "<cmd>lua ToggleTigTerminal()<CR>", { noremap = true, silent = true })