LSPクライアントをcoc.nvimにした話

以前 Pythonの補完環境をjedi-vimからvim-lspに移行した話 — kashew_nuts-blog という記事を書きましたが、現在は neoclide/coc.nvim に完全移行しています。

coc.nvimとは

いろんな記事で説明されてますが、Vim8/Neovim向けのインテリセンスエンジンでLSPのフルサポートを謳ったプラグインです。

移行理由

移行しようと思った理由は以下の3つです。「これなんてVSCode?」となったとかならなかったとか。

  • 自動補完が「ちょうどいい」感じで補完してくれる

  • 拡張が豊富で、LSPに限らずIDEとしての機能を提供してくれる

  • Language Serverの設定でvimrcが膨れない

1. 自動補完が「ちょうどいい」感じで補完してくれる

vim-lspではオムニ補完、 prabirshrestha/asyncomplete.vim も使うと自動補完が使えるようになります。個人的には自動補完はほしい補完ソースを、「期待したタイミングで」動かすのが面倒でした。 LSP導入以前にも自動補完プラグインを使うのに、「連携のための設定」を書くのに苦労していました。

vim-lspを使ってるときはオムニ補完で自分で補完のタイミングをコントロールして書けてよかったのですが、coc.nvimは「これでいいのでは?」というくらい楽に動いたので一気に虜になりました。

2. 拡張が豊富で、LSPに限らずIDEとしての機能を提供してくれる

昨今LSPで各言語ごとに開発環境をインストールするのが楽になってきましたが、インストール自体は手動で行う必要がありました。 よく知らない言語を触るときにLanguage Serverのセットアップで時間を使うのは大変です。

最近では mattn/vim-lsp-settings がでてきたので事情が変わってきましたが、 coc.nvimはcoc.nvim自体がLanguage Serverを含む拡張をインストールできます。 Gitやエクスプローラーなどの拡張もあるので他のVimプラグインを探す時間も省けます。

3. Language Serverの設定でvimrcが膨れない

LSPを活用するにはクライアント側の設定だけでなく、Language Serverの設定も書いたりします。 vimrcにその両方を書いていくとどうしてもvimrcが肥大化していきますが、coc.nvimはLanguage Serverの設定をJSONファイルに書けます。 キーマッピングやcoc.nvimで使う拡張などはvimrcに書く必要がありますが、設定を分離できるのでvimrcが肥大化しにくいです。

インストール要件

現在のインストール要件は以下となっています。

  • Vim >= 8.0.1453

  • node.js >= 8.10.0

  • npm

