268 lines
7.7 KiB
Lua
268 lines
7.7 KiB
Lua
vim.opt_local.cinoptions = vim.opt_local.cinoptions + '0{1' -- Places opening brace on the same line as function signature
|
|
vim.opt_local.smartindent = true -- Automatically adds indentation where needed
|
|
vim.opt_local.tabstop = 4 -- Set tab stop to 4 spaces
|
|
vim.opt_local.shiftwidth = 4 -- Set shift width to 4 spaces
|
|
vim.opt_local.softtabstop = 4 -- Use spaces instead of tabs
|
|
|
|
vim.api.nvim_create_user_command('RunCpp', function(opts)
|
|
vim.cmd('w') -- Save the current file
|
|
local filename = vim.fn.expand('%') -- Get current file name
|
|
local output = 'bin/' .. vim.fn.expand('%:r'):match('([^/]+)$') .. '.out' -- Output file with .out extension
|
|
|
|
-- Use provided compiler or default to 'clang++'
|
|
local compiler = opts.args ~= '' and opts.args or 'clang++'
|
|
|
|
-- Ensure the output directory exists
|
|
vim.fn.mkdir('bin', 'p')
|
|
|
|
-- Compile and run the C++ code
|
|
vim.cmd('!' .. compiler .. ' ' .. filename .. ' -o ' .. output .. ' && ./' .. output)
|
|
end, { nargs = '?' }) -- Accepts one optional argument for the compiler
|
|
|
|
local function cmake_build_dir(name)
|
|
local dir = vim.fn.getcwd() .. '/' .. (name or 'build')
|
|
if vim.fn.isdirectory(dir) == 0 then
|
|
vim.fn.mkdir(dir, 'p')
|
|
end
|
|
return dir
|
|
end
|
|
|
|
local function cmake_configure(build_dir, extra_args)
|
|
local args = extra_args or ''
|
|
local bin_dir = vim.fn.getcwd() .. '/bin'
|
|
vim.fn.mkdir(bin_dir, 'p')
|
|
local out_args = ' -DCMAKE_RUNTIME_OUTPUT_DIRECTORY='
|
|
.. vim.fn.shellescape(bin_dir)
|
|
.. ' -DCMAKE_LIBRARY_OUTPUT_DIRECTORY='
|
|
.. vim.fn.shellescape(bin_dir)
|
|
.. ' -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY='
|
|
.. vim.fn.shellescape(bin_dir)
|
|
vim.cmd('!cmake -S . -B ' .. vim.fn.fnameescape(build_dir) .. out_args .. ' ' .. args)
|
|
end
|
|
|
|
local function cmake_build(build_dir, extra_args)
|
|
local args = extra_args or ''
|
|
vim.cmd('!cmake --build ' .. vim.fn.fnameescape(build_dir) .. ' ' .. args)
|
|
end
|
|
|
|
local function cmake_ctest(build_dir)
|
|
vim.cmd('!ctest --test-dir ' .. vim.fn.fnameescape(build_dir) .. ' --output-on-failure')
|
|
end
|
|
|
|
local function select_cmake_preset()
|
|
local lines = vim.fn.systemlist('cmake --list-presets')
|
|
local presets = {}
|
|
for _, line in ipairs(lines) do
|
|
local name = line:match('^%s*"([^"]+)"')
|
|
if name then
|
|
table.insert(presets, name)
|
|
end
|
|
end
|
|
if #presets == 0 then
|
|
return
|
|
end
|
|
vim.ui.select(presets, { prompt = 'CMake preset' }, function(choice)
|
|
if choice then
|
|
vim.g.cmake_preset = choice
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function cmake_configure_with_preset()
|
|
local preset = vim.g.cmake_preset
|
|
if preset and preset ~= '' then
|
|
vim.cmd('!cmake --preset ' .. preset)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function cmake_build_with_preset()
|
|
local preset = vim.g.cmake_preset
|
|
if preset and preset ~= '' then
|
|
vim.cmd('!cmake --build --preset ' .. preset)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function toggle_debugger_repl()
|
|
local dbg = vim.__cpp_dbg_term
|
|
if dbg and dbg.is_open and dbg:is_open() then
|
|
dbg:close()
|
|
return
|
|
end
|
|
|
|
local ok_term, term_mod = pcall(require, 'toggleterm.terminal')
|
|
if not ok_term then
|
|
return
|
|
end
|
|
|
|
local cmd
|
|
if vim.fn.has('mac') == 1 then
|
|
cmd = 'lldb'
|
|
else
|
|
cmd = 'gdb'
|
|
end
|
|
|
|
if not vim.__cpp_dbg_term then
|
|
vim.__cpp_dbg_term = term_mod.Terminal:new({
|
|
cmd = cmd,
|
|
direction = 'vertical',
|
|
size = function()
|
|
return math.floor(vim.o.columns * 0.4)
|
|
end,
|
|
hidden = true,
|
|
on_open = function(term)
|
|
vim.api.nvim_buf_set_keymap(term.bufnr, 'n', 'q', '<cmd>close<CR>', { noremap = true, silent = true })
|
|
vim.api.nvim_buf_set_keymap(term.bufnr, 't', '<Esc><Esc>', '<C-\\><C-n>', { noremap = true, silent = true })
|
|
end,
|
|
})
|
|
end
|
|
|
|
vim.__cpp_dbg_term:open()
|
|
vim.cmd('wincmd p')
|
|
end
|
|
|
|
local opts = { buffer = true, silent = true }
|
|
|
|
local function switch_source_header()
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
local function edit_file(fname)
|
|
if not fname or fname == '' then
|
|
return
|
|
end
|
|
vim.cmd('edit ' .. vim.fn.fnameescape(fname))
|
|
end
|
|
|
|
local clangd
|
|
for _, client in ipairs(vim.lsp.get_clients({ bufnr = bufnr })) do
|
|
if client.name == 'clangd' then
|
|
clangd = client
|
|
break
|
|
end
|
|
end
|
|
|
|
if clangd then
|
|
clangd.request('textDocument/switchSourceHeader', { uri = vim.uri_from_bufnr(bufnr) }, function(err, result)
|
|
if err then
|
|
vim.notify('clangd: switchSourceHeader failed', vim.log.levels.WARN)
|
|
return
|
|
end
|
|
if not result then
|
|
vim.notify('No corresponding header/source found', vim.log.levels.INFO)
|
|
return
|
|
end
|
|
vim.schedule(function()
|
|
edit_file(vim.uri_to_fname(result))
|
|
end)
|
|
end, bufnr)
|
|
return
|
|
end
|
|
|
|
local file = vim.api.nvim_buf_get_name(bufnr)
|
|
if file == '' then
|
|
return
|
|
end
|
|
|
|
local root = file:gsub('%.[^%.]+$', '')
|
|
local ext = vim.fn.fnamemodify(file, ':e')
|
|
local candidates
|
|
|
|
if ext == 'h' or ext == 'hpp' or ext == 'hh' or ext == 'hxx' then
|
|
candidates = { 'cpp', 'cc', 'cxx', 'c' }
|
|
elseif ext == 'c' then
|
|
candidates = { 'h' }
|
|
else
|
|
candidates = { 'h', 'hpp', 'hh', 'hxx' }
|
|
end
|
|
|
|
for _, e in ipairs(candidates) do
|
|
local cand = root .. '.' .. e
|
|
if vim.fn.filereadable(cand) == 1 then
|
|
edit_file(cand)
|
|
return
|
|
end
|
|
end
|
|
|
|
vim.notify('No corresponding header/source found', vim.log.levels.INFO)
|
|
end
|
|
|
|
local function memory_profile_command(run_cmd)
|
|
if vim.fn.has('mac') == 1 then
|
|
return 'leaks --atExit -- ' .. run_cmd
|
|
end
|
|
return 'valgrind --leak-check=full --track-origins=yes ' .. run_cmd
|
|
end
|
|
|
|
vim.keymap.set('n', '<leader>cS', select_cmake_preset, vim.tbl_extend('force', opts, { desc = 'CMake: Select preset' }))
|
|
|
|
vim.keymap.set('n', '<leader>cb', function()
|
|
if cmake_configure_with_preset() then
|
|
cmake_build_with_preset()
|
|
return
|
|
end
|
|
local build_dir = cmake_build_dir('build')
|
|
cmake_configure(build_dir)
|
|
cmake_build(build_dir)
|
|
end, vim.tbl_extend('force', opts, { desc = 'CMake: Build' }))
|
|
|
|
vim.keymap.set('n', '<leader>ct', function()
|
|
local build_dir = cmake_build_dir('build')
|
|
cmake_ctest(build_dir)
|
|
end, vim.tbl_extend('force', opts, { desc = 'CMake: Test' }))
|
|
|
|
vim.keymap.set('n', '<leader>cr', function()
|
|
local cmd = vim.fn.input('Run command: ', './')
|
|
if cmd == '' then
|
|
return
|
|
end
|
|
vim.cmd('!' .. cmd)
|
|
end, vim.tbl_extend('force', opts, { desc = 'CMake: Run (prompt)' }))
|
|
|
|
vim.keymap.set(
|
|
'n',
|
|
'<leader>ch',
|
|
switch_source_header,
|
|
vim.tbl_extend('force', opts, { desc = 'C++: Switch header/source' })
|
|
)
|
|
|
|
vim.keymap.set(
|
|
'n',
|
|
'<leader>cg',
|
|
toggle_debugger_repl,
|
|
vim.tbl_extend('force', opts, { desc = 'C++: Toggle debugger REPL' })
|
|
)
|
|
|
|
vim.keymap.set('n', '<leader>cm', function()
|
|
local cmd = vim.fn.input('Memory profile run: ', './bin/')
|
|
if cmd == '' then
|
|
return
|
|
end
|
|
vim.cmd('!' .. memory_profile_command(cmd))
|
|
end, vim.tbl_extend('force', opts, { desc = 'C++: Memory profile (leaks/valgrind)' }))
|
|
|
|
vim.keymap.set('n', '<leader>Rr', toggle_debugger_repl, vim.tbl_extend('force', opts, { desc = 'Run/REPL: Toggle' }))
|
|
|
|
vim.keymap.set('n', '<leader>Rf', function()
|
|
local cmd = vim.fn.input('Run: ', './bin/')
|
|
if cmd == '' then
|
|
return
|
|
end
|
|
vim.cmd('!' .. cmd)
|
|
end, vim.tbl_extend('force', opts, { desc = 'Run/REPL: Run' }))
|
|
|
|
vim.keymap.set('n', '<leader>Ri', function()
|
|
local dbg = vim.__cpp_dbg_term
|
|
if dbg and dbg.send and dbg.is_open and dbg:is_open() then
|
|
dbg:send('\x03')
|
|
end
|
|
end, vim.tbl_extend('force', opts, { desc = 'Run/REPL: Interrupt' }))
|
|
|
|
vim.keymap.set('n', '<leader>ca', function()
|
|
local build_dir = cmake_build_dir('build-asan')
|
|
local flags =
|
|
"-DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS='-fsanitize=address -fno-omit-frame-pointer' -DCMAKE_CXX_FLAGS='-fsanitize=address -fno-omit-frame-pointer'"
|
|
cmake_configure(build_dir, flags)
|
|
cmake_build(build_dir)
|
|
end, vim.tbl_extend('force', opts, { desc = 'CMake: Build (ASan)' }))
|