Skip to content

Instantly share code, notes, and snippets.

@cdecompilador
Created March 9, 2026 12:26
Show Gist options
  • Select an option

  • Save cdecompilador/cf07db88b3a6156874de3a482d19d568 to your computer and use it in GitHub Desktop.

Select an option

Save cdecompilador/cf07db88b3a6156874de3a482d19d568 to your computer and use it in GitHub Desktop.
neovim config 2026
vim.cmd("syntax on")
-- General
vim.opt.swapfile = false
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.ruler = true
vim.opt.colorcolumn = "100"
-- Searching
vim.opt.hlsearch = true
vim.opt.incsearch = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.showmatch = false
-- Whitespace
vim.opt.wrap = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.softtabstop = 4
vim.opt.expandtab = false
vim.opt.shiftround = false
-- Rendering
vim.opt.ttyfast = true
vim.opt.visualbell = false
-- Indenting
vim.opt.smartindent = true
-- C/C++ indent style
vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, {
pattern = { "*.c", "*.cpp", "*.h" },
callback = function()
vim.opt_local.cindent = true
vim.opt_local.cinoptions = ":0,g0,t0"
end,
})
-- Mouse
vim.opt.mouse = "a"
-- Disable auto comment leader on 'o'
vim.api.nvim_create_autocmd("FileType", {
pattern = "*",
callback = function()
vim.opt_local.formatoptions:remove("o")
end,
})
-- Remaps
vim.g.mapleader = " "
-- Disable search highlight with <space><space>
vim.keymap.set("n", "<space><space>", ":noh<cr>", { silent = true })
-- Python: swap ' and "
vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, {
pattern = "*.py",
callback = function()
vim.keymap.set("i", "'", '"', { buffer = true })
vim.keymap.set("i", '"', "'", { buffer = true })
end,
})
-- Colors
vim.api.nvim_set_hl(0, "Search", { ctermbg = "LightGray" })
vim.api.nvim_set_hl(0, "Visual", { ctermfg = "LightGray" })
vim.api.nvim_set_hl(0, "QuickFixLine", { ctermfg = "LightGray" })
-- Tags: auto-regenerate on save
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "*",
callback = function()
vim.fn.jobstart({ "ctags", "-R", ".", "--sort=yes", "--sort=foldcase" })
end,
})
vim.opt.cursorline = true
vim.cmd.colorscheme("vim")
vim.opt.tags = "tags"
vim.keymap.set("n", "gd", "<C-]>")
vim.keymap.set("n", "gi", "<C-]>")
-- vim.keymap.set("n", "gr", ":vimgrep <C-R><C-W> **/*.{c,cpp} <CR>:copen<CR>")
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
local build_file = vim.fn.getcwd() .. "/build.bat"
if vim.fn.filereadable(build_file) == 0 then return end
vim.opt.makeprg = "build.bat"
local content = table.concat(vim.fn.readfile(build_file), "\n"):lower()
if content:find("clang") or content:find("gcc") or content:find("g++") then
vim.opt.errorformat = "%f:%l:%c: %t%*[^:]: %m,%f:%l:%c: %m"
elseif content:find("cl ") or content:find("cl.exe") then
vim.opt.errorformat = "%f(%l): %t%*[^:]: %m,%f(%l,%c): %t%*[^:]: %m"
end
end,
})
vim.o.grepprg = "rg --glob \"!tags\" --vimgrep --smart-case --fixed-strings"
vim.o.grepformat = "%f:%l:%c:%m"
local function get_main_win()
local best_win = nil
local best_height = -1
for _, win in ipairs(vim.api.nvim_list_wins()) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative == "" then
local h = vim.api.nvim_win_get_height(win)
if h > best_height then
best_height = h
best_win = win
end
end
end
return best_win
end
-- format_line(item) puede devolver un string o una tabla de strings (para
-- errores de build que ocupan varias líneas).
-- preview_fn(item, win) es opcional; si no se pasa, se abre el fichero.
local function show_qf_with_preview(qflist, title, format_line, preview_fn, on_buf_ready, height)
local main_win = get_main_win()
local original_buf = vim.api.nvim_win_get_buf(main_win)
local original_pos = vim.api.nvim_win_get_cursor(main_win)
-- Guardar alturas antes de abrir el split
local win_heights = {}
for _, win in ipairs(vim.api.nvim_list_wins()) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative == "" then
win_heights[win] = vim.api.nvim_win_get_height(win)
end
end
vim.api.nvim_set_current_win(main_win)
vim.cmd("botright " .. (height or 1) .. "split")
local qf_win = vim.api.nvim_get_current_win()
local qf_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_win_set_buf(qf_win, qf_buf)
-- Restaurar alturas tras el split
for win, h in pairs(win_heights) do
if vim.api.nvim_win_is_valid(win) and win ~= qf_win then
pcall(vim.api.nvim_win_set_height, win, h)
end
end
vim.api.nvim_win_set_height(qf_win, height or 1)
vim.wo[qf_win].statusline = " " .. title
vim.wo[qf_win].number = false
vim.wo[qf_win].relativenumber = false
vim.wo[qf_win].cursorline = true
vim.wo[qf_win].wrap = true
vim.wo[qf_win].breakindent = true
vim.wo[qf_win].breakindentopt = "shift:5"
vim.bo[qf_buf].buftype = "nofile"
vim.bo[qf_buf].bufhidden = "wipe"
-- Construir líneas del buffer y mapa línea -> índice de item
local buf_lines = {}
local line_to_item = {}
for i, item in ipairs(qflist) do
local formatted = format_line(item)
local block = type(formatted) == "table" and formatted or { formatted }
for _, l in ipairs(block) do
table.insert(buf_lines, l)
line_to_item[#buf_lines] = i
end
end
vim.api.nvim_buf_set_lines(qf_buf, 0, -1, false, buf_lines)
vim.bo[qf_buf].modifiable = false
if on_buf_ready then
on_buf_ready(qf_buf, line_to_item, qflist)
end
local function current_item()
if not vim.api.nvim_win_is_valid(qf_win) then return nil end
local cursor_line = vim.api.nvim_win_get_cursor(qf_win)[1]
local idx = line_to_item[cursor_line]
return idx and qflist[idx] or nil
end
local function show_preview()
local item = current_item()
if not item then return end
if preview_fn then
preview_fn(item, main_win)
else
local bufnr = (item.bufnr and item.bufnr ~= 0) and item.bufnr
or vim.fn.bufadd(item.filename)
vim.fn.bufload(bufnr)
vim.api.nvim_win_set_buf(main_win, bufnr)
pcall(vim.api.nvim_win_set_cursor, main_win, { item.lnum, (item.col or 1) - 1 })
vim.api.nvim_win_call(main_win, function() vim.cmd("normal! zz") end)
end
end
show_preview()
local augroup = vim.api.nvim_create_augroup("QfPreview", { clear = true })
vim.api.nvim_create_autocmd("CursorMoved", {
group = augroup,
buffer = qf_buf,
callback = show_preview,
})
local function save_heights()
local heights = {}
for _, win in ipairs(vim.api.nvim_list_wins()) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative == "" and win ~= qf_win then
heights[win] = vim.api.nvim_win_get_height(win)
end
end
return heights
end
local function restore_heights(heights)
for win, h in pairs(heights) do
if vim.api.nvim_win_is_valid(win) then
pcall(vim.api.nvim_win_set_height, win, h)
end
end
end
local function close_qf()
vim.api.nvim_del_augroup_by_name("QfPreview")
if vim.api.nvim_win_is_valid(qf_win) then
vim.api.nvim_win_close(qf_win, true)
end
end
-- <CR>: confirmar selección
vim.keymap.set("n", "<CR>", function()
local item = current_item()
close_qf()
-- sin restore_heights aquí
if item then
local bufnr = (item.bufnr and item.bufnr ~= 0) and item.bufnr
or vim.fn.bufadd(item.filename)
vim.fn.bufload(bufnr)
vim.api.nvim_set_current_win(main_win)
vim.api.nvim_win_set_buf(main_win, bufnr)
pcall(vim.api.nvim_win_set_cursor, main_win, { item.lnum, (item.col or 1) - 1 })
vim.cmd("normal! zz")
end
end, { buffer = qf_buf })
-- <Esc> / q: cancelar y restaurar
local function cancel()
close_qf()
vim.api.nvim_win_set_buf(main_win, original_buf)
vim.api.nvim_set_current_win(main_win)
pcall(vim.api.nvim_win_set_cursor, main_win, original_pos)
vim.cmd("normal! zz")
end
vim.keymap.set("n", "<Esc>", cancel, { buffer = qf_buf })
vim.keymap.set("n", "q", cancel, { buffer = qf_buf })
end
vim.keymap.set("n", "<leader>b", function()
local build_file = vim.fn.getcwd() .. "/build.bat"
local cmd, parse_header
if vim.fn.filereadable(build_file) == 1 then
cmd = "build.bat 2>&1"
parse_header = function(line)
local fname, lnum, col, kind = line:match("^([^:]+):(%d+):(%d+): (%w+):")
if fname and (kind == "error" or kind == "warning") then
return fname, lnum, col, kind
end
end
elseif vim.bo.filetype == "python" then
cmd = "ruff check --output-format concise " .. vim.fn.shellescape(vim.fn.expand("%")) .. " 2>&1"
parse_header = function(line)
local fname, lnum, col, msg = line:match("^(.+):(%d+):(%d+): (.+)$")
if fname then
return fname, lnum, col, "warning"
end
end
else
print("no build.bat found")
return
end
local output = vim.fn.systemlist(cmd)
local items = {}
local current = nil
local function flush()
if current then table.insert(items, current) end
current = nil
end
for _, raw_line in ipairs(output) do
local line = raw_line:gsub("\r", "")
local fname, lnum, col, kind = parse_header(line)
if fname then
flush()
current = {
filename = fname,
lnum = tonumber(lnum),
col = tonumber(col),
kind = kind,
lines = { line },
}
elseif current then
table.insert(current.lines, line)
end
end
flush()
if #items == 0 then
print(vim.bo.filetype == "python" and "lint ok" or "build ok")
return
end
local title = (vim.bo.filetype == "python" and "lint: " or "build: ") .. #items .. " issues"
show_qf_with_preview(
items,
title,
function(item)
local fname = vim.fn.fnamemodify(item.filename, ":.")
local msg = item.lines[1]:match(": (.+)$") or ""
local result = { string.format("%s:%d %s", fname, item.lnum, msg) }
for i = 2, #item.lines do
table.insert(result, " " .. item.lines[i])
end
return result
end,
nil,
function(buf, line_to_item, qflist)
vim.api.nvim_set_hl(0, "QfError", { ctermfg = "Red", fg = "#ff5555" })
vim.api.nvim_set_hl(0, "QfWarning", { ctermfg = "Yellow", fg = "#f1fa8c" })
for line_idx, item_idx in pairs(line_to_item) do
local item = qflist[item_idx]
local fname = vim.fn.fnamemodify(item.filename, ":.")
local buf_line = vim.api.nvim_buf_get_lines(buf, line_idx - 1, line_idx, false)[1] or ""
if buf_line:match("^" .. vim.pesc(fname)) then
local hl = item.kind == "error" and "QfError" or "QfWarning"
local prefix_len = #fname + 1 + #tostring(item.lnum)
vim.api.nvim_buf_add_highlight(buf, -1, hl, line_idx - 1, 0, prefix_len)
end
end
end,
20
)
end, { silent = true })
-- Find file
vim.keymap.set("n", "<leader>f", function()
local query = vim.fn.input("find file: ")
if query == "" then return end
local results = vim.fn.systemlist("fd --type f " .. vim.fn.shellescape(query))
if #results == 0 then
print("no files found")
return
end
local qflist = {}
for _, file in ipairs(results) do
table.insert(qflist, { filename = file, lnum = 1, col = 1, text = "" })
end
show_qf_with_preview(qflist, "find: " .. query, function(item)
return vim.fn.fnamemodify(item.filename, ":.")
end)
end)
local function get_visual_selection()
local lines = vim.fn.getregion(vim.fn.getpos("v"), vim.fn.getpos("."))
return table.concat(lines, "\n")
end
local function do_grep(default)
local query = vim.fn.input("file grep: ", default or "")
if query == "" then return end
vim.cmd("silent grep! " .. vim.fn.shellescape(query))
local qflist = vim.fn.getqflist()
if #qflist == 0 then
print("No results for: " .. query)
return
end
show_qf_with_preview(qflist, "grep: " .. query, function(item)
local fname = vim.fn.fnamemodify(
item.bufnr ~= 0 and vim.fn.bufname(item.bufnr) or item.filename, ":.")
local text = item.text:gsub("^%s+", "")
return string.format("%-40s %4d %s", fname, item.lnum, text)
end)
end
vim.keymap.set("n", "<leader>g", function() do_grep() end)
vim.keymap.set("v", "<leader>g", function() do_grep(get_visual_selection()) end)
vim.keymap.set("n", "gr", function()
local word = vim.fn.expand("<cword>")
if word == "" then return end
vim.cmd("silent grep! " .. vim.fn.shellescape(word))
local qflist = vim.fn.getqflist()
if #qflist == 0 then
print("no references for: " .. word)
return
end
show_qf_with_preview(qflist, "refs: " .. word, function(item)
local fname = vim.fn.fnamemodify(
item.bufnr ~= 0 and vim.fn.bufname(item.bufnr) or item.filename, ":.")
local text = item.text:gsub("^%s+", "")
return string.format("%-40s %4d %s", fname, item.lnum, text)
end)
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment