もた日記

くだらないことを真面目にやる

Vimメモ : Neovimで開発環境を段階的に構築する(5)スニペット、入力補完と定義元ジャンプ

スニペット

スニペットを利用するには下記のスニペットエンジンと、

Repository スター数
SirVer/ultisnips 4737
garbas/vim-snipmate 1795
Shougo/neosnippet.vim 937
drmingdrmer/xptemplate 278

スニペット定義ファイルをインストールする必要がある。

Repository スター数
honza/vim-snippets 3079
Shougo/neosnippet-snippets 266

ultisnipsとvim-snippetsまたはneosnippe.vimとneosnippet-snippetsの組み合わせが人気のようなので、今回はultisnipsを利用する。

 Plug 'SirVer/ultisnips'
 Plug 'honza/vim-snippets'

まずは利用できるスニペットを確認してみる。
既にfzf.vimをインストールしているので:Snippetsを実行してみると以下のように開いているファイルタイプで利用可能なスニペット一覧が確認できる。

f:id:wonder-wall:20190816185935p:plain

例えばPythonのifeeというスニペットの場合は、ifeeを入力した後でスニペットを展開するトリガーキーを入力する。トリガーキーはデフォルトでは<tab>だが、以下のように設定を変更することもできる。

let g:UltiSnipsExpandTrigger='<c-j>'

スニペットによっては複数入力できる場合もあり、デフォルトでは<c-j><c-k>で前後に移動できる(例の場合はifconditionの次に<c-j>ifpassに移動)。なお、キーは下記設定で変更可能。

let g:UltiSnipsJumpForwardTrigger="<c-b>"
let g:UltiSnipsJumpBackwardTrigger="<c-z>"

f:id:wonder-wall:20190816190020p:plain

入力補完

入力補完プラグラインとしては下記が挙げられる。

Repository スター数
ycm-core/YouCompleteMe 19578
neoclide/coc.nvim 5586
Shougo/deoplete.nvim 4470
maralla/completor.vim 1019

今回はLanguage Server Protocolに対応したcoc.nvimを試してみる。
なお、coc.nvimはnodejsを使うので:checkhealthしたときにNode.js provider (optional)OKになるようにしておく必要がある。

Plug 'neoclide/coc.nvim', {'branch': 'release'}

もしプラグインをインストールした後で下記メッセージが表示される場合は、

[coc.nvim] javascript file not found, please compile the code or use release branch.

以下のコマンドを実行してみるとよい。

:call coc#util#install()

インストールが成功していれば:CocInfoコマンドでcoc.nvimの情報を確認できる。

## versions

vim version: NVIM v0.4.0-1381-g4aabe4a0d
node version: v12.7.0
coc.nvim version: 0.0.73-a540bcf7b0
term: xterm
platform: linux

## Messages

coc.nvimでは必要な拡張機能:CocInstallコマンドで追加していくというスタイルなので、coc.nvimのjson設定ファイル用にcoc-json、ultisnips用にcoc-ultisnips、そして試しにPython向けにcoc-pythonを追加してみる。

:CocInstall coc-python coc-json coc-ultisnips

coc-pythonではデフォルトではjediを使うようなので下記コマンドでインストールする(今回はpyenv-virtualenvでneovim3という環境を作成済み)。

pyenv shell neovim3
pip install jedi

次に:CocConfigコマンドを実行するとcoc-settings.jsonを編集するモードになるので下記設定を追加する。
今回はALEでLinterを実行しているので、diagnostic.enablepython.linting.enabledfalseにして、python.jediPathjediのパスを指定する。

{
        "diagnostic.enable": false,
        "python.linting.enabled": false,
        "python.jediPath": "~/.pyenv/versions/neovim3/lib/python3.7/site-packages/"
}

なお、coc-jsonを追加しているので以下のように補完が効いているはず。

f:id:wonder-wall:20190817000135p:plain

