diff --git a/.gitignore b/.gitignore index 4d9a834..42fe6e5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ local.lua *.coder.* .coder/ .vscode/ +.codetyper/ diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..13aae22 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,3 @@ +{ + "Lua": { "diagnostics": { "globals": ["vim"] } } +} diff --git a/ftplugin/java.lua b/ftplugin/java.lua index db7c6c3..8fb195e 100644 --- a/ftplugin/java.lua +++ b/ftplugin/java.lua @@ -4,6 +4,9 @@ local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") local workspace_dir = workspace_path .. project_name local keymap = vim.keymap.set +-- JDTLS requires JDK 21+ to run; use JDK 25 for the server process +local jdtls_java = "/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/bin/java" + local status, jdtls = pcall(require, "jdtls") if not status then @@ -44,7 +47,7 @@ end local config = { cmd = { - "java", + jdtls_java, "-Declipse.application=org.eclipse.jdt.ls.core.id1", "-Dosgi.bundles.defaultStartLevel=4", "-Declipse.product=org.eclipse.jdt.ls.core.product", @@ -60,7 +63,7 @@ local config = { "-jar", vim.fn.glob(home .. "/.local/share/nvim/mason/packages/jdtls/plugins/org.eclipse.equinox.launcher_*.jar"), "-configuration", - home .. "/.local/share/nvim/mason/packages/jdtls/config_mac", + home .. "/.local/share/nvim/mason/packages/jdtls/config_mac_arm", "-data", workspace_dir, }, @@ -85,7 +88,20 @@ local config = { }, }, format = { - enabled = false, + enabled = true, + }, + configuration = { + runtimes = { + { + name = "JavaSE-17", + path = "/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home", + }, + { + name = "JavaSE-25", + path = "/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home", + default = true, + }, + }, }, }, }, diff --git a/lua/cargdev/core/function/highlights.lua b/lua/cargdev/core/function/highlights.lua index 9fccb5b..79d84ac 100644 --- a/lua/cargdev/core/function/highlights.lua +++ b/lua/cargdev/core/function/highlights.lua @@ -1,5 +1,12 @@ --- Highlight .njk files as htmldjango for better templating syntax -vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, { +--- Custom filetype detection for unsupported or ambiguous file extensions. +--- Registers autocmds that assign the correct filetype when buffers are +--- opened, enabling proper syntax highlighting and LSP support. +--- @module highlights + +--- Set .njk (Nunjucks) files to use the htmldjango filetype, +--- which provides the closest built-in syntax support for +--- Nunjucks/Jinja-style template syntax (blocks, extends, includes). +vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { pattern = "*.njk", callback = function() vim.cmd("set filetype=htmldjango") diff --git a/lua/cargdev/core/function/notification_manager.lua b/lua/cargdev/core/function/notification_manager.lua index 63e6f46..75e4913 100644 --- a/lua/cargdev/core/function/notification_manager.lua +++ b/lua/cargdev/core/function/notification_manager.lua @@ -1,15 +1,33 @@ --- Custom notification manager to handle overlapping and improve UX +--- Custom notification manager to handle overlapping and improve UX. +--- Provides a centralized notification system that prevents overlapping, +--- supports dashboard-aware notifications, and integrates with nvim-notify +--- when available, falling back to cmd echo otherwise. +--- @module notification_manager local M = {} +local log = vim.log +local api = vim.api +local cmd = vim.cmd +local o = vim.o +local loop = vim.loop +local opt = vim.opt +local deferFn = vim.defer_fn --- Track active notifications to prevent overlapping +--- List of currently displayed notifications (each entry has win, message, timestamp). +--- @type table[] local active_notifications = {} -local notification_queue = {} --- Function to show a notification without overlapping +--- Show a notification without overlapping existing ones. +--- Uses nvim-notify if available; otherwise falls back to echo. +--- Automatically detects the alpha dashboard and delegates to +--- `show_dashboard_notification` when appropriate. +--- @param message string The notification message to display. +--- @param level number|nil Log level from log.levels (default: INFO). +--- @param opts table|nil Optional overrides (timeout, title, render, position, etc.). +--- @return number|nil notification_id The nvim-notify notification id, or nil on fallback. function M.show_notification(message, level, opts) - level = level or vim.log.levels.INFO + level = level or log.levels.INFO opts = opts or {} - + -- Default options local default_opts = { timeout = 3000, @@ -22,64 +40,64 @@ function M.show_notification(message, level, opts) background_colour = "#000000", border_style = "rounded", } - + -- Merge options for k, v in pairs(default_opts) do if opts[k] == nil then opts[k] = v end end - + -- Check if we're in alpha dashboard - local current_buf = vim.api.nvim_get_current_buf() - local current_ft = vim.api.nvim_buf_get_option(current_buf, "filetype") - + local current_buf = api.nvim_get_current_buf() + local current_ft = api.nvim_buf_get_option(current_buf, "filetype") + if current_ft == "alpha" then -- If in dashboard, use a different approach M.show_dashboard_notification(message, level, opts) return end - + -- Use nvim-notify if available local notify_available = pcall(require, "notify") if notify_available then local notify = require("notify") - + -- Position notification to avoid overlapping opts.on_open = function(win) -- Calculate position to avoid overlapping with other notifications local row = 2 - local col = vim.o.columns - 60 - + local col = o.columns - 60 + -- Adjust position if there are other notifications for _, notif in ipairs(active_notifications) do - if notif.win and vim.api.nvim_win_is_valid(notif.win) then + if notif.win and api.nvim_win_is_valid(notif.win) then row = row + 10 -- Stack notifications vertically end end - + -- Ensure notification doesn't go off-screen - if row > vim.o.lines - 15 then + if row > o.lines - 15 then row = 2 col = col - 20 end - - vim.api.nvim_win_set_config(win, { + + api.nvim_win_set_config(win, { row = row, col = col, relative = "editor", width = opts.max_width, height = opts.max_height, }) - + -- Track this notification table.insert(active_notifications, { win = win, message = message, - timestamp = vim.loop.now(), + timestamp = loop.now(), }) end - + opts.on_close = function(win) -- Remove from active notifications for i, notif in ipairs(active_notifications) do @@ -89,69 +107,78 @@ function M.show_notification(message, level, opts) end end end - + -- Show notification local notification_id = notify(message, level, opts) return notification_id else -- Fallback to echo instead of vim.notify to avoid circular dependency local icon = "๐Ÿ’ฌ" - if level == vim.log.levels.ERROR then + if level == log.levels.ERROR then icon = "โŒ" - elseif level == vim.log.levels.WARN then + elseif level == log.levels.WARN then icon = "โš ๏ธ" - elseif level == vim.log.levels.INFO then + elseif level == log.levels.INFO then icon = "โ„น๏ธ" end - + -- Use echo for fallback notifications - vim.cmd("echo '" .. icon .. " " .. message .. "'") - + cmd("echo '" .. icon .. " " .. message .. "'") + -- Clear message after a delay - vim.defer_fn(function() - vim.cmd("echo ''") + deferFn(function() + cmd("echo ''") end, opts.timeout or 3000) end end --- Function to show notifications specifically for dashboard +--- Show a minimal notification tailored for the alpha dashboard. +--- Truncates messages longer than 50 characters and uses echo +--- to avoid interfering with the dashboard layout. +--- @param message string The notification message to display. +--- @param level number|nil Log level from log.levels. +--- @param opts table|nil Optional overrides (timeout, etc.). function M.show_dashboard_notification(message, level, opts) -- In dashboard, show minimal notifications local icon = "๐Ÿ’ฌ" - if level == vim.log.levels.ERROR then + if level == log.levels.ERROR then icon = "โŒ" - elseif level == vim.log.levels.WARN then + elseif level == log.levels.WARN then icon = "โš ๏ธ" - elseif level == vim.log.levels.INFO then + elseif level == log.levels.INFO then icon = "โ„น๏ธ" end - + -- Show message in status line or use echo local short_message = message:sub(1, 50) if #message > 50 then short_message = short_message .. "..." end - + -- Use echo for dashboard notifications to avoid overlapping - vim.cmd("echo '" .. icon .. " " .. short_message .. "'") - + cmd("echo '" .. icon .. " " .. short_message .. "'") + -- Clear message after a delay - vim.defer_fn(function() - vim.cmd("echo ''") + deferFn(function() + cmd("echo ''") end, opts.timeout or 3000) end --- Function to clear all notifications +--- Close and clear all active notifications. +--- Iterates through `active_notifications`, closes any valid windows, +--- and resets the tracking list. function M.clear_all_notifications() for _, notif in ipairs(active_notifications) do - if notif.win and vim.api.nvim_win_is_valid(notif.win) then - vim.api.nvim_win_close(notif.win, true) + if notif.win and api.nvim_win_is_valid(notif.win) then + api.nvim_win_close(notif.win, true) end end active_notifications = {} end --- Function to show performance notification +--- Show a performance-related notification with a chart icon prefix. +--- @param message string The performance message to display. +--- @param level number|nil Log level from log.levels. function M.show_performance_notification(message, level) M.show_notification("๐Ÿ“Š " .. message, level, { title = "Performance Monitor", @@ -160,7 +187,9 @@ function M.show_performance_notification(message, level) }) end --- Function to show LSP notification +--- Show an LSP status notification with a wrench icon prefix. +--- @param message string The LSP status message to display. +--- @param level number|nil Log level from log.levels. function M.show_lsp_notification(message, level) M.show_notification("๐Ÿ”ง " .. message, level, { title = "LSP Status", @@ -169,7 +198,9 @@ function M.show_lsp_notification(message, level) }) end --- Function to show file operation notification +--- Show a file operation notification with a folder icon prefix. +--- @param message string The file operation message to display. +--- @param level number|nil Log level from log.levels. function M.show_file_notification(message, level) M.show_notification("๐Ÿ“ " .. message, level, { title = "File Operation", @@ -178,7 +209,9 @@ function M.show_file_notification(message, level) }) end --- Function to show plugin notification +--- Show a plugin manager notification with a sloth icon prefix. +--- @param message string The plugin-related message to display. +--- @param level number|nil Log level from log.levels. function M.show_plugin_notification(message, level) M.show_notification("๐Ÿฆฅ " .. message, level, { title = "Plugin Manager", @@ -187,7 +220,9 @@ function M.show_plugin_notification(message, level) }) end --- Function to show startup notification +--- Show a startup notification with a rocket icon prefix and a short timeout. +--- @param message string The startup message to display. +--- @param level number|nil Log level from log.levels. function M.show_startup_notification(message, level) M.show_notification("๐Ÿš€ " .. message, level, { title = "Startup", @@ -196,116 +231,126 @@ function M.show_startup_notification(message, level) }) end --- Function to handle startup messages aggressively +--- Aggressively suppress startup messages and "Press ENTER" prompts. +--- Configures `shortmess` with all relevant flags, sets `cmdheight` to 0, +--- disables `showmode`, and schedules deferred redraws to clear any +--- lingering messages. function M.handle_startup_messages() -- Clear any existing messages immediately - vim.cmd("redraw!") - vim.cmd("echo ''") - + cmd("redraw!") + cmd("echo ''") + -- Suppress all startup messages - vim.opt.shortmess = vim.opt.shortmess + "I" -- No intro message - vim.opt.shortmess = vim.opt.shortmess + "c" -- No completion messages - vim.opt.shortmess = vim.opt.shortmess + "F" -- No file info message - vim.opt.shortmess = vim.opt.shortmess + "W" -- No "written" message - vim.opt.shortmess = vim.opt.shortmess + "A" -- No attention message - vim.opt.shortmess = vim.opt.shortmess + "o" -- No overwrite messages - vim.opt.shortmess = vim.opt.shortmess + "t" -- No truncation messages - vim.opt.shortmess = vim.opt.shortmess + "T" -- No truncation messages - vim.opt.shortmess = vim.opt.shortmess + "f" -- No file info messages - vim.opt.shortmess = vim.opt.shortmess + "i" -- No intro messages - vim.opt.shortmess = vim.opt.shortmess + "l" -- No line number messages - vim.opt.shortmess = vim.opt.shortmess + "m" -- No modification messages - vim.opt.shortmess = vim.opt.shortmess + "n" -- No line number messages - vim.opt.shortmess = vim.opt.shortmess + "r" -- No read messages - vim.opt.shortmess = vim.opt.shortmess + "s" -- No search messages - vim.opt.shortmess = vim.opt.shortmess + "x" -- No truncation messages - vim.opt.shortmess = vim.opt.shortmess + "O" -- No overwrite messages - + opt.shortmess = opt.shortmess + "I" -- No intro message + opt.shortmess = opt.shortmess + "c" -- No completion messages + opt.shortmess = opt.shortmess + "F" -- No file info message + opt.shortmess = opt.shortmess + "W" -- No "written" message + opt.shortmess = opt.shortmess + "A" -- No attention message + opt.shortmess = opt.shortmess + "o" -- No overwrite messages + opt.shortmess = opt.shortmess + "t" -- No truncation messages + opt.shortmess = opt.shortmess + "T" -- No truncation messages + opt.shortmess = opt.shortmess + "f" -- No file info messages + opt.shortmess = opt.shortmess + "i" -- No intro messages + opt.shortmess = opt.shortmess + "l" -- No line number messages + opt.shortmess = opt.shortmess + "m" -- No modification messages + opt.shortmess = opt.shortmess + "n" -- No line number messages + opt.shortmess = opt.shortmess + "r" -- No read messages + opt.shortmess = opt.shortmess + "s" -- No search messages + opt.shortmess = opt.shortmess + "x" -- No truncation messages + opt.shortmess = opt.shortmess + "O" -- No overwrite messages + -- Disable command line messages - vim.opt.cmdheight = 0 - vim.opt.showmode = false - + opt.cmdheight = 0 + opt.showmode = false + -- Clear any existing messages - vim.cmd("echo ''") - + cmd("echo ''") + -- Force clear any pending messages - vim.defer_fn(function() - vim.cmd("redraw!") - vim.cmd("echo ''") + deferFn(function() + cmd("redraw!") + cmd("echo ''") end, 100) end --- Function to eliminate "Press ENTER" prompts completely +--- Eliminate "Press ENTER" prompts by overriding `cmd.echo` and +--- installing autocmds that forcibly clear messages on VimEnter and +--- BufReadPost events. function M.eliminate_enter_prompts() -- Override the message display to prevent "Press ENTER" prompts - local original_echo = vim.cmd.echo - vim.cmd.echo = function(msg) + local original_echo = cmd.echo + cmd.echo = function(msg) local msg_str = tostring(msg) -- Block any messages that might cause "Press ENTER" prompts - if msg_str:match("Press ENTER") or - msg_str:match("lazyredraw") or - msg_str:match("You have enabled") or - msg_str:match("This is only meant") or - msg_str:match("You'll experience issues") then + if + msg_str:match("Press ENTER") + or msg_str:match("lazyredraw") + or msg_str:match("You have enabled") + or msg_str:match("This is only meant") + or msg_str:match("You'll experience issues") + then return -- Don't show these messages end -- Allow other messages original_echo(msg) end - + -- Create autocmd to handle any remaining prompts - vim.api.nvim_create_autocmd("VimEnter", { + api.nvim_create_autocmd("VimEnter", { callback = function() -- Clear any startup messages immediately - vim.cmd("redraw!") - vim.cmd("echo ''") - + cmd("redraw!") + cmd("echo ''") + -- Force clear any pending messages multiple times for i = 1, 5 do - vim.defer_fn(function() - vim.cmd("redraw!") - vim.cmd("echo ''") + deferFn(function() + cmd("redraw!") + cmd("echo ''") end, i * 50) end end, once = true, }) - + -- Create autocmd to handle message events - use valid events - vim.api.nvim_create_autocmd("BufReadPost", { + api.nvim_create_autocmd("BufReadPost", { callback = function() -- Clear messages that might cause prompts - vim.cmd("redraw!") + cmd("redraw!") end, }) end --- Function to setup notification system +--- Initialize the notification system. +--- Registers VimEnter and FileType autocmds to handle startup messages +--- and dashboard transitions, and calls `eliminate_enter_prompts`. +--- Called automatically when the module is first required. function M.setup() -- Create autocmd to handle startup messages - vim.api.nvim_create_autocmd("VimEnter", { + api.nvim_create_autocmd("VimEnter", { callback = function() M.handle_startup_messages() end, once = true, }) - + -- Create autocmd to handle alpha dashboard - vim.api.nvim_create_autocmd("FileType", { + api.nvim_create_autocmd("FileType", { pattern = "alpha", callback = function() M.handle_startup_messages() end, }) - + -- Eliminate "Press ENTER" prompts M.eliminate_enter_prompts() - + -- Don't override vim.notify here to avoid circular dependency -- Let the system handle notifications naturally end --- Initialize notification manager +--- Initialize notification manager on require. M.setup() return M diff --git a/lua/cargdev/core/function/openTerminal.lua b/lua/cargdev/core/function/openTerminal.lua index b0e7f95..a578aa5 100644 --- a/lua/cargdev/core/function/openTerminal.lua +++ b/lua/cargdev/core/function/openTerminal.lua @@ -1,48 +1,54 @@ +--- Terminal toggle utility. +--- Provides a function and keybinding (`ter`) to open or close a +--- vertical-split zsh terminal on the right side of the editor. The terminal +--- automatically enters insert mode on focus and exits insert mode on blur. +--- @module openTerminal + vim.g.mapleader = " " local keymap = vim.keymap +local cmd = vim.cmd +local api = vim.api --- Function to open terminal +--- Toggle a terminal split on the rightmost side of the editor. +--- If the rightmost buffer is already a terminal, it closes it. +--- Otherwise, opens a new vertical split running zsh with line numbers +--- disabled and auto insert-mode behavior on focus/blur. local function open_terminal() -- Move to the rightmost buffer (equivalent to `` multiple times) - vim.cmd("wincmd l") - vim.cmd("wincmd l") - vim.cmd("wincmd l") - vim.cmd("wincmd l") + cmd("wincmd l") + cmd("wincmd l") + cmd("wincmd l") + cmd("wincmd l") -- Get current buffer number and type - local buf_num = vim.api.nvim_get_current_buf() - local buf_type = vim.api.nvim_buf_get_option(buf_num, "buftype") + local buf_num = apinvim_get_current_buf() + local buf_type = apinvim_buf_get_option(buf_num, "buftype") if buf_type == "terminal" then -- If already in a terminal, close it - vim.cmd("q") + cmd("q") else -- Open terminal in a vertical split - vim.cmd("vsp | term zsh") + cmd("vsp | term zsh") -- Disable line numbers in terminal - vim.cmd("setlocal nonumber norelativenumber") + cmd("setlocal nonumber norelativenumber") -- Auto-start insert mode in terminal - vim.api.nvim_create_autocmd({ "BufWinEnter", "WinEnter" }, { + apinvim_create_autocmd({ "BufWinEnter", "WinEnter" }, { buffer = 0, command = "startinsert!", }) - vim.api.nvim_create_autocmd("BufLeave", { + apinvim_create_autocmd("BufLeave", { buffer = 0, command = "stopinsert!", }) - -- Terminal key mappings - vim.keymap.set("t", "", "h", { buffer = true }) - vim.keymap.set("t", "", ":q", { buffer = true }) - vim.keymap.set("t", "", "", { buffer = true }) - -- Start terminal in insert mode - vim.cmd("startinsert!") + cmd("startinsert!") end end --- Keybinding to open terminal +--- Map `ter` in normal mode to toggle the terminal split. keymap.set("n", "ter", open_terminal, { noremap = true, silent = true }) diff --git a/lua/cargdev/core/function/performance_monitor.lua b/lua/cargdev/core/function/performance_monitor.lua index 560c337..f9ee2c8 100644 --- a/lua/cargdev/core/function/performance_monitor.lua +++ b/lua/cargdev/core/function/performance_monitor.lua @@ -1,11 +1,22 @@ +--- Performance and LSP health monitoring utilities. +--- Provides functions to inspect memory usage, startup time, loaded plugins, +--- active LSP clients, and per-buffer diagnostic summaries. +--- @module performance_monitor local M = {} --- Check overall Neovim performance +local fn = vim.fn +local notify = vim.notify +local lsp = vim.lsp + +--- Collect and display overall Neovim performance statistics. +--- Reports memory usage, open buffer count, active LSP clients, and +--- (when lazy.nvim is available) loaded plugin count and startup time. +--- Results are shown via `vim.notify` at INFO level. function M.check_performance() local stats = {} -- Startup time - stats.startup = vim.fn.reltimefloat(vim.fn.reltime(vim.g.start_time or vim.fn.reltime())) + stats.startup = fn.reltimefloat(fn.reltime(vim.g.start_time or fn.reltime())) -- Memory usage local mem = collectgarbage("count") @@ -13,7 +24,7 @@ function M.check_performance() stats.memory_mb = string.format("%.2f", mem / 1024) -- Buffer count - stats.buffers = #vim.fn.getbufinfo({ buflisted = 1 }) + stats.buffers = #fn.getbufinfo({ buflisted = 1 }) -- Loaded plugins (lazy.nvim) local lazy_ok, lazy = pcall(require, "lazy") @@ -25,7 +36,7 @@ function M.check_performance() end -- Active LSP clients - stats.lsp_clients = #vim.lsp.get_clients() + stats.lsp_clients = #lsp.get_clients() -- Display results local lines = { @@ -41,15 +52,19 @@ function M.check_performance() table.insert(lines, string.format("Startup Time: %s ms", stats.startup_ms)) end - vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "Performance" }) + notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "Performance" }) end --- Check LSP health for current buffer +--- Check LSP health for the current buffer. +--- Lists all attached LSP clients with their root directory and +--- server capabilities (completion, hover, definition, references, +--- formatting, code_actions), followed by a diagnostic severity summary. +--- Warns if no LSP clients are attached. function M.check_lsp_health() - local clients = vim.lsp.get_clients({ bufnr = 0 }) + local clients = lsp.get_clients({ bufnr = 0 }) if #clients == 0 then - vim.notify("No LSP clients attached to this buffer", vim.log.levels.WARN, { title = "LSP Health" }) + notify("No LSP clients attached to this buffer", vim.log.levels.WARN, { title = "LSP Health" }) return end @@ -103,10 +118,18 @@ function M.check_lsp_health() end table.insert(lines, "") - table.insert(lines, string.format("Diagnostics: %d errors, %d warnings, %d hints, %d info", - counts.errors, counts.warnings, counts.hints, counts.info)) + table.insert( + lines, + string.format( + "Diagnostics: %d errors, %d warnings, %d hints, %d info", + counts.errors, + counts.warnings, + counts.hints, + counts.info + ) + ) - vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "LSP Health" }) + notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "LSP Health" }) end return M diff --git a/lua/cargdev/core/function/project_commands.lua b/lua/cargdev/core/function/project_commands.lua index 8500fed..a0dfd38 100644 --- a/lua/cargdev/core/function/project_commands.lua +++ b/lua/cargdev/core/function/project_commands.lua @@ -1,27 +1,43 @@ -vim.api.nvim_create_user_command("RunProject", function() +--- Project-level user commands for running and debugging. +--- Reads a per-project configuration file at `.nvim/project.lua` that should +--- return a table with `run` and/or `debug` keys containing shell commands. +--- Registers the `:RunProject` and `:DebugProject` user commands. +--- @module project_commands + +local api = vim.api +local fn = vim.fn +local notify = vim.notify + +--- Run the project using the `run` command from `.nvim/project.lua`. +--- Opens a vertical split terminal with the configured run command. +--- Notifies the user if the config file or run command is missing. +api.nvim_create_user_command("RunProject", function() local config_path = ".nvim/project.lua" - if vim.fn.filereadable(config_path) == 0 then - vim.notify("No project config found. Run `:e` on any file to generate it.", vim.log.levels.WARN) + if fn.filereadable(config_path) == 0 then + notify("No project config found. Run `:e` on any file to generate it.", vim.log.levels.WARN) return end local config = loadfile(config_path)() if config and config.run then vim.cmd("vsplit | terminal " .. config.run) else - vim.notify("Run command not found in project config.", vim.log.levels.ERROR) + notify("Run command not found in project config.", vim.log.levels.ERROR) end end, {}) -vim.api.nvim_create_user_command("DebugProject", function() +--- Debug the project using the `debug` command from `.nvim/project.lua`. +--- Opens a vertical split terminal with the configured debug command. +--- Notifies the user if the config file or debug command is missing. +api.nvim_create_user_command("DebugProject", function() local config_path = ".nvim/project.lua" - if vim.fn.filereadable(config_path) == 0 then - vim.notify("No project config found. Run `:e` on any file to generate it.", vim.log.levels.WARN) + if fn.filereadable(config_path) == 0 then + notify("No project config found. Run `:e` on any file to generate it.", vim.log.levels.WARN) return end local config = loadfile(config_path)() if config and config.debug then vim.cmd("vsplit | terminal " .. config.debug) else - vim.notify("Debug command not found in project config.", vim.log.levels.ERROR) + notify("Debug command not found in project config.", vim.log.levels.ERROR) end end, {}) diff --git a/lua/cargdev/core/keymaps/README.md b/lua/cargdev/core/keymaps/README.md deleted file mode 100644 index 79df2b5..0000000 --- a/lua/cargdev/core/keymaps/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Keymaps Structure - -This folder contains all the keymaps organized by category for better maintainability. - -## File Structure - -``` -keymaps/ -โ”œโ”€โ”€ README.md # This file -โ”œโ”€โ”€ general.lua # General keymaps (leader, basic navigation, obsidian) -โ”œโ”€โ”€ personal.lua # Personal workflow keymaps -โ”œโ”€โ”€ lsp.lua # LSP and function navigation keymaps -โ”œโ”€โ”€ dap.lua # DAP debugging keymaps -โ”œโ”€โ”€ snacks.lua # Snacks search and navigation keymaps -โ”œโ”€โ”€ window.lua # Window management keymaps -โ”œโ”€โ”€ copilot.lua # Copilot AI keymaps -โ”œโ”€โ”€ plugins.lua # Plugin-specific keymaps (Telescope, trouble, etc.) -โ”œโ”€โ”€ project.lua # Project-specific keymaps -โ”œโ”€โ”€ ufo.lua # Folding keymaps -โ”œโ”€โ”€ leet.lua # LeetCode keymaps -โ”œโ”€โ”€ sudoku.lua # Sudoku game keymaps -โ””โ”€โ”€ database.lua # Database keymaps -``` - -## How It Works - -The main `keymaps.lua` file automatically loads all `.lua` files from this folder: - -```lua -local function load_keymaps() - local keymaps_path = vim.fn.stdpath("config") .. "/lua/cargdev/core/keymaps" - local scan = vim.fn.globpath(keymaps_path, "*.lua", false, true) - - for _, file in ipairs(scan) do - local module_name = "cargdev.core.keymaps." .. file:match("([^/]+)%.lua$") - pcall(require, module_name) - end -end -``` - -## Keymap Categories - -| Category | File | Description | -|----------|------|-------------| -| General | `general.lua` | Basic setup, escape, obsidian links | -| Personal | `personal.lua` | Your workflow shortcuts | -| LSP | `lsp.lua` | Function navigation and LSP features | -| Debugging | `dap.lua` | DAP debugging (Java, Node.js, Python) | -| Search | `snacks.lua` | File and text search (Snacks) | -| Window | `window.lua` | Window/split management | -| Copilot | `copilot.lua` | AI assistant keymaps | -| Plugins | `plugins.lua` | Plugin-specific (Telescope, trouble, etc.) | -| Project | `project.lua` | Project commands | -| Folding | `ufo.lua` | Code folding | -| LeetCode | `leet.lua` | LeetCode integration | -| Database | `database.lua` | Database operations | - -## Adding New Keymaps - -1. **Choose the appropriate file** based on the category -2. **Add your keymaps** using the standard format: - ```lua - local keymap = vim.keymap - keymap.set("n", "key", "command", { desc = "Description" }) - ``` -3. **The keymaps will be automatically loaded** when Neovim starts - -## Leader Key Prefixes - -| Prefix | Category | -|--------|----------| -| `f` | Find/Files (Telescope) | -| `g` | Git | -| `d` | Debug | -| `l` | LSP/LeetCode | -| `x` | Trouble/Diagnostics | -| `s` | Search/Session | -| `t` | Toggle/Text/Treesj | -| `h` | Harpoon | -| `k` | Kulala (HTTP) | -| `o` | Overseer/Octo | -| `n` | NPM/Navbuddy | -| `c` | Code/Crates | -| `v` | Vim learning | -| `z` | Zen mode | -| `q` | Session (persistence) | -| `r` | Rename/Regex | -| `p` | Portal/Projects | - -## Full Reference - -See [KEYMAPS.md](../../../../KEYMAPS.md) in the root directory for complete keybinding reference. - -## Notes - -- All files are automatically loaded by `keymaps.lua` -- Each file should have its own `local keymap = vim.keymap` declaration -- Use descriptive `desc` for all keymaps (shows in which-key) -- Follow the existing naming conventions for consistency diff --git a/lua/cargdev/core/keymaps/copilot.lua b/lua/cargdev/core/keymaps/copilot.lua index e6f68e2..d4108a6 100644 --- a/lua/cargdev/core/keymaps/copilot.lua +++ b/lua/cargdev/core/keymaps/copilot.lua @@ -1,24 +1,32 @@ --- Copilot keymaps +--- Copilot keymaps. +--- Bindings for GitHub Copilot panel navigation (normal mode) and inline +--- suggestion cycling (insert mode). All Copilot modules are loaded via +--- pcall to avoid errors when Copilot is not installed or authenticated. +--- @module keymaps.copilot + local keymap = vim.keymap --- Helper function to safely get Copilot modules +--- Safely require the copilot.panel module. +--- @return table|nil panel The panel module, or nil if unavailable. local function get_copilot_panel() local ok, panel = pcall(require, "copilot.panel") return ok and panel or nil end +--- Safely require the copilot.suggestion module. +--- @return table|nil suggestion The suggestion module, or nil if unavailable. local function get_copilot_suggestion() local ok, suggestion = pcall(require, "copilot.suggestion") return ok and suggestion or nil end --- Copilot panel and status +--- Copilot commands โ€” open panel, disable, enable, and check status. keymap.set("n", "cp", ":Copilot panel", { desc = "Copilot: Open copilot panel" }) -keymap.set("n", "cd", ":Copilot disable", { desc = "Copilot: Disable" }) -keymap.set("n", "ce", ":Copilot enable", { desc = "Copilot: Enable" }) +keymap.set("n", "cD", ":Copilot disable", { desc = "Copilot: Disable" }) +keymap.set("n", "cE", ":Copilot enable", { desc = "Copilot: Enable" }) keymap.set("n", "cs", ":Copilot status", { desc = "Copilot: Status" }) --- Copilot panel keymaps +--- Panel navigation โ€” jump previous/next, accept, refresh, and open. keymap.set("n", "[[", function() local panel = get_copilot_panel() if panel and panel.is_open() then @@ -40,7 +48,7 @@ keymap.set("n", "", function() end end, { desc = "Copilot: Accept suggestion in panel" }) -keymap.set("n", "gr", function() +keymap.set("n", "rp", function() local panel = get_copilot_panel() if panel and panel.is_open() then panel.refresh() @@ -49,10 +57,7 @@ end, { desc = "Copilot: Refresh panel" }) keymap.set("n", "", ":Copilot panel", { desc = "Copilot: Open panel" }) --- Copilot suggestion keymaps (insert mode) --- Note: Tab mapping is handled in nvim-cmp.lua to avoid conflicts --- Tab is reserved exclusively for Copilot inline suggestions - +--- Inline suggestion cycling (insert mode) โ€” next, previous, dismiss. keymap.set("i", "]", function() local suggestion = get_copilot_suggestion() if suggestion and suggestion.is_visible() then @@ -73,6 +78,3 @@ keymap.set("i", "", function() suggestion.dismiss() end end, { desc = "Copilot: Dismiss suggestion" }) - --- CodeCompanion keymaps -keymap.set("n", "cc", ":CodeCompanion", { desc = "CodeCompanion: Open CodeCompanion" }) diff --git a/lua/cargdev/core/keymaps/dap.lua b/lua/cargdev/core/keymaps/dap.lua index 9263b7d..1ede2b5 100644 --- a/lua/cargdev/core/keymaps/dap.lua +++ b/lua/cargdev/core/keymaps/dap.lua @@ -1,35 +1,6 @@ --- DAP (Debug Adapter Protocol) keymaps - -local ok_dap, dap = pcall(require, "dap") -local ok_dapui, dapui = pcall(require, "dapui") -local fn = vim.fn -local keymap = vim.keymap.set - -if ok_dap and ok_dapui then - keymap("n", "dcr", 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" }) - - -- ๐Ÿ”ญ Snacks Integration (replacing Telescope) -keymap("n", "dcf", "lua require('snacks.picker').dap_configurations()", { desc = "๐Ÿ”ญ DAP Configs" }) -keymap("n", "dcb", "lua require('snacks.picker').dap_list_breakpoints()", { desc = "๐Ÿงท List Breakpoints" }) -keymap("n", "dco", "lua require('snacks.picker').dap_commands()", { desc = "โš™๏ธ DAP Commands" }) -end +--- DAP (Debug Adapter Protocol) keymaps. +--- All DAP keymaps are registered in `plugins/dap.lua` inside the plugin's +--- config function to ensure nvim-dap and nvim-dap-ui are fully loaded +--- before any keybindings reference them. This file is intentionally empty. +--- @module keymaps.dap +--- @see plugins.dap diff --git a/lua/cargdev/core/keymaps/database.lua b/lua/cargdev/core/keymaps/database.lua index 3ff5034..7b52498 100644 --- a/lua/cargdev/core/keymaps/database.lua +++ b/lua/cargdev/core/keymaps/database.lua @@ -1,53 +1,57 @@ --- Database keymaps --- Using D prefix to avoid conflicts with DAP keymaps (d) +--- Database keymaps (vim-dadbod, MongoDB, Redis). +--- Uses `D` prefix to avoid conflicts with DAP keymaps (`d`). +--- Covers DBUI toggle, connections, query execution/saving, buffer management, +--- and quick-connect shortcuts for PostgreSQL Docker, MongoDB, and Redis. +--- @module keymaps.database + local keymap = vim.keymap -- ============================================================================= -- DATABASE KEYMAPS (vim-dadbod) -- ============================================================================= --- Toggle database UI +--- Toggle the vim-dadbod Database UI panel. keymap.set("n", "Du", "DBUIToggle", { desc = "Toggle Database UI" }) --- Add a new database connection +--- Add a new database connection interactively. keymap.set("n", "Da", "DBUIAddConnection", { desc = "Add DB Connection" }) --- Find buffer (useful when you have multiple query buffers) +--- Find a DBUI query buffer (useful when multiple query buffers are open). keymap.set("n", "Df", "DBUIFindBuffer", { desc = "Find DB Buffer" }) --- Execute query (works in sql buffers) +--- Execute a SQL query (full buffer in normal mode, selection in visual mode). keymap.set("n", "De", "(DBUI_ExecuteQuery)", { desc = "Execute Query" }) keymap.set("v", "De", "(DBUI_ExecuteQuery)", { desc = "Execute Selected Query" }) --- Save query +--- Save the current query buffer to disk. keymap.set("n", "Dw", "(DBUI_SaveQuery)", { desc = "Save Query" }) --- Rename buffer +--- Rename the current DBUI query buffer. keymap.set("n", "Dr", "(DBUI_RenameBuf)", { desc = "Rename DB Buffer" }) -- ============================================================================= -- QUICK CONNECTIONS -- ============================================================================= --- PostgreSQL Docker (default: 5432 postgres postgres postgres) +--- Connect to a PostgreSQL instance running in Docker (default port 5432). keymap.set("n", "Dp", "DBPostgresDocker", { desc = "Connect PostgreSQL Docker" }) -- ============================================================================= -- MONGODB -- ============================================================================= --- Open MongoDB shell (local) +--- Open a local MongoDB shell. keymap.set("n", "Dm", "MongoDB", { desc = "Open MongoDB Shell" }) --- Open MongoDB in Docker container +--- Open a MongoDB shell inside a Docker container. keymap.set("n", "DM", "MongoDBDocker", { desc = "MongoDB Docker Shell" }) -- ============================================================================= -- REDIS -- ============================================================================= --- Open Redis CLI (local) +--- Open a local Redis CLI session. keymap.set("n", "Di", "Redis", { desc = "Open Redis CLI" }) --- Open Redis in Docker container +--- Open a Redis CLI session inside a Docker container. keymap.set("n", "DI", "RedisDocker", { desc = "Redis Docker CLI" }) diff --git a/lua/cargdev/core/keymaps/general.lua b/lua/cargdev/core/keymaps/general.lua index aac0e23..9452fea 100644 --- a/lua/cargdev/core/keymaps/general.lua +++ b/lua/cargdev/core/keymaps/general.lua @@ -1,4 +1,8 @@ --- General keymaps +--- General-purpose keymaps. +--- Provides essential editor-wide shortcuts: insert-mode escape (`jj`), +--- search highlight clearing, and quit-all. +--- @module keymaps.general + local keymap = vim.keymap local opts = { noremap = true, silent = true } @@ -6,116 +10,12 @@ local opts = { noremap = true, silent = true } -- GENERAL KEYMAPS -- ============================================================================= --- General keymaps --- Changed from "jk" to "jj" to avoid conflicts when typing words containing 'j' followed by other letters --- "jj" is much less likely to appear in normal typing and won't interfere -keymap.set("i", "jj", "", opts) -- Exit insert mode with jj -keymap.set("n", "nh", ":nohl", opts) -- Clear search highlights +--- Exit insert mode by pressing `jj` (chosen over `jk` to avoid +--- conflicts when typing words containing "j" followed by other letters). +keymap.set("i", "jj", "", opts) --- Save and quit (additional) +--- Clear search highlights. +keymap.set("n", "nh", ":nohl", opts) + +--- Quit all open buffers and windows without saving. keymap.set("n", "Q", ":qa!", { desc = "Quit all" }) - --- Obsidian vault path with validation -local vault_path = vim.env.IDEA_DIR -if not vault_path or vault_path == "" then - -- Silently skip Obsidian link setup if IDEA_DIR is not configured - return -end - - - -local function follow_obsidian_link() - -- Extract the full [[...]] link from the current line under/around the cursor - local line = vim.api.nvim_get_current_line() - local col = vim.fn.col('.') - local start_idx, end_idx, raw - -- Search for all [[...]] in the line, pick the one under/around the cursor - local i = 1 - while true do - local s, e = line:find('%[%[.-%]%]', i) - if not s then break end - if col >= s and col <= e + 1 then - start_idx, end_idx = s, e - break - end - i = e + 1 - end - if not start_idx then - vim.notify('No [[link]] under cursor', vim.log.levels.WARN) - return - end - raw = line:sub(start_idx, end_idx) - raw = raw:gsub('^!%[%[', '[[') -- strip leading ! from embeds - local link = raw:gsub('%[%[', ''):gsub('%]%]', '') - - -- split off alias (|) and heading (#) only after extracting the full link - local alias - local heading - -- first, split off alias if present - local pipe_idx = link:find("|", 1, true) - if pipe_idx then - alias = link:sub(pipe_idx + 1) - link = link:sub(1, pipe_idx - 1) - end - -- then, split off heading if present - local hash_idx = link:find("#", 1, true) - if hash_idx then - heading = link:sub(hash_idx + 1) - link = link:sub(1, hash_idx - 1) - end - - -- normalize spaces - link = link:gsub("\\ ", " "):gsub("^%s+", ""):gsub("%s+$", "") - - local function goto_heading(h) - if not h or h == "" then - return - end - -- crude jump: search for markdown heading or block that contains it - vim.cmd("keepjumps normal! gg") - local pat = "^%s*#+%s*" .. vim.pesc(h) - if vim.fn.search(pat) == 0 then - -- fallback: plain search - vim.fn.search(vim.pesc(h)) - end - end - - -- if link contains a '/', treat it as a relative path inside the vault - if link:find("/") then - local target = vault_path .. "/" .. link .. ".md" - vim.cmd.edit(target) - goto_heading(heading) - return - end - - -- otherwise search recursively for basename match anywhere in the vault - local pattern = "**/" .. link .. ".md" - local matches = vim.fn.globpath(vault_path, pattern, false, true) -- list - - if #matches == 1 then - vim.cmd.edit(matches[1]) - goto_heading(heading) - elseif #matches > 1 then - vim.ui.select(matches, { prompt = "Multiple matches for " .. link .. ":" }, function(choice) - if choice then - vim.cmd.edit(choice) - goto_heading(heading) - end - end) - else - -- not found: offer to create at vault root - local target = vault_path .. "/" .. link .. ".md" - vim.ui.input({ prompt = "Create " .. target .. " ? (y/N) " }, function(ans) - if ans and ans:lower():sub(1, 1) == "y" then - vim.cmd.edit(target) - -- optional: insert a title - if vim.fn.line("$") == 1 and vim.fn.getline(1) == "" then - vim.api.nvim_buf_set_lines(0, 0, 0, false, { "# " .. (alias or link), "" }) - end - goto_heading(heading) - end - end) - end -end - -vim.keymap.set("n", "o", follow_obsidian_link, { noremap = true, silent = true }) diff --git a/lua/cargdev/core/keymaps/leet.lua b/lua/cargdev/core/keymaps/leet.lua index dbeaf67..4662442 100644 --- a/lua/cargdev/core/keymaps/leet.lua +++ b/lua/cargdev/core/keymaps/leet.lua @@ -1,6 +1,16 @@ --- Leet keymaps +--- LeetCode keymaps. +--- Bindings for testing, submitting, and retrieving solutions via the +--- leetcode.nvim plugin. All mappings use the `l` prefix. +--- @module keymaps.leet + local keymap = vim.keymap -keymap.set("n", "lr", ":Leet test", { desc = "Test the leet current problem code" }) -keymap.set("n", "ls", ":Leet submit", { desc = "Submit the leet solution" }) -keymap.set("n", "lls", ":Leet last_submit", { desc = "Brings the latest submition from leetcode" }) +--- LeetCode โ€” run, submit, daily challenge, list, console, cookie, hints. +keymap.set("n", "lr", "Leet run", { desc = "LeetCode: Run Code" }) +keymap.set("n", "ls", "Leet submit", { desc = "LeetCode: Submit Code" }) +keymap.set("n", "ld", "Leet daily", { desc = "LeetCode: Daily Challenge" }) +keymap.set("n", "ll", "Leet list", { desc = "LeetCode: List Problems" }) +keymap.set("n", "lc", "Leet console", { desc = "LeetCode: Open Console" }) +keymap.set("n", "lu", "Leet cookie update", { desc = "LeetCode: Update Cookie" }) +keymap.set("n", "lh", "Leet hints", { desc = "LeetCode: Open hints" }) +keymap.set("n", "lls", "Leet last", { desc = "LeetCode: Get latest submission" }) diff --git a/lua/cargdev/core/keymaps/lsp.lua b/lua/cargdev/core/keymaps/lsp.lua index e736be9..6407c17 100644 --- a/lua/cargdev/core/keymaps/lsp.lua +++ b/lua/cargdev/core/keymaps/lsp.lua @@ -1,27 +1,33 @@ --- LSP and function navigation keymaps +--- LSP and function navigation keymaps. +--- Uses fzf-lua pickers for go-to-definition, implementation, references, +--- type definitions, symbol search, code actions, rename, hover docs, +--- and diagnostic navigation. +--- @module keymaps.lsp + local keymap = vim.keymap -- ============================================================================= -- LSP NAVIGATION (FUNCTION NAVIGATION) - Using fzf-lua -- ============================================================================= --- Primary LSP navigation +--- Primary LSP navigation โ€” definition, implementation, references, type def. keymap.set("n", "gd", "FzfLua lsp_definitions", { desc = "Go to definition" }) keymap.set("n", "gi", "FzfLua lsp_implementations", { desc = "Go to implementation" }) keymap.set("n", "gr", "FzfLua lsp_references", { desc = "Show references" }) keymap.set("n", "gt", "FzfLua lsp_typedefs", { desc = "Go to type definition" }) --- Symbol search +--- Symbol search โ€” document and workspace symbols via fzf-lua. keymap.set("n", "ds", "FzfLua lsp_document_symbols", { desc = "Document symbols" }) keymap.set("n", "lw", "FzfLua lsp_workspace_symbols", { desc = "LSP: Workspace symbols" }) --- Code actions and documentation +--- Code actions, rename, and hover documentation. keymap.set("n", "ca", "FzfLua lsp_code_actions", { desc = "Code actions" }) keymap.set("n", "rn", "lua vim.lsp.buf.rename()", { desc = "Rename" }) -keymap.set("n", "K", "lua vim.lsp.buf.hover()", { desc = "Hover documentation" }) +keymap.set("n", "H", "lua vim.lsp.buf.hover()", { desc = "Hover documentation" }) --- Diagnostics +--- Diagnostics โ€” workspace list, line float, and prev/next navigation. keymap.set("n", "D", "FzfLua diagnostics_workspace", { desc = "Show diagnostics" }) keymap.set("n", "dd", "lua vim.diagnostic.open_float()", { desc = "Line diagnostics" }) keymap.set("n", "[d", "lua vim.diagnostic.goto_prev()", { desc = "Previous diagnostic" }) -keymap.set("n", "]d", "lua vim.diagnostic.goto_next()", { desc = "Next diagnostic" }) \ No newline at end of file +keymap.set("n", "]d", "lua vim.diagnostic.goto_next()", { desc = "Next diagnostic" }) + diff --git a/lua/cargdev/core/keymaps/openterminal.lua b/lua/cargdev/core/keymaps/openterminal.lua new file mode 100644 index 0000000..b8e7199 --- /dev/null +++ b/lua/cargdev/core/keymaps/openterminal.lua @@ -0,0 +1,17 @@ +--- Terminal-mode keymaps. +--- Provides shortcuts for navigating out of terminal buffers and closing them. +--- - `Ctrl+H` โ€” switch to the left window from terminal mode. +--- - `Ctrl+T` โ€” close the terminal window. +--- - `Ctrl+\ Ctrl+\` โ€” escape to normal mode from terminal mode. +--- @module keymaps.openterminal + +local keymap = vim.keymap + +--- Switch to the left window from terminal mode. +keymap.set("t", "", "h", { buffer = true }) + +--- Close the terminal window. +keymap.set("t", "", ":q", { buffer = true }) + +--- Escape terminal mode to normal mode. +keymap.set("t", "", "", { buffer = true }) diff --git a/lua/cargdev/core/keymaps/personal.lua b/lua/cargdev/core/keymaps/personal.lua index 2436daf..86cc537 100644 --- a/lua/cargdev/core/keymaps/personal.lua +++ b/lua/cargdev/core/keymaps/personal.lua @@ -1,66 +1,50 @@ --- Personal keymaps (original workflow) +--- Personal keymaps โ€” custom workflow shortcuts. +--- Includes text selection helpers, file management (save, quit, source), +--- number increment/decrement, buffer management with smart close via +--- snacks.bufdelete, coding shortcuts (React import, semicolons, commas, +--- console.log), Copilot Chat commands, clipboard HTML-to-Markdown paste, +--- and quickfix/location list navigation. +--- @module keymaps.personal + local keymap = vim.keymap -- ============================================================================= -- PERSONAL KEYMAPS (ORIGINAL WORKFLOW) -- ============================================================================= +--- Select the entire file contents (visual mode). keymap.set("n", "u", function() vim.cmd("normal! ggVG$") end, { desc = "Select the whole file" }) + +--- Duplicate the current line below. keymap.set("n", "4", function() -- Copy current line and paste below vim.cmd("normal! yy") vim.cmd("normal! p") end, { desc = "Copy the entire line and paste just below" }) --- file management +--- File management โ€” save, quit, force quit, source, and clear search. keymap.set("n", "w", ":w", { desc = "Save the current file" }) keymap.set("n", "xa", ":xa", { desc = "Save and close all the files" }) keymap.set("n", "q", ":q", { desc = "Quit" }) -keymap.set("n", "Q", ":q!", { desc = "Force quit" }) keymap.set("n", "so", ":source %", { desc = "Reload nvim" }) keymap.set("n", "no", ":noh ", { desc = "Reset search a word" }) --- increment/decrement numbers -keymap.set("n", "+", "", { desc = "Increment number" }) -- increment -keymap.set("n", "-", "", { desc = "Decrement number" }) -- decrement +--- Increment/decrement the number under the cursor. +keymap.set("n", "+", "", { desc = "Increment number" }) +keymap.set("n", "-", "", { desc = "Decrement number" }) -- Window management keymaps are centralized in lua/cargdev/core/keymaps/window.lua --- sintax fixer +--- Re-indent the entire file using Neovim's built-in `=` operator. keymap.set("n", "sy", "gg=G", { desc = "Format current file" }) +--- Fast scroll โ€” 10 lines at a time with Ctrl+E / Ctrl+Y. keymap.set("n", "", "10", { noremap = true, silent = true }) keymap.set("n", "", "10", { noremap = true, silent = true }) --- Buffer management with safe close (confirms if unsaved changes) --- Uses snacks.bufdelete for smart buffer deletion, shows dashboard on last buffer -local function close_buffer(force) - local ok, snacks = pcall(require, "snacks") - if ok and snacks.bufdelete then - -- snacks.bufdelete handles everything smartly - snacks.bufdelete({ force = force }) - else - -- Fallback to manual handling - local current_buf = vim.api.nvim_get_current_buf() - local buffers = vim.tbl_filter(function(buf) - return vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buflisted - end, vim.api.nvim_list_bufs()) - - if #buffers > 1 then - vim.cmd("bprevious") - vim.cmd((force and "bdelete! " or "bdelete ") .. current_buf) - else - -- Last buffer: show dashboard instead of quitting - vim.cmd("enew") - if ok and snacks.dashboard then - snacks.dashboard() - end - end - end -end - +--- Close the current buffer with a confirmation prompt if modified. keymap.set("n", "bd", function() if vim.bo.modified then vim.ui.select({ "Save & Close", "Discard & Close", "Cancel" }, { @@ -68,26 +52,24 @@ keymap.set("n", "bd", function() }, function(choice) if choice == "Save & Close" then vim.cmd("w") - close_buffer(false) + vim.cmd("bd") elseif choice == "Discard & Close" then - close_buffer(true) + vim.cmd("bd!") end end) else - close_buffer(false) + vim.cmd("bd") end end, { desc = "Buffer: Close (safe)" }) --- Force close buffer without confirmation -keymap.set("n", "bD", function() - close_buffer(true) -end, { desc = "Buffer: Force close" }) +--- Force close the current buffer, discarding unsaved changes. +keymap.set("n", "bD", ":db!", { desc = "Buffer: Force close" }) --- Set buftabline mappings +--- Buftabline navigation โ€” Ctrl+P next buffer, Ctrl+N previous buffer. keymap.set("n", "", ":bnext", { noremap = true, silent = true }) keymap.set("n", "", ":bprev", { noremap = true, silent = true }) --- Coding hacks +--- Coding shortcuts โ€” React import, trailing comma/semicolon, run with Node. keymap.set( "n", "re", @@ -100,11 +82,7 @@ keymap.set("n", "xr", ":!node %", { desc = "Run file with node" }) -- Resize splits keymaps are centralized in lua/cargdev/core/keymaps/window.lua --- Run and Debug Project -keymap.set("n", "pr", ":RunProject", { desc = "Run Project" }) -keymap.set("n", "pd", ":DebugProject", { desc = "Debug Project" }) - --- Copilot Chat (all Copilot keymaps moved to lua/cargdev/core/keymaps/copilot.lua) +--- Copilot Chat โ€” rename, explain, review, fix, optimize, and generate docs. keymap.set("v", "zn", ":CopilotChatRename", { desc = "Rename variable (Copilot Chat)" }) keymap.set("n", "zc", ":CopilotChat", { desc = "Open Copilot Chat" }) keymap.set("v", "ze", ":CopilotChatExplain", { desc = "Explain code (Copilot Chat)" }) @@ -113,15 +91,17 @@ keymap.set("v", "zf", ":CopilotChatFix", { desc = "Fix code issues ( keymap.set("v", "zo", ":CopilotChatOptimize", { desc = "Optimize code (Copilot Chat)" }) keymap.set("v", "zd", ":CopilotChatDocs", { desc = "Generate docs (Copilot Chat)" }) --- Paste HTML as Markdown using pandoc -keymap.set("n", "p", function() - vim.cmd("read !pbpaste -Prefer html | pandoc -f html -t gfm") -end, { desc = "Paste HTML clipboard as Markdown" }) +--- Paste HTML from the system clipboard as GitHub-Flavored Markdown (via pandoc). +--- TODO: Fix this keymap +-- keymap.set("n", "p", function() +-- vim.cmd("read !pbpaste -Prefer html | pandoc -f html -t gfm") +-- end, { desc = "Paste HTML clipboard as Markdown" }) -- ============================================================================= -- QUICKFIX NAVIGATION (under x for Trouble/Diagnostics group) -- ============================================================================= +--- Quickfix list navigation โ€” next, previous, open, close, first, last. keymap.set("n", "xn", ":cnextzz", { desc = "Quickfix: Next item" }) keymap.set("n", "xp", ":cprevzz", { desc = "Quickfix: Previous item" }) keymap.set("n", "xo", ":copen", { desc = "Quickfix: Open list" }) @@ -129,7 +109,7 @@ keymap.set("n", "xq", ":cclose", { desc = "Quickfix: Close list" }) keymap.set("n", "xf", ":cfirstzz", { desc = "Quickfix: First item" }) keymap.set("n", "xl", ":clastzz", { desc = "Quickfix: Last item" }) --- Location list navigation +--- Location list navigation โ€” next, previous, open, close. keymap.set("n", "ln", ":lnextzz", { desc = "Location: Next item" }) keymap.set("n", "lp", ":lprevzz", { desc = "Location: Previous item" }) keymap.set("n", "lo", ":lopen", { desc = "Location: Open list" }) diff --git a/lua/cargdev/core/keymaps/plugins.lua b/lua/cargdev/core/keymaps/plugins.lua index 6430445..0c4db40 100644 --- a/lua/cargdev/core/keymaps/plugins.lua +++ b/lua/cargdev/core/keymaps/plugins.lua @@ -1,125 +1,140 @@ --- Plugin-specific keymaps +--- Plugin-specific keymaps. Centralizes keybindings for third-party plugins: NvimTree, Comment, +--- LazyGit, DAP/DAP-UI, Trouble, ToggleTerm, sessions, formatting, +--- Substitute, Surround, LeetCode, Telescope/safe_files, database clients, +--- text wrapping, and auto-wrap controls. +--- @module keymaps.plugins + local keymap = vim.keymap -- ============================================================================= -- PLUGIN KEYMAPS -- ============================================================================= --- NvimTree +--- Toggle the NvimTree file explorer sidebar. keymap.set("n", "e", "NvimTreeToggle", { desc = "Toggle file explorer" }) --- Buffer management +--- Buffer management โ€” cycle through listed buffers with Shift+L / Shift+H. keymap.set("n", "", ":bnext", { noremap = true, silent = true }) keymap.set("n", "", ":bprevious", { noremap = true, silent = true }) --- Comment -keymap.set("n", "/", "lua require('Comment.api').toggle_current_linewise()", { desc = "Toggle comment" }) -keymap.set("v", "/", "lua require('Comment.api').toggle_linewise_op(vim.fn.visualmode())", { desc = "Toggle comment" }) +--- Comment.nvim โ€” toggle line/block comments in normal and visual mode. +keymap.set( + "n", + "/", + "lua require('Comment.api').toggle_current_linewise()", + { desc = "Toggle comment" } +) +keymap.set( + "v", + "/", + "lua require('Comment.api').toggle_linewise_op(vim.fn.visualmode())", + { desc = "Toggle comment" } +) --- Git +--- Open LazyGit in a floating terminal. keymap.set("n", "gg", "LazyGit", { desc = "LazyGit" }) --- DAP -keymap.set("n", "db", "lua require'dap'.toggle_breakpoint()", { desc = "Toggle breakpoint" }) -keymap.set("n", "dcc", "lua require'dap'.continue()", { desc = "Continue" }) -keymap.set("n", "di", "lua require'dap'.step_into()", { desc = "Step into" }) -keymap.set("n", "do", "lua require'dap'.step_over()", { desc = "Step over" }) -keymap.set("n", "dO", "lua require'dap'.step_out()", { desc = "Step out" }) -keymap.set("n", "dr", "lua require'dap'.repl.toggle()", { desc = "Toggle REPL" }) -keymap.set("n", "dl", "lua require'dap'.run_last()", { desc = "Run last" }) -keymap.set("n", "du", "lua require'dapui'.toggle()", { desc = "Toggle DAP UI" }) -keymap.set("n", "dt", "lua require'dapui'.float_element()", { desc = "Float element" }) +--- Trouble โ€” diagnostics, workspace errors, location list, and quickfix viewer. +keymap.set("n", "Xx", "TroubleToggle", { desc = "Toggle Trouble" }) +keymap.set("n", "Xw", "TroubleToggle workspace_diagnostics", { desc = "Workspace diagnostics" }) +keymap.set("n", "Xd", "TroubleToggle document_diagnostics", { desc = "Document diagnostics" }) +keymap.set("n", "Xl", "TroubleToggle loclist", { desc = "Location list" }) +keymap.set("n", "Xq", "TroubleToggle quickfix", { desc = "Quickfix list" }) --- Trouble -keymap.set("n", "xx", "TroubleToggle", { desc = "Toggle Trouble" }) -keymap.set("n", "xw", "TroubleToggle workspace_diagnostics", { desc = "Workspace diagnostics" }) -keymap.set("n", "xd", "TroubleToggle document_diagnostics", { desc = "Document diagnostics" }) -keymap.set("n", "xl", "TroubleToggle loclist", { desc = "Location list" }) -keymap.set("n", "xq", "TroubleToggle quickfix", { desc = "Quickfix list" }) - --- Terminal +--- ToggleTerm โ€” open terminals as float, horizontal, or vertical splits. keymap.set("n", "tf", "ToggleTerm direction=float", { desc = "ToggleTerm float" }) -keymap.set("n", "th", "ToggleTerm size=10 direction=horizontal", { desc = "ToggleTerm horizontal split" }) +keymap.set( + "n", + "th", + "ToggleTerm size=10 direction=horizontal", + { desc = "ToggleTerm horizontal split" } +) keymap.set("n", "tv", "ToggleTerm size=80 direction=vertical", { desc = "ToggleTerm vertical split" }) --- Session management (using sS and sR to avoid conflicts with substitute) +--- Session management โ€” save/restore sessions (uppercase S/R to avoid substitute conflicts). keymap.set("n", "sS", "SessionSave", { desc = "Session: Save" }) keymap.set("n", "sR", "SessionRestore", { desc = "Session: Restore" }) --- Formatting -keymap.set("n", "f", "lua vim.lsp.buf.format()", { desc = "Format buffer" }) +--- Format the current buffer using the attached LSP formatter. +--- TODO: add format buffer keymap +--- keymap.set("n", "f", "lua vim.lsp.buf.format()", { desc = "Format buffer" }) --- Substitute +--- Substitute.nvim โ€” operator, line, and end-of-line substitution. keymap.set("n", "sub", "lua require('substitute').operator()", { desc = "Substitute: With motion" }) keymap.set("n", "sl", "lua require('substitute').line()", { desc = "Substitute: Line" }) keymap.set("n", "S", "lua require('substitute').eol()", { desc = "Substitute: To end of line" }) --- Surround +--- nvim-surround โ€” add, delete, and replace surrounding pairs. keymap.set("n", "sa", "lua require('nvim-surround').surround_add()", { desc = "Add surrounding" }) -keymap.set("n", "sd", "lua require('nvim-surround').surround_delete()", { desc = "Delete surrounding" }) -keymap.set("n", "sr", "lua require('nvim-surround').surround_replace()", { desc = "Replace surrounding" }) +keymap.set( + "n", + "sd", + "lua require('nvim-surround').surround_delete()", + { desc = "Delete surrounding" } +) +keymap.set( + "n", + "sr", + "lua require('nvim-surround').surround_replace()", + { desc = "Replace surrounding" } +) -- Git conflicts (moved to lua/cargdev/core/keymaps/gitconflict.lua) --- LeetCode -keymap.set("n", "lr", "Leet run", { desc = "LeetCode: Run Code" }) -keymap.set("n", "ls", "Leet submit", { desc = "LeetCode: Submit Code" }) -keymap.set("n", "ld", "Leet daily", { desc = "LeetCode: Daily Challenge" }) -keymap.set("n", "ll", "Leet list", { desc = "LeetCode: List Problems" }) -keymap.set("n", "lc", "Leet console", { desc = "LeetCode: Open Console" }) -keymap.set("n", "lu", "Leet cookie update", { desc = "LeetCode: Update Cookie" }) -keymap.set("n", "lh", "Leet hints", { desc = "LeetCode: Open hints" }) -keymap.set("n", "lls", "Leet last", { desc = "LeetCode: Get latest submission" }) +--- Run project command via project_commands module. +keymap.set( + "n", + "p", + "lua require('cargdev.core.function.project_commands').run_project()", + { desc = "Run project" } +) --- Linting -keymap.set("n", "l", "Lint", { desc = "Lint current file" }) - --- Project commands -keymap.set("n", "p", "lua require('cargdev.core.function.project_commands').run_project()", { desc = "Run project" }) - --- Console log (different from personal con) +--- Insert a `console.log()` snippet on the line below and enter insert mode. keymap.set("n", "cl", "oconsole.log()i", { desc = "Add console.log" }) --- DAP UI reset -keymap.set("n", "drt", "lua require('dapui').float_element()", { desc = "Reset DAP UI layout" }) - --- DAP commands -keymap.set("n", "dco", "lua require('dap').commands()", { desc = "DAP commands" }) -keymap.set("n", "dcf", "lua require('dap').list_breakpoints()", { desc = "DAP configs" }) -keymap.set("n", "dcb", "lua require('dap').list_breakpoints()", { desc = "List breakpoints" }) - --- Step out -keymap.set("n", "dot", "lua require('dap').step_out()", { desc = "Step out" }) - --- Todos in trouble +--- Open TODO comments in the Trouble panel. keymap.set("n", "xt", "TodoTrouble", { desc = "Open todos in trouble" }) --- Surround mappings +--- nvim-surround classic-style mappings (ys, yss, yS, ySS). keymap.set("n", "ys", "lua require('nvim-surround').surround_add()", { desc = "Add surrounding" }) keymap.set("n", "yss", "lua require('nvim-surround').surround_add()", { desc = "Add surrounding to line" }) keymap.set("n", "yS", "lua require('nvim-surround').surround_add()", { desc = "Add surrounding on new lines" }) -keymap.set("n", "ySS", "lua require('nvim-surround').surround_add()", { desc = "Add surrounding to line on new lines" }) +keymap.set( + "n", + "ySS", + "lua require('nvim-surround').surround_add()", + { desc = "Add surrounding to line on new lines" } +) --- Comment mappings +--- Comment.nvim classic-style mappings (gc, gcc, gco, gcO, gcA, gb, gbc). keymap.set("n", "gc", "lua require('Comment.api').toggle_current_linewise()", { desc = "Toggle comment" }) -keymap.set("n", "gcc", "lua require('Comment.api').toggle_current_linewise()", { desc = "Toggle current line comment" }) +keymap.set( + "n", + "gcc", + "lua require('Comment.api').toggle_current_linewise()", + { desc = "Toggle current line comment" } +) keymap.set("n", "gco", "lua require('Comment.api').insert_below()", { desc = "Insert comment below" }) keymap.set("n", "gcO", "lua require('Comment.api').insert_above()", { desc = "Insert comment above" }) keymap.set("n", "gcA", "lua require('Comment.api').insert_eol()", { desc = "Insert comment at end of line" }) -keymap.set("n", "gb", "lua require('Comment.api').toggle_current_blockwise()", { desc = "Toggle block comment" }) -keymap.set("n", "gbc", "lua require('Comment.api').toggle_current_blockwise()", { desc = "Toggle current block comment" }) +keymap.set( + "n", + "gb", + "lua require('Comment.api').toggle_current_blockwise()", + { desc = "Toggle block comment" } +) +keymap.set( + "n", + "gbc", + "lua require('Comment.api').toggle_current_blockwise()", + { desc = "Toggle current block comment" } +) -- ============================================================================= -- TELESCOPE KEYMAPS (Enhanced with safe file searching) -- ============================================================================= --- Safe file search (prevents LSP errors and image freezing) -keymap.set("n", "ff", "Telescope safe_files find_files", { desc = "Find files (safe)" }) -keymap.set("n", "fs", "Telescope live_grep", { desc = "Live grep (safe)" }) -keymap.set("n", "fg", "Telescope git_files", { desc = "Git files (safe)" }) -keymap.set("n", "ft", "Telescope text_files find_files", { desc = "Text files only" }) - --- Regular telescope (use with caution) +--- Unfiltered Telescope file search (may include binaries โ€” use with caution). keymap.set("n", "fF", "Telescope find_files", { desc = "Find files (all)" }) -- ============================================================================= @@ -158,44 +173,44 @@ keymap.set("n", "fF", "Telescope find_files", { desc = "Find fi -- keymap.set("n", "dus", "DBUISaveBuffer", { desc = "Save database buffer" }) -- keymap.set("n", "dul", "DBUILoadBuffer", { desc = "Load database buffer" }) --- Redis specific +--- Redis CLI โ€” open, list keys, show info. keymap.set("n", "rds", "Redis", { desc = "Open Redis" }) keymap.set("n", "rdk", "RedisKeys", { desc = "Show Redis keys" }) keymap.set("n", "rdi", "RedisInfo", { desc = "Show Redis info" }) --- MongoDB specific +--- MongoDB โ€” open shell, connect, disconnect. keymap.set("n", "mdb", "MongoDB", { desc = "Open MongoDB" }) keymap.set("n", "mdc", "MongoDBConnect", { desc = "Connect to MongoDB" }) -keymap.set("n", "mdd", "MongoDBDisconnect", { desc = "Disconnect from MongoDB" }) +keymap.set("n", "mdd", "MongoDBDisconnect", { desc = "Disconnect from MongoDB" }) -- ============================================================================= -- NATIVE AUTO WRAPPER KEYMAPS -- ============================================================================= --- Text wrapping controls +--- Text wrapping controls โ€” toggle wrap, linebreak, and column guide. keymap.set("n", "tw", "set wrap!", { desc = "Toggle line wrapping" }) keymap.set("n", "tl", "set linebreak!", { desc = "Toggle line break" }) -keymap.set("n", "tc", "set colorcolumn=80", { desc = "Show 80 char column" }) -keymap.set("n", "tC", "set colorcolumn=", { desc = "Hide column guide" }) +keymap.set("n", "tx", "set colorcolumn=80", { desc = "Show 80 char column" }) +keymap.set("n", "tH", "set colorcolumn=", { desc = "Hide column guide" }) --- Format text using native Neovim commands -keymap.set("n", "tf", "gqap", { desc = "Format paragraph" }) +--- Format text using native Neovim `gq` command (paragraph, selection, file). +keymap.set("n", "tpg", "gqap", { desc = "Format paragraph" }) keymap.set("v", "tf", "gq", { desc = "Format selection" }) keymap.set("n", "tF", "gggqG", { desc = "Format entire file" }) --- Text width adjustments +--- Text width adjustments โ€” set to 80, 100, 120, or disable (0). keymap.set("n", "t80", "set textwidth=80", { desc = "Set text width to 80" }) keymap.set("n", "t100", "set textwidth=100", { desc = "Set text width to 100" }) keymap.set("n", "t120", "set textwidth=120", { desc = "Set text width to 120" }) keymap.set("n", "t0", "set textwidth=0", { desc = "Disable text width" }) --- Auto-wrap controls +--- Auto-wrap controls โ€” toggle `formatoptions` flags for text and comments. keymap.set("n", "ta", "set formatoptions+=t", { desc = "Enable auto-wrap text" }) keymap.set("n", "tA", "set formatoptions-=t", { desc = "Disable auto-wrap text" }) keymap.set("n", "tc", "set formatoptions+=c", { desc = "Enable auto-wrap comments" }) keymap.set("n", "tC", "set formatoptions-=c", { desc = "Disable auto-wrap comments" }) --- Indent and wrap +--- Indent and wrap โ€” toggle break indent and show/hide break indicator. keymap.set("n", "ti", "set breakindent!", { desc = "Toggle break indent" }) keymap.set("n", "ts", "set showbreak=โ†ช ", { desc = "Show break indicator" }) -keymap.set("n", "tS", "set showbreak=", { desc = "Hide break indicator" }) +keymap.set("n", "tS", "set showbreak=", { desc = "Hide break indicator" }) diff --git a/lua/cargdev/core/keymaps/project.lua b/lua/cargdev/core/keymaps/project.lua index 0ad6e97..bef6a2a 100644 --- a/lua/cargdev/core/keymaps/project.lua +++ b/lua/cargdev/core/keymaps/project.lua @@ -1,5 +1,12 @@ --- Project Run/Debug keymaps +--- Project run and debug keymaps. +--- Provides `p` prefixed shortcuts that invoke the `:RunProject` +--- and `:DebugProject` user commands defined in `project_commands.lua`. +--- @module keymaps.project + local keymap = vim.keymap +--- Execute the project run command from `.nvim/project.lua`. keymap.set("n", "pr", ":RunProject", { desc = "Run Project" }) + +--- Execute the project debug command from `.nvim/project.lua`. keymap.set("n", "pd", ":DebugProject", { desc = "Debug Project" }) diff --git a/lua/cargdev/core/keymaps/snacks.lua b/lua/cargdev/core/keymaps/snacks.lua index 14b363d..71bc83f 100644 --- a/lua/cargdev/core/keymaps/snacks.lua +++ b/lua/cargdev/core/keymaps/snacks.lua @@ -1,28 +1,33 @@ --- Snacks keymaps (replacing Telescope) +--- Snacks.nvim picker keymaps (replacing Telescope for most navigation). +--- Uses snacks.picker for file, grep, buffer, marks, keymaps, and command +--- searching. Falls back to Telescope for git operations and TODO search +--- where Snacks pickers are not yet available. +--- @module keymaps.snacks + local keymap = vim.keymap -- ============================================================================= -- SNACKS NAVIGATION -- ============================================================================= --- File navigation +--- File navigation โ€” find files, live grep, grep string, recent files. keymap.set("n", "ff", "lua require('snacks.picker').files()", { desc = "Find files" }) keymap.set("n", "fs", "lua require('snacks.picker').grep()", { desc = "Live grep" }) keymap.set("n", "fc", "lua require('snacks.picker').grep_string()", { desc = "Grep string" }) keymap.set("n", "fr", "lua require('snacks.picker').oldfiles()", { desc = "Recent files" }) --- Buffer and session management +--- Buffer and session management โ€” buffers, help tags, marks, keymaps, commands. keymap.set("n", "fb", "lua require('snacks.picker').buffers()", { desc = "Find buffers" }) keymap.set("n", "fh", "lua require('snacks.picker').help_tags()", { desc = "Help tags" }) keymap.set("n", "fm", "lua require('snacks.picker').marks()", { desc = "Find marks" }) keymap.set("n", "fk", "lua require('snacks.picker').keymaps()", { desc = "Find keymaps" }) keymap.set("n", "fC", "lua require('snacks.picker').commands()", { desc = "Find commands" }) --- Git (using Telescope for git features as Snacks may not have all git pickers) +--- Git โ€” commits, buffer commits, branches, and status (via Telescope fallback). keymap.set("n", "fG", "Telescope git_commits", { desc = "Git commits" }) keymap.set("n", "fB", "Telescope git_bcommits", { desc = "Git buffer commits" }) keymap.set("n", "fg", "Telescope git_branches", { desc = "Git branches" }) keymap.set("n", "gs", "Telescope git_status", { desc = "Git status" }) --- Todos (keep Telescope for todos as Snacks may not have this) -keymap.set("n", "ft", "TodoTelescope", { desc = "Find todos" }) +--- Search TODO/FIXME/HACK comments (via Telescope โ€” Snacks lacks this picker). +keymap.set("n", "ft", "TodoTelescope", { desc = "Find todos" }) diff --git a/lua/cargdev/core/keymaps/sudoku.lua b/lua/cargdev/core/keymaps/sudoku.lua deleted file mode 100644 index cfd78a9..0000000 --- a/lua/cargdev/core/keymaps/sudoku.lua +++ /dev/null @@ -1,18 +0,0 @@ --- Sudoku keymaps --- WARNING: sng is mapped to two different commands below. Only the last one will take effect in Neovim. --- Consider changing one of the mappings if you want both actions available. -local keymap = vim.keymap - -keymap.set("n", "si1", ":Sudoku insert=1", { desc = "Add number 1" }) -keymap.set("n", "si2", ":Sudoku insert=2", { desc = "Add number 2" }) -keymap.set("n", "si3", ":Sudoku insert=3", { desc = "Add number 3" }) -keymap.set("n", "si4", ":Sudoku insert=4", { desc = "Add number 4" }) -keymap.set("n", "si5", ":Sudoku insert=5", { desc = "Add number 5" }) -keymap.set("n", "si6", ":Sudoku insert=6", { desc = "Add number 6" }) -keymap.set("n", "si7", ":Sudoku insert=7", { desc = "Add number 7" }) -keymap.set("n", "si8", ":Sudoku insert=8", { desc = "Add number 8" }) -keymap.set("n", "si9", ":Sudoku insert=9", { desc = "Add number 9" }) -keymap.set("n", "scc", ":Sudoku clear_cell", { desc = "Clear current cell" }) -keymap.set("n", "su", ":Sudoku undo", { desc = "Undo last action" }) -keymap.set("n", "sng", ":Sudoku new_game", { desc = "Starts new game" }) -keymap.set("n", "sng", ":Sudoku view=settings", { desc = "Display the settings" }) diff --git a/lua/cargdev/core/keymaps/ufo.lua b/lua/cargdev/core/keymaps/ufo.lua index 69b1425..b322167 100644 --- a/lua/cargdev/core/keymaps/ufo.lua +++ b/lua/cargdev/core/keymaps/ufo.lua @@ -1,12 +1,27 @@ --- nvim-ufo folding keymaps +--- nvim-ufo folding keymaps. +--- Overrides the default `z` fold commands with nvim-ufo equivalents for +--- improved fold rendering. Also overrides `K` to peek folded lines under +--- the cursor, falling back to LSP hover when no fold is present. +--- All keymaps are only registered when nvim-ufo loads successfully. +--- @module keymaps.ufo + local ok_ufo, ufo = pcall(require, "ufo") local keymap = vim.keymap.set if ok_ufo then + --- Open every fold in the buffer. keymap("n", "zR", ufo.openAllFolds, { desc = "Open all folds" }) + + --- Close every fold in the buffer. keymap("n", "zM", ufo.closeAllFolds, { desc = "Close all folds" }) + + --- Open folds except specific kinds (e.g. imports, comments). keymap("n", "zr", ufo.openFoldsExceptKinds, { desc = "Open folds except kinds" }) + + --- Incrementally close folds by level. keymap("n", "zm", ufo.closeFoldsWith, { desc = "Close folds with" }) + + --- Peek the folded lines under the cursor; fall back to LSP hover. keymap("n", "K", function() local winid = ufo.peekFoldedLinesUnderCursor() if not winid then diff --git a/lua/cargdev/core/keymaps/window.lua b/lua/cargdev/core/keymaps/window.lua index 128995a..b5efc33 100644 --- a/lua/cargdev/core/keymaps/window.lua +++ b/lua/cargdev/core/keymaps/window.lua @@ -1,15 +1,24 @@ --- Window management keymaps +--- Window and tab management keymaps. +--- Provides split creation/closing, tab navigation, and Ctrl-based +--- resize shortcuts for both vertical and horizontal splits. +--- @module keymaps.window + local keymap = vim.keymap +--- Split management โ€” vertical, horizontal, equalize, and close. keymap.set("n", "sv", "v", { desc = "Split window vertically" }) keymap.set("n", "sh", "s", { desc = "Split window horizontally" }) keymap.set("n", "se", "=", { desc = "Make splits equal size" }) keymap.set("n", "sx", "close", { desc = "Close current split" }) + +--- Tab management โ€” new, close, next, previous, and open buffer in new tab. keymap.set("n", "to", "tabnew", { desc = "Open new tab" }) -keymap.set("n", "tx", "tabclose", { desc = "Close current tab" }) +keymap.set("n", "cx", "tabclose", { desc = "Close current tab" }) keymap.set("n", "tn", "tabn", { desc = "Go to next tab" }) keymap.set("n", "tp", "tabp", { desc = "Go to previous tab" }) -keymap.set("n", "tf", "tabnew %", { desc = "Open current buffer in new tab" }) +keymap.set("n", "tt", "tabnew %", { desc = "Open current buffer in new tab" }) + +--- Resize splits โ€” Ctrl+H/L for width, Ctrl+K/J for height (step of 5). keymap.set("n", "", ":vertical resize -5", { noremap = true, silent = true }) keymap.set("n", "", ":vertical resize +5", { noremap = true, silent = true }) keymap.set("n", "", ":resize +5", { noremap = true, silent = true }) diff --git a/lua/cargdev/plugins/codetyper.lua b/lua/cargdev/plugins/codetyper.lua deleted file mode 100644 index c7c27d7..0000000 --- a/lua/cargdev/plugins/codetyper.lua +++ /dev/null @@ -1,125 +0,0 @@ --- ============================================================================ --- CODETYPER.NVIM: AI-powered coding assistant plugin --- ============================================================================ --- A local development plugin that provides AI-assisted coding capabilities --- using various LLM providers (Ollama, Claude, OpenAI, Gemini, Copilot). --- Features include: inline code transformation with /@ @/ tags, Ask panel --- for interactive queries, Agent panel for autonomous coding tasks, --- Tree-sitter integration for scope detection, and diff review. --- --- Key keymaps: --- co - Open Coder view ca - Open Ask panel --- ct - Toggle Coder view cg - Open Agent panel --- cp - Process prompt cd - Open Diff Review --- ctt - Transform tag at cursor (also works in visual mode) --- ============================================================================ - --- Get local config (loaded in core/init.lua) -local local_cfg = vim.g.cargdev_local or {} - --- Skip plugin if local config is missing required values -if not local_cfg.CODE_TYPER_DIR then - return {} -end - -return { - -- Codetyper.nvim - AI-powered coding partner - -- Local development version - dir = local_cfg.CODE_TYPER_DIR, - name = "codetyper.nvim", - lazy = false, -- Load on startup to create .coder folder - priority = 100, -- Load early - dependencies = { - "nvim-lua/plenary.nvim", -- Required: async utilities - -- "nvim-treesitter/nvim-treesitter", -- Required: scope detection via Tree-sitter - -- "nvim-treesitter/nvim-treesitter-textobjects", -- Optional: better text object support - "MunifTanjim/nui.nvim", -- Optional: UI components - }, - event = { - "BufReadPre *.coder.*", - "BufNewFile *.coder.*", - }, - cmd = { - "Coder", - "CoderOpen", - "CoderClose", - "CoderToggle", - "CoderProcess", - "CoderTree", - "CoderTreeView", - -- Ask commands - "CoderAsk", - "CoderAskToggle", - "CoderAskClear", - -- Agent commands - "CoderAgent", - "CoderAgentToggle", - "CoderAgentStop", - "CoderMode", - }, - keys = { - -- Coder view commands - { "co", "Coder open", desc = "Coder: Open view" }, - { "cC", "Coder close", desc = "Coder: Close view" }, - { "ct", "Coder toggle", desc = "Coder: Toggle view" }, - { "cp", "Coder process", desc = "Coder: Process prompt" }, - { "cs", "Coder status", desc = "Coder: Show status" }, - { "cf", "Coder focus", desc = "Coder: Switch focus" }, - { "cv", "Coder tree-view", desc = "Coder: View tree" }, - { "cr", "Coder tree", desc = "Coder: Refresh tree" }, - -- Ask panel commands - { "ca", "Coder ask", desc = "Coder: Open Ask panel" }, - { "cA", "Coder ask-toggle", desc = "Coder: Toggle Ask panel" }, - { "cx", "Coder ask-clear", desc = "Coder: Clear Ask history" }, - -- Agent panel commands - { "cg", "Coder agent", desc = "Coder: Open Agent panel" }, - { "cG", "Coder agent-toggle", desc = "Coder: Toggle Agent panel" }, - { "cS", "Coder agent-stop", desc = "Coder: Stop Agent" }, - { "cd", "CoderDiffReview", desc = "Coder: Open Diff Review" }, - -- Transform commands (inline /@ @/ replacement) - { "ctt", mode = "n", desc = "Coder: Transform tag at cursor" }, - { "ctt", mode = "v", desc = "Coder: Transform selected tags" }, - { "ctT", "Coder transform", desc = "Coder: Transform all tags" }, - }, - config = function() - require("codetyper").setup({ - llm = { - -- Available providers: "ollama", "claude", "openai", "gemini", "copilot" - provider = "copilot", -- Using GitHub Copilot - - -- Ollama (local LLM) - ollama = { - host = "http://localhost:11434", - model = "deepseek-coder:6.7b", - -- model = "codellama:7b", - -- model = "qwen2.5-coder:7b", - }, - - -- GitHub Copilot (uses OAuth from copilot.vim/copilot.lua) - copilot = { - model = "gpt-4o", -- or "gpt-4", "gpt-3.5-turbo" - }, - }, - window = { - width = 0.25, -- 1/4 of window - position = "left", - border = "rounded", - }, - patterns = { - open_tag = "/@", - close_tag = "@/", - file_pattern = "*.coder.*", - }, - auto_gitignore = true, - auto_open_ask = false, -- Don't auto-open Ask panel on startup - scheduler = { - enabled = true, - ollama_scout = false, -- Disabled since using Copilot directly - escalation_threshold = 0.7, - max_concurrent = 2, - completion_delay_ms = 100, -- Delay before checking completion visibility - apply_delay_ms = 2000, -- Wait 2 seconds before applying code - }, - }) - end, -} diff --git a/lua/cargdev/plugins/copilot.lua b/lua/cargdev/plugins/copilot.lua index 40b2e90..790c018 100644 --- a/lua/cargdev/plugins/copilot.lua +++ b/lua/cargdev/plugins/copilot.lua @@ -1,27 +1,33 @@ -- ============================================================================ -- COPILOT: GitHub AI code completion and chat -- ============================================================================ --- AI-powered code suggestions that appear as ghost text while typing. --- Includes copilot-cmp for completion menu integration and codecompanion --- for AI chat/refactoring. Disabled for LaTeX files. Accept with . +-- AI-powered code suggestions (ghost text + cmp menu) and chat. +-- Autocomplete: accept ghost text, also shows in cmp menu. +-- Chat: cc toggle, cq quick chat, ce explain. +-- +-- MODEL SELECTION: +-- Chat: :CopilotChatModels (pick from gpt-4o, claude-sonnet-4, o3-mini, etc.) +-- Autocomplete: controlled server-side by GitHub, not user-selectable. -- ============================================================================ return { + -- Copilot core: ghost text suggestions + LSP backend { "zbirenbaum/copilot.lua", cmd = "Copilot", event = "InsertEnter", config = function() require("copilot").setup({ + copilot_model = "gpt-41-copilot", panel = { enabled = true, auto_refresh = false, layout = { - position = "bottom", -- | top | left | right + position = "bottom", ratio = 0.4, }, }, suggestion = { - enabled = true, -- Codetyper will use copilot when available + enabled = true, auto_trigger = true, debounce = 75, keymap = { @@ -39,10 +45,10 @@ return { hgcommit = true, svn = true, cvs = true, - tex = false, -- Disable Copilot for LaTeX files by default + tex = false, ["."] = true, }, - copilot_node_command = "node", -- Node.js version must be > 16.x + copilot_node_command = "node", server_opts_overrides = {}, }) @@ -50,18 +56,18 @@ return { vim.api.nvim_create_autocmd({ "FileType", "BufEnter" }, { pattern = "tex", callback = function() - -- Safely dismiss any active suggestions local ok, suggestion = pcall(require, "copilot.suggestion") if ok and suggestion and suggestion.is_visible() then suggestion.dismiss() end - -- Disable Copilot for this buffer vim.cmd("Copilot disable") end, desc = "Disable Copilot for LaTeX files", }) end, }, + + -- Copilot CMP: adds Copilot as a completion source in nvim-cmp menu { "zbirenbaum/copilot-cmp", dependencies = { "zbirenbaum/copilot.lua" }, @@ -69,28 +75,58 @@ return { require("copilot_cmp").setup() end, }, + + -- CopilotChat: full chat interface with model selection { - "olimorris/codecompanion.nvim", + "CopilotC-Nvim/CopilotChat.nvim", dependencies = { "zbirenbaum/copilot.lua", "nvim-lua/plenary.nvim", - "nvim-telescope/telescope.nvim", }, - cmd = { "CodeCompanion" }, + build = "make tiktoken", + cmd = { + "CopilotChat", + "CopilotChatToggle", + "CopilotChatModels", + "CopilotChatExplain", + "CopilotChatReview", + "CopilotChatFix", + "CopilotChatOptimize", + "CopilotChatDocs", + "CopilotChatTests", + "CopilotChatCommit", + }, + keys = { + { "cc", "CopilotChatToggle", desc = "CopilotChat: Toggle chat window" }, + { "cq", function() + local input = vim.fn.input("Quick Chat: ") + if input ~= "" then + require("CopilotChat").ask(input, { selection = require("CopilotChat.select").buffer }) + end + end, desc = "CopilotChat: Quick chat (whole buffer)" }, + { "ce", "CopilotChatExplain", mode = { "n", "v" }, desc = "CopilotChat: Explain code" }, + { "cr", "CopilotChatReview", mode = { "n", "v" }, desc = "CopilotChat: Review code" }, + { "cf", "CopilotChatFix", mode = { "n", "v" }, desc = "CopilotChat: Fix code" }, + { "co", "CopilotChatOptimize", mode = { "n", "v" }, desc = "CopilotChat: Optimize code" }, + { "cd", "CopilotChatDocs", mode = { "n", "v" }, desc = "CopilotChat: Generate docs" }, + { "ct", "CopilotChatTests", mode = { "n", "v" }, desc = "CopilotChat: Generate tests" }, + { "cm", "CopilotChatModels", desc = "CopilotChat: Select model" }, + }, config = function() - require("codecompanion").setup({ - -- Use GitHub Copilot as the provider - providers = { - copilot = { - enabled = true, - }, + require("CopilotChat").setup({ + -- Default model (change with :CopilotChatModels at runtime) + -- Options include: gpt-4o, claude-sonnet-4, o3-mini, gemini-2.0-flash, etc. + model = "claude-sonnet-4", + window = { + layout = "vertical", + width = 0.35, + border = "rounded", }, - -- Configure the UI - ui = { - window = { - width = 0.8, - height = 0.8, - }, + mappings = { + complete = { insert = "" }, + close = { normal = "q", insert = "" }, + reset = { normal = "", insert = "" }, + submit_prompt = { normal = "", insert = "" }, }, }) end, diff --git a/lua/cargdev/plugins/curls.lua b/lua/cargdev/plugins/curls.lua deleted file mode 100644 index 571d9d8..0000000 --- a/lua/cargdev/plugins/curls.lua +++ /dev/null @@ -1,87 +0,0 @@ --- ============================================================================ --- REST.NVIM: HTTP client for making API requests from Neovim --- ============================================================================ --- A fork of rest.nvim that allows executing HTTP requests directly from --- .http files within Neovim. Features include request highlighting, --- automatic response formatting, cookie management, and environment --- variable support. Requires LuaRocks dependencies: mimetypes and xml2lua. --- --- Key UI keybinds: --- H - Navigate to previous response --- L - Navigate to next response --- ============================================================================ - -return { - "CarGDev/rest.nvim", - build = function() - -- Install LuaRocks dependencies for Lua 5.1 (Neovim uses LuaJIT which is Lua 5.1 compatible) - local packages = { "mimetypes", "xml2lua" } - for _, pkg in ipairs(packages) do - local result = vim.fn.system("luarocks install --local --lua-version=5.1 " .. pkg .. " 2>&1") - if vim.v.shell_error ~= 0 and not result:match("already installed") then - vim.notify("Warning: Failed to install " .. pkg .. ": " .. result, vim.log.levels.WARN) - end - end - end, - dependencies = { - "nvim-treesitter/nvim-treesitter", - opts = function(_, opts) - opts.ensure_installed = opts.ensure_installed or {} - table.insert(opts.ensure_installed, "http") - end, - }, - config = function() - -- Verify LuaRocks dependencies are available - local function check_dependency(name) - local success = pcall(require, name) - if not success then - vim.notify( - string.format( - "rest.nvim: Missing dependency '%s'. Please run: luarocks install --local %s", - name, - name - ), - vim.log.levels.WARN - ) - end - return success - end - - -- Check for required dependencies - check_dependency("mimetypes") - check_dependency("xml2lua") - - -- Basic configuration for rest.nvim - vim.g.rest_nvim = { - -- Enable request highlighting - highlight = { - enable = true, - timeout = 750, - }, - -- Enable response formatting - response = { - hooks = { - format = true, - decode_url = true, - }, - }, - -- Enable cookies - cookies = { - enable = true, - }, - -- Enable environment variables - env = { - enable = true, - pattern = ".*%.env.*", - }, - -- UI configuration - ui = { - winbar = true, - keybinds = { - prev = "H", - next = "L", - }, - }, - } - end, -} diff --git a/lua/cargdev/plugins/dap.lua b/lua/cargdev/plugins/dap.lua index 1609bdc..2f1d001 100644 --- a/lua/cargdev/plugins/dap.lua +++ b/lua/cargdev/plugins/dap.lua @@ -88,7 +88,9 @@ return { require("mason-nvim-dap").setup({ ensure_installed = { "python", "js", "javadbg", "javatest" }, automatic_installation = true, - handlers = {}, + handlers = { + firefox = function() end, -- Disable Firefox auto-config for TS/JS + }, }) -- ๐Ÿ” Virtual Text @@ -232,6 +234,20 @@ return { } dap.configurations.typescript = { + { + name = "Launch with Bun", + type = "pwa-node", + request = "launch", + runtimeExecutable = "bun", + runtimeArgs = { "--inspect-brk" }, + program = "${file}", + cwd = "${workspaceFolder}", + sourceMaps = true, + resolveSourceMapLocations = { "${workspaceFolder}/**", "!**/node_modules/**" }, + console = "internalConsole", + skipFiles = { "/**", "node_modules/**" }, + attachSimplePort = 6499, + }, { name = "Launch NestJS (dist/main.js)", type = "pwa-node", @@ -245,7 +261,7 @@ return { skipFiles = { "/**", "node_modules/**" }, }, { - name = "Launch Current File", + name = "Launch Current File (Node)", type = "pwa-node", request = "launch", program = "${file}", @@ -289,6 +305,74 @@ return { }, } + -- ๐ŸŽฎ DAP Keymaps (registered here so dap/dapui are already loaded) + keymap("n", "dcr", 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" }) + keymap("n", "drt", function() + dap.terminate() + dapui.close() + vim.defer_fn(function() + dapui.open() + end, 200) + end, { desc = "๐Ÿงผ Reset DAP UI Layout" }) + keymap("n", "dcf", function() + local ft = vim.bo.filetype + local configs = dap.configurations[ft] or {} + if #configs == 0 then + vim.notify("No DAP configurations for filetype: " .. ft, vim.log.levels.WARN) + return + end + vim.ui.select(configs, { + prompt = "Select DAP configuration:", + format_item = function(item) return item.name end, + }, function(config) + if config then dap.run(config) end + end) + end, { desc = "๐Ÿ”ญ DAP Configs" }) + keymap("n", "dcb", function() + dap.list_breakpoints() + vim.cmd("copen") + end, { desc = "๐Ÿงท List Breakpoints" }) + keymap("n", "dco", dap.repl.open, { desc = "โš™๏ธ DAP Commands" }) + + -- ๐Ÿ”Œ Dynamic Debug Attach (no hardcoded ports) + -- Bun requires --inspect=host:port/debug to set a known WebSocket path + -- because it doesn't implement /json/list for auto-discovery. + keymap("n", "jd", function() + vim.ui.input({ prompt = "Inspector port (default 6499): " }, function(input) + if input == nil then return end -- cancelled + local port = input ~= "" and input or "6499" + local port_num = tonumber(port) + if not port_num then + vim.notify("Invalid port number", vim.log.levels.ERROR) + return + end + local ws_url = "ws://localhost:" .. port .. "/debug" + vim.notify("Attaching to " .. ws_url, vim.log.levels.INFO) + dap.run({ + name = "Attach (" .. ws_url .. ")", + type = "pwa-node", + request = "attach", + websocketAddress = ws_url, + cwd = fn.getcwd(), + sourceMaps = true, + resolveSourceMapLocations = { "${workspaceFolder}/**", "!**/node_modules/**" }, + skipFiles = { "/**", "node_modules/**" }, + restart = true, + }) + end) + end, { desc = "๐Ÿ”Œ Debug Attach (dynamic)" }) + -- JavaScript uses same configurations as TypeScript dap.configurations.javascript = { { @@ -309,5 +393,9 @@ return { skipFiles = { "/**", "node_modules/**" }, }, } + + -- TSX/JSX use the same configurations as their base languages + dap.configurations.typescriptreact = dap.configurations.typescript + dap.configurations.javascriptreact = dap.configurations.javascript end, } diff --git a/lua/cargdev/plugins/edgy.lua b/lua/cargdev/plugins/edgy.lua deleted file mode 100644 index 86412e8..0000000 --- a/lua/cargdev/plugins/edgy.lua +++ /dev/null @@ -1,48 +0,0 @@ --- ============================================================================ --- EDGY: Window layout management --- ============================================================================ --- Manages fixed window layouts for sidebars and panels (file tree, outline, etc.). --- Keeps special windows docked at consistent positions and sizes. --- Configures NvimTree (left), Outline (right), and various bottom panels. --- ============================================================================ - -return { - "folke/edgy.nvim", - event = "VeryLazy", - opts = { - left = { - { - ft = "NvimTree", - size = { width = 35 }, - }, - }, - right = { - { - ft = "Outline", - size = { width = 30 }, - }, - }, - bottom = { - { - ft = "snacks_terminal", - size = { height = 0.25 }, - }, - { - ft = "trouble", - size = { height = 0.25 }, - }, - { - ft = "qf", - size = { height = 0.2 }, - }, - }, - animate = { enabled = false }, - exit_when_last = true, - wo = { - winbar = false, - winfixwidth = true, - winfixheight = false, - signcolumn = "no", - }, - }, -} diff --git a/lua/cargdev/plugins/flash.lua b/lua/cargdev/plugins/flash.lua deleted file mode 100644 index 24daaeb..0000000 --- a/lua/cargdev/plugins/flash.lua +++ /dev/null @@ -1,61 +0,0 @@ --- ============================================================================ --- FLASH: Fast navigation with search labels --- ============================================================================ --- Enhanced motion plugin - type a few chars, then jump directly to any match --- using labeled hints. Works across windows. Press 's' to start flash jump, --- or use with 'f', 't' motions. Much faster than repeated w/b movements. --- ============================================================================ -return { - "folke/flash.nvim", - event = "VeryLazy", - opts = { - labels = "asdfghjklqwertyuiopzxcvbnm", - search = { - multi_window = true, - forward = true, - wrap = true, - }, - jump = { - jumplist = true, - pos = "start", - history = false, - register = false, - nohlsearch = false, - autojump = false, - }, - label = { - uppercase = false, - rainbow = { - enabled = true, - shade = 5, - }, - }, - modes = { - search = { - enabled = false, -- set to true to enable flash in search mode - }, - char = { - enabled = true, - jump_labels = true, - multi_line = false, - }, - treesitter = { - labels = "asdfghjklqwertyuiopzxcvbnm", - jump = { pos = "range" }, - search = { incremental = false }, - label = { before = true, after = true, style = "inline" }, - highlight = { - backdrop = false, - matches = false, - }, - }, - }, - }, - keys = { - { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" }, - { "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" }, - { "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" }, - { "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" }, - { "", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" }, - }, -} diff --git a/lua/cargdev/plugins/formatting.lua b/lua/cargdev/plugins/formatting.lua index d59b05c..091c24c 100644 --- a/lua/cargdev/plugins/formatting.lua +++ b/lua/cargdev/plugins/formatting.lua @@ -45,7 +45,8 @@ return { lua = { "stylua" }, python = { "isort", "black" }, sql = { "sqlfluff" }, -- SQL formatting - java = { "google-java-format" }, + -- java: no conform formatter (google-java-format needs JDK 21+) + -- falls through to JDTLS LSP via lsp_fallback = true }, format_on_save = function(bufnr) -- Disable autoformat for certain filetypes diff --git a/lua/cargdev/plugins/linting.lua b/lua/cargdev/plugins/linting.lua index 2f93e11..197836b 100644 --- a/lua/cargdev/plugins/linting.lua +++ b/lua/cargdev/plugins/linting.lua @@ -88,7 +88,7 @@ return { end, }) - keymap.set("n", "l", function() + keymap.set("n", "lf", function() lint.try_lint() end, { desc = "Trigger linting for current file" }) end, diff --git a/lua/cargdev/plugins/nvim-cmp.lua b/lua/cargdev/plugins/nvim-cmp.lua index eef7ed9..be35398 100644 --- a/lua/cargdev/plugins/nvim-cmp.lua +++ b/lua/cargdev/plugins/nvim-cmp.lua @@ -75,6 +75,7 @@ return { }), -- sources for autocompletion sources = cmp.config.sources({ + { name = "copilot", priority = 1100, group_index = 1 }, -- Copilot suggestions in menu { name = "nvim_lsp", priority = 1000 }, { name = "luasnip", priority = 750 }, -- snippets { name = "buffer", priority = 500, keyword_length = 3 }, -- text within current buffer @@ -86,6 +87,7 @@ return { format = lspkind.cmp_format({ maxwidth = 50, ellipsis_char = "...", + symbol_map = { Copilot = "" }, }), }, diff --git a/lua/cargdev/plugins/obsidian.lua b/lua/cargdev/plugins/obsidian.lua new file mode 100644 index 0000000..488a698 --- /dev/null +++ b/lua/cargdev/plugins/obsidian.lua @@ -0,0 +1,53 @@ +-- ============================================================================ +-- OBSIDIAN: Full Obsidian vault integration for Neovim +-- ============================================================================ +-- Wiki-link completion, daily notes, templates, backlinks, Telescope search, +-- and more. Replaces the custom follow_obsidian_link() function. +-- ============================================================================ +return { + "epwalsh/obsidian.nvim", + version = "*", + dependencies = { + "nvim-lua/plenary.nvim", + "hrsh7th/nvim-cmp", + "nvim-telescope/telescope.nvim", + }, + event = { + "BufReadPre " .. vim.fn.expand("~") .. "/Nextcloud/ObsidianVault/**.md", + "BufNewFile " .. vim.fn.expand("~") .. "/Nextcloud/ObsidianVault/**.md", + }, + ft = "markdown", + keys = { + { "on", "ObsidianNew", desc = "New note" }, + { "od", "ObsidianToday", desc = "Today's daily note" }, + { "os", "ObsidianSearch", desc = "Search notes" }, + { "ob", "ObsidianBacklinks", desc = "Show backlinks" }, + { "ot", "ObsidianTemplate", desc = "Insert template" }, + { "of", "ObsidianFollowLink", desc = "Follow link" }, + { "ol", "ObsidianLink", mode = "v", desc = "Link selection to note" }, + }, + opts = { + workspaces = { + { + name = "vault", + path = "/Users/carlos/Nextcloud/ObsidianVault", + }, + }, + daily_notes = { + folder = "daily", + date_format = "%Y-%m-%d", + }, + templates = { + folder = "templates", + }, + completion = { + nvim_cmp = true, + min_chars = 2, + }, + preferred_link_style = "wiki", + use_advanced_uri = false, + picker = { + name = "telescope.nvim", + }, + }, +} diff --git a/scripts/detect_keymap_conflicts.lua b/scripts/detect_keymap_conflicts.lua new file mode 100644 index 0000000..e0504ce --- /dev/null +++ b/scripts/detect_keymap_conflicts.lua @@ -0,0 +1,149 @@ +--- detect_keymap_conflicts.lua +--- Detects duplicate keymap bindings across neovim config files. +--- Run from the nvim config root: lua scripts/detect_keymap_conflicts.lua + +local handle = io.popen("find lua -name '*.lua' -print") +if not handle then + print("Error: could not search for lua files") + os.exit(1) +end +local files = {} +for path in handle:lines() do + files[#files + 1] = path +end +handle:close() + +--- Build a lookup table: line_starts[i] = byte position where line i begins. +local function build_line_index(content) + local starts = { 1 } + for i = 1, #content do + if content:sub(i, i) == "\n" then + starts[#starts + 1] = i + 1 + end + end + return starts +end + +--- Binary search to find the line number for a byte position. +local function line_at(line_starts, pos) + local lo, hi = 1, #line_starts + while lo < hi do + local mid = math.floor((lo + hi + 1) / 2) + if line_starts[mid] <= pos then + lo = mid + else + hi = mid - 1 + end + end + return lo +end + +--- Return true if `pos` sits after a "--" comment marker on the same line. +local function is_in_comment(content, line_starts, pos) + local ln = line_at(line_starts, pos) + local line_start = line_starts[ln] + local before = content:sub(line_start, pos - 1) + return before:match("%-%-") ~= nil +end + +--- Split a mode argument into individual mode strings. +--- "n" -> {"n"}, {"n", "v"} -> {"n", "v"} +local function extract_modes(mode_arg) + local modes = {} + for m in mode_arg:gmatch([=["([^"]+)"]=]) do + modes[#modes + 1] = m + end + if #modes == 0 then + for m in mode_arg:gmatch("'([^']+)'") do + modes[#modes + 1] = m + end + end + if #modes == 0 then + local trimmed = mode_arg:gsub("^%s+", ""):gsub("%s+$", "") + if #trimmed > 0 then + modes[#modes + 1] = trimmed + end + end + return modes +end + +-- Patterns to match keymap setter calls. +-- Two variants per caller style: single-string mode and table mode. +-- Using [=[...]=] to avoid issues with ]] inside patterns. +-- () captures the byte position for line-number lookup. +local patterns = { + -- vim.keymap.set("n", "", ...) + [=[()vim%.keymap%.set%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=], + -- vim.keymap.set({"n", "v"}, "", ...) + [=[()vim%.keymap%.set%(%s*(%b{})%s*,%s*["']([^"']+)["']]=], + -- keymap.set("n", "", ...) + [=[()keymap%.set%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=], + -- keymap.set({"n", "v"}, "", ...) + [=[()keymap%.set%(%s*(%b{})%s*,%s*["']([^"']+)["']]=], + -- keymap("n", "", ...) โ€” when local keymap = vim.keymap.set + [=[()keymap%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=], + -- keymap({"n", "v"}, "", ...) + [=[()keymap%(%s*(%b{})%s*,%s*["']([^"']+)["']]=], + -- map("n", "", ...) โ€” wrapper function + [=[()map%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=], + -- map({"n", "v"}, "", ...) + [=[()map%(%s*(%b{})%s*,%s*["']([^"']+)["']]=], +} + +local keymaps = {} -- "mode|lhs" -> list of "file:line" +local seen = {} -- dedup: "file:line:lhs" -> true + +for _, f in ipairs(files) do + local fh = io.open(f, "r") + if fh then + -- Read entire file so patterns can span multiple lines (%s matches \n). + local content = fh:read("*a") + fh:close() + local line_starts = build_line_index(content) + + for _, pat in ipairs(patterns) do + for pos, mode_arg, lhs in content:gmatch(pat) do + if not is_in_comment(content, line_starts, pos) then + local ln = line_at(line_starts, pos) + local dedup_key = f .. ":" .. ln .. ":" .. lhs + if not seen[dedup_key] then + seen[dedup_key] = true + local modes = extract_modes(mode_arg) + for _, mode in ipairs(modes) do + local key = mode .. "|" .. lhs + keymaps[key] = keymaps[key] or {} + keymaps[key][#keymaps[key] + 1] = string.format("%s:%d", f, ln) + end + end + end + end + end + end +end + +-- Collect and sort conflicts +local conflicts = {} +for key, locations in pairs(keymaps) do + if #locations > 1 then + local mode, lhs = key:match("^(.-)|(.*)") + conflicts[#conflicts + 1] = { mode = mode, lhs = lhs, locations = locations } + end +end + +table.sort(conflicts, function(a, b) + if a.mode == b.mode then return a.lhs < b.lhs end + return a.mode < b.mode +end) + +if #conflicts == 0 then + print("No duplicate keymaps found.") +else + print(string.format("Found %d conflicting keymap(s):\n", #conflicts)) + for _, c in ipairs(conflicts) do + print(string.format(" [%s] %s", c.mode, c.lhs)) + for _, loc in ipairs(c.locations) do + print(" - " .. loc) + end + print() + end +end