nvim-dap
nvim-dap copied to clipboard
Feature Request: Breakpoint persistence
Hi there,
Would be great if I could persist the breakpoints over sessions - I often have to restart nvim (esp. because the dap UI is still a bit brittle over .stop() and .start() https://github.com/rcarriga/nvim-dap-ui/issues/18. Persisting this over sessions would be great! :)
thanks a lot this awesome plugin.
You could write some custom functions that let you export and import the active breakpoints:
local breakpoints = require('dap.breakpoints')
function M.store()
local bps = {}
local breakpoints_by_buf = breakpoints.get()
for buf, buf_bps in pairs(breakpoints_by_buf) do
bps[tostring(buf)] = buf_bps
end
local fp = io.open('/tmp/breakpoints.json', 'w')
fp:write(vim.fn.json_encode(bps))
fp:close()
end
function M.load()
local fp = io.open('/tmp/breakpoints.json', 'r')
local content = fp:read('*a')
local bps = vim.fn.json_decode(content)
for buf, buf_bps in pairs(bps) do
for _, bp in pairs(buf_bps) do
local line = bp.line
local opts = {
condition = bp.condition,
log_message = bp.logMessage,
hit_condition = bp.hitCondition
}
breakpoints.set(opts, tonumber(buf), line)
end
end
end
Not sure if this is a common enough use-case to warrant including something like that out of the box. (And the dap.breakpoints API is currently considered internal - meaning I might break BWC without going through a deprecation phase)
Thanks for the pointer and implementation suggestion here! I'm fine having a local function and dealing with breakages. Nonetheless I do think normal IDEs persist by default (although it's been a while so might be wrong here) so my biased opinion is that this should likely be default behaviour :) In any event, this solves my issue so thanks a lot, and feel free to notfix or close this issue if you don't think you'll implement it!
Just my two cents, I think persisting breakpoints would be great. At least for the life of the current open file.
At least for the life of the current open file.
This is currently already the case, or maybe I misunderstand what you mean with persisting?
I'm all for persistent breakpoints! I think it would make sense to make it an optional feature that is off by default but having the option built in would be great!
If anyone is interested I've added to the functions that @mfussenegger left in a comment so that the breakpoints persist even when neovim is closed and the numbers of the buffers change.
HOME = os.getenv("HOME")
local breakpoints = require('dap.breakpoints')
function _G.store_breakpoints(clear)
local load_bps_raw = io.open(HOME .. '/.cache/dap/breakpoints.json', 'r'):read("*a")
local bps = vim.fn.json_decode(load_bps_raw)
local breakpoints_by_buf = breakpoints.get()
if (clear) then
for _, bufrn in ipairs(vim.api.nvim_list_bufs()) do
local file_path = vim.api.nvim_buf_get_name(bufrn)
if (bps[file_path] ~= nil) then
bps[file_path] = {}
end
end
else
for buf, buf_bps in pairs(breakpoints_by_buf) do
bps[vim.api.nvim_buf_get_name(buf)] = buf_bps
end
end
local fp = io.open(HOME .. '/.cache/dap/breakpoints.json', 'w')
local final = vim.fn.json_encode(bps)
fp:write(final)
fp:close()
end
function _G.load_breakpoints()
local fp = io.open(HOME .. '/.cache/dap/breakpoints.json', 'r')
local content = fp:read('*a')
local bps = vim.fn.json_decode(content)
local loaded_buffers = {}
local found = false
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
local file_name = vim.api.nvim_buf_get_name(buf)
if (bps[file_name] ~= nil and bps[file_name] ~= {}) then
found = true
end
loaded_buffers[file_name] = buf
end
if (found == false) then
return
end
for path, buf_bps in pairs(bps) do
for _, bp in pairs(buf_bps) do
local line = bp.line
local opts = {
condition = bp.condition,
log_message = bp.logMessage,
hit_condition = bp.hitCondition
}
breakpoints.set(opts, tonumber(loaded_buffers[path]), line)
end
end
end
Then I trigger the storing at the same time as a breakpoint toggle or clear
vim.keymap.set( {'n', 'i', 'v'}, '<F3>', '<cmd>lua require"dap".clear_breakpoints();store_breakpoints(true)<CR>' )
vim.keymap.set( {'n', 'i', 'v'}, '<F4>', '<cmd>lua require"dap".toggle_breakpoint();store_breakpoints(false)<CR>' )
and I load the breakpoints with an autocommand everytime a file opens
autocmd BufRead * :lua load_breakpoints()
This could probably be cleaned up because this is my first time writing lua but so far its been working pretty well for me
If anyone is interested I've added to the functions that @mfussenegger left in a comment so that the breakpoints persist even when neovim is closed and the numbers of the buffers change.
HOME = os.getenv("HOME") local breakpoints = require('dap.breakpoints') function _G.store_breakpoints(clear) local load_bps_raw = io.open(HOME .. '/.cache/dap/breakpoints.json', 'r'):read("*a") local bps = vim.fn.json_decode(load_bps_raw) local breakpoints_by_buf = breakpoints.get() if (clear) then for _, bufrn in ipairs(vim.api.nvim_list_bufs()) do local file_path = vim.api.nvim_buf_get_name(bufrn) if (bps[file_path] ~= nil) then bps[file_path] = {} end end else for buf, buf_bps in pairs(breakpoints_by_buf) do bps[vim.api.nvim_buf_get_name(buf)] = buf_bps end end local fp = io.open(HOME .. '/.cache/dap/breakpoints.json', 'w') local final = vim.fn.json_encode(bps) fp:write(final) fp:close() end function _G.load_breakpoints() local fp = io.open(HOME .. '/.cache/dap/breakpoints.json', 'r') local content = fp:read('*a') local bps = vim.fn.json_decode(content) local loaded_buffers = {} local found = false for _, buf in ipairs(vim.api.nvim_list_bufs()) do local file_name = vim.api.nvim_buf_get_name(buf) if (bps[file_name] ~= nil and bps[file_name] ~= {}) then found = true end loaded_buffers[file_name] = buf end if (found == false) then return end for path, buf_bps in pairs(bps) do for _, bp in pairs(buf_bps) do local line = bp.line local opts = { condition = bp.condition, log_message = bp.logMessage, hit_condition = bp.hitCondition } breakpoints.set(opts, tonumber(loaded_buffers[path]), line) end end endThen I trigger the storing at the same time as a breakpoint toggle or clear
vim.keymap.set( {'n', 'i', 'v'}, '<F3>', '<cmd>lua require"dap".clear_breakpoints();store_breakpoints(true)<CR>' ) vim.keymap.set( {'n', 'i', 'v'}, '<F4>', '<cmd>lua require"dap".toggle_breakpoint();store_breakpoints(false)<CR>' )and I load the breakpoints with an autocommand everytime a file opens
autocmd BufRead * :lua load_breakpoints()This could probably be cleaned up because this is my first time writing lua but so far its been working pretty well for me
Hey! Thanks for your solution! I faced some bugs with this implementation, so I decided to rewrite it in a more concise manner. Please, check it out in my repo if you are interested
Haha I'll have to check it out because I've also had annoying bugs and have been too lazy to fix them. Thanks!
I write a lua plugin for persistent checkpoints. https://github.com/Weissle/persistent-breakpoints.nvim
Why not provide commands to export and import breakpoints? A better solution would be to record the actions in nvim-dap and let the user edit them.
The ideal solution can transpile the simple things between gdb/lldb and nvim-dap (probably only in one direction is feasible).