まえがき
最近vimからneovimに移行するにあたってPythonの開発環境を整えているのですが,jedi-language-serverの設定に関する情報が少なかったので,備忘録がてらまとめます.
LSPを使うのが初めてということもあり,「とりあえずは補完が出てくれればいいや,リンターやフォーマッタなどはあとからにしよう」という感じで今回は補完オンリーをターゲットにして環境を開発構築していきます.
環境
今回セットアップした環境は以下の通りです.
- OS: macOS 10.14 (Mojave)
- neovim: 0.5.0 (HomebrewのHEAD 1ca67a7)
- Python: 3.8.5 (Homebrewでインストールしたやつ)
- LSPクライアント: neovim0.5.0から標準のクライアント + nvim-lsp
- LSPサーバ: jedi-language-server
- 補完: deoplete.nvim
巷ではpython-language-serverを使用している例が多いのですが,私の環境では補完候補が出てくるのがものすごく遅かったのでjedi-language-serverを採用しています.
Luaの設定
jedi-language-serverをnvim-lspのデフォルト設定で使うのは簡単です.以下の設定を init.vim
に入れておけば使えるようにはなります.
lua <<END
local nvim_lsp = require 'nvim_lsp'
nvim_lsp.jedi_language_server.setup{}
END
が,デフォルトだとリンターの反応が早い & 張り切りすぎてうるさい.タイプ途中に構文エラーで怒られても...という感じなので,リンターをオフにします.
lua << END
local nvim_lsp = require 'nvim_lsp'
nvim_lsp.jedi_language_server.setup{
init_options = {
diagnostics = {
enable = false,
},
completion = {
disableSnippets = true,
},
jediSettings = {
autoImportModules = {'numpy', 'pandas'},
},
},
}
END
設定可能な項目は,GitHubのREADMEを参照ください.
pyls
の場合はsettings.pyls.plugins
なんかに記載するのですが,jedi-language-serverはinit_options
のようです.このへんの設定のお作法?がよく分かっていないのがつらいところ.
補完をした際にtextEditのエラーが発生する
上記の設定を行い,構文エラーで怒られないことを確認した後,意気揚々とnp.
と打ったところ,以下のエラーが発生しました.
Error executing vim.schedule lua callback: ...vim/HEAD-1ca67a7/share/nvim/runtime/lua/vim/lsp/util.lua:277: attempt to index field 'textEdit' (a userdata value)
どうやらLuaのnil
とvim.NIL
の違いが原因のエラーのようです.既にバグは報告されており,近いうちに修正されるとは思うのですが,今のところは以下の変更を加えることで使えるようになります.
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 52a6fe89f..fc619c8a8 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -274,13 +274,13 @@ end
-- textEdit.newText > insertText > label
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil then
+ if item.textEdit ~= vim.NIL and item.textEdit.newText ~= vim.NIL then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.textEdit.newText
else
return M.parse_snippet(item.textEdit.newText)
end
- elseif item.insertText ~= nil then
+ elseif item.insertText ~= vim.NIL then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.insertText
else
この修正を行うことで,ようやく補完が出てくるようになりました.それなりに早くて快適.
ちなみに,ROS2のrclpyもさくさく補完できるようになりました.これは便利.
これは本当にPyCharmがいらなくなる日がくるかもしれない.
おまけ1
最終的には使いませんでしたが,pylsを使ってリンターの出力を消したときの設定も載せておきます.
nvim_lsp.pyls.setup{
settings = {
pyls = {
plugins = {
pyflakes = {enabled = false},
pycodestyle = {enabled = false},
pydocstyle = {enabled = false},
}
}
}
}
おまけ2
jedi-language-serverでどんな設定が最終的にされているのかを確認するために使ったコードです.
参考: https://stackoverflow.com/a/27028488
function dump_impl(o, level)
if type(o) == 'table' then
local s = '{\n'
for k, v in pairs(o) do
if type(k) ~= 'number' then
k = '"' .. k .. '"'
end
local indent = string.rep(' ', level + 1)
s = s .. indent .. '['..k..'] = ' .. dump_impl(v, level + 1) .. ',\n'
end
local indent = string.rep(' ', level)
return s .. indent .. '}'
else
return tostring(o)
end
end
function dump(o)
return dump_impl(o, 0)
end
local c = nvim_lsp.jedi_language_server.make_config()
print(dump(c))