[Vim] filetype毎の設定を関数単位で分ける
この記事は Vim2 advent calendar の5日目の記事です。
vimrcを書いていると以下のようなことがありませんか?
- filetype毎にマップを定義したい
- filetype毎にインデントを変えたい
こんな感じです。
augroup vimrc
autocmd!
autocmd Filetype javascript setlocal expandtab softtabstop=4 shiftwidth=4 tabstop=4
autocmd Filetype javascript inoremap <buffer> <C-CR> <End>;<CR>
autocmd Filetype javascript inoremap <buffer> jk <End>;
augroup END
この方法でいろんなファイルタイプに対応するとなると、横にも縦にも伸びます。
文字数が多く、ぱっと見たときにわかりづらいです。
augroup vimrc
autocmd!
autocmd Filetype javascript setlocal expandtab softtabstop=4 shiftwidth=4 tabstop=4
autocmd Filetype typescript setlocal expandtab softtabstop=4 shiftwidth=4 tabstop=4
autocmd Filetype ruby setlocal expandtab softtabstop=2 shiftwidth=2 tabstop=2
autocmd Filetype gitconfig setlocal noexpandtab softtabstop=4 shiftwidth=4 tabstop=4
autocmd Filetype gitcommit setlocal expandtab softtabstop=2 shiftwidth=2 tabstop=2
autocmd Filetype gitcommit setlocal spell
autocmd Filetype javascript,typescript inoremap <buffer> <C-CR> <End>;<CR>
autocmd Filetype javascript,typescript inoremap <buffer> jk <End>;
autocmd Filetype help nnoremap <buffer> q ZZ
autocmd Filetype vimfiler nmap <buffer> l <Plug>(vimfiler_expand_tree)
:
:
:
:
augroup END
filetype毎の設定については filetype plugin indent on
で読み込まれるようになる
ftplugin/
, indent/
ディレクトリ配下のファイルで設定するのがVimおすすめの方法のようなのですが、その場合filetype毎にファイルを作成しなければいけません(javascript.vim, ruby.vim, help.vim.....)。
ファイルの量が増えてしまい、(私としては)管理しづらくなってしまします。
そこで、filetype毎の設定を関数単位で管理する方法を紹介します。
やり方
以下をvimrcに記載します。
augroup vimrc
autocmd!
autocmd Filetype * call s:filetype(expand('<amatch>'))
augroup END
function! s:filetype(ftype) abort
if !empty(a:ftype) && exists('*' . 's:filetype_' . a:ftype)
execute 'call s:filetype_' . a:ftype . '()'
endif
endfunction
function! s:filetype_javascript() abort
call s:set_indent(4, 0)
inoremap <buffer> <C-CR> <End>;<CR>
inoremap <buffer> jk <End>;
endfunction
function! s:filetype_typescript() abort
call s:filetype_javascript()
endfunction
function! s:filetype_ruby() abort
call s:set_indent(2, 0)
endfunction
function! s:filetype_gitconfig() abort
call s:set_indent(4, 1)
endfunction
function! s:filetype_gitcommit() abort
call s:set_indent(2, 0)
setlocal spell
endfunction
function! s:set_indent(tab_length, is_hard_tab) abort
if a:is_hard_tab
setlocal noexpandtab
else
setlocal expandtab
endif
let &l:shiftwidth = a:tab_length
let &l:softtabstop = a:tab_length
let &l:tabstop = a:tab_length
endfunction
最初のaugroupでは、Filetypeイベントが発生した際に s:filetype
関数を呼び出しています。
Filetypeイベントの場合 expand('<amatch>')
でfiletype("javascript", "ruby", "help", etc...)
が取れるのでs:filetype
関数の引数にfiletypeを渡すことができます。
次に s:filetype
関数では引数で受け取った文字列より s:filetype_<引数で受け取った文字列>
という関数があるかをチェックしています。存在する場合はその関数を呼び出します。
s:filetype_xxx
という関数ではfiletype毎の設定を行っています。
これで関数単位で管理することができます。
管理したいfiletypeが増えた場合は s:filetype_xxxx
関数を増やすだけです。
好みの問題だとは思いますが、私はこちらの関数単位での管理の方が好きです。別のfiletypeと設定内容が一致する場合は別のfiletype用の関数をコールするだけで済みます。あと、なんかかっこよくも見えます(ここが一番大事)
最後の s:set_indent
という関数ですがインデントの設定を行っています
- expandtab or noexpandtabの設定
- shiftwidthの指定
- softtabstopの指定
- tabstopの指定
こちらもあると便利ですので、ぜひ使ってみてください。
注意
提案した管理方法ですが、無効化または2重読み込み防止用のb:did_ftplugin
, b:did_indent
相当の処理については何も考慮していません。また別のFiletypeが割り当たった場合に初期化するための設定を指定しておくb:undo_ftplugin
, b:undo_indent
についても考慮していません。
現状私が使っている中で問題も出ていないのでおそらく大丈夫だとは思いますが、b:did... やb:undo...について知っておいていただけるといいかなと思います。
その他
filetype毎の設定をvimrcから抜き出して1つのファイルにまとめるのも手です(私の設定ではそうしています)
最後に
説明した管理方法については私が考えたわけではなく、どなたかのvimrcを参考に作りました。
おそらくvimrc読書会の過去ログ探っているときに見つけたのかなーという記憶。どなたかに感謝です。
説明内容に関連するヘルプやファイルを以下に記載しておきます (vimrc読書会の過去ログも参考になります。)
" :h FileType
" :h :filetype
" :h ftplugin
" :h undo_ftplugin
" :h undo_indent
" :h 30.3
" :e $VIMRUNTIME\ftplugin.vim (filetype plugin on時に何が行われているか分かります)
" :e $VIMRUNTIME\indent.vim (filetype indent on時に何が行われているか分かります)
ではでは、 Happy Vim Life!!!!
Discussion