- Remove locally-developed or rarely-used plugin packages (codetyper, curls, edgy, flash) to reduce maintenance surface, external deps, and startup cost; these were local/optional and caused complexity. - Clean up stale/keymap docs and small duplicate or experimental files (keymaps/README.md, sudoku keymaps). - Apply targeted refactors to core modules for readability, robustness, and to align Java/DAP/Copilot integrations with local environment. - Removed plugin modules: - lua/cargdev/plugins/codetyper.lua (deleted) - lua/cargdev/plugins/curls.lua (deleted) - lua/cargdev/plugins/edgy.lua (deleted) - lua/cargdev/plugins/flash.lua (deleted) - Added .codetyper to .gitignore - Java / JDTLS: - ftplugin/java.lua: pin jdtls java executable to JDK 25, switch to mac_arm config, enable LSP formatter and declare multiple runtimes (JavaSE-17, JavaSE-25). - Notifications & startup UX: - lua/cargdev/core/function/notification_manager.lua: large refactor — use local aliases (api, cmd, loop, opt), improved docs, dashboard-aware handling, nvim-notify fallback, safer tracking and cleanup of notifications. - Utilities & monitors: - lua/cargdev/core/function/performance_monitor.lua: use local references, better notify usage and docs. - lua/cargdev/core/function/project_commands.lua: add docs, use local api/fn/notify, clearer RunProject/DebugProject commands. - Terminal helper: - lua/cargdev/core/function/openTerminal.lua: add module docs and small refactor to use local cmd/api aliases and expose keymap. - Keymaps & docs: - Removed stale keymaps README (lua/cargdev/core/keymaps/README.md). - Reworked many keymap files: docstrings, renamed/moved mappings to avoid conflicts, moved DAP mappings into dap plugin config, removed/disabled experimental mappings (see files under lua/cargdev/core/keymaps/*). - Adjusted quick keybindings and prefixes to reduce overlaps. - Plugins & integrations: - lua/cargdev/plugins/formatting.lua: disabled google-java-format fallback for java (prefer JDTLS). - lua/cargdev/plugins/dap.lua: added additional DAP configs (Bun launch, attach helper), moved/registered dap keymaps here. - lua/cargdev/plugins/copilot.lua: reorganized Copilot/Copilot-cmp/CopilotChat config and added copilot source to nvim-cmp. - lua/cargdev/plugins/nvim-cmp.lua: added copilot source and small formatting tweak.
150 lines
4.5 KiB
Lua
150 lines
4.5 KiB
Lua
--- 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", "<key>", ...)
|
|
[=[()vim%.keymap%.set%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=],
|
|
-- vim.keymap.set({"n", "v"}, "<key>", ...)
|
|
[=[()vim%.keymap%.set%(%s*(%b{})%s*,%s*["']([^"']+)["']]=],
|
|
-- keymap.set("n", "<key>", ...)
|
|
[=[()keymap%.set%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=],
|
|
-- keymap.set({"n", "v"}, "<key>", ...)
|
|
[=[()keymap%.set%(%s*(%b{})%s*,%s*["']([^"']+)["']]=],
|
|
-- keymap("n", "<key>", ...) — when local keymap = vim.keymap.set
|
|
[=[()keymap%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=],
|
|
-- keymap({"n", "v"}, "<key>", ...)
|
|
[=[()keymap%(%s*(%b{})%s*,%s*["']([^"']+)["']]=],
|
|
-- map("n", "<key>", ...) — wrapper function
|
|
[=[()map%(%s*["']([^"']+)["']%s*,%s*["']([^"']+)["']]=],
|
|
-- map({"n", "v"}, "<key>", ...)
|
|
[=[()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
|