diff --git a/.config/nvim/after/plugin/dap.lua b/.config/nvim/after/plugin/dap.lua index 1fcfbb7..a085cda 100644 --- a/.config/nvim/after/plugin/dap.lua +++ b/.config/nvim/after/plugin/dap.lua @@ -3,139 +3,42 @@ local dapui = require("dapui") dapui.setup() -dap.listeners.after.event_initialized["dapui_config"] = function() - dapui.open() -end +dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end +dap.listeners.after.event_terminated["dapui_config"] = function() dapui.close() end +dap.listeners.after.event_exited["dapui_config"] = function() dapui.close() end -dap.listeners.after.event_terminated["dapui_config"] = function() - dapui.close() -end +local dotnet = require("dap.dap-dotnet") +local odoo = require("dap.dap-odoo") -dap.listeners.after.event_exited["dapui_config"] = function() - dapui.close() -end -require("dap-python").setup("~/projects/odoo/venv/bin/python3") -dap.adapters['pwa-chrome'] = { - type = 'server', - host = 'localhost', - port = '${port}', - executable = { - command = 'node', - args = { - vim.fn.stdpath('data') .. '/mason/packages/js-debug-adapter/js-debug/src/dapDebugServer.js', - '${port}', - }, - } -} - -local function is_odoo_project() - local cwd = vim.fn.getcwd() - return vim.fn.filereadable(cwd .. "/odoo-bin") == 1 or - vim.fn.filereadable(cwd .. "/odoo/odoo-bin") == 1 -end - -local function debug_odoo() - local db_name = vim.fn.input('Odoo DB: ', 'master') - if db_name == "" then return end - - dap.run({ - type = "python", - request = "launch", - name = "Odoo Server", - program = "/home/admac/projects/odoo/odoo/odoo-bin", - pythonPath = "/home/admac/projects/odoo/venv/bin/python3", - args = { - "--addons-path", "/home/admac/projects/odoo/enterprise/,/home/admac/projects/odoo/odoo/addons/", - "--dev", "all", - "-d", db_name - }, - console = "externalTerminal", - }) -end - -local function run_odoo_test_at_cursor() - -- Safely get the node at cursor - local status, node = pcall(vim.treesitter.get_node) - if not status or not node then - print("Error: Tree-sitter parser not found. Ensure you are in a Python buffer.") +vim.keymap.set("n", "5", function() + if odoo.is_odoo_project() then + odoo.debug_odoo() + return + end + local ft = vim.bo.filetype + if ft == "cs" or ft == "fsharp" or ft == "vb" then + dotnet.debug_dotnet_from_sln() return end - -- Safety Limit: Prevent infinite loop if tree is malformed - local attempts = 0 - while node do - if node:type() == 'function_definition' then - break - end - node = node:parent() - attempts = attempts + 1 - if attempts > 50 then - print("Error: Could not find function definition (traversal limit reached).") - return - end - end - - if not node then - print("Cursor is not inside a function definition.") + if dotnet.find_sln_upwards and dotnet.find_sln_upwards() then + dotnet.debug_dotnet_from_sln() return end - -- Extract function name safely - local func_name = nil - for child in node:iter_children() do - if child:type() == 'identifier' then - func_name = vim.treesitter.get_node_text(child, 0) - break - end - end - - if not func_name then - print("Error: Could not extract function name.") - return - end - - local db = vim.fn.input("Database: ") - if db == nil or db == "" then - print("Canceled (no database provided).") - return - end - - print("Launching Debugger for: " .. func_name) - - dap.run({ - type = "python", - request = "launch", - name = "Debug Test: " .. func_name, - pythonPath = "/home/admac/projects/odoo/venv/bin/python3", - program = "/home/admac/projects/odoo/odoo/odoo-bin", - args = { - "--addons-path", "/home/admac/projects/odoo/enterprise/,/home/admac/projects/odoo/odoo/addons/", - "-d", db, - "--log-level=warn", - "--test-tags", "." .. func_name, - "--stop-after-init", - }, - console = "externalTerminal", - }) -end - -vim.keymap.set("n", "d5", function() - if is_odoo_project() then - debug_odoo() - else - dap.continue() - end + dap.continue() end) -vim.keymap.set("n", "5", function() dap.continue() end) -vim.keymap.set("n", "6", function() dap.step_over() end) -vim.keymap.set("n", "+", function() dap.step_into() end) -vim.keymap.set("n", "-", function() dap.step_out() end) + +vim.keymap.set("n", "", function() dap.continue() end) +vim.keymap.set("n", "", function() dap.step_over() end) +vim.keymap.set("n", "", function() dap.step_into() end) +vim.keymap.set("n", "", function() dap.step_out() end) vim.keymap.set("n", "b", function() dap.toggle_breakpoint() end) vim.keymap.set("n", "B", function() dap.set_breakpoint(vim.fn.input("Breakpoint condition: ")) end) vim.keymap.set("n", "qd", function() dap.terminate() end) vim.keymap.set("n", "rd", function() dap.restart() end) -vim.keymap.set("n", "dt", run_odoo_test_at_cursor, { desc = "Debug Odoo Test unde Cursor" }) +vim.keymap.set("n", "dt", odoo.run_odoo_test_at_cursor, { desc = "Debug Odoo Test unde Cursor" }) dap.configurations.python = { { diff --git a/.config/nvim/lua/dap/dap-dotnet.lua b/.config/nvim/lua/dap/dap-dotnet.lua new file mode 100644 index 0000000..350a5ee --- /dev/null +++ b/.config/nvim/lua/dap/dap-dotnet.lua @@ -0,0 +1,211 @@ +local dap = require("dap") + +local M = {} + +-- Adapter (netcoredbg) +dap.adapters.coreclr = { + type = "executable", + command = vim.fn.stdpath("data") .. "/mason/packages/netcoredbg/netcoredbg", + args = { "--interpreter=vscode" }, +} + +-- -------- helpers -------- + +local function is_windows() + return vim.loop.os_uname().sysname:match("Windows") ~= nil +end + +local function joinpath(...) + local sep = is_windows() and "\\" or "/" + return table.concat({ ... }, sep) +end + +local function parent_dir(path) + return vim.fn.fnamemodify(path, ":h") +end + +local function file_exists(path) + return vim.fn.filereadable(path) == 1 +end + +local function find_csproj_upwards() + local start = vim.api.nvim_buf_get_name(0) + if start ~= "" then + start = parent_dir(start) + else + start = vim.fn.getcwd() + end + + local projs = vim.fs.find(function(name) + return name:match("%.csproj$") + end, { upward = true, path = start, type = "file" }) + + if #projs == 0 then return nil end + if #projs == 1 then return projs[1] end + + -- multiple csproj found upwards: pick one + local display = { "Select project:" } + for _, p in ipairs(projs) do + table.insert(display, vim.fn.fnamemodify(p, ":~")) + end + local choice = vim.fn.inputlist(display) + if choice < 2 then return nil end + return projs[choice - 1] +end + +local function find_sln_upwards() + -- Prefer starting from current file's dir (if any), else cwd + local start = vim.api.nvim_buf_get_name(0) + if start ~= "" then + start = parent_dir(start) + else + start = vim.fn.getcwd() + end + + -- Requires Neovim 0.9+ (vim.fs.find) + local slns = vim.fs.find(function(name) + return name:match("%.sln$") + end, { upward = true, path = start, type = "file" }) + + if #slns == 0 then return nil end + if #slns == 1 then return slns[1] end + + -- Multiple solutions found: pick one + local choice = vim.fn.inputlist(vim.list_extend({ "Select solution:" }, slns)) + if choice < 1 then return nil end + return slns[choice] +end + +local function dotnet_sln_list_projects(sln) + local out = vim.fn.system({ "dotnet", "sln", sln, "list" }) + if vim.v.shell_error ~= 0 then + vim.notify(out, vim.log.levels.ERROR) + return nil + end + + local projects = {} + local sln_dir = parent_dir(sln) + + for line in out:gmatch("[^\r\n]+") do + -- dotnet sln list outputs relative paths like: + -- "src/MyApp/MyApp.csproj" + if line:match("%.csproj%s*$") then + line = line:gsub("^%s+", ""):gsub("%s+$", "") + local full = line + + -- Make absolute if needed + if not full:match("^/") and not full:match("^[A-Za-z]:\\") then + full = joinpath(sln_dir, line) + end + + table.insert(projects, full) + end + end + + if #projects == 0 then + vim.notify("No .csproj entries found in solution list output.", vim.log.levels.ERROR) + return nil + end + + table.sort(projects) + return projects +end + +local function pick_startup_project(projects) + if #projects == 1 then return projects[1] end + + local display = { "Select startup project:" } + for _, p in ipairs(projects) do + table.insert(display, vim.fn.fnamemodify(p, ":~")) + end + + local choice = vim.fn.inputlist(display) + if choice < 2 then return nil end + return projects[choice - 1] +end + +local function dotnet_build(csproj) + vim.notify("dotnet build: " .. vim.fn.fnamemodify(csproj, ":~")) + local out = vim.fn.system({ "dotnet", "build", csproj, "-c", "Debug" }) + if vim.v.shell_error ~= 0 then + vim.notify(out, vim.log.levels.ERROR) + return false + end + return true +end + +local function find_built_dll(csproj) + local proj_dir = parent_dir(csproj) + local proj_name = vim.fn.fnamemodify(csproj, ":t:r") + + -- Find all candidate DLLs under bin/Debug/**/.dll + local glob_pat = joinpath(proj_dir, "bin", "Debug", "*", proj_name .. ".dll") + local matches = vim.fn.glob(glob_pat, false, true) + + if #matches == 0 then + return nil + end + + -- If multiple TFMs, pick the newest by mtime + table.sort(matches, function(a, b) + local sa = vim.loop.fs_stat(a) + local sb = vim.loop.fs_stat(b) + local ta = sa and sa.mtime.sec or 0 + local tb = sb and sb.mtime.sec or 0 + return ta < tb + end) + + return matches[#matches] +end + +-- -------- main -------- + +local function debug_dotnet_from_sln() + local sln = find_sln_upwards() + local csproj = nil + if sln then + local projects = dotnet_sln_list_projects(sln) + if projects then + csproj = pick_startup_project(projects) + end + end + if not csproj then + csproj = find_csproj_upwards() + if not csproj then + vim.notify("No .sln or .csproj found upwards from current file/cwd.", vim.log.levels.WARN) + return + end + end + + + if not dotnet_build(csproj) then + vim.notify("Build failed — not starting debugger.", vim.log.levels.ERROR) + return + end + + local dll = find_built_dll(csproj) + if not dll or not file_exists(dll) then + vim.notify("Could not locate built DLL for: " .. csproj, vim.log.levels.ERROR) + return + end + + dap.run({ + type = "coreclr", + request = "launch", + name = "Debug .NET (sln startup)", + program = dll, + cwd = parent_dir(sln), + console = "integratedTerminal", + stopAtEntry = false, + env = { + ASPNETCORE_ENVIRONMENT = "Development", + DOTNET_ENVIRONMENT = "Development", + }, + }) +end + +-- Export if you want to require it elsewhere +M.debug_dotnet_from_sln = debug_dotnet_from_sln +M.find_sln_upwards = find_sln_upwards + +return M diff --git a/.config/nvim/lua/dap/dap-odoo.lua b/.config/nvim/lua/dap/dap-odoo.lua new file mode 100644 index 0000000..2b7af4a --- /dev/null +++ b/.config/nvim/lua/dap/dap-odoo.lua @@ -0,0 +1,114 @@ +local dap = require("dap") + +local M = {} + +require("dap-python").setup("~/projects/odoo/venv/bin/python3") +dap.adapters['pwa-chrome'] = { + type = 'server', + host = 'localhost', + port = '${port}', + executable = { + command = 'node', + args = { + vim.fn.stdpath('data') .. '/mason/packages/js-debug-adapter/js-debug/src/dapDebugServer.js', + '${port}', + }, + } +} + +local function is_odoo_project() + local cwd = vim.fn.getcwd() + return vim.fn.filereadable(cwd .. "/odoo-bin") == 1 or + vim.fn.filereadable(cwd .. "/odoo/odoo-bin") == 1 +end + +local function debug_odoo() + local db_name = vim.fn.input('Odoo DB: ', 'master') + if db_name == "" then return end + + dap.run({ + type = "python", + request = "launch", + name = "Odoo Server", + program = "/home/admac/projects/odoo/odoo/odoo-bin", + pythonPath = "/home/admac/projects/odoo/venv/bin/python3", + args = { + "--addons-path", "/home/admac/projects/odoo/enterprise/,/home/admac/projects/odoo/odoo/addons/", + "--dev", "all", + "-d", db_name + }, + console = "externalTerminal", + }) +end + +local function run_odoo_test_at_cursor() + -- Safely get the node at cursor + local status, node = pcall(vim.treesitter.get_node) + if not status or not node then + print("Error: Tree-sitter parser not found. Ensure you are in a Python buffer.") + return + end + + -- Safety Limit: Prevent infinite loop if tree is malformed + local attempts = 0 + while node do + if node:type() == 'function_definition' then + break + end + node = node:parent() + attempts = attempts + 1 + if attempts > 50 then + print("Error: Could not find function definition (traversal limit reached).") + return + end + end + + if not node then + print("Cursor is not inside a function definition.") + return + end + + -- Extract function name safely + local func_name = nil + for child in node:iter_children() do + if child:type() == 'identifier' then + func_name = vim.treesitter.get_node_text(child, 0) + break + end + end + + if not func_name then + print("Error: Could not extract function name.") + return + end + + local db = vim.fn.input("Database: ") + if db == nil or db == "" then + print("Canceled (no database provided).") + return + end + + print("Launching Debugger for: " .. func_name) + + dap.run({ + type = "python", + request = "launch", + name = "Debug Test: " .. func_name, + pythonPath = "/home/admac/projects/odoo/venv/bin/python3", + program = "/home/admac/projects/odoo/odoo/odoo-bin", + args = { + "--addons-path", "/home/admac/projects/odoo/enterprise/,/home/admac/projects/odoo/odoo/addons/", + "-d", db, + "--log-level=warn", + "--test-tags", "." .. func_name, + "--stop-after-init", + }, + console = "externalTerminal", + }) +end + +M.is_odoo_project = is_odoo_project +M.debug_odoo = debug_odoo +M.run_odoo_test_at_cursor = run_odoo_test_at_cursor + +return M