当初2019年に試したときはVimの場合Python3インターフェイスや neoclide/vim-node-rpc 、yarnに依存していましたが、現在は依存が消えているので使いやすくなりました。(ソースからビルドするときはyarnが必要となります。

Vimの下限のバージョンはUbuntu 18.04LTSとかならデフォルトで満たしていますが、できればpopup-windowを活用するのに、8.2以上だとよりよいでしょう。 node.jsも現在のLTSの下限である10.x系にしたほうがよいと思います。

詳細は Install coc.nvim を参照のこと。

インストール方法

vim-plugを使ってる場合、 vimrcに以下のように記述します。 その後、Vimを再起動しコマンドラインモードで :PlugInstall すればOKです。

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

Quick Start の通りですね。

使い方

coc.nvimをインストールしたあとは拡張やLanguage Serverをインストールして使うだけ。 どんな設定ができるかや、Language Serverの設定をすればいいかは以下のWikiやヘルプを参照するとよいです。

まずは自分の使うFileTypeの拡張を入れたり、設定ファイルの記述を便利にするために :CocInstall coc-json はしておくとよいでしょう。

よく使うコマンド

個人的によく使うコマンドは以下です。

  • CocInstall <extension-name>: 拡張のインストール

  • CocUninstall <extension-name>: 拡張のアンインストール

  • CocCommand: インストールした拡張機能の実行

  • CocConfig: Language Serverの設定

  • CocLocalConfig: プロジェクトごとのLanguage Serverの設定

気になるところ

良い点もあれば気になるところもあるということでかんたんに書きます。

  • 設定ファイルがJSONのため、PATHの設定を書くと異なるOS間で共通化しにくい

  • NeoVimでのみサポートとかあるので、coc及び拡張の機能が自分の「Vim」に対応しているか確認する必要がある

  • トラブルシューティングにはJavaScript/TypeScriptまわりの知識が必要になる

現状なにか詰まったりしたときに「そういえばそうだったな」と思うぐらいなので、気に留めておくぐらいでいいかもしれません。

設定ファイル例

設定ファイルは README を参考に自分に必要な記述をすればと思いますが、自分のも一例としてリンクを載せておきます。 こだわりとしてはcoc.nvimを使わないFileTypeでは、キーマッピングなどを上書きしないようにしています。

終わりに

使用感自体は快適です。vim-lspと比べるとnode.jsへの依存が増えていますが、Language Serverによっては関係なく必要なのであまり気にしていません。LSPひとつとっても、Vim界隈はこういった切磋琢磨が目に見えるので使っていても楽しいですね。

Pythonの補完環境をjedi-vimからvim-lspに移行した話

VimでPython書いてますか?

長らくPythonの補完用プラグインとして davidhalter/jedi-vim を使用してきましたが、 あの mattn (@mattn_jp) さんがCollaboratorになった prabirshrestha/vim-lsppalantir/python-language-server がいよいよ実用的になってきたので移行してみました。

とはいえ実際に使うにはまだ設定するところもちょこちょこあるので、一度まとめておこうと思います。

前提とする環境

  • Vim: 8.0以上、かつコンパイルオプションで jobs, timer, channel, lambda が有効になっている

  • Python: 2.7もしくは3.4以降 (pyenvやAnacondaでインストールしたPythonは考慮しない)

Note

VimでPython/Python3インターフェースを有効にする必要があるかもしれませんが、少なくともドキュメントには記載されていませんでした。もし必要な場合は以前書いたVimとPythonの補完についてのメモを参照してください。

その後Python/Python3インターフェースを無効にした状態で試したところ、問題なく動作しました。外部インターフェイスに依存せず補完や定義ジャンプができるのはいいですね。

LSP(Language Server Protocol)とは

ざっくりいうと自動補完や定義ジャンプ、ドキュメントのホバー表示などエディタごとに実装していた機能を、言語Serverを用意して複数の開発ツールで使えるようにしましょうというものです。LSPを活用することでエディタのプラグインなどは最小限の労力で言語をサポートできるようになります。

各エディタ用にLSPを使うプラグインや機能を用意する必要がありますが、補完機能をサポートするサーバーが共通化できるのでそのServerが便利になると1つのエディタだけでなく複数のエディタで活用できるメリットがあります。

どのようなプロトコルでどのように通信しているのかは、 Microsoftが公開しているドキュメント が詳しいのでそちらを参照してください。 Software Design 2018年3月号 にも詳しく書いています。(残念ながら紙の書籍は2019/01/26現在在庫がないようです)

LSP導入

Python用のLanguage ServerとClientとなるVimプラグインを導入します。ServerとClientは以下になります。

Language Serverの導入

python-language-server をpipコマンドを使ってインストールします。 どこからでも使えるようにグローバルな環境に入れますが、macOSやLinuxを使っている人は注意する必要があります。 OSが使うPythonに様々なライブラリが入っており、無理やりインストールしようとすると後で困る可能性があるからです。

ここではユーザーディレクトリにインストールすることでその問題を回避します。 以下のPATHにインストールされるので、予め ~/.bash_profile などに PATH の設定を追加しておいてください。 インストールされる箇所は site.USER_BASE によって決まります。

  • Windows: %APPDATA%\Python

  • macOSで www.python.org からPythonをインストールした場合: ~/Library/Python/X.Y/bin (X.YはPythonのバージョン)

  • macOSでHomebrewを使いPythonをインストールした場合: ~/.local/bin

  • Ubuntuの場合: ~/.local/bin

ターミナルを開き以下のコマンドを使いインストールします。

$ pip install --user python-language-server

インストールが完了したら $ which pyls などでLanguage ServerにPATHが通っていることを確認してください。 インストールに失敗したり、whichコマンドで何も出力されない場合、 PATH の設定がうまく言っていない可能性があるので確認してみてください。

pipコマンドを実行した結果でもわかりますが、 python-language-server はこれまでもPythonの自動補完や静的解析で使われていた davidhalter/jedi をLSPで使えるようにしたものです。フォースの魂は脈々と受け継がれるようですね。

Clientの導入

vim-lspは依存で prabirshrestha/async.vim が必要なので合わせてインストールします。 私は junegunn/vim-plug を使用しているので、以下のようにvimrcに記述します。

" vimrc
Plug 'prabirshrestha/vim-lsp'
Plug 'prabirshrestha/async.vim'

Vimを開いたまま :so $MYVIMRC のようにして設定の変更を読み込み、 :PlugIntall するとプラグインをインストールできます。

Vimプラグインの設定

ServerとClientとなるVimプラグインは導入しましたが、実際に連携するには設定が必要です。以下のようにvimrcに追記します。

" vimrc
" デバッグ用設定
let g:lsp_log_verbose = 1  " デバッグ用ログを出力
let g:lsp_log_file = expand('~/.cache/tmp/vim-lsp.log')  " ログ出力のPATHを設定

" 言語用Serverの設定
augroup MyLsp
  autocmd!
  " pip install python-language-server
  if executable('pyls')
    " Python用の設定を記載
    " workspace_configで以下の設定を記載
    " - pycodestyleの設定はALEと重複するので無効にする
    " - jediの定義ジャンプで一部無効になっている設定を有効化
    autocmd User lsp_setup call lsp#register_server({
        \ 'name': 'pyls',
        \ 'cmd': { server_info -> ['pyls'] },
        \ 'whitelist': ['python'],
        \ 'workspace_config': {'pyls': {'plugins': {
        \   'pycodestyle': {'enabled': v:false},
        \   'jedi_definition': {'follow_imports': v:true, 'follow_builtin_imports': v:true},}}}
        \})
    autocmd FileType python call s:configure_lsp()
  endif
