Skip to content

Debug Adapter installation

Mathias Fußenegger edited this page Dec 5, 2024 · 185 revisions

Contents


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

Ansible

  • Install ansibug
    • python -m pip install ansibug (If installed in a virtualenv, ansible must 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

  1. Install the roku-debug node package. See the bot comment here for an example of how to install.
  2. Install node and npx
  3. Set the ROKU_DEV_TARGET and DEVPASSWORD environment 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.

Some samples:

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,
    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}'
  },
}

Notes:

  • To attach, you might need privileges. For Linux, see for example how to use Yama.
  • To pass some setup to gdb, use --eval-command or --init-eval-command in args list of the adapter, as in the example above (can be used multiple times).

C/C++/Rust (via vscode-cpptools)

NOTE: recomended for dap ui users

Moved to C/C++/Rust (cpptools)

C/C++/Rust (via codelldb)

See C/C++/Rust (codelldb)

C/C++/Rust (via lldb-vscode)

NOTE: lldb-vscode has been renamed to lldb-dap.

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,
  },
}

You can find more configurations options here:

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

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'))

      local script_import = 'command script import "' .. rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
      local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'

      local commands = {}
      local file = io.open(commands_file, 'r')
      if file then
        for line in file:lines() do
          table.insert(commands, line)
        end
        file:close()
      end
      table.insert(commands, 1, script_import)

      return commands
    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 Release mode as building DEBUG binaries requires considerably more resources. You can check Building LLVM with CMake documentation for more details about cmake flags.
  • 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-go
    • cd vscode-go/extension
    • npm install
    • npm 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 :cd the 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

  • 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.

Be aware that this package contains 2 debugger executables:

  • vsDebugServer.js vscode specific (to make it work with nvim-dap is recommended to use nvim-dap-vscode-js plugin which provides the adapter definition and configurations. See the documentation on that repo for installation instructions)
  • dapDebugServer.js implemented since vscode-js-debug v1.77.0. All examples below that use dapDebugServer require you to use this executable instead.

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.gz from 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.
  • 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}",
  },
}

node-debug2

  • Install node-debug2

    • mkdir -p ~/dev/microsoft
    • git clone https://github.com/microsoft/vscode-node-debug2.git ~/dev/microsoft/vscode-node-debug2
    • cd ~/dev/microsoft/vscode-node-debug2
    • npm install
    • NODE_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.gz from 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.
  • 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

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

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.git
  • cd vscode-php-debug
  • npm 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,
    },

}

Lua

local-lua-debugger-vscode

Install local-lua-debugger-vscode, either via:

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

  1. Install earlybird with opam install earlybird
  2. Change to (lang dune 3.7) or above and add (map_workspace_root false) to your dune-project. See dune documentation for more information.
  3. 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:

image

The Debug with External Editor checkbox in the Script view must be checked. Go to Debug -> Debug with External Editor

image

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 = {};
    env = {};
    terminalKind = "integrated";
  }
}