diff --git a/init.lua b/init.lua index d9af5f14..22f70d6a 100644 --- a/init.lua +++ b/init.lua @@ -974,8 +974,7 @@ require('lazy').setup({ -- Here are some example plugins that I've included in the Kickstart repository. -- Uncomment any of the lines below to enable them (you will need to restart nvim). -- - require 'kickstart.plugins.debug_python', - require 'kickstart.plugins.debug_scala', + require 'kickstart.plugins.debug', -- require 'kickstart.plugins.indent_line', -- require 'kickstart.plugins.lint', -- require 'kickstart.plugins.autopairs', diff --git a/lazy-lock.json b/lazy-lock.json index fcbeb3b8..16560ac9 100644 --- a/lazy-lock.json +++ b/lazy-lock.json @@ -17,7 +17,8 @@ "nvim-dap": { "branch": "master", "commit": "5860c7c501eb428d3137ee22c522828d20cca0b3" }, "nvim-dap-python": { "branch": "master", "commit": "64652d1ae1db80870d9aac7132d76e37acd86a26" }, "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, - "nvim-lspconfig": { "branch": "master", "commit": "b7c48a7111534b66bee077da8035ac7208a294ff" }, + "nvim-lspconfig": { "branch": "master", "commit": "e0fae251f8459940331960106d4bd9457cec23de" }, + "nvim-metals": { "branch": "main", "commit": "40f7b9ea6ded898319136f4d6a94da9487584309" }, "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, "nvim-web-devicons": { "branch": "master", "commit": "8dcb311b0c92d460fac00eac706abd43d94d68af" }, diff --git a/lua/kickstart/plugins/debug.lua b/lua/kickstart/plugins/debug.lua new file mode 100644 index 00000000..c84d66f5 --- /dev/null +++ b/lua/kickstart/plugins/debug.lua @@ -0,0 +1,232 @@ +-- debug.lua +-- +-- Unified debugging configuration for multiple languages. +-- Supports Python (with debugpy) and Scala (with nvim-metals). +-- Can be extended to other languages as well. + +return { + -- Main DAP plugin + 'mfussenegger/nvim-dap', + dependencies = { + -- Creates a beautiful debugger UI + 'rcarriga/nvim-dap-ui', + + -- Required dependency for nvim-dap-ui + 'nvim-neotest/nvim-nio', + + -- Installs the debug adapters for you + 'mason-org/mason.nvim', + 'jay-babu/mason-nvim-dap.nvim', + + -- Python debugging support + 'mfussenegger/nvim-dap-python', + + -- Scala debugging and LSP support + { + 'scalameta/nvim-metals', + ft = { 'scala', 'sbt', 'java' }, + }, + + -- Fidget for LSP progress notifications + { + 'j-hui/fidget.nvim', + opts = {}, + }, + }, + keys = { + -- Unified keymaps for debugging (works for both Python and Scala) + { 'dc', function() require('dap').continue() end, desc = 'Debug: Start/Continue' }, + { 'dr', function() require('dap').repl.toggle() end, desc = 'Debug: Toggle REPL' }, + { 'dK', function() require('dap.ui.widgets').hover() end, desc = 'Debug: Hover Widget' }, + { 'dt', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint' }, + { 'dso', function() require('dap').step_over() end, desc = 'Debug: Step Over' }, + { 'dsi', function() require('dap').step_into() end, desc = 'Debug: Step Into' }, + { 'dl', function() require('dap').run_last() end, desc = 'Debug: Run Last' }, + { 'du', function() require('dapui').toggle() end, desc = 'Debug: Toggle DAP UI' }, + { 'dB', function() require('dap').set_breakpoint(vim.fn.input 'Breakpoint condition: ') end, desc = 'Debug: Set Conditional Breakpoint' }, + }, + config = function() + local dap = require 'dap' + local dapui = require 'dapui' + + -- Mason DAP setup for automatic debugger installation + require('mason-nvim-dap').setup { + automatic_installation = true, + handlers = {}, + ensure_installed = { + 'debugpy', -- Python debugger + }, + } + + -- DAP UI setup + dapui.setup { + icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, + controls = { + icons = { + pause = '⏸', + play = '▶', + step_into = '⏎', + step_over = '⏭', + step_out = '⏮', + step_back = 'b', + run_last = '▶▶', + terminate = '⏹', + disconnect = '⏏', + }, + }, + } + + -- Auto-open/close DAP 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 + + -- === PYTHON DEBUGGING SETUP === + local function setup_python_debugging() + -- Function to find the best Python executable with debugpy + local function find_python_with_debugpy() + local python_candidates = { + '~/.config/nvim/.venv/bin/python', -- nvim config venv + vim.fn.exepath('python3'), -- system python3 + vim.fn.exepath('python'), -- system python + } + + for _, python_path in ipairs(python_candidates) do + if python_path and python_path ~= '' and vim.fn.executable(python_path) == 1 then + -- Test if this python has debugpy + local handle = io.popen(python_path .. ' -c "import debugpy; print(debugpy.__file__)" 2>/dev/null') + if handle then + local result = handle:read('*a') + handle:close() + if result and result:match('debugpy') then + return python_path + end + end + end + end + + -- Fallback to the nvim config venv + return '~/.config/nvim/.venv/bin/python' + end + + require('dap-python').setup(find_python_with_debugpy()) + + -- Add Python debugging configurations + table.insert(dap.configurations.python, { + type = 'python', + request = 'launch', + name = 'Launch file', + program = '${file}', + pythonPath = function() + local cwd = vim.fn.getcwd() + if vim.fn.executable(cwd .. '/venv/bin/python') == 1 then + return cwd .. '/venv/bin/python' + elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then + return cwd .. '/.venv/bin/python' + else + return '/usr/bin/python' + end + end, + }) + end + + -- === SCALA DEBUGGING SETUP === + local function setup_scala_debugging() + -- Scala DAP configurations + dap.configurations.scala = { + { + type = 'scala', + request = 'launch', + name = 'RunOrTest', + metals = { + runType = 'runOrTestFile', + }, + }, + { + type = 'scala', + request = 'launch', + name = 'Test Target', + metals = { + runType = 'testTarget', + }, + }, + } + end + + -- === METALS (SCALA LSP) SETUP === + local function setup_metals() + local metals_config = require('metals').bare_config() + + metals_config.settings = { + showImplicitArguments = true, + gradleScript = vim.fn.getcwd() .. '/gradlew', + customProjectRoot = vim.fn.getcwd(), + excludedPackages = { 'akka.actor.typed.javadsl', 'com.github.swagger.akka.javadsl' }, + } + + metals_config.init_options.statusBarProvider = 'off' + -- metals_config.capabilities = require('cmp_nvim_lsp').default_capabilities() + + metals_config.on_attach = function(client, bufnr) + require('metals').setup_dap() + + -- Helper function for buffer-local keymaps + local function map(mode, lhs, rhs, opts) + opts = opts or {} + opts.buffer = bufnr + vim.keymap.set(mode, lhs, rhs, opts) + end + + -- LSP mappings (Scala-specific) + map('n', 'gD', vim.lsp.buf.definition) + map('n', 'K', vim.lsp.buf.hover) + map('n', 'gi', vim.lsp.buf.implementation) + map('n', 'gr', vim.lsp.buf.references) + map('n', 'gds', vim.lsp.buf.document_symbol) + map('n', 'gws', vim.lsp.buf.workspace_symbol) + map('n', 'cl', vim.lsp.codelens.run) + map('n', 'sh', vim.lsp.buf.signature_help) + map('n', 'rn', vim.lsp.buf.rename) + map('n', 'f', vim.lsp.buf.format) + map('n', 'ca', vim.lsp.buf.code_action) + + -- Scala worksheet support + map('n', 'ws', function() + require('metals').hover_worksheet() + end) + + -- Diagnostic mappings + map('n', 'aa', vim.diagnostic.setqflist) -- all workspace diagnostics + map('n', 'ae', function() -- all workspace errors + vim.diagnostic.setqflist { severity = 'E' } + end) + map('n', 'aw', function() -- all workspace warnings + vim.diagnostic.setqflist { severity = 'W' } + end) + map('n', 'd', vim.diagnostic.setloclist) -- buffer diagnostics only + + map('n', '[c', function() + vim.diagnostic.goto_prev { wrap = false } + end) + map('n', ']c', function() + vim.diagnostic.goto_next { wrap = false } + end) + end + + -- Auto-attach Metals for Scala files + local nvim_metals_group = vim.api.nvim_create_augroup('nvim-metals', { clear = true }) + vim.api.nvim_create_autocmd('FileType', { + pattern = { 'scala', 'sbt', 'java' }, + callback = function() + require('metals').initialize_or_attach(metals_config) + end, + group = nvim_metals_group, + }) + end + + -- Initialize debugging support for all languages + setup_python_debugging() + setup_scala_debugging() + setup_metals() + end, +} \ No newline at end of file diff --git a/lua/kickstart/plugins/debug_python.lua b/lua/kickstart/plugins/debug_python.lua deleted file mode 100644 index f9d242cb..00000000 --- a/lua/kickstart/plugins/debug_python.lua +++ /dev/null @@ -1,147 +0,0 @@ --- debug.lua --- --- Shows how to use the DAP plugin to debug your code. --- --- Configured for Python debugging with debugpy. --- Can be extended to other languages as well. - -return { - -- NOTE: Yes, you can install new plugins here! - 'mfussenegger/nvim-dap', - -- NOTE: And you can specify dependencies as well - dependencies = { - -- Creates a beautiful debugger UI - 'rcarriga/nvim-dap-ui', - - -- Required dependency for nvim-dap-ui - 'nvim-neotest/nvim-nio', - - -- Installs the debug adapters for you - 'mason-org/mason.nvim', - 'jay-babu/mason-nvim-dap.nvim', - - -- Python debugging support - 'mfussenegger/nvim-dap-python', - }, - keys = { - -- Keymaps aligned with debug_scala.lua - { 'dc', function() require('dap').continue() end, desc = 'Debug: Start/Continue' }, - { 'dr', function() require('dap').repl.toggle() end, desc = 'Debug: Toggle REPL' }, - { 'dK', function() require('dap.ui.widgets').hover() end, desc = 'Debug: Hover Widget' }, - { 'dt', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint' }, - { 'dso', function() require('dap').step_over() end, desc = 'Debug: Step Over' }, - { 'dsi', function() require('dap').step_into() end, desc = 'Debug: Step Into' }, - { 'dl', function() require('dap').run_last() end, desc = 'Debug: Run Last' }, - { 'du', function() require('dapui').toggle() end, desc = 'Debug: Toggle DAP UI' }, - { 'dB', function() require('dap').set_breakpoint(vim.fn.input 'Breakpoint condition: ') end, desc = 'Debug: Set Conditional Breakpoint' }, - }, - config = function() - local dap = require 'dap' - local dapui = require 'dapui' - - require('mason-nvim-dap').setup { - -- Makes a best effort to setup the various debuggers with - -- reasonable debug configurations - automatic_installation = true, - - -- You can provide additional configuration to the handlers, - -- see mason-nvim-dap README for more information - handlers = {}, - - -- You'll need to check that you have the required things installed - -- online, please don't ask me how to install them :) - ensure_installed = { - -- Update this to ensure that you have the debuggers for the langs you want - 'debugpy', - }, - } - - -- Dap UI setup - -- For more information, see |:help nvim-dap-ui| - dapui.setup { - -- Set icons to characters that are more likely to work in every terminal. - -- Feel free to remove or use ones that you like more! :) - -- Don't feel like these are good choices. - icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, - controls = { - icons = { - pause = '⏸', - play = '▶', - step_into = '⏎', - step_over = '⏭', - step_out = '⏮', - step_back = 'b', - run_last = '▶▶', - terminate = '⏹', - disconnect = '⏏', - }, - }, - } - - -- Change breakpoint icons - -- vim.api.nvim_set_hl(0, 'DapBreak', { fg = '#e51400' }) - -- vim.api.nvim_set_hl(0, 'DapStop', { fg = '#ffcc00' }) - -- local breakpoint_icons = vim.g.have_nerd_font - -- and { Breakpoint = '', BreakpointCondition = '', BreakpointRejected = '', LogPoint = '', Stopped = '' } - -- or { Breakpoint = '●', BreakpointCondition = '⊜', BreakpointRejected = '⊘', LogPoint = '◆', Stopped = '⭔' } - -- for type, icon in pairs(breakpoint_icons) do - -- local tp = 'Dap' .. type - -- local hl = (type == 'Stopped') and 'DapStop' or 'DapBreak' - -- vim.fn.sign_define(tp, { text = icon, texthl = hl, numhl = hl }) - -- end - - 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 - - -- Install Python specific config - -- Function to find the best Python executable with debugpy - local function find_python_with_debugpy() - local python_candidates = { - '~/.config/nvim/.venv/bin/python', -- nvim config venv - vim.fn.exepath('python3'), -- system python3 - vim.fn.exepath('python'), -- system python - } - - for _, python_path in ipairs(python_candidates) do - if python_path and python_path ~= '' and vim.fn.executable(python_path) == 1 then - -- Test if this python has debugpy - local handle = io.popen(python_path .. ' -c "import debugpy; print(debugpy.__file__)" 2>/dev/null') - if handle then - local result = handle:read('*a') - handle:close() - if result and result:match('debugpy') then - return python_path - end - end - end - end - - -- Fallback to the nvim config venv (we just installed debugpy there) - return '~/.config/nvim/.venv/bin/python' - end - - require('dap-python').setup(find_python_with_debugpy()) - - -- Add Python debugging configurations - table.insert(dap.configurations.python, { - type = 'python', - request = 'launch', - name = 'Launch file', - program = '${file}', - pythonPath = function() - -- debugpy supports launching an application with a different interpreter - -- The code below looks for a `venv` or `.venv` folder in the current directly and uses the python within. - -- You could adapt this - to for example use the `VIRTUAL_ENV` environment variable. - local cwd = vim.fn.getcwd() - if vim.fn.executable(cwd .. '/venv/bin/python') == 1 then - return cwd .. '/venv/bin/python' - elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then - return cwd .. '/.venv/bin/python' - else - return '/usr/bin/python' - end - end, - }) - end, -} diff --git a/lua/kickstart/plugins/debug_scala.lua b/lua/kickstart/plugins/debug_scala.lua deleted file mode 100644 index 40532637..00000000 --- a/lua/kickstart/plugins/debug_scala.lua +++ /dev/null @@ -1,129 +0,0 @@ -return { - 'scalameta/nvim-metals', - dependencies = { - { - 'j-hui/fidget.nvim', - opts = {}, - }, - { - 'mfussenegger/nvim-dap', - config = function(self, opts) - -- Debug settings if you're using nvim-dap - local dap = require 'dap' - - dap.configurations.scala = { - { - type = 'scala', - request = 'launch', - name = 'RunOrTest', - metals = { - runType = 'runOrTestFile', - --args = { "firstArg", "secondArg", "thirdArg" }, -- here just as an example - }, - }, - { - type = 'scala', - request = 'launch', - name = 'Test Target', - metals = { - runType = 'testTarget', - }, - }, - } - end, - }, - }, - ft = { 'scala', 'sbt', 'java' }, - opts = function() - local metals_config = require('metals').bare_config() - - -- Example of settings - metals_config.settings = { - showImplicitArguments = true, - gradleScript = vim.fn.getcwd() .. '/gradlew', - customProjectRoot = vim.fn.getcwd(), - excludedPackages = { 'akka.actor.typed.javadsl', 'com.github.swagger.akka.javadsl' }, - } - - -- *READ THIS* - -- I *highly* recommend setting statusBarProvider to either "off" or "on" - -- - -- "off" will enable LSP progress notifications by Metals and you'll need - -- to ensure you have a plugin like fidget.nvim installed to handle them. - -- - -- "on" will enable the custom Metals status extension and you *have* to have - -- a have settings to capture this in your statusline or else you'll not see - -- any messages from metals. There is more info in the help docs about this - metals_config.init_options.statusBarProvider = 'off' - - -- Example if you are using cmp how to make sure the correct capabilities for snippets are set - metals_config.capabilities = require('cmp_nvim_lsp').default_capabilities() - - metals_config.on_attach = function(client, bufnr) - require('metals').setup_dap() - - -- LSP mappings - map('n', 'gD', vim.lsp.buf.definition) - map('n', 'K', vim.lsp.buf.hover) - map('n', 'gi', vim.lsp.buf.implementation) - map('n', 'gr', vim.lsp.buf.references) - map('n', 'gds', vim.lsp.buf.document_symbol) - map('n', 'gws', vim.lsp.buf.workspace_symbol) - map('n', 'cl', vim.lsp.codelens.run) - map('n', 'sh', vim.lsp.buf.signature_help) - map('n', 'rn', vim.lsp.buf.rename) - map('n', 'f', vim.lsp.buf.format) - map('n', 'ca', vim.lsp.buf.code_action) - - map('n', 'ws', function() - require('metals').hover_worksheet() - end) - - -- all workspace diagnostics - map('n', 'aa', vim.diagnostic.setqflist) - - -- all workspace errors - map('n', 'ae', function() - vim.diagnostic.setqflist { severity = 'E' } - end) - - -- all workspace warnings - map('n', 'aw', function() - vim.diagnostic.setqflist { severity = 'W' } - end) - - -- buffer diagnostics only - map('n', 'd', vim.diagnostic.setloclist) - - map('n', '[c', function() - vim.diagnostic.goto_prev { wrap = false } - end) - - map('n', ']c', function() - vim.diagnostic.goto_next { wrap = false } - end) - - -- Example mappings for usage with nvim-dap. If you don't use that, you can - -- skip these - map('n', 'dc', function() require('dap').continue() end, { desc = 'Debug: Start/Continue' }) - map('n', 'dr', function() require('dap').repl.toggle() end, { desc = 'Debug: Toggle REPL' }) - map('n', 'dK', function() require('dap.ui.widgets').hover() end, { desc = 'Debug: Hover Widget' }) - map('n', 'dt', function() require('dap').toggle_breakpoint() end, { desc = 'Debug: Toggle Breakpoint' }) - map('n', 'dso', function() require('dap').step_over() end, { desc = 'Debug: Step Over' }) - map('n', 'dsi', function() require('dap').step_into() end, { desc = 'Debug: Step Into' }) - map('n', 'dl', function() require('dap').run_last() end, { desc = 'Debug: Run Last' }) - end - - return metals_config - end, - config = function(self, metals_config) - local nvim_metals_group = vim.api.nvim_create_augroup('nvim-metals', { clear = true }) - vim.api.nvim_create_autocmd('FileType', { - pattern = self.ft, - callback = function() - require('metals').initialize_or_attach(metals_config) - end, - group = nvim_metals_group, - }) - end, -}