2022/03/15

NeovimをちょっとLuaLuaさせた

せっかくNeovim専をやっているのにLua製プラグインに手を出さないのはアレかと思い、とりあえず3つほど移行させてみることにした。真のルアラーはおそらくinit.vimもLuaで書いているのだろうし、プラグインマネージャもpackerとかを使っているのだろう。だが、僕としてはそこまで一気やるのは正直面倒くさい。こういうのはやるにしてもじわじわと段階を経て触っていきたいものだ。

lualine.nvim

lualine.nvimはlightlineやairlineのLua実装とでも言うべきstatusline系プラグインである。Vimの扱いに習熟していたり、モダンなIDEの仕様に慣れたユーザはデフォルトのstatuslineではとても満足できない。そこで十年近く前からstatuslineの情報量や視認性を手軽に改善する手段としてこの手のプラグインが出回るようになった。先に述べたlightlineやairlineはその中でもとりわけ知名度が高く、ほとんどのVimmerに一度は使われていると言っても過言ではない。

lualineは豊富な機能を持ちながらもLua実装ゆえの高速さを兼ね備えた、いわば期待のニューホープだ。上のリポジトリページに掲載されている起動速度の検証では、もともと重いことで知られるairlineはもちろん、ミニマルを意識して設計されたlightlineをも僅かに上回る結果を叩き出している。もっとも約2msの差が知覚できるとは思えないが、見たところLuaの知識がなくても簡単に導入できそうなので試しにやってみることにした。なお、下記の設定例はすべてdein.vimの利用を前提にしている。

 1#dein.toml
 2[[plugins]]
 3 repo = 'nvim-lualine/lualine.nvim'
 4 hook_add = '''
 5 lua << EOF
 6 require('lualine').setup {
 7 options = {
 8  icons_enabled = true,
 9  theme = 'auto',
10  component_separators = { left = '|', right = '|'},
11  section_separators = { left = '', right = ''},
12  disabled_filetypes = {},
13  always_divide_middle = true,
14  colored = false,
15  },
16  sections = {
17   lualine_a = {'mode'},
18   lualine_b = {'branch', 'diff'},
19   lualine_c = {
20    {
21     'filename',
22     path = 1,
23     file_status = true,
24     shorting_target = 40,
25     symbols = {
26     modified = ' [+]',
27     readonly = ' [RO]',
28     unnamed = 'Untitled',
29     }
30    }
31   },
32   lualine_x = {'filetype', 'encoding'},
33   lualine_y = {
34    {
35     'diagnostics',
36     source = {'nvim-lsp'},
37      }
38     },
39   lualine_z = {'location'}
40 },
41  inactive_sections = {
42   lualine_a = {},
43   lualine_b = {},
44   lualine_c = {'filename'},
45   lualine_x = {'location'},
46   lualine_y = {},
47   lualine_z = {}
48 },
49  tabline = {},
50  extensions = {}
51 }
52EOF
53'''

以上が僕の設定となる。lualineの仕様はlightlineよりはairlineの方式に近く、下記にあるstatuslineの模式図に示されたアルファベットが個別の設定項目とそれぞれ対応する形を採っている。

1+-------------------------------------------------+
2| A | B | C                             X | Y | Z |
3+-------------------------------------------------+

つまり「B」の内容を変更したければlualine_b = {}の中身を編集すればよいということになる。僕の設定ではGitのbranchとdiffを表示させている。lightlineでは設計思想上、これらを表示するのに外部プラグインとの連携が必要だったが、lualineならオプション名を指定するだけで行える。diagnosticsの表示も大抵は使っているLSPの名称をsource = {}に書き込めば利用できる。

tablineをいじると文字通りタブの外観を変更することも可能だが、僕はタブではなくfzf.vimのバッファラインで管理しているので今回は空欄のままにした。なお、statuslineにカッチョいいファイルアイコンを生やすにはnvim-web-deviconsが必須である。

1#dein.toml
2[[plugins]]
3 repo = 'kyazdani42/nvim-web-devicons'

ファイルアイコンをフルカラー表示させたい場合は本項冒頭の設定のcoloredtrueにすること。

gitsigns.nvim

