coc.nvimをまともに使えるようにする設定

2021-12-05

なにこれ

LSP目的でcoc.nvimを利用しているのですが、全然使いこなせていないので調べていきます。
自分が知らなかった事を記載していきます。

READMEを読んでみる

まずはREADMEを読んでみましょう。
https://github.com/neoclide/coc.nvim

coc.nvimの設定ファイル

coc-settings.jsonファイルを用意すればcoc.nvimの設定を記述できる。
すぐにWikiの案内があります。どうやらWikiにドキュメントを格納しているようです。

configファイルの説明を読むと、 JSONではあるが実際はjsonc(JSON with Comments)というコメントを記述できるJSONを取り扱う旨が記載されています。

そしてユーザ設定とワークスペース設定が行えるようです。
ファイルの格納先は以下に従います。

設定単位 エディタ path
ユーザ単位 Neovim $HOME/.config/nvim/coc-settings.json
ユーザ単位 Vim $HOME/.vim/coc-settings.json
ワークスペース単位 共通 $WORK_SPACE/.vim/coc-settings.json

それぞれ設定をVim内で確認できるコマンドも用意されています。

  • :CocConfig ユーザ単位のcoc-settings.jsonを開く
  • :CocLocalConfig ワークスペース単位のcoc-settings.jsonを開く

なるほど。試しに簡単に設定してみましょう。
プラグインマネージャはdein.vimを利用します。
Rustの設定を入れるため、coc-rust-analyzerを入れてみます。

dein.toml
[[plugins]]
repo = 'neoclide/coc.nvim'
rev = 'release'
merged = '0'
hook_add = '''
  let g:coc_global_extensions = [
    \'coc-rust-analyzer',
  \]
'''
$HOME/.config/nvim/coc-settings.json
{
  "rust-analyzer.inlayHints.typeHintsSeparator": "***"
}
$WORK_SPACE/.vim/coc-settings.json
{
  "rust-analyzer.inlayHints.typeHintsSeparator": "@@@@@"
}

typeHintsSeparatorはヒント表示時の文字の先頭についてくる文字です。
デフォルトだと、になっています。

上記の設定を適応して、ユーザ単位、特定のワークスペース、それぞれでヒントの先頭文字が変更されていることを確認しました。

キーバインドなどの設定

各種キーバインドの設定例が記述されています。
https://github.com/neoclide/coc.nvim/tree/master#example-vim-configuration

以下のような設定で、coc.nvimのグローバル設定的なものだと思われます。

" GoTo code navigation.
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)

色々設定することはわかったのですが、これらのcoc.nvimのコマンドはどこに説明や詳細が記載されているのでしょうか?
coc.txtを参照するか、:h coc-nvimでヘルプ開けますので、詳細はそこから確認しましょう。

何を行うのかわかったのでREADMEに書いてある設定を1つづつ読んで内容を書き出しまた。
使わなそうなものは読み飛ばします。

init.vim
" --------------------------------------------------
" coc.nvim
" --------------------------------------------------
set encoding=utf-8      " coc.nvimでutf-8を利用するので合わせておく
set hidden              " bufferを切り返す際に編集中のファイルを保存しなくても良くする
set cmdheight=2         " commandモードのラインを2行で表示 (default 1)
set updatetime=500      " 変更検知を500msにする (default 4000ms)
set shortmess+=c        " ins-completion-menu関連のメッセージを表示しない
set signcolumn=yes      " 行番号左のサインを表示する領域を常に表示する

" https://github.com/neoclide/coc.nvim/issues/649の回避手段として設定する
set nobackup            " バックアップファイルを作成しない
set nowritebackup       " バックアップファイルを作成しない

" TABとShift + TABで補完候補の対象選択を行えるようにする
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 <silent><expr> <c-@> coc#refresh() 

" enter押したらnvimに何かしら通知する
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

" warningやerrorを出している箇所にカーソル移動を行う
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)

" 便利機能諸々
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)

" floating windowにドキュメントを表示する
nnoremap <silent> K :call <SID>show_documentation()<CR>
function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

" カーソルが当たっているワード(シンボル)の箇所をハイライトする。(gruvboxだからかなんか薄い)
autocmd CursorHold * silent call CocActionAsync('highlight')

" ワード、シンボルのリネーム
nmap <leader>rn <Plug>(coc-rename)

" 選択したコードのフォーマット
xmap <leader>fmt  <Plug>(coc-format-selected)
nmap <leader>fmt  <Plug>(coc-format-selected)
augroup mygroup
  autocmd!
  " Setup formatexpr specified filetype(s).
  autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
  " Update signature help on jump placeholder.
  autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

" 選択範囲にcodeActionを適応する
" Example: `<leader>aap` for current paragraph
xmap <leader>a  <Plug>(coc-codeaction-selected)
nmap <leader>a  <Plug>(coc-codeaction-selected)

" codeActionを適応する
nmap <leader>ac  <Plug>(coc-codeaction)
" 該当行をautofixする
nmap <leader>qf  <Plug>(coc-fix-current)

こんな感じでした。dein.tomlのadd_hookに書くには長いので一旦init.vimに書いています。
特に良いと思った設定は以下あたりですね。


warningやerrorを出している箇所にカーソル移動を行う

" warningやerrorを出している箇所にカーソル移動を行う
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)

修正対象にすぐに移動できるので便利。


便利機能諸々

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)

定義元に飛べないと開発効率落ちますよね。
特にライブラリの関数やメソッドなどをすぐに調べられるので便利。
むしろこれが出来ないと開発に用いるエディタとしては不十分だと思っています。


floating windowにドキュメントを表示する

nnoremap <silent> K :call <SID>show_documentation()<CR>
function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

ノーマルモードでKを押せばドキュメントを表示してくれます。
サクッと定義を見たいときに便利です。

ここまででREADMEをだいたい読めました。

さらに詳しく知るには

coc.nvim本体

coc.nvimはGitHubのWikiにドキュメントを準備しています。
https://github.com/neoclide/coc.nvim/wiki

コードを読んで追うのが面倒い場合はWikiから調べると良いと思います。
デバッグ方法やFAQについてもドキュメントがありました。
ただ、ドキュメントが充実しているとは言い難いと感じます。
IDEとしてフルサポートを目指しているのでドキュメンテーションも大変だろうなとは思いますので、困った時はコード読むか。

言語毎の拡張機能

拡張機能についてもWikiでの紹介を見るのが良さそうです。
https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions#implemented-coc-extensions

各リポジトリを見ると大体はREADMEに設定オプションが記載されている印象があります。

coc-extentionを作成するツールが出ていて、これを使って拡張機能を作ることもできるみたいです。
fannheyward/create-coc-extension

また、coc.nvimの拡張機能で提供していないLanguageServerを扱えることもできるようなので、それで対応することもできるかもしれませんね。

終わりに

調べまくった気がしますが、文字に起こすとそんなに分量ないですね。
調べる → 試す → 記事を書くの順で進めていきましたが、これで大体はまともに使えるようになったと思います。