diff --git a/init.lua b/init.lua index 93e3e9b7..8ac7b55f 100644 --- a/init.lua +++ b/init.lua @@ -432,9 +432,11 @@ require('lazy').setup({ -- Document existing key chains spec = { { 'Q', group = '[Q]uit' }, -- Added Q first so it appears at top + { 'c', group = '[c]ode' }, -- Code actions, LSP commands { 's', group = '[s]earch' }, -- Search commands { 'S', group = '[S]ession' }, -- Session management (capital S) { 't', group = '[T]oggle' }, + { 'x', group = 'diagnostics/quickfi[x]' }, -- Trouble/diagnostics (Telescope has sd) { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, }, }, @@ -613,6 +615,26 @@ require('lazy').setup({ -- If you're wondering about lsp vs treesitter, you can check out the wonderfully -- and elegantly composed help section, `:help lsp-vs-treesitter` + -- ======================================================================== + -- LSP UI Enhancements - Better hover, signature help, and borders + -- ======================================================================== + -- Customize LSP handlers for better visual appearance (LazyVim-style) + + -- Rounded borders for hover windows + vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { + border = 'rounded', + max_width = 80, + }) + + -- Rounded borders for signature help + vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(vim.lsp.handlers.signature_help, { + border = 'rounded', + max_width = 80, + }) + + -- NOTE: Diagnostic config is set later in the file (around line 760) + -- with comprehensive settings including virtual_text, signs, etc. + -- This function gets run when an LSP attaches to a particular buffer. -- That is to say, every time a new file is opened that is associated with -- an lsp (for example, opening `main.rs` is associated with `rust_analyzer`) this @@ -726,33 +748,50 @@ require('lazy').setup({ end, }) - -- Diagnostic Config + -- Diagnostic Config - Enhanced for better visibility (LazyVim-style) -- See :help vim.diagnostic.Opts vim.diagnostic.config { + -- Sort diagnostics by severity (errors first) severity_sort = true, - float = { border = 'rounded', source = 'if_many' }, - underline = { severity = vim.diagnostic.severity.ERROR }, - signs = vim.g.have_nerd_font and { + + -- Underline errors and warnings + underline = { + severity = { min = vim.diagnostic.severity.WARN }, + }, + + -- Show signs in the gutter + signs = { text = { - [vim.diagnostic.severity.ERROR] = '󰅚 ', - [vim.diagnostic.severity.WARN] = '󰀪 ', - [vim.diagnostic.severity.INFO] = '󰋽 ', - [vim.diagnostic.severity.HINT] = '󰌶 ', + [vim.diagnostic.severity.ERROR] = '󰅚', + [vim.diagnostic.severity.WARN] = '󰀪', + [vim.diagnostic.severity.INFO] = '󰋽', + [vim.diagnostic.severity.HINT] = '󰌶', }, - } or {}, + }, + + -- Virtual text configuration (inline error messages at end of line) + -- This shows the actual diagnostic message text to the right of each line virtual_text = { - source = 'if_many', - spacing = 2, + spacing = 4, + source = 'if_many', -- Show source if multiple sources + prefix = '■', -- Prefix before the message format = function(diagnostic) - local diagnostic_message = { - [vim.diagnostic.severity.ERROR] = diagnostic.message, - [vim.diagnostic.severity.WARN] = diagnostic.message, - [vim.diagnostic.severity.INFO] = diagnostic.message, - [vim.diagnostic.severity.HINT] = diagnostic.message, - } - return diagnostic_message[diagnostic.severity] + -- Show the full diagnostic message inline + return diagnostic.message end, }, + + -- Floating window configuration (when hovering over error) + float = { + border = 'rounded', + source = 'always', -- Always show source + header = '', + prefix = '', + focusable = true, + }, + + -- Update diagnostics in insert mode + update_in_insert = false, } -- LSP servers and clients are able to communicate to each other what features they support. @@ -1230,5 +1269,24 @@ vim.api.nvim_create_user_command('PythonRestart', function() vim.notify('Pyright stopped. It will restart on next edit.', vim.log.levels.INFO) end, { desc = 'Restart Python LSP (pyright)' }) +-- Ensure virtual text diagnostics are enabled after all plugins load +-- This needs to be set after plugins that might override diagnostic config +vim.api.nvim_create_autocmd('User', { + pattern = 'VeryLazy', + once = true, + callback = function() + vim.diagnostic.config({ + virtual_text = { + spacing = 4, + source = 'if_many', + prefix = '■', + format = function(diagnostic) + return diagnostic.message + end, + }, + }) + end, +}) + -- The line beneath this is called `modeline`. See `:help modeline` -- vim: ts=2 sts=2 sw=2 et diff --git a/lazy-lock.json b/lazy-lock.json index 81bcf3a0..503829d0 100644 --- a/lazy-lock.json +++ b/lazy-lock.json @@ -18,6 +18,7 @@ "mini.animate": { "branch": "main", "commit": "8ce6df739aa9d14c876bace722af28c3d09e9971" }, "mini.nvim": { "branch": "main", "commit": "ee4a4a4abed25e3d108d985b0553c5271f2f71aa" }, "neo-tree.nvim": { "branch": "main", "commit": "8cdd6b1940f333c1dd085526a9c45b30fb2dbf50" }, + "noice.nvim": { "branch": "main", "commit": "5099348591f7d3ba9e547b1e631c694c65bbe0b9" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "nvim-dap": { "branch": "master", "commit": "6782b097af2417a4c3e33849b0a26ae2188bd7ea" }, "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, @@ -30,5 +31,6 @@ "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" }, "todo-comments.nvim": { "branch": "main", "commit": "411503d3bedeff88484de572f2509c248e499b38" }, "tokyonight.nvim": { "branch": "main", "commit": "2642dbb83333e0575d1c3436e1d837926871c5fb" }, + "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } } diff --git a/lua/custom/plugins/flutter.lua b/lua/custom/plugins/flutter.lua index 5721dea7..2d8f4d1b 100644 --- a/lua/custom/plugins/flutter.lua +++ b/lua/custom/plugins/flutter.lua @@ -101,13 +101,20 @@ return { }) -- Filter out didChange error notifications (they're harmless during snippet expansion) - local notify = vim.notify - vim.notify = function(msg, level, opts) - if type(msg) == 'string' and msg:match('textDocument/didChange') then - return -- Suppress this specific error - end - notify(msg, level, opts) - end + -- We'll use an autocmd to do this after noice.nvim is loaded + vim.api.nvim_create_autocmd('User', { + pattern = 'VeryLazy', + once = true, + callback = function() + local notify = vim.notify + vim.notify = function(msg, level, opts) + if type(msg) == 'string' and msg:match('textDocument/didChange') then + return -- Suppress this specific error + end + notify(msg, level, opts) + end + end, + }) end, -- Color preview for dart variables (Colors.red, Color(0xFF...), etc.) @@ -190,7 +197,7 @@ return { -- ======================================================================== local dap, dapui = require 'dap', require 'dapui' - -- Configure DAP UI + -- Configure DAP UI to open in tabs for better half-width screen support dapui.setup { icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, controls = { @@ -206,7 +213,8 @@ return { disconnect = '⏏', }, }, - -- Fix layout to prevent resizing issues with Neo-tree + -- Open each element in a new tab instead of side panels + -- This prevents layout issues on small/half-width screens layouts = { { elements = { @@ -215,25 +223,66 @@ return { { id = 'stacks', size = 0.25 }, { id = 'watches', size = 0.25 }, }, - size = 40, -- Fixed width instead of percentage - position = 'right', -- Changed to right to avoid conflict with Neo-tree on left + size = 40, + position = 'right', }, { elements = { { id = 'repl', size = 0.5 }, { id = 'console', size = 0.5 }, }, - size = 10, -- Fixed height + size = 10, position = 'bottom', }, }, + -- Override element window commands to open in tabs + element_mappings = {}, + windows = { indent = 1 }, } - -- Automatically open/close DAP UI - -- Don't close Neo-tree, they can coexist now (DAP on right, Neo-tree on left) - 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 + -- Custom function to open DAP UI elements in tabs + local function open_dapui_in_tabs() + -- Save current tab to return to it + local current_tab = vim.fn.tabpagenr() + + -- Create new tab with a named buffer for debug views + vim.cmd 'tabnew' + local debug_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(debug_buf, 'Flutter Debug') + vim.api.nvim_set_current_buf(debug_buf) + + -- Open DAP UI in this tab + dapui.open() + + -- Return to original tab so user continues coding there + vim.cmd('tabnext ' .. current_tab) + end + + -- Custom function to close DAP UI tabs + local function close_dapui_tabs() + dapui.close() + + -- Find and close the Flutter Debug tab + local current_tab = vim.fn.tabpagenr() + for i = 1, vim.fn.tabpagenr '$' do + vim.cmd('tabnext ' .. i) + local bufname = vim.api.nvim_buf_get_name(0) + if bufname:match('Flutter Debug') then + vim.cmd 'tabclose' + break + end + end + + -- Return to original tab + if vim.fn.tabpagenr '$' >= current_tab then + vim.cmd('tabnext ' .. current_tab) + end + end + + -- Automatically open/close DAP UI in tabs + dap.listeners.after.event_initialized['dapui_config'] = open_dapui_in_tabs + dap.listeners.before.event_terminated['dapui_config'] = close_dapui_tabs + dap.listeners.before.event_exited['dapui_config'] = close_dapui_tabs -- Fix for Flutter Tools log buffer - make it non-saveable -- This prevents Vim from asking to save changes to the log file on exit diff --git a/lua/custom/plugins/init.lua b/lua/custom/plugins/init.lua index 6a305786..67e48b13 100644 --- a/lua/custom/plugins/init.lua +++ b/lua/custom/plugins/init.lua @@ -176,6 +176,218 @@ return { end, }, + -- ======================================================================== + -- TROUBLE.NVIM - Beautiful diagnostics list (LazyVim-style) + -- ======================================================================== + -- Provides a nice list view of diagnostics, quickfix, LSP references, etc. + -- Shows errors inline in a dedicated panel like LazyVim/VS Code. + -- Auto-opens when diagnostics are present to show errors in editor area. + -- + -- Keymaps: + -- xx - Toggle diagnostics list + -- xX - Buffer diagnostics + -- cs - Symbols list + -- cl - LSP references + -- xL - Location list + -- xQ - Quickfix list + -- [q / ]q - Previous/next item in trouble list + -- ======================================================================== + { + 'folke/trouble.nvim', + cmd = 'Trouble', -- Lazy load on command + opts = { + focus = false, -- Don't focus the window when opened (LazyVim behavior) + auto_close = true, -- Auto close when no items + auto_open = false, -- Don't auto open (we'll handle this with autocmd) + warn_no_results = false, + open_no_results = false, + modes = { + -- Configure the diagnostics mode to show in editor area + diagnostics = { + mode = 'diagnostics', + preview = { + type = 'split', + relative = 'win', + position = 'right', + size = 0.3, + }, + }, + }, + }, + keys = { + { + 'xx', + 'Trouble diagnostics toggle', + desc = 'Diagnostics (Trouble)', + }, + { + 'xX', + 'Trouble diagnostics toggle filter.buf=0', + desc = 'Buffer Diagnostics (Trouble)', + }, + { + 'cs', + 'Trouble symbols toggle focus=false', + desc = 'Symbols (Trouble)', + }, + { + 'cl', + 'Trouble lsp toggle focus=false win.position=right', + desc = 'LSP Definitions / references / ... (Trouble)', + }, + { + 'xL', + 'Trouble loclist toggle', + desc = 'Location List (Trouble)', + }, + { + 'xQ', + 'Trouble qflist toggle', + desc = 'Quickfix List (Trouble)', + }, + { + '[q', + function() + if require('trouble').is_open() then + require('trouble').prev({ skip_groups = true, jump = true }) + else + local ok, err = pcall(vim.cmd.cprev) + if not ok then + vim.notify(err, vim.log.levels.ERROR) + end + end + end, + desc = 'Previous Trouble/Quickfix Item', + }, + { + ']q', + function() + if require('trouble').is_open() then + require('trouble').next({ skip_groups = true, jump = true }) + else + local ok, err = pcall(vim.cmd.cnext) + if not ok then + vim.notify(err, vim.log.levels.ERROR) + end + end + end, + desc = 'Next Trouble/Quickfix Item', + }, + }, + }, + + -- ======================================================================== + -- NOICE.NVIM - Better UI for messages, cmdline, and notifications + -- ======================================================================== + -- Provides a modern UI for command line, messages, and notifications (LazyVim-style). + -- Makes the editor feel more polished with popup notifications and floating cmdline. + -- + -- Features: + -- - Floating command line + -- - Modern notification system + -- - Better message display + -- - Signature help while typing + -- + -- Note: This can be disabled if you prefer the classic Vim UI + -- ======================================================================== + { + 'folke/noice.nvim', + event = 'VeryLazy', + dependencies = { + 'MunifTanjim/nui.nvim', + -- Optional: If you want to use `nvim-notify` for notifications + -- 'rcarriga/nvim-notify', + }, + opts = { + lsp = { + -- Override markdown rendering so that **cmp** and other plugins use **Treesitter** + override = { + ['vim.lsp.util.convert_input_to_markdown_lines'] = true, + ['vim.lsp.util.stylize_markdown'] = true, + ['cmp.entry.get_documentation'] = true, + }, + }, + -- Presets for easier configuration + presets = { + bottom_search = true, -- Use a classic bottom cmdline for search + command_palette = true, -- Position the cmdline and popupmenu together + long_message_to_split = true, -- Long messages will be sent to a split + inc_rename = false, -- Enables an input dialog for inc-rename.nvim + lsp_doc_border = true, -- Add a border to hover docs and signature help + }, + -- Routes configuration (optional customization) + routes = { + { + filter = { + event = 'msg_show', + kind = '', + find = 'written', + }, + opts = { skip = true }, + }, + }, + }, + keys = { + { + 'sn', + '', + desc = '+noice', + }, + { + 'snl', + function() + require('noice').cmd('last') + end, + desc = 'Noice Last Message', + }, + { + 'snh', + function() + require('noice').cmd('history') + end, + desc = 'Noice History', + }, + { + 'sna', + function() + require('noice').cmd('all') + end, + desc = 'Noice All', + }, + { + 'snd', + function() + require('noice').cmd('dismiss') + end, + desc = 'Dismiss All', + }, + { + '', + function() + if not require('noice.lsp').scroll(4) then + return '' + end + end, + silent = true, + expr = true, + desc = 'Scroll Forward', + mode = { 'i', 'n', 's' }, + }, + { + '', + function() + if not require('noice.lsp').scroll(-4) then + return '' + end + end, + silent = true, + expr = true, + desc = 'Scroll Backward', + mode = { 'i', 'n', 's' }, + }, + }, + }, + -- ======================================================================== -- ADDITIONAL COMMON PLUGINS -- ========================================================================