Gitの差分をリアルタイムに表示させるためにvim-gitgutterなどを入れている人はかなり多いと思う。実際、hunk(変更箇所へのジャンプ)のkeymapも用意されているし、これで困ることはまったくなかった。僕にとってLua製プラグインへの移行は趣味と挑戦を兼ねている。本項で紹介するgitsigns.nvimはそんなvim-gitgutterの上位互換を目指す意欲的なプラグインだ。

 1#dein.toml
 2
 3[[plugins]]
 4 repo = 'nvim-lua/plenary.nvim'
 5
 6[[plugins]]
 7 repo = 'lewis6991/gitsigns.nvim'
 8 hook_add = '''
 9 lua << EOF
10 require('gitsigns').setup {
11 signs = {
12   add          = {hl = 'GitSignsAdd'   , text = '', numhl='GitSignsAddNr'   , linehl='GitSignsAddLn'},
13   change       = {hl = 'GitSignsChange', text = '', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'},
14   delete       = {hl = 'GitSignsDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'},
15   topdelete    = {hl = 'GitSignsDelete', text = '', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'},
16   changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'},
17 },
18 signcolumn = true,
19 numhl      = false,
20 linehl     = false,
21 word_diff  = false,
22 watch_gitdir = {
23   interval = 1000,
24   follow_files = true
25 },
26 attach_to_untracked = true,
27 current_line_blame = false,
28 current_line_blame_opts = {
29   virt_text = true,
30   virt_text_pos = 'eol',
31   delay = 1000,
32   ignore_whitespace = false,
33 },
34 current_line_blame_formatter = '<author>, <author_time:%Y-%m-%d> - <summary>',
35 sign_priority = 6,
36 update_debounce = 100,
37 status_formatter = nil,
38 max_file_length = 40000,
39 preview_config = {
40   border = 'single',
41   style = 'minimal',
42   relative = 'cursor',
43   row = 0,
44   col = 1
45 },
46 yadm = {
47   enable = false
48  },
49  on_attach = function(bufnr)
50    local gs = package.loaded.gitsigns
51    local function map(mode, l, r, opts)
52      opts = opts or {}
53      opts.buffer = bufnr
54      vim.keymap.set(mode, l, r, opts)
55    end
56    map('n', ']c', function()
57      if vim.wo.diff then return ']c' end
58      vim.schedule(function() gs.next_hunk() end)
59      return '<Ignore>'
60    end, {expr=true})
61
62    map('n', '[c', function()
63      if vim.wo.diff then return '[c' end
64      vim.schedule(function() gs.prev_hunk() end)
65      return '<Ignore>'
66    end, {expr=true})
67    map({'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>')
68    map({'n', 'v'}, '<leader>hr', ':Gitsigns reset_hunk<CR>')
69    map('n', '<leader>hS', gs.stage_buffer)
70    map('n', '<leader>hu', gs.undo_stage_hunk)
71    map('n', '<leader>hR', gs.reset_buffer)
72    map('n', '<leader>hp', gs.preview_hunk)
73    map('n', '<leader>hb', function() gs.blame_line{full=true} end)
74    map('n', '<leader>tb', gs.toggle_current_line_blame)
75    map('n', '<leader>hd', gs.diffthis)
76    map('n', '<leader>hD', function() gs.diffthis('~') end)
77    map('n', '<leader>td', gs.toggle_deleted)
78    map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')
79  end
80  }
81EOF
82'''

そのわりにはやたら設定の文字数が多く見えるかもしれないが、これらは大半がデフォルト設定のコピペなので使用感を確かめる目的ならおそらくもっと少ない行数で事足りる。ただ、基本設定から個人的なベストを探る上では予め全部書き写しておいた方が後々いじりやすい。vim.keymap.setはNeovim v0.7以降にのみ実装されている機能なので注意。

Lua製と高々に宣伝するだけのことはあって差分の反映は相当に速いと感じる。vim-gitgutterはハイライトされるまでにだいぶ時間を要したが、本プラグインではほぼ即時に行われる。競合より圧倒的に豊富らしい機能の半分も使わないと僕は確信しているが、これだけでも移行して良かったと思える。

ちなみにset signcolumn=yesをinit.vimに書いておかないと、初めて差分がハイライトされるタイミングでVimがガクンと揺れてしまうので設定しておくことをおすすめする。

nvim-colorizer.lua

カラーコードに対応した背景色をつける定番のプラグイン。下記のおまじないを書き加えれば簡単に機能する。ついでに僕は遅延起動させているがどっちでも構わない。

1#dein_lazy.toml
2[[plugins]]
3 repo = 'norcalli/nvim-colorizer.lua'
4 on_event = 'BufEnter'
5 hook_source = '''
6 lua << EOF
7 require('colorizer').setup()
8EOF
9'''

LuaLuaしていないがすばらしいプラグイン

本来ここではかの有名なvim-easymotionの事実上のLua実装であるhop.nvimを紹介する予定だったが、それらよりずっと体験に優れたプラグインを見つけたので紹介したい。

fuzzy-motionは横移動と縦移動に別個のkeymapを提供する前述の二つと異なり、絞り込んだ候補に向かってアルファベット大文字一文字で飛ぶ単純明快な仕様である。奇妙な話だが、これを使ったことで僕はようやくeasymotion系のプラグインに感じていた不満に気づかされた。

従来のeasymotion系の作法では必ずしも単一のキーアサインで目的の位置に飛べるとは限らないのだ。ゆえに行頭に飛ぶキーや二文字で絞り込むキーなどがあれこれと用意されている。どれか一つでは用が足りず、かといってすべて使おうとすると僕には煩雑すぎる。これこそが内心抱いていた不満の正体だったようだ。

そこへいくと本プラグインの作法はすっきりしている。絞り込みに用いる文字数を上限なく受け付けることでキーアサインをたった一つに減らしている。ジャンプキーはデフォルトで大文字アルファベット一文字なので迷う余地もまずない。コロンブスの卵とはまさにことのことではないか。

おわりに

そのうち気が変わって冒頭で述べたinit.vimのLua化やpackerにも手を出すのかもしれないが、さしあたりはこんな程度で僕のLua欲は満たされた。たとえ実際の生産性にそこまで寄与していないとしても、とりあえずトレンドを追っていけばなにかしら役に立つこともあるだろう。

あわせて読ませたい

NeovimをもっとLuaLuaさせた
続編。Lua製プラグインをもっと増やした。

©2011 Rikuoh Tsujitani | Fediverse | Bluesky | Keyoxide | RSS | 小説