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 の両方がインストールされます。
Python 仮想環境下に Python デバッガ debugpy をインストールする
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