UUUMエンジニアブログ

UUUMのエンジニアによる技術ブログです

vimサークル活動報告 #2

システムユニットのxxuxa_kです。ついにUUUMでの生活も3年目に入りました。今日もvim活動報告です。普段使っているneovimの設定について書きたいと思います。たぶん5億番煎じくらいの内容になると思います。前回分もぜひご覧ください。

system.blog.uuum.jp

プラグインと設定

プラグイン管理にはvim-plugを使っています。インストールやアップデートが並列で走るので早いのとコマンドがシンプルなのがとてもいいです。使っているプラグインと設定を一部紹介していきます。

GitHub - junegunn/vim-plug: Minimalist Vim Plugin Manager

Denite

github.com

Deniteはありとあらゆる検索をやってくれるプラグインでDark poweredシリーズの一つです。前回の活動報告にもありますがこれがなくてはもはやvim生活は成り立ちません。まさにDark powerです。denite本体に合わせてgit関連のdenite sourceを追加してくれるプラグインも合わせて入れています。

Plug 'Shougo/denite.nvim', { 'do': ':UpdateRemotePlugins' }
Plug 'chemzqm/denite-git'

Deniteを起動するkeymapをいくつか設定していますが実際に使うのはやはりfile/recとlineが圧倒的に多いです。Deniteはsourceを複数与えることができるのでfile/recとlineを一度に指定するkeymapもつけていますが、あんまり使わないですね。ここに挙げているsource以外を見たいときはDeniteでsourceを検索してそのまま見つけた別のsourceで検索しています。(実はこれだけでいいんじゃね?と思ったりします。)ちなみにLeaderにはスペースキーを割り当てているのですが大きくて打ちやすいのが好みです。

nnoremap <Leader>f :Denite file/rec<CR>
nnoremap <Leader>l :Denite line<CR>
nnoremap <Leader>e :Denite file/rec line<CR>
nnoremap <Leader>h :Denite defx/history<CR>
nnoremap <Leader>g :Denite grep<CR>
nnoremap <Leader>b :Denite buffer<CR>
nnoremap <Leader>m :Denite menu<CR>
nnoremap <Leader>o :Denite outline<CR>
nnoremap <Leader>u :Denite source<CR>
nnoremap <Leader>c :Denite gitstatus<CR>

次に自動コマンドの設定です。DeniteはdeniteというFileTypeをもっているので、Deniteのバッファにいるときはこのkeymapが有効になります。iキーを押すとfilterが開始されますが、filterを終了したいときにCtrl-oが使えるというようになっています。

autocmd FileType denite call s:denite_my_settings()
function! s:denite_my_settings() abort
  nnoremap <silent><buffer><expr> <CR> denite#do_map('do_action')
  nnoremap <silent><buffer><expr> d denite#do_map('do_action', 'delete')
  nnoremap <silent><buffer><expr> p denite#do_map('do_action', 'preview')
  nnoremap <silent><buffer><expr> q denite#do_map('quit')
  nnoremap <silent><buffer><expr> i denite#do_map('open_filter_buffer')
  nnoremap <silent><buffer><expr> <Space> denite#do_map('toggle_select').'j'
endfunction

autocmd FileType denite-filter call s:denite_filter_my_settings()
function! s:denite_filter_my_settings() abort
  imap <silent><buffer><C-o> <Plug>(denite_filter_quit)
endfunction

1つおすすめの設定を書いておきます。↑のようにkeymapを設定すればDeniteのバッファからはiキーで検索を開始できますが、start_filter: v:trueを指定することでDeniiteを開くと同時にfilterが開始されます。filter時のプロンプトも好きなものに変えられるようになっています。

call denite#custom#option('default', {
      \ 'split': 'horizontal',
      \ 'highlight_filter_background': 'DeniteFilter',
      \ 'prompt': '> ',
      \ 'start_filter': v:true
      \})

最後にDeniteのmenuに関する設定です。Deniteのmenuについてはがんばってhelp読んではいるんですがまだ理解できておらず、使い道を探っているところです。とりあえず今できるとわかっていることは

  • 事前に用意したコマンドを実行する
  • 特定のファイルを開く

の2つです。bundle exec ...系のコマンド追加と、dotfileが開けるようにしました。追加はしたものの普段はtmux起動してpaneとかwindowを移動しながらやっているので今後もあまり使わない気がします。そもそもこういうのはDeniteに求める機能ではないですね。

