[第11回] Neovimのすゝめ – LSPをセットアップ(coc編)

[第11回] Neovimのすゝめ – LSPをセットアップ(coc編)

連載

本記事は複数記事の連載記事の1つです。

LSP

LSPとはLanguage Server Protocolの略です。
手っ取り早く伝えると、これを入れるとプログラミング言語の構文解析・構文ハイライト・静的エラーチェック・リファレンス参照・入力補完といった機能がNeovimに搭載されます。

LSPはVSCodeの一部

といっても過言ではありません。
LSPはMicrosoftが提唱したプロトコルです。
VSCodeの構文ハイライトや補完、リファレンス参照など言語固有の機能はVSCodeエディタの中に実装されておらず、ランゲージサーバーとして外に出されています。
そのランゲージサーバーとVSCodeがLSPというプロトコルを介してやり取りすることで上記の補完や参照といった機能を実現しています。

2016年にMicrosoftによってLSPの仕様がオープンになったことで、文字通りNeovimからもVSCodeと同じ言語の支援が受けられるようになりました。

Microsoft、VS Codeの言語サーバープロトコルをオープンソース化
https://www.infoq.com/jp/news/2016/07/language-server-protocol/

NeovimでLSPを導入する場合

LSPの設定は結構大変です。
プログラミング言語ごとに異なるランゲージサーバーに対して、それぞれ設定する必要があります。
ランゲージサーバーはあくまで補完候補などの情報だけ送られるため、何もしないとNeovimの入力補完に表示されません。

各自で使用しているプラグインによって設定も変わってくるため、導入の敷居が高くなりがちですが、
ランゲージサーバーの導入・設定・エディタへの補完などの反映などを全部入りにしたcoc.nvimプラグインが最も有名で導入が簡単です。

ただし、cocは賛否両論です。
若干重いとか入力補完のプラグインの補完対象やソートルールが好みに合わないとか、cocの範疇のプラグインを別の好みのプラグインに変えようと思うと超ハードルが高くなるなど批判も多くあります。

そもそもNeovim利用者は自分の好きなようにカスタムしたい人が多いですからね。

ですが、私はcocはアリだと思います。

cocを使わずにLSPをセットアップするのはNeovimを始めて間もない人には敷居が高いです。
LSPって具体的に何が出来るの? とイメージが沸かないまま始めると、何を設定すべきかも分からず迷子になります。

また、cocは馬鹿にしたものでもありません。
cocを使わずに自分でセットアップするとcocって凄かったんだな…と思い知る場面が必ずあります。

まず使ってみて、不満が出てきた頃に卒業するのがオススメです。

その他のLSPクライアントの選択肢は、Neovim公式でサポートされたNeovim builtinやvim-lspなどのLSPクライアントがあります。
vim-lspは設定が劇的に簡単になるvim-lsp-settingsとセットで使います。

Neovim builtinの方がNeovimに特化しているのでNeovim固有の機能もサポートされており、Lua製のプラグインで統一していく場合はオススメですが設定難度はvim-lspよりも高いです。

coc.nvimをインストールする

deinのtoml形式でインストールする場合の私が使っていた設定例です。

[[plugins]]
repo = 'neoclide/coc.nvim'
rev = 'release'
merged = 0
hook_add = '''
  let g:coc_global_extensions = [
    \'@yaegassy/coc-volar',
    \'coc-css',
    \'coc-docker',
    \'coc-eslint',
    \'coc-git',
    \'coc-html',
    \'coc-json',
    \'coc-lua',
    \'coc-markdownlint',
    \'coc-phpls',
    \'coc-prettier',
    \'coc-sql',
    \'coc-sumneko-lua',
    \'coc-toml',
    \'coc-tsserver',
    \'coc-vimlsp',
    \'coc-jedi',
    \'coc-diagnostic',
  \]
  inoremap <silent><expr> <c-space> coc#refresh()
  inoremap <silent><expr> <tab> pumvisible() ? coc#_select_confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
  nmap <silent> gd <Plug>(coc-definition)
  nmap <silent> <C-w>gd <cmd>vs<cr><Plug>(coc-definition)
  nmap <silent> gy <Plug>(coc-type-definition)
  nmap <silent> gi <Plug>(coc-implementation)
  nmap <silent> gF <Plug>(coc-references)
  nmap <f2> <Plug>(coc-rename)
  nmap <silent> [g <Plug>(coc-diagnostic-prev)
  nmap <silent> ]g <Plug>(coc-diagnostic-next)
  nmap <esc><esc> <cmd>call coc#float#close_all()<cr>
  " Find symbol of current document.
  nnoremap <silent><nowait> <leader>o  :<C-u>CocList outline<cr>
  " Search workspace symbols.
  nnoremap <silent><nowait> <leader>s  :<C-u>CocList -I symbols<cr>
  nnoremap <silent> gh :call <SID>show_documentation()<CR>
  nnoremap <silent> <leader>a <cmd>CocAction<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
  " higlight when hover cursor
  autocmd CursorHold * silent call CocActionAsync('highlight')
'''

