この記事ではLanguage Server Protocol(LSP)の基礎知識とneovim/VimでLSPを利用するための設定方法を紹介します。
Language Server Protocol(LSP)とは
Language Server Protocol(LSP)は、エディタや統合開発環境などの開発支援ツールと言語サーバ(Language Server)のコミュニケーションの手順を定めた仕様です。
従来、開発支援ツールが各種のプログラミング支援機能(自動補完や定義ジャンプ)を提供するためにはプログラミング言語ごとに個別の機能を実装する必要がありました。
LSPでは、言語サーバがクライアントに対して一般的なプログラミング支援機能を提供します。その結果、開発支援ツールは単一のLSPクライアント機能を実装するだけで自動的に言語サーバを提供する全てのプログラミング言語のプログラミング支援機能を提供することが可能になります。
neovim/Vimユーザの目線から見ると、これまでプログラミング言語毎に様々なプラグインを導入していたところが、LSPクライアントのプラグインを一つ導入するだけであらゆるプログラミング言語の支援を得られるということになります。
LSPの詳細は以下の公式サイトで確認できます。
LSPの実装
以下の2つのサイトでLSPサーバ/クライアント実装の一覧を確認できます。
一つ目はMicrosoftが運営するLSP公式サイトのImplementationsページです。言語サーバ、開発ツールの一覧が見られるほか、各言語サーバの動作環境を確認できます。
二つ目のLangserver.orgは、Sourcegraphが管理するコミュニティ運営のLSP情報集約サイトです。言語サーバ、開発ツールの一覧とそれぞれのサーバ、クライアントが対応している基本的な機能がまとめられています。
各実装は必ずしもLSPが定める全ての機能を網羅しているわけではなく、一部の機能のみを提供している場合があります。Langserver.orgでは以下の機能について各実装の対応状況がまとめられています。一覧の内容からLSPで提供される機能がなんとなくイメージできます。
- Code Completion
- Hover
- Jump to def
- Workspace symbols
- Find references
- Diagnostics
- Additional capabilities
主な言語サーバの実装を以下に一覧します。
プログラミング言語 | 言語サーバ |
---|---|
Ruby | Solargraph |
Go | gopls |
Python | python-language-server |
JavaScript | typescript-language-server |
neovim/VimのLSP対応プラグイン
neovim/Vimで利用できるLSP対応プラグインで有名なものは、今の所以下の2つのようです。
この記事を書いている時点でGithubのスターの数はLanguageClient-neovimが1,893、vim-lspが801でした。
vim-lsp
この記事ではvim-lspを利用する手順を記載します。
vim-lspはmattnさんが積極的にcommitされている点を個人的には注目しています。
vim-lspのインストール
vim-plugを利用する場合、設定ファイルに以下を記述します。
Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
vim-lspに加えてasync.vimのインストールが必要です。また、自動補完を有効にするためにasyncomplete.vimとasyncomplete-lsp.vimをインストールしています。
vim-lspの設定
設定ファイルに以下を記述します。
letg:lsp_diagnostics_enabled=0" debugletg:lsp_log_verbose=1letg:lsp_log_file= expand('~/vim-lsp.log')letg:asyncomplete_log_file= expand('~/asyncomplete.log')
let g:lsp_diagnostics_enabled = 0
はLSPのLint機能を無効にするオプションです。w0rp/ale
など別のLinterプラグインを使用している場合は競合しないよう無効にします。有効にしたい場合は1を設定します。
# debug
以下はログ出力用の設定です。LSPの機能が思ったように動作しない場合にログを見て原因を確認します。動作に問題がない場合は設定をコメントアウトして無効にして問題ありません。
vim-lspでは以下のコマンドが提供されます。必要に応じてキーのマッピングを設定します。各コマンドの詳細は:help vim-lsp
で確認してください。
:LspCodeAction
:LspDocumentDiagnostics
:LspDeclaration
:LspDefinition
:LspDocumentFormat
:LspDocumentFormatSync
:LspDocumentRangeFormat
:LspDocumentSymbol
:LspHover
:LspNextError
:LspPreviousError
:LspImplementation
:LspReferences
:LspRename
:LspTypeDefinition
:LspWorkspaceSymbol
プログラミング言語ごとの設定
利用するプログラミング言語ごとに設定を行います。
Ruby
Solargraphをインストールします。Rubyのgemとして提供されます。
$ gem install solargraph
設定ファイルに以下を記述します。
if executable('solargraph')" gem install solargraphauUser lsp_setup call lsp#register_server({ \'name':'solargraph', \'cmd':{server_info->[&shell,&shellcmdflag,'solargraph stdio']}, \'initialization_options':{"diagnostics":"true"}, \'whitelist':['ruby'], \})endif
Go
goplsをインストールします。golang公式のツールとして提供されています。
$ go get golang.org/x/tools/cmd/gopls
設定ファイルに以下を記述します。
if executable('gopls')auUser lsp_setup call lsp#register_server({ \'name':'gopls', \'cmd':{server_info->['gopls','-mode','stdio']}, \'whitelist':['go'], \})endif
goplsを動かすためには編集対象のファイルがGOPATH以下にあるなど、開発の作法がgolangの基本にしたがっている必要があるようです。GOPATH外でファイルを編集した場合はgoplsが例外で落ちてしまい機能しません。
Python
python-language-serverをインストールします。pipモジュールとして提供されます。
$ pip install python-language-server
設定ファイルに以下を記述します。
if executable('pyls')auUser lsp_setup call lsp#register_server({ \'name':'pyls', \'cmd':{server_info->['pyls']}, \'whitelist':['python'], \})endif
JavaScript
typescript-language-serverをインストールします。npmパッケージとして提供されます。名前が示しているようにTypeScriptの言語サーバですが、JavaScriptにも対応しています。
$ npm install -g typescript typescript-language-server
.git
ディレクトリ、package.json
のいずれかが存在するディレクトリをプロジェクトのルートディレクトリとして指定します。
.git
をルートにする場合は設定ファイルに以下を記述します。
if executable('typescript-language-server')auUser lsp_setup call lsp#register_server({ \'name':'javascript support using 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_directory(lsp#utils#get_buffer_path(),'.git/..'))}, \'whitelist':['javascript','javascript.jsx'] \})endif
package.json
をルートにする場合は設定ファイルに以下を記述します。
if executable('typescript-language-server')auUser lsp_setup call lsp#register_server({ \'name':'javascript support using 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(),'package.json'))}, \'whitelist':['javascript','javascript.jsx'], \})endif
その他の言語
vim-lspのwikiにその他いくつかの言語の設定方法が記載されています。
終わりに
個人的にLanguage Protocol Serverは筋の良い取り組みだなあと感じているので、より一層普及して利用環境が整っていくことを望んでいます。将来的にはneovim本体でLSPをサポートしようという動きもあるようです。