diff --git a/lua/custom/plugins/debug.lua b/lua/custom/plugins/debug.lua index 61066d1d..8a3080de 100644 --- a/lua/custom/plugins/debug.lua +++ b/lua/custom/plugins/debug.lua @@ -55,6 +55,7 @@ return { }, config = function() local dap = require 'dap' + local utils = require 'custom.utils' -- Configure the LLDB DAP adapter for C/C++ -- Assumes 'lldb-dap' executable is in PATH (from pkgs.llvmPackages_XX.lldb) @@ -63,19 +64,15 @@ return { command = 'lldb-dap', name = 'lldb-dap (Nix)', } + dap.configurations.cpp = { { name = 'Launch C/C++ (lldb-dap)', type = 'lldb', request = 'launch', - program = coroutine.wrap(function() - local exe = require('custom.utils').pick_executable(vim.fn.getcwd() .. '/build') - if not exe then - vim.notify('Debug session cancelled: executable not selected', vim.log.levels.INFO) - return nil - end - return exe - end), + program = function() + return utils.pick_executable_for_dap(vim.fn.getcwd() .. '/build') + end, cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, diff --git a/lua/custom/utils.lua b/lua/custom/utils.lua index 68dba294..0ae2d965 100644 --- a/lua/custom/utils.lua +++ b/lua/custom/utils.lua @@ -1,64 +1,70 @@ -local function is_executable(path) - -- Use `file` command to identify Mach-O or ELF binaries - local output = vim.fn.system { 'file', '-b', path } - return output:match 'Mach%-O' or output:match 'ELF' -end +local async = require 'plenary.async.util' +local pickers = require 'telescope.pickers' +local finders = require 'telescope.finders' +local sorters = require('telescope.config').values +local actions = require 'telescope.actions' +local action_state = require 'telescope.actions.state' +local Path = require 'plenary.path' -local function collect_executables(dir) - local files = vim.fn.globpath(dir, '**', true, true) - local binaries = {} - for _, path in ipairs(files) do - if vim.fn.filereadable(path) == 1 and is_executable(path) and not path:match 'CMakeFiles' then - table.insert(binaries, path) +local function collect_executables(start_dir) + local results = {} + local fd = vim.fn.systemlist { 'fd', '--type', 'x', '--exec', 'file', '{}', start_dir } + for _, line in ipairs(fd) do + local path, typeinfo = line:match '^(.-):%s*(.+)$' + if path and not path:match 'CMakeFiles' and typeinfo and typeinfo:match 'Mach%-O' or typeinfo:match 'ELF' then + table.insert(results, path) end end - return binaries + return results end -local function pick_executable(start_dir) - local co = coroutine.running() - if not co then - error 'pick_executable must be called from a coroutine' - end - +local function pick_executable(start_dir, on_choice) local executables = collect_executables(start_dir) if #executables == 0 then vim.notify('No executables found in ' .. start_dir, vim.log.levels.WARN) + on_choice(nil) return end - local actions = require 'telescope.actions' - local action_state = require 'telescope.actions.state' - - require('telescope.pickers') + pickers .new({}, { prompt_title = 'Select Executable', - finder = require('telescope.finders').new_table { - results = executables, - }, - sorter = require('telescope.config').values.generic_sorter {}, + finder = finders.new_table { results = executables }, + sorter = sorters.generic_sorter {}, attach_mappings = function(_, map) - map('i', '', function(prompt_bufnr) + actions.select_default:replace(function(prompt_bufnr) local entry = action_state.get_selected_entry() actions.close(prompt_bufnr) - coroutine.resume(co, entry.value) + on_result(entry.value) end) - map('i', '', function(prompt_bufnr) - actions.close(prompt_bufnr) - coroutine.resume(co, nil) + map('i', '', function(buf) + actions.close(buf) + on_result(nil) end) - map('n', 'q', function(prompt_bufnr) - actions.close(prompt_bufnr) - coroutine.resume(co, nil) + map('n', 'q', function(buf) + actions.close(buf) + on_result(nil) end) return true end, }) :find() +end - return coroutine.yield() +local function pick_executable_for_dap(start_dir) + return async.void(function() + local co = coroutine.running() + pick_executable(start_dir, function(choice) + coroutine.resume(co, choice) + end) + local result = coroutine.yield() + if not result then + vim.notify('Debug launch cancelled', vim.log.levels.INFO) + end + return result + end)() end return { - pick_executable = pick_executable, + pick_executable_for_dap = pick_executable_for_dap, }