let s:menus = {}
let s:menus.rails = {
      \ 'description': 'Commands in Rails project',
      \ 'command_candidates': [
        \ ['install gems', '!bundle install'],
        \ ['execute tests', '!bundle exec rails test'],
        \ ['rubocop', '!bundle exec rubocop'],
        \ ['rubocop auto-correct', '!bundle exec rubocop --auto-correct'],
        \ ['slim-lint', '!bundle exec slim-lint app/**/*.slim'],
      \ ]
      \ }
let s:menus.dotfiles = {
      \ 'description': 'Edit dotfiles',
      \ 'file_candidates': [
        \ ['neovim', '~/.config/nvim/init.vim'],
        \ ['vim', '~/.vimrc'],
        \ ['tmux', '~/.tmux.conf'],
        \ ['zshrc', '~/.zsh/.zshrc'],
      \ ]
      \ }
call denite#custom#var('menu', 'menus', s:menus)

Defx

github.com

またしてもDark poweredシリーズです。Defxはファイルブラウザです。Deniteと同様にsourceを対象に起動するという作りになっていて、Deniteと同じように設定ファイルがかけます。(ただしDefxはDeniteに依存しておらず単体で動きます)

Plug 'Shougo/defx.nvim', { 'do': ':UpdateRemotePlugins' }

keymapはこれだけです。defx導入前はNERDTreeを使っていた(今も入ってはいる)んですが全く使わなくなりました。

nnoremap <silent>,d :Defx<CR>

私は普段ファイルを指定してneovimを起動するということをしないので、プロジェクトのルートに移動したらすぐnvimとかnvとかを引数なしで叩いてneovimを起動します。(nnvnviを全部nvimのaliasにしています。)その際にdefxが自動で起動するようにしているという設定です。

autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | Defx | endif

DefxもDeniteと同様にdefxのバッファにdefxという独自のFileTypeがあるので、自動コマンドで専用のkeymapを割り当てることができます。ほぼDefxのhelpの内容そのままです。スペースキーで複数のファイルを選択して一気に開けるのはたまに嬉しいときがあります。Defxも便利なんですがほとんどの場合Deniteで済むのでめちゃくちゃ活用できているという感じではないです。

autocmd FileType defx call s:defx_my_settings()
function! s:defx_my_settings() abort
  nnoremap <silent><buffer><expr><CR> defx#do_action('open')
  nnoremap <silent><buffer><expr> c defx#do_action('copy')
  nnoremap <silent><buffer><expr> m defx#do_action('move')
  nnoremap <silent><buffer><expr> p defx#do_action('paste')
  nnoremap <silent><buffer><expr> v defx#do_action('open', 'vsplit')
  nnoremap <silent><buffer><expr> i defx#do_action('open', 'split')
  nnoremap <silent><buffer><expr> o defx#do_action('open_or_close_tree')
  nnoremap <silent><buffer><expr> O defx#do_action('open_tree_recursive')
  nnoremap <silent><buffer><expr> x defx#do_action('close_tree')
  nnoremap <silent><buffer><expr> P defx#do_action('open', 'pedit')
  nnoremap <silent><buffer><expr> K defx#do_action('new_directory')
  nnoremap <silent><buffer><expr> N defx#do_action('new_file')
  nnoremap <silent><buffer><expr> M defx#do_action('new_multiple_files')
  nnoremap <silent><buffer><expr> C defx#do_action('toggle_columns', 'mark:indent:icon:filename:type:size:time')
  nnoremap <silent><buffer><expr> S defx#do_action('toggle_sort', 'time')
  nnoremap <silent><buffer><expr> d defx#do_action('remove')
  nnoremap <silent><buffer><expr> r defx#do_action('rename')
  nnoremap <silent><buffer><expr> ! defx#do_action('execute_command')
  nnoremap <silent><buffer><expr> ee defx#do_action('execute_system')
  nnoremap <silent><buffer><expr> yy defx#do_action('yank_path')
  nnoremap <silent><buffer><expr> . defx#do_action('toggle_ignored_files')
  nnoremap <silent><buffer><expr> ; defx#do_action('repeat')
  nnoremap <silent><buffer><expr> b defx#do_action('cd', ['..'])
  nnoremap <silent><buffer><expr> ~ defx#do_action('cd')
  nnoremap <silent><buffer><expr> q defx#do_action('quit')
  nnoremap <silent><buffer><expr> <Space> defx#do_action('toggle_select') . 'j'
  nnoremap <silent><buffer><expr> * defx#do_action('toggle_select_all')
  nnoremap <silent><buffer><expr> j line('.') == line('$') ? 'gg' : 'j'
  nnoremap <silent><buffer><expr> k line('.') == 1 ? 'G' : 'k'
  nnoremap <silent><buffer><expr> <C-l> defx#do_action('redraw')
  nnoremap <silent><buffer><expr> <C-g> defx#do_action('print')
  nnoremap <silent><buffer><expr> cd defx#do_action('change_vim_cwd')
endfunction

vimdoc-ja

github.com

vimだったりneovimだったりvimscriptだったりを理解するスピードが最近上がってきたなと思うんですが、完全にこれのおかげです。おそらくvimでもneovimでも特定の内容についてhelpを引くと英語版の内容がヒットすると思いますが、これを日本語版で出せるようになるというものです。インストールしてset helplang=jaするだけで日本語のhelpがある内容であれば日本語で出してくれます。一度参加したゴリラ.vimで教えていただきました。翻訳をされた皆様には本当に感謝しかありません。 普段vimのhelp引いてないっていう方はぜひ使ってください。Deniteのline検索と一緒に使えばスラスラ読めます。

gorillavim.connpass.com

vim-lsp

github.com

さて最後にLsp周りです。私は特に理由はないのですがvim-lspを使っています。(というか他のものを使ったことがありません。)非同期補完にはまたしてもDark poweredシリーズdeoplete.nvimを使っています。 mattnさんのvim-lsp-settingsも入れているのですが:LspInstallServerしたあとの挙動がどうにも?となることが多かったので最近はあまり使っておらずvim-lspのwikiを見て1つずつLanguage Serverをセットするようにしています。

Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
Plug 'lighttiger2505/deoplete-vim-lsp'
Plug 'mattn/vim-lsp-icons'
Plug 'ryanolsonx/vim-lsp-typescript'

Language Server設定の例です。すべてに共通して実行できるLanguage Serverがあれば自動コマンドを実行してregister_serverするという形式になっています。これはvim-lspのwikiに書いてある内容をそのまま実行すればすぐに使えるようになります。とはいえwikiだけでは設定がうまくいかないこともあってGroovy用のLanguage Serverがその例です。設定方法をQiitaに書いたのでこちらも良ければ見てください。

qiita.com

if executable('solargraph')
  augroup LspRuby
    autocmd!
    autocmd User lsp_setup call lsp#register_server({
          \ 'name': 'solargraph',
          \ 'cmd': {server_info->[&shell, &shellcmdflag, 'solargraph stdio']},
          \ 'initialization_options': {"diagnostics": "true"},
          \ 'whitelist': ['ruby'],
          \ })
  augroup END
endif

if executable('vim-language-server')
  augroup LspVim
    autocmd!
    autocmd User lsp_setup call lsp#register_server({
          \ 'name': 'vim-language-server',
          \ 'cmd': {server_info->['vim-language-server', '--stdio']},
          \ 'whitelist': ['vim'],
          \ 'initialization_options': {
          \   'iskeyword': '@,48-57,_,192-255,-#',
          \   'vimruntime': expand($VIMRUNTIME),
          \   'suggest': { 'fromVimruntime': v:true },
          \ }})
  augroup END
endif

if executable('java') && filereadable(expand('$HOME/.lsp/groovy-language-server/build/libs/groovy-language-server.jar'))
  autocmd User lsp_setup call lsp#register_server({
        \'name': 'groovy-language-server',
        \'cmd': {server_info->[
        \  'java',
        \  '-jar',
        \  expand('$HOME/.lsp/groovy-language-server/build/libs/groovy-language-server.jar')
        \]},
        \'whitelist': ['groovy']
        \})
endif

まとめ

こうしてみると私のneovim設定はDark power無しにしては成り立たないものになっているなと思います。Shougoさんの作るプラグインは思想が一貫していて1つ何かがわかると他にも応用できるのがとても美しい作りになっているなと思います。またLspについてですが、もうちょっとなんとかできるよね?という感想を日々持っています。Lspを使ってneovimによりIDE感を出す活動をvimサークルではやっていきたいです。

最後に

我々システムユニットではvimmerもneovimmerも募集しています。興味のある方はぜひ採用サイトを見てみてください。

recruit.uuum.co.jp