diff --git a/lua/dap-lldb/COPYRIGHT b/lua/dap-lldb/COPYRIGHT new file mode 100644 index 0000000..fbc1091 --- /dev/null +++ b/lua/dap-lldb/COPYRIGHT @@ -0,0 +1,27 @@ +Contents of `dap-lldb/init.lua` were copied from julianolf's +nvim-dap-lldb plugin at Git commit `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 +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. diff --git a/lua/dap-lldb/init.lua b/lua/dap-lldb/init.lua new file mode 100644 index 0000000..b892e1a --- /dev/null +++ b/lua/dap-lldb/init.lua @@ -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 diff --git a/lua/dot/debugger.lua b/lua/dot/debugger.lua index af347e9..a5f7e8f 100644 --- a/lua/dot/debugger.lua +++ b/lua/dot/debugger.lua @@ -18,5 +18,8 @@ end -- Languages +-- C/C++ +require("dap-lldb").setup() + -- Go require("dap-go").setup() diff --git a/lua/dot/formatting.lua b/lua/dot/formatting.lua index 090326e..4ae8b2e 100644 --- a/lua/dot/formatting.lua +++ b/lua/dot/formatting.lua @@ -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) diff --git a/lua/dot/lsp.lua b/lua/dot/lsp.lua index d53a56f..7bd6c19 100644 --- a/lua/dot/lsp.lua +++ b/lua/dot/lsp.lua @@ -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" } }, diff --git a/package.nix b/package.nix index 0bd8216..f8e40cf 100644 --- a/package.nix +++ b/package.nix @@ -109,6 +109,7 @@ in vscode-langservers-extracted # cssls, eslint, html, jsonls, typescript + clang-tools docker-language-server emmet-language-server golangci-lint-langserver