-- lua/c/utils.lua local M = {} -- Find the project root (same markers clangd likes) local function project_root(buf) return vim.fs.root(buf or 0, { "compile_commands.json", ".git", "Makefile", "makefile" }) end local function relpath(abs, root) if not abs or not root then return nil end if abs:sub(1, #root) ~= root then return nil end local r = abs:sub(#root + 1) if r:sub(1, 1) == "/" then r = r:sub(2) end return r end local function dirpart(p) return p:match("^(.*)/") end -- Build the alternate path (header <-> source), preserving nested subdirs local function alternate_rel(rel) local first = rel:match("^([^/]+)/") local ext = rel:match("%.([^.]+)$") or "" local rest = rel:gsub("^" .. (first or "") .. "/", "") if first == "include" and ext == "h" then return "src/" .. rest:gsub("%.h$", ".c") elseif first == "src" and ext == "c" then return "include/" .. rest:gsub("%.c$", ".h") end -- Fallbacks if file isn’t directly under include/ or src/ if ext == "h" then return ("src/" .. rel):gsub("^src/include/", "src/"):gsub("%.h$", ".c") elseif ext == "c" then return ("include/" .. rel):gsub("^include/src/", "include/"):gsub("%.c$", ".h") end return nil end function M.toggle_header_source() local buf = 0 local abs = vim.api.nvim_buf_get_name(buf) if abs == "" then vim.notify("No file name (unsaved buffer).", vim.log.levels.WARN) return end local root = project_root(buf) if not root then vim.notify("Couldn’t locate project root (no .git/Makefile/compile_commands.json).", vim.log.levels.WARN) return end local rel = relpath(abs, root) if not rel then vim.notify("File not under project root: " .. abs, vim.log.levels.WARN) return end local alt_rel = alternate_rel(rel) if not alt_rel then vim.notify("Not a .c/.h under include/ or src/: " .. rel, vim.log.levels.WARN) return end local alt_abs = root .. "/" .. alt_rel if vim.loop.fs_stat(alt_abs) then vim.cmd.edit(vim.fn.fnameescape(alt_abs)) return end -- Prompt to create local choice = vim.fn.confirm(("Create %s?\n\n%s"):format(alt_rel, alt_abs), "&Yes\n&No", 2) if choice ~= 1 then return end -- Ensure dirs exist, then open the new file (unsaved until you write) local dir = dirpart(alt_abs) if dir and not vim.loop.fs_stat(dir) then vim.fn.mkdir(dir, "p") end vim.cmd.edit(vim.fn.fnameescape(alt_abs)) -- Optional: write an empty file immediately. Comment out if you prefer unsaved buffer. -- vim.cmd.write() end return M