return { { "mfussenegger/nvim-dap", event = "VeryLazy", dependencies = { "leoluz/nvim-dap-go", }, config = function() local dap = require("dap") -- Find project root (where go.mod is) local function find_project_root() local current = vim.fn.getcwd() local function find_go_mod(dir) if vim.fn.filereadable(dir .. "/go.mod") == 1 then return dir end local parent = vim.fn.fnamemodify(dir, ":h") if parent == dir or parent == "" then return nil end return find_go_mod(parent) end return find_go_mod(current) end -- Find all main.go files in project local function find_main_files(root) -- Use vim glob to find main.go files (excludes vendor) local pattern = root .. "/**/main.go" local files = vim.fn.glob(pattern, true, true) -- Filter out vendor local main_files = {} for _, f in ipairs(files) do if not string.match(f, "/vendor/") then table.insert(main_files, f) end end return main_files end -- Store last args per main file local last_args = {} -- Custom debug function with Telescope picker local function debug_go_custom() local root = find_project_root() if not root then vim.notify("Could not find project root (go.mod)", vim.log.levels.ERROR) return end local main_files = find_main_files(root) if #main_files == 0 then vim.notify("No main.go files found", vim.log.levels.WARN) return end -- Use Telescope to pick local telescope = require("telescope") local actions = require("telescope.actions") local pickers = require("telescope.pickers") local finders = require("telescope.finders") local conf = require("telescope.config").values -- Format: relative path + last args local entries = {} for _, f in ipairs(main_files) do local rel = vim.fn.fnamemodify(f, ":.") local args = last_args[f] or "" table.insert(entries, { path = f, display = rel .. (args ~= "" and (" [" .. args .. "]") or "") }) end pickers.new({}, { prompt_title = "Select main.go", finder = finders.new_table({ results = entries, entry_maker = function(entry) return { value = entry.path, display = entry.display, ordinal = entry.display, } end, }), sorter = conf.generic_sorter({}), attach_mappings = function(prompt_bufnr) actions.select_default:replace(function() local selection = require("telescope.actions.state").get_selected_entry(prompt_bufnr) local main_path = selection.value -- Get last args or empty local default_args = last_args[main_path] or "" vim.api.nvim_buf_delete(prompt_bufnr, { force = true }) -- Ask for args vim.ui.input({ prompt = "Args: ", default = default_args, }, function(input) if input ~= nil then -- Save args last_args[main_path] = input -- Parse args into table local args_tbl = {} if input ~= "" then for w in string.gmatch(input, "[^%s]+") do table.insert(args_tbl, w) end end -- Build config local config = { type = "go", name = "Debug: " .. vim.fn.fnamemodify(main_path, ":t"), request = "launch", program = main_path, cwd = root, outputMode = "remote", args = args_tbl, } -- Check if already running require("dap-go").setup() -- Ensure Go adapter is registered if dap.session() then dap.terminate(nil, nil, function() dap.run(config) end) else dap.run(config) end end end) end) return true end, }):find() end -- Go adapter configuration dap.configurations.go = { { type = "go", name = "Debug", request = "launch", program = "${file}", outputMode = "remote", }, { type = "go", name = "Debug test", request = "launch", mode = "test", program = "${file}", outputMode = "remote", }, { type = "go", name = "Debug test (package)", request = "launch", mode = "test", program = "${workspaceFolder}", outputMode = "remote", }, } -- Register custom command vim.api.nvim_create_user_command("DapGoCustom", debug_go_custom, {}) end, keys = { { "", function() require("dap").continue() end, desc = "Debug continue", }, { "", function() -- S-F5 require("dap").restart() end, desc = "Debug restart", }, { "", function() -- C-F5 require("dap").terminate() end, desc = "Debug terminate", }, { "", function() require("dap").step_over() end, desc = "Debug step over", }, { "", function() require("dap").step_into() end, desc = "Debug step into", }, { "", function() -- S-F7 require("dap").step_out() end, desc = "Debug step out", }, { "dd", function() vim.cmd("DapGoCustom") end, desc = "Debug Go (custom)", }, { "", function() require("dap").toggle_breakpoint() end, desc = "Toggle breakpoint", }, { "", function() local widgets = require("dap.ui.widgets") widgets.centered_float(widgets.scopes) end, desc = "Debug scopes", }, }, }, { "rcarriga/nvim-dap-ui", event = "VeryLazy", dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio", }, config = function() local dapui = require("dapui") dapui.setup({ icons = { expanded = "▼", collapsed = "▶", current_frame = "→", }, controls = { enabled = false, }, mappings = { open = "o", remove = "d", edit = "e", repl = "r", toggle = "t", }, expand_lines = vim.fn.has("nvim-0.7"), layouts = { { elements = { "repl", "scopes", }, size = 0.3, position = "bottom", }, }, floating = { max_height = nil, max_width = nil, border = "single", mappings = { close = { "q", "" }, }, }, windows = { indent = 1 }, render = { max_type_length = nil, }, }) local dap = require("dap") dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end end, keys = { { "", function() require("dapui").toggle() end, desc = "Toggle debug UI", }, { "dh", function() require("dap.ui.widgets").hover() end, desc = "Debug hover", }, { "dp", function() require("dap.ui.widgets").preview() end, desc = "Debug preview", }, }, }, { "leoluz/nvim-dap-go", dependencies = { "mfussenegger/nvim-dap" }, ft = "go", keys = { { "dt", function() require("dap-go").debug_test() end, desc = "Debug test", }, }, }, }