augroup END
" 言語ごとにServerが実行されたらする設定を関数化
function! s:configure_lsp() abort
  setlocal omnifunc=lsp#complete   " オムニ補完を有効化
  " LSP用にマッピング
  nnoremap <buffer> <C-]> :<C-u>LspDefinition<CR>
  nnoremap <buffer> gd :<C-u>LspDefinition<CR>
  nnoremap <buffer> gD :<C-u>LspReferences<CR>
  nnoremap <buffer> gs :<C-u>LspDocumentSymbol<CR>
  nnoremap <buffer> gS :<C-u>LspWorkspaceSymbol<CR>
  nnoremap <buffer> gQ :<C-u>LspDocumentFormat<CR>
  vnoremap <buffer> gQ :LspDocumentRangeFormat<CR>
  nnoremap <buffer> K :<C-u>LspHover<CR>
  nnoremap <buffer> <F1> :<C-u>LspImplementation<CR>
  nnoremap <buffer> <F2> :<C-u>LspRename<CR>
endfunction
let g:lsp_diagnostics_enabled = 0  " 警告やエラーの表示はALEに任せるのでOFFにする

vim-lspは警告やエラーの表示などもできますが、そちらは w0rp/ale に寄せているため機能を無効化しています。 ALEについては 2018年版Life ChangingだったVimプラグインTOP3 に書きましたのでそちらを参照してください。

vimrcに記載したら保存、終了します。

使用感について

jedi-vimと比べたときのメリット/デメリットを書いていきます。

メリット

高速化されたのが一番大きいですね。

  • Vimの起動が高速化できた(Ubuntu環境で100〜150ms、macOS環境でも200〜300ms短縮)

  • 補完候補がでる時間が高速化できた

  • 発展途上の技術なので進化を体験していける

  • 他の言語と使用できるプラグインを共通化できるため、vimrcからプラグインを減らせた

デメリット

まだ発展途上というところからきてると思うのですが、少しだけ今までできていたことができない感じがあります。

  • 言語用のServerが別途インストールが必要なので環境を壊さわないようにするのに気を使う。

    • jedi-vimはjediを git submodule で独立して入れていたので楽だった。

  • Vimを起動後に :setf python しても補完や定義ジャンプが動かない。

    • 既存のPythonファイルを開いたり、 $ vim hoge.py などすると動くが、少し気になる。

  • リネームが不安定(動かない?)

    • Cannot LspRename / Issue #218

    • 動かすには python-rope/rope が必要という情報もあるがそれでも動かない

    • jedi-vimはじゃじゃ馬だったり、マルチバイト文字が対象行に混じっていると動かなかったりしたが….

