Table of Contents
- Disclaimer/Contributions
- Ansible
- Python
- Brightscript
- C/C++/Rust (via gdb)
- C/C++/Rust (via vscode-cpptools)
- C/C++/Rust (via codelldb)
- C/C++/Rust (via lldb-vscode)
- Adapter
- Configurations
- Additional Config
- Rust types
- Environment variables
- LLDB commands
- Building lldb-vscode
- Installation with Homebrew
- Probe-rs
- Mockdebug
- Go
- Go (using delve directly)
- Ruby
- Dart
- Haskell (hdb)
- Haskell
- Javascript
- Javascript-Deno
- Javascript Chrome
- Javascript Firefox
- PHP
- Perl
- Scala
- Kotlin
- Emmylua
- local-lua-debugger-vscode
- Neovim Lua
- OCaml earlybird
- Dotnet
- Unity
- Elixir
- Godot GDScript
- Bash
Contents
- .NET (csharp, fsharp)
- Ansible
- Bash via bashdb
- Brightscript
- C/C++/Rust (via gdb native)
- C/C++/Rust (via codelldb)
- C/C++/Rust (via lldb-vscode)
- C/C++/Rust (via vscode-cpptools)
- Dart
- Elixir
- Go (using delve directly)
- Go
- Godot gdscript
- Haskell (hdb)
- Haskell
- Java
- Javascript/Node
- Javascript/Chrome
- Javascript/Deno
- Javascript/Firefox
- Kotlin
- Mockdebug
- Emmylua
- local-lua-debugger-vscode
- Neovim Lua
- OCaml earlybird
- PHP
- Perl
- PowerShell
- probe-rs
- Python
- Ruby
- Scala
- Unity
Some examples below are in lua, if you want to set up nvim-dap in a .vim file, you have to wrap the code blocks like this:
lua <<EOF
-- lua code goes here
EOF
See :help lua-commands
Note that the dap.configuration examples are just that - examples. In many
cases it is advisable to create project specific configurations in a
.vscode/launch.json file. See :help dap-launch.json for more information.
Disclaimer/Contributions
Please note that this page is community maintained.
When adding/editing debug adapters, please prefer referring to upstream installation instructions instead of duplicating the information here, and don't assume nvim specific tooling like mason.
Ansible
- Install ansibug
python -m pip install ansibug(If installed in a virtualenv,ansiblemust be available in the same virtualenv)
- Configure an adapter:
local dap = require("dap")
dap.adapters.ansible = {
type = "executable",
command = "python", -- or "/path/to/virtualenv/bin/python",
args = { "-m", "ansibug", "dap" },
}
Add a configuration like:
local ansibug_configurations = {
{
type = "ansible",
request = "launch",
name = "Debug playbook",
playbook = "${file}"
},
}
dap.configurations["yaml.ansible"] = ansibug_configurations
-- You may need to replace "yaml.ansible" with the filetype you use for ansible playbooks
-- "yaml.ansible" depends on a `ftdetect/ansible.vim` plugin with:
-- au BufRead,BufNewFile */playbooks/*.yml setlocal ft=yaml.ansible
-- au BufRead,BufNewFile */playbooks/*.yaml setlocal ft=yaml.ansible
See Debug Configurations under ansibug for all available options.
Python
Install debugpy into a virtualenv
mkdir .virtualenvs
cd .virtualenvs
python -m venv debugpy
debugpy/bin/python -m pip install debugpy
You can then either use nvim-dap-python - it comes with adapter and configurations definitions - or define them manually as follows:
local dap = require('dap')
dap.adapters.python = function(cb, config)
if config.request == 'attach' then
---@diagnostic disable-next-line: undefined-field
local port = (config.connect or config).port
---@diagnostic disable-next-line: undefined-field
local host = (config.connect or config).host or '127.0.0.1'
cb({
type = 'server',
port = assert(port, '`connect.port` is required for a python `attach` configuration'),
host = host,
options = {
source_filetype = 'python',
},
})
else
cb({
type = 'executable',
command = 'path/to/virtualenvs/debugpy/bin/python',
args = { '-m', 'debugpy.adapter' },
options = {
source_filetype = 'python',
},
})
end
end
local dap = require('dap')
dap.configurations.python = {
{
-- The first three options are required by nvim-dap
type = 'python'; -- the type here established the link to the adapter definition: `dap.adapters.python`
request = 'launch';
name = "Launch file";
-- Options below are for debugpy, see https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings for supported options
program = "${file}"; -- This configuration will launch the current file if used.
pythonPath = function()
-- debugpy supports launching an application with a different interpreter then the one used to launch debugpy itself.
-- The code below looks for a `venv` or `.venv` folder in the current directly and uses the python within.
-- You could adapt this - to for example use the `VIRTUAL_ENV` environment variable.
local cwd = vim.fn.getcwd()
if vim.fn.executable(cwd .. '/venv/bin/python') == 1 then
return cwd .. '/venv/bin/python'
elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then
return cwd .. '/.venv/bin/python'
else
return '/usr/bin/python'
end
end;
},
}
You can refer to the virtualenv environment variable with
command = os.getenv("VIRTUAL_ENV") .. "/bin/python"
Brightscript
Setup steps
- Install the roku-debug node package. See the bot comment here for an example of how to install.
- Install node and npx
- Set the
ROKU_DEV_TARGETandDEVPASSWORDenvironment variables to the IP address and password of your Roku device
Example adapter
local dap = require('dap')
dap.adapters.brightscript = {
type = 'executable',
command = 'npx',
args = { 'roku-debug', '--dap' },
}
Example configuration
dap.configurations.brs = {
{
type = 'brightscript',
request = 'launch',
name = "Debug app",
rootDir = "${workspaceFolder}",
files = {
"manifest",
"source/**/*.*",
"components/**/*.*",
"images/**/*.*",
"locale/**/*.*"
},
host = "${env:ROKU_DEV_TARGET}",
remotePort = 8060,
password = "${env:DEVPASSWORD}",
outDir = "${workspaceFolder}/out/",
enableDebugProtocol = true,
fileLogging = false,
enableVariablesPanel = true,
logLevel = "off"
},
}
C/C++/Rust (via gdb)
Requires gdb 14.0+
Adapter
local dap = require("dap")
dap.adapters.gdb = {
type = "executable",
command = "gdb",
args = { "--interpreter=dap", "--eval-command", "set print pretty on" }
}
See the GDB Debug adapter configuration docs for the configuration options.
Configuration example
local dap = require("dap")
dap.configurations.c = {
{
name = "Launch",
type = "gdb",
request = "launch",
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
args = {}, -- provide arguments if needed
cwd = "${workspaceFolder}",
stopAtBeginningOfMainSubprogram = false,
},
{
name = "Select and attach to process",
type = "gdb",
request = "attach",
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
pid = function()
local name = vim.fn.input('Executable name (filter): ')
return require("dap.utils").pick_process({ filter = name })
end,
cwd = '${workspaceFolder}'
},
{
name = 'Attach to gdbserver :1234',
type = 'gdb',
request = 'attach',
target = 'localhost:1234',
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}'
}
}
You can then reuse the same configuration for C++ and Rust as follows:
dap.configurations.cpp = dap.configurations.c
dap.configurations.rust = dap.configurations.c
More Rust specific set up
While above works for simple Rust debugging set up, Rust project provides some gdb extensions and helpers that add Rust types' pretty printing. They can be loaded using rust-gdb wrapper script instead of using gdb directly. To use it, you can set up your adapter like this:
local dap = require("dap")
dap.adapters["rust-gdb"] = {
type = "executable",
command = "rust-gdb",
args = { "--interpreter=dap", "--eval-command", "set print pretty on" }
}
And then add configuration for Rust using that adapter:
local dap = require("dap")
dap.configurations.rust = {
{
name = "Launch",
type = "rust-gdb",
request = "launch",
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
args = {}, -- provide arguments if needed
cwd = "${workspaceFolder}",
stopAtBeginningOfMainSubprogram = false,
},
{
name = "Select and attach to process",
type = "rust-gdb",
request = "attach",
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
pid = function()
local name = vim.fn.input('Executable name (filter): ')
return require("dap.utils").pick_process({ filter = name })
end,
cwd = "${workspaceFolder}"
},
{
name = "Attach to gdbserver :1234",
type = "rust-gdb",
request = "attach",
target = "localhost:1234",
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}'
}
}
Notes
- To attach, you might need privileges. For Linux, see for example how to use Yama.
- To pass some setup to gdb, use
--eval-commandor--init-eval-commandinargslist of the adapter, as in the example above (can be used multiple times). - To configure program arguments more flexibly, see example in the cookbook.
C/C++/Rust (via vscode-cpptools)
NOTE: recomended for dap ui users
Moved to C/C++/Rust (cpptools)
C/C++/Rust (via codelldb)
C/C++/Rust (via lldb-vscode)
Note
lldb-vscodehas been renamed tolldb-dap. You may need to replacelldb-vscodewithlldb-dapbelow.
Modern LLDB installations come with a binary called lldb-vscode (or lldb-vscode-11).
For the following to work, make sure the binaries lldb-vscode depends on (llvm-symbolizer) are in your PATH.
Adapter
local dap = require('dap')
dap.adapters.lldb = {
type = 'executable',
command = '/usr/bin/lldb-vscode', -- adjust as needed, must be absolute path
name = 'lldb'
}
Configurations
local dap = require('dap')
dap.configurations.cpp = {
{
name = 'Launch',
type = 'lldb',
request = 'launch',
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
-- 💀
-- if you change `runInTerminal` to true, you might need to change the yama/ptrace_scope setting:
--
-- echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
--
-- Otherwise you might get the following error:
--
-- Error on launch: Failed to attach to the target process
--
-- But you should be aware of the implications:
-- https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html
-- runInTerminal = false,
},
}
Additional Config
You can find more configurations options here:
- https://github.com/llvm/llvm-project/tree/main/lldb/tools/lldb-dap#configuration-settings-reference
- https://github.com/llvm/llvm-project/blob/main/lldb/tools/lldb-dap/package.json
If you want to use this for Rust and C, add something like this:
dap.configurations.c = dap.configurations.cpp
dap.configurations.rust = dap.configurations.cpp
Rust types
You can get rust types by adding:
dap.configurations.rust = {
{
-- ... the previous config goes here ...,
initCommands = function()
-- Find out where to look for the pretty printer Python module.
local rustc_sysroot = vim.fn.trim(vim.fn.system 'rustc --print sysroot')
assert(
vim.v.shell_error == 0,
'failed to get rust sysroot using `rustc --print sysroot`: '
.. rustc_sysroot
)
local script_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py'
local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'
-- The following is a table/list of lldb commands, which have a syntax
-- similar to shell commands.
--
-- To see which command options are supported, you can run these commands
-- in a shell:
--
-- * lldb --batch -o 'help command script import'
-- * lldb --batch -o 'help command source'
--
-- Commands prefixed with `?` are quiet on success (nothing is written to
-- debugger console if the command succeeds).
--
-- Prefixing a command with `!` enables error checking (if a command
-- prefixed with `!` fails, subsequent commands will not be run).
--
-- NOTE: it is possible to put these commands inside the ~/.lldbinit
-- config file instead, which would enable rust types globally for ALL
-- lldb sessions (i.e. including those run outside of nvim). However,
-- that may lead to conflicts when debugging other languages, as the type
-- formatters are merely regex-matched against type names. Also note that
-- .lldbinit doesn't support the `!` and `?` prefix shorthands.
return {
([[!command script import '%s']]):format(script_file),
([[command source '%s']]):format(commands_file),
}
end,
-- ...,
},
}
If you want to be able to attach to running processes, add another configuration entry like described here:
Environment variables
lldb-vscode by default doesn't inherit the environment variables from the parent. If you want to inherit them, add the env property definition below to your configurations entries.
env = function()
local variables = {}
for k, v in pairs(vim.fn.environ()) do
table.insert(variables, string.format("%s=%s", k, v))
end
return variables
end,
LLDB commands
You can execute LLDB debugger commands such as bt, parray or register read rax on the dap> command line by prefixing them with ` (for example `bt).
Building lldb-vscode
Adapted from build instructions for clangd
For a minimal setup on building lldb-vscode:
-
Clone the LLVM repo to
$LLVM_ROOT. -
Create a build directory, for example at
$LLVM_ROOT/build. -
Inside the build directory run:
cmake $LLVM_ROOT/llvm/ -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="lldb" -G Ninja.- We suggest building in
Releasemode as building DEBUG binaries requires considerably more resources. You can check Building LLVM with CMake documentation for more details about cmake flags.
- We suggest building in
-
Afterwards you can build lldb-vscode with
cmake --build $LLVM_ROOT/build --target lldb-vscode. -
same for lldb-server
cmake --build $LLVM_ROOT/build --target lldb-server.
Installation with Homebrew
An easy way to install lldb-vscode is to use brew
$ brew install llvm
Then executable file lldb-vscode and lldb-server are under this path $(brew --prefix llvm)/bin.
Probe-rs
Probe-rs provides debugging for embedded rust (and other languages). It provides a feature to write data via RTT to the debug probe and provide this information during debugging. See probe-rs for instructions. I tested the following configuration within astronvim which configures the automatic import of .vscode/launch.json. Since the configuration can be expected to vary strongly from project to project the config relies on launch.json.
local dap = require "dap"
if not dap.adapters then dap.adapters = {} end
dap.adapters["probe-rs-debug"] = {
type = "server",
port = "${port}",
executable = {
command = vim.fn.expand "$HOME/.cargo/bin/probe-rs",
args = { "dap-server", "--port", "${port}" },
},
}
-- Connect the probe-rs-debug with rust files. Configuration of the debugger is done via project_folder/.vscode/launch.json
require("dap.ext.vscode").type_to_filetypes["probe-rs-debug"] = { "rust" }
-- Set up of handlers for RTT and probe-rs messages.
-- In addition to nvim-dap-ui I write messages to a probe-rs.log in project folder
-- If RTT is enabled, probe-rs sends an event after init of a channel. This has to be confirmed or otherwise probe-rs wont sent the rtt data.
dap.listeners.before["event_probe-rs-rtt-channel-config"]["plugins.nvim-dap-probe-rs"] = function(session, body)
local utils = require "dap.utils"
utils.notify(
string.format('probe-rs: Opening RTT channel %d with name "%s"!', body.channelNumber, body.channelName)
)
local file = io.open("probe-rs.log", "a")
if file then
file:write(
string.format(
'%s: Opening RTT channel %d with name "%s"!\n',
os.date "%Y-%m-%d-T%H:%M:%S",
body.channelNumber,
body.channelName
)
)
end
if file then file:close() end
session:request("rttWindowOpened", { body.channelNumber, true })
end
-- After confirming RTT window is open, we will get rtt-data-events.
-- I print them to the dap-repl, which is one way and not separated.
-- If you have better ideas, let me know.
dap.listeners.before["event_probe-rs-rtt-data"]["plugins.nvim-dap-probe-rs"] = function(_, body)
local message =
string.format("%s: RTT-Channel %d - Message: %s", os.date "%Y-%m-%d-T%H:%M:%S", body.channelNumber, body.data)
local repl = require "dap.repl"
repl.append(message)
local file = io.open("probe-rs.log", "a")
if file then file:write(message) end
if file then file:close() end
end
-- Probe-rs can send messages, which are handled with this listener.
dap.listeners.before["event_probe-rs-show-message"]["plugins.nvim-dap-probe-rs"] = function(_, body)
local message = string.format("%s: probe-rs message: %s", os.date "%Y-%m-%d-T%H:%M:%S", body.message)
local repl = require "dap.repl"
repl.append(message)
local file = io.open("probe-rs.log", "a")
if file then file:write(message) end
if file then file:close() end
end
Mockdebug
Vscode offers a mock implementation for a debug adapter for testing. It can "debug" READMEs.
Clone the repo and run npm:
git clone https://github.com/Microsoft/vscode-mock-debug.git
cd vscode-mock-debug
npm install
Add the adapter and configuration:
local dap = require "dap"
dap.adapters.markdown = {
type = "executable",
name = "mockdebug",
command = "node",
args = {"./out/debugAdapter.js"},
cwd = "path/to/vscode-mock-debug/"
}
dap.configurations.markdown = {
{
type = "mock",
request = "launch",
name = "mock test",
program = "/path/to/a/readme.md",
stopOnEntry = true,
debugServer = 4711
}
}
Go
-
Install delve
go install github.com/go-delve/delve/cmd/dlv@latest- or via package manager (
pacman -S delve)
-
Install vscode-go
git clone https://github.com/golang/vscode-gocd vscode-go/extensionnpm installnpm run compile
-
Add the adapter and configuration:
dap.adapters.go = {
type = 'executable';
command = 'node';
args = {os.getenv('HOME') .. '/dev/golang/vscode-go/extension/dist/debugAdapter.js'};
}
dap.configurations.go = {
{
type = 'go';
name = 'Debug';
request = 'launch';
showLog = false;
program = "${file}";
dlvToolPath = vim.fn.exepath('dlv') -- Adjust to where delve is installed
},
}
Go (using delve directly)
Newer version of delve experimentally implement the DAP directly so that it can be used without installing vscode-go. More info
Install delve:
go install github.com/go-delve/delve/cmd/dlv@latest- or via package manager. For example
pacman -S delve
Once delve is installed you can use nvim-dap-go (a nvim-dap extension) to automatically configure delve running in dap mode. The extension also allows debugging individual Go tests.
If you prefer to provide your own configuration, you will need to setup the dap.adapters.go and the dap.configurations.go like:
dap.adapters.delve = function(callback, config)
if config.mode == 'remote' and config.request == 'attach' then
callback({
type = 'server',
host = config.host or '127.0.0.1',
port = config.port or '38697'
})
else
callback({
type = 'server',
port = '${port}',
executable = {
command = 'dlv',
args = { 'dap', '-l', '127.0.0.1:${port}', '--log', '--log-output=dap' },
detached = vim.fn.has("win32") == 0,
}
})
end
end
-- https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_dap.md
dap.configurations.go = {
{
type = "delve",
name = "Debug",
request = "launch",
program = "${file}"
},
{
type = "delve",
name = "Debug test", -- configuration for debugging test files
request = "launch",
mode = "test",
program = "${file}"
},
-- works with go.mod packages and sub packages
{
type = "delve",
name = "Debug test (go.mod)",
request = "launch",
mode = "test",
program = "./${relativeFileDirname}"
}
}
If you prefer to start delve manually, you can use the following adapter definition instead:
dap.adapters.delve = {
type = "server",
host = "127.0.0.1",
port = 38697,
}
And start delve like this:
dlv dap -l 127.0.0.1:38697 --log --log-output="dap"
Ruby
Via nvim-dap-ruby or manually:
with Ruby Debug
Add debug to your Gemfile
dap.adapters.ruby = function(callback, config)
callback {
type = "server",
host = "127.0.0.1",
port = "${port}",
executable = {
command = "bundle",
args = { "exec", "rdbg", "-n", "--open", "--port", "${port}",
"-c", "--", "bundle", "exec", config.command, config.script,
},
},
}
end
dap.configurations.ruby = {
{
type = "ruby",
name = "debug current file",
request = "attach",
localfs = true,
command = "ruby",
script = "${file}",
},
{
type = "ruby",
name = "run current spec file",
request = "attach",
localfs = true,
command = "rspec",
script = "${file}",
},
}
with redapt
- Install readapt
- Add the adapter and configuration:
local dap = require('dap')
dap.adapters.ruby = {
type = 'executable';
command = 'bundle';
args = {'exec', 'readapt', 'stdio'};
}
dap.configurations.ruby = {
{
type = 'ruby';
request = 'launch';
name = 'Rails';
program = 'bundle';
programArgs = {'exec', 'rails', 's'};
useBundler = true;
},
}
Dart
Note that your SDK path might be different. fvm users will also need to provide the correct path.
dap.configurations.dart = {
{
type = "dart",
request = "launch",
name = "Launch dart",
dartSdkPath = "/opt/flutter/bin/cache/dart-sdk/bin/dart", -- ensure this is correct
flutterSdkPath = "/opt/flutter/bin/flutter", -- ensure this is correct
program = "${workspaceFolder}/lib/main.dart", -- ensure this is correct
cwd = "${workspaceFolder}",
},
{
type = "flutter",
request = "launch",
name = "Launch flutter",
dartSdkPath = "/opt/flutter/bin/cache/dart-sdk/bin/dart", -- ensure this is correct
flutterSdkPath = "/opt/flutter/bin/flutter", -- ensure this is correct
program = "${workspaceFolder}/lib/main.dart", -- ensure this is correct
cwd = "${workspaceFolder}",
}
}
For the debug adapter, you have two options:
- Install the mason package
dart-debug-adapter - Use the Dart CLI's built-in adapter:
dart debug_adapter(recommended)
-- Dart CLI adapter (recommended)
dap.adapters.dart = {
type = 'executable',
command = 'dart', -- if you're using fvm, you'll need to provide the full path to dart (dart.exe for windows users), or you could prepend the fvm command
args = { 'debug_adapter' },
-- windows users will need to set 'detached' to false
options = {
detached = false,
}
}
dap.adapters.flutter = {
type = 'executable',
command = 'flutter', -- if you're using fvm, you'll need to provide the full path to flutter (flutter.bat for windows users), or you could prepend the fvm command
args = { 'debug_adapter' },
-- windows users will need to set 'detached' to false
options = {
detached = false,
}
}
-- Node based adapter (Legacy)
dap.adapters.dart = {
type = 'executable',
command = vim.fn.stdpath('data')..'/mason/bin/dart-debug-adapter', -- dart-debug-adapter.cmd for windows users
args = { 'dart' }
}
dap.adapters.flutter = {
type = 'executable',
command = vim.fn.stdpath('data')..'/mason/bin/dart-debug-adapter', -- dart-debug-adapter.cmd for windows users
args = { 'flutter' }
}
If nothing hapens when you run :DapContinue, make sure:
- You've
:cdthe root dir of the project (Normally your program code is in/lib. Run DAP a directory above from there, that's the root). - If still fails, check
:DapShowLog. - If you are using flutter, make sure the project is valid by running
flutter create .on a clean environment.
Haskell (hdb)
- Install Haskell-Debugger
Haskell-Debugger requires at least GHC 9.14. To install it via ghcup:
ghcup install ghc 9.14.1
ghcup set ghc 9.14.1
Note this links ghc in $PATH to ghc-9.14.1 - that's required for hdb to work, but you might run into issues with your projects if they're not yet compatible with GHC 9.14.
Then install hdb::
cabal install haskell-debugger \
--allow-newer=base,time,containers,ghc,ghc-bignum,template-haskell \
--enable-executable-dynamic
And add the adapter configuration:
dap.adapters["haskell-debugger"] = {
type = "server",
port = "${port}",
executable = {
command = "hdb",
args = {
"server", "--port", "${port}"
}
}
}
And configurations as needed. For example:
dap.configurations.haskell = {
{
type = "haskell-debugger",
request = "launch",
name = "hdb:file:main",
entryFile = "${file}",
entryPoint = "main",
projectRoot = "${workspaceFolder}",
entryArgs = {},
extraGhcArgs = {},
},
}
Haskell
- Install haskell-debug-adapter
stack install haskell-dap ghci-dap haskell-debug-adapter
- Add the adapter and configuration:
dap.adapters.haskell = {
type = 'executable';
command = 'haskell-debug-adapter';
args = {'--hackage-version=0.0.33.0'};
}
dap.configurations.haskell = {
{
type = 'haskell',
request = 'launch',
name = 'Debug',
workspace = '${workspaceFolder}',
startup = "${file}",
stopOnEntry = true,
logFile = vim.fn.stdpath('data') .. '/haskell-dap.log',
logLevel = 'WARNING',
ghciEnv = vim.empty_dict(),
ghciPrompt = "λ: ",
-- Adjust the prompt to the prompt you see when you invoke the stack ghci command below
ghciInitialPrompt = "λ: ",
ghciCmd= "stack ghci --test --no-load --no-build --main-is TARGET --ghci-options -fprint-evld-with-show",
},
}
Javascript
vscode-js-debug
A DAP-compatible JavaScript debugger.
vscode-js-debug contains 2 executables:
dapDebugServer.js(recommended) available since v1.77.0. All examples below that usedapDebugServerrequire you to use this executable.vsDebugServer.jsvscode specific (to make it work withnvim-dapyou can use use nvim-dap-vscode-js but the above variant is recommended)
A Manual setup using the dapDebugServer executable looks like this:
-
Make sure you're using nvim-dap 0.6.0+
-
Install vscode-js-debug:
- Download
js-debug-dap-${version}.tar.gzfrom their releases page. You need at least version 1.77.0 (it might be not available yet in newer versions) - Extract it to some folder via
tar xvzf path/to/vscode-js-debug.tar.gz.
- Download
-
Add the adapter definition:
require("dap").adapters["pwa-node"] = {
type = "server",
host = "localhost",
port = "${port}",
executable = {
command = "node",
-- 💀 Make sure to update this path to point to your installation
args = {"/path/to/js-debug/src/dapDebugServer.js", "${port}"},
}
}
- Add a configuration:
require("dap").configurations.javascript = {
{
type = "pwa-node",
request = "launch",
name = "Launch file",
program = "${file}",
cwd = "${workspaceFolder}",
},
}
See the upstream options document and the configuration attributes page for documentation about the available configuration options.
To configure it for debugging of TypeScript make sure to read the source map section.
node-debug2
-
Install node-debug2
mkdir -p ~/dev/microsoftgit clone https://github.com/microsoft/vscode-node-debug2.git ~/dev/microsoft/vscode-node-debug2cd ~/dev/microsoft/vscode-node-debug2npm installNODE_OPTIONS=--no-experimental-fetch npm run build
-
Add the adapter and configuration:
local dap = require('dap')
dap.adapters.node2 = {
type = 'executable',
command = 'node',
args = {os.getenv('HOME') .. '/dev/microsoft/vscode-node-debug2/out/src/nodeDebug.js'},
}
dap.configurations.javascript = {
{
name = 'Launch',
type = 'node2',
request = 'launch',
program = '${file}',
cwd = vim.fn.getcwd(),
sourceMaps = true,
protocol = 'inspector',
console = 'integratedTerminal',
},
{
-- For this to work you need to make sure the node process is started with the `--inspect` flag.
name = 'Attach to process',
type = 'node2',
request = 'attach',
processId = require'dap.utils'.pick_process,
},
}
Javascript-Deno
vscode-js-debug
-
Make sure you're using nvim-dap 0.6.0+
-
Install vscode-js-debug:
- Download
js-debug-dap-${version}.tar.gzfrom their releases page. You need at least version 1.77.0 (it might be not available yet in newer versions) - Extract it to some folder via
tar xvzf path/to/vscode-js-debug.tar.gz.
- Download
-
Add the adapter definition:
require("dap").adapters["pwa-node"] = {
type = "server",
host = "localhost",
port = "${port}",
executable = {
command = "node",
-- 💀 Make sure to update this path to point to your installation
args = {"/path/to/js-debug/src/dapDebugServer.js", "${port}"},
}
}
- Add a configuration:
dap.configurations.typescript = {
{
type = 'pwa-node',
request = 'launch',
name = "Launch file",
runtimeExecutable = "deno",
runtimeArgs = {
"run",
"--inspect-wait",
"--allow-all"
},
program = "${file}",
cwd = "${workspaceFolder}",
attachSimplePort = 9229,
},
}
Javascript Chrome
-
build vscode-chrome-debug
- git clone https://github.com/Microsoft/vscode-chrome-debug
- cd ./vscode-chrome-debug
- npm install
- npm run build
-
add the adapter cfg:
dap.adapters.chrome = {
type = "executable",
command = "node",
args = {os.getenv("HOME") .. "/path/to/vscode-chrome-debug/out/src/chromeDebug.js"} -- TODO adjust
}
dap.configurations.javascriptreact = { -- change this to javascript if needed
{
type = "chrome",
request = "attach",
program = "${file}",
cwd = vim.fn.getcwd(),
sourceMaps = true,
protocol = "inspector",
port = 9222,
webRoot = "${workspaceFolder}"
}
}
dap.configurations.typescriptreact = { -- change to typescript if needed
{
type = "chrome",
request = "attach",
program = "${file}",
cwd = vim.fn.getcwd(),
sourceMaps = true,
protocol = "inspector",
port = 9222,
webRoot = "${workspaceFolder}"
}
}
NOTE: chrome has to be started with a remote debugging port
google-chrome-stable --remote-debugging-port=9222
Javascript Firefox
- build vscode-firefox-debug
- git clone https://github.com/firefox-devtools/vscode-firefox-debug.git
- cd vscode-firefox-debug
- npm install
- npm run build
adapter.bundle.js depends on other files from the dist folder. If you want to change the output files' location, make sure you copy the whole dist folder - DO NOT try to copy adapter.bundle.js on its own to some other folder.
- add the adapter cfg:
local dap = require('dap')
dap.adapters.firefox = {
type = 'executable',
command = 'node',
args = {os.getenv('HOME') .. '/path/to/vscode-firefox-debug/dist/adapter.bundle.js'},
}
dap.configurations.typescript = {
{
name = 'Debug with Firefox',
type = 'firefox',
request = 'launch',
reAttach = true,
url = 'http://localhost:3000',
webRoot = '${workspaceFolder}',
firefoxExecutable = '/usr/bin/firefox'
}
}
PHP
Install vscode-php-debug:
git clone https://github.com/xdebug/vscode-php-debug.gitcd vscode-php-debugnpm install && npm run build
If you have not configured Xdebug, read Installation at vscode-php-debug.
Add the adapter configuration:
dap.adapters.php = {
type = 'executable',
command = 'node',
args = { '/path/to/vscode-php-debug/out/phpDebug.js' }
}
dap.configurations.php = {
{
type = 'php',
request = 'launch',
name = 'Listen for Xdebug',
port = 9003
}
}
Supported configuration options for PHP can be found under Supported launch.json settings at the vscode-php-debug repo.
Perl
perl-debug-adapter
First, install perl-debug-adapter via one of the following methods:
Add the adapter configuration:
local dap = require 'dap'
dap.adapters.perl = {
type = 'executable',
-- Path to perl-debug-adapter - will be different based on the installation method
-- mason.nvim
command = vim.env.MASON .. '/bin/perl-debug-adapter',
-- AUR (or if perl-debug-adapter is in PATH)
-- command = 'perl-debug-adapter',
args = {},
}
dap.configurations.perl = {
{
type = 'perl',
request = 'launch',
name = 'Launch Perl',
program = '${workspaceFolder}/${relativeFile}',
}
}
-- this is optional but can be helpful when starting out
dap.set_log_level 'TRACE'
Perl::LanguageServer
Install Perl::LanguageServer from cpan, e.g. via cpanm Perl::LanguageServer.
You have to run the lsp in the background via: perl -MPerl::LanguageServer -e "Perl::LanguageServer::run" -- --port 27011, where 27011 is an arbitrary port that has to be open.
Add the adapter configuration:
dap.adapters.perlsp = {
type = 'server';
host = '127.0.0.1';
port = '27011';
}
dap.configurations.perl = {
{
name = 'Launch Perl';
type = 'perlsp';
request = 'launch';
program = "${workspaceFolder}/${relativeFile}";
reloadModules = true;
stopOnEntry = false;
cwd = "${workspaceFolder}";
}
}
There is an issue with the hover() method because the "$", "@" and "%" signs are not handled properly per default. You can fix it by adding them to iskeyword:
vim.opt.iskeyword:append({"$", "@-@", "%"})
Scala
Possible via nvim-metals
Kotlin
Install kotlin-debug-adaptor
Add the following to config:
dap.adapters.kotlin = {
type = "executable",
command = "kotlin-debug-adapter",
options = { auto_continue_if_many_stopped = false },
}
dap.configurations.kotlin = {
{
type = "kotlin",
request = "launch",
name = "This file",
-- may differ, when in doubt, whatever your project structure may be,
-- it has to correspond to the class file located at `build/classes/`
-- and of course you have to build before you debug
mainClass = function()
local root = vim.fs.find("src", { path = vim.uv.cwd(), upward = true, stop = vim.env.HOME })[1] or ""
local fname = vim.api.nvim_buf_get_name(0)
-- src/main/kotlin/websearch/Main.kt -> websearch.MainKt
return fname:gsub(root, ""):gsub("main/kotlin/", ""):gsub(".kt", "Kt"):gsub("/", "."):sub(2, -1)
end,
projectRoot = "${workspaceFolder}",
jsonLogFile = "",
enableJsonLogging = false,
},
{
-- Use this for unit tests
-- First, run
-- ./gradlew --info cleanTest test --debug-jvm
-- then attach the debugger to it
type = "kotlin",
request = "attach",
name = "Attach to debugging session",
port = 5005,
args = {},
projectRoot = vim.fn.getcwd,
hostName = "localhost",
timeout = 2000,
},
}
Emmylua
Download the latest release from the releases page and extract the executable to your desired location.
Requires the emmy_core library to be available in the environment:
🖥️ Windows
-- Add the emmy_core library to your package path
package.cpath = package.cpath .. ";<path_to_emmy_core>/?.dll"
-- Initialize the debugger
local dbg = require("emmy_core")
dbg.tcpListen("localhost", 9966)
-- Optional: Wait for IDE connection
-- dbg.waitIDE()
-- Optional: Set a breakpoint
-- dbg.breakHere()
🐧 Linux
-- Add the emmy_core library to your package path
package.cpath = package.cpath .. ";<path_to_emmy_core>/?.so"
-- Initialize the debugger
local dbg = require("emmy_core")
dbg.tcpListen("localhost", 9966)
-- Optional: Wait for IDE connection
-- dbg.waitIDE()
-- Optional: Set a breakpoint
-- dbg.breakHere()
🍎 macOS
-- Add the emmy_core library to your package path
package.cpath = package.cpath .. ";<path_to_emmy_core>/?.dylib"
-- Initialize the debugger
local dbg = require("emmy_core")
dbg.tcpListen("localhost", 9966)
-- Optional: Wait for IDE connection
-- dbg.waitIDE()
-- Optional: Set a breakpoint
-- dbg.breakHere()
Example adapter setup
dap.adapters.emmylua = {
type = 'executable',
command = '/path/to/emmylua_dap',
args = {}
}
Example configuration
dap.configurations.lua = {
{
type = 'emmylua',
request = 'launch',
name = 'EmmyLua Debug',
host = 'localhost',
port = 9966,
sourcePaths = { 'path/to/your/workspace' }, -- maybe exist some env variable
ext = { '.lua' },
ideConnectDebugger = true
}
}
local-lua-debugger-vscode
Install local-lua-debugger-vscode, either via:
- Your package manager
- From source:
- git clone https://github.com/tomblind/local-lua-debugger-vscode
- cd local-lua-debugger-vscode
- npm install
- npm run build
Configure the adapter:
local dap = require("dap")
dap.adapters["local-lua"] = {
type = "executable",
command = "node",
args = {
"/absolute/path/to/local-lua-debugger-vscode/extension/debugAdapter.js"
},
enrich_config = function(config, on_config)
if not config["extensionPath"] then
local c = vim.deepcopy(config)
-- 💀 If this is missing or wrong you'll see
-- "module 'lldebugger' not found" errors in the dap-repl when trying to launch a debug session
c.extensionPath = "/absolute/path/to/local-lua-debugger-vscode/"
on_config(c)
else
on_config(config)
end
end,
}
If you install local-lua-debugger via a package manager, it may create a
local-lua-dbg (or similar) executable, which you can use as command instead
of invoking node.
See Debugging Lua in Neovim for how to use this with Neovim itself as custom Lua interpreter.
Neovim Lua
-
one-small-step-for-vimkind for debugging with breakpoint support using two nvim instances: One where you set breakpoints and navigate the source. One that is running the code in question.
-
nluarepl for expression evaluation and logpoint support, no breakpoints. Uses the active instance mainly intended for REPL use.
OCaml earlybird
- Install earlybird with
opam install earlybird - Change to
(lang dune 3.7)or above and add(map_workspace_root false)to yourdune-project. See dune documentation for more information. - Build binary executable for debugging by adding
(modes byte exe)to the dune stanza e.g. for tests
(tests
(names test)
(modes byte exe)
(libraries alcotest))
Example adapter setup
dap.adapters.ocamlearlybird = {
type = 'executable',
command = 'ocamlearlybird',
args = { 'debug' }
}
Example configs
dap.configurations.ocaml = {
{
name = 'OCaml Debug test.bc',
type = 'ocamlearlybird',
request = 'launch',
program = '${workspaceFolder}/_build/default/test/test.bc',
},
{
name = 'OCaml Debug main.bc',
type = 'ocamlearlybird',
request = 'launch',
program = '${workspaceFolder}/_build/default/bin/main.bc',
},
}
Dotnet
Install netcoredbg, either via:
- Your package manager
- Downloading it from the release page and extracting it to a folder
- Building from source by following the instructions in the netcoredbg repo.
Note: On macOS arm64, you must compile the binaries yourself
Add the adapter configuration:
dap.adapters.coreclr = {
type = 'executable',
command = '/path/to/dotnet/netcoredbg/netcoredbg',
args = {'--interpreter=vscode'}
}
Add a configuration:
dap.configurations.cs = {
{
type = "coreclr",
name = "launch - netcoredbg",
request = "launch",
program = function()
return vim.fn.input('Path to dll', vim.fn.getcwd() .. '/bin/Debug/', 'file')
end,
},
}
Alternatively for macOS arm64, you can use this plugin: netcoredbg-macOS-arm64.nvim
For Windows, set noshellslash might be required in order for netcoredbg to find source references for breakpoints.
Unity
Install vscode-unity-debug
Install mono dependency if doesn't exist
Add the adapter configuration:
dap.adapters.unity = {
type = 'executable',
command = '<path-to-mono-directory>/Commands/mono',
args = {'<path-to-unity-debug-directory>/unity.unity-debug-x.x.x/bin/UnityDebug.exe'}
}
Add a configuration:
dap.configurations.cs = {
{
type = 'unity',
request = 'attach',
name = 'Unity Editor',
}
}
Elixir
Install elixir-ls.
Add the adapter configuration:
dap.adapters.mix_task = {
type = 'executable',
command = '/path/to/elixir-ls/debug_adapter.sh', -- debug_adapter.bat for windows
args = {}
}
Add a configuration (see configuration options):
dap.configurations.elixir = {
{
type = "mix_task",
name = "mix test",
task = 'test',
taskArgs = {"--trace"},
request = "launch",
startApps = true, -- for Phoenix projects
projectDir = "${workspaceFolder}",
requireFiles = {
"test/**/test_helper.exs",
"test/**/*_test.exs"
}
},
}
Godot GDScript
Godot 4.0 includes support for the debug adapter protocol.
You need to have a Godot instance running to use it.
Adapter definition:
local dap = require('dap')
dap.adapters.godot = {
type = "server",
host = '127.0.0.1',
port = 6006,
}
The port must match the Godot setting. Go to Editor -> Editor Settings, then find Debug Adapter under Network:
The Debug with External Editor checkbox in the Script view must be checked. Go to Debug -> Debug with External Editor
Configuration:
dap.configurations.gdscript = {
{
type = "godot",
request = "launch",
name = "Launch scene",
project = "${workspaceFolder}",
}
}
See the Configuration section in the
godot-vscode-plugin
README for a description of the configuration properties.
Bash
Make sure you have nodejs installed. Otherwise, starting the debugger will exit with 127.
Add the adapter configuration :
dap.adapters.bashdb = {
type = 'executable';
command = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/bash-debug-adapter';
name = 'bashdb';
}
Add the configuration:
dap.configurations.sh = {
{
type = 'bashdb',
request = 'launch',
name = "Launch file",
showDebugOutput = true,
pathBashdb = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/extension/bashdb_dir/bashdb',
pathBashdbLib = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/extension/bashdb_dir',
trace = true,
file = "${file}",
program = "${file}",
cwd = '${workspaceFolder}',
pathCat = "cat",
pathBash = "/bin/bash",
pathMkfifo = "mkfifo",
pathPkill = "pkill",
args = {},
argsString = '',
env = {},
terminalKind = "integrated",
}
}
