local MAP = vim.keymap.set local AUTOCMD = vim.api.nvim_create_autocmd vim.cmd("colorscheme scheme") vim.cmd("filetype indent on") -- Language people you have more important things to do than police the code style, if people -- want consistent styling it should be done at a project level using a linter or .editorconfig vim.g.rust_recommended_style = false vim.g.python_recommended_style = false vim.g.go_recommended_style = false vim.g.zig_recommended_style = false vim.g.markdown_recommended_style = false vim.g.arduino_recommended_style = false vim.g.gdscript_recommended_style = false vim.g.yaml_recommended_style = false -- Basic options vim.o.tabstop = 2 vim.o.shiftwidth = 0 vim.o.expandtab = false vim.o.list = true vim.o.clipboard = "unnamedplus" vim.o.ff = "unix" vim.o.ffs = "unix,dos" vim.o.switchbuf = "useopen,uselast" vim.o.textwidth = 95 vim.o.wrapmargin = 5 vim.o.number = true vim.o.relativenumber = true vim.o.splitbelow = true vim.o.splitright = true vim.o.autoread = true vim.o.lazyredraw = true vim.o.cursorline = true vim.o.ignorecase = true vim.o.wrap = false vim.o.wildmenu = false vim.o.termguicolors = true vim.o.cindent = true vim.o.timeoutlen = 1500 vim.o.completeopt = "preview" vim.o.wildmode = "full" vim.o.cinoptions = "l1,b-s" vim.o.statusline = "%#LineNr# [%n] %#Default# %f%m%r %= %#StatusLineNC# %w[%{&ft == '' ? 'None' : ''}%Y] %#LineNr# Line: %l Column: %c " vim.opt.errorformat = { "%f:%l:%c: fatal %trror: %m", -- gcc/clang fatal error "%f:%l:%c: %trror: %m", -- gcc/clang error "%f:%l:%c: %tarning: %m", -- gcc/clang warning "%-G%m" -- Ignore anything else } vim.opt.formatoptions:append("/") vim.opt.cinkeys:append("0=break") vim.opt.listchars:append({ lead = "." }) -------------------------------------------------------------------------------- -- Functions -------------------------------------------------------------------------------- -- Building & Errors -- Diagnostics configuration vim.diagnostic.config({ signs = { text = { [vim.diagnostic.severity.ERROR] = "", [vim.diagnostic.severity.WARN] = "" }, linehl = { [vim.diagnostic.severity.ERROR] = "DiagnosticLineError", [vim.diagnostic.severity.WARN] = "DiagnosticLineWarn" }, }, virtual_text = { virt_text_pos = "eol_right_align", prefix = function(d, i, total) return d.severity == vim.diagnostic.severity.ERROR and "!" or "#" end }, underline = false, severity_sort = true }) local _last_build = "" local _quickfix = {} local _namespace = vim.api.nvim_create_namespace("__qf.buffer.errors") local function LoadDiagnostics(args) local bufnr = args.buf if _quickfix[bufnr] then vim.diagnostic.set(_namespace, bufnr, _quickfix[bufnr]) end end local function ExecuteBuild() vim.cmd("silent make") -- Reset currently displayed diagnostics _quickfix = {} vim.diagnostic.reset() local entries = vim.fn.getqflist() if #entries ~= 0 then -- Group diagnostics by buffer number local diagnostics = vim.diagnostic.fromqflist(entries) for _, d in ipairs(diagnostics) do if not _quickfix[d.bufnr] then _quickfix[d.bufnr] = {} end table.insert(_quickfix[d.bufnr], d) end -- Display the new diagnostics for it, v in pairs(_quickfix) do vim.diagnostic.set(_namespace, it, v) end end end local function PromptBuild() vim.ui.input( { prompt = "Compile: ", completion = "shellcmdline", default = _last_build }, function(input) if input ~= nil then local makeprg = vim.o.makeprg vim.o.makeprg = input ExecuteBuild() _last_build = input vim.o.makeprg = makeprg end end ) end local function FormatJumpList() local qf = vim.fn.getqflist( { winid = true, qfbufnr = true }) local loc = vim.fn.getloclist(0, { winid = true, qfbufnr = true }) if qf.winid ~= 0 then -- This means the quickfix list is open and needs formatting local ns = vim.api.nvim_create_namespace("__qf.format") local bufnr = qf.qfbufnr local entries = vim.fn.getqflist() local text = {} local max = { 0, 0, 0 } vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) vim.api.nvim_buf_set_option(bufnr, "modifiable", true) for _, it in ipairs(entries) do max[1] = math.max(max[1], #tostring(it.lnum)) max[2] = math.max(max[2], #tostring(it.col)) max[3] = math.max(max[3], #vim.fn.bufname(it.bufnr)) end -- +2 for the extra colons we put between the filename, line number and column number local extformat = string.format("[%%s] %%-%ds | %%s", max[1] + max[2] + max[3] + 2) for _, it in ipairs(entries) do local sign = it.type:lower() == "e" and "!" or "#" local fname = string.format("%s:%d:%d" , vim.fn.bufname(it.bufnr), it.lnum, it.col) table.insert(text, extformat:format(sign, fname, it.text)) end vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, text) vim.api.nvim_buf_set_option(bufnr, "modifiable", false) vim.api.nvim_buf_set_option(bufnr, "modified", false) end if loc.winid ~= 0 then -- This means the location list is open and needs formatting local ns = vim.api.nvim_create_namespace("__loc.format") local bufnr = loc.qfbufnr local entries = vim.fn.getloclist(0) local text = {} local max = { 0, 0 } vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) vim.api.nvim_buf_set_option(bufnr, "modifiable", true) for _, it in ipairs(entries) do max[1] = math.max(max[1], #vim.fn.bufname(it.bufnr)) max[2] = math.max(max[2], #tostring(it.lnum)) end -- +1 for the extra colon we put between the filename and the line number local extformat = string.format("[~] %%-%ds | %%s", max[1] + max[2] + 1) for _, it in ipairs(entries) do local fname = string.format("%s:%d", vim.fn.bufname(it.bufnr), it.lnum) table.insert(text, extformat:format(fname, it.text)) end vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, text) vim.api.nvim_buf_set_option(bufnr, "modifiable", false) vim.api.nvim_buf_set_option(bufnr, "modified", false) end end -- Buffers local function BufferComplete() local pos = vim.api.nvim_win_get_cursor(0) local line = vim.fn.getline('.') local ident = line:sub(1, pos[2]):match("[^ \t]*$") return #ident ~= 0 and "" or "" end local function TrimBuffer() local view = vim.fn.winsaveview() vim.cmd("%s/\\s\\+$//e") vim.fn.winrestview(view) end local function MakeScratch() local bufnr = vim.api.nvim_create_buf(true, true) vim.api.nvim_buf_set_option(bufnr, "buftype", "nofile") vim.api.nvim_buf_set_option(bufnr, "bufhidden", "hide") vim.api.nvim_buf_set_option(bufnr, "swapfile", false) vim.api.nvim_buf_set_option(bufnr, "modified", false) vim.api.nvim_buf_set_name(bufnr, "scratch") vim.api.nvim_buf_set_text(bufnr, 0, 0, 0, -1, { "-*- Scratch Buffer -*-" }) -- @Hack: There isn't really any way to get the fullscreen size, 150 greater than the -- half-screen width on all of my devices as we don't want to split in that case if vim.o.columns > 150 then vim.cmd(string.format("vsplit #%d", bufnr)) vim.cmd("wincmd p") end end -- Windowing local _is_help = { help = true, man = true, qf = true } function ManageSplit() local windows = vim.api.nvim_tabpage_list_wins(0) local bufnr = vim.api.nvim_win_get_buf(windows[#windows]) local filetype = vim.bo[bufnr].filetype local target_count = _is_help[filetype] and 3 or 2 if #windows == target_count then -- We already have the target number of windows open so we should close our split window, -- always close 2 because that is the right-most split. The third window if it exists is -- the bottom most window and is a "help" window vim.api.nvim_win_close(windows[2], false) else -- We have less than the required windows open so split the first window, we open the -- "scratch" buffer in this new window, if it is still open. Otherwise the same buffer in -- the first window is opened. local scratch = vim.fn.bufnr("scratch") local first = vim.api.nvim_win_get_buf(windows[1]) bufnr = vim.api.nvim_buf_is_loaded(scratch) and scratch or first vim.api.nvim_open_win(bufnr, true, { split = "right", win = windows[1] }) end end local function ProjectSearch() local input = vim.fn.input("Search: ") if input and input ~= "" then vim.cmd("silent lgrep! \"" .. input .. "\" **") vim.cmd("lopen | lfirst | wincmd p") end end local function ExpandHelp(a) local winid = vim.api.nvim_get_current_win() local filetype = vim.bo[vim.api.nvim_win_get_buf(winid)].filetype local height = a.event == "WinEnter" and math.floor(0.8 * vim.o.lines) or 5 if _is_help[filetype] then vim.api.nvim_win_set_height(winid, height) end end local function LayoutHelp(a) if _is_help[vim.bo.filetype] then local winid = vim.api.nvim_get_current_win() local windows = vim.api.nvim_tabpage_list_wins(0) -- We need to close other "help" windows that may be open so search through the open -- windows. There *should* only ever be 3 windows open if my window handling is working -- correctly for _, w in ipairs(windows) do if w ~= winid then local bufnr = vim.api.nvim_win_get_buf(w) if _is_help[vim.bo[bufnr].filetype] then vim.api.nvim_win_close(w, true) end end end -- Force the new window to the bottom and expand it to be 80% of the height vim.cmd("wincmd J") vim.cmd(string.format("resize %d", math.floor(0.8 * vim.o.lines))) end end -------------------------------------------------------------------------------- -- Input mappings -------------------------------------------------------------------------------- vim.g.mapleader = " " MAP("n", "", "mz:m+") MAP("n", "", "mz:m-2") MAP("n", "", ":nohl") MAP("n", "n", ":cnext") MAP("n", "N", ":cprev") MAP("n", "J", "}") MAP("v", "J", "}") MAP("n", "K", "{") MAP("v", "K", "{") MAP("n", "k", ":Man") MAP("i", "", "<<") MAP("i", "", BufferComplete, { expr = true }) MAP("n", "s", ManageSplit) MAP("n", "f", ProjectSearch) MAP("n", "m", PromptBuild) -------------------------------------------------------------------------------- -- Autocommands -------------------------------------------------------------------------------- AUTOCMD('BufEnter', { command = "let b:man_default_sects=\"2,3\"", pattern = "*.c" }) AUTOCMD('BufReadPost', { command = "setlocal nornu", pattern = "quickfix" }) AUTOCMD('BufReadPost', { callback = FormatJumpList, pattern = "quickfix" }) AUTOCMD('BufWritePre', { callback = TrimBuffer, pattern = "*" }) AUTOCMD('VimEnter', { callback = MakeScratch, pattern = "*" }) AUTOCMD('BufWinEnter', { callback = LayoutHelp, pattern = "*" }) AUTOCMD('WinEnter', { callback = ExpandHelp, pattern = "*" }) AUTOCMD('WinLeave', { callback = ExpandHelp, pattern = "*" }) AUTOCMD('BufRead', { callback = LoadDiagnostics, pattern = "*" })