Files
modulus/tools/nvim/modulus.lua
T
khodowany b9d30fb975
publish / publish (push) Successful in 11m40s
Clangd
2026-09-11 00:01:55 -05:00

97 lines
3.4 KiB
Lua

local M = {}
local function within(path, root)
return path == root or path:sub(1, #root + 1) == root .. "/"
end
function M.setup(root)
if vim.fn.has("nvim-0.11") ~= 1 then
vim.notify("Modulus container clangd requires Neovim 0.11+", vim.log.levels.ERROR)
return
end
root = vim.fs.normalize(root)
-- Finish LazyVim's server setup before changing only the command and root routing.
local has_lazy, lazy = pcall(require, "lazy")
if has_lazy then
lazy.load({ plugins = { "nvim-lspconfig" } })
end
local state = rawget(_G, "modulus_clangd")
if not state then
state = { roots = {} }
_G.modulus_clangd = state
local original = vim.lsp.config.clangd or { cmd = { "clangd" }, filetypes = { "c", "cpp", "objc", "objcpp", "cuda" } }
if not vim.lsp.config.clangd then
vim.lsp.config("clangd", original)
end
local original_cmd, original_root = original.cmd, original.root_dir
local cache = vim.fs.normalize((vim.env.XDG_CACHE_HOME or (vim.env.HOME .. "/.cache")) .. "/modulus/clangd")
local function project(path)
local best
for candidate in pairs(state.roots) do
if within(path, candidate) and (not best or #candidate > #best) then
best = candidate
end
end
return best
end
state.project = project
vim.lsp.config("clangd", {
cmd = function(dispatchers, config)
local selected = config.root_dir and state.roots[config.root_dir]
if selected then
config._modulus_container = true
return vim.lsp.rpc.start(selected, dispatchers, { cwd = config.root_dir })
end
if type(original_cmd) == "function" then
return original_cmd(dispatchers, config)
end
return vim.lsp.rpc.start(original_cmd or { "clangd" }, dispatchers, { cwd = config.root_dir })
end,
root_dir = function(buf, on_dir)
local name = vim.api.nvim_buf_get_name(buf)
if within(name, cache) then
local clients = vim.tbl_filter(function(client) return client.config._modulus_container end, vim.lsp.get_clients({ name = "clangd" }))
if #clients == 1 then
vim.lsp.buf_attach_client(buf, clients[1].id)
end
return
end
local selected = project(name)
if selected then
on_dir(selected)
elseif type(original_root) == "function" then
original_root(buf, on_dir)
elseif type(original_root) == "string" then
on_dir(original_root)
else
on_dir(vim.fs.root(name, { ".clangd", "compile_commands.json", ".git" }) or vim.fs.dirname(name))
end
end,
})
vim.api.nvim_create_autocmd("BufReadPost", {
group = vim.api.nvim_create_augroup("ModulusClangdHeaders", { clear = true }),
callback = function(event)
if within(vim.api.nvim_buf_get_name(event.buf), cache) then
vim.bo[event.buf].readonly = true
vim.bo[event.buf].modifiable = false
end
end,
})
end
local command
if vim.uv.fs_stat(root .. "/modulus/cli.py") then
command = { "python3", root .. "/tools/clangd.py" }
else
command = { "python3", root .. "/modulus.py", "clangd" }
end
state.roots[root] = command
for _, client in ipairs(vim.lsp.get_clients({ name = "clangd" })) do
if not client.config._modulus_container and client.config.root_dir and within(client.config.root_dir, root) then
client:stop()
end
end
vim.lsp.enable("clangd")
end
return M