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.
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.
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
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()
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 errorThen it starts
delve
anddelve
runs as a serverThen it tells
nvim-dap
that it can connect todelve
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 runningdelve
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
- Configure how the
debuggee
(our application) should be launched, in this case we're creating a configuration nameDebug
and tellingnvim-dap
to start debugging the current opened file
dap.configurations.go = {
{
type = "go",
name = "Debug",
request = "launch",
program = "${file}",
},
...
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, {})
- 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
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
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 =''})
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)
What do you think?
What do you think about this article? Leave your comments 💬
Top comments (1)
You are a rockstar! Thank you so much for the terse guide on how to get started with all this. Literally took be 20 mins to get up and running 🙂