DEV Community

Miguel Crespo
Miguel Crespo

Posted on • Updated on • Originally published at miguelcrespo.co

How to debug like a PRO using Neovim 🔥

I've been using Neovim to write code since a couple of years, but I must admit that every time I wanted to debug something I would switch to VS Code or Firefox debugging tools (For Node and Javascript), but recently I decided to research how to set up Neovim to do it as well, I started digging into some documentations and found it so interesting that I ended up writing this article 😅.

Few editors are as extensible and powerful as Neovim, I believe everything you can think of can be implemented on it, including debugging (like a PRO).

In this article, we will learn how to prepare our Neovim to debug any kind of language using DAP (Debug Adapter Protocol) and in the process understand a bit better this technology. We will focus mainly on Golang, but the same knowledge can be applied to any other language with DAP support.

Debugging

What is DAP

The Debug Adapter Protocol (DAP) is a standardized protocol introduced by Microsoft and used in Visual Studio Code that allows editors and IDEs (Tools) to outsource much of the logic that before had to be implemented in every editor to an intermediary called a Debug Adapter that can be reused across multiple tools, resulting in faster development times for editors' developers and better user experiences for normal developers as they can get similar top code assistance across multiple editors.

Before DAP

Before DAP, every time you developed a new editor, and you wanted to support code debugging inside of it, you had to take care of all the implementation details of features like:

  • breakpoints

  • watch expressions

  • step in, step out

  • etc

And besides this, you also had to take care of all the UI implementation, all of these of course was a lot of effort for tool's developers that some tools simple could not afford.

Before DAP

After DAP

Now with DAP editors' developers only have to focus on:

  • Integrating a DAP Client that will handle all the communication with the Debug Adapter

  • Focus on the UI development to send or show the data from the Debug Adapter

After DAP

Installing a DAP Client

Currently, Neovim doesn't have a built-in DAP Client, as it does for the Language Server Protocol (LSP). So we need to manually install nvim-dap which is a normal Neovim plugin that will act as the DAP Client.

If you're using Packer, simply add to your plugin list:

use 'mfussenegger/nvim-dap'

Installing a debugger

In order to start debugging, you first need to install a debugger for the language you're using, for Go:

| Language | Debugger |
| Golang | delve |

Installing **`delve*`* for `Golang**`

go install github.com/go-delve/delve/cmd/dlv@latest

Configuring the Debug Adapter

The next thing is to tell Neovim how to communicate with delve, you don't really need to install another plugin to do this, but there are multiple plugins that basically do most of the configuration of the adapter for us.

For Go, we can use nvim-dap-go which only require the following lines:

use 'leoluz/nvim-dap-go' -- Install the plugin with Packer
require('dap-go').setup()
Enter fullscreen mode Exit fullscreen mode

What does nvim-dap-go actually do?

You can skip this section unless you want to understand what nvim-dap-go actually does.

This plugin basically tells how Neovim should launch the debugger (in our case delve) by using the API provided by the plugin nvim-dap

  • First it checks if delve is installed in the system, otherwise it returns an error

  • Then it starts delve and delve runs as a server

  • Then it tells nvim-dap that it can connect to delve by attaching to the server that was started in the previous step

A very simplistic version of this extension's code would be:

  • Tell nvim-dap how to connect to the running delve server
dap.adapters.go = function(callback, config)
  -- Wait for delve to start
    vim.defer_fn(function()
        callback({type = "server", host = "127.0.0.1", port = "port"})
      end,
    100)
end
Enter fullscreen mode Exit fullscreen mode
  • Configure how the debuggee (our application) should be launched, in this case we're creating a configuration name Debug and telling nvim-dap to start debugging the current opened file
dap.configurations.go = {
    {
      type = "go",
      name = "Debug",
      request = "launch",
      program = "${file}",
    },
...
Enter fullscreen mode Exit fullscreen mode

nvim-dap-go complete code

Defining configuration using launch.json file

If you're familiar with how VS Code works, you might know that you can provide the above configuration using a file called launch.json that is located in your project folder, if you prefer this method, this is also possible using nvim-dap!

Simply add the following line to your configuration:

require('dap.ext.vscode').load_launchjs(nil, {})
Enter fullscreen mode Exit fullscreen mode
  • The first parameter is the path where you store the file, by default is .vscode/launch.json, but you can change it to whatever you want

Making the debugging interface amazing 💅!

Installing nvim-dap-ui

Debugging

This is a nice package that will make the debugging much nicer, it basically puts all the DAP information into Neovim buffers.

To install it:

use { "rcarriga/nvim-dap-ui", requires = {"mfussenegger/nvim-dap"} }

Open automatically when a new debug session is created

local dap, dapui =require("dap"),require("dapui")
dap.listeners.after.event_initialized["dapui_config"]=function()
  dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"]=function()
  dapui.close()
end
dap.listeners.before.event_exited["dapui_config"]=function()
  dapui.close()
end
Enter fullscreen mode Exit fullscreen mode

Make the breakpoints look nicer

In your configuration, put this to change the ugly B text for these icons

vim.fn.sign_define('DapBreakpoint',{ text ='🟥', texthl ='', linehl ='', numhl =''})
vim.fn.sign_define('DapStopped',{ text ='▶️', texthl ='', linehl ='', numhl =''})
Enter fullscreen mode Exit fullscreen mode

Set some keymaps

vim.keymap.set('n', '<F5>', require 'dap'.continue)
vim.keymap.set('n', '<F10>', require 'dap'.step_over)
vim.keymap.set('n', '<F11>', require 'dap'.step_into)
vim.keymap.set('n', '<F12>', require 'dap'.step_out)
vim.keymap.set('n', '<leader>b', require 'dap'.toggle_breakpoint)
Enter fullscreen mode Exit fullscreen mode

What do you think?

What do you think about this article? Leave your comments 💬

Top comments (0)