88 lines
2.5 KiB
Lua
88 lines
2.5 KiB
Lua
-- 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
|