終わりに

自動補完(オムニ補完)や定義ジャンプの使い方は変わらないですし、仮想環境(venv/virtualenv)にも対応しています。 リネームに関してはropeよりjediに統一したほうがよいのでは?と思ったりしますが、現状リネームに関してはさほど気にしていない感じです。 今後LSPが進化していって、安定して高速な開発環境ができていくといいなぁと期待しています。

2018年の振り返りと2019年抱負

2018年の総括

だいたいこんな感じでしたが、せっかくなのでちょっと細かく見ていきたいと思います。

健康面

  • 怪我はなかった。クライミングが数週間や数ヶ月できなくなるレベルでは。

  • しかし歯医者は1年で34回いったので月に3回近く通っていました。

開発面

引き続きPython x Djangoの組み合わせで開発をしています。去年はDjango REST frameworkを使い始めました。 一昨年に比べて去年は以下のようなことを考えることが多かったように思います。

  • どの技術やアーキテクチャを採用するのか

  • なぜその技術・やり方を選択するのか

  • 一度選択したものをどのように良くしていくのか

  • チームメンバーにどのように仕事をしてもらうと全体がよくなるのか

  • 上記をどうやって伝えていくか・継続していくか

そもそもの理解が足りていないためきっちり応えられなかったり、 全体を見る機会をもっと早めに作れたのにそれを選択しなかったがために後でしっぺ返しを食らったりしていた気がします。

普通に開発してると足を止める暇がなくなるので、「足を止めて振り返ったり、現状がどうで今後どうしていくことを考える機会をつくる」のがいいのだろうなと思いつつ、それをはじめて継続するにはどうしたらいいのかなと思う今日この頃です。

書籍執筆・レビュー

初の執筆を経験した年でした。

クライミング

さらなるレベルアップには日々の行動を変えないとなーという感じですね。

  • アメリカのヨセミテ国立公園でクライミング漬けの1週間を過ごしました。なんという幸せ。次は2週間くらい行きたい。

  • 国内でも瑞牆でキャンプデビューしたり、小川山、御岳にいったりしました。

  • 成果は初段 1本、1級3本、2級 2本, V5 2本, V4 1本

  • ジムでのクライミングも週1〜2回ぐらいのペースで通っていた気がします。

  • 3年連続初段は1本らしいので、もうちょっと落とせるようになりたいですね。

発表とか

Blog

毎月1本….というわけではないですが、計15本の記事を書きました。

購入したものとか

買ってよかったなーと思うのを厳選するなら以下の5つ。

  • Inspiron 13インチ 7000シリーズのフルスペック版: 現在はUbuntuデスクトップで運用

https://i.dell.com/das/dih.ashx/527x340/sites/imagecontent/products/publishingimages/inspiron-7000-series/laptops/15/inspiron-laptop-13-7000-polaris_hero.jpg
  • Apple iPhone XR 128GB: 色はコーラルを選択。6sからの乗り換え。バッテリー持ちの良さは安心

https://www.apple.com/v/iphone/home/y/images/overview/hero-iphone-xr_large.jpg
  • Amazon Kindle Paperwhite: 読書が捗るけど、PDFはA4が多いから向かないですね….

  • Anker PowerCore Fusion 5000: プラグ搭載型なので移動時の持ち物が減って便利

  • Cable Matters USB C to DisplayPort 変換アダプター: Ubuntuデスクトップでも4K対応

2019年の抱負

優先度が高いのは絞らないと10、20と並んでいくので3つ書きます。

開発面

変わらずサーバーサイドの開発をしていくと思いますが、どうやったらチーム全体が(結果自分が)「楽に」目的に集中していけるかを追求できたらいいかなと思ったりしています。ふわっとしてますが。

クライミング

外岩で初段5本完登ですかね。12月にネタを仕込めたのでそのための「練習」をしていきます。 具体的には良いフォームで登ることと、それを高いグレードでも可能にするための身体づくりですね。

健康面

大きな病気や怪我がないように。歯医者も虫歯での通院をなくす。結果他に回せる時間が増えると思うので。

終わりに

まずはじめて、それを習慣化していくしかないかなーと思うので2019年はそれを増やしていきたいですね。