vim+uniteからneovim+deniteへの移行

社内の傾向としてはエディタはIntelliJやvscodeが主流で、私は元々vi系エディタのユーザなので最近はvscodeをvimのキーバインドで使ってます。

IDEは多機能なのはいいんですが、正直使いこなせてない感がすごいし、ぶっちゃけほぼeclipseしか使ったことない。
vscodeはvimキーバインドでもかなり使えるんですが、ちょいちょい気になる瞬間があり、なによりエディタは慣れたやつのほうがいい。
というか正直vimも使い切れてない。なので、まあそれならと比較的指が慣れてるvimの実装の一つでありずっと気になっていたneovimをこのブログ書く機会に一念発起してつかってみることにしました。

ubuntu18.04でapt使って入れようとすると、何もしないとneovimのバージョンが0.2系なのですが、リポジトリを追加することでneovim0.3系がinstallできます。その前にpython関係のやつもわすれずにいれときます。
sudo apt-get install python-dev python-pip python4-dev python3-pip 
sudo apt-get install software-properties-common 
sudo add-apt-repository ppa:neovim-ppa/stable 
sudo apt-get update 
sudo apt-get install neovim
今まで利用していた.vimrcをそのまま.config/nvim/init.vimにコピーしてまずは起動してみると、起動自体はできたのですが、プラグインを動かそうとするとTERMの設定がおかしというエラーが。
:checkhealthしてみろ、と親切なメッセージがでていたのでやってみると、TERMの設定がtmuxとzshの間で設定が異なっているのが問題だったらしく以下の設定を追加。

.tmux.conf
set-option -g default-terminal "screen-256color"
 