g:coc_global_extensions

cocはCocInstall [language server]のコマンドを実行するだけでランゲージサーバーのセットアップが完了します。

ランゲージサーバーの一覧は公式のLanguage serversに記載されています。

また、Vue3用のランゲージサーバーのVolarなど一覧に記載がない物でも追加可能です。

g:coc_global_extensionsにランゲージサーバーをセットするとNeovim起動時に自動的にインストールされます。

キーマップ

定義参照や問題箇所の移動、アクション実行などのキーマップを設定します。
公式におすすめサンプルが用意されています。 >> Example vim configuration

公式にないものとして、1つ便利なものを紹介します。
cocではフロートウィンドウで様々な情報を表示しますが、時々期待通りに閉じなくなることがあります。
そんな時、以下のコマンドを実行すると強制的に閉じることが出来ます。
また、<C-w><C-w>で次のウィンドウへ移るとフロートウィンドウ内へ移動でき、フロートウィンドウの内容をヤンクすることが出来ます。

 

nmap <esc><esc> <cmd>call coc#float#close_all()<cr>

 

coc-settings.json

:CocConfigコマンドを実行すると、Neovim設定ディレクトリのcoc-settings.jsonが開かれます。
存在しない場合は自動的に作られます。

このファイルに各ランゲージサーバーごとの詳細な設定を記載できます。
必ずしも必要ではなく、デフォルトの挙動を変えたり拡張する場合に使用します。

以下、私の設定値です。
coc.preferences.formatOnSaveFiletypesでファイル保存時に自動的にフォーマットするファイルタイプを設定したり、Pythonのランゲージサーバー(jedi)のフォーマッターやデバッグ設定を記載しています。

{
  "languageserver": {
  },
  "codeLens.enable": true,
  "jedi.executable.command": "~/.local/bin/jedi-language-server",
  "eslint.packageManager": "yarn",
  "intelephense.licenceKey": "**************",
  "volar.formatting.enable": false,
  "coc.preferences.formatOnSaveFiletypes": [
    "css",
    "vue",
    "typescript",
    "typescriptreact",
    "javascript",
    "python"
  ],
  "prettier.disableLanguages": [
  ],
  "suggest.minTriggerInputLength": 3,
  "diagnostic-languageserver.formatFiletypes": {
    "python": ["black", "isort", "docformatter"]
  },
  "diagnostic-languageserver.formatters": {
    "black": {
      "command": "black",
      "args": ["-q", "-"]
    },
    "isort": {
      "command": "isort",
      "args": ["-q", "-"]
    },
    "docformatter": {
      "command": "docformatter",
      "args": ["-"]
    }
  },
  "diagnostic-languageserver.filetypes": {
    "python": "pylint"
  },
  "diagnostic-languageserver.linters": {
    "pylint": {
      "sourceName": "pylint",
      "command": "pylint",
      "args": [
        "--output-format",
        "text",
        "--score",
        "no",
        "--msg-template",
        "'{line}:{column}:{category}:{msg} ({msg_id}:{symbol})'",
        "%file"
      ],
      "formatPattern": [
        "^(\\d+?):(\\d+?):([a-z]+?):(.*)$",
        {
          "line": 1,
          "column": 2,
          "security": 3,
          "message": 4
        }
      ],
      "rootPatterns": [".git", "pyproject.toml", "setup.py"],
      "securities": {
        "informational": "hint",
        "refactor": "info",
        "convention": "info",
        "warning": "warning",
        "error": "error",
        "fatal": "error"
      },
      "offsetColumn": 1,
      "formatLines": 1
    }
  }
}

設定完了

使用したいファイルタイプ(言語)のランゲージサーバーを入れてキーマップを設定するだけで、随分とNeovimがVSCodeらしくなると思います。
入力時には言語特有の関数やリファレンス、バッファ内の単語も補完されたり、間違った構文や型が一致しないコードを掛けばハイライトされて警告が表示されて賑やかになります。

Neovimのすゝめカテゴリの最新記事