🏎️

vimrc の分割管理を上手くやる 2020年版

2020/11/07に公開

筆者の環境について

Vim にはプラグインマネージャも沢山あります。その中で僕は vim-plug を使っています。Software Design という雑誌で「Vim の細道」という連載をやっている事もあり、皆さんに一番リーチしやすいプラグインマネージャを選んでいるという事もありますが、基本は「トラブルが発生しづらい」という理由で使っているのもあります。

vimrc の分割管理

ところで最近は Vim プラグインが沢山あり、個々に設定が必要な場合もあります。全て vimrc の中に書いてしまうのも良いのですが、そうするとどこからどこまでが、どのプラグインの設定か分からないといった問題が起きてしまいます。僕は vimrc をプラグイン毎に分割して管理しています。

Big Sky :: ぼくがかんがえたさいきょうの Vim のこうせい 2019年 年末版

" Vim 本体の機能のデフォルト値を経項する設定
setglobal cmdheight=2
setglobal laststatus=2

......

setglobal fileformat=unix
setglobal formatoptions+=mb

if !has('win32') && !has('win64')
  setglobal shell=/bin/bash
endif

if exists('&termguicolors')
  setglobal termguicolors
endif

if exists('&completeslash')
  setglobal completeslash=slash
endif

let g:no_gvimrc_example=1
let g:no_vimrc_example=1

let g:loaded_gzip               = 1
let g:loaded_tar                = 1
let g:loaded_tarPlugin          = 1
let g:loaded_zip                = 1
let g:loaded_zipPlugin          = 1
let g:loaded_rrhelper           = 1
let g:loaded_vimball            = 1
let g:loaded_vimballPlugin      = 1
let g:loaded_getscript          = 1
let g:loaded_getscriptPlugin    = 1
let g:loaded_netrw              = 1
let g:loaded_netrwPlugin        = 1
let g:loaded_netrwSettings      = 1
let g:loaded_netrwFileHandlers  = 1
let g:did_install_default_menus = 1
let g:skip_loading_mswin        = 1
let g:did_install_syntax_menu   = 1
"let g:loaded_2html_plugin       = 1

let g:mapleader = '\'
let g:maplocalleader = ','

" git commit 時にはプラグインは読み込まない
if $HOME != $USERPROFILE && $GIT_EXEC_PATH != ''
  finish
end

" Windows の場合は必要なパスを追加しておく
if has('win32')
  let $PATH='c:\dev\vim;c:\msys64\mingw64\bin;c:\msys64\usr\bin;'
  \ .'c:\Program Files\Java\jdk1.8.0_221\bin;'.$PATH
endif

" プラグインの読み込み
let g:plug_shallow = 0

call plug#begin('~/.vim/plugged')

Plug 'thinca/vim-ambicmd'
Plug 'thinca/vim-openbuf'
Plug 'thinca/vim-quickrun'

......

call plug#end()

" 各種設定の読み込み
call map(sort(split(globpath(&runtimepath, '_config/*.vim'))), {->[execute('exec "so" v:val')]})

また個々の設定ファイルはプラグイン名を付けて管理しています。

000-mappings.vim
001-filetype.vim
002-commands.vim
003-path.vim
102-autofmt.vim
104-conda.vim
105-ctrlp.vim
106-emmet.vim
107-lightline.vim
200-lsp.vim
201-languageclient-nvim.vim
202-vsnip.vim
204-quickrun.vim
300-vim.vim
301-java.vim
400-memolist.vim
401-openbrowser.vim
402-previm.vim
403-termdebug.vim
404-tohtml.vim
405-twitvim.vim

おおよそこれで問題なく動いていて、各 _config 配下のファイルではプラグインの有効無効を確認する為に

if empty(globpath(&rtp, 'autoload/lsp.vim'))
  finish
endif

こういうコードを先頭に書いています。こうする事で vimrc の Plug をコメントアウトすれば自動で設定も読み込まない様にしていたのです。しかしこの globpath という関数はランタイムパス上のファイルを探索する機能である為、幾分遅いという問題が当初から気になっていました。なんとかしないとなぁと感じていました。

vim-plug の機能を使う

なるべく vim-plug に依存しない様にとこの形にしていたのですが、さすがに起動速度が気になってきたので管理方法を変更する事にしました。

" 各種設定の読み込み
call map(sort(split(globpath(&runtimepath, '_config/*.vim'))), {->[execute('exec "so" v:val')]})

まずこの部分を以下の様に変えました。

let s:plugs = get(s:, 'plugs', get(g:, 'plugs', {}))
function! FindPlugin(name) abort
  return has_key(s:plugs, a:name) ? isdirectory(s:plugs[a:name].dir) : 0
endfunction
command! -nargs=1 UsePlugin if !FindPlugin(<args>) | finish | endif

runtime! _config/*.vim

vim-plug はグローバル変数 g:plugs に読み込んだプラグイン情報を管理しています。これを使えば globpath でファイルの存在を確認する必要が無くなります。個々の設定ファイルでは先頭に UsePlugin コマンド使って以下の様に書きます。

UsePlugin 'ctrlp.vim'

こうすると、もし Plug 'ctrlvim/ctrlp.vim' をコメントアウトした場合にはそれ以降は読み込まれません。前述の通り globpath はファイルを探索するので遅かったですが、この方法だとメモリ内の検索だけになり、起動速度も速くなります。僕の環境(Linux)では Vim の起動が500ミリ秒程度掛かっていたのですが、これにより200ミリ秒程度までに抑えられる様になりました。

僕の様に分割ファイルを使っていて globpath でプラグインの有効無効を確認している方には、この方法を試してみて欲しいです。

Discussion