Skip to content
Doron Behar edited this page May 21, 2023 · 16 revisions

Configuration Cookbook

This page is for various configuration snippets that might be useful.

Pick a process

Some debug adapters support attaching to a running process. If you want to have a "pick pid" dialog, you can use the pick_process utils function in your configuration. For example, in a configuration using the lldb-vscode debug adapter, it can be used like this:

dap.configurations.cpp = {
    {
      -- If you get an "Operation not permitted" error using this, try disabling YAMA:
      --  echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
      name = "Attach to process",
      type = 'cpp',  -- Adjust this to match your adapter name (`dap.adapters.<name>`)
      request = 'attach',
      pid = require('dap.utils').pick_process,
      args = {},
    },
}

See :help dap-configuration for more information about nvim-dap configuration.

Map K to hover while session is active.

local dap = require('dap')
local api = vim.api
local keymap_restore = {}
dap.listeners.after['event_initialized']['me'] = function()
  for _, buf in pairs(api.nvim_list_bufs()) do
    local keymaps = api.nvim_buf_get_keymap(buf, 'n')
    for _, keymap in pairs(keymaps) do
      if keymap.lhs == "K" then
        table.insert(keymap_restore, keymap)
        api.nvim_buf_del_keymap(buf, 'n', 'K')
      end
    end
  end
  api.nvim_set_keymap(
    'n', 'K', '<Cmd>lua require("dap.ui.widgets").hover()<CR>', { silent = true })
end

dap.listeners.after['event_terminated']['me'] = function()
  for _, keymap in pairs(keymap_restore) do
    api.nvim_buf_set_keymap(
      keymap.buffer,
      keymap.mode,
      keymap.lhs,
      keymap.rhs,
      { silent = keymap.silent == 1 }
    )
  end
  keymap_restore = {}
end

Reload dap.configuration before starting a debug session

If you configure the dap.configurations table in a Lua module and load that module via a require call, the module gets cached. If you then modify the module with the configurations the changes won't be picked up automatically.

There are two options to force load the changes:

1. Read the file via :luafile:

You could add something like autocmd BufWritePost ~/your_dap_config_file :luafile % in init.vim.

2. Clear the Lua package cache and reload the configuration module:

Assuming the following conditions:

  • The code below is in the file ~/.config/nvim/lua/dap_util.lua
  • dap.configurations is set in ~/.config/nvim/lua/dap_config.lua
local M = {}
local dap =  require'dap'

function M.reload_continue()
  package.loaded['dap_config'] = nil
  require('dap_config')
  dap.continue()
end

local opts = { noremap=false, silent=true }

-- <Leader>ec to continue
vim.api.nvim_buf_set_keymap( 0, 'n', '<Leader>ec',
       '<cmd>lua require"dap".continue()<CR>', opts)

-- <Leader>eC to reload and then continue
vim.api.nvim_buf_set_keymap(0, 'n', '<Leader>eC',
    '<cmd>lua require"dap_setup".reload_continue()<CR>', opts)

return M

Making debugging .NET easier

When debugging .NET projects, you sometimes need to rebuild it. You also don't want to input the path to dll and/or the path to your proj file.

TL;DR - replace your config with the config below, it should be intuitive. Otherwise, read a detailed description.

vim.g.dotnet_build_project = function()
    local default_path = vim.fn.getcwd() .. '/'
    if vim.g['dotnet_last_proj_path'] ~= nil then
        default_path = vim.g['dotnet_last_proj_path']
    end
    local path = vim.fn.input('Path to your *proj file', default_path, 'file')
    vim.g['dotnet_last_proj_path'] = path
    local cmd = 'dotnet build -c Debug ' .. path .. ' > /dev/null'
    print('')
    print('Cmd to execute: ' .. cmd)
    local f = os.execute(cmd)
    if f == 0 then
        print('\nBuild: ✔️ ')
    else
        print('\nBuild: ❌ (code: ' .. f .. ')')
    end
end

