-- hazard3_dap.lua — Hazard3 helpers for nvim-dap (GDB DAP) local M = {} local disasm_buf ---@type integer|nil local function ensure_disasm_buf() if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then return disasm_buf end disasm_buf = vim.api.nvim_create_buf(false, true) pcall(vim.api.nvim_buf_set_name, disasm_buf, "[DAP Disassembly]") vim.bo[disasm_buf].buftype = "nofile" vim.bo[disasm_buf].bufhidden = "hide" vim.bo[disasm_buf].swapfile = false vim.bo[disasm_buf].filetype = "asm" vim.bo[disasm_buf].modifiable = true vim.api.nvim_buf_set_lines(disasm_buf, 0, -1, false, { "(DAP) Disassembly" }) vim.bo[disasm_buf].modifiable = false return disasm_buf end local function show_buf(buf) local wins = vim.fn.win_findbuf(buf) if wins and #wins > 0 then vim.api.nvim_set_current_win(wins[1]) vim.wo.cursorline = true return end vim.cmd("vsplit") vim.api.nvim_set_current_buf(buf) vim.wo.cursorline = true end local function update_disassembly(buf, opts) opts = opts or {} local count = opts.count or 80 local before = opts.before or 20 local ok, dap = pcall(require, "dap") if not ok then return end local session = dap.session() if not session then return end session:request("stackTrace", { threadId = 1, startFrame = 0, levels = 1 }, function(err, resp) if err then vim.notify("DAP disasm: stackTrace failed: " .. tostring(err), vim.log.levels.ERROR) return end local frames = resp and resp.stackFrames or {} if #frames == 0 then vim.notify("DAP disasm: no stack frames", vim.log.levels.WARN) return end local frame = frames[1] local memref = frame.instructionPointerReference if memref == nil or memref == "" then vim.notify("DAP disasm: no instructionPointerReference", vim.log.levels.WARN) return end session:request("disassemble", { memoryReference = memref, instructionOffset = -before, instructionCount = count, resolveSymbols = true, }, function(err2, resp2) if err2 then vim.notify("DAP disasm: disassemble failed: " .. tostring(err2), vim.log.levels.ERROR) return end local insts = resp2 and resp2.instructions or {} local lines = {} lines[#lines + 1] = "memref: " .. memref for _, i in ipairs(insts) do local addr = i.address or "?" local ins = i.instruction or "" local mark = (addr == memref) and "=> " or " " lines[#lines + 1] = mark .. addr .. " " .. ins end if not vim.api.nvim_buf_is_valid(buf) then return end vim.bo[buf].modifiable = true vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) vim.bo[buf].modifiable = false local pc_line for idx, line in ipairs(lines) do if line:sub(1, 3) == "=> " then pc_line = idx break end end if pc_line then local wins = vim.fn.win_findbuf(buf) if wins and #wins > 0 then for _, win in ipairs(wins) do pcall(vim.api.nvim_win_set_cursor, win, { pc_line, 0 }) vim.wo[win].cursorline = true end end end end) end) end function M.open_disassembly(opts) local buf = ensure_disasm_buf() local cur_win = vim.api.nvim_get_current_win() show_buf(buf) update_disassembly(buf, opts) if opts and opts.keep_focus then pcall(vim.api.nvim_set_current_win, cur_win) end end function M.setup() local ok, dap = pcall(require, "dap") if not ok then return end if vim.api.nvim_create_user_command then pcall(vim.api.nvim_create_user_command, "DapDisassembly", function() M.open_disassembly() end, {}) end dap.listeners.after.event_stopped["hazard3_disasm"] = function() if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then update_disassembly(disasm_buf, { keep_focus = true }) end end end return M