🦍

2年間ほとんどメンテしていなかったinit.luaを整理した話

はじめに

振り返ると、vimrcからinit.luaに乗り換えてluaを使うようになってから約2年ほど経ちました。

https://github.com/skanehira/dotfiles/commit/e5404091befff12bdeb952b6287e371bd8a4f434

しかし、それ以降はinit.luaをまともにメンテしておらず、ずっと一枚岩のままでした。
一枚岩の設定ファイルは検索するのは便利ではありますが、見通しが悪いなとは前から感じていたので、重い腰を上げてファイル分割することにしました。

本記事はどんな感じの構成になったのかについて、ざっくり書いていこうと思います。

メンテの結果

メンテ前はinit.lua一枚でしたが、メンテ後はinit.luaが7行になりました。

init.lua
require('my/settings/disable')
require('my/settings/options')
require('my/settings/lsp')
require('my/settings/autocmd')
require('my/settings/keymaps')
require('my/command')
require('my/plugins/lazy')

また、ディレクトリ構成に次のようになりました。

ディレクトリ構成
├── init.lua
└── lua
   └── my
       ├── command.lua
       ├── plugins
       │   ├── colorscheme
       │   │   └── nightfox.lua
       │   ├── completion
       │   │   ├── copilot.lua
       │   │   ├── emmet.lua
       │   │   ├── nvim-autopairs.lua
       │   │   ├── nvim-cmp.lua
       │   │   └── sonictemplate.lua
       │   ├── develop
       │   │   ├── capture.lua
       │   │   ├── graphql.lua
       │   │   ├── prettyprint.lua
       │   │   ├── quickrun.lua
       │   │   ├── ssr.lua
       │   │   ├── vital-whisky.lua
       │   │   └── vital.lua
       │   ├── docs
       │   │   ├── gyazo.lua
       │   │   ├── helpful.lua
       │   │   ├── helpgenerator.lua
       │   │   ├── list2tree.lua
       │   │   ├── maketable.lua
       │   │   ├── markdown.lua
       │   │   ├── memolist.lua
       │   │   ├── previm.lua
       │   │   ├── silicon.lua
       │   │   ├── tabular.lua
       │   │   ├── translate.lua
       │   │   └── vimdoc-ja.lua
       │   ├── etc
       │   │   ├── denops.lua
       │   │   ├── kensaku.lua
       │   │   └── skkeleton.lua
       │   ├── filer
       │   │   ├── fern-renderer-nerdfont.lua
       │   │   └── fern.lua
       │   ├── fuzzyfinder
       │   │   ├── telescope-egrepify.lua
       │   │   └── telescope.lua
       │   ├── git
       │   │   ├── gina.lua
       │   │   └── gitsigns.lua
       │   ├── infra
       │   │   ├── docker.lua
       │   │   └── k8s.lua
       │   ├── lang
       │   │   ├── go
       │   │   │   └── goimports.lua
       │   │   └── rust
       │   │       └── crates.lua
       │   ├── lazy.lua
       │   ├── list.lua
       │   ├── lsp
       │   │   ├── fidget.lua
       │   │   ├── lang
       │   │   ├── lsp_lines.lua
       │   │   ├── lsp_signature.lua
       │   │   ├── mason.lua
       │   │   └── null-ls.lua
       │   ├── quickfix
       │   │   ├── bqf.lua
       │   │   └── qfreplace.lua
       │   ├── statusline
       │   │   ├── bufferline.lua
       │   │   └── lualine.lua
       │   ├── test
       │   │   ├── test.lua
       │   │   └── themis.lua
       │   ├── ui
       │   │   ├── hlchunk.lua
       │   │   └── treesitter.lua
       │   ├── utils
       │   │   ├── dial.lua
       │   │   ├── guise.lua
       │   │   ├── octo.lua
       │   │   └── open-browser.lua
       │   └── window
       │       ├── winresizer.lua
       │       ├── winselector.lua
       │       └── zoom.lua
       ├── settings
       │   ├── autocmd.lua
       │   ├── disable.lua
       │   ├── keymaps.lua
       │   ├── lsp.lua
       │   └── options.lua
       └── utils.lua

ディレクトリ構成について

myディレクトリ配下に各種プラグインとその設定や、それ以外の設定などをまとめています。
myというディレクトリを切っているのは、なるべく他のプラグインと名前空間をかぶらないようにするためです。

myプレフィックスがない場合、たとえばcmp.luaという設定ファイルがあるとcmpプラグインと名前空間が被ってしまいrequire('cmp')はパスで見つけたファイルを優先してimportしてしまうからです。

プラグインマネージャーはlazy.nvimを使っていて、plugins配下には各種プラグインとその設定の定義をおいています。
プラグインは、おおまかなカテゴリごとにさらにディレクトリを切っています。

lazy.lua
local plugins = utils.array_map(
  require('my/plugins/list'),
  function(plugin)
    return require('my/plugins/' .. plugin)
  end
)

require("lazy").setup(plugins)

list.luaで実際に使うプラグイン一覧を管理しています。

list.lua
local plugins = {
  -- etc
  'etc/kensaku',
  'etc/skkeleton',
  'etc/denops',

  -- ui
  'ui/treesitter',
  'ui/hlchunk',

  -- omitted
}

もし一時的に無効にしたいプラグインがある場合は配列の要素をコメントアウトすれば済むし、
プラグイン追加時はplugins/foo/bar.luaを追加したあとに配列に追加するだけなので、もろもろ管理が楽です。

各種プラグインの定義は次のようになっています。
lazy.nvimに渡すオブジェクトの形になっています。

fern.lua
local utils = require('my/utils')
local nmap = utils.keymaps.nmap

local config = function()
  vim.g['fern#renderer'] = 'nerdfont'
  vim.g['fern#window_selector_use_popup'] = true
  vim.g['fern#default_hidden'] = 1
  vim.g['fern#default_exclude'] = '\\.git$\\|\\.DS_Store'

  vim.api.nvim_create_autocmd('FileType', {
    pattern = 'fern',
    callback = function()
      nmap('q', ':q<CR>', { silent = true, buffer = true })
      nmap('<C-x>', '<Plug>(fern-action-open:split)', { silent = true, buffer = true })
      nmap('<C-v>', '<Plug>(fern-action-open:vsplit)', { silent = true, buffer = true })
      nmap('<C-t>', '<Plug>(fern-action-tcd)', { silent = true, buffer = true })
    end,
    group = vim.api.nvim_create_augroup('fernInit', { clear = true }),
  })
end

local fern = {
  'lambdalisue/fern-hijack.vim',
  dependencies = {
    'lambdalisue/fern.vim',
    cmd = 'Fern',
    config = config,
  },
  init = function()
    nmap('<Leader>f', '<Cmd>Fern . -drawer<CR>', { silent = true })
  end
}

return fern

ただ、このように動的にrequireするファイルパスを組み上げているため、定義ジャンプが効かないのが少し不便だなと思っています。
なにかよい改善方法があれば知りたいところです。

さいごに

vimrcからinit.lua乗り換え時は一日かかっていたんですが、整理は意外とそれほど時間がかからずあっさり終わりました。
しばらくこの構成を試しつつ改善していきたいと思います。

Discussion