return { "mfussenegger/nvim-dap", dependencies = { { "nvim-neotest/nvim-nio", lazy = false }, "rcarriga/nvim-dap-ui", "jay-babu/mason-nvim-dap.nvim", "mfussenegger/nvim-dap-python", "theHamsta/nvim-dap-virtual-text", "nvim-telescope/telescope-dap.nvim", "Weissle/persistent-breakpoints.nvim", { "nvim-neotest/neotest", dependencies = { "nvim-neotest/neotest-jest", "nvim-neotest/neotest-python", "nvim-lua/plenary.nvim", "antoinemadec/FixCursorHold.nvim", }, config = function() require("neotest").setup({ adapters = { require("neotest-jest")({}), require("neotest-python")({}), }, }) end, }, }, config = function() local dap = require("dap") local dapui = require("dapui") local widgets = require("dap.ui.widgets") local api, fn = vim.api, vim.fn local keymap = vim.keymap.set -- ๐ŸŽฏ Fixed Layout UI (no floating) dapui.setup({ layouts = { { elements = { { id = "scopes", size = 0.25 }, { id = "breakpoints", size = 0.25 }, { id = "stacks", size = 0.25 }, { id = "watches", size = 0.25 }, }, size = 40, -- width (left) position = "left", }, { elements = { { id = "repl", size = 0.5 }, { id = "console", size = 0.5 }, }, size = 10, -- height (bottom) position = "bottom", }, }, controls = { enabled = true, element = "repl", icons = { pause = "โธ", play = "โ–ถ", step_into = "โคต", step_over = "โญ", step_out = "โคด", step_back = "โฎ", run_last = "๐Ÿ”", terminate = "โน", }, }, floating = { border = "rounded", mappings = { close = { "q", "" } } }, windows = { indent = 1 }, }) -- ๐Ÿง  Mason DAP require("mason-nvim-dap").setup({ ensure_installed = { "node2", "chrome", "firefox" }, automatic_setup = true, }) -- ๐Ÿ” Virtual Text require("nvim-dap-virtual-text").setup({ commented = true, highlight_changed_variables = true, highlight_new_as_changed = true, virt_text_pos = "eol", all_frames = true, }) -- ๐Ÿ’พ Persistent Breakpoints require("persistent-breakpoints").setup({ load_breakpoints_event = { "BufReadPost" }, }) api.nvim_create_autocmd("VimLeavePre", { callback = function() local ok, pb = pcall(require, "persistent-breakpoints.api") if ok and type(pb.save_breakpoints) == "function" then pb.save_breakpoints() end end, }) -- ๐Ÿ” Auto-open/close UI on debug events dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close() end dap.listeners.before.event_exited["dapui_config"] = function() dapui.close() end -- ๐ŸŽฎ Keymaps keymap("n", "dc", dap.continue, { desc = "โ–ถ Start Debugging" }) keymap("n", "do", dap.step_over, { desc = "โญ Step Over" }) keymap("n", "di", dap.step_into, { desc = "โคต Step Into" }) keymap("n", "dot", dap.step_out, { desc = "โคด Step Out" }) keymap("n", "db", dap.toggle_breakpoint, { desc = "๐Ÿ”ด Toggle Breakpoint" }) keymap("n", "dB", function() dap.set_breakpoint(fn.input("Breakpoint condition: ")) end, { desc = "โš  Conditional Breakpoint" }) keymap("n", "dr", dap.repl.open, { desc = "๐Ÿ’ฌ Open REPL" }) keymap("n", "dl", dap.run_last, { desc = "๐Ÿ” Run Last Debug" }) keymap("n", "du", dapui.toggle, { desc = "๐Ÿงฉ Toggle DAP UI" }) keymap("n", "dq", dap.terminate, { desc = "โ›” Stop Debugging" }) -- ๐Ÿงผ Reset UI keymap("n", "drt", function() dap.terminate() dapui.close() vim.defer_fn(function() dapui.open() end, 200) end, { desc = "๐Ÿงผ Reset DAP UI Layout" }) -- ๐Ÿ”ญ Telescope Integration require("telescope").load_extension("dap") keymap("n", "dcf", "Telescope dap configurations", { desc = "๐Ÿ”ญ DAP Configs" }) keymap("n", "dcb", "Telescope dap list_breakpoints", { desc = "๐Ÿงท List Breakpoints" }) keymap("n", "dco", "Telescope dap commands", { desc = "โš™๏ธ DAP Commands" }) -- ๐Ÿงฟ Sign Icons for name, icon in pairs({ DapBreakpoint = "๐Ÿ”ด", DapBreakpointCondition = "โš ๏ธ", DapBreakpointRejected = "๐Ÿšซ", DapLogPoint = "๐Ÿ’ฌ", DapStopped = "โ–ถ", }) do fn.sign_define(name, { text = icon, texthl = "DiagnosticSignInfo", linehl = "", numhl = "" }) end -- ๐Ÿ” NestJS Smart Watcher (optional) api.nvim_create_autocmd("BufWritePost", { pattern = "*.ts", callback = function() if fn.filereadable(".nvim/project.lua") == 1 then local config = loadfile(".nvim/project.lua")() if config and config.run and config.run:match("nest") then -- Your custom logic here end end end, }) -- โ˜• Java Debug Adapter dap.adapters.java = function(callback) callback({ type = "server", host = "127.0.0.1", port = { port } }) end dap.configurations.java = { { name = "Attach to running Java process", type = "java", request = "attach", hostName = "127.0.0.1", port = { port }, }, } -- ๐Ÿง  Node.js (NestJS / TypeScript) dap.adapters.node2 = { type = "executable", command = "node", args = { os.getenv("HOME") .. "/.local/share/nvim/mason/packages/node-debug2-adapter/out/src/nodedebug.js", }, } dap.configurations.typescript = { { name = "Launch NestJS", type = "node2", request = "launch", program = "${workspaceFolder}/dist/main.js", args = {}, console = "integratedTerminal", outFiles = { "${workspaceFolder}/dist/**/*.js" }, sourceMaps = true, protocol = "inspector", cwd = fn.getcwd(), runtimeArgs = { "--inspect-brk" }, restart = true, }, { name = "Attach to NestJS (start:debug)", type = "node2", request = "attach", port = 9229, protocol = "inspector", cwd = fn.getcwd(), sourceMaps = true, outFiles = { "${workspaceFolder}/dist/**/*.js" }, skipFiles = { "/**" }, }, } end, }