minimalistic nvim-dap-ui alternative
dap-view-demo2.mp4
Warning
Requires neovim 0.11+
return {
{
"igorlfs/nvim-dap-view",
opts = {},
},
}
For a better experience, consider adding nvim-dap-view
as a dependency for
nvim-dap
(instead of declaring it as a standalone plugin)
Why?
By default, when launching a session, nvim-dap
's terminal window takes half
the screen. As a saner default, nvim-dap-view
hijacks the terminal window
(even if not invoked), making the split take only 12 (configurable) lines.
-- Your nvim-dap config
return {
{
"mfussenegger/nvim-dap",
dependencies = {
{ "igorlfs/nvim-dap-view", opts = {} },
...,
},
...,
},
}
Note
nvim-dap-view
heavily relies on the winbar option. If you're using a plugin that overrides it, consider disabling the plugin for nvim-dap-view
buffers (e.g., lualine)
The plugin provides 6 "views" that share the same window (so there's clutter)
- Watches view
- Shows a list of (user defined) expressions, that are evaluated by the debug adapter
- Add, edit and delete expressions from the watch list
- Including adding the variable under the cursor
- Exceptions view
- Control when the debugger should stop, outside of breakpoints (e.g., whenever an exception is thrown, or when an exception is caught1).
- Toggle filter with
<CR>
- Breakpoints view
- List all breakpoints
- Uses syntax highlighting2
- Shows filename and number line
- Jump to a breakpoint with
<CR>
- List all breakpoints
- Threads view
- List all threads and their stack traces
- Jump to a function in the call stack
- Toggle
subtle
(hidden) frames witht
- REPL view
- Use REPL provided by nvim-dap
- Scopes view
- Use the scopes widget provided by nvim-dap
- Expand variables with
<CR>
- Expand variables with
- Use the scopes widget provided by nvim-dap
You can also interact with the console, which is also provided by nvim-dap
. By the default, the console has its own window, but it can be configured to be shown with the other views. See details on the default config section.
The console's default size (height) is resized to match your nvim-dap-view
configuration. You can also either completely hide it (if it's not being used at all) or hide it only during session initialization.
Default options
return {
winbar = {
show = true,
-- You can add a "console" section to merge the terminal with the other views
sections = { "watches", "scopes", "exceptions", "breakpoints", "threads", "repl" },
-- Must be one of the sections declared above
default_section = "watches",
headers = {
breakpoints = "Breakpoints [B]",
scopes = "Scopes [S]",
exceptions = "Exceptions [E]",
watches = "Watches [W]",
threads = "Threads [T]",
repl = "REPL [R]",
console = "Console [C]",
},
},
windows = {
height = 12,
terminal = {
-- 'left'|'right'|'above'|'below': Terminal position in layout
position = "left",
-- List of debug adapters for which the terminal should be ALWAYS hidden
hide = {},
-- Hide the terminal when starting a new session
start_hidden = false,
},
},
-- Controls how to jump when selecting a breakpoint or navigating the stack
switchbuf = "usetab,newtab",
}
Start a regular debugging session. When desired, you can use :DapViewOpen
to
start the plugin. You can switch to a view (section) using the letter outlined
in the 'winbar'
(e.g., B
for the breakpoints view).
The breakpoints view, the exceptions view and the scopes view only have 1
mapping: <CR>
. It jumps to a breakpoint, toggles an exception filter, and
expands a variable, respectively. The watches view comes with 3 mappings:
i
to insert a new expressione
to edit an expressiond
to delete an expression
Though, the preferred way of adding a new expression is using the
:DapViewWatch
command. In normal mode, it adds the variable under the cursor
to the watch list. The threads view has 2 mappings:
<CR>
jumps to a location in the call stackt
toggles subtle frames
When you finish your session, you can use :DapViewClose
to close the
nvim-dap-view
window.
In total, there are 5 commands:
DapViewOpen
DapViewClose
DapViewToggle
DapViewWatch
DapViewJump [view]
You can :DapViewJump [view]
to jump directly to a view, from any window. For instance, to jump to the REPL, you can use :DapViewJump repl
.
Additionally, you can use DapViewClose!
and DapViewToggle!
to also hide the
terminal window, if you'd rather have a tidy view.
If you prefer using lua functions, I got you covered! The following provide the same functionality as above:
require("dap-view").open()
require("dap-view").close()
require("dap-view").close(true) -- Same as `DapViewClose!`
require("dap-view").toggle()
require("dap-view").toggle(true) -- Same as `DapViewToggle!`
require("dap-view").add_expr()
require("dap-view").jump("[view]") -- Can be used to jump to a specific view, from any window
nvim-dap-view
doesn't define any keybindings (outside its own buffer, of
course). An example for the toggle functionality, using the lua API:
vim.keymap.set("n", "<leader>v", function()
require("dap-view").toggle()
end, { desc = "Toggle nvim-dap-view" })
If you find yourself constantly toggling nvim-dap-view
once a session starts and then closing on session end, you might want to add the following snippet to your configuration:
local dap, dv = require("dap"), require("dap-view")
dap.listeners.before.attach["dap-view-config"] = function()
dv.open()
end
dap.listeners.before.launch["dap-view-config"] = function()
dv.open()
end
dap.listeners.before.event_terminated["dap-view-config"] = function()
dv.close()
end
dap.listeners.before.event_exited["dap-view-config"] = function()
dv.close()
end
Some debug adapters don't use the integrated terminal (console). To avoid having a useless window lying around, you can completely hide the terminal for them. To achieve that, add the following snippet to your nvim-dap-view
setup:
-- Goes into your opts table (if using lazy.nvim), otherwise goes into the setup function
-- No need to include the "return" statement
return {
windows = {
terminal = {
-- NOTE Don't copy paste this snippet
-- Use the actual names for the adapters you want to hide
-- `go` is known to not use the terminal.
hide = { "go", "some-other-adapter" },
},
},
}
When setting windows.terminal.position
to right
, nvim-dap-view
's main window may be used to display the current frame (after execution stops), because nvim-dap
defaults to the global switchbuf
setting. To address this, update your switchbuf
configuration. For instance:
require("dap").defaults.fallback.switchbuf = "useopen" -- See :h dap-defaults to learn more
When jumping via nvim-dap-view
(to a breakpoint or to a frame in the stack), nvim-dap-view
uses its own switchbuf
, which supports a subset of the default neovim options ("newtab", "useopen", "usetab" and "uselast"). You can customize it with:
-- Goes into your opts table (if using lazy.nvim), otherwise goes into the setup function
-- No need to include the "return" statement
return {
switchbuf = "useopen",
}
:DapViewWatch
expands the <cexpr>
under the cursor (see :h <cexpr>
). By default, this setting works really well for C-like languages, but it can be cumbersome for other languages. To handle that, you can tweak the value for the iskeyword
option (see :h iskeyword
).
nvim-dap-view
defines 10 highlight groups:
NvimDapViewMissingData
NvimDapViewWatchText
NvimDapViewWatchTextChanged
NvimDapViewExceptionFilterEnabled
NvimDapViewExceptionFilterDisabled
NvimDapViewFileName
NvimDapViewLineNumber
NvimDapViewSeparator
NvimDapViewThread
NvimDapViewThreadStopped
They are linked to (somewhat) reasonable defaults, but they may look odd with your colorscheme. If the links aren't defined, no highlighting will be applied. To fix that, you have to manually define the highlight groups (see :h nvim_set_hl()
). Consider contributing to your colorscheme by sending a PR to add support to nvim-dap-view
!
nvim-dap-view
sets buffer filetypes for the following Views
Window | Filetype |
---|---|
watches, exceptions, ... | dap-view |
terminal | dap-view-term |
These filetypes can be used to override buffer and window options set by nvim-dap-view
Example autocommand
Map q to quit in nvim-dap-view
filetypes:
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "dap-view", "dap-view-term", "dap-repl" }, -- dap-repl is set by `nvim-dap`
callback = function(evt)
vim.keymap.set("n", "q", "<C-w>q", { silent = true, buffer = evt.buf })
end,
})
- Watches
- Actions
- Expanding variables
- Yank expression's value
- Actions
Missing something? Create an issue with a feature request!
- Breakpoints view doesn't show breakpoint conditions
- That's a limitation with the current breakpoints API from
nvim-dap
. We could use a workaround, but a new API is planned
- That's a limitation with the current breakpoints API from
- nvim-dap-ui is obviously a huge inspiration!
- Code to inject treesitter highlights into line is taken from
quicker.nvim
; - Some snippets are directly extracted from
nvim-dap
:- Currently, there's no API to extract breakpoint information (see issue), so we resort to using nvim-dap internal mechanism, that tracks extmarks;
- The magic to extract expressions from visual mode is also a courtesy of
nvim-dap
.
- lucaSartore for the inspiration for handling breakpoint exceptions;
- Kulala for the creative usage
of neovim's
'winbar'
to handle multiple views. - blink.cmp for the config validation (which is partially taken from a PR to indent-blankline)