feat(lsp,debugger,formatting): C/C++ setup with clang

This commit is contained in:
Guz
2026-03-30 17:06:53 -03:00
parent 769f2c9168
commit 4c9f1b911f
6 changed files with 275 additions and 0 deletions

27
lua/dap-lldb/COPYRIGHT Normal file
View File

@@ -0,0 +1,27 @@
Contents of `dap-lldb/init.lua` were copied from julianolf's
nvim-dap-lldb plugin at Git commit `0bf5fcd01a160fd918831591e715e2bd1e01073e` <https://github.com/julianolf/nvim-dap-lldb/tree/0bf5fcd01a160fd918831591e715e2bd1e01073e>,
which is licensed under the MIT License. A copy of the original license can be found at
the nvim-dap-go's GitHub repositoy <https://github.com/julianolf/nvim-dap-lldb/blob/b4421153ead5d726603b02743ea40cf26a51ed5f/LICENSE>
and below:
The MIT License (MIT)
Copyright (c) 2024 Juliano Fernandes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

238
lua/dap-lldb/init.lua Normal file
View File

@@ -0,0 +1,238 @@
---@mod dap-lldb LLDB extension for nvim-dap
---@brief [[
---An extension for nvim-dap to provide C, C++, and Rust debugging support.
---@brief ]]
local M = {}
local ts_query = [[
(mod_item
name: (identifier) @module
body: (declaration_list
(attribute_item (attribute (identifier) @attribute (#eq? @attribute "test")))
(function_item
name: (identifier) @function)))
]]
local function require_dap()
local ok, dap = pcall(require, "dap")
assert(ok, "nvim-dap is required to use dap-lldb")
return dap
end
local function compiler_error(input)
local _, json = pcall(vim.fn.json_decode, input)
if type(json) == "table" and json.reason == "compiler-message" then
return json.message.rendered
end
return nil
end
local function compiler_target(input)
local _, json = pcall(vim.fn.json_decode, input)
if
type(json) == "table"
and json.reason == "compiler-artifact"
and json.executable ~= nil
and (vim.tbl_contains(json.target.kind, "bin") or json.profile.test)
then
return json.executable
end
return nil
end
local function read_target()
local cwd = vim.fs.joinpath(vim.fn.getcwd(), "")
return vim.fn.input("Path to executable: ", cwd, "file")
end
local function list_targets(selection)
local arg = string.format("--%s", selection or "bins")
local cmd = { "cargo", "build", arg, "--quiet", "--message-format", "json" }
local out = vim.fn.systemlist(cmd)
if vim.v.shell_error ~= 0 then
local errors = vim.tbl_map(compiler_error, out)
vim.notify(table.concat(errors, "\n"), vim.log.levels.ERROR)
return nil
end
local function filter(e)
return e ~= nil
end
return vim.tbl_filter(filter, vim.tbl_map(compiler_target, out))
end
local function select_target(selection)
local targets = list_targets(selection)
if targets == nil then
return nil
end
if #targets == 0 then
return read_target()
end
if #targets == 1 then
return targets[1]
end
local options = { "Select a target:" }
for index, target in ipairs(targets) do
local name = vim.fs.basename(target)
local option = string.format("%d. %s", index, name)
table.insert(options, option)
end
local choice = vim.fn.inputlist(options)
return targets[choice]
end
local function select_test()
local filetype = vim.bo.filetype
if filetype ~= "rust" or vim.treesitter.language.get_lang(filetype) == nil then
return nil
end
local bufnr = vim.api.nvim_get_current_buf()
local query = vim.treesitter.query.parse(filetype, ts_query)
local parser = vim.treesitter.get_parser(bufnr, filetype)
local tree = parser:parse()[1]
local root = tree:root()
local stop = vim.api.nvim_win_get_cursor(0)[1]
local mod = nil
local fun = nil
for id, node in query:iter_captures(root, bufnr, 0, stop) do
local capture = query.captures[id]
if capture == "module" then
mod = vim.treesitter.get_node_text(node, 0)
elseif capture == "function" then
fun = vim.treesitter.get_node_text(node, 0)
end
end
if not mod or not fun then
return nil
end
return string.format("%s::%s", mod, fun)
end
local function read_args()
local args = vim.fn.input("Enter args: ")
return vim.split(args, " ", { trimempty = true })
end
local function default_configurations(dap)
local cfg = {
name = "Debug",
type = "lldb",
request = "launch",
cwd = "${workspaceFolder}",
program = read_target,
stopOnEntry = false,
}
dap.configurations.c = {
cfg,
vim.tbl_extend("force", cfg, { name = "Debug (+args)", args = read_args }),
vim.tbl_extend("force", cfg, { name = "Attach debugger", request = "attach" }),
}
dap.configurations.cpp = vim.tbl_extend("keep", {}, dap.configurations.c)
dap.configurations.rust = {
vim.tbl_extend("force", cfg, { program = select_target }),
vim.tbl_extend("force", cfg, { name = "Debug (+args)", program = select_target, args = read_args }),
vim.tbl_extend("force", cfg, {
name = "Debug tests",
program = function()
return select_target("tests")
end,
args = { "--test-threads=1" },
}),
vim.tbl_extend("force", cfg, {
name = "Debug tests (+args)",
program = function()
return select_target("tests")
end,
args = function()
return vim.list_extend(read_args(), { "--test-threads=1" })
end,
}),
vim.tbl_extend("force", cfg, {
name = "Debug test (cursor)",
program = function()
return select_target("tests")
end,
args = function()
local test = select_test()
local args = test and { "--exact", test } or {}
return vim.list_extend(args, { "--test-threads=1" })
end,
}),
vim.tbl_extend("force", cfg, { name = "Attach debugger", request = "attach", program = select_target }),
}
end
local function custom_configurations(dap, opts)
if type(opts.configurations) == "table" then
for lang, cfg in pairs(opts.configurations) do
local config = dap.configurations[lang] or {}
dap.configurations[lang] = vim.list_extend(config, cfg)
end
end
end
---@class SetupOpts
---@field codelldb_path string|nil Path to CodeLLDB extension
---@field configurations table|nil Per programming language configuration
---@see https://github.com/vadimcn/codelldb/blob/master/MANUAL.md
---Register LLDB debug adapter
---@param opts SetupOpts|nil See |dap-lldb.SetupOpts|
function M.setup(opts)
opts = type(opts) == "table" and opts or {}
local dap = require_dap()
local codelldb = opts.codelldb_path or "codelldb"
dap.adapters.lldb = {
type = "server",
port = "${port}",
executable = {
command = codelldb,
args = { "--port", "${port}" },
detached = vim.loop.os_uname().sysname ~= "Windows",
},
}
default_configurations(dap)
custom_configurations(dap, opts)
end
---Debug test function above the cursor
function M.debug_test()
if vim.bo.filetype ~= "rust" then
vim.notify("This feature is available only for Rust", vim.log.levels.ERROR)
return nil
end
local dap = require_dap()
local cfg = dap.configurations.rust[5]
dap.run(cfg)
end
return M

View File

@@ -18,5 +18,8 @@ end
-- Languages
-- C/C++
require("dap-lldb").setup()
-- Go
require("dap-go").setup()

View File

@@ -24,6 +24,8 @@ end
conform.setup({
formatters_by_ft = {
c = { "clang-format", lsp_format = "fallback" },
cpp = { "clang-format", lsp_format = "fallback" },
css = prettier,
gdscript = { "gdformat" },
go = function(bufnr)

View File

@@ -1,6 +1,10 @@
---@type lze.PluginSpec[] | { lsp: vim.lsp.Config }[]
return {
-- Language Server Providers
{
"clangd",
lsp = { filetypes = { "c", "cpp", "objc", "objcpp", "cuda" } },
},
{
"cssls",
lsp = { filetypes = { "css", "scss", "less" } },

View File

@@ -109,6 +109,7 @@ in
vscode-langservers-extracted # cssls, eslint, html, jsonls, typescript
clang-tools
docker-language-server
emmet-language-server
golangci-lint-langserver