diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43c723 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.backup.* +.DS_Store diff --git a/init.lua b/init.lua index a22706e..8f9d3ab 100644 --- a/init.lua +++ b/init.lua @@ -2,10 +2,15 @@ if vim.loader then vim.loader.enable() end -require("config.globals") -require("config.neovide") -require("config.lazy") -require("config.options") -require("config.autocmds") -require("config.keymaps") -require("config.lsp") +local modules = { + "config.globals", + "config.options", + "config.lazy", + "config.autocmds", + "config.keymaps", + "config.lsp", +} + +for _, module in ipairs(modules) do + require(module) +end diff --git a/init.lua.bak b/init.lua.bak index a22706e..8f9d3ab 100644 --- a/init.lua.bak +++ b/init.lua.bak @@ -2,10 +2,15 @@ if vim.loader then vim.loader.enable() end -require("config.globals") -require("config.neovide") -require("config.lazy") -require("config.options") -require("config.autocmds") -require("config.keymaps") -require("config.lsp") +local modules = { + "config.globals", + "config.options", + "config.lazy", + "config.autocmds", + "config.keymaps", + "config.lsp", +} + +for _, module in ipairs(modules) do + require(module) +end diff --git a/lazy-lock.json b/lazy-lock.json index bb72636..eadefc9 100644 --- a/lazy-lock.json +++ b/lazy-lock.json @@ -1,34 +1,26 @@ { - "amp.nvim": { "branch": "main", "commit": "3b9ad5ef0328de1b35cc9bfa723a37db5daf9434" }, - "arctic": { "branch": "main", "commit": "46da33aeb3a8ee48533f41911b3aaf4f1541dd6f" }, - "catppuccin": { "branch": "main", "commit": "426dbebe06b5c69fd846ceb17b42e12f890aedf1" }, - "everforest": { "branch": "master", "commit": "b03a03148c8b34c24c96960b93da9c8883d11f54" }, - "fff.nvim": { "branch": "feat/implement-buffers-support", "commit": "c134566edc684870bcf3069c0bfdfe058562166a" }, - "fidget.nvim": { "branch": "main", "commit": "889e2e96edef4e144965571d46f7a77bcc4d0ddf" }, - "grug-far.nvim": { "branch": "main", "commit": "21604255d0e8f9968322f61f2b6c09e5efe1285a" }, - "gruvbox-material": { "branch": "master", "commit": "afb275d8e6dc379762d122bd24e1773fc057abf3" }, + "conform.nvim": { "branch": "master", "commit": "619363c30309d29ffa631e67c8183f2a72caa373" }, + "fff.nvim": { "branch": "feat/implement-buffers-support", "commit": "18504c1bf05add6be1db8c31bad793d9348d7d90" }, + "fidget.nvim": { "branch": "main", "commit": "82404b196e73a00b1727a91903beef5ddc319d22" }, + "gitsigns.nvim": { "branch": "main", "commit": "25050e4ed39e628282831d4cbecb1850454ce915" }, + "grug-far.nvim": { "branch": "main", "commit": "c995bbacf8229dc096ec1c3d60f8531059c86c1b" }, "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" }, - "kanagawa.nvim": { "branch": "master", "commit": "aef7f5cec0a40dbe7f3304214850c472e2264b10" }, + "lanciabones.nvim": { "branch": "main", "commit": "65545cefba9d5d6d59cad047f1103de40aad16a5" }, "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, "lazygit.nvim": { "branch": "main", "commit": "a04ad0dbc725134edbee3a5eea29290976695357" }, "leap.nvim": { "branch": "main", "commit": "e7ca7b92f996bcbc34beee8b244ea5ef6692b478" }, - "lualine.nvim": { "branch": "master", "commit": "a905eeebc4e63fdc48b5135d3bf8aea5618fb21c" }, + "lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" }, "lush.nvim": { "branch": "main", "commit": "9c60ec2279d62487d942ce095e49006af28eed6e" }, - "monokai-pro-vim": { "branch": "main", "commit": "130e87e68e23167e8216eed72d883c35844bd3e4" }, - "nightfox.nvim": { "branch": "main", "commit": "ba47d4b4c5ec308718641ba7402c143836f35aa9" }, - "nvim-highlight-colors": { "branch": "main", "commit": "e2cb22089cc2358b2b995c09578224f142de6039" }, - "nvim-rg": { "branch": "main", "commit": "27c9ca583d05a73048186dc3048b26428ef21753" }, - "nvim-surround": { "branch": "main", "commit": "61319d4bd1c5e336e197defa15bd104c51f0fb29" }, - "nvim-tree.lua": { "branch": "master", "commit": "509962f21ab7289d8dcd28568af539be39a8c01e" }, + "nvim-highlight-colors": { "branch": "main", "commit": "e4c7af0211866162d999ce0bdd6a029302e19139" }, + "nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" }, + "nvim-tree.lua": { "branch": "master", "commit": "07f541fcaa4a5ae019598240362449ab7e9812b3" }, "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" }, "nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" }, - "nvim-web-devicons": { "branch": "master", "commit": "c72328a5494b4502947a022fe69c0c47e53b6aa6" }, - "oscura-vim": { "branch": "main", "commit": "6103dc6942755e88e60103df741e4c4a7ba8b4ce" }, + "nvim-web-devicons": { "branch": "master", "commit": "dfbfaa967a6f7ec50789bead7ef87e336c1fa63c" }, "promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" }, - "rose-pine": { "branch": "main", "commit": "9504524e5ed0e326534698f637f9d038ba4cd0ee" }, - "tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" }, "tsc.nvim": { "branch": "main", "commit": "e083bcf1e54bc3af7df92b33235efb334e8c782c" }, "vim-commentary": { "branch": "master", "commit": "64a654ef4a20db1727938338310209b6a63f60c9" }, - "vimwiki": { "branch": "dev", "commit": "72792615e739d0eb54a9c8f7e0a46a6e2407c9e8" }, - "zenbones.nvim": { "branch": "main", "commit": "22b7fb75593412e0dc81b4bdefae718e9e84aa82" } + "vim-sleuth": { "branch": "master", "commit": "be69bff86754b1aa5adcbb527d7fcd1635a84080" }, + "vimwiki": { "branch": "dev", "commit": "a54a3002e229c4b43d69ced170ff77663a5b2c40" }, + "zenbones.nvim": { "branch": "main", "commit": "239bdb9a09c8d38c752c43590d02f654ed0da2b0" } } diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua index 76d5fe0..ad7b690 100644 --- a/lua/config/autocmds.lua +++ b/lua/config/autocmds.lua @@ -1,64 +1,95 @@ -local function strip_trailing_whitespace() - if vim.bo.binary or vim.bo.filetype == "diff" then - return - end - local cursor = vim.api.nvim_win_get_cursor(0) - local pattern = vim.bo.filetype == "mail" and [[\(^--\)\@/dev/null") - out = (out or ""):gsub("%s+", "") - return out == "Dark" +local function apply_bg(bg) + if bg == current_bg then + return + end + current_bg = bg + vim.opt.background = bg + pcall(vim.cmd.colorscheme, "lanciabones") end -local function desired_background() +local function override_bg() local override = vim.env.NVIM_BACKGROUND if override == "dark" or override == "light" then return override end - - if vim.g.neovide then - return macos_is_dark() and "dark" or "light" - end - - return "dark" + return nil end -local function switch_background_and_colorscheme() - local bg = desired_background() - if bg == current_bg then +local function refresh_background_async() + local override = override_bg() + if override then + apply_bg(override) return end - current_bg = bg - vim.opt.background = bg - - if bg == "dark" then - vim.cmd.colorscheme("arctic") - else - vim.cmd.colorscheme("grb-lucius") + if vim.fn.has("mac") ~= 1 then + apply_bg("dark") + return end + + vim.system( + { "defaults", "read", "-g", "AppleInterfaceStyle" }, + { text = true }, + function(obj) + local is_dark = obj.code == 0 and (obj.stdout or ""):find("Dark") ~= nil + vim.schedule(function() + apply_bg(is_dark and "dark" or "light") + end) + end + ) end -local user_augroup = vim.api.nvim_create_augroup("user_autocmds", { clear = true }) +-- Defer the initial colorscheme application to UIEnter, off the startup +-- critical path. Lanciabones plugin still loads eagerly (priority=1000), +-- so the only thing moved here is the actual `:colorscheme` invocation. +vim.api.nvim_create_autocmd("UIEnter", { + group = user_augroup, + once = true, + callback = function() + -- Set a default immediately so there is no flash of default colours. + apply_bg(override_bg() or "dark") + -- Then refine with the real macOS appearance asynchronously. + refresh_background_async() + end, +}) -vim.api.nvim_create_autocmd({ "FocusGained", "BufEnter" }, { +-- Re-check on focus; cheap because vim.system is async. +vim.api.nvim_create_autocmd("FocusGained", { group = user_augroup, - callback = switch_background_and_colorscheme, + callback = refresh_background_async, }) -switch_background_and_colorscheme() +-- --------------------------------------------------------------------------- +-- Strip trailing whitespace on save +-- --------------------------------------------------------------------------- +local function strip_trailing_whitespace() + if vim.bo.binary or vim.bo.filetype == "diff" then + return + end + local cursor = vim.api.nvim_win_get_cursor(0) + local pattern = vim.bo.filetype == "mail" and [[\(^--\)\@, , ) for fff.nvim are registered +-- lazily in config.plugins so they don't drag the plugin in at startup. +if vim.uv.os_uname().sysname == "Linux" then keymap.set("n", "", function() require("fzf-lua").files() end, { desc = "Find files" }) keymap.set("n", "", function() require("fzf-lua").buffers() end, { desc = "Find buffers" }) keymap.set("n", "", function() require("fzf-lua").git_files() end, { desc = "Find Git files" }) -else - keymap.set("n", "", function() require("fff").find_files() end, { desc = "Find files" }) - keymap.set("n", "", function() require("fff").buffers() end, { desc = "Find buffers" }) - keymap.set("n", "", function() require("fff").git_files() end, { desc = "Find Git files" }) end keymap.set("n", "", ":NvimTreeToggle") @@ -30,6 +27,17 @@ keymap.set("n", "rn", function() vim.lsp.buf.rename() end, { desc = "Ren keymap.set("n", "ca", function() vim.lsp.buf.code_action() end, { desc = "Code action" }) keymap.set("n", "K", function() vim.lsp.buf.hover() end, { desc = "Hover documentation" }) +local function smart_delete(key) + local line = vim.api.nvim_get_current_line() + return (line:match("^%s*$") and '"_' or "") .. key +end + +for _, lhs in ipairs({ "d", "dd", "x", "c", "s", "C", "X" }) do + keymap.set({ "n", "v" }, lhs, function() + return smart_delete(lhs) + end, { desc = "Smart delete", expr = true, noremap = true }) +end + keymap.set("n", "", "G") keymap.set("n", "", "gg") keymap.set("n", "j", "gj") @@ -57,15 +65,3 @@ keymap.set("i", "", function() end return "\r" end, expr_opts) - -if vim.g.neovide then - keymap.set({ "n", "v" }, "", function() - vim.g.neovide_scale_factor = vim.g.neovide_scale_factor + 0.1 - end, { desc = "Increase Neovide scale" }) - keymap.set({ "n", "v" }, "", function() - vim.g.neovide_scale_factor = math.max(0.5, vim.g.neovide_scale_factor - 0.1) - end, { desc = "Decrease Neovide scale" }) - keymap.set({ "n", "v" }, "", function() - vim.g.neovide_scale_factor = 1.0 - end, { desc = "Reset Neovide scale" }) -end diff --git a/lua/config/lsp.lua b/lua/config/lsp.lua index 5009444..1dde558 100644 --- a/lua/config/lsp.lua +++ b/lua/config/lsp.lua @@ -4,6 +4,15 @@ vim.diagnostic.config({ float = { border = "rounded" }, }) +local border = "rounded" +local original_open_floating_preview = vim.lsp.util.open_floating_preview + +function vim.lsp.util.open_floating_preview(contents, syntax, opts, ...) + opts = opts or {} + opts.border = opts.border or border + return original_open_floating_preview(contents, syntax, opts, ...) +end + vim.api.nvim_create_autocmd("LspAttach", { group = lsp_group, callback = function(args) @@ -75,13 +84,34 @@ vim.lsp.config("lua_ls", { }, }) -local opam_switch_prefix = vim.fn.system("opam var prefix"):gsub("\n", "") -local ocamllsp_bin = opam_switch_prefix .. "/bin/ocamllsp" +-- Resolve the opam-managed ocamllsp lazily. Shelling out to `opam var prefix` +-- at startup costs ~30-40ms even when we never open an OCaml file. +local ocamllsp_resolved = false +vim.api.nvim_create_autocmd("FileType", { + group = lsp_group, + pattern = { "ocaml", "ocamlinterface" }, + callback = function() + if ocamllsp_resolved then + return + end + ocamllsp_resolved = true + + local out = vim.fn.system("opam var prefix") + if vim.v.shell_error ~= 0 then + return + end + local prefix = (out or ""):gsub("%s+$", "") + if prefix == "" then + return + end -vim.lsp.config("ocamllsp", { - cmd = { ocamllsp_bin }, - filetypes = { "ocaml", "ocamlinterface" }, - root_markers = { ".opam", "dune-project", ".git" }, + vim.lsp.config("ocamllsp", { + cmd = { prefix .. "/bin/ocamllsp" }, + filetypes = { "ocaml", "ocamlinterface" }, + root_markers = { ".opam", "dune-project", ".git" }, + }) + vim.lsp.enable("ocamllsp") + end, }) vim.lsp.config("pyright", { @@ -98,4 +128,12 @@ vim.lsp.config("pyright", { }, }) -vim.lsp.enable({ "ruby_lsp", "ts_ls", "lua_ls", "ocamllsp", "pyright" }) +local capabilities = vim.lsp.protocol.make_client_capabilities() +capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = false + +vim.lsp.config("*", { + capabilities = capabilities, +}) + +-- ocamllsp is enabled lazily once the first OCaml buffer is opened. +vim.lsp.enable({ "ruby_lsp", "ts_ls", "lua_ls", "pyright" }) diff --git a/lua/config/neovide.lua b/lua/config/neovide.lua deleted file mode 100644 index 77c9078..0000000 --- a/lua/config/neovide.lua +++ /dev/null @@ -1,24 +0,0 @@ -if not vim.g.neovide then - return -end - -if vim.env.NEOVIDE_FONT and vim.env.NEOVIDE_FONT ~= "" then - vim.opt.guifont = vim.env.NEOVIDE_FONT -end - -vim.g.neovide_scale_factor = 1.0 -vim.g.neovide_padding_top = 10 -vim.g.neovide_padding_bottom = 10 -vim.g.neovide_padding_left = 10 -vim.g.neovide_padding_right = 10 -vim.g.neovide_refresh_rate = 120 -vim.g.neovide_floating_blur_amount_x = 2.0 -vim.g.neovide_floating_blur_amount_y = 2.0 -vim.g.neovide_floating_shadow = true -vim.g.neovide_floating_z_height = 10 -vim.g.neovide_light_angle_degrees = 45 -vim.g.neovide_light_radius = 5 -vim.g.neovide_cursor_animation_length = 0.05 -vim.g.neovide_cursor_trail_size = 0.8 -vim.g.neovide_cursor_antialiasing = true -vim.g.neovide_confirm_quit = true diff --git a/lua/config/options.lua b/lua/config/options.lua index 3b17aaf..b0ca05c 100644 --- a/lua/config/options.lua +++ b/lua/config/options.lua @@ -1,29 +1,99 @@ +-- ============================================================================= +-- options.lua - editor settings +-- ============================================================================= vim.scriptencoding = "utf-8" vim.opt.encoding = "utf-8" vim.opt.fileencoding = "utf-8" +-- UI vim.opt.title = true +vim.opt.cmdheight = 1 +vim.opt.laststatus = 2 +vim.opt.cursorline = true +vim.opt.number = true +vim.opt.relativenumber = true +vim.opt.scrolloff = 3 +vim.opt.sidescroll = 0 +vim.opt.sidescrolloff = 3 +vim.opt.showmode = true +vim.opt.showcmd = false +vim.opt.showtabline = 2 +vim.opt.signcolumn = "yes" +vim.opt.pumheight = 20 +vim.opt.pummaxwidth = 80 +vim.opt.pumborder = "rounded" +vim.opt.winborder = "rounded" +vim.opt.termguicolors = true +vim.opt.guifont = "JetBrainsMono Nerd Font:h15" + +-- Indent / formatting vim.opt.autoindent = true vim.opt.smarttab = true vim.opt.smartindent = true vim.opt.breakindent = true -vim.opt.cmdheight = 1 -vim.opt.laststatus = 2 +vim.opt.expandtab = true +vim.opt.shiftwidth = 2 +vim.opt.softtabstop = 2 +vim.opt.tabstop = 2 +vim.opt.shiftround = false +vim.opt.textwidth = 80 +vim.opt.formatoptions:append("jn") +-- Search +vim.opt.ignorecase = true +vim.opt.smartcase = true +vim.opt.hlsearch = true +vim.opt.inccommand = "split" + +-- Editing +vim.opt.backspace = { "start", "eol", "indent" } vim.opt.clipboard = { "unnamed", "unnamedplus" } +vim.opt.hidden = true +vim.opt.joinspaces = false +vim.opt.linebreak = true +vim.opt.modelines = 5 +vim.opt.virtualedit = "block" +vim.opt.whichwrap = "b,h,l,s,<,>,[,],~" +vim.opt.wrap = true + +-- Persistence +vim.opt.backup = false +vim.opt.writebackup = false +vim.opt.swapfile = false +vim.opt.updatetime = 300 +vim.opt.updatecount = 0 +vim.opt.undofile = true +vim.opt.undodir = vim.fn.stdpath("data") .. "/undo//" +vim.opt.viewoptions = "cursor,folds" + +-- Splits / buffers +vim.opt.splitbelow = true +vim.opt.splitright = true +vim.opt.switchbuf = "usetab" +-- Folds (ufo overrides foldcolumn/foldlevel on BufReadPost; treesitter expr +-- is the eager fallback for buffers ufo doesn't touch) vim.opt.foldlevelstart = 99 vim.opt.foldmethod = "expr" vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()" -vim.opt.formatoptions:append("jn") +-- Wildmenu / completion +vim.opt.completeopt = { "menu", "menuone", "popup", "fuzzy", "nearest" } +vim.opt.wildmenu = true +vim.opt.wildmode = "longest:full,full" +vim.opt.wildoptions = "pum" +vim.opt.wildcharm = 26 +vim.opt.wildignore:append({ "*.o", "*.rej", "*.so", "*/node_modules/*" }) + +-- Quiet vim.opt.belloff = "all" +vim.opt.visualbell = true vim.opt.emoji = false -vim.opt.hidden = true -vim.opt.inccommand = "split" -vim.opt.ignorecase = true -vim.opt.joinspaces = false -vim.opt.linebreak = true +vim.opt.spellcapcheck = "" +-- A and I dedup'd; was "AIIOTWacot" +vim.opt.shortmess:append("AIOTWacot") + +-- listchars / fillchars (single canonical assignment, no later append) vim.opt.list = true vim.opt.listchars = { nbsp = "⦸", @@ -31,66 +101,16 @@ vim.opt.listchars = { precedes = "«", tab = "▷─", trail = "•", + leadtab = "▷─", } vim.opt.fillchars = { diff = "╱", eob = " ", fold = "·", + foldinner = " ", vert = "│", } - -vim.opt.modelines = 5 -vim.opt.number = true -vim.opt.pumheight = 20 -vim.opt.pummaxwidth = 80 -vim.opt.relativenumber = true -vim.opt.scrolloff = 3 - -vim.opt.shiftround = false -vim.opt.shiftwidth = 2 -vim.opt.shortmess:append("AIIOTWacot") vim.opt.showbreak = "↳ " -vim.opt.showcmd = false -vim.opt.sidescroll = 0 -vim.opt.sidescrolloff = 3 -vim.opt.smartcase = true -vim.opt.spellcapcheck = "" -vim.opt.splitbelow = true -vim.opt.splitright = true -vim.opt.suffixes:remove(".h") -vim.opt.swapfile = false -vim.opt.switchbuf = "usetab" -vim.opt.tabstop = 2 -vim.opt.termguicolors = true -vim.opt.textwidth = 80 -vim.opt.signcolumn = "yes" -vim.opt.completeopt = { "menu", "menuone", "popup", "fuzzy", "nearest" } -vim.opt.updatetime = 300 -vim.opt.updatecount = 0 -vim.opt.undofile = true -vim.opt.undodir = vim.fn.stdpath("data") .. "/undo//" -vim.opt.viewoptions = "cursor,folds" -vim.opt.virtualedit = "block" -vim.opt.visualbell = true -vim.opt.whichwrap = "b,h,l,s,<,>,[,],~" -vim.opt.wildcharm = 26 -vim.opt.wildignore:append({ "*.o", "*.rej", "*.so", "*/node_modules/*" }) -vim.opt.wildmenu = true -vim.opt.wildmode = "longest:full,full" -vim.opt.writebackup = false -vim.opt.cursorline = true -vim.opt.backup = false -vim.opt.showmode = true -vim.opt.showtabline = 2 -vim.opt.expandtab = true -vim.opt.softtabstop = 2 -vim.opt.backspace = { "start", "eol", "indent" } -vim.opt.hlsearch = true -vim.opt.wrap = true -vim.opt.wildoptions = "pum" -vim.opt.pumborder = "rounded" -vim.opt.winborder = "rounded" - -vim.opt.fillchars:append({ foldinner = " " }) -vim.opt.listchars:append({ leadtab = "▷─" }) +-- Misc +vim.opt.suffixes:remove(".h") diff --git a/lua/config/plugins.lua b/lua/config/plugins.lua index ec0a97a..00ec31b 100644 --- a/lua/config/plugins.lua +++ b/lua/config/plugins.lua @@ -24,19 +24,112 @@ local bubbles_theme = { }, } -local is_linux = vim.uv.os_uname().sysname == "Linux" - return { - is_linux and { - "ibhagwan/fzf-lua", - lazy = false, - dependencies = { "nvim-tree/nvim-web-devicons" }, - } or { + { + "stevearc/conform.nvim", + cmd = { "ConformInfo", "Format" }, + keys = { + { + "f", + function() + require("conform").format({ lsp_format = "fallback" }) + end, + desc = "Format buffer", + }, + }, + opts = { + formatters_by_ft = { + lua = { "stylua" }, + python = { "isort", "black" }, + javascript = { "prettierd", "prettier", stop_after_first = true }, + javascriptreact = { "prettierd", "prettier", stop_after_first = true }, + typescript = { "prettierd", "prettier", stop_after_first = true }, + typescriptreact = { "prettierd", "prettier", stop_after_first = true }, + json = { "prettierd", "prettier", stop_after_first = true }, + yaml = { "prettierd", "prettier", stop_after_first = true }, + markdown = { "prettierd", "prettier", stop_after_first = true }, + html = { "prettierd", "prettier", stop_after_first = true }, + css = { "prettierd", "prettier", stop_after_first = true }, + ruby = { "rubocop" }, + ocaml = { "ocamlformat" }, + }, + }, + config = function(_, opts) + local conform = require("conform") + conform.setup(opts) + + vim.api.nvim_create_user_command("Format", function() + conform.format({ lsp_format = "fallback" }) + end, { desc = "Format current buffer" }) + end, + }, + { + "lewis6991/gitsigns.nvim", + event = { "BufReadPost", "BufNewFile" }, + opts = { + signs = { + add = { text = "┃" }, + change = { text = "┃" }, + delete = { text = "_" }, + topdelete = { text = "‾" }, + changedelete = { text = "~" }, + untracked = { text = "┆" }, + }, + on_attach = function(bufnr) + local gitsigns = require("gitsigns") + local function map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc, silent = true }) + end + + map("n", "]c", function() + if vim.wo.diff then + vim.cmd("normal! ]c") + return + end + gitsigns.next_hunk() + end, "Next Git hunk") + map("n", "[c", function() + if vim.wo.diff then + vim.cmd("normal! [c") + return + end + gitsigns.prev_hunk() + end, "Previous Git hunk") + map("n", "gp", gitsigns.preview_hunk, "Preview Git hunk") + map("n", "gS", gitsigns.stage_hunk, "Stage Git hunk") + map("n", "gR", gitsigns.reset_hunk, "Reset Git hunk") + map("v", "gS", function() + gitsigns.stage_hunk({ vim.fn.line("."), vim.fn.line("v") }) + end, "Stage Git hunk") + map("v", "gR", function() + gitsigns.reset_hunk({ vim.fn.line("."), vim.fn.line("v") }) + end, "Reset Git hunk") + map("n", "gb", function() + gitsigns.blame_line({ full = true }) + end, "Blame Git line") + map("n", "gB", gitsigns.toggle_current_line_blame, "Toggle Git blame") + end, + }, + }, + { + "tpope/vim-sleuth", + event = { "BufReadPost", "BufNewFile" }, + }, + { "vinitkumar/fff.nvim", branch = "feat/implement-buffers-support", build = "cargo build --release", - lazy = false, - keys = {}, + -- Load on demand. The keymaps in config.keymaps invoke `require("fff").*`, + -- so listing the trigger keys here lets lazy.nvim defer loading until a + -- finder is actually opened. Linux uses fzf-lua so we don't trigger fff. + cond = function() + return vim.uv.os_uname().sysname ~= "Linux" + end, + keys = { + { "", function() require("fff").find_files() end, desc = "Find files" }, + { "", function() require("fff").buffers() end, desc = "Find buffers" }, + { "", function() require("fff").git_files() end, desc = "Find Git files" }, + }, }, { "dmmulroy/tsc.nvim", @@ -53,15 +146,6 @@ return { }, { "tpope/vim-commentary", keys = { { "gc", mode = { "n", "v" } } } }, - "duane9/nvim-rg", - "vinitkumar/oscura-vim", - "vinitkumar/monokai-pro-vim", - { "catppuccin/nvim", name = "catppuccin", priority = 1000 }, - { "folke/tokyonight.nvim", priority = 1000 }, - { "rebelot/kanagawa.nvim", priority = 1000 }, - { "EdenEast/nightfox.nvim", priority = 1000 }, - { "rose-pine/neovim", name = "rose-pine", priority = 1000 }, - "sainnhe/gruvbox-material", { "nvim-lualine/lualine.nvim", dependencies = { "nvim-tree/nvim-web-devicons" }, @@ -101,7 +185,6 @@ return { }) end, }, - "sainnhe/everforest", { "lukas-reineke/indent-blankline.nvim", event = "BufReadPost", @@ -115,12 +198,6 @@ return { opts = {}, }, { "kdheepak/lazygit.nvim", cmd = "LazyGit" }, - { - "sourcegraph/amp.nvim", - branch = "main", - lazy = false, - opts = { auto_start = true, log_level = "info" }, - }, { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", @@ -218,19 +295,12 @@ return { opts = {}, }, { - "zenbones-theme/zenbones.nvim", - dependencies = "rktjmp/lush.nvim", + "vinitkumar/lanciabones.nvim", + dependencies = { + "rktjmp/lush.nvim", + "zenbones-theme/zenbones.nvim", + }, lazy = false, priority = 1000, }, - { - "rockyzhang24/arctic.nvim", - dependencies = { "rktjmp/lush.nvim" }, - name = "arctic", - branch = "main", - priority = 1000, - config = function() - vim.cmd("colorscheme arctic") - end - } } diff --git a/readme.md b/readme.md index 9352535..c9b6ae6 100644 --- a/readme.md +++ b/readme.md @@ -9,9 +9,8 @@ This repository contains a Lua-based Neovim configuration with a small `init.lua `init.lua` loads these modules in order: - `config.globals` -- `config.neovide` -- `config.lazy` - `config.options` +- `config.lazy` - `config.autocmds` - `config.keymaps` - `config.lsp` @@ -20,13 +19,11 @@ This repository contains a Lua-based Neovim configuration with a small `init.lua - `init.lua` bootstraps the config - `lua/config/globals.lua` sets leader keys and disables built-in providers/netrw -- `lua/config/neovide.lua` applies GUI-only Neovide settings - `lua/config/lazy.lua` bootstraps `lazy.nvim` - `lua/config/options.lua` sets core editor options - `lua/config/autocmds.lua` defines colorscheme switching and filetype-specific behavior - `lua/config/keymaps.lua` defines custom mappings -- `lua/config/lsp.lua` enables Neovim's built-in LSP clients for Sorbet and OCaml and turns on 0.12 native LSP features on attach -- `coc-settings.json` stores CoC configuration +- `lua/config/lsp.lua` enables Neovim's built-in LSP clients and turns on native LSP features on attach - `colors/` contains local colorscheme files ## Plugin Manager @@ -42,20 +39,9 @@ The current `lua/config/plugins.lua` declares these plugins: | [ibhagwan/fzf-lua](https://github.com/ibhagwan/fzf-lua) | Loaded on Linux as the fuzzy finder UI, with `nvim-web-devicons` integration | | [vinitkumar/fff.nvim](https://github.com/vinitkumar/fff.nvim) | Loaded on non-Linux systems, built with `cargo build --release`, pinned to branch `feat/implement-buffers-support` | | [dmmulroy/tsc.nvim](https://github.com/dmmulroy/tsc.nvim) | Lazy-loaded for TypeScript buffers, configured to run `tsgo --noEmit --pretty false` | -| [neoclide/coc.nvim](https://github.com/neoclide/coc.nvim) | Built with `npm ci`, loaded on `BufReadPre` | | [tpope/vim-commentary](https://github.com/tpope/vim-commentary) | Comment operator on `gc` | -| [duane9/nvim-rg](https://github.com/duane9/nvim-rg) | Ripgrep integration | -| [vinitkumar/oscura-vim](https://github.com/vinitkumar/oscura-vim) | Colorscheme | -| [vinitkumar/monokai-pro-vim](https://github.com/vinitkumar/monokai-pro-vim) | Colorscheme | -| [catppuccin/nvim](https://github.com/catppuccin/nvim) | Installed as `catppuccin` | -| [folke/tokyonight.nvim](https://github.com/folke/tokyonight.nvim) | Colorscheme | -| [rebelot/kanagawa.nvim](https://github.com/rebelot/kanagawa.nvim) | Colorscheme | -| [EdenEast/nightfox.nvim](https://github.com/EdenEast/nightfox.nvim) | Colorscheme | -| [rose-pine/neovim](https://github.com/rose-pine/neovim) | Installed as `rose-pine` | -| [sainnhe/gruvbox-material](https://github.com/sainnhe/gruvbox-material) | Colorscheme | | [nvim-lualine/lualine.nvim](https://github.com/nvim-lualine/lualine.nvim) | Custom "bubbles" statusline theme with native diagnostics/progress segments | | [nvim-tree/nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) | Dependency for lualine and nvim-tree | -| [sainnhe/everforest](https://github.com/sainnhe/everforest) | Colorscheme | | [lukas-reineke/indent-blankline.nvim](https://github.com/lukas-reineke/indent-blankline.nvim) | Loaded via `ibl` on `BufReadPost` | | [nvim-tree/nvim-tree.lua](https://github.com/nvim-tree/nvim-tree.lua) | `:NvimTreeToggle` file tree | | [kdheepak/lazygit.nvim](https://github.com/kdheepak/lazygit.nvim) | `:LazyGit` integration | @@ -64,27 +50,23 @@ The current `lua/config/plugins.lua` declares these plugins: | [brenoprata10/nvim-highlight-colors](https://github.com/brenoprata10/nvim-highlight-colors) | Background color previews, including Tailwind and variable usage | | [kevinhwang91/nvim-ufo](https://github.com/kevinhwang91/nvim-ufo) | Folding with Tree-sitter/indent providers | | [kevinhwang91/promise-async](https://github.com/kevinhwang91/promise-async) | `nvim-ufo` dependency | -| [nvim-pack/nvim-spectre](https://github.com/nvim-pack/nvim-spectre) | Search and replace UI | -| [nvim-lua/plenary.nvim](https://github.com/nvim-lua/plenary.nvim) | `spectre` dependency | +| [MagicDuck/grug-far.nvim](https://github.com/MagicDuck/grug-far.nvim) | Search and replace UI | | [vimwiki/vimwiki](https://github.com/vimwiki/vimwiki) | Wiki and diary support | | [ggandor/leap.nvim](https://github.com/ggandor/leap.nvim) | Motion plugin mapped on `s`, `S`, and `gs` | | [kylechui/nvim-surround](https://github.com/kylechui/nvim-surround) | Surround text objects | | [j-hui/fidget.nvim](https://github.com/j-hui/fidget.nvim) | LSP progress UI | -| [zenbones-theme/zenbones.nvim](https://github.com/zenbones-theme/zenbones.nvim) | Colorscheme | -| [rktjmp/lush.nvim](https://github.com/rktjmp/lush.nvim) | `zenbones.nvim` dependency | +| [rockyzhang24/arctic.nvim](https://github.com/rockyzhang24/arctic.nvim) | Dark colorscheme | +| [rktjmp/lush.nvim](https://github.com/rktjmp/lush.nvim) | `arctic.nvim` dependency | ## Colorschemes -Startup colors are chosen in `lua/config/autocmds.lua`: - -- `catppuccin-mocha` when the desired background is `dark` -- `catppuccin-latte` when the desired background is `light` +`lua/config/autocmds.lua` always loads the local `lancia` colorscheme, which renders both light and dark variants based on `vim.o.background`. Background selection works like this: - `NVIM_BACKGROUND=dark` or `NVIM_BACKGROUND=light` overrides everything -- in Neovide on macOS, the config reads `AppleInterfaceStyle` and follows the system appearance -- outside Neovide, the default is always `dark` +- on macOS, the config reads `AppleInterfaceStyle` and follows the system appearance +- elsewhere, the default is `dark` The repo also ships a `colors/` directory with local colorscheme files. @@ -108,18 +90,16 @@ The current defaults from `lua/config/options.lua` include: - `swapfile`, `backup`, and `writebackup` disabled - `list` enabled by default with visible tab/trailing/extends markers - `splitbelow` and `splitright` enabled -- `shell = "sh"` ## Autocommands The config defines these behaviors in `lua/config/autocmds.lua`: - strip trailing whitespace before write, except for binary buffers and diff files -- re-evaluate background/colorscheme on `FocusGained` and `BufEnter` +- re-evaluate background/colorscheme on `FocusGained` - for `~/vimwiki/diary/*.wiki`, insert a template from `~/.vim/bin/generate-vimwiki-diary-template` - for `*.md`, force `markdown` filetype and set `softtabstop`/`shiftwidth` to 4 -- for `*.md`, `*.txt`, and `COMMIT_EDITMSG`, enable wrap, linebreak, spell, and `kspell` completion -- for `.html`, `*.txt`, `*.md`, and `*.adoc`, enable spelling +- for `*.md`, `*.txt`, `*.adoc`, `*.html`, and `COMMIT_EDITMSG`, enable wrap, linebreak, spell, and `kspell` completion - for `gitcommit`, enable spelling and set `textwidth = 72` - for `javascript`, `typescript`, `json`, `c`, `html`, and `htmldjango`, enforce 2-space indentation - for `*.tsx`, force `filetype=typescript.tsx` @@ -130,8 +110,11 @@ The config defines these behaviors in `lua/config/autocmds.lua`: `lua/config/lsp.lua` enables Neovim's built-in LSP for: -- Sorbet: `srb tc --lsp` with root markers `Gemfile` and `.git` +- Ruby LSP: `ruby-lsp` with root markers `Gemfile`, `.ruby-version`, and `.git` +- TypeScript LSP: `typescript-language-server --stdio` +- Lua LSP: `lua-language-server` - OCaml: `$(opam var prefix)/bin/ocamllsp` with root markers `.opam`, `dune-project`, and `.git` +- Pyright: `pyright-langserver --stdio` On `LspAttach`, the config also enables these Neovim 0.12 native features when the server supports them: @@ -140,8 +123,6 @@ On `LspAttach`, the config also enables these Neovim 0.12 native features when t - linked editing ranges - rounded diagnostic floats -CoC is still installed as a fallback for filetypes that do not have a native LSP client configured here. - ## Keymaps The current custom mappings from `lua/config/keymaps.lua` are: @@ -153,54 +134,32 @@ The current custom mappings from `lua/config/keymaps.lua` are: | Normal | `` | Linux: `require("fzf-lua").git_files()`, otherwise `require("fff").git_files()` | | Normal | `` | `:NvimTreeToggle` | | Normal | `` | `:tabNext` | -| Normal | `` | native diagnostics loclist when a built-in LSP client is attached, otherwise `:CocDiagnostics` | -| Normal | `` | `:GFiles` | +| Normal | `` | open buffer diagnostics in the location list | | Normal | `` | `:LazyGit` | -| Normal | `gd` | native LSP definition when available, otherwise CoC definition | -| Normal | `gy` | native LSP type definition when available, otherwise CoC type definition | -| Normal | `gr` | native LSP references when available, otherwise CoC references | -| Normal | `gi` | native LSP implementation when available, otherwise CoC implementation | +| Normal | `gd` | native LSP definition | +| Normal | `gy` | native LSP type definition | +| Normal | `gr` | native LSP references | +| Normal | `gi` | native LSP implementation | | Normal | `h` | horizontal split | -| Normal | `lr` | `:lsp restart` | +| Normal | `lr` | `:LspRestart` | | Normal | `lw` | native workspace diagnostics | -| Normal | `z` | `:Goyo` | | Normal | `v` | vertical split | | Normal | `t` | new tab | | Normal | `dt` | insert `strftime("%c")` | +| Normal | `rn` | rename symbol | +| Normal | `ca` | code action | | Normal | `` | go to end of file (`G`) | | Normal | `` | go to start of file (`gg`) | | Normal | `` | display-line down (`gj`) | | Normal | `` | display-line up (`gk`) | | Normal | `grx` | run native LSP code lens | -| Insert | `` | CoC popup next item, otherwise native popup next item, native omni-completion trigger, or literal tab | -| Insert | `` | CoC popup previous item or native popup previous item | -| Insert | `` | CoC confirm, otherwise native popup confirm when an item is selected, otherwise newline | -| Normal/Visual | `` | increase Neovide scale | -| Normal/Visual | `` | decrease Neovide scale | -| Normal/Visual | `` | reset Neovide scale | +| Normal | `K` | hover documentation | +| Insert | `` | native popup next item or literal tab | +| Insert | `` | native popup previous item or literal shifted tab | +| Insert | `` | native popup confirm when an item is selected, otherwise newline | Leader is `,`. -## Neovide - -`lua/config/neovide.lua` is only applied when `vim.g.neovide` is set. - -Current Neovide settings: - -- `guifont` is set only when `NEOVIDE_FONT` is non-empty -- scale factor starts at `1.0` -- padding is `10` on all sides -- refresh rate is `120` -- floating blur is `2.0` on both axes -- floating shadow is enabled -- floating z height is `10` -- light angle is `45` -- light radius is `5` -- cursor animation length is `0.05` -- cursor trail size is `0.8` -- cursor antialiasing is enabled -- quit confirmation is enabled - ## Requirements Based on the current config, these external tools are expected: @@ -209,12 +168,12 @@ Based on the current config, these external tools are expected: - `git` to bootstrap `lazy.nvim` - `fzf` for `fzf-lua` on Linux - `cargo` to build `fff.nvim` on non-Linux systems -- `npm` to build `coc.nvim` -- `node` to run `coc.nvim` -- `ripgrep` for `nvim-rg` - `lazygit` for `:LazyGit` -- `srb` for the Sorbet LSP +- `ruby-lsp` for Ruby +- `typescript-language-server` for JavaScript and TypeScript +- `lua-language-server` for Lua - `opam` and `ocamllsp` for the OCaml LSP +- `pyright-langserver` for Python - `tsgo` for `tsc.nvim` - `~/.vim/bin/generate-vimwiki-diary-template` if you use vimwiki diary creation