diff options
Diffstat (limited to 'dot_config/nvim/lua/config')
| -rw-r--r-- | dot_config/nvim/lua/config/autocmds.lua | 128 | ||||
| -rw-r--r-- | dot_config/nvim/lua/config/keymaps.lua | 136 | ||||
| -rw-r--r-- | dot_config/nvim/lua/config/options.lua | 114 |
3 files changed, 378 insertions, 0 deletions
diff --git a/dot_config/nvim/lua/config/autocmds.lua b/dot_config/nvim/lua/config/autocmds.lua new file mode 100644 index 0000000..2d1ea9b --- /dev/null +++ b/dot_config/nvim/lua/config/autocmds.lua @@ -0,0 +1,128 @@ +local function augroup(name) + return vim.api.nvim_create_augroup(name, { clear = true }) +end + +local autocmd = vim.api.nvim_create_autocmd + +-- Check if we need to reload the file when it changed +autocmd({ "FocusGained", "TermClose", "TermLeave" }, { + group = augroup("checktime"), + callback = function() + if vim.o.buftype ~= "nofile" then + vim.cmd("checktime") + end + end, +}) + +-- Highlight on yank +autocmd("TextYankPost", { + group = augroup("highlight_yank"), + callback = vim.hl.on_yank, +}) + +-- go to last loc when opening a buffer +autocmd("BufReadPost", { + group = augroup("last_loc"), + callback = function(event) + local exclude = { "gitcommit" } + local buf = event.buf + if + vim.tbl_contains(exclude, vim.bo[buf].filetype) or vim.b[buf].last_loc + then + return + end + vim.b[buf].last_loc = true + local mark = vim.api.nvim_buf_get_mark(buf, '"') + local lcount = vim.api.nvim_buf_line_count(buf) + if mark[1] > 0 and mark[1] <= lcount then + pcall(vim.api.nvim_win_set_cursor, 0, mark) + end + end, +}) + +-- close some filetypes with <q> +autocmd("FileType", { + group = augroup("close_with_q"), + pattern = { + "PlenaryTestPopup", + "checkhealth", + "dbout", + "gitsigns-blame", + "help", + "lspinfo", + "notify", + "qf", + "startuptime", + }, + callback = function(event) + vim.bo[event.buf].buflisted = false + vim.schedule(function() + vim.keymap.set("n", "q", function() + vim.cmd("close") + pcall(vim.api.nvim_buf_delete, event.buf, { force = true }) + end, { + buffer = event.buf, + silent = true, + desc = "Quit buffer", + }) + end) + end, +}) + +-- make it easier to close man-files when opened inline +autocmd("FileType", { + group = augroup("man_unlisted"), + pattern = { "man" }, + callback = function(event) + vim.bo[event.buf].buflisted = false + end, +}) + +-- Auto create dir when saving a file, in case some intermediate directory does not exist +autocmd({ "BufWritePre" }, { + group = augroup("auto_create_dir"), + callback = function(event) + if event.match:match("^%w%w+:[\\/][\\/]") then + return + end + local file = vim.uv.fs_realpath(event.match) or event.match + vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p") + end, +}) + +autocmd("BufWritePost", { + group = augroup("sway"), + pattern = "*/sway/config", + command = "!swaymsg reload", +}) +autocmd("BufWritePost", { + group = augroup("waybar"), + pattern = "*/waybar/*", + command = "!killall -SIGUSR2 waybar", +}) +autocmd("BufWritePost", { + group = augroup("xdg-user-dirs"), + pattern = "user-dirs.dirs,user-dirs.locale", + command = "!xdg-user-dirs-update", +}) +autocmd("BufWritePost", { + group = augroup("mako"), + pattern = "*/mako/config", + command = "!makoctl reload", +}) +autocmd("BufWritePost", { + group = augroup("fc-cache"), + pattern = "fonts.conf", + command = "!fc-cache", +}) + +autocmd("FileType", { + group = augroup("treesitter_start"), + pattern = { "*" }, + callback = function() + if pcall(vim.treesitter.start) then + vim.wo.foldexpr = "v:lua.vim.treesitter.foldexpr()" + vim.bo.indentexpr = "v:lua.vim.treesitter.indentexpr()" + end + end, +}) diff --git a/dot_config/nvim/lua/config/keymaps.lua b/dot_config/nvim/lua/config/keymaps.lua new file mode 100644 index 0000000..366a37e --- /dev/null +++ b/dot_config/nvim/lua/config/keymaps.lua @@ -0,0 +1,136 @@ +local function map(mode, l, r, desc) + vim.keymap.set(mode, l, r, { desc = desc }) +end +local function cmd(mode, l, r, desc) + map(mode, l, "<cmd>" .. r .. "<cr>", desc) +end +local function cmdi(mode, l, r, desc) + map(mode, l, ":" .. r, desc) +end +local function nmap(l, r, desc) + map("n", l, r, desc) +end +local function vmap(l, r, desc) + map("v", l, r, desc) +end +local function nvmap(l, r, desc) + map({ "n", "v" }, l, r, desc) +end +local function ncmd(l, r, desc) + cmd("n", l, r, desc) +end +local function ncmdi(l, r, desc) + cmdi("n", l, r, desc) +end +local function vcmdi(l, r, desc) + cmdi("v", l, r, desc) +end + +ncmd("<esc>", "nohlsearch") + +-- highlight last inserted text +nmap("gV", "`[v`]") + +nmap("<down>", "<c-e>") +nmap("<up>", "<c-y>") + +-- paste over selection without clobbering registers +vmap("p", '"_dP') + +-- Find and Replace binds +ncmdi("<localleader>s", "%s/") +vcmdi("<localleader>s", "s/") + +ncmd("<leader>x", "wall") +ncmd("<leader>z", "wqall") +ncmd("<leader>q", "quitall") + +vim.keymap.set( + "t", + "<esc><esc>", + "<c-\\><c-n>", + { silent = true, noremap = true, desc = "Exit terminal mode" } +) + +nmap("[w", function() + vim.diagnostic.jump({ + count = -vim.v.count1, + severity = { min = vim.diagnostic.severity.WARN }, + }) +end) +nmap("]w", function() + vim.diagnostic.jump({ + count = vim.v.count1, + severity = { min = vim.diagnostic.severity.WARN }, + }) +end) +nmap("[e", function() + vim.diagnostic.jump({ + count = -vim.v.count1, + severity = vim.diagnostic.severity.ERROR, + }) +end) +nmap("]e", function() + vim.diagnostic.jump({ + count = vim.v.count1, + severity = vim.diagnostic.severity.ERROR, + }) +end) + +nmap("yp", function() + vim.fn.setreg("+", vim.fn.expand("%")) +end, "[Y]ank [P]ath") + +local doas_exec = function(_cmd) + vim.fn.inputsave() + local password = vim.fn.inputsecret("Password: ") + vim.fn.inputrestore() + if not password or #password == 0 then + vim.notify("Invalid password, doas aborted", vim.log.levels.WARN) + return false + end + local out = vim.fn.system(string.format("doas -S %s", _cmd), password .. "\n") + if vim.v.shell_error ~= 0 then + print("\r\n") + vim.notify(out, vim.log.levels.ERROR) + return false + end + return true +end + +vim.api.nvim_create_user_command("DoasWrite", function(opts) + local tmpfile = vim.fn.tempname() + local filepath + if #opts.fargs == 1 then + filepath = opts.fargs[1] + else + filepath = vim.fn.expand("%") + end + if not filepath or #filepath == 0 then + vim.notify("E32: No file name", vim.log.levels.ERROR) + return + end + -- `bs=1048576` is equivalent to `bs=1M` for GNU dd or `bs=1m` for BSD dd + -- Both `bs=1M` and `bs=1m` are non-POSIX + local _cmd = string.format( + "dd if=%s of=%s bs=1048576", + vim.fn.shellescape(tmpfile), + vim.fn.shellescape(filepath) + ) + -- no need to check error as this fails the entire function + vim.api.nvim_exec2(string.format("write! %s", tmpfile), { output = true }) + if doas_exec(_cmd) then + -- refreshes the buffer and prints the "written" message + vim.cmd.checktime() + -- exit command mode + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("<Esc>", true, false, true), + "n", + true + ) + end + vim.fn.delete(tmpfile) +end, { + nargs = "?", + desc = "Write using doas permissions", +}) diff --git a/dot_config/nvim/lua/config/options.lua b/dot_config/nvim/lua/config/options.lua new file mode 100644 index 0000000..b2409b5 --- /dev/null +++ b/dot_config/nvim/lua/config/options.lua @@ -0,0 +1,114 @@ +local opt = vim.o + +-- Persistence +opt.undofile = true -- persist undo history across sessions +opt.swapfile = false -- no swap files; rely on undofile for recovery + +-- Gutter +opt.number = true -- show line numbers +opt.cursorline = true -- highlight current line +opt.signcolumn = "auto:2" -- up to 2 sign columns, auto-hide when empty +opt.laststatus = 3 -- single global statusline + +-- Indentation (defaults; guess-indent overrides per-buffer) +opt.expandtab = true -- spaces instead of tabs +opt.shiftround = true -- round indent to shiftwidth multiples +opt.shiftwidth = 0 -- follow tabstop value +opt.softtabstop = -1 -- follow shiftwidth value +opt.tabstop = 4 -- 4-space tabs + +-- Search +opt.gdefault = true -- substitute all matches per line by default +opt.ignorecase = true -- case-insensitive search +opt.smartcase = true -- ...unless query has uppercase + +-- Splits +opt.splitbelow = true -- horizontal splits open below +opt.splitright = true -- vertical splits open right +opt.splitkeep = "screen" -- keep text position stable on split + +-- Line wrapping +opt.linebreak = true -- wrap at word boundaries +opt.breakindent = true -- indent wrapped lines +opt.textwidth = 80 -- wrap column for formatting +opt.colorcolumn = "+1" -- highlight column after textwidth +vim.opt.formatoptions:remove("t") -- don't auto-wrap text while typing + +-- Messages +opt.messagesopt = "wait:5000,history:500" -- message display timing and history depth + +vim.opt.shortmess:append({ a = true }) -- abbreviate all file messages + +-- Timing +opt.updatetime = 250 -- CursorHold delay (ms); affects gitsigns, diagnostics +opt.timeoutlen = 300 -- key sequence timeout (ms); affects which-key popup delay + +-- Completion and scrolling +vim.opt.completeopt = { "menuone", "noselect", "popup", "fuzzy", "nearest" } +opt.scrolloff = 999 -- keep cursor vertically centered +opt.sidescrolloff = 5 -- horizontal scroll margin + +-- Clipboard (deferred to avoid blocking startup on clipboard detection) +vim.schedule(function() + opt.clipboard = vim.env.SSH_TTY and "" or "unnamedplus" +end) + +opt.mouse = "a" -- enable mouse in all modes + +vim.opt.wildmode = { "longest", "full" } -- cmdline completion: longest match, then full menu + +vim.opt.cpoptions:remove({ "_" }) -- cw changes to end of word (not compatible vi behavior) + +-- Visible whitespace +vim.opt.listchars = { + tab = "> ", + space = "ยท", + extends = ">", + precedes = "<", + nbsp = "+", +} +opt.list = true -- show whitespace characters + +opt.confirm = true -- confirm before closing unsaved buffers + +opt.virtualedit = "block" -- allow cursor past end of line in visual block +opt.spelloptions = "camel" -- treat camelCase words as separate words for spell check + +-- Disable unused providers +vim.g.loaded_node_provider = 0 +vim.g.loaded_perl_provider = 0 +vim.g.loaded_python3_provider = 0 + +-- Diff +vim.opt.diffopt:append({ + hiddenoff = true, -- disable diff on hidden buffers + iblank = true, -- ignore blank line changes + iwhiteall = true, -- ignore all whitespace changes + algorithm = "histogram", -- better diff algorithm +}) + +-- Use ripgrep for :grep +if vim.fn.executable("rg") then + opt.grepprg = "rg\\ --vimgrep" + opt.grepformat = "%f:%l:%c:%m" +end + +-- Popup and window borders +opt.pumblend = 20 -- popup menu transparency +opt.pumborder = "rounded" +opt.winborder = "rounded" -- default border for floating windows + +-- Folding: set up treesitter-based folds but start with them closed. +-- Autocmd in autocmds.lua sets foldexpr per-buffer when treesitter is available. +vim.o.foldmethod = "expr" +vim.o.foldenable = false + +vim.g.mapleader = " " +vim.g.maplocalleader = "," + +-- Session persistence (for auto-session) +opt.sessionoptions = + "blank,buffers,curdir,help,tabpages,winsize,winpos,terminal,localoptions" + +opt.exrc = true -- source project-local .nvim.lua files + |