vimのpluginとしておなじみ便利なuniteをつかってたんですが、この機会に後継?っぽいdeniteを使ってみることにします。(これがneovim0.2系だと動かない
 
.vimrc
"unite.vim関連


" バッファ一覧
nnoremap <silent> ,ub :<C-u>Unite buffer<CR>
" ファイル一覧
nnoremap <silent> ,uf :<C-u>UniteWithBufferDir -buffer-name=files file<CR>
" レジスタ一覧
nnoremap <silent> ,ur :<C-u>Unite -buffer-name=register register<CR>
" 最近使用したファイル一覧
nnoremap <silent> ,um :<C-u>Unite file_mru<CR>
" 常用セット
nnoremap <silent> ,uu :<C-u>Unite buffer file_mru<CR>
" 全部乗せ
nnoremap <silent> ,ua :<C-u>UniteWithBufferDir -buffer-name=files buffer file_mru bookmark file<CR>


" ウィンドウを分割して開く
au FileType unite nnoremap <silent> <buffer> <expr> <C-j> unite#do_action('split')
au FileType unite inoremap <silent> <buffer> <expr> <C-j> unite#do_action('split')
" ウィンドウを縦に分割して開く
au FileType unite nnoremap <silent> <buffer> <expr> <C-l> unite#do_action('vsplit')
au FileType unite inoremap <silent> <buffer> <expr> <C-l> unite#do_action('vsplit')
" ESCキーを2回押すと終了する
au FileType unite nnoremap <silent> <buffer> <ESC><ESC> :q<CR>
au FileType unite inoremap <silent> <buffer> <ESC><ESC> <ESC>:q<CR>
uniteの時につかってた設定。これを。。。
 
init.vim
" denite.vim関連


" バッファ一覧
nnoremap <silent> ,ub :<C-u>Denite buffer<CR>
" ファイル一覧
nnoremap <silent> ,uf :<C-u>DeniteWithBufferDir -buffer-name=files file<CR>
" レジスタ一覧
nnoremap <silent> ,ur :<C-u>Denite -buffer-name=register register<CR>
" 最近使用したファイル一覧
nnoremap <silent> ,um :<C-u>Denite file_mru<CR>
" 常用セット
nnoremap <silent> ,uu :<C-u>Denite buffer file_mru<CR>
" 全部乗せ
nnoremap <silent> ,ua :<C-u>DeniteBufferDir -buffer-name=files buffer file_mru bookmark file<CR>


" ウィンドウを分割して開く
au FileType denite nnoremap <silent> <buffer> <expr> <C-j> denite#do_action('split')
au FileType denite inoremap <silent> <buffer> <expr> <C-j> denite#do_action('split')
" ウィンドウを縦に分割して開く
au FileType denite nnoremap <silent> <buffer> <expr> <C-l> denite#do_action('vsplit')
au FileType denite inoremap <silent> <buffer> <expr> <C-l> denite#do_action('vsplit')
" ESCキーを2回押すと終了する
au FileType denite nnoremap <silent> <buffer> <ESC><ESC> :q<CR>
au FileType denite inoremap <silent> <buffer> <ESC><ESC> <ESC>:q<CR>
 
こうだなきっと(uniteをdeniteに変えただけ

なんとこんな雑な変換のやり方でも一覧系はだいたい全部うごいたんですが、ファイル一覧だけ動かない。DeniteWithBufferDirがないっぽいので、:help DeniteしてみてみるとDeniteBufferDirという名前でほしいのが取れそう。
 
なので該当箇所をこれに変更します。あとなぜかbookmarkがなかったので全部乗せからbookmarkも外しときます(uniteだと定義がなくてもエラーになってなかった
 
init.vim
" ファイル一覧
nnoremap <silent> ,uf :<C-u>DeniteBufferDir -buffer-name=files file<CR>
 :
" 全部乗せ
nnoremap <silent> ,ua :<C-u>DeniteBufferDir -buffer-name=files buffer file_mru file<CR>
 
これだけだと動かなくて、checkhealthした際に出ていたアドバイスに従って、:UpdateRemotePluginを実行。これでDenite自体は起動できました。

ただ、なんかinsertモードから戻るときのescapeの反応が異様に遅い。。
しらべてるとtmuxのescape-timeのせいだという記事をみかけたのでとりあえずそれのせいにしとこう。

.tmux.conf
set -s escape-time 10
 
よしこれで終わりかと思ったら、deniteのbufferから該当箇所にとべない?
denite.txtにあるexampleからまるっと以下の設定をコピー

init.vim
  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
 
。。。ってここで気づいたんですが分割して開くやつも動いてなかった。
 
 
au FileType denite nnoremap <silent> <buffer> <expr> <C-j> denite#do_action('split')
これをこんなかんじでdenite_my_settingsの中に張り付けなおしていきます。
 
init.vim
autocmd FileType denite call s:denite_my_settings()
function! s:denite_my_settings() abort
             :
  nnoremap <silent><buffer><expr><C-j> denite#do_map('do_action','split')
             :
endfunction
これで検索結果のバッファからEnterキーでとんだりsplitして画面を開いたりできるようになりました。


ついでに最近でも開発が続いてるripgrepをdeniteから使うようにしてみたいと思います。
つかってるubuntuのバージョンだとaptにはなかったし、snapは利用できない環境だし、しょうがないのでdebian向けのパッケージでいれます。
curl -LO https://github.com/BurntSushi/ripgrep/releases/download/11.0.2/ripgrep_11.0.2_amd64.deb
sudo dpkg -i ripgrep_11.0.2_amd64.deb

以下の設定をinit.vimに追加。
 
init.vim
nnoremap <silent> ,ug :<C-u>Denite grep -buffer-name=grep-buffer-denite<CR>
nnoremap <silent> ,uw :<C-u>DeniteCursorWord grep -buffer-name=grep-buffer-denite<CR>
nnoremap <silent> ,ue :<C-u>Denite -resume -buffer-name=grep-buffer-denite<CR>
nnoremap <silent> ,un :<C-u>Denite -resume -buffer-name=grep-buffer-denite -select=+1 -immediately<CR>
nnoremap <silent> ,up :<C-u>Denite -resume -buffer-name=grep-buffer-denite -select=-1 -immediately<CR>


call denite#custom#var('grep', 'command', ['rg'])
call denite#custom#var('grep', 'recursive_opts', [])
call denite#custom#var('grep', 'pattern_opt', [])
call denite#custom#var('grep', 'default_opts', ['-S', '--vimgrep','--no-heading'])
 

これでripgrep結果のdenite bufferからファイルにとんだり、キャッシュした検索結果からfilterして探したりとできるようになりました。

これだけだとなんなので、次に補完機能などを強力にしてくれるvim-lspを導入します。
vim-lsp-settingsで簡略化できるようですが、まずはそのありがたみと、素の設定のめんどくささを知るためにそのまま設定してみます。とりあえずtypescriptとphpでチャレンジ。
vundleユーザなので以下をpluginとして追加。
asynccompleteでtypescriptを補完するためにtscompletejob、asynccomplete-tscompletejob(長い)を入れます
 
init.vim
Plugin 'prabirshrestha/async.vim'
Plugin 'prabirshrestha/vim-lsp'
Plugin 'prabirshrestha/asynccomplete.vim'
Plugin 'prabirshrestha/asynccomplete-lsp.vim'
Plugin 'runoshun/tscompletejob'
Plugin 'prabirshrestha/asynccomplete-tscompletejob.vim'
このあたり(https://github.com/prabirshrestha/vim-lsp/wiki/Servers-TypeScript)を参考に入れていきます。まずはlanguageサーバをインストール。*1
npm install -g typescript-language-server intelephense
plugin設定に追加。intelephenseはプラグイン設定いらない模様。
 
init.vim
Plugin 'ryanolsonx/vim-lsp-typescript'
各language server設定。このあたりは記載されてるまま。*2
init.vim
" php 
if executable('intelephense')
  augroup LspPHPIntelephense
    au!
    au User lsp_setup call lsp#register_server({
        \ 'name': 'intelephense',
        \ 'cmd': {server_info->[&shell, &shellcmdflag, 'intelephense --stdio']},
        \ 'whitelist': ['php'],
        \ 'initialization_options': {'storagePath': '/tmp/intelephense'},
        \ 'workspace_config': {
        \   'intelephense': {
        \     'files': {
        \       'maxSize': 1000000,
        \       'associations': ['*.php', '*.phtml'],
        \       'exclude': [],
        \     },
        \     'completion': {
        \       'insertUseDeclaration': v:true,
        \       'fullyQualifyGlobalConstantsAndFunctions': v:false,
        \       'triggerParameterHints': v:true,
        \       'maxItems': 100,
        \     },
        \     'format': {
        \       'enable': v:true
        \     },
        \   },
        \ }
        \})
  augroup END
endif



" typescript
if executable('typescript-language-server')
    au User lsp_setup call lsp#register_server({
        \ 'name': 'typescript-language-server',
        \ 'cmd': {server_info->[&shell, &shellcmdflag, 'typescript-language-server --stdio']},
        \ 'root_uri':{server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'tsconfig.json'))},
        \ 'whitelist': ['typescript', 'typescript.tsx'],
        \ })
