return { -- Define language plugins at top level to prevent luarocks installation { 'mfussenegger/nvim-dap-python', ft = 'python', rocks = { enabled = false }, }, { 'leoluz/nvim-dap-go', ft = 'go', rocks = { enabled = false }, }, { 'mfussenegger/nvim-dap', dependencies = { 'rcarriga/nvim-dap-ui', 'nvim-neotest/nvim-nio', { 'thehamsta/nvim-dap-virtual-text', opts = {} }, 'mfussenegger/nvim-dap-python', 'leoluz/nvim-dap-go', 'jay-babu/mason-nvim-dap.nvim', }, config = function() local dap = require('dap') local dapui = require('dapui') dapui.setup() -- Mason handles debugger installation and configuration require('mason-nvim-dap').setup({ automatic_installation = true, ensure_installed = { 'codelldb', 'python', 'delve' }, handlers = {}, }) -- Python setup with proper path detection local function get_python_path() -- Check if we're in a virtual environment local venv = os.getenv('VIRTUAL_ENV') if venv then local venv_python = venv .. '/bin/python' if vim.fn.executable(venv_python) == 1 then return venv_python end end -- Fall back to system python3 or python local python3 = vim.fn.exepath('python3') if python3 ~= '' then return python3 end local python = vim.fn.exepath('python') if python ~= '' then return python end -- Last resort - use system default return 'python3' end require('dap-python').setup(get_python_path()) -- Go setup (helper does the heavy lifting) require('dap-go').setup() -- C/C++ configurations dap.configurations.c = { { name = 'Launch', type = 'codelldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, }, { name = 'Launch with ASan', type = 'codelldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, env = { ASAN_OPTIONS = 'detect_leaks=1' }, }, } dap.configurations.cpp = dap.configurations.c -- Rust configuration dap.configurations.rust = { { name = 'Launch', type = 'codelldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/target/debug/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, }, } -- Zig configuration dap.configurations.zig = { { name = 'Launch', type = 'codelldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/zig-out/bin/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, }, } -- Auto-open/close UI dap.listeners.after.event_initialized['dapui_config'] = dapui.open dap.listeners.before.event_terminated['dapui_config'] = dapui.close dap.listeners.before.event_exited['dapui_config'] = dapui.close -- Breakpoint signs vim.fn.sign_define('DapBreakpoint', { text = '🔴', texthl = '', linehl = '', numhl = '' }) vim.fn.sign_define('DapStopped', { text = '➡️', texthl = '', linehl = 'debugPC', numhl = '' }) end, keys = { { 'dc', function() require('dap').continue() end, desc = 'Debug: Continue', }, { 'ds', function() require('dap').step_over() end, desc = 'Debug: Step Over', }, { 'di', function() require('dap').step_into() end, desc = 'Debug: Step Into', }, { 'do', function() require('dap').step_out() end, desc = 'Debug: Step Out', }, { 'db', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint', }, { 'dB', function() require('dap').set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, desc = 'Debug: Conditional Breakpoint', }, { 'dr', function() require('dap').repl.toggle() end, desc = 'Debug: Toggle REPL', }, { 'dl', function() require('dap').run_last() end, desc = 'Debug: Run Last', }, { 'du', function() require('dapui').toggle() end, desc = 'Debug: Toggle UI', }, { 'dt', function() require('dap').terminate() end, desc = 'Debug: Terminate', }, }, ft = { 'python', 'go', 'rust', 'c', 'cpp', 'zig' }, }, }