vim.g.dotnet_get_dll_path = function()
    local request = function()
        return vim.fn.input('Path to dll', vim.fn.getcwd() .. '/bin/Debug/', 'file')
    end

    if vim.g['dotnet_last_dll_path'] == nil then
        vim.g['dotnet_last_dll_path'] = request()
    else
        if vim.fn.confirm('Do you want to change the path to dll?\n' .. vim.g['dotnet_last_dll_path'], '&yes\n&no', 2) == 1 then
            vim.g['dotnet_last_dll_path'] = request()
        end
    end

    return vim.g['dotnet_last_dll_path']
end

local config = {
  {
    type = "coreclr",
    name = "launch - netcoredbg",
    request = "launch",
    program = function()
        if vim.fn.confirm('Should I recompile first?', '&yes\n&no', 2) == 1 then
            vim.g.dotnet_build_project()
        end
        return vim.g.dotnet_get_dll_path()
    end,
  },
}

dap.configurations.cs = config
dap.configurations.fsharp = config

Detailed description

So basically, when you start a debug session, the dialog will look like this:

Step 1.

Should I recompile first? y/n

  • y
    • Path to your *proj file (if you already input once, it will substitute it ; but you still can change it)
  • n

Step 2.

If you didn't specify the dll path yet:

  • Path to dll

If you did:

  • Do you want to change the path to dll? (it previews your previous dll path)
    • y
      • Path to dll
    • n

Pro tip

You can bind building the project to some hotkey:

vim.api.nvim_set_keymap('n', '<C-b>', ':lua vim.g.dotnet_build_project()<CR>', { noremap = true, silent = true })

Using telescope for selecting an executable to debug

nvim-dap supports asynchrounous operations using Lua's coroutines. This helps launching telescope as the selector for choosing an executable to debug.

With variables declared as follows:

local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")

The following code snippet lets you choose an executable through telescope, passes to nvim-dap, and launches a C++ debuggin session. (The snippets assume that you have fd installed, please replace { "fd", "--hidden", "--no-ignore", "--type", "x" } with a command that fits you)

dap.configurations.cpp = {
  {
    name = "Launch an executable",
    type = "cppdbg",
    request = "launch",
    cwd = "${workspaceFolder}",
    program = function()
      return coroutine.create(function(coro)
        local opts = {}
        pickers
          .new(opts, {
            prompt_title = "Path to executable",
            finder = finders.new_oneshot_job({ "fd", "--hidden", "--no-ignore", "--type", "x" }, {}),
            sorter = conf.generic_sorter(opts),
            attach_mappings = function(buffer_number)
              actions.select_default:replace(function()
                actions.close(buffer_number)
                coroutine.resume(coro, action_state.get_selected_entry()[1])
              end)
              return true
            end,
          })
          :find()
      end)
    end,
  },
}

Run the current buffer script with CLI arguments, and enter into debug mode

The following creates a user command, named RunScriptWithArgs that accepts shell expanded arguments, and spawns a DAP session for the process. The configuration uses the type = vim.bo.filetype - i.e chosen automatically according to your currently edited buffer's &filetype. The arguments are expanded using expand() which expands wildcards and environment variables (see :help expand()).

The keymap defined at the end makes typing that command faster, and also helps you in terms of command line history - in case you run the script you are editing with the same arguments over and over.

vim.api.nvim_create_user_command("RunScriptWithArgs", function(t)
	-- :help nvim_create_user_command
	args = vim.split(vim.fn.expand(t.args), '\n')
	approval = vim.fn.confirm(
		"Will try to run:\n    " ..
		vim.bo.filetype .. " " ..
		vim.fn.expand('%') .. " " ..
		t.args .. "\n\n" ..
		"Do you approve? ",
		"&Yes\n&No", 1
	)
	if approval == 1 then
		dap.run({
			type = vim.bo.filetype,
			request = 'launch',
			name = 'Launch file with custom arguments (adhoc)',
			program = '${file}',
			args = args,
		})
	end
end, {
	complete = 'file',
	nargs = '*'
})
vim.keymap.set('n', '<leader>R', ":RunScriptWithArgs ")