🐛

nvim-dap で neovim でも Python コードをデバッグしたい

2022/07/24に公開

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-dapPython 用に設定できる のですが、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 によるデバッグから卒業します。

GitHubで編集を提案

Discussion