nvim-dapper ships two layers of keymaps:
| Layer | Where it lives | Default policy |
|---|---|---|
| View-local | Each panel view's scratch buffer | Always on |
| Global | Your normal-mode (and visual) maps | Opt-in only |
The single module
lua/nvim-dapper/keymaps/ owns every
vim.keymap.set call in the plugin (enforced by the keymap-isolation
lint). All bindings carry nowait = true, silent = true,
noremap = true, and a self-documenting desc field — so :map <key>
inside any panel buffer tells you what the key does.
These bindings are installed on the view's scratch buffer at mount time.
They follow Neovim list-view idiom (<CR> / o to go-to, l / h to
expand / collapse).
| Key | Action |
|---|---|
<CR>, o, <2-LeftMouse> |
jump-to-source |
<Tab>, l |
expand |
h |
collapse |
zR |
expand-all-children |
zM |
collapse-all-children |
r |
retry |
]p |
load-more |
L |
cycle-view-next |
H |
cycle-view-prev |
| Key | Action | Notes |
|---|---|---|
<CR> |
jump-to-frame |
Falls through to jump-to-source when not on a frame row. |
The previous
<C-]>secondary binding was removed because it shadowed Neovim's built-in tag jump. Restore it viasetup({ keymaps = { stack = { ["jump-to-frame"] = { "<CR>", "<C-]>" } } } })if you want it back.
| Key | Action |
|---|---|
t |
toggle-enabled |
dd |
remove |
e |
edit-condition |
| Key | Action |
|---|---|
a |
add-watch |
dd |
remove-watch |
e |
edit-watch-expression |
| Key | Mode | Action |
|---|---|---|
<CR> |
i | submit |
The console view registers three more actions that ship without a default key because every plausible Ctrl-binding shadows a built-in Neovim shortcut:
clear-console— previously<C-l>(collided with redraw / window navigation).recall-previous(insert mode) — previously<C-p>(collided with keyword completion).recall-next(insert mode) — previously<C-n>(collided with keyword completion).
Restore the prior bindings (or pick your own) with:
require("nvim-dapper").setup({
keymaps = {
console = {
["clear-console"] = "<C-l>",
["recall-previous"] = { mode = "i", lhs = "<C-p>" },
["recall-next"] = { mode = "i", lhs = "<C-n>" },
},
},
})The view-local defaults never bind anything in the
<leader>d…namespace (reserved for the AI feature surface) or any function key (reserved for the opt-in global layer). A runtime check (_assert_namespace_safe) enforces this every time the defaults table loads.
These bindings are installed in your normal-mode (and, for evaluate,
visual-mode) keymap table — but only when you opt in.
| Key | Mode | Operation |
|---|---|---|
<F5> |
n | continue |
<F10> |
n | step_over |
<F11> |
n | step_into |
<S-F11> |
n | step_out |
<F9> |
n | toggle_breakpoint |
<leader>dB |
n | set_conditional_breakpoint (prompts) |
<leader>dL |
n | set_logpoint (prompts) |
<leader>de |
n | evaluate — word under cursor |
<leader>de |
x | evaluate — visual selection |
<leader>dr |
n | restart |
<leader>dt |
n | terminate |
<leader>dC |
n | run_to_cursor |
<leader>du |
n | toggle_panel |
Enable with:
require("nvim-dapper").setup({
keymaps = { global = { enabled = true } },
})Recommended with panel-focused stepping:
require("nvim-dapper").setup({
stopped_frame_source_targeting = true,
keymaps = { global = { enabled = true } },
})continue, step_over, step_into, and step_out remain thin calls to
nvim-dap. The protection comes from Dapper's opt-in managed switchbuf
policy. It reuses an eligible editing window for stopped source, including
unopened source buffers, and opens a split only when no editing window is
available. The managed policy covers dap.defaults.fallback.switchbuf and
adapter-specific dap.defaults.<type>.switchbuf values for active sessions,
then restores the prior values when disabled. If nvim-dap loads after
setup(), Dapper keeps the opt-in pending and installs once dap is
available.
Plain setup({}) does not change dap.defaults.fallback.switchbuf, so
custom nvim-dap switchbuf strings or functions are preserved unless you
explicitly enable stopped_frame_source_targeting.
Pass per-view overrides under each view's existing options table:
require("nvim-dapper").setup({
variables = {
keymaps = {
["jump-to-source"] = "gd", -- replace default
["expand"] = { "<Tab>", "l", "<Right>" }, -- multiple keys
["retry"] = false, -- disable
-- ["collapse"] = true -- keep default (no-op)
},
},
watches = {
keymaps = false, -- disable the entire view-local layer for watches
},
})Value grammar (identical for view-local and global):
| Value | Meaning |
|---|---|
string |
Replace the default key for this action. |
string[] |
Bind multiple keys to this action. |
false |
Disable this action's default key(s); install nothing. |
true |
Keep the default (no-op). |
For REPL mode-aware bindings, pass a record:
repl = { keymaps = { submit = { mode = "i", lhs = "<C-j>" } } },Unknown action names raise a clear error at setup() time naming both
the offender and the actions the view actually registers.
require("nvim-dapper").setup({
keymaps = {
global = {
enabled = true,
prefix = "<leader>x", -- rewrites <leader>d* defaults
overrides = {
continue = "<F8>", -- replace default
terminate = false, -- disable
evaluate = { "<leader>k", { mode = "x", lhs = "<leader>k" } },
},
},
},
})prefix is a string substitution that runs over the default table
before merging. It only rewrites defaults whose key starts with
<leader>d; function-key defaults and any literal override keys you
supply are untouched.
apply_global_keymaps is idempotent: re-calling setup() with new
overrides cleanly removes the previous installation and installs the
new set — safe to wire into lazy-loaders.
require("nvim-dapper.keymaps").actions() returns a frozen named table
of stable debugger operation names — the documented contract you build
your own keymaps against without depending on nvim-dap's API surface
directly:
local actions = require("nvim-dapper.keymaps").actions()
vim.keymap.set("n", "<leader>kk", actions.continue)
vim.keymap.set("n", "<leader>kj", actions.step_into)
vim.keymap.set("n", "<leader>kl", function() actions.evaluate("foo.bar") end)
vim.keymap.set("n", "<leader>kw", function() actions.focus_view("watches") end)Available operations:
continue, step_over, step_into, step_out,
toggle_breakpoint, set_conditional_breakpoint, set_logpoint,
clear_breakpoint, evaluate, restart, terminate,
run_to_cursor, toggle_panel, focus_view.
- View-local maps live in the plugin's own scratch buffers — no collision risk, so they ship on by default.
- Global maps live in your namespace — high collision risk, so the opt-in flag is the only way to get them.