Files
nvim/lua/plugins/debugger.lua
T
2026-10-23 08:57:19 -04:00

331 lines
13 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local function fzf_pick_executable()
return coroutine.create(function(coro)
local current_cwd = vim.fn.getcwd():gsub("\\", "/")
require("fzf-lua").files({
cwd = current_cwd,
cmd = "fd --type x --hidden --no-ignore",
previewer = false,
header = false,
prompt = "Select Executable ❯ ",
winopts = {
title = " Select Executable ",
title_pos = "center",
height = 0.40,
width = 0.60,
},
actions = {
["default"] = function(selected)
if selected and selected[1] then
-- 1. Strip icons and formatting spaces to isolate the file string
local entry = require("fzf-lua.path").entry_to_file(selected[1], { cwd = current_cwd })
-- 2. Automatically resolve it to an absolute path (handles relative or absolute safely)
local full_path = vim.fn.fnamemodify(entry.path, ":p")
-- 3. Clean up slashes and hand it to DAP
coroutine.resume(coro, full_path:gsub("\\", "/"))
else
coroutine.resume(coro, vim.fn.empty())
end
end
}
})
end)
end
local function start_debug_session()
local is_windows = vim.fn.has("win32") == 1
local dap = require("dap")
-- 3. Platform-agnostic Launcher Configuration
if not is_windows then
dap.configurations.cpp = {
{
name = "Launch file",
type = "codelldb",
request = "launch",
program = fzf_pick_executable,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
},
}
else
local vscode = require('dap.ext.vscode')
local launch_json = vim.fn.getcwd() .. "/.vscode/launch.json"
if vim.fn.filereadable(launch_json) == 1 then
dap.configurations.cpp = {}
dap.configurations.c = {}
-- 1. CRUCIAL FIX: Alias the adapters directly so nvim-dap doesn't complain about a missing engine
--if dap.adapters.cppdbg then
-- dap.adapters.cppvsdbg = dap.adapters.cppdbg
--elseif dap.adapters.codelldb then
-- dap.adapters.cppvsdbg = dap.adapters.codelldb
--end
-- 2. Map types inside launch.json to your C++ filetypes
vscode.type_to_filetypes = {
cppvsdbg = { "cpp", "c" },
cppdbg = { "cpp", "c" },
codelldb = { "cpp", "c" },
}
else
-- No launch.json exists. Re-inject your FZF picker template rules
dap.configurations.cpp = {
{
name = "Launch file",
type = "cppdbg",
request = "launch",
program = fzf_pick_executable,
cwd = "${workspaceFolder}",
stopOnEntry = false,
-- Crucial configurations for Microsoft cpptools:
-- On Windows, this automatically uses MinGW/MSVC underlying debugger engines
MIMode = is_windows and "gdb" or "lldb",
miDebuggerPath = is_windows and "gdb.exe" or nil, -- Uses system path for gdb on Windows
setupCommands = {
{
text = "-enable-pretty-printing",
description = "enable pretty printing",
ignoreFailures = false,
},
},
},
}
end
end
dap.continue()
end
vim.keymap.set("n", "<leader>cdc", start_debug_session, { desc = "Debug: Start/Continue with Smart Config" })
return {
"mfussenegger/nvim-dap",
dependencies = {
"rcarriga/nvim-dap-ui",
"theHamsta/nvim-dap-virtual-text",
"nvim-neotest/nvim-nio",
"williamboman/mason.nvim",
"jay-babu/mason-nvim-dap.nvim",
},
config = function()
local dap = require("dap")
local dapui = require("dapui")
-- Initialize UI and virtual text components
dapui.setup()
require("nvim-dap-virtual-text").setup()
-- The dap-ui "disconnect" control (the plug icon) and dap.disconnect in
-- general send a DAP `disconnect` request. With the codelldb server
-- adapter that path crashes the adapter
-- (STATUS_ACCESS_VIOLATION / 0xC0000005, see dap.log) while the inferior
-- is still attached, and on Windows that takes Neovim down with it.
--
-- The `terminate` request path (the square Stop button / <leader>cdt)
-- tears the inferior down cleanly first, so codelldb's crash on the way
-- out is harmless. Route the disconnect control through terminate too.
-- (_G._dapui is created when dapui.controls is required; force-load it so
-- the override is in place before the controls winbar renders.)
require("dapui.controls")
_G._dapui.disconnect = function()
dap.terminate()
end
-- Automatically manage debugger installations via Mason
require("mason-nvim-dap").setup({
ensure_installed = { "codelldb", "cppdbg" }, -- Auto-installs CodeLLDB
automatic_installation = true,
})
-- Automatically open/close DAP UI panels
-- Defer the open so nvim-dap-ui's async (nio) element buffers are ready.
-- Opening synchronously from inside the DAP event listener races buffer
-- creation and throws "layout.lua: Invalid buffer id: -1".
dap.listeners.after.event_initialized["dapui_config"] = function()
vim.schedule(function() dapui.open() end)
end
dap.listeners.before.event_terminated["dapui_config"] = function()
vim.schedule(function() dapui.close() end)
end
dap.listeners.before.event_exited["dapui_config"] = function()
vim.schedule(function() dapui.close() end)
end
-- Step 2 & 3 configurations will go here...
-- 1. Detect operating system
local is_windows = vim.fn.has("win32") == 1
local ext = is_windows and ".exe" or ""
-- 2. Platform-agnostic CodeLLDB Adapter setup
local mason_registry = require("mason-registry")
local codelldb = mason_registry.get_package("codelldb")
local extension_path = codelldb:get_install_path() .. "/extension/"
-- CodeLLDB executable is in different subfolders depending on Mason's package version
local codelldb_path = extension_path .. "adapter/codelldb" .. ext
local cpptools_pkg = mason_registry.get_package("cpptools")
local cpptools_path = cpptools_pkg:get_install_path()
.. "/extension/debugAdapters/bin/OpenDebugAD7"
.. ext
dap.adapters.codelldb = {
type = 'server',
port = "${port}",
executable = {
command = codelldb_path,
args = {"--port", "${port}"},
-- Keep the spawned server (and any inferior it launches) off
-- Neovim's console group so terminating a session can't take
-- Neovim down on Windows. Matches the cppvsdbg adapter above.
detached = true,
}
}
dap.adapters.cppdbg = {
id = "cppdbg",
type = "executable",
command = cpptools_path,
-- Must stay detached (the default) so the adapter + gdb + debuggee
-- run in their own process group. With detached=false on Windows
-- they share Neovim's console group, so terminating the session
-- (dap.terminate) sends the Ctrl-C/terminate signal to Neovim too.
options = {
detached = true,
},
}
-- Route MSVC `cppvsdbg` launch configs (as VS/msbuild launch.json
-- generates) to codelldb. We deliberately do NOT use Microsoft's vsdbg:
-- its license permits use only within Visual Studio / VS Code. codelldb
-- is LLVM/LLDB-based (Apache-2.0) and has a native PDB reader, so it can
-- debug MSVC/msbuild binaries without that restriction.
--
-- codelldb is a TCP `server` adapter, so terminating/disconnecting
-- talks over a socket and can't broadcast a console CTRL event to
-- Neovim the way the gdb-backed cppdbg path does. detached=true keeps
-- the spawned server (and the inferior it launches) off Neovim's
-- console group too -- the original `detached = false` is what still
-- let a disconnect take Neovim down.
dap.adapters.cppvsdbg = {
type = "server",
port = "${port}",
executable = {
command = codelldb_path,
args = { "--port", "${port}" },
detached = true,
},
}
-- 3. Platform-agnostic Launcher Configuration
if not is_windows then
dap.configurations.cpp = {
{
name = "Launch file",
type = "codelldb",
request = "launch",
program = fzf_pick_executable,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
},
}
else
dap.configurations.cpp = {
{
name = "Launch file",
type = "cppdbg",
request = "launch",
program = fzf_pick_executable,
cwd = "${workspaceFolder}",
stopOnEntry = false,
-- Crucial configurations for Microsoft cpptools:
-- On Windows, this automatically uses MinGW/MSVC underlying debugger engines
MIMode = is_windows and "gdb" or "lldb",
miDebuggerPath = is_windows and "gdb.exe" or nil, -- Uses system path for gdb on Windows
setupCommands = {
{
text = "-enable-pretty-printing",
description = "enable pretty printing",
ignoreFailures = false,
},
},
},
}
end
dap.configurations.c = dap.configurations.cpp
-- Unity (Mono soft debugger) via Unity's MIT-licensed vscode-unity-debug
-- adapter, extracted from its .vsix into stdpath("data")/unity-debug.
-- Avoids vstuc, whose license (like vsdbg's) is VS Code only.
--
-- UnityDebug.exe is patched by unity-debug/install.sh (see patch.cs there;
-- original kept as UnityDebug.exe.orig): stock, it only matches editor
-- processes named exactly "Unity"/"Unity Editor", but Unity Hub launches
-- the editor as "unityhub-unity-editor-<version>". The patch makes it a
-- case-insensitive "Unity" prefix match, covering both launch methods and
-- any version. Helper processes (UnityPackageManager, import workers...)
-- match too, so the adapter picks the real editor by the process_id in
-- EditorInstance.json -- which needs the config name to contain "Editor".
local unity_debug_exe = vim.fn.stdpath("data") .. "/unity-debug/extension/bin/UnityDebug.exe"
local unity_debug_install = vim.fn.stdpath("config") .. "/unity-debug/install.sh"
dap.adapters.unity = function(callback)
if vim.fn.filereadable(unity_debug_exe) == 0 then
vim.notify("Installing Unity debug adapter...")
local result = vim.system({ unity_debug_install }, { text = true }):wait()
if result.code ~= 0 then
vim.notify("Unity debug adapter install failed:\n" .. result.stderr, vim.log.levels.ERROR)
return
end
end
callback({
type = "executable",
command = "mono",
args = { unity_debug_exe },
-- The adapter strips the leading "/" from `path` (a Windows-ism)
-- before reading it, so run it from / for the now-relative path
-- to resolve.
options = { cwd = "/" },
})
end
-- Library/EditorInstance.json only exists while the editor is open; the
-- adapter reads the editor PID from it to derive the debugger port.
-- The Unity project may sit in a repo subfolder, so search upward from
-- the buffer first, then downward from cwd.
local function unity_editor_instance()
local root = vim.fs.root(0, "ProjectSettings")
if root then
return root .. "/Library/EditorInstance.json"
end
local found = vim.fs.find("EditorInstance.json", { path = vim.fn.getcwd(), type = "file", limit = 1 })
return found[1]
end
dap.configurations.cs = {
{
name = "Unity Editor",
type = "unity",
request = "launch", -- unity-debug attaches to the running editor despite "launch"
path = unity_editor_instance,
},
}
end
}