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", "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 / 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 end }