nvim-dap で neovim でも Python コードをデバッグしたい
Python のデバッガといえば pudb が便利ですが、やっぱり IDE ライクにデバッグしたい、でも neovim から VSCode には乗り換えたくない、ということでいろいろと調査した結果、nvim-dap が良さそうでしたので導入方法を紹介します。
DAP を扱えるプラグイン
vim / neovim でデバッグするプラグインには
があり、どちらも Debug Adapter Protocol (DAP) でその機能を実現しています。エディタとデバッガの間を DAP が仲介することで、どんなエディタからでもデバッグ機能を実現できる、ということのようです。LSP の デバッガ版、といったところでしょうか。ちなみに LSP と DAP はどちらも MS 製。
今回は nvim-dap
で Python コードのデバッグができるよう設定していきます。
nvim-dap, nvim-dap-ui, nvim-dap-python のインストール
nvim-dap だけでは UI が提供されないので、nvim-dap-ui を一緒にインストールする必要があります。また、Python 用 nvim-dap 設定プラグインである nvim-dap-python もインストールしておきます。
私はパッケージマネージャとして packer.nvim を使用しているので、以下を ~/.config/nvim/lua/plugins.lua
に記述、:PackerInstall
でインストールしました。
use 'mfussenegger/nvim-dap'
use 'rcarriga/nvim-dap-ui'
use 'https://github.com/mfussenegger/nvim-dap-python'
これで nvim-dap と nvim-dap-ui の両方がインストールされます。
debugpy
をインストールする
Python 仮想環境下に Python デバッガ DAP に対応している Python デバッガとして debugpy があり、pip
でインストールできます。VSCode での Python のデバッグ機能にもこれが使われているようです。なお MS 製。こいつを仮想環境下にインストールして、nvim-dap
に認識させます。
私は pipenv を使っているので、適当なディレクトリを掘ってそこに debugpy
をインストール。
$ pipenv install --dev debugpy
また、direnv を使って自動的に仮想環境を切り替えているので .envrc
に
layout pipenv
と記述しておきます。これで ディレクトリに cd
するだけで仮想環境が activate
されます。
nvim-dap-python の設定
nvim-dap-python
を使わなくても nvim-dap
をPython 用に設定できる のですが、nvim-dap-python
を使えばクラスやメソッドのテストを nvim-dap
から走らせることができます。
nvim-dap-python
の設定はとても簡単で、debugpy
をインストールした仮想環境の Python の実行ファイルへのパスを渡すだけです。仮想環境のパスを自動で取得するため、ちょっとだけ工夫します。
local venv = os.getenv('VIRTUAL_ENV')
command = string.format('%s/bin/python', venv)
require('dap-python').setup(command)
direnv
のおかげで仮想環境下に cd
していれば自動で仮想環境が activate
され、環境変数 VIRTUAL_ENV
がセットされます。それを読み取って /bin/python
をくっつけて実行ファイルのパスとしています。
nvim-dap の設定
キーマップを設定していきます。なぜなら...
nvim-dap does not configure any mappings by default.
硬派ですね。VSCode に寄せる設定が nvim-dap
のヘルプにありましたので lua
に変更して使用します。
vim.api.nvim_set_keymap('n', '<F5>', ':DapContinue<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<F10>', ':DapStepOver<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<F11>', ':DapStepInto<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<F12>', ':DapStepOut<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<leader>b', ':DapToggleBreakpoint<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<leader>B', ':lua require("dap").set_breakpoint(nil, nil, vim.fn.input("Breakpoint condition: "))<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<leader>lp', ':lua require("dap").set_breakpoint(nil, nil, vim.fn.input("Log point message: "))<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<leader>dr', ':lua require("dap").repl.open()<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<leader>dl', ':lua require("dap").run_last()<CR>', { silent = true })
nvim-dap-ui の設定
dap-ui の起動をマッピング
VSCode に合わせて Ctrl+Shift+D
で起動させたかったのですが、無理だったので <leader>d
で妥協しています。
vim.api.nvim_set_keymap('n', '<leader>d', ':lua require("dapui").toggle()<CR>', {})
レイアウトの変更
現状使いこなせていないためデフォルトでも十分なのですが、
- アイコンの変更
- レイアウトの変更(右レイアウト、
scopes
を一番上に) - サイズの調整
をしています。
require("dapui").setup({
icons = { expanded = "", collapsed = "" },
layouts = {
{
elements = {
{ id = "watches", size = 0.20 },
{ id = "stacks", size = 0.20 },
{ id = "breakpoints", size = 0.20 },
{ id = "scopes", size = 0.40 },
},
size = 64,
position = "right",
},
{
elements = {
"repl",
"console",
},
size = 0.20,
position = "bottom",
},
},
})
実際に使用する
以上の設定を終えたら適当な python
ファイルで <leader>d
を押下すると、nvim-dap-ui
が起動してくれます。閉じるときも <leader>d
。
上記のキーマップであれば、こんな感じでデバッグできます。
キー | 説明 |
---|---|
<F5> | コンティニュー |
<F10> | ステップオーバー |
<F12> | ステップイン |
<leader>b | ブレイクポイント切り替え |
また、カーソル下の変数に対して :lua require("dapui").eval()
するとホバーウィンドウに値が表示されます。
動作イメージは以下の通り。
あとがき
さすが MS。宗旨替えして VSCode を使えば幸せになれそう...
今後はこれを機に print()
デバッグ、ipython
によるデバッグから卒業します。
Discussion