上記の設定が完了すると*.pyファイルを編集するときに補完が効くようになる([US]はultisnipsの補完、[JD]はjediの補完)。

f:id:wonder-wall:20190817000206p:plain

f:id:wonder-wall:20190817000219p:plain

その他の設定例はREADMEに書いてあり、<tab>Shift-<tab>で補完候補の切り替え、<CR>で候補を確定するときは以下の設定をinit.vimに追加する。

inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"

function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"

また、:CocConfigで‘"python.jediEnabled": falseという設定を追加すると、Microsoft Python Language Serverによる補完が有効になる。

f:id:wonder-wall:20190817001804p:plain

定義元ジャンプ

Language Server Protocolに対応したcoc.nvimでは関数定義元へのジャンプや、参照一覧を表示することができるので、下記設定をinit.vimに追加。

nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  else
    call CocAction('doHover')
  endif
endfunction

これでgdを押すと関数定義元にジャンプでき、grを押すと以下のように参照一覧を表示できる。

f:id:wonder-wall:20190825165849p:plain

また、Kを押すとドキュメントをホバー表示できる(ただし、vim, helpファイルタイプでは別ウィンドウ表示)。

f:id:wonder-wall:20190825165905p:plain

まとめ

今回の~/.config/nvim/init.vim

call plug#begin('~/.vim/plugged')
Plug 'joshdick/onedark.vim'
Plug 'itchyny/lightline.vim'
Plug 'ntpeters/vim-better-whitespace'
Plug 'Yggdroot/indentLine'
Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
Plug 'majutsushi/tagbar', { 'on': 'TagbarToggle' }
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
Plug 'junegunn/fzf.vim'
Plug 'airblade/vim-rooter'
Plug 'tpope/vim-fugitive'
Plug 'airblade/vim-gitgutter'
Plug 'xuyuanp/nerdtree-git-plugin', { 'on': 'NERDTreeToggle' }
Plug 'sheerun/vim-polyglot'
Plug 'dense-analysis/ale'
Plug 'SirVer/ultisnips'
Plug 'honza/vim-snippets'
Plug 'neoclide/coc.nvim', {'branch': 'release'}
call plug#end()

set number
set termguicolors
" airblade/vim-gitgutter
set updatetime=100

let g:python_host_prog = $HOME . '/.pyenv/versions/neovim2/bin/python'
let g:python3_host_prog = $HOME . '/.pyenv/versions/neovim3/bin/python'

" joshdick/onedark.vim
colorscheme onedark
" itchyny/lightline.vim
let g:lightline = {'colorscheme': 'onedark'}
" sheerun/vim-polyglot
" let g:polyglot_disabled = ['csv']
" SirVer/ultisnips
let g:UltiSnipsExpandTrigger='<c-j>'

" dense-analysis/ale
let g:ale_set_highlights = 0
let g:ale_linters = {'python': ['flake8']}
let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
let g:ale_fixers = {
  \   '*': ['remove_trailing_lines', 'trim_whitespace'],
  \   'python': ['black'],
  \ }
" let g:ale_fix_on_save = 1
nmap <silent> <C-k> <Plug>(ale_previous_wrap)
nmap <silent> <C-j> <Plug>(ale_next_wrap)

" scrooloose/nerdtree
nmap <C-e> :NERDTreeToggle<CR>
" majutsushi/tagbar
nmap <F8> :TagbarToggle<CR>
" junegunn/fzf.vim
nmap <C-p> :History<CR>

" neoclide/coc.nvim
inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"

function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"

nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  else
    call CocAction('doHover')
  endif
endfunction

:CocConfigの設定。

{
        "diagnostic.enable": false,
        "python.linting.enabled": false,
        "python.jediPath": "~/.pyenv/versions/neovim3/lib/python3.7/site-packages/"
}

実践Vim 思考のスピードで編集しよう! (アスキー書籍)

実践Vim 思考のスピードで編集しよう! (アスキー書籍)

wonderwall.hatenablog.com

wonderwall.hatenablog.com