-- ── Bootstrap lazy.nvim ─────────────────────────────────────────────────────── -- lazy.nvim is the plugin manager. On first launch it clones itself into the -- Neovim data directory (~/.local/share/nvim/lazy/lazy.nvim). -- vim.uv is the preferred libuv binding in Neovim >= 0.10; vim.loop is the -- legacy alias kept for compatibility with older releases. local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not (vim.uv or vim.loop).fs_stat(lazypath) then vim.fn.system({ "git", "clone", "--filter=blob:none", -- blobless clone: skips file blobs, fetching only on checkout "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath, }) end -- Prepend lazy's path so Neovim can find and load it before any other plugin. vim.opt.rtp:prepend(lazypath) -- ── Plugins ─────────────────────────────────────────────────────────────────── require("lazy").setup({ -- CyberQueer colorscheme loaded from the local dotfiles tree (not from GitHub) -- so that apply-theme.sh changes are picked up without a plugin update. { dir = vim.fn.expand("~/Dotfiles/nvim/theme/cyberqueer.nvim") }, "rktjmp/lush.nvim", -- HSL-based colorscheme builder used by cyberqueer "tpope/vim-sensible", -- Sensible Vim defaults (better backspace, history, etc.) "junegunn/goyo.vim", -- Distraction-free writing mode (centers and pads the buffer) "arecarn/vim-crunch", -- Evaluate math expressions inside the buffer "preservim/nerdtree", -- File-tree sidebar navigator "ryanoasis/vim-devicons", -- Nerd Font icons for NERDTree and airline -- fzf binary + Vim integration; build step runs fzf#install() to compile the binary { "junegunn/fzf", build = function() vim.fn["fzf#install"]() end }, "junegunn/fzf.vim", -- :Files, :Rg, :Buffers etc. powered by fzf "vim-airline/vim-airline", -- Status/tabline with Powerline-style separators "vim-airline/vim-airline-themes", -- Theme library for vim-airline (cyberqueer theme used) "voldikss/vim-floaterm", -- Floating terminal windows inside Neovim "rust-lang/rust.vim", -- Rust syntax, fmt-on-save, and cargo integration "norcalli/nvim-colorizer.lua", -- Inline hex/RGB color previews in CSS, config files, etc. -- CoC: full LSP client with completion, hover, diagnostics; pinned to "release" branch { "neoclide/coc.nvim", branch = "release" }, -- vim-visual-multi: multiple-cursor editing (like Sublime Text ctrl+d) { "mg979/vim-visual-multi", branch = "master" }, "SirVer/ultisnips", -- Snippet engine (expand/jump triggers configured below) "honza/vim-snippets", -- Community snippet library consumed by UltiSnips "mfussenegger/nvim-dap", -- Debug Adapter Protocol client for step-through debugging "elihunter173/dirbuf.nvim", -- Edit directory contents like a buffer (rename, delete, etc.) "tpope/vim-dadbod", -- Database client (SQL queries against any DB URL) "kristijanhusak/vim-dadbod-ui", -- TUI for vim-dadbod (tree browser + result panes) "kristijanhusak/vim-dadbod-completion", -- CoC/native completion source for SQL via dadbod "nvim-mini/mini.icons", -- Minimal icon provider used by dadbod-ui and others "tadmccorkle/markdown.nvim", -- Markdown helpers: table formatting, checkboxes, etc. -- glow.nvim: render Markdown in a floating window via the `glow` CLI { "ellisonleao/glow.nvim", config = true }, "itchyny/calendar.vim", -- Interactive calendar; used in the PIM overlay (key "g") -- claude-code.nvim: Claude Code integration (opens/communicates with the `claude` CLI) { "greggh/claude-code.nvim", dependencies = { "nvim-lua/plenary.nvim" }, config = function() require("claude-code").setup() end, }, }, { -- Use a bundled colorscheme during the initial lazy install so the UI is -- readable even before cyberqueer is downloaded and compiled. install = { colorscheme = { "habamax" } }, }) -- ── Colorscheme & UI ────────────────────────────────────────────────────────── vim.cmd("colorscheme cyberqueer") -- Tell airline to use Powerline-patched glyphs for segment separators. vim.g.airline_powerline_fonts = 1 vim.g.airline_theme = "cyberqueer" -- Embed machine identity in the airline status bar (section_x = right side). -- Useful when SSHed into multiple machines or running nested Neovim sessions. local ipaddr = vim.trim(vim.fn.system("hostname -i")) local hostname = vim.trim(vim.fn.system("hostname -s")) vim.g.airline_section_x = "IP:" .. ipaddr .. " DNS:" .. hostname -- ── Providers ───────────────────────────────────────────────────────────────── -- Disable unused remote plugin providers to suppress ":checkhealth" warnings -- and avoid the startup penalty of probing for ruby/perl interpreters. vim.g.loaded_ruby_provider = 0 vim.g.loaded_perl_provider = 0 -- ── Editor options ──────────────────────────────────────────────────────────── -- Enable filetype-specific plugins and indentation rules (e.g. Python 4-space). vim.cmd("filetype plugin indent on") vim.cmd("syntax enable") vim.opt.number = true -- show absolute line numbers in the gutter vim.opt.relativenumber = true -- show relative numbers above/below for fast j/k jumps vim.opt.cursorline = true -- highlight the entire line the cursor is on vim.opt.cursorcolumn = true -- highlight the entire column the cursor is in vim.opt.showmode = false -- airline already shows mode; suppress the redundant "-- INSERT --" message vim.opt.shiftwidth = 4 -- number of spaces for each indentation level (>> / <<) vim.opt.scrolloff = 5 -- keep at least 5 lines visible above/below the cursor vim.opt.wrap = false -- disable line wrapping (horizontal scroll instead) vim.opt.incsearch = true -- show matches incrementally as you type the search pattern vim.opt.ignorecase = true -- case-insensitive search by default vim.opt.smartcase = true -- override ignorecase when the pattern contains uppercase vim.opt.showcmd = true -- show the partial command being typed in the status line vim.opt.showmatch = true -- briefly jump to the matching bracket when one is typed vim.opt.hlsearch = true -- highlight all search matches (clear with :noh) vim.opt.history = 1000 -- keep 1000 entries in command and search history vim.opt.wildmenu = true -- show a completion menu for : commands vim.opt.wildmode = "list:longest" -- first tab lists completions, second completes to longest common prefix -- ── Keymaps ─────────────────────────────────────────────────────────────────── -- window navigation (normal mode) -- Use Ctrl+hjkl to move between splits instead of the two-key prefix. -- cycles to the next window (wraps); others jump directionally. vim.keymap.set("n", "", "w") vim.keymap.set("n", "", "h") vim.keymap.set("n", "", "j") vim.keymap.set("n", "", "k") -- window navigation from terminal-insert mode (exit insert, then move) -- exits terminal-insert mode first; without it the control sequence -- is sent to the shell process instead of being handled by Neovim. vim.keymap.set("t", "", "h", { silent = true }) vim.keymap.set("t", "", "j", { silent = true }) vim.keymap.set("t", "", "k", { silent = true }) vim.keymap.set("t", "", "w", { silent = true }) -- auto-enter insert mode when focusing a terminal buffer (skip floaterm) -- Without this, focusing a terminal buffer requires pressing 'i' manually. -- Floaterm manages its own mode, so we exclude it to avoid conflicting with -- its internal state machine. vim.api.nvim_create_autocmd("BufEnter", { pattern = "term://*", callback = function() if vim.bo.filetype ~= "floaterm" then vim.cmd("startinsert") end end, }) -- calendar.vim steals for month nav; restore window movement -- The plugin sets buffer-local mappings on FileType calendar, so we override -- them with { buffer = true } mappings of our own that fire after the plugin's. vim.api.nvim_create_autocmd("FileType", { pattern = "calendar", callback = function() local o = { buffer = true, silent = true } vim.keymap.set("n", "", "w", o) vim.keymap.set("n", "", "h", o) vim.keymap.set("n", "", "j", o) vim.keymap.set("n", "", "k", o) end, }) -- quick actions: single-key shortcuts for frequently used commands -- t: open a new floating terminal via vim-floaterm vim.keymap.set("n", "t", ":FloatermNew", { silent = true }) -- e: toggle the NERDTree sidebar; l immediately moves focus back to the -- editing window so the cursor doesn't land in the tree vim.keymap.set("n", "e", ":NERDTreeTogglel", { silent = true }) -- s: toggle the vim-dadbod-ui database browser vim.keymap.set("n", "s", ":DBUIToggle", { silent = true }) -- q: try to save-quit (wq); fall back to force-quit (q) for unsaved/read-only buffers vim.keymap.set("n", "q", function() local ok = pcall(vim.cmd, "wq") if not ok then vim.cmd("q") end end, { silent = true }) -- insert mode completion: Tab triggers Neovim's built-in keyword completion () -- S-Tab reverts to a literal Tab character so indentation still works normally. vim.keymap.set("i", "", "") vim.keymap.set("i", "", "") -- sudo save: :w!! pipes the buffer through sudo tee, bypassing a read-only -- file system permission. The '%' is the current file path. vim.cmd("ca w!! w !sudo tee '%'") -- :Vb is a convenience alias for entering visual-block mode without pressing -- Ctrl+v, which conflicts with paste in some terminal emulators. vim.cmd("command! Vb normal! ") -- ── UltiSnips ───────────────────────────────────────────────────────────────── vim.g.UltiSnipsExpandTrigger = "" vim.g.UltiSnipsJumpForwardTrigger = "" vim.g.UltiSnipsJumpBackwardTrigger = "" vim.g.UltiSnipsEditSplit = "vertical" -- ── CoC ─────────────────────────────────────────────────────────────────────── vim.g.coc_global_extensions = { "coc-snippets", "coc-powershell", "coc-sh", "coc-omnisharp", "coc-clangd", "coc-json", "coc-css", "coc-git", "coc-pyright", "coc-sql", } vim.g.coc_snippet_next = "" vim.g.coc_snippet_prev = "" vim.keymap.set("i", "", "(coc-snippets-expand)", { remap = true }) vim.keymap.set("v", "", "(coc-snippets-select)", { remap = true }) vim.keymap.set("i", "", "(coc-snippets-expand-jump)", { remap = true }) vim.keymap.set("x", "x", "(coc-convert-snippet)", { remap = true }) -- tab/s-tab navigate CoC pum, else fall through vim.keymap.set("i", "", function() return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#next"](1) or "" end, { expr = true, silent = true }) vim.keymap.set("i", "", function() return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#prev"](1) or "" end, { expr = true, silent = true }) -- CR confirms CoC selection vim.keymap.set("i", "", function() return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#confirm"]() or "" end, { expr = true, silent = true }) -- ── PIM floating windows (sideward T: left column overlay) ─────────────────── local function _pim_scratch(label, err) vim.cmd("enew") vim.bo.buftype = "nofile" vim.bo.buflisted = false vim.api.nvim_buf_set_lines(0, 0, -1, false, { "[" .. label .. " unavailable]", "", err or "" }) end local function _pim_close_win(win) if not vim.api.nvim_win_is_valid(win) then return end local buf = vim.api.nvim_win_get_buf(win) vim.api.nvim_win_close(win, true) if vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buftype == "terminal" then pcall(vim.api.nvim_buf_delete, buf, { force = true }) end end local function _pim_float(row, col, height, width, border) local buf = vim.api.nvim_create_buf(false, true) local win = vim.api.nvim_open_win(buf, true, { relative = "editor", row = row, col = col, height = math.max(1, height), width = math.max(1, width), style = "minimal", border = border or "none", zindex = 50, }) vim.api.nvim_set_option_value("winhighlight", "Normal:Normal,NormalNC:Normal", { win = win }) return win end -- n/g/f: individual centered floating windows local _solo = {} local function toggle_solo(key, cmd, label) local win = _solo[key] if win and vim.api.nvim_win_is_valid(win) then _pim_close_win(win) _solo[key] = nil return end local H = vim.o.lines - 2 local W = vim.o.columns local h = math.max(1, math.floor(H * 0.85)) local w = math.max(1, math.floor(W * 0.85)) -- centre accounting for the 1-cell rounded border on each side local r = math.max(0, math.floor((H - h - 2) / 2)) local c = math.max(0, math.floor((W - w - 2) / 2)) local win_id = _pim_float(r, c, h, w, "rounded") local ok, err = pcall(vim.cmd, cmd) if not ok then _pim_scratch(label, err) end _solo[key] = win_id end vim.keymap.set("n", "n", function() toggle_solo("n", "terminal alot", "alot") end, { silent = true }) vim.keymap.set("n", "g", function() toggle_solo("g", "terminal khal interactive", "khal") end, { silent = true }) vim.keymap.set("n", "f", function() toggle_solo("f", "terminal abook", "abook") end, { silent = true }) -- r: sideward-T overlay — left column (bar of the T) with three stacked panes, -- document remains visible to the right (the stem of the T) local _pim_wins = {} local function toggle_pim() if #_pim_wins > 0 and vim.api.nvim_win_is_valid(_pim_wins[1]) then for _, w in ipairs(_pim_wins) do _pim_close_win(w) end _pim_wins = {} return end -- full-screen: alot left, abook top-right, calendar bottom-right local H = vim.o.lines - 2 local W = vim.o.columns local right_w = math.max(80, math.floor(W * 0.45)) local left_w = math.max(1, W - right_w) local top_h = math.max(20, math.floor(H / 2)) local bot_h = math.max(1, H - top_h) local w1 = _pim_float(0, 0, H, left_w) local ok, err = pcall(vim.cmd, "terminal alot") if not ok then _pim_scratch("alot", err) end local w2 = _pim_float(0, left_w, top_h, right_w) ok, err = pcall(vim.cmd, "terminal abook") if not ok then _pim_scratch("abook", err) end local w3 = _pim_float(top_h, left_w, bot_h, right_w) ok, err = pcall(vim.cmd, "terminal khal interactive") if not ok then _pim_scratch("khal", err) end _pim_wins = { w1, w2, w3 } vim.api.nvim_set_current_win(w1) end vim.keymap.set("n", "x", toggle_pim, { silent = true }) -- ── CalDAV background sync on startup ──────────────────────────────────────── vim.api.nvim_create_autocmd("VimEnter", { once = true, callback = function() vim.fn.jobstart( { "sh", "-c", "vdirsyncer sync && ics-to-calendarim" }, { detach = true } ) end, })