endif
 
vim-lspの設定。これもとりあえずはそのまま。g dでカーソル箇所のワードの定義箇所を探してくれます便利。F2でシンボル一括リネーム。
 
init.vim
function! s:on_lsp_buffer_enabled() abort
    setlocal omnifunc=lsp#complete
    setlocal signcolumn=yes
    nmap <buffer> gd <plug>(lsp-definition)
    nmap <buffer> <f2> <plug>(lsp-rename)
    " refer to doc to add more commands
endfunction


augroup lsp_install
    au!
    " call s:on_lsp_buffer_enabled only for languages that has the server registered.
    autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
augroup END
 
エラー表示と補完候補のリフレッシュタイミングの設定はいろいろためしてみましたが、結局デフォルトの挙動が好みだったのと、popup更新もう少しゆっくりでもいいかなってことでg:asyncomplete_popup_delayの値を少し増やすだけにしました。
 
で、結局lsp-serverと補完の設定とか、言語を追加するたびにペタペタやらんといかんわけですね。手順は決まってるといえ、確かに長いし、なにより面倒くさい。そこでvim-lsp-settingsの出番。
 
init.vim
Plugin 'mattn/vim-lsp-settings'
を:VundleInstallして、
 
init.vim
if empty(globpath(&rtp, 'autoload/lsp.vim'))
  finish
endif
 
でおわり。*1,*2の設定はいらなくなります。 あとは使いたい言語編集時に:LspInstallServerするようにすれば裏で設定してくれます。ありがたや。
 
 
思ったより長くなってしまったので、ここまでにしたいと思います。最後までご覧いただきありがとうございました。