From: Samir Benmendil Date: Sun, 2 Mar 2025 16:28:57 +0000 (+0000) Subject: Merge branch 'remove-lazyvim' X-Git-Url: https://git.rmz.io/dotfiles.git/commitdiff_plain/8de4d1975c84ef549c29f2a1d8b5ccc70f957679?hp=8d84045fd04e6f9ad045cd29a9faea4f056f2357 Merge branch 'remove-lazyvim' --- diff --git a/nvim/init.lua b/nvim/init.lua index ffbd395..13a0be5 100644 --- a/nvim/init.lua +++ b/nvim/init.lua @@ -4,6 +4,6 @@ vim.cmd([[ source $XDG_CONFIG_HOME/vim/vimrc ]]) -- TODO: review if I want this for some buffers vim.g.autoformat = false --- bootstrap lazy.nvim, LazyVim and your plugins -package.loaded["lazyvim.config.options"] = true -require("config.lazy") +_G.rmz = require("rmz.util") + +require("config") diff --git a/nvim/lua/config/autocmds.lua b/nvim/lua/config/autocmds.lua index 2e26068..596f65d 100644 --- a/nvim/lua/config/autocmds.lua +++ b/nvim/lua/config/autocmds.lua @@ -1,14 +1,52 @@ --- Autocmds are automatically loaded on the VeryLazy event +-- Autocmds are automatically loaded by config.init local function augroup(name) - return vim.api.nvim_create_augroup("lazyvim_" .. name, { clear = true }) + return vim.api.nvim_create_augroup("rmz_" .. name, { clear = true }) end +-- Check if we need to reload the file when it changed +vim.api.nvim_create_autocmd({ "FocusGained", "TermClose", "TermLeave" }, { + group = augroup("checktime"), + callback = function() + if vim.o.buftype ~= "nofile" then + vim.cmd("checktime") + end + end, +}) + -- Highlight on yank vim.api.nvim_create_autocmd("TextYankPost", { group = augroup("highlight_yank"), callback = function() - vim.highlight.on_yank() + (vim.hl or vim.highlight).on_yank() + end, +}) + +-- resize splits if window got resized +vim.api.nvim_create_autocmd({ "VimResized" }, { + group = augroup("resize_splits"), + callback = function() + local current_tab = vim.fn.tabpagenr() + vim.cmd("tabdo wincmd =") + vim.cmd("tabnext " .. current_tab) + end, +}) + +-- go to last loc when opening a buffer +vim.api.nvim_create_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].rmz_last_loc then + return + end + vim.b[buf].rmz_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, }) @@ -16,10 +54,11 @@ vim.api.nvim_create_autocmd("TextYankPost", { vim.api.nvim_create_autocmd("FileType", { group = augroup("close_with_q"), pattern = { - "PlenaryTestPopup", + "checkhealth", + "dbout", + "gitsigns-blame", "help", "lspinfo", - "man", "notify", "qf", "spectre_panel", @@ -28,6 +67,33 @@ vim.api.nvim_create_autocmd("FileType", { }, callback = function(event) vim.bo[event.buf].buflisted = false - vim.keymap.set("n", "q", "close", { buffer = event.buf, silent = true }) + 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 +vim.api.nvim_create_autocmd("FileType", { + group = augroup("man_unlisted"), + pattern = { "man" }, + callback = function(event) + vim.bo[event.buf].buflisted = false + end, +}) + +-- Fix conceallevel for json files +vim.api.nvim_create_autocmd({ "FileType" }, { + group = augroup("json_conceal"), + pattern = { "json", "jsonc", "json5" }, + callback = function() + vim.opt_local.conceallevel = 0 end, }) diff --git a/nvim/lua/config/init.lua b/nvim/lua/config/init.lua new file mode 100644 index 0000000..e9ad2fd --- /dev/null +++ b/nvim/lua/config/init.lua @@ -0,0 +1,12 @@ +require("config.lazy") +require("config.options") +require("config.autocmds") + +local group = vim.api.nvim_create_augroup("Init", { clear = true }) +vim.api.nvim_create_autocmd("User", { + group = group, + pattern = "VeryLazy", + callback = function() + require("config.keymaps") + end, +}) diff --git a/nvim/lua/config/keymaps.lua b/nvim/lua/config/keymaps.lua index fc3ba2f..fbe320c 100644 --- a/nvim/lua/config/keymaps.lua +++ b/nvim/lua/config/keymaps.lua @@ -1,60 +1,75 @@ -- Keymaps are automatically loaded on the VeryLazy event --- NOTE: Default LazyVim keys are not loaded automatically, review upstream keymas occasionnaly. --- ~/.local/share/nvim/lazy/LazyVim/lua/lazyvim/config/keymaps.lua -local Util = require("lazyvim.util") +local map = vim.keymap.set + +-- better up/down +map({ "n", "x" }, "j", "v:count == 0 ? 'gj' : 'j'", { desc = "Down", expr = true, silent = true }) +map({ "n", "x" }, "", "v:count == 0 ? 'gj' : 'j'", { desc = "Down", expr = true, silent = true }) +map({ "n", "x" }, "k", "v:count == 0 ? 'gk' : 'k'", { desc = "Up", expr = true, silent = true }) +map({ "n", "x" }, "", "v:count == 0 ? 'gk' : 'k'", { desc = "Up", expr = true, silent = true }) + +-- Move to window using the hjkl keys +map("n", "", "W", { desc = "Go to Prev Window", remap = true }) +map("n", "", "w", { desc = "Go to Next Window", remap = true }) +map("n", "", "j", { desc = "Go to Lower Window", remap = true }) +map("n", "", "k", { desc = "Go to Upper Window", remap = true }) -- Resize window using arrow keys -- TODO: this is cool, but should also support a number -vim.keymap.set("n", "", "resize +2", { desc = "Increase window height" }) -vim.keymap.set("n", "", "resize -2", { desc = "Decrease window height" }) -vim.keymap.set("n", "", "vertical resize -2", { desc = "Decrease window width" }) -vim.keymap.set("n", "", "vertical resize +2", { desc = "Increase window width" }) +map("n", "", "resize +2", { desc = "Increase Window Height" }) +map("n", "", "resize -2", { desc = "Decrease Window Height" }) +map("n", "", "vertical resize -2", { desc = "Decrease Window Width" }) +map("n", "", "vertical resize +2", { desc = "Increase Window Width" }) -- Move Lines -vim.keymap.set("n", "", "m .+1==", { desc = "Move down" }) -vim.keymap.set("n", "", "m .-2==", { desc = "Move up" }) --- NOTE: this triggers with when I'm too quick at doing "go to normal mode then move line" -vim.keymap.set("i", "", "m .+1==gi", { desc = "Move down" }) -vim.keymap.set("i", "", "m .-2==gi", { desc = "Move up" }) -vim.keymap.set("v", "", ":m '>+1gv=gv", { desc = "Move down" }) -vim.keymap.set("v", "", ":m '<-2gv=gv", { desc = "Move up" }) +-- NOTE: these trigger with when I'm too quick at doing "go to normal mode then move line" +map("n", "", "execute 'move .+' . v:count1==", { desc = "Move Down" }) +map("n", "", "execute 'move .-' . (v:count1 + 1)==", { desc = "Move Up" }) +map("i", "", "m .+1==gi", { desc = "Move Down" }) +map("i", "", "m .-2==gi", { desc = "Move Up" }) +map("v", "", ":execute \"'<,'>move '>+\" . v:count1gv=gv", { desc = "Move Down" }) +map("v", "", ":execute \"'<,'>move '<-\" . (v:count1 + 1)gv=gv", { desc = "Move Up" }) -- buffers -vim.keymap.set("n", "", "BufferLineCyclePrev", { desc = "Prev buffer" }) -vim.keymap.set("n", "", "BufferLineCycleNext", { desc = "Next buffer" }) -vim.keymap.set("n", "[b", "BufferLineCyclePrev", { desc = "Prev buffer" }) -vim.keymap.set("n", "]b", "BufferLineCycleNext", { desc = "Next buffer" }) +map("n", "", "BufferLineCyclePrev", { desc = "Prev Buffer" }) +map("n", "", "BufferLineCycleNext", { desc = "Next Buffer" }) +map("n", "[b", "BufferLineCyclePrev", { desc = "Prev Buffer" }) +map("n", "]b", "BufferLineCycleNext", { desc = "Next Buffer" }) -vim.keymap.set("n", "bb", "e #", { desc = "Switch to Other Buffer" }) +map("n", "bb", "e #", { desc = "Switch to Other Buffer" }) --- Clear search with -vim.keymap.set({ "i", "n" }, "", "noh", { desc = "Escape and clear hlsearch" }) +-- Clear search and stop snippet on escape +map({ "i", "n", "s" }, "", function() + vim.cmd("noh") + -- TODO: stop snippet with esc *but don't do it in insert mode* + -- require("luasnip").unlink_current() + return "" +end, { expr = true, desc = "Escape and Clear hlsearch" }) -- Clear search, diff update and redraw -- taken from runtime/lua/_editor.lua -vim.keymap.set( +map( "n", "ur", "nohlsearchdiffupdatenormal! ", - { desc = "Redraw / clear hlsearch / diff update" } + { desc = "Redraw / Clear hlsearch / Diff Update" } ) -vim.keymap.set({ "n", "x" }, "gw", "*N", { desc = "Search word under cursor" }) +map({ "n", "x" }, "gw", "*N", { desc = "Search word under cursor" }) -- Add undo break-points -vim.keymap.set("i", ",", ",u") -vim.keymap.set("i", ".", ".u") -vim.keymap.set("i", ";", ";u") +map("i", ",", ",u") +map("i", ".", ".u") +map("i", ";", ";u") + +--keywordprg +map("n", "K", "norm! K", { desc = "Keywordprg" }) -- lazy -vim.keymap.set("n", "l", ":Lazy", { desc = "Lazy" }) +map("n", "l", "Lazy", { desc = "Lazy" }) -- new file -vim.keymap.set("n", "fn", "enew", { desc = "New File" }) - -vim.keymap.set("n", "[q", vim.cmd.cprev, { desc = "Previous quickfix" }) -vim.keymap.set("n", "]q", vim.cmd.cnext, { desc = "Next quickfix" }) +map("n", "fn", "enew", { desc = "New File" }) -- diagnostic local diagnostic_goto = function(next, severity) @@ -64,38 +79,49 @@ local diagnostic_goto = function(next, severity) go({ severity = severity }) end end -vim.keymap.set("n", "cd", vim.diagnostic.open_float, { desc = "Line Diagnostics" }) -vim.keymap.set("n", "]d", diagnostic_goto(true), { desc = "Next Diagnostic" }) -vim.keymap.set("n", "[d", diagnostic_goto(false), { desc = "Prev Diagnostic" }) -vim.keymap.set("n", "]e", diagnostic_goto(true, "ERROR"), { desc = "Next Error" }) -vim.keymap.set("n", "[e", diagnostic_goto(false, "ERROR"), { desc = "Prev Error" }) -vim.keymap.set("n", "]w", diagnostic_goto(true, "WARN"), { desc = "Next Warning" }) -vim.keymap.set("n", "[w", diagnostic_goto(false, "WARN"), { desc = "Prev Warning" }) +map("n", "cd", vim.diagnostic.open_float, { desc = "Line Diagnostics" }) --- stylua: ignore start +map("n", "]d", diagnostic_goto(true), { desc = "Next Diagnostic" }) +map("n", "[d", diagnostic_goto(false), { desc = "Prev Diagnostic" }) +map("n", "]e", diagnostic_goto(true, "ERROR"), { desc = "Next Error" }) +map("n", "[e", diagnostic_goto(false, "ERROR"), { desc = "Prev Error" }) +map("n", "]w", diagnostic_goto(true, "WARN"), { desc = "Next Warning" }) +map("n", "[w", diagnostic_goto(false, "WARN"), { desc = "Prev Warning" }) -- toggle options -vim.keymap.set("n", "uf", Util.format.toggle, { desc = "Toggle format on Save" }) -vim.keymap.set("n", "ud", Snacks.toggle.diagnostics, { desc = "Toggle Diagnostics" }) -local conceallevel = vim.o.conceallevel > 0 and vim.o.conceallevel or 3 -vim.keymap.set("n", "uc", function() Snacks.toggle("conceallevel", false, {0, conceallevel}) end, { desc = "Toggle Conceal" }) +Snacks.toggle.option("spell", { name = "Spelling" }):map("us") +Snacks.toggle.option("wrap", { name = "Wrap" }):map("uw") +Snacks.toggle.option("relativenumber", { name = "Relative Number" }):map("uL") +Snacks.toggle.diagnostics():map("ud") +Snacks.toggle.line_number():map("ul") +Snacks.toggle.option("conceallevel", { off = 0, on = vim.o.conceallevel > 0 and vim.o.conceallevel or 2, name = "Conceal Level" }):map("uc") +Snacks.toggle.option("showtabline", { off = 0, on = vim.o.showtabline > 0 and vim.o.showtabline or 2, name = "Tabline" }):map("uA") +Snacks.toggle.treesitter():map("uT") +Snacks.toggle.option("background", { off = "light", on = "dark" , name = "Dark Background" }):map("ub") +Snacks.toggle.dim():map("uD") +Snacks.toggle.animate():map("ua") +Snacks.toggle.indent():map("ug") +Snacks.toggle.scroll():map("uS") +Snacks.toggle.profiler():map("dpp") +Snacks.toggle.profiler_highlights():map("dph") + +if vim.lsp.inlay_hint then + Snacks.toggle.inlay_hints():map("uh") +end + +map({ "n", "x" }, "gB", function() Snacks.gitbrowse() end, { desc = "Git Browse (open)" }) +map({"n", "x" }, "gY", function() + Snacks.gitbrowse({ open = function(url) vim.fn.setreg("+", url) end, notify = false }) +end, { desc = "Git Browse (copy)" }) -- highlights under cursor -if vim.fn.has("nvim-0.9.0") == 1 then - vim.keymap.set("n", "ui", vim.show_pos, { desc = "Inspect Pos" }) -end +map("n", "ui", vim.show_pos, { desc = "Inspect Pos" }) +map("n", "uI", "InspectTree", { desc = "Inspect Tree" }) -- floating terminal -- TODO: I prefer a split buffer for terminal, need to review how to make this more vim-like -local lazyterm = function() LazyVim.terminal(nil, { cwd = LazyVim.root() }) end -vim.keymap.set("n", "ft", lazyterm, { desc = "Terminal (Root Dir)" }) -vim.keymap.set("n", "fT", function() LazyVim.terminal() end, { desc = "Terminal (cwd)" }) +map("n", "", function() Snacks.terminal() end, { desc = "Terminal (cmd)" }) -- Terminal Mappings -vim.keymap.set("t", "", "", { desc = "Enter Normal Mode" }) -vim.keymap.set("t", "", "wincmd h", { desc = "Go to Left Window" }) -vim.keymap.set("t", "", "wincmd j", { desc = "Go to Lower Window" }) -vim.keymap.set("t", "", "wincmd k", { desc = "Go to Upper Window" }) -vim.keymap.set("t", "", "wincmd l", { desc = "Go to Right Window" }) -vim.keymap.set("t", "", "close", { desc = "Hide Terminal" }) -vim.keymap.set("t", "", "close", { desc = "which_key_ignore" }) +map("t", "", "", { desc = "Enter Normal Mode" }) +map("t", "", "close", { desc = "Hide Terminal" }) diff --git a/nvim/lua/config/lazy.lua b/nvim/lua/config/lazy.lua index b88ded7..226c16d 100644 --- a/nvim/lua/config/lazy.lua +++ b/nvim/lua/config/lazy.lua @@ -1,35 +1,21 @@ local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then -- bootstrap lazy.nvim - -- stylua: ignore vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath }) end vim.opt.rtp:prepend(vim.env.LAZY or lazypath) require("lazy").setup({ spec = { - -- add LazyVim and import its plugins - { "LazyVim/LazyVim", import = "lazyvim.plugins" }, - -- import any extras modules here - -- { import = "lazyvim.plugins.extras.lang.typescript" }, - -- { import = "lazyvim.plugins.extras.lang.json" }, - -- { import = "lazyvim.plugins.extras.ui.mini-animate" }, - -- import/override with your plugins { import = "plugins" }, { import = "plugins.lang" }, }, defaults = { - -- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup. - -- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default. lazy = false, - -- It's recommended to leave version=false for now, since a lot the plugin that support versioning, - -- have outdated releases, which may break your Neovim install. version = false, -- always use the latest git commit - -- version = "*", -- try installing the latest stable version for plugins that support semver }, - install = { colorscheme = { "tokyonight", "habamax" } }, + install = { colorscheme = { "onenord", "habamax" } }, checker = { - -- automatically check for plugin updates enabled = true, notify = false, }, diff --git a/nvim/lua/config/options.lua b/nvim/lua/config/options.lua index cd18d69..aeaca61 100644 --- a/nvim/lua/config/options.lua +++ b/nvim/lua/config/options.lua @@ -2,6 +2,16 @@ local opt = vim.opt +opt.fillchars = { + foldopen = "", + foldclose = "", + fold = " ", + foldsep = " ", + eob = " ", + diff = "⣿", + vert = "│", +} + vim.g.man_hardwrap = 1 vim.env.MANWIDTH = 80 @@ -16,13 +26,10 @@ opt.pumheight = 10 -- Maximum number of entries in a popup opt.showmode = false -- Dont show mode since we have a statusline opt.signcolumn = "yes" -- Always show the signcolumn, otherwise it would shift the text each time opt.updatetime = 200 -- Save swap file and trigger CursorHold -if vim.fn.has("nvim-0.9.0") == 1 then - opt.splitkeep = "screen" - opt.shortmess:append({ C = true }) -end +opt.splitkeep = "screen" +opt.shortmess:append({ C = true }) +opt.foldmethod = "expr" +opt.foldexpr = "v:lua.require'rmz.util'.foldexpr()" + -- do not let markdown plugin change indent vim.g.markdown_recommended_style = 0 - -vim.g.lazyvim_picker = "telescope" - -vim.g.snacks_animate = false diff --git a/nvim/lua/plugins/blink.lua b/nvim/lua/plugins/blink.lua new file mode 100644 index 0000000..e98ae95 --- /dev/null +++ b/nvim/lua/plugins/blink.lua @@ -0,0 +1,78 @@ +return { + { + "saghen/blink.cmp", + version = "*", + -- build = "cargo build --release", + dependencies = { + "rafamadriz/friendly-snippets", + }, + -- NOTE: not described in upstream install docs + event = "InsertEnter", + + ---@module 'blink.cmp' + ---@type blink.cmp.Config + opts = { + completion = { + menu = { draw = { treesitter = { "lsp" }, }, }, + documentation = { + auto_show = true, + auto_show_delay_ms = 200, + }, + ghost_text = { + enabled = true, + }, + }, + + cmdline = { + enabled = false, + }, + sources = { + default = { "lsp", "path", "snippets", "buffer" }, + }, + snippets = { + preset = "luasnip", + }, + + keymap = { + preset = "enter", + [""] = { "select_and_accept" }, + }, + }, + opts_extend = { "sources.default", }, + }, + + -- add icons + { + "saghen/blink.cmp", + opts = function(_, opts) + opts.appearance = opts.appearance or {} + opts.appearance.kind_icons = vim.tbl_extend("force", opts.appearance.kind_icons or {}, rmz.ui.icons.kinds) + end, + }, + + -- lazydev + { + "saghen/blink.cmp", + opts = { + sources = { + -- add lazydev to your completion providers + default = { "lazydev" }, + providers = { + lazydev = { + name = "LazyDev", + module = "lazydev.integrations.blink", + score_offset = 100, -- show at a higher priority than lsp + }, + }, + }, + }, + }, + -- catppuccin support + { + "catppuccin", + optional = true, + opts = { + integrations = { blink_cmp = true }, + }, + }, +} diff --git a/nvim/lua/plugins/coding.lua b/nvim/lua/plugins/coding.lua index 131fc4a..77d4955 100644 --- a/nvim/lua/plugins/coding.lua +++ b/nvim/lua/plugins/coding.lua @@ -1,106 +1,93 @@ ----@type LazyPluginSpec +---@type LazySpec return { -- snippets - { - "L3MON4D3/LuaSnip", - -- disable luasnip bindings for and + { "l3mon4d3/luasnip", + build = "make install_jsregexp", -- optional dependencies = { - { - "honza/vim-snippets", - config = function() - require("luasnip.loaders.from_snipmate").lazy_load() - require("luasnip.loaders.from_lua").load({ paths = vim.fn.stdpath("config") .. "/lua/snippets" }) - end, - }, + { "honza/vim-snippets", }, }, + config = function() + local snippets = vim.fn.stdpath("config").."/snippets" + require("luasnip.loaders.from_snipmate").load({ paths = { snippets }}) + require("luasnip.loaders.from_lua").load({ paths = { snippets }}) + end, keys = function() return {} end, opts = { + history = true, + delete_check_events = "TextChanged", store_selection_keys = "", }, }, - -- auto completion - { - "hrsh7th/nvim-cmp", - version = false, -- last release is way too old - event = "InsertEnter", - dependencies = { - "hrsh7th/cmp-nvim-lsp", - "hrsh7th/cmp-buffer", - "hrsh7th/cmp-path", - "saadparwaiz1/cmp_luasnip", + { "echasnovski/mini.pairs", + event = "VeryLazy", + opts = { + modes = { insert = true, command = true, terminal = false }, + -- skip autopair when next character is one of these + skip_next = [=[[%w%%%'%[%"%.%`%$]]=], + -- skip autopair when the cursor is inside these treesitter nodes + skip_ts = { "string" }, + -- skip autopair when next character is closing pair + -- and there are more closing pairs than opening pairs + skip_unbalanced = true, + -- better deal with markdown code blocks + markdown = true, }, - ---@param opts cmp.ConfigSchema - opts = function(_, opts) - local has_words_before = function() - unpack = unpack or table.unpack - local line, col = unpack(vim.api.nvim_win_get_cursor(0)) - return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil - end - - local cmp = require("cmp") - local luasnip = require("luasnip") - - local upstream_format = opts.formatting.format - opts.formatting.format = function(entry, vim_item) - vim_item = upstream_format(entry, vim_item) - local menu = { - nvim_lsp = "[lsp]", - luasnip = "[snip]", - buffer = "[buf]", - path = "[path]", - } - vim_item.menu = menu[entry.source.name] - return vim_item - end + config = function(_, opts) + Snacks.toggle({ + name = "Mini Pairs", + get = function() + return not vim.g.minipairs_disable + end, + set = function(state) + vim.g.minipairs_disable = not state + end, + }):map("up") - opts.completion = vim.tbl_extend("force", opts.completion, { - completeopt = "menu,menuone,noselect", - }) - -- TODO: review if I want to keep any of LazyVim's mappings - opts.mapping = { - -- lazyvims - [""] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }), - [""] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }), - [""] = cmp.mapping.scroll_docs(-4), - [""] = cmp.mapping.scroll_docs(4), - [""] = cmp.mapping.abort(), - [""] = cmp.mapping.complete(), - -- mine - [""] = cmp.mapping(function(fallback) - if cmp.visible() then - cmp.select_next_item() - elseif luasnip.expand_or_jumpable() then - luasnip.expand_or_jump() - elseif has_words_before() then - cmp.complete() - else - fallback() + local pairs = require("mini.pairs") + pairs.setup(opts) + local open = pairs.open + --- Custom open command with extensions from LazyVim + ---@diagnostic disable-next-line: duplicate-set-field + pairs.open = function(pair, neigh_pattern) + if vim.fn.getcmdline() ~= "" then + return open(pair, neigh_pattern) + end + local o, c = pair:sub(1, 1), pair:sub(2, 2) + local line = vim.api.nvim_get_current_line() + local cursor = vim.api.nvim_win_get_cursor(0) + local next = line:sub(cursor[2] + 1, cursor[2] + 1) + local before = line:sub(1, cursor[2]) + -- don't add fourth ` in markdown ``` + if opts.markdown and o == "`" and vim.bo.filetype == "markdown" and before:match("^%s*``") then + return "`\n```" .. vim.api.nvim_replace_termcodes("", true, true, true) + end + if opts.skip_next and next ~= "" and next:match(opts.skip_next) then + return o + end + if opts.skip_ts and #opts.skip_ts > 0 then + local ok, captures = pcall(vim.treesitter.get_captures_at_pos, 0, cursor[1] - 1, math.max(cursor[2] - 1, 0)) + for _, capture in ipairs(ok and captures or {}) do + if vim.tbl_contains(opts.skip_ts, capture.capture) then + return o + end end - end, { "i", "s" }), - [""] = cmp.mapping(function(fallback) - if cmp.visible() then - cmp.select_prev_item() - elseif luasnip.jumpable(-1) then - luasnip.jump(-1) - else - fallback() + end + if opts.skip_unbalanced and next == c and c ~= o then + local _, count_open = line:gsub(vim.pesc(pair:sub(1, 1)), "") + local _, count_close = line:gsub(vim.pesc(pair:sub(2, 2)), "") + if count_close > count_open then + return o end - end, { "i", "s" }), - } + end + return open(pair, neigh_pattern) + end end, }, - -- auto pairs - { - "echasnovski/mini.pairs", - }, - - -- surround - { - "echasnovski/mini.surround", + { "echasnovski/mini.surround", keys = function() -- HACK: use function to disable merging with LazyVim's keys definition return { @@ -131,20 +118,117 @@ return { vim.keymap.set("x", "S", [[:lua MiniSurround.add('visual')]], { silent = true }) end, }, - -- comments - { - "numToStr/Comment.nvim", + + { "echasnovski/mini.ai", + -- TODO: port vim text objects over + event = "VeryLazy", + opts = function() + local ai = require("mini.ai") + return { + n_lines = 500, + custom_textobjects = { + o = ai.gen_spec.treesitter({ -- code block + a = { "@block.outer", "@conditional.outer", "@loop.outer" }, + i = { "@block.inner", "@conditional.inner", "@loop.inner" }, + }), + f = ai.gen_spec.treesitter({ a = "@function.outer", i = "@function.inner" }), -- function + c = ai.gen_spec.treesitter({ a = "@class.outer", i = "@class.inner" }), -- class + t = { "<([%p%w]-)%f[^<%w][^<>]->.-", "^<.->().*()$" }, -- tags + d = { "%f[%d]%d+" }, -- digits + e = { -- Word with case + { "%u[%l%d]+%f[^%l%d]", "%f[%S][%l%d]+%f[^%l%d]", "%f[%P][%l%d]+%f[^%l%d]", "^[%l%d]+%f[^%l%d]" }, + "^().*()$", + }, + u = ai.gen_spec.function_call(), -- u for "Usage" + U = ai.gen_spec.function_call({ name_pattern = "[%w_]" }), -- without dot in function name + }, + } + end, + }, + { "numToStr/Comment.nvim", + dependencies = { + { "JoosepAlviste/nvim-ts-context-commentstring", -- nested language commenting (f.ex markdown code blocks) + opts = { enable_autocmd = false, }, + } + }, + opts = function(_, opts) + local tscci = require('ts_context_commentstring.integrations.comment_nvim') + vim.tbl_deep_extend('force', opts, { + toggler = { + line = "gcc", + block = "gbb", + }, + mappings = { + basic = true, + extra = true, + }, + pre_hook = tscci.create_pre_hook() + }) + return opts + end, + }, + { "danymat/neogen", -- Generate annotations like doxygen + cmd = "Neogen", + keys = { + { "cn", function() require("neogen").generate() end, desc = "Generate Annotations (Neogen)", }, + }, opts = { - toggler = { - line = "gcc", - block = "gbb", + snippet_engine = "luasnip", + }, + }, + { "folke/lazydev.nvim", + ft = "lua", + cmd = "LazyDev", + opts = { + library = { + { path = "${3rd}/luv/library", words = { "vim%.uv" } }, + { path = "snacks.nvim", words = { "Snacks" } }, + { path = "lazy.nvim", words = { "LazySpec" } }, }, - mappings = { - basic = true, - extra = true, + }, + }, + { "gbprod/yanky.nvim", enabled = false, -- better yank/paste + -- TODO: integrate? + -- I'm not entirely convinced, it doesn't provide functionality like vim-peekaboo, + -- maybe it can supplement it though? + -- Also, see issues on OSC52: https://github.com/gbprod/yanky.nvim/issues/213 + recommended = true, + desc = "Better Yank/Paste", + event = "LazyFile", + opts = { + highlight = { timer = 150 }, + }, + keys = { + { + "p", + function() vim.cmd([[YankyRingHistory]]) end, + mode = { "n", "x" }, + desc = "Open Yank History", }, + -- stylua: ignore + { "y", "(YankyYank)", mode = { "n", "x" }, desc = "Yank Text" }, + { "p", "(YankyPutAfter)", mode = { "n", "x" }, desc = "Put Text After Cursor" }, + { "P", "(YankyPutBefore)", mode = { "n", "x" }, desc = "Put Text Before Cursor" }, + { "gp", "(YankyGPutAfter)", mode = { "n", "x" }, desc = "Put Text After Selection" }, + { "gP", "(YankyGPutBefore)", mode = { "n", "x" }, desc = "Put Text Before Selection" }, + { "[y", "(YankyCycleForward)", desc = "Cycle Forward Through Yank History" }, + { "]y", "(YankyCycleBackward)", desc = "Cycle Backward Through Yank History" }, + { "]p", "(YankyPutIndentAfterLinewise)", desc = "Put Indented After Cursor (Linewise)" }, + { "[p", "(YankyPutIndentBeforeLinewise)", desc = "Put Indented Before Cursor (Linewise)" }, + { "]P", "(YankyPutIndentAfterLinewise)", desc = "Put Indented After Cursor (Linewise)" }, + { "[P", "(YankyPutIndentBeforeLinewise)", desc = "Put Indented Before Cursor (Linewise)" }, + { ">p", "(YankyPutIndentAfterShiftRight)", desc = "Put and Indent Right" }, + { "(YankyPutIndentAfterShiftLeft)", desc = "Put and Indent Left" }, + { ">P", "(YankyPutIndentBeforeShiftRight)", desc = "Put Before and Indent Right" }, + { "(YankyPutIndentBeforeShiftLeft)", desc = "Put Before and Indent Left" }, + { "=p", "(YankyPutAfterFilter)", desc = "Put After Applying a Filter" }, + { "=P", "(YankyPutBeforeFilter)", desc = "Put Before Applying a Filter" }, }, }, - { "JoosepAlviste/nvim-ts-context-commentstring", enabled = false }, - { "echasnovski/mini.comment", enabled = false }, + + { "andrewferrier/debugprint.nvim", + dependencies = { "nvim-treesitter" }, + -- TODO: setup debugprint (or maybe not) + opts = { } + } } diff --git a/nvim/lua/plugins/colorscheme.lua b/nvim/lua/plugins/colorscheme.lua index 8e1ae25..873902e 100644 --- a/nvim/lua/plugins/colorscheme.lua +++ b/nvim/lua/plugins/colorscheme.lua @@ -1,83 +1,77 @@ ----@type LazyPluginSpec +---@type LazySpec return { - { - "LazyVim", - opts = { colorscheme = "onenord" }, - dependencies = { "onenord.nvim" }, - }, - { - -- this meta plugin can be used to install all colorschemes to test with telescope - name = "colorschemes", - dir = "", - lazy = true, - dependencies = { - { - -- requires too much configuration to make look good - "rmehri01/onenord.nvim", - opts = function(_, opts) - local colors = require("onenord.colors").load() - opts = vim.tbl_extend("force", opts, { - custom_highlights = { - ["@parameter"] = { fg = colors.fg }, - ["@namespace"] = { fg = colors.fg }, - ["@type"] = { fg = colors.fg }, - ["@variable.builtin"] = { fg = colors.fg }, - }, - }) - return opts - end, - }, - { "folke/tokyonight.nvim" }, - { - "catppuccin/nvim", - name = "catppuccin", - opts = { - color_overrides = { - -- https://github.com/catppuccin/nvim/discussions/323#discussioncomment-5760383 - mocha = { - rosewater = "#BF616A", - flamingo = "#BF616A", - pink = "#B48EAD", - mauve = "#B48EAD", - red = "#BF616A", - maroon = "#BF616A", - peach = "#D08770", - yellow = "#EBCB8B", - green = "#A3BE8C", - teal = "#88c0d0", - sky = "#88c0d0", - sapphire = "#88c0d0", - blue = "#81a1c1", - lavender = "#81a1c1", - text = "#ECEFF4", - subtext1 = "#E5E9F0", - subtext0 = "#D8DEE9", - overlay2 = "#8d9196", - overlay1 = "#81858b", - overlay0 = "#4C566A", - surface2 = "#434C5E", - surface1 = "#3B4252", - surface0 = "#292e39", - base = "#242933", - mantle = "#20242d", - crust = "#1c2028", - }, - }, + { "rmehri01/onenord.nvim", + lazy = false, + priority = 1000, + opts = function(_, opts) + local colors = require("onenord.colors").load() + opts = vim.tbl_extend("force", opts, { + custom_highlights = { + ["@parameter"] = { fg = colors.fg }, + ["@namespace"] = { fg = colors.fg }, + ["@type"] = { fg = colors.fg }, + ["@variable.builtin"] = { fg = colors.fg }, }, - }, - { - "shaunsingh/nord.nvim", - dev = true, - init = function() - vim.g.nord_contrats = true - vim.g.nord_borders = true - vim.g.nord_bold = false - vim.g.nord_uniform_diff_background = false - vim.g.nord_disable_background = false - end, - }, - -- conflicts with above - -- { "gbprod/nord.nvim"}, - }, + }) + return opts + end, }, + -- FIXME: Find a way to lazy load all colorschemes when invoking the picker + -- https://github.com/folke/lazy.nvim/discussions/1167 + -- { name = "colorschemes", + -- -- this meta plugin can be used to install all colorschemes to test with a picker + -- dir = "", -- NOTE: empty dir no longer works + -- lazy = true, + -- dependencies = { + -- { + -- "catppuccin/nvim", + -- name = "catppuccin", + -- opts = { + -- color_overrides = { + -- -- https://github.com/catppuccin/nvim/discussions/323#discussioncomment-5760383 + -- mocha = { + -- rosewater = "#BF616A", + -- flamingo = "#BF616A", + -- pink = "#B48EAD", + -- mauve = "#B48EAD", + -- red = "#BF616A", + -- maroon = "#BF616A", + -- peach = "#D08770", + -- yellow = "#EBCB8B", + -- green = "#A3BE8C", + -- teal = "#88c0d0", + -- sky = "#88c0d0", + -- sapphire = "#88c0d0", + -- blue = "#81a1c1", + -- lavender = "#81a1c1", + -- text = "#ECEFF4", + -- subtext1 = "#E5E9F0", + -- subtext0 = "#D8DEE9", + -- overlay2 = "#8d9196", + -- overlay1 = "#81858b", + -- overlay0 = "#4C566A", + -- surface2 = "#434C5E", + -- surface1 = "#3B4252", + -- surface0 = "#292e39", + -- base = "#242933", + -- mantle = "#20242d", + -- crust = "#1c2028", + -- }, + -- }, + -- }, + -- }, + -- { + -- "shaunsingh/nord.nvim", + -- init = function() + -- vim.g.nord_contrats = true + -- vim.g.nord_borders = true + -- vim.g.nord_bold = false + -- vim.g.nord_uniform_diff_background = false + -- vim.g.nord_disable_background = false + -- end, + -- }, + -- -- conflicts with above + -- -- { "gbprod/nord.nvim"}, + -- }, + -- }, } diff --git a/nvim/lua/plugins/core.lua b/nvim/lua/plugins/core.lua index b3f49a2..acec598 100644 --- a/nvim/lua/plugins/core.lua +++ b/nvim/lua/plugins/core.lua @@ -1,18 +1,13 @@ ----@type LazyPluginSpec +---@type LazySpec return { - { - "LazyVim/LazyVim", - opts = { - defaults = { - autocmds = false, - options = false, - keymaps = false, - }, - }, - }, - { - "vhyrro/luarocks.nvim", + { "vhyrro/luarocks.nvim", priority = 1000, config = true, + }, + { "dstein64/vim-startuptime", + cmd = "StartupTime", + config = function() + vim.g.startuptime_tries = 10 + end, } } diff --git a/nvim/lua/plugins/dap.lua b/nvim/lua/plugins/dap.lua index 8657ab8..46840f7 100644 --- a/nvim/lua/plugins/dap.lua +++ b/nvim/lua/plugins/dap.lua @@ -1,148 +1,106 @@ ----@param config {args?:string[]|fun():string[]?} +---@param config {type?:string, args?:string[]|fun():string[]?} local function get_args(config) - local args = type(config.args) == "function" and (config.args() or {}) or config.args or {} + local args = type(config.args) == "function" and (config.args() or {}) or config.args or {} --[[@as string[] | string ]] + local args_str = type(args) == "table" and table.concat(args, " ") or args --[[@as string]] + config = vim.deepcopy(config) ---@cast args string[] config.args = function() - local new_args = vim.fn.input("Run with args: ", table.concat(args, " ")) --[[@as string]] - return vim.split(vim.fn.expand(new_args) --[[@as string]], " ") + local new_args = vim.fn.expand(vim.fn.input("Run with args: ", args_str)) --[[@as string]] + return require("dap.utils").splitstr(new_args) end return config end return { - "mfussenegger/nvim-dap", - - dependencies = { - -- fancy UI for the debugger - { + { "mfussenegger/nvim-dap", + dependencies = { "rcarriga/nvim-dap-ui", - -- stylua: ignore - keys = { - { "du", function() require("dapui").toggle({ }) end, desc = "Dap UI" }, - { "de", function() require("dapui").eval() end, desc = "Eval", mode = {"n", "v"} }, + { "theHamsta/nvim-dap-virtual-text", -- virtual text for the debugger + opts = {}, }, - opts = {}, - config = function(_, opts) - local dap = require("dap") - local dapui = require("dapui") - dapui.setup(opts) - dap.listeners.after.event_initialized["dapui_config"] = function() - dapui.open({}) - end - end, }, - -- virtual text for the debugger - { - "theHamsta/nvim-dap-virtual-text", - opts = {}, + -- stylua: ignore + keys = { + { "dB", function() require("dap").set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, desc = "Breakpoint Condition" }, + { "db", function() require("dap").toggle_breakpoint() end, desc = "Toggle Breakpoint" }, + { "dc", function() require("dap").continue() end, desc = "Run/Continue" }, + { "da", function() require("dap").continue({ before = get_args }) end, desc = "Run with Args" }, + { "dC", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" }, + { "", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" }, + { "dg", function() require("dap").goto_() end, desc = "Go to Line (No Execute)" }, + { "di", function() require("dap").step_into() end, desc = "Step Into" }, + { "", function() require("dap").step_into() end, desc = "Step Into" }, + { "dj", function() require("dap").down() end, desc = "Down" }, + { "dk", function() require("dap").up() end, desc = "Up" }, + { "dl", function() require("dap").run_last() end, desc = "Run Last" }, + { "do", function() require("dap").step_out() end, desc = "Step Out" }, + { "", function() require("dap").step_out() end, desc = "Step Over" }, + { "dO", function() require("dap").step_over() end, desc = "Step Over" }, + { "", function() require("dap").step_over() end, desc = "Step Over" }, + { "dP", function() require("dap").pause() end, desc = "Pause" }, + { "dr", function() require("dap").repl.toggle() end, desc = "Toggle REPL" }, + { "ds", function() require("dap").session() end, desc = "Session" }, + { "dt", function() require("dap").terminate() end, desc = "Terminate" }, + { "dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" }, }, - -- which key integration - { - "folke/which-key.nvim", - opts = { - defaults = { - ["d"] = { name = "+debug" }, - }, - }, - }, + config = function() + -- load mason-nvim-dap here, after all adapters have been setup + require("mason-nvim-dap").setup(rmz.lazy.opts("mason-nvim-dap.nvim")) - -- mason.nvim integration - { - "jay-babu/mason-nvim-dap.nvim", - dependencies = "mason.nvim", - cmd = { "DapInstall", "DapUninstall" }, - opts = { - -- Makes a best effort to setup the various debuggers with - -- reasonable debug configurations - automatic_installation = true, + vim.api.nvim_set_hl(0, "DapStoppedLine", { default = true, link = "Visual" }) - -- You can provide additional configuration to the handlers, - -- see mason-nvim-dap README for more information - handlers = {}, - }, - }, - { - "jbyuki/one-small-step-for-vimkind", - -- stylua: ignore - keys = { - { "daL", function() require("osv").launch({ port = 8086 }) end, desc = "Adapter Lua Server" }, - { "dal", function() require("osv").run_this() end, desc = "Adapter Lua" }, - }, - config = function() - local dap = require("dap") - dap.adapters.nlua = function(callback, config) - local adapter = { - type = "server", - host = config.host or "127.0.0.1", - port = config.port or 8086, - } - if config.start_neovim then - local dap_run = dap.run - dap.run = function(c) - adapter.port = c.port - adapter.host = c.host - end - require("osv").run_this() - dap.run = dap_run - end - callback(adapter) - end - dap.configurations.lua = { - { - type = "nlua", - request = "attach", - name = "Run this file", - start_neovim = {}, - }, - { - type = "nlua", - request = "attach", - name = "Attach to running Neovim instance (port 8086)", - port = 8086, - }, - } - end, + for name, sign in pairs(rmz.ui.icons.dap) do + sign = type(sign) == "table" and sign or { sign } ---@cast sign table + vim.fn.sign_define( + "Dap" .. name, + { text = sign[1], texthl = sign[2] or "DiagnosticInfo", linehl = sign[3], numhl = sign[3] } + ) + end + + -- setup dap config by VsCode launch.json file + local vscode = require("dap.ext.vscode") + local json = require("plenary.json") + vscode.json_decode = function(str) + return vim.json.decode(json.json_strip_comments(str)) + end + end, + }, + { "mason.nvim", + opts = { + log_level = vim.log.levels.TRACE, + } + }, + { "rcarriga/nvim-dap-ui", -- fancy UI for the debugger + dependencies = { "nvim-neotest/nvim-nio" }, + -- stylua: ignore + keys = { + { "du", function() require("dapui").toggle({ }) end, desc = "Dap UI" }, + { "de", function() require("dapui").eval() end, desc = "Eval", mode = {"n", "v"} }, }, + opts = {}, + config = function(_, opts) + local dap = require("dap") + local dapui = require("dapui") + dapui.setup(opts) + dap.listeners.after.event_initialized["dapui_config"] = function() + dapui.open({}) + end + end, }, - - -- stylua: ignore - keys = { - { "dB", function() require("dap").set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, desc = "Breakpoint Condition" }, - { "db", function() require("dap").toggle_breakpoint() end, desc = "Toggle Breakpoint" }, - { "dc", function() require("dap").continue() end, desc = "Continue" }, - { "da", function() require("dap").continue({ before = get_args }) end, desc = "Run with Args" }, - { "dC", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" }, - { "", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" }, - { "dg", function() require("dap").goto_() end, desc = "Go to line (no execute)" }, - { "di", function() require("dap").step_into() end, desc = "Step Into" }, - { "", function() require("dap").step_into() end, desc = "Step Into" }, - { "dj", function() require("dap").down() end, desc = "Down" }, - { "dk", function() require("dap").up() end, desc = "Up" }, - { "dl", function() require("dap").run_last() end, desc = "Run Last" }, - { "do", function() require("dap").step_out() end, desc = "Step Out" }, - { "", function() require("dap").step_out() end, desc = "Step Over" }, - { "dO", function() require("dap").step_over() end, desc = "Step Over" }, - { "", function() require("dap").step_over() end, desc = "Step Over" }, - { "dp", function() require("dap").pause() end, desc = "Pause" }, - { "dr", function() require("dap").repl.toggle() end, desc = "Toggle REPL" }, - { "ds", function() require("dap").session() end, desc = "Session" }, - { "dt", function() require("dap").terminate() end, desc = "Terminate" }, - { "dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" }, + -- mason.nvim integration + { "jay-babu/mason-nvim-dap.nvim", + dependencies = "mason.nvim", + cmd = { "DapInstall", "DapUninstall" }, + opts_extend = { "ensure_installed" }, + opts = { + automatic_installation = true, + handlers = {}, + ensure_installed = { }, + }, + -- mason-nvim-dap is loaded when nvim-dap loads + config = function() end, }, - - config = function() - local Config = require("lazyvim.config") - vim.api.nvim_set_hl(0, "DapStoppedLine", { default = true, link = "Visual" }) - - for name, sign in pairs(Config.icons.dap) do - sign = type(sign) == "table" and sign or { sign } - vim.fn.sign_define( - "Dap" .. name, - { text = sign[1], texthl = sign[2] or "DiagnosticInfo", linehl = sign[3], numhl = sign[3] } - ) - end - end, } diff --git a/nvim/lua/plugins/editor.lua b/nvim/lua/plugins/editor.lua index 0407b47..1e94ab0 100644 --- a/nvim/lua/plugins/editor.lua +++ b/nvim/lua/plugins/editor.lua @@ -1,97 +1,6 @@ ----@type LazyPluginSpec +---@type LazySpec return { - { - "nvim-telescope/telescope.nvim", - keys = { - { "fF", LazyVim.pick("files", { cwd = false }), desc = "Find Files (cwd)" }, - -- from lazyvim - { ",", "Telescope buffers show_all_buffers=true", desc = "Switch Buffer" }, - { "/", LazyVim.pick("live_grep"), desc = "Grep (root dir)" }, - { ":", "Telescope command_history", desc = "Command History" }, - { "", LazyVim.pick("files"), desc = "Find Files (root dir)" }, - { "", false }, - -- find - { "fb", "Telescope buffers sort_lastused=true", desc = "Buffers" }, - -- { "ff", LazyVim.pick("files"), desc = "Find Files (root dir)" }, - { "ff", false }, -- fswitch - { "fF", LazyVim.pick("files", { cwd = false }), desc = "Find Files (cwd)" }, - { "fr", "Telescope oldfiles", desc = "Recent" }, - { "fR", LazyVim.pick("oldfiles", { cwd = vim.loop.cwd() }), desc = "Recent (cwd)" }, - -- git - -- { "gc", "Telescope git_commits", desc = "commits" }, - { "gc", false }, - -- { "gs", "Telescope git_status", desc = "status" }, - { "gs", false }, - -- search - { "sa", "Telescope autocommands", desc = "Auto Commands" }, - { "sb", "Telescope current_buffer_fuzzy_find", desc = "Buffer" }, - { "sc", "Telescope command_history", desc = "Command History" }, - { "sC", "Telescope commands", desc = "Commands" }, - { "sd", "Telescope diagnostics bufnr=0", desc = "Document diagnostics" }, - { "sD", "Telescope diagnostics", desc = "Workspace diagnostics" }, - { "sg", LazyVim.pick("live_grep"), desc = "Grep (root dir)" }, - { "sG", LazyVim.pick("live_grep", { cwd = false }), desc = "Grep (cwd)" }, - { "sh", "Telescope help_tags", desc = "Help Pages" }, - { "sH", "Telescope highlights", desc = "Search Highlight Groups" }, - { "sk", "Telescope keymaps", desc = "Key Maps" }, - { "sM", "Telescope man_pages", desc = "Man Pages" }, - { "sm", "Telescope marks", desc = "Jump to Mark" }, - { "so", "Telescope vim_options", desc = "Options" }, - { "sR", "Telescope resume", desc = "Resume" }, - { "sw", LazyVim.pick("grep_string"), desc = "Word (root dir)" }, - { "sW", LazyVim.pick("grep_string", { cwd = false }), desc = "Word (cwd)" }, - { "uC", LazyVim.pick("colorscheme", { enable_preview = true }), desc = "Colorscheme with preview" }, - -- { - -- "ss", - -- LazyVim.pick("lsp_document_symbols", { - -- symbols = { - -- "Class", - -- "Function", - -- "Method", - -- "Constructor", - -- "Interface", - -- "Module", - -- "Struct", - -- "Trait", - -- "Field", - -- "Property", - -- }, - -- }), - -- desc = "Goto Symbol", - -- }, - { "ss", false }, -- use for snippets below - { - "sS", - LazyVim.pick("lsp_dynamic_workspace_symbols", { - symbols = { - "Class", - "Function", - "Method", - "Constructor", - "Interface", - "Module", - "Struct", - "Trait", - "Field", - "Property", - }, - }), - desc = "Goto Symbol (Workspace)", - }, - }, - }, - { - "benfowler/telescope-luasnip.nvim", - dependencies = { - "telescope.nvim", - }, - config = function() - require("telescope").load_extension("luasnip") - end, - keys = { { "ss", "Telescope luasnip", desc = "Snippets" } }, - }, - { - "stevearc/oil.nvim", + { "stevearc/oil.nvim", opts = { keymaps = { ["g?"] = "actions.show_help", @@ -117,9 +26,6 @@ return { { "folke/trouble.nvim", cmd = { "Trouble" }, opts = { - keys = { - [""] = "fold_toggle", - }, modes = { lsp = { win = { position = "right" }, @@ -164,40 +70,140 @@ return { }, }, { "folke/todo-comments.nvim", - cmd = { "TodoTrouble", "TodoTelescope" }, - event = "LazyFile", + -- Finds and lists all of the TODO, HACK, BUG, etc comment + -- in your project and loads them into a browsable list. + cmd = { "TodoTrouble"}, + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, -- stylua: ignore keys = { { "]t", function() require("todo-comments").jump_next() end, desc = "Next Todo Comment" }, { "[t", function() require("todo-comments").jump_prev() end, desc = "Previous Todo Comment" }, { "xt", "Trouble todo toggle", desc = "Todo (Trouble)" }, { "xT", "Trouble todo toggle filter = {tag = {TODO,FIX,FIXME}}", desc = "Todo/Fix/Fixme (Trouble)" }, - { "st", "TodoTelescope", desc = "Todo" }, - { "sT", "TodoTelescope keywords=TODO,FIX,FIXME", desc = "Todo/Fix/Fixme" }, }, + opts = {} }, - { - "neo-tree.nvim", - enabled = false, - lazy = false, - opts = { - filesystem = { - -- TODO: review these - bind_to_cwd = false, - follow_current_file = { - enabled = false, - }, - }, - }, - keys = function() - local NeoTree = function(args) - return function() - require("neo-tree.command").execute(args) - end - end + { "echasnovski/mini.hipatterns", -- highlight hex colours and other matched words + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = function() + local hi = require("mini.hipatterns") return { - { "fe", NeoTree({ toggle = true, dir = vim.loop.cwd() }) }, + highlighters = { + hex_color = hi.gen_highlighter.hex_color({ priority = 2000 }), + }, } end, }, + { "folke/flash.nvim", + -- Flash enhances the built-in search functionality by showing labels + -- at the end of each match, letting you quickly jump to a specific + -- location. + event = "VeryLazy", + vscode = true, + ---@type Flash.Config + opts = {}, + -- stylua: ignore + keys = { + { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" }, + { "S", mode = { "n", "o", "x" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" }, + { "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" }, + { "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" }, + { "", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" }, + }, + }, + { "lewis6991/gitsigns.nvim", + -- git signs highlights text that has changed since the list + -- git commit, and also lets you interactively stage & unstage + -- hunks in a commit. + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = { + signs = { + add = { text = "▎" }, + change = { text = "▎" }, + delete = { text = "" }, + topdelete = { text = "" }, + changedelete = { text = "▎" }, + untracked = { text = "▎" }, + }, + signs_staged = { + add = { text = "▎" }, + change = { text = "▎" }, + delete = { text = "" }, + topdelete = { text = "" }, + changedelete = { text = "▎" }, + }, + on_attach = function(buffer) + local gs = package.loaded.gitsigns + + local function map(mode, l, r, desc) + vim.keymap.set(mode, l, r, { buffer = buffer, desc = desc }) + end + + -- stylua: ignore start + map("n", "]h", function() + if vim.wo.diff then + vim.cmd.normal({ "]c", bang = true }) + else + gs.nav_hunk("next") + end + end, "Next Hunk") + map("n", "[h", function() + if vim.wo.diff then + vim.cmd.normal({ "[c", bang = true }) + else + gs.nav_hunk("prev") + end + end, "Prev Hunk") + map("n", "]H", function() gs.nav_hunk("last") end, "Last Hunk") + map("n", "[H", function() gs.nav_hunk("first") end, "First Hunk") + map({ "n", "v" }, "ghs", ":Gitsigns stage_hunk", "Stage Hunk") + map({ "n", "v" }, "ghr", ":Gitsigns reset_hunk", "Reset Hunk") + map("n", "ghS", gs.stage_buffer, "Stage Buffer") + map("n", "ghu", gs.undo_stage_hunk, "Undo Stage Hunk") + map("n", "ghR", gs.reset_buffer, "Reset Buffer") + map("n", "ghp", gs.preview_hunk_inline, "Preview Hunk Inline") + map("n", "ghb", function() gs.blame_line({ full = true }) end, "Blame Line") + map("n", "ghB", function() gs.blame() end, "Blame Buffer") + map("n", "ghd", gs.diffthis, "Diff This") + map("n", "ghD", function() gs.diffthis("~") end, "Diff This ~") + map({ "o", "x" }, "ih", ":Gitsigns select_hunk", "GitSigns Select Hunk") + Snacks.toggle({ + name = "Git Signs", + get = function() + return require("gitsigns.config").config.signcolumn + end, + set = function(state) + require("gitsigns").toggle_signs(state) + end, + }):map("uG") + end, + }, + }, + + -- TODO: currently unused plugins, check and enable/remove + { "MagicDuck/grug-far.nvim", enabled = false, + -- search/replace in multiple files + opts = { headerMaxWidth = 80 }, + cmd = "GrugFar", + keys = { + { + "sr", + function() + local grug = require("grug-far") + local ext = vim.bo.buftype == "" and vim.fn.expand("%:e") + grug.open({ + transient = true, + prefills = { + filesFilter = ext and ext ~= "" and "*." .. ext or nil, + }, + }) + end, + mode = { "n", "v" }, + desc = "Search and Replace", + }, + }, + }, + { "aymericbeaumet/vim-symlink", + dependencies = { "moll/vim-bbye" }, -- NOTE: Snacks.buddelete also provides this + }, } diff --git a/nvim/lua/plugins/formatter.lua b/nvim/lua/plugins/formatter.lua new file mode 100644 index 0000000..2dd44ab --- /dev/null +++ b/nvim/lua/plugins/formatter.lua @@ -0,0 +1,47 @@ +--- TODO: add toggle for format on save +--- https://github.com/stevearc/conform.nvim/blob/master/doc/recipes.md#command-to-toggle-format-on-save +--- previous keymap was uf + +---@type LazySpec +return { + { "stevearc/conform.nvim", + dependencies = { "mason.nvim" }, + lazy = true, + cmd = "ConformInfo", + keys = { + {"cf", function() require("conform").format({ async = true }) end, mode = "", desc = "Format buffer" }, + { + "cF", + function() + require("conform").format({ formatters = { "injected" }, timeout_ms = 3000 }) + end, + mode = { "n", "v" }, + desc = "Format Injected Langs", + }, + }, + ---@module "conform" + ---@type conform.setupOpts + opts = { + default_format_opts = { + timeout_ms = 3000, + async = false, -- not recommended to change + quiet = false, -- not recommended to change + lsp_format = "fallback", -- not recommended to change + }, + formatters_by_ft = { + lua = { "stylua" }, + fish = { "fish_indent" }, + sh = { "shfmt" }, + }, + -- The options you set here will be merged with the builtin formatters. + -- You can also define any custom formatters here. + ---@type table + formatters = { + injected = { options = { ignore_errors = true } }, + }, + }, + init = function () + vim.opt.formatexpr = "v:lua.require'conform'.formatexpr()" + end, + }, +} diff --git a/nvim/lua/plugins/init.lua b/nvim/lua/plugins/init.lua new file mode 100644 index 0000000..c21defb --- /dev/null +++ b/nvim/lua/plugins/init.lua @@ -0,0 +1,4 @@ +return { + { "folke/lazy.nvim", version = "*" }, + { "folke/snacks.nvim", priority = 1000, lazy = false }, +} diff --git a/nvim/lua/plugins/lang/cmake.lua b/nvim/lua/plugins/lang/cmake.lua new file mode 100644 index 0000000..2e76835 --- /dev/null +++ b/nvim/lua/plugins/lang/cmake.lua @@ -0,0 +1,60 @@ +return { + { + "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "cmake" } }, + }, + { + "nvimtools/none-ls.nvim", + optional = true, + opts = function(_, opts) + local nls = require("null-ls") + opts.sources = vim.list_extend(opts.sources or {}, { + nls.builtins.diagnostics.cmake_lint, + }) + end, + }, + { + "mfussenegger/nvim-lint", + optional = true, + opts = { + linters_by_ft = { + cmake = { "cmakelint" }, + }, + }, + }, + { + "mason.nvim", + opts = { ensure_installed = { "cmakelang", "cmakelint" } }, + }, + { + "neovim/nvim-lspconfig", + opts = { + servers = { + neocmake = {}, + }, + }, + }, + { + "Civitasv/cmake-tools.nvim", + lazy = true, + init = function() + local loaded = false + local function check() + local cwd = vim.uv.cwd() + if vim.fn.filereadable(cwd .. "/CMakeLists.txt") == 1 then + require("lazy").load({ plugins = { "cmake-tools.nvim" } }) + loaded = true + end + end + check() + vim.api.nvim_create_autocmd("DirChanged", { + callback = function() + if not loaded then + check() + end + end, + }) + end, + opts = {}, + }, +} diff --git a/nvim/lua/plugins/lang/cpp.lua b/nvim/lua/plugins/lang/cpp.lua index ff6624e..d3153db 100644 --- a/nvim/lua/plugins/lang/cpp.lua +++ b/nvim/lua/plugins/lang/cpp.lua @@ -2,11 +2,7 @@ return { -- Add C/C++ to treesitter { "nvim-treesitter/nvim-treesitter", - opts = function(_, opts) - if type(opts.ensure_installed) == "table" then - vim.list_extend(opts.ensure_installed, { "c", "cpp" }) - end - end, + opts = { ensure_installed = { "c", "cpp" } }, }, { @@ -48,7 +44,7 @@ return { -- Ensure mason installs the server clangd = { keys = { - { "cR", "ClangdSwitchSourceHeader", desc = "Switch Source/Header (C/C++)" }, + { "ch", "ClangdSwitchSourceHeader", desc = "Switch Source/Header (C/C++)" }, }, root_dir = function(fname) return require("lspconfig.util").root_pattern( "compile_commands.json", "compile_flags.txt")(fname) @@ -84,31 +80,24 @@ return { }, setup = { clangd = function(_, opts) - local clangd_ext_opts = require("lazyvim.util").opts("clangd_extensions.nvim") + local clangd_ext_opts = rmz.lazy.opts("clangd_extensions.nvim") require("clangd_extensions").setup(vim.tbl_deep_extend("force", clangd_ext_opts or {}, { server = opts })) return false end, }, }, }, - - { - "nvim-cmp", - opts = function(_, opts) - table.insert(opts.sorting.comparators, 1, require("clangd_extensions.cmp_scores")) - end, - }, - - { - "mfussenegger/nvim-dap", + -- { "blink.cmp", + -- opts = function(_, opts) + -- -- TODO: make sure this works + -- table.insert(opts.fuzzy.sorts, 1, require("clangd_extensions.cmp_scores")) + -- end, + -- }, + { "mfussenegger/nvim-dap", dependencies = { -- Ensure C/C++ debugger is installed "williamboman/mason.nvim", - opts = function(_, opts) - if type(opts.ensure_installed) == "table" then - vim.list_extend(opts.ensure_installed, { "codelldb" }) - end - end, + opts = { ensure_installed = { "codelldb" } }, }, opts = function() local dap = require("dap") @@ -141,7 +130,7 @@ return { type = "codelldb", request = "attach", name = "Attach to process", - processId = require("dap.utils").pick_process, + pid = require("dap.utils").pick_process, cwd = "${workspaceFolder}", }, } diff --git a/nvim/lua/plugins/lang/docker.lua b/nvim/lua/plugins/lang/docker.lua new file mode 100644 index 0000000..e375fa4 --- /dev/null +++ b/nvim/lua/plugins/lang/docker.lua @@ -0,0 +1,38 @@ +return { + { + "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "dockerfile" } }, + }, + { + "mason.nvim", + opts = { ensure_installed = { "hadolint" } }, + }, + { + "nvimtools/none-ls.nvim", + optional = true, + opts = function(_, opts) + local nls = require("null-ls") + opts.sources = vim.list_extend(opts.sources or {}, { + nls.builtins.diagnostics.hadolint, + }) + end, + }, + { + "mfussenegger/nvim-lint", + optional = true, + opts = { + linters_by_ft = { + dockerfile = { "hadolint" }, + }, + }, + }, + { + "neovim/nvim-lspconfig", + opts = { + servers = { + dockerls = {}, + docker_compose_language_service = {}, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/git.lua b/nvim/lua/plugins/lang/git.lua new file mode 100644 index 0000000..a45d4da --- /dev/null +++ b/nvim/lua/plugins/lang/git.lua @@ -0,0 +1,19 @@ +return { + -- Treesitter git support + { "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "git_config", "gitcommit", "git_rebase", "gitignore", "gitattributes" } }, + }, + + { + "hrsh7th/nvim-cmp", + optional = true, + dependencies = { + { "petertriho/cmp-git", opts = {} }, + }, + ---@module 'cmp' + ---@param opts cmp.ConfigSchema + opts = function(_, opts) + table.insert(opts.sources, { name = "git" }) + end, + }, +} diff --git a/nvim/lua/plugins/lang/go.lua b/nvim/lua/plugins/lang/go.lua new file mode 100644 index 0000000..d63a201 --- /dev/null +++ b/nvim/lua/plugins/lang/go.lua @@ -0,0 +1,147 @@ +return { + { + "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "go", "gomod", "gowork", "gosum" } }, + }, + { + "neovim/nvim-lspconfig", + opts = { + servers = { + gopls = { + settings = { + gopls = { + gofumpt = true, + codelenses = { + gc_details = false, + generate = true, + regenerate_cgo = true, + run_govulncheck = true, + test = true, + tidy = true, + upgrade_dependency = true, + vendor = true, + }, + hints = { + assignVariableTypes = true, + compositeLiteralFields = true, + compositeLiteralTypes = true, + constantValues = true, + functionTypeParameters = true, + parameterNames = true, + rangeVariableTypes = true, + }, + analyses = { + fieldalignment = true, + nilness = true, + unusedparams = true, + unusedwrite = true, + useany = true, + }, + usePlaceholders = true, + completeUnimported = true, + staticcheck = true, + directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" }, + semanticTokens = true, + }, + }, + }, + }, + setup = { + gopls = function(_, opts) + -- workaround for gopls not supporting semanticTokensProvider + -- https://github.com/golang/go/issues/54531#issuecomment-1464982242 + rmz.lsp.on_attach(function(client, _) + if not client.server_capabilities.semanticTokensProvider then + local semantic = client.config.capabilities.textDocument.semanticTokens + client.server_capabilities.semanticTokensProvider = { + full = true, + legend = { + tokenTypes = semantic.tokenTypes, + tokenModifiers = semantic.tokenModifiers, + }, + range = true, + } + end + end, "gopls") + -- end workaround + end, + }, + }, + }, + -- Ensure Go tools are installed + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "goimports", "gofumpt" } }, + }, + { + "nvimtools/none-ls.nvim", + optional = true, + dependencies = { + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "gomodifytags", "impl" } }, + }, + }, + opts = function(_, opts) + local nls = require("null-ls") + opts.sources = vim.list_extend(opts.sources or {}, { + nls.builtins.code_actions.gomodifytags, + nls.builtins.code_actions.impl, + nls.builtins.formatting.goimports, + nls.builtins.formatting.gofumpt, + }) + end, + }, + { + "stevearc/conform.nvim", + optional = true, + opts = { + formatters_by_ft = { + go = { "goimports", "gofumpt" }, + }, + }, + }, + { + "mfussenegger/nvim-dap", + optional = true, + dependencies = { + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "delve" } }, + }, + { + "leoluz/nvim-dap-go", + opts = {}, + }, + }, + }, + { + "nvim-neotest/neotest", + optional = true, + dependencies = { + "fredrikaverpil/neotest-golang", + }, + opts = { + adapters = { + ["neotest-golang"] = { + -- Here we can set options for neotest-golang, e.g. + -- go_test_args = { "-v", "-race", "-count=1", "-timeout=60s" }, + dap_go_enabled = true, -- requires leoluz/nvim-dap-go + }, + }, + }, + }, + + -- Filetype icons + { + "echasnovski/mini.icons", + opts = { + file = { + [".go-version"] = { glyph = "", hl = "MiniIconsBlue" }, + }, + filetype = { + gotmpl = { glyph = "󰟓", hl = "MiniIconsGrey" }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/haskell.lua b/nvim/lua/plugins/lang/haskell.lua new file mode 100644 index 0000000..b1c67ff --- /dev/null +++ b/nvim/lua/plugins/lang/haskell.lua @@ -0,0 +1,89 @@ +return { + + -- Add Haskell to treesitter + { + "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "haskell" } }, + }, + + { + "mrcjkb/haskell-tools.nvim", + version = "^3", + ft = { "haskell", "lhaskell", "cabal", "cabalproject" }, + dependencies = { + { "nvim-telescope/telescope.nvim", optional = true }, + }, + config = function() + local ok, telescope = pcall(require, "telescope") + if ok then + telescope.load_extension("ht") + end + end, + }, + + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "haskell-language-server" } }, + }, + + { + "mfussenegger/nvim-dap", + optional = true, + dependencies = { + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "haskell-debug-adapter" } }, + }, + }, + }, + + { + "nvim-neotest/neotest", + optional = true, + dependencies = { + { "mrcjkb/neotest-haskell" }, + }, + opts = { + adapters = { + ["neotest-haskell"] = {}, + }, + }, + }, + + { + "mrcjkb/haskell-snippets.nvim", + dependencies = { "l3mon4d3/luasnip" }, + ft = { "haskell", "lhaskell", "cabal", "cabalproject" }, + config = function() + local haskell_snippets = require("haskell-snippets").all + require("luasnip").add_snippets("haskell", haskell_snippets, { key = "haskell" }) + end, + }, + + { + "luc-tielen/telescope_hoogle", + ft = { "haskell", "lhaskell", "cabal", "cabalproject" }, + dependencies = { + { "nvim-telescope/telescope.nvim" }, + }, + config = function() + local ok, telescope = pcall(require, "telescope") + if ok then + telescope.load_extension("hoogle") + end + end, + }, + + -- Make sure lspconfig doesn't start hls, + -- as it conflicts with haskell-tools + { + "neovim/nvim-lspconfig", + opts = { + setup = { + hls = function() + return true + end, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/json.lua b/nvim/lua/plugins/lang/json.lua new file mode 100644 index 0000000..88acd8c --- /dev/null +++ b/nvim/lua/plugins/lang/json.lua @@ -0,0 +1,39 @@ +return { + -- add json to treesitter + { + "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "json5" } }, + }, + + -- yaml schema support + { + "b0o/SchemaStore.nvim", + lazy = true, + version = false, -- last release is way too old + }, + + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = { + -- make sure mason installs the server + servers = { + jsonls = { + -- lazy-load schemastore when needed + on_new_config = function(new_config) + new_config.settings.json.schemas = new_config.settings.json.schemas or {} + vim.list_extend(new_config.settings.json.schemas, require("schemastore").json.schemas()) + end, + settings = { + json = { + format = { + enable = true, + }, + validate = { enable = true }, + }, + }, + }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/markdown.lua b/nvim/lua/plugins/lang/markdown.lua new file mode 100644 index 0000000..a4862e3 --- /dev/null +++ b/nvim/lua/plugins/lang/markdown.lua @@ -0,0 +1,119 @@ +return { + { + "stevearc/conform.nvim", + optional = true, + opts = { + formatters = { + ["markdown-toc"] = { + condition = function(_, ctx) + for _, line in ipairs(vim.api.nvim_buf_get_lines(ctx.buf, 0, -1, false)) do + if line:find("") then + return true + end + end + end, + }, + ["markdownlint-cli2"] = { + condition = function(_, ctx) + local diag = vim.tbl_filter(function(d) + return d.source == "markdownlint" + end, vim.diagnostic.get(ctx.buf)) + return #diag > 0 + end, + }, + }, + formatters_by_ft = { + ["markdown"] = { "prettier", "markdownlint-cli2", "markdown-toc" }, + ["markdown.mdx"] = { "prettier", "markdownlint-cli2", "markdown-toc" }, + }, + }, + }, + { + "williamboman/mason.nvim", + opts = { ensure_installed = { "markdownlint-cli2", "markdown-toc" } }, + }, + { + "nvimtools/none-ls.nvim", + optional = true, + opts = function(_, opts) + local nls = require("null-ls") + opts.sources = vim.list_extend(opts.sources or {}, { + nls.builtins.diagnostics.markdownlint_cli2, + }) + end, + }, + { + "mfussenegger/nvim-lint", + optional = true, + opts = { + linters_by_ft = { + markdown = { "markdownlint-cli2" }, + }, + }, + }, + { + "neovim/nvim-lspconfig", + opts = { + servers = { + marksman = {}, + }, + }, + }, + + -- Markdown preview + { + "iamcco/markdown-preview.nvim", + cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" }, + build = function() + require("lazy").load({ plugins = { "markdown-preview.nvim" } }) + vim.fn["mkdp#util#install"]() + end, + keys = { + { + "cp", + ft = "markdown", + "MarkdownPreviewToggle", + desc = "Markdown Preview", + }, + }, + config = function() + vim.cmd([[do FileType]]) + end, + }, + + { + "MeanderingProgrammer/render-markdown.nvim", + opts = { + code = { + sign = false, + width = "block", + right_pad = 1, + }, + heading = { + sign = false, + icons = {}, + }, + checkbox = { + enabled = false, + }, + }, + ft = { "markdown", "norg", "rmd", "org", "codecompanion" }, + config = function(_, opts) + require("render-markdown").setup(opts) + Snacks.toggle({ + name = "Render Markdown", + get = function() + return require("render-markdown.state").enabled + end, + set = function(enabled) + local m = require("render-markdown") + if enabled then + m.enable() + else + m.disable() + end + end, + }):map("um") + end, + }, +} diff --git a/nvim/lua/plugins/lang/nix.lua b/nvim/lua/plugins/lang/nix.lua new file mode 100644 index 0000000..e524602 --- /dev/null +++ b/nvim/lua/plugins/lang/nix.lua @@ -0,0 +1,19 @@ +return { + { "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "nix" } }, + }, + { "neovim/nvim-lspconfig", + opts = { + servers = { + -- nil_ls = {}, -- FIXME: this causes big CPU usage when tried to be installed and eventually just fails + }, + }, + }, + { "stevearc/conform.nvim", + opts = { + formatters_by_ft = { + nix = { "nixfmt" }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/nlua.lua b/nvim/lua/plugins/lang/nlua.lua new file mode 100644 index 0000000..f604b6f --- /dev/null +++ b/nvim/lua/plugins/lang/nlua.lua @@ -0,0 +1,42 @@ +return { + "mfussenegger/nvim-dap", + dependencies = { + { + "jbyuki/one-small-step-for-vimkind", + config = function() + local dap = require("dap") + dap.adapters.nlua = function(callback, conf) + local adapter = { + type = "server", + host = conf.host or "127.0.0.1", + port = conf.port or 8086, + } + if conf.start_neovim then + local dap_run = dap.run + dap.run = function(c) + adapter.port = c.port + adapter.host = c.host + end + require("osv").run_this() + dap.run = dap_run + end + callback(adapter) + end + dap.configurations.lua = { + { + type = "nlua", + request = "attach", + name = "Run this file", + start_neovim = {}, + }, + { + type = "nlua", + request = "attach", + name = "Attach to running Neovim instance (port = 8086)", + port = 8086, + }, + } + end, + }, + }, +} diff --git a/nvim/lua/plugins/lang/python.lua b/nvim/lua/plugins/lang/python.lua index 899bfb3..09ee671 100644 --- a/nvim/lua/plugins/lang/python.lua +++ b/nvim/lua/plugins/lang/python.lua @@ -1,65 +1,46 @@ return { - { - "nvim-treesitter/nvim-treesitter", - opts = function(_, opts) - if type(opts.ensure_installed) == "table" then - vim.list_extend(opts.ensure_installed, { "ninja", "python", "rst", "toml" }) - end - end, + { "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "ninja", "rst" } }, }, - { - "neovim/nvim-lspconfig", + { "neovim/nvim-lspconfig", opts = { - ---@type lspconfig.options servers = { - pyright = {}, ruff = { - root_dir = function(fname) - return require("lspconfig.util").root_pattern("pyproject.toml", "setup.cfg", "ruff.toml")(fname) - end, + cmd_env = { RUFF_TRACE = "messages" }, + init_options = { + settings = { + logLevel = "error", + }, + }, keys = { { "co", - function() - vim.lsp.buf.code_action({ - apply = true, - context = { - only = { "source.organizeImports" }, - diagnostics = {}, - }, - }) - end, + rmz.lsp.action["source.organizeImports"], desc = "Organize Imports", }, }, }, - pylsp = { - settings = { - pylsp = { - plugins = { - autopep8 = { enabled = false }, - flake8 = { enabled = false }, - mccabe = { enabled = false }, - pycodestyle = { enabled = false }, - pydocstyle = { enabled = false }, - pyflakes = { enabled = false }, -- covered by flake8 - } - } - } - } }, setup = { - ruff_lsp = function() - require("lazyvim.util").lsp.on_attach(function(client, _) - if client.name == "ruff_lsp" then - -- Disable hover in favor of Pyright - client.server_capabilities.hoverProvider = false - end - end) + ["ruff"] = function() + rmz.lsp.on_attach(function(client, _) + -- Disable hover in favor of Pyright + client.server_capabilities.hoverProvider = false + end, "ruff") end, }, }, }, + { + "neovim/nvim-lspconfig", + opts = function(_, opts) + local servers = { "pyright", "ruff"} + for _, server in ipairs(servers) do + opts.servers[server] = opts.servers[server] or {} + opts.servers[server].enabled = server == lsp or server == ruff + end + end, + }, { "nvim-neotest/neotest", dependencies = { @@ -76,7 +57,6 @@ return { "mfussenegger/nvim-dap", dependencies = { "mfussenegger/nvim-dap-python", - -- stylua: ignore keys = { { "dPt", function() require('dap-python').test_method() end, desc = "Debug Method", ft = "python" }, { "dPc", function() require('dap-python').test_class() end, desc = "Debug Class", ft = "python" }, @@ -87,22 +67,34 @@ return { end, }, }, - { - "linux-cultist/venv-selector.nvim", + { "jay-babu/mason-nvim-dap.nvim", + opts = { + handlers = { + python = function() end, + }, + ensure_installed = { "python" }, + }, + }, + { "linux-cultist/venv-selector.nvim", enabled = false, + -- TODO needs fd to be installed (I don't know what that is) + branch = "regexp", -- Use this branch for the new version cmd = "VenvSelect", - opts = function(_, opts) - if require("lazyvim.util").has("nvim-dap-python") then - opts.dap_enabled = true - end - return vim.tbl_deep_extend("force", opts, { - name = { - "venv", - ".venv", - "env", - ".env", + opts = { + settings = { + options = { + notify_user_on_venv_activation = true, }, - }) + }, + }, + -- Call config for python files and load the cached venv automatically + ft = "python", + keys = { { "cv", ":VenvSelect", desc = "Select VirtualEnv", ft = "python" } }, + }, + { "hrsh7th/nvim-cmp", + optional = true, + opts = function(_, opts) + opts.auto_brackets = opts.auto_brackets or {} + table.insert(opts.auto_brackets, "python") end, - keys = { { "cv", ":VenvSelect", desc = "Select VirtualEnv" } }, }, } diff --git a/nvim/lua/plugins/lang/ruby.lua b/nvim/lua/plugins/lang/ruby.lua new file mode 100644 index 0000000..d434dcf --- /dev/null +++ b/nvim/lua/plugins/lang/ruby.lua @@ -0,0 +1,54 @@ +return { + { "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "ruby" } }, + }, + { "neovim/nvim-lspconfig", + ---@class PluginLspOpts + opts = { + ---@type lspconfig.options + servers = { + ruby_lsp = { enabled = true, }, + rubocop = { enabled = true, }, + }, + }, + }, + { "williamboman/mason.nvim", + opts = { ensure_installed = { "erb-formatter", "erb-lint" } }, + }, + { + "mfussenegger/nvim-dap", + dependencies = { + "suketa/nvim-dap-ruby", + config = function() + require("dap-ruby").setup() + end, + }, + }, + { "stevearc/conform.nvim", + opts = { + formatters_by_ft = { + ruby = { "rubocop" }, + eruby = { "erb_format" }, + }, + }, + }, + { "nvim-neotest/neotest", + dependencies = { + "olimorris/neotest-rspec", + }, + opts = { + adapters = { + ["neotest-rspec"] = { + -- NOTE: By default neotest-rspec uses the system wide rspec gem instead of the one through bundler + -- rspec_cmd = function() + -- return vim.tbl_flatten({ + -- "bundle", + -- "exec", + -- "rspec", + -- }) + -- end, + }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/rust.lua b/nvim/lua/plugins/lang/rust.lua new file mode 100644 index 0000000..97f9718 --- /dev/null +++ b/nvim/lua/plugins/lang/rust.lua @@ -0,0 +1,117 @@ +return { + { "Saecki/crates.nvim", + event = { "BufRead Cargo.toml" }, + opts = { + completion = { + crates = { + enabled = true, + }, + }, + lsp = { + enabled = true, + actions = true, + completion = true, + hover = true, + }, + }, + }, + { "nvim-treesitter/nvim-treesitter", + opts = { ensure_installed = { "rust", "ron" } }, + }, + { "williamboman/mason.nvim", + optional = true, + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { "codelldb" }) + end, + }, + { "mrcjkb/rustaceanvim", + version = vim.fn.has("nvim-0.10.0") == 0 and "^4" or false, + ft = { "rust" }, + opts = { + server = { + on_attach = function(_, bufnr) + vim.keymap.set("n", "cR", function() + vim.cmd.RustLsp("codeAction") + end, { desc = "Code Action", buffer = bufnr }) + vim.keymap.set("n", "dr", function() + vim.cmd.RustLsp("debuggables") + end, { desc = "Rust Debuggables", buffer = bufnr }) + end, + default_settings = { + -- rust-analyzer language server configuration + ["rust-analyzer"] = { + cargo = { + allFeatures = true, + loadOutDirsFromCheck = true, + buildScripts = { + enable = true, + }, + }, + -- Add clippy lints for Rust if using rust-analyzer + checkOnSave = true, + -- Enable diagnostics if using rust-analyzer + diagnostics = { + enable = true, + }, + procMacro = { + enable = true, + ignored = { + ["async-trait"] = { "async_trait" }, + ["napi-derive"] = { "napi" }, + ["async-recursion"] = { "async_recursion" }, + }, + }, + files = { + excludeDirs = { + ".direnv", + ".git", + ".github", + ".gitlab", + "bin", + "node_modules", + "target", + "venv", + ".venv", + }, + }, + }, + }, + }, + }, + config = function(_, opts) + local package_path = require("mason-registry").get_package("codelldb"):get_install_path() + local codelldb = package_path .. "/extension/adapter/codelldb" + local library_path = package_path .. "/extension/lldb/lib/liblldb.dylib" + local uname = io.popen("uname"):read("*l") + if uname == "Linux" then + library_path = package_path .. "/extension/lldb/lib/liblldb.so" + end + opts.dap = { + adapter = require("rustaceanvim.config").get_codelldb_adapter(codelldb, library_path), + } + vim.g.rustaceanvim = vim.tbl_deep_extend("keep", vim.g.rustaceanvim or {}, opts or {}) + if vim.fn.executable("rust-analyzer") == 0 then + rmz.lazy.error( + "**rust-analyzer** not found in PATH, please install it.\nhttps://rust-analyzer.github.io/", + { title = "rustaceanvim" } + ) + end + end, + }, + { "neovim/nvim-lspconfig", + opts = { + servers = { + rust_analyzer = { enabled = false }, -- rustaceanvim is used instead + }, + }, + }, + { "nvim-neotest/neotest", + optional = true, + opts = { + adapters = { + ["rustaceanvim.neotest"] = {}, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/tex.lua b/nvim/lua/plugins/lang/tex.lua new file mode 100644 index 0000000..0d0fdb4 --- /dev/null +++ b/nvim/lua/plugins/lang/tex.lua @@ -0,0 +1,44 @@ +return { + -- Add BibTeX/LaTeX to treesitter + { + "nvim-treesitter/nvim-treesitter", + opts = function(_, opts) + opts.highlight = opts.highlight or {} + if type(opts.ensure_installed) == "table" then + vim.list_extend(opts.ensure_installed, { "bibtex" }) + end + if type(opts.highlight.disable) == "table" then + vim.list_extend(opts.highlight.disable, { "latex" }) + else + opts.highlight.disable = { "latex" } + end + end, + }, + + { + "lervag/vimtex", + lazy = false, -- lazy-loading will disable inverse search + config = function() + vim.g.vimtex_mappings_disable = { ["n"] = { "K" } } -- disable `K` as it conflicts with LSP hover + vim.g.vimtex_quickfix_method = vim.fn.executable("pplatex") == 1 and "pplatex" or "latexlog" + end, + keys = { + { "l", "", desc = "+vimtex", ft = "tex" }, + }, + }, + + -- Correctly setup lspconfig for LaTeX 🚀 + { + "neovim/nvim-lspconfig", + optional = true, + opts = { + servers = { + texlab = { + keys = { + { "K", "(vimtex-doc-package)", desc = "Vimtex Docs", silent = true }, + }, + }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lang/toml.lua b/nvim/lua/plugins/lang/toml.lua new file mode 100644 index 0000000..9020962 --- /dev/null +++ b/nvim/lua/plugins/lang/toml.lua @@ -0,0 +1,8 @@ +return { + "neovim/nvim-lspconfig", + opts = { + servers = { + taplo = {}, + }, + }, +} diff --git a/nvim/lua/plugins/lang/yaml.lua b/nvim/lua/plugins/lang/yaml.lua new file mode 100644 index 0000000..977590f --- /dev/null +++ b/nvim/lua/plugins/lang/yaml.lua @@ -0,0 +1,64 @@ +return { + -- yaml schema support + { + "b0o/SchemaStore.nvim", + lazy = true, + version = false, -- last release is way too old + }, + + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = { + -- make sure mason installs the server + servers = { + yamlls = { + -- Have to add this for yamlls to understand that we support line folding + capabilities = { + textDocument = { + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + }, + }, + }, + -- lazy-load schemastore when needed + on_new_config = function(new_config) + new_config.settings.yaml.schemas = vim.tbl_deep_extend( + "force", + new_config.settings.yaml.schemas or {}, + require("schemastore").yaml.schemas() + ) + end, + settings = { + redhat = { telemetry = { enabled = false } }, + yaml = { + keyOrdering = false, + format = { + enable = true, + }, + validate = true, + schemaStore = { + -- Must disable built-in schemaStore support to use + -- schemas from SchemaStore.nvim plugin + enable = false, + -- Avoid TypeError: Cannot read properties of undefined (reading 'length') + url = "", + }, + }, + }, + }, + }, + setup = { + yamlls = function() + -- Neovim < 0.10 does not have dynamic registration for formatting + if vim.fn.has("nvim-0.10") == 0 then + rmz.lsp.on_attach(function(client, _) + client.server_capabilities.documentFormattingProvider = true + end, "yamlls") + end + end, + }, + }, + }, +} diff --git a/nvim/lua/plugins/lint.lua b/nvim/lua/plugins/lint.lua new file mode 100644 index 0000000..0184577 --- /dev/null +++ b/nvim/lua/plugins/lint.lua @@ -0,0 +1,35 @@ +local M = {} + +return { + { "mfussenegger/nvim-lint", + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = { + -- Event to trigger linters + events = { "BufWritePost", "BufReadPost", "InsertLeave" }, + linters_by_ft = { }, + -- NOTE: LazyVim had an extension to support global and fallback linters, + -- and conditionally enable a linter + }, + config = function(_, opts) + function M.debounce(ms, fn) + local timer = vim.uv.new_timer() + return function(...) + local argv = { ... } + timer:start(ms, 0, function() + timer:stop() + vim.schedule_wrap(fn)(unpack(argv)) + end) + end + end + + local lint = require('lint') + function M.lint() + lint.try_lint() + end + vim.api.nvim_create_autocmd(opts.events, { + group = vim.api.nvim_create_augroup("nvim-lint", { clear = true }), + callback = M.debounce(100, M.lint), + }) + end, + }, +} diff --git a/nvim/lua/plugins/lsp.lua b/nvim/lua/plugins/lsp.lua deleted file mode 100644 index d020443..0000000 --- a/nvim/lua/plugins/lsp.lua +++ /dev/null @@ -1,123 +0,0 @@ -return { - -- lspconfig - { - "neovim/nvim-lspconfig", - ---@class PluginLspOpts - opts = function() - local keys = require("lazyvim.plugins.lsp.keymaps").get() - -- TODO: setup a new mapping for this - -- { "gr", "Telescope lsp_references", desc = "References" }, - keys[#keys+1] = { "gr", false } - - return { - diagnostics = { - underline = true, - update_in_insert = false, - virtual_text = { - spacing = 4, - source = "if_mani", - prefix = "●", - }, - severity_sort = true, - signs = { - text = { - [vim.diagnostic.severity.ERROR] = " ", - [vim.diagnostic.severity.WARN] = " ", - [vim.diagnostic.severity.HINT] = " ", - [vim.diagnostic.severity.INFO] = " ", - }, - }, - }, - inlay_hints = { enabled = false, }, - codelens = { enabled = false, }, - document_highlight = { enabled = true, }, - capabilities = { - workspace = { - fileOperations = { - didRename = true, - willRename = true, - }, - }, - }, - format = { - formatting_options = nil, - timeout_ms = nil, - }, - -- Automatically format on save - -- autoformat = false, - -- LSP Server Settings - ---@type lspconfig.options - servers = { - lua_ls = { - settings = { - Lua = { - workspace = { checkThirdParty = false, }, - codeLens = { enable = true, }, - completion = { callSnippet = "Replace", }, - doc = { privateName = { "^_" }, }, - hint = { - enable = true, - setType = false, - paramType = true, - paramName = "Disable", - semicolon = "Disable", - arrayIndex = "Disable", - }, - diagnostics = { - disable = { "missing-fields", }, - }, - }, - }, - }, - -- Add clangd extensions - -- https://github.com/p00f/clangd_extensions.nvim - }, - -- you can do any additional lsp server setup here - -- return true if you don't want this server to be setup with lspconfig - ---@type table - setup = { - -- example to setup with typescript.nvim - -- tsserver = function(_, opts) - -- require("typescript").setup({ server = opts }) - -- return true - -- end, - -- Specify * to use this function as a fallback for any server - -- ["*"] = function(server, opts) end, - }, - } - end, - }, - - -- cmdline tools and lsp servers - { - - "williamboman/mason.nvim", - cmd = "Mason", - keys = { { "cm", "Mason", desc = "Mason" } }, - opts = { - ensure_installed = { - "stylua", - "shfmt", - -- "flake8", - }, - }, - ---@param opts MasonSettings | {ensure_installed: string[]} - config = function(_, opts) - require("mason").setup(opts) - local mr = require("mason-registry") - local function ensure_installed() - for _, tool in ipairs(opts.ensure_installed) do - local p = mr.get_package(tool) - if not p:is_installed() then - p:install() - end - end - end - if mr.refresh then - mr.refresh(ensure_installed) - else - ensure_installed() - end - end, - }, -} diff --git a/nvim/lua/plugins/lsp/init.lua b/nvim/lua/plugins/lsp/init.lua new file mode 100644 index 0000000..132c412 --- /dev/null +++ b/nvim/lua/plugins/lsp/init.lua @@ -0,0 +1,172 @@ +return { + { "neovim/nvim-lspconfig", + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + dependencies = { + "mason.nvim", + { "williamboman/mason-lspconfig.nvim", config = function() end }, -- don't configure yet + }, + ---@class PluginLspOpts + opts = { + ---@type vim.diagnostic.Opts + diagnostics = { + underline = true, + update_in_insert = false, + virtual_text = { + spacing = 4, + source = "if_many", + prefix = "●", + }, + severity_sort = true, + signs = { + text = { + [vim.diagnostic.severity.ERROR] = " ", + [vim.diagnostic.severity.WARN] = " ", + [vim.diagnostic.severity.HINT] = " ", + [vim.diagnostic.severity.INFO] = " ", + }, + }, + }, + inlay_hints = { enabled = false, }, + codelens = { enabled = false, }, + document_highlight = { enabled = true, }, + capabilities = { + workspace = { + fileOperations = { + didRename = true, + willRename = true, + }, + }, + }, + format = { + formatting_options = nil, + timeout_ms = nil, + }, + -- LSP Server Settings + ---@type lspconfig.options + servers = { + lua_ls = { + settings = { + Lua = { + workspace = { checkThirdParty = false, }, + codeLens = { enable = true, }, + completion = { callSnippet = "Replace", }, + doc = { privateName = { "^_" }, }, + hint = { + enable = true, + setType = false, + paramType = true, + paramName = "Disable", + semicolon = "Disable", + arrayIndex = "Disable", + }, + diagnostics = { + disable = { "missing-fields", }, + }, + }, + }, + }, + -- TODO: Add clangd extensions + -- https://github.com/p00f/clangd_extensions.nvim + }, + -- you can do any additional lsp server setup here + -- return true if you don't want this server to be setup with lspconfig + ---@type table + setup = { + -- example to setup with typescript.nvim + -- tsserver = function(_, opts) + -- require("typescript").setup({ server = opts }) + -- return true + -- end, + -- Specify * to use this function as a fallback for any server + -- ["*"] = function(server, opts) end, + }, + }, + ---@param opts PluginLspOpts + config = function(_, opts) + local lspconfig = require('lspconfig') + for server, config in pairs(opts.servers) do + local capabilities = vim.tbl_deep_extend("force", + vim.lsp.protocol.make_client_capabilities() or {}, + require('blink.cmp').get_lsp_capabilities() or {}, + config.capabilities or {} + ) + config.capabilities = capabilities + lspconfig[server].setup(config) + end + + -- setup keymaps + rmz.lsp.on_attach(function(client, buffer) + require("plugins.lsp.keymaps").on_attach(client, buffer) + end) + + rmz.lsp.setup() + rmz.lsp.on_dynamic_capability(require("plugins.lsp.keymaps").on_attach) + -- TODO: should vim.diagnostics be condigured directly? + vim.diagnostic.config(vim.deepcopy(opts.diagnostics)) + + -- get all the servers that are available through mason-lspconfig + local mlsp = require("mason-lspconfig") + -- TODO: use mason-lspconfig.get_available_servers()? + local all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package) + + local ensure_installed = {} ---@type string[] + for server, server_opts in pairs(opts.servers) do + if server_opts then + server_opts = server_opts == true and {} or server_opts + if server_opts.enabled ~= false then + -- run manual setup if mason=false or if this is a server that cannot be installed with mason-lspconfig + if server_opts.mason == false or not vim.tbl_contains(all_mslp_servers, server) then + require("lspconfig")[server].setup(server_opts) + else + ensure_installed[#ensure_installed + 1] = server + end + end + end + end + + mlsp.setup({ + ensure_installed = vim.tbl_deep_extend( + "force", + ensure_installed, + rmz.lazy.opts("mason-lspconfig.nvim").ensure_installed or {} + ), + handlers = { setup }, + }) + end + }, + { "williamboman/mason.nvim", + cmd = "Mason", + keys = { { "cm", "Mason", desc = "Mason" } }, + build = ":MasonUpdate", + opts_extend = { "ensure_installed" }, + opts = { + ensure_installed = { + "stylua", + "shfmt", + }, + }, + ---@param opts MasonSettings | {ensure_installed: string[]} + config = function(_, opts) + require("mason").setup(opts) + local mr = require("mason-registry") + mr:on("package:install:success", function() + vim.defer_fn(function() + -- trigger FileType event to possibly load this newly installed LSP server + require("lazy.core.handler.event").trigger({ + event = "FileType", + buf = vim.api.nvim_get_current_buf(), + }) + end, 100) + end) + + mr.refresh(function() + for _, tool in ipairs(opts.ensure_installed) do + local p = mr.get_package(tool) + if not p:is_installed() then + p:install() + end + end + end) + end, + }, +} diff --git a/nvim/lua/plugins/lsp/keymaps.lua b/nvim/lua/plugins/lsp/keymaps.lua new file mode 100644 index 0000000..42e210b --- /dev/null +++ b/nvim/lua/plugins/lsp/keymaps.lua @@ -0,0 +1,99 @@ +local M = {} + +---@type LazyKeysLspSpec[]|nil +M._keys = nil + +---@alias LazyKeysLspSpec LazyKeysSpec|{has?:string|string[], cond?:fun():boolean} +---@alias LazyKeysLsp LazyKeys|{has?:string|string[], cond?:fun():boolean} + +---@return LazyKeysLspSpec[] +function M.get() + if M._keys then + return M._keys + end + -- stylua: ignore + M._keys = { + { "cl", "LspInfo", desc = "Lsp Info" }, + { "gd", vim.lsp.buf.definition, desc = "Goto Definition", has = "definition" }, + -- { "gr", vim.lsp.buf.references, desc = "References", nowait = true }, + { "gI", vim.lsp.buf.implementation, desc = "Goto Implementation" }, + { "gy", vim.lsp.buf.type_definition, desc = "Goto T[y]pe Definition" }, + { "gD", vim.lsp.buf.declaration, desc = "Goto Declaration" }, + { "K", function() return vim.lsp.buf.hover() end, desc = "Hover" }, + { "gK", function() return vim.lsp.buf.signature_help() end, desc = "Signature Help", has = "signatureHelp" }, + { "", function() return vim.lsp.buf.signature_help() end, mode = "i", desc = "Signature Help", has = "signatureHelp" }, + { "ca", vim.lsp.buf.code_action, desc = "Code Action", mode = { "n", "v" }, has = "codeAction" }, + { "cc", vim.lsp.codelens.run, desc = "Run Codelens", mode = { "n", "v" }, has = "codeLens" }, + { "cC", vim.lsp.codelens.refresh, desc = "Refresh & Display Codelens", mode = { "n" }, has = "codeLens" }, + { "cR", function() Snacks.rename.rename_file() end, desc = "Rename File", mode ={"n"}, has = { "workspace/didRenameFiles", "workspace/willRenameFiles" } }, + { "cr", vim.lsp.buf.rename, desc = "Rename", has = "rename" }, + { "cA", rmz.lsp.action.source, desc = "Source Action", has = "codeAction" }, + { "]]", function() Snacks.words.jump(vim.v.count1) end, has = "documentHighlight", + desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end }, + { "[[", function() Snacks.words.jump(-vim.v.count1) end, has = "documentHighlight", + desc = "Prev Reference", cond = function() return Snacks.words.is_enabled() end }, + { "", function() Snacks.words.jump(vim.v.count1, true) end, has = "documentHighlight", + desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end }, + { "", function() Snacks.words.jump(-vim.v.count1, true) end, has = "documentHighlight", + desc = "Prev Reference", cond = function() return Snacks.words.is_enabled() end }, + } + + return M._keys +end + +---@param method string|string[] +function M.has(buffer, method) + if type(method) == "table" then + for _, m in ipairs(method) do + if M.has(buffer, m) then + return true + end + end + return false + end + method = method:find("/") and method or "textDocument/" .. method + local clients = rmz.lsp.get_clients({ bufnr = buffer }) + for _, client in ipairs(clients) do + if client.supports_method(method) then + return true + end + end + return false +end + +---@return LazyKeysLsp[] +function M.resolve(buffer) + local Keys = require("lazy.core.handler.keys") + if not Keys.resolve then + return {} + end + local spec = vim.tbl_extend("force", {}, M.get()) + local opts = rmz.lazy.opts("nvim-lspconfig") + local clients = rmz.lsp.get_clients({ bufnr = buffer }) + for _, client in ipairs(clients) do + local maps = opts.servers[client.name] and opts.servers[client.name].keys or {} + vim.list_extend(spec, maps) + end + return Keys.resolve(spec) +end + +function M.on_attach(_, buffer) + local Keys = require("lazy.core.handler.keys") + local keymaps = M.resolve(buffer) + + for _, keys in pairs(keymaps) do + local has = not keys.has or M.has(buffer, keys.has) + local cond = not (keys.cond == false or ((type(keys.cond) == "function") and not keys.cond())) + + if has and cond then + local opts = Keys.opts(keys) + opts.cond = nil + opts.has = nil + opts.silent = opts.silent ~= false + opts.buffer = buffer + vim.keymap.set(keys.mode or "n", keys.lhs, keys.rhs, opts) + end + end +end + +return M diff --git a/nvim/lua/plugins/nvim-treesitter.lua b/nvim/lua/plugins/nvim-treesitter.lua deleted file mode 100644 index 8b87f09..0000000 --- a/nvim/lua/plugins/nvim-treesitter.lua +++ /dev/null @@ -1,23 +0,0 @@ -return { - -- add more treesitter parsers - { - "nvim-treesitter/nvim-treesitter", - opts = { - -- overwrites the settings from LazyVim - ensure_installed = { - "bash", - "c", - "html", - "json", - "lua", - "markdown", - "markdown_inline", - "python", - "regex", - "vim", - "vimdoc", - "yaml", - }, - }, - }, -} diff --git a/nvim/lua/plugins/picker.lua b/nvim/lua/plugins/picker.lua new file mode 100644 index 0000000..3878e03 --- /dev/null +++ b/nvim/lua/plugins/picker.lua @@ -0,0 +1,128 @@ +---@type LazySpec +return { + desc = "Fast and modern file picker", + { "folke/snacks.nvim", + opts = { + picker = { + win = { + input = { + keys = { + [""] = { "trouble_open", mode = { "n", "i" }, }, + }, + }, + }, + actions = { + actions = require("trouble.sources.snacks").actions, + }, + }, + }, + -- stylua: ignore + keys = { + { ",", function() Snacks.picker.buffers() end, desc = "Buffers" }, + { "/", function() Snacks.picker.grep() end, desc = "Grep" }, + { ":", function() Snacks.picker.command_history() end, desc = "Command History" }, + { "n", function() Snacks.picker.notifications() end, desc = "Notification History" }, + -- find + { "fb", function() Snacks.picker.buffers() end, desc = "Buffers" }, + { "fB", function() Snacks.picker.buffers({ hidden = true, nofile = true }) end, desc = "Buffers (all)" }, + { "fc", function() Snacks.picker.files({ cwd = vim.fn.stdpath("config") }) end, desc = "Find Config File" }, + { "fF", function() Snacks.picker.files() end, desc = "Find Files" }, + { "fg", function() Snacks.picker.git_files() end, desc = "Find Files (git-files)" }, + -- TODO: swap fr and fR ? + { "fr", function() Snacks.picker.recent() end, desc = "Recent" }, + { "fR", function() Snacks.picker.recent({ filter = { cwd = true }}) end, desc = "Recent (cwd)" }, + { "fp", function() Snacks.picker.projects() end, desc = "Projects" }, + -- git + -- { "gc", function() Snacks.picker.git_log() end, desc = "Git Log" }, + -- { "gd", function() Snacks.picker.git_diff() end, desc = "Git Diff (hunks)" }, + -- { "gs", function() Snacks.picker.git_status() end, desc = "Git Status" }, + -- Grep + { "sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" }, + { "sB", function() Snacks.picker.grep_buffers() end, desc = "Grep Open Buffers" }, + { "sg", function() Snacks.picker.grep() end, desc = "Grep" }, + { "sp", function() Snacks.picker.lazy() end, desc = "Search for Plugin Spec" }, + { "sw", function() Snacks.picker.grep_word() end, desc = "Visual selection or word", mode = { "n", "x" } }, + -- search + { 's"', function() Snacks.picker.registers() end, desc = "Registers" }, + { "sa", function() Snacks.picker.autocmds() end, desc = "Autocmds" }, + { "sc", function() Snacks.picker.command_history() end, desc = "Command History" }, + { "sC", function() Snacks.picker.commands() end, desc = "Commands" }, + { "sd", function() Snacks.picker.diagnostics() end, desc = "Diagnostics" }, + { "sh", function() Snacks.picker.help() end, desc = "Help Pages" }, + { "sH", function() Snacks.picker.highlights() end, desc = "Highlights" }, + { "si", function() Snacks.picker.icons() end, desc = "Icons" }, + { "sj", function() Snacks.picker.jumps() end, desc = "Jumps" }, + { "sk", function() Snacks.picker.keymaps() end, desc = "Keymaps" }, + { "sl", function() Snacks.picker.loclist() end, desc = "Location List" }, + { "sM", function() Snacks.picker.man() end, desc = "Man Pages" }, + { "sm", function() Snacks.picker.marks() end, desc = "Marks" }, + { "sR", function() Snacks.picker.resume() end, desc = "Resume" }, + { "sq", function() Snacks.picker.qflist() end, desc = "Quickfix List" }, + { "su", function() Snacks.picker.undo() end, desc = "Undotree" }, + -- ui + { "uC", function() Snacks.picker.colorschemes() end, desc = "Colorschemes" }, + }, + }, + { "neovim/nvim-lspconfig", + opts = function() + local Keys = require("plugins.lsp.keymaps").get() + -- stylua: ignore + vim.list_extend(Keys, { + { "gd", function() Snacks.picker.lsp_definitions() end, desc = "Goto Definition", has = "definition" }, + -- { "gr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" }, + { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" }, + { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" }, + { "ss", function() Snacks.picker.lsp_symbols({ filter = rmz.lsp.kind_filter }) end, desc = "LSP Symbols", has = "documentSymbol" }, + { "sS", function() Snacks.picker.lsp_workspace_symbols({ filter = rmz.lsp.kind_filter }) end, desc = "LSP Workspace Symbols", has = "workspace/symbols" }, + }) + end, + }, + { "folke/todo-comments.nvim", + optional = true, + -- stylua: ignore + keys = { + { "st", function() Snacks.picker.todo_comments() end, desc = "Todo" }, + { "sT", function () Snacks.picker.todo_comments({ keywords = { "TODO", "FIX", "FIXME" } }) end, desc = "Todo/Fix/Fixme" }, + }, + }, + { "folke/flash.nvim", + -- TODO: verify if I want to keep this + specs = { + { + "folke/snacks.nvim", + opts = { + picker = { + win = { + input = { + keys = { + [""] = { "flash", mode = { "n", "i" } }, + ["s"] = { "flash" }, + }, + }, + }, + actions = { + flash = function(picker) + require("flash").jump({ + pattern = "^", + label = { after = { 0, 0 } }, + search = { + mode = "search", + exclude = { + function(win) + return vim.bo[vim.api.nvim_win_get_buf(win)].filetype ~= "snacks_picker_list" + end, + }, + }, + action = function(match) + local idx = picker.list:row2idx(match.pos[1]) + picker.list:_move(idx, true, true) + end, + }) + end, + }, + }, + }, + }, + }, + }, +} diff --git a/nvim/lua/plugins/snacks.lua b/nvim/lua/plugins/snacks.lua new file mode 100644 index 0000000..89eec46 --- /dev/null +++ b/nvim/lua/plugins/snacks.lua @@ -0,0 +1,81 @@ +-- Terminal Mappings +local function term_nav(dir) + ---@param self snacks.terminal + return function(self) + return self:is_floating() and "" or vim.schedule(function() + vim.cmd.wincmd(dir) + end) + end +end + +return { + { "folke/snacks.nvim", + -- stylua: ignore + keys = { + -- Snacks Scratch + { ".", function() Snacks.scratch() end, desc = "Toggle Scratch Buffer" }, + { "S", function() Snacks.scratch.select() end, desc = "Select Scratch Buffer" }, + { "dps", function() Snacks.profiler.scratch() end, desc = "Profiler Scratch Buffer" }, + }, + opts = { + bigfile = { enabled = true }, + indent = { enabled = true }, + input = { enabled = true }, -- NOTE: seems unecessary + quickfile = { enabled = true }, -- TODO: review if needed + scope = { enabled = true }, + scroll = { enabled = false }, -- fuck this! + statuscolumn = { enabled = true }, + terminal = { + win = { + keys = { + nav_h = { "", term_nav("W"), desc = "Go to Prev Window", expr = true, mode = "t" }, + nav_j = { "", term_nav("j"), desc = "Go to Lower Window", expr = true, mode = "t" }, + nav_k = { "", term_nav("k"), desc = "Go to Upper Window", expr = true, mode = "t" }, + nav_l = { "", term_nav("w"), desc = "Go to Next Window", expr = true, mode = "t" }, + }, + }, + }, + dashboard = { + preset = { + -- TODO: replace header + header = [[ + ██╗ █████╗ ██╗ ██╗███████╗██╗ ██╗██╗ ██╗██╗███╗ ███╗ Z + ██║ ██╔══██╗██║ ██║╚══███╔╝╚██╗ ██╔╝██║ ██║██║████╗ ████║ Z + ██║ ██║ ██║██║ ██║ ███╔╝ ╚████╔╝ ██║ ██║██║██╔████╔██║ z + ██║ ██║ ██║██║ ██║ ███╔╝ ╚██╔╝ ╚██╗ ██╔╝██║██║╚██╔╝██║ z + ███████╗╚█████╔╝╚██████╔╝███████╗ ██║ ╚████╔╝ ██║██║ ╚═╝ ██║ + ╚══════╝ ╚════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ]], + -- stylua: ignore + ---@type snacks.dashboard.Item[] + keys = { + { icon = " ", key = "f", desc = "Find File", action = ":lua Snacks.dashboard.pick('files')" }, + { icon = " ", key = "n", desc = "New File", action = ":ene | startinsert" }, + { icon = " ", key = "g", desc = "Find Text", action = ":lua Snacks.dashboard.pick('live_grep')" }, + { icon = " ", key = "r", desc = "Recent Files", action = ":lua Snacks.dashboard.pick('oldfiles')" }, + { icon = " ", key = "c", desc = "Config", action = ":lua Snacks.dashboard.pick('files', {cwd = vim.fn.stdpath('config')})" }, + { icon = " ", key = "s", desc = "Restore Session", section = "session" }, + { icon = " ", key = "x", desc = "Lazy Extras", action = ":LazyExtras" }, + { icon = "󰒲 ", key = "l", desc = "Lazy", action = ":Lazy" }, + }, + }, + }, + }, + }, + { "folke/snacks.nvim", + opts = { + notifier = { enabled = true }, + }, + -- stylua: ignore + keys = { + -- TODO: review after picking picker (ha) + { "n", function() + if Snacks.config.picker and Snacks.config.picker.enabled then + Snacks.picker.notifications() + else + Snacks.notifier.show_history() + end + end, desc = "Notification History" }, + { "un", function() Snacks.notifier.hide() end, desc = "Dismiss All Notifications" }, + }, + } +} diff --git a/nvim/lua/plugins/test.lua b/nvim/lua/plugins/test.lua index e4bc07c..9ed3fc4 100644 --- a/nvim/lua/plugins/test.lua +++ b/nvim/lua/plugins/test.lua @@ -1,22 +1,15 @@ return { - { - "folke/which-key.nvim", - opts = { - defaults = { ["t"] = { name = "+test" }, }, - }, - }, - { - "nvim-neotest/neotest", + { "nvim-neotest/neotest", + dependencies = { "nvim-neotest/nvim-nio" }, opts = { + adapters = {}, status = { virtual_text = true }, output = { open_on_run = true }, quickfix = { open = function() - if require("lazyvim.util").has("trouble.nvim") then - require("trouble").open({ mode = "quickfix", focus = false }) - else - vim.cmd("copen") - end + -- TODO: review if I'd prefer to just use quickfix here + -- vim.cmd("copen") + require("trouble").open({ mode = "quickfix", focus = false }) end, }, }, @@ -32,37 +25,36 @@ return { }, }, neotest_ns) - if require("lazyvim.util").has("trouble.nvim") then - opts.consumers = opts.consumers or {} - -- Refresh and auto close trouble after running tests - ---@type neotest.Consumer - opts.consumers.trouble = function(client) - client.listeners.results = function(adapter_id, results, partial) - if partial then - return - end - local tree = assert(client:get_position(nil, { adapter = adapter_id })) + opts.consumers = opts.consumers or {} + -- Refresh and auto close trouble after running tests + ---@type neotest.Consumer + opts.consumers.trouble = function(client) + client.listeners.results = function(adapter_id, results, partial) + if partial then + return + end + local tree = assert(client:get_position(nil, { adapter = adapter_id })) - local failed = 0 - for pos_id, result in pairs(results) do - if result.status == "failed" and tree:get_key(pos_id) then - failed = failed + 1 - end + local failed = 0 + for pos_id, result in pairs(results) do + if result.status == "failed" and tree:get_key(pos_id) then + failed = failed + 1 end - vim.schedule(function() - local trouble = require("trouble") - if trouble.is_open() then - trouble.refresh() - if failed == 0 then - trouble.close() - end - end - end) end - return {} + vim.schedule(function() + local trouble = require("trouble") + if trouble.is_open() then + trouble.refresh() + if failed == 0 then + trouble.close() + end + end + end) end + return {} end + -- NOTE: support setting up test adapters in lang if opts.adapters then local adapters = {} for name, config in pairs(opts.adapters or {}) do @@ -77,8 +69,11 @@ return { local meta = getmetatable(adapter) if adapter.setup then adapter.setup(config) + elseif adapter.adapter then + adapter.adapter(config) + adapter = adapter.adapter elseif meta and meta.__call then - adapter(config) + adapter = adapter(config) else error("Adapter " .. name .. " does not support setup") end @@ -93,13 +88,16 @@ return { end, -- stylua: ignore keys = { + {"t", "", desc = "+test"}, { "tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run File" }, { "tT", function() require("neotest").run.run(vim.loop.cwd()) end, desc = "Run All Test Files" }, { "tr", function() require("neotest").run.run() end, desc = "Run Nearest" }, + { "tl", function() require("neotest").run.run_last() end, desc = "Run Last" }, { "ts", function() require("neotest").summary.toggle() end, desc = "Toggle Summary" }, { "to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "Show Output" }, { "tO", function() require("neotest").output_panel.toggle() end, desc = "Toggle Output Panel" }, { "tS", function() require("neotest").run.stop() end, desc = "Stop" }, + { "tw", function() require("neotest").watch.toggle(vim.fn.expand("%")) end, desc = "Toggle Watch" }, }, }, { diff --git a/nvim/lua/plugins/treesitter.lua b/nvim/lua/plugins/treesitter.lua new file mode 100644 index 0000000..5ffa798 --- /dev/null +++ b/nvim/lua/plugins/treesitter.lua @@ -0,0 +1,113 @@ +return { + { "nvim-treesitter/nvim-treesitter", + dependencies = { "nvim-treesitter/nvim-treesitter-textobjects" }, + build = ":TSUpdate", + event = { "BufReadPost", "BufNewFile", "BufWritePre" , "VeryLazy" }, + lazy = vim.fn.argc(-1) == 0, -- load treesitter early when opening a file from the cmdline + init = function(plugin) + -- PERF: add nvim-treesitter queries to the rtp and it's custom query predicates early + -- This is needed because a bunch of plugins no longer `require("nvim-treesitter")`, which + -- no longer trigger the **nvim-treesitter** module to be loaded in time. + -- Luckily, the only things that those plugins need are the custom queries, which we make available + -- during startup. + require("lazy.core.loader").add_to_rtp(plugin) + require("nvim-treesitter.query_predicates") + end, + cmd = { "TSUpdateSync", "TSUpdate", "TSInstall" }, + keys = { + { "", desc = "Increment Selection" }, + { "", desc = "Decrement Selection", mode = "x" }, + }, + opts_extend = { "ensure_installed" }, + ---@type TSConfig + ---@diagnostic disable-next-line: missing-fields + opts = { + highlight = { enable = true }, + indent = { enable = true }, + ensure_installed = { + "bash", + "c", + "diff", + "git_config", + "git_rebase", + "gitattributes", + "gitcommit", + "gitignore", + "html", + "javascript", + "jsdoc", + "json", + "jsonc", + "lua", + "luadoc", + "luap", + "markdown", + "markdown_inline", + "printf", + "python", + "query", + "regex", + "toml", + "tsx", + "typescript", + "vim", + "vimdoc", + "xml", + "yaml", + }, + incremental_selection = { + enable = true, + keymaps = { + init_selection = "", + node_incremental = "", + scope_incremental = false, + node_decremental = "", + }, + }, + textobjects = { + move = { + enable = true, + goto_next_start = { ["]f"] = "@function.outer", ["]c"] = "@class.outer", ["]a"] = "@parameter.inner" }, + goto_next_end = { ["]F"] = "@function.outer", ["]C"] = "@class.outer", ["]A"] = "@parameter.inner" }, + goto_previous_start = { ["[f"] = "@function.outer", ["[c"] = "@class.outer", ["[a"] = "@parameter.inner" }, + goto_previous_end = { ["[F"] = "@function.outer", ["[C"] = "@class.outer", ["[A"] = "@parameter.inner" }, + }, + }, + }, + ---@param opts TSConfig + config = function(_, opts) + -- make sure extra langs are not duplicated + opts.ensure_installed = rmz.dedup(opts.ensure_installed) + + local configs = require("nvim-treesitter.configs") + configs.setup(opts) + + -- When in diff mode, we want to use the default + -- vim text objects c & C instead of the treesitter ones. + local move = require("nvim-treesitter.textobjects.move") ---@type table + for name, fn in pairs(move) do + if name:find("goto") == 1 then + move[name] = function(q, ...) + if not vim.wo.diff then return fn(q, ...) end + + local config = configs.get_module("textobjects.move")[name] ---@type table + for key, query in pairs(config or {}) do + if q == query and key:find("[%]%[][cC]") then + vim.cmd("normal! " .. key) + return + end + end + end + end + end + end, + }, + + -- Automatically add closing tags for HTML and JSX + { + "windwp/nvim-ts-autotag", + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = {}, + }, +} + diff --git a/nvim/lua/plugins/ui.lua b/nvim/lua/plugins/ui.lua index 0cb899a..8e52958 100644 --- a/nvim/lua/plugins/ui.lua +++ b/nvim/lua/plugins/ui.lua @@ -1,10 +1,91 @@ ----@type LazyPluginSpec +local M = {} + +---@param component any -- actually a lualine.component +---@param text string +---@param hl_group? string +---@return string +function M.format(component, text, hl_group) + text = text:gsub("%%", "%%%%") + if not hl_group or hl_group == "" then + return text + end + --- hl_cache is added here, not modified + ---@type table + component.hl_cache = component.hl_cache or {} + local lualine_hl_group = component.hl_cache[hl_group] + if not lualine_hl_group then + local utils = require("lualine.utils.utils") + ---@type string[] + local gui = vim.tbl_filter(function(x) return x end, { + utils.extract_highlight_colors(hl_group, "bold") and "bold", + utils.extract_highlight_colors(hl_group, "italic") and "italic", + }) + + lualine_hl_group = component:create_hl({ + fg = utils.extract_highlight_colors(hl_group, "fg"), + gui = #gui > 0 and table.concat(gui, ",") or nil, + }, "LV_" .. hl_group) --[[@as string]] + component.hl_cache[hl_group] = lualine_hl_group + end + return component:format_hl(lualine_hl_group) .. text .. component:get_default_hl() +end + +function M.pretty_path() + return function(self) + local opts = { + directory_hl = "", + filename_hl = "Bold", + length = 3, + modified_hl = "MatchParen", -- hl group when file is modified + modified_sign = "", + readonly_icon = " 󰌾 ", + } + + local path = vim.fn.expand("%:p") --[[@as string]] + + if path == "" then + return "" + end + + path = require("lazy.core.util").norm(path) + local cwd = vim.uv.cwd() + + if path:find(cwd, 1, true) == 1 then + path = path:sub(#cwd + 2) + end + + local sep = package.config:sub(1, 1) + local parts = vim.split(path, "[\\/]") + + if opts.length == 0 then + parts = parts + elseif #parts > opts.length then + parts = { parts[1], "…", unpack(parts, #parts - opts.length + 2, #parts) } + end + + if vim.bo.modified then + parts[#parts] = parts[#parts] .. opts.modified_sign + parts[#parts] = M.format(self, parts[#parts], opts.modified_hl) + else + parts[#parts] = M.format(self, parts[#parts], opts.filename_hl) + end + + local dir = "" + if #parts > 1 then + dir = table.concat({ unpack(parts, 1, #parts - 1) }, sep) + dir = M.format(self, dir .. sep, opts.directory_hl) + end + + local readonly = "" + if vim.bo.readonly then + readonly = M.format(self, opts.readonly_icon, opts.modified_hl) + end + return dir .. parts[#parts] .. readonly + end +end + +---@type LazySpec return { - -- disabled plugins {{{ - { "folke/which-key.nvim", - enabled = false, - }, - -- }}} { 'echasnovski/mini.clue', -- NOTE: this caused some issue with unterminated mappings, i.e. gc when there -- is also gcc. It would simply terminate early and not allow for next mapping. @@ -33,63 +114,50 @@ return { }, }, }, - { - "nvim-notify", - }, - { - "bufferline.nvim", + { "akinsho/bufferline.nvim", + event = "VeryLazy", + keys = { + { "bp", "BufferLineTogglePin", desc = "Toggle Pin" }, + { "bP", "BufferLineGroupClose ungrouped", desc = "Delete Non-Pinned Buffers" }, + { "br", "BufferLineCloseRight", desc = "Delete Buffers to the Right" }, + { "bl", "BufferLineCloseLeft", desc = "Delete Buffers to the Left" }, + { "", "BufferLineCyclePrev", desc = "Prev Buffer" }, + { "", "BufferLineCycleNext", desc = "Next Buffer" }, + { "[b", "BufferLineCyclePrev", desc = "Prev Buffer" }, + { "]b", "BufferLineCycleNext", desc = "Next Buffer" }, + { "[B", "BufferLineMovePrev", desc = "Move buffer prev" }, + { "]B", "BufferLineMoveNext", desc = "Move buffer next" }, + }, ---@type BufferlineConfig opts = { options = { + close_command = function(n) Snacks.bufdelete(n) end, + right_mouse_command = function(n) Snacks.bufdelete(n) end, + diagnostics = "nvim_lsp", always_show_bufferline = false, - }, - }, - }, - { - "stevearc/dressing.nvim", - lazy = true, - opts = { - input = { - start_in_insert = false, - insert_only = false, - }, - }, - }, - { - "noice.nvim", - enabled = false, - ---@type NoiceConfig - opts = { - presets = { - command_palette = false, -- don't position the cmdline and popupmenu together - lsp_doc_border = true, - long_message_to_split = true, - }, - cmdline = { - view = "cmdline", - }, - routes = { - { -- send file written messages to mini - filter = { - event = "msg_show", - kind = "", - find = "%[w%]", + diagnostics_indicator = function(_, _, diag) + local icons = rmz.ui.icons.diagnostics + local ret = (diag.error and icons.Error .. diag.error .. " " or "") + .. (diag.warning and icons.Warn .. diag.warning or "") + return vim.trim(ret) + end, + offsets = { + { + filetype = "man", + text = "Man page", + highlight = "Directory", + text_align = "left", }, - opts = { skip = true }, }, + ---@param opts bufferline.IconFetcherOpts + get_element_icon = function(opts) + return rmz.ui.icons.ft[opts.filetype] + end, }, }, - config = function(_, opts) - -- ensure [w] is written to msg_show so we can match it - vim.opt.shortmess:append("w") - vim.opt.shortmess:remove("W") - - require("noice").setup(opts) - end, }, - { - "lualine.nvim", - --TODO: Things that were in vim but are missing + { "nvim-lualine/lualine.nvim", + -- TODO: Things that were in vim but are missing -- - git line add/mod/del ar next to branch name rather on right -- - one status line per splits -- - maybe a single one is OK too? @@ -97,98 +165,193 @@ return { -- - unix/dos eof markers -- - really I only want to know if it's not unix -- - filetype in text form. It's quite important to glance this quickly - }, - { - "indent-blankline.nvim", - }, - { - "echasnovski/mini.indentscope", - version = false, -- wait till new 0.7.0 release to put it back on semver - event = "LazyFile", - opts = function (_, opts) - return { - draw = { - animation = require("mini.indentscope").gen_animation.linear({ duration = 10 }) - } - } + event = "VeryLazy", + init = function() + vim.g.lualine_laststatus = vim.o.laststatus + if vim.fn.argc(-1) > 0 then + -- set an empty statusline till lualine loads + vim.o.statusline = " " + else + -- hide the statusline on the starter page + vim.o.laststatus = 0 + end end, - }, - { - "nvimdev/dashboard-nvim", - -- TODO: create cool dashboard header - -- generate logo from text here: - -- https://patorjk.com/software/taag/#p=testall&v=0&f=Computer&t=NEOVIM - -- Can also use use `chafa` and aplha.term to generate text from cli, see - -- https://github.com/goolord/alpha-nvim/discussions/16#discussioncomment-4915947 - -- https://github.com/goolord/alpha-nvim/discussions/16#discussioncomment-5065731 - -- To add orgmode agenda button in alpha - -- dashboard.button("a", "󰕪 " .. " View Agenda", ":lua require('orgmode').action('agenda.agenda')"), opts = function() - local logo = [[ - ██╗ █████╗ ███████╗██╗ ██╗██╗ ██╗██╗███╗ ███╗ Z - ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝██║ ██║██║████╗ ████║ Z - ██║ ███████║ ███╔╝ ╚████╔╝ ██║ ██║██║██╔████╔██║ z - ██║ ██╔══██║ ███╔╝ ╚██╔╝ ╚██╗ ██╔╝██║██║╚██╔╝██║ z - ███████╗██║ ██║███████╗ ██║ ╚████╔╝ ██║██║ ╚═╝ ██║ - ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ - ]] + -- PERF: we don't need this lualine require madness 🤷 + local lualine_require = require("lualine_require") + lualine_require.require = require + + local icons = rmz.ui.icons - logo = string.rep("\n", 8) .. logo .. "\n\n" + vim.o.laststatus = vim.g.lualine_laststatus local opts = { - theme = "doom", - hide = { - -- this is taken care of by lualine - -- enabling this messes up the actual laststatus setting after loading a file - statusline = false, + options = { + theme = "auto", + globalstatus = vim.o.laststatus == 3, + disabled_filetypes = { statusline = { "dashboard", "alpha", "ministarter", "snacks_dashboard" } }, }, - config = { - header = vim.split(logo, "\n"), - -- stylua: ignore - center = { - { action = [[lua require('orgmode').action('agenda.agenda')]], desc = " View Agenda", icon = "󰕪 ", key = "a" }, - { action = "Telescope find_files", desc = " Find file", icon = " ", key = "f" }, - { action = "ene | startinsert", desc = " New file", icon = " ", key = "n" }, - { action = "Telescope oldfiles", desc = " Recent files", icon = " ", key = "r" }, - { action = [[lua require("lazyvim.util").telescope.config_files()()]], desc = " Config", icon = " ", key = "c" }, - { action = 'lua require("persistence").load()', desc = " Restore Session", icon = " ", key = "s" }, - { action = "LazyExtras", desc = " Lazy Extras", icon = " ", key = "x" }, - { action = "Lazy", desc = " Lazy", icon = "󰒲 ", key = "l" }, + sections = { + lualine_a = { "mode" }, + lualine_b = { "branch" }, + + lualine_c = { + { + "diagnostics", + symbols = { + error = icons.diagnostics.Error, + warn = icons.diagnostics.Warn, + info = icons.diagnostics.Info, + hint = icons.diagnostics.Hint, + }, + }, + { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } }, + { M.pretty_path() }, + }, + lualine_x = { + Snacks.profiler.status(), + -- stylua: ignore + { + function() return " " .. require("dap").status() end, + cond = function() return package.loaded["dap"] and require("dap").status() ~= "" end, + color = function() return { fg = Snacks.util.color("Debug") } end, + }, + -- stylua: ignore + { + require("lazy.status").updates, + cond = require("lazy.status").has_updates, + color = function() return { fg = Snacks.util.color("Special") } end, + }, + { + "diff", + symbols = { + added = icons.git.added, + modified = icons.git.modified, + removed = icons.git.removed, + }, + source = function() + local gitsigns = vim.b.gitsigns_status_dict + if gitsigns then + return { + added = gitsigns.added, + modified = gitsigns.changed, + removed = gitsigns.removed, + } + end + end, + }, + }, + lualine_y = { + { "progress", separator = " ", padding = { left = 1, right = 0 } }, + { "location", padding = { left = 0, right = 1 } }, + }, + lualine_z = { + function() + return " " .. os.date("%R") + end, }, - footer = function() - local stats = require("lazy").stats() - local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100) - return { "⚡ Neovim loaded " .. stats.loaded .. "/" .. stats.count .. " plugins in " .. ms .. "ms" } - end, }, + inactive_sections = { + lualine_c = { + { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } }, + { M.pretty_path() }, + }, + }, + extensions = { "neo-tree", "lazy", "fzf" }, } - for _, button in ipairs(opts.config.center) do - button.desc = button.desc .. string.rep(" ", 43 - #button.desc) - button.key_format = " %s" - end - - -- close Lazy and re-open when the dashboard is ready - if vim.o.filetype == "lazy" then - vim.cmd.close() - vim.api.nvim_create_autocmd("User", { - pattern = "DashboardLoaded", - callback = function() - require("lazy").show() - end, - }) - end + local trouble = require("trouble") + local symbols = trouble.statusline({ + mode = "symbols", + groups = {}, + title = false, + filter = { range = true }, + format = "{kind_icon}{symbol.name:Normal}", + hl_group = "lualine_c_normal", + }) + table.insert(opts.sections.lualine_c, { + symbols and symbols.get, + cond = function() + return vim.b.trouble_lualine ~= false and symbols.has() + end, + }) return opts - end + end, }, { - "SmiteshP/nvim-navic", + "echasnovski/mini.icons", + lazy = true, + opts = { + file = { + [".keep"] = { glyph = "󰊢", hl = "MiniIconsGrey" }, + ["devcontainer.json"] = { glyph = "", hl = "MiniIconsAzure" }, + }, + filetype = { + dotenv = { glyph = "", hl = "MiniIconsYellow" }, + }, + }, + init = function() + -- TODO: try without this + -- see :h MiniIcons.mock_nvim_web_devicons() + package.preload["nvim-web-devicons"] = function() + require("mini.icons").mock_nvim_web_devicons() + return package.loaded["nvim-web-devicons"] + end + end, }, - { - "nvim-web-devicons", + { "MunifTanjim/nui.nvim", lazy = true }, + { "stevearc/dressing.nvim", + enabled = false, -- replaced by snacks.input + lazy = true, + opts = { + input = { + start_in_insert = false, + insert_only = false, + }, + }, }, - { - "nui.nvim", + { "indent-blankline.nvim", + enabled = false, -- replaced by snacks.indent + }, + { "echasnovski/mini.indentscope", + enabled = false, -- replaced by snacks.indent + version = false, -- wait till new 0.7.0 release to put it back on semver + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = function (_, opts) + return { + draw = { + animation = require("mini.indentscope").gen_animation.linear({ duration = 10 }) + } + } + end, + }, + { "SmiteshP/nvim-navic", + lazy = true, + init = function() + vim.g.navic_silence = true + rmz.lsp.on_attach(function(client, buffer) + if client.supports_method("textDocument/documentSymbol") then + require("nvim-navic").attach(client, buffer) + end + end) + end, + opts = function() + return { + separator = " ", + highlight = true, + depth_limit = 5, + icons = rmz.ui.icons.kinds, + lazy_update_context = true, + } + end, + }, + { "nvim-lualine/lualine.nvim", + optional = true, + opts = function(_, opts) + if not vim.g.trouble_lualine then + table.insert(opts.sections.lualine_c, { "navic", color_correction = "dynamic" }) + end + end, }, } diff --git a/nvim/lua/rmz/util/init.lua b/nvim/lua/rmz/util/init.lua new file mode 100644 index 0000000..abf0c21 --- /dev/null +++ b/nvim/lua/rmz/util/init.lua @@ -0,0 +1,41 @@ +---@class rmz.util +local M = { + ui = require("rmz.util.ui"), + lazy = require("rmz.util.lazy"), + lsp = require("rmz.util.lsp"), +} + +--- Deduplicates a list. +---@generic T +---@param list T[] +---@return T[] +function M.dedup(list) + local ret = {} + local seen = {} + for _, v in ipairs(list) do + if not seen[v] then + table.insert(ret, v) + seen[v] = true + end + end + return ret +end + +function M.foldexpr() + local buf = vim.api.nvim_get_current_buf() + if vim.b[buf].ts_folds == nil then + -- as long as we don't have a filetype, don't bother + -- checking if treesitter is available (it won't) + if vim.bo[buf].filetype == "" then + return "0" + end + if vim.bo[buf].filetype:find("dashboard") then + vim.b[buf].ts_folds = false + else + vim.b[buf].ts_folds = pcall(vim.treesitter.get_parser, buf) + end + end + return vim.b[buf].ts_folds and vim.treesitter.foldexpr() or "0" +end + +return M diff --git a/nvim/lua/rmz/util/lazy.lua b/nvim/lua/rmz/util/lazy.lua new file mode 100644 index 0000000..81a5534 --- /dev/null +++ b/nvim/lua/rmz/util/lazy.lua @@ -0,0 +1,28 @@ +---@class rmz.util.lazy +local M = {} + +---@param name string +function M.get_plugin(name) + return require("lazy.core.config").spec.plugins[name] +end + +---@param name string +function M.opts(name) + local plugin = M.get_plugin(name) + if not plugin then + return {} + end + local Plugin = require("lazy.core.plugin") + return Plugin.values(plugin, "opts", false) +end + +for _, level in ipairs({ "info", "warn", "error" }) do + M[level] = function(msg, opts) + opts = opts or {} + opts.title = opts.title or "RmZ" + local Util = require("lazy.core.util") + return Util[level](msg, opts) + end +end + +return M diff --git a/nvim/lua/rmz/util/lsp.lua b/nvim/lua/rmz/util/lsp.lua new file mode 100644 index 0000000..662781c --- /dev/null +++ b/nvim/lua/rmz/util/lsp.lua @@ -0,0 +1,206 @@ +-- Absorbed from LazyVim +-- TODO: review, understand and adapt +---@class rmz.util.lsp +local M = {} + +---@alias lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: lsp.Client):boolean} + +M.kind_filter = { + default = { + "Class", + "Constructor", + "Enum", + "Field", + "Function", + "Interface", + "Method", + "Module", + "Namespace", + "Package", + "Property", + "Struct", + "Trait", + }, + markdown = false, + help = false, + -- you can specify a different filter for each filetype + lua = { + "Class", + "Constructor", + "Enum", + "Field", + "Function", + "Interface", + "Method", + "Module", + "Namespace", + -- "Package", -- remove package since luals uses it for control flow structures + "Property", + "Struct", + "Trait", + }, +} + +---@param opts? lsp.Client.filter +function M.get_clients(opts) + local ret = {} ---@type vim.lsp.Client[] + if vim.lsp.get_clients then + ret = vim.lsp.get_clients(opts) + else + ---@diagnostic disable-next-line: deprecated + ret = vim.lsp.get_active_clients(opts) + if opts and opts.method then + ---@param client vim.lsp.Client + ret = vim.tbl_filter(function(client) + return client.supports_method(opts.method, { bufnr = opts.bufnr }) + end, ret) + end + end + return opts and opts.filter and vim.tbl_filter(opts.filter, ret) or ret +end + +---@param on_attach fun(client:vim.lsp.Client, buffer) +---@param name? string +function M.on_attach(on_attach, name) + return vim.api.nvim_create_autocmd("LspAttach", { + callback = function(args) + local buffer = args.buf ---@type number + local client = vim.lsp.get_client_by_id(args.data.client_id) + if client and (not name or client.name == name) then + return on_attach(client, buffer) + end + end, + }) +end + +---@type table>> +M._supports_method = {} + +function M.setup() + local register_capability = vim.lsp.handlers["client/registerCapability"] + vim.lsp.handlers["client/registerCapability"] = function(err, res, ctx) + ---@diagnostic disable-next-line: no-unknown + local ret = register_capability(err, res, ctx) + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + for buffer in pairs(client.attached_buffers) do + vim.api.nvim_exec_autocmds("User", { + pattern = "LspDynamicCapability", + data = { client_id = client.id, buffer = buffer }, + }) + end + end + return ret + end + M.on_attach(M._check_methods) + M.on_dynamic_capability(M._check_methods) +end + +---@param client vim.lsp.Client +function M._check_methods(client, buffer) + -- don't trigger on invalid buffers + if not vim.api.nvim_buf_is_valid(buffer) then + return + end + -- don't trigger on non-listed buffers + if not vim.bo[buffer].buflisted then + return + end + -- don't trigger on nofile buffers + if vim.bo[buffer].buftype == "nofile" then + return + end + for method, clients in pairs(M._supports_method) do + clients[client] = clients[client] or {} + if not clients[client][buffer] then + if client.supports_method and client.supports_method(method, { bufnr = buffer }) then + clients[client][buffer] = true + vim.api.nvim_exec_autocmds("User", { + pattern = "LspSupportsMethod", + data = { client_id = client.id, buffer = buffer, method = method }, + }) + end + end + end +end + +---@param fn fun(client:vim.lsp.Client, buffer):boolean? +---@param opts? {group?: integer} +function M.on_dynamic_capability(fn, opts) + return vim.api.nvim_create_autocmd("User", { + pattern = "LspDynamicCapability", + group = opts and opts.group or nil, + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + local buffer = args.data.buffer ---@type number + if client then + return fn(client, buffer) + end + end, + }) +end + +---@param method string +---@param fn fun(client:vim.lsp.Client, buffer) +function M.on_supports_method(method, fn) + M._supports_method[method] = M._supports_method[method] or setmetatable({}, { __mode = "k" }) + return vim.api.nvim_create_autocmd("User", { + pattern = "LspSupportsMethod", + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + local buffer = args.data.buffer ---@type number + if client and method == args.data.method then + return fn(client, buffer) + end + end, + }) +end + +---@return _.lspconfig.options +function M.get_config(server) + local configs = require("lspconfig.configs") + return rawget(configs, server) +end + +---@return {default_config:lspconfig.Config} +function M.get_raw_config(server) + local ok, ret = pcall(require, "lspconfig.configs." .. server) + if ok then + return ret + end + return require("lspconfig.server_configurations." .. server) +end + +function M.is_enabled(server) + local c = M.get_config(server) + return c and c.enabled ~= false +end + +---@param server string +---@param cond fun( root_dir, config): boolean +function M.disable(server, cond) + local util = require("lspconfig.util") + local def = M.get_config(server) + ---@diagnostic disable-next-line: undefined-field + def.document_config.on_new_config = util.add_hook_before(def.document_config.on_new_config, function(config, root_dir) + if cond(root_dir, config) then + config.enabled = false + end + end) +end + +M.action = setmetatable({}, { + __index = function(_, action) + return function() + vim.lsp.buf.code_action({ + apply = true, + context = { + only = { action }, + diagnostics = {}, + }, + }) + end + end, +}) + +return M diff --git a/nvim/lua/rmz/util/ui.lua b/nvim/lua/rmz/util/ui.lua new file mode 100644 index 0000000..024ec05 --- /dev/null +++ b/nvim/lua/rmz/util/ui.lua @@ -0,0 +1,72 @@ +local M = {} + +-- icons used by other plugins +M.icons = { + misc = { + dots = "󰇘", + }, + ft = { + octo = "", + }, + dap = { + Stopped = { "󰁕 ", "DiagnosticWarn", "DapStoppedLine" }, + Breakpoint = " ", + BreakpointCondition = " ", + BreakpointRejected = { " ", "DiagnosticError" }, + LogPoint = ".>", + }, + diagnostics = { + Error = " ", + Warn = " ", + Hint = " ", + Info = " ", + }, + git = { + added = " ", + modified = " ", + removed = " ", + }, + kinds = { + Array = " ", + Boolean = "󰨙 ", + Class = " ", + Codeium = "󰘦 ", + Color = " ", + Control = " ", + Collapsed = " ", + Constant = "󰏿 ", + Constructor = " ", + Copilot = " ", + Enum = " ", + EnumMember = " ", + Event = " ", + Field = " ", + File = " ", + Folder = " ", + Function = "󰊕 ", + Interface = " ", + Key = " ", + Keyword = " ", + Method = "󰊕 ", + Module = " ", + Namespace = "󰦮 ", + Null = " ", + Number = "󰎠 ", + Object = " ", + Operator = " ", + Package = " ", + Property = " ", + Reference = " ", + Snippet = "󱄽 ", + String = " ", + Struct = "󰆼 ", + Supermaven = " ", + TabNine = "󰏚 ", + Text = " ", + TypeParameter = " ", + Unit = " ", + Value = " ", + Variable = "󰀫 ", + }, +} +return M diff --git a/nvim/lua/snippets/all.lua b/nvim/snippets/all.lua similarity index 100% rename from nvim/lua/snippets/all.lua rename to nvim/snippets/all.lua diff --git a/nvim/snippets/all.snippets b/nvim/snippets/all.snippets new file mode 100644 index 0000000..1d91cec --- /dev/null +++ b/nvim/snippets/all.snippets @@ -0,0 +1,2 @@ +# Add TextMate style snippets here +# see https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#snipmate diff --git a/vim/vimrc b/vim/vimrc index 0c341cb..0a09c8a 100644 --- a/vim/vimrc +++ b/vim/vimrc @@ -563,34 +563,37 @@ augroup ft_stdin au StdinReadPost * :set buftype=nofile augroup END -" Jump to last known cursor position {{{2 -augroup last_loc +if !has("nvim") + " autocmds that already exist in neovim + " Jump to last known cursor position {{{2 + augroup last_loc + au! + " blacklist certain filetype + let blacklist = ['gitcommit'] + autocmd BufReadPost * + \ if index(blacklist, &ft) < 0 && line("'\"") > 1 && line("'\"") <= line("$") | + \ exe "normal! g`\"" | + \ endif + augroup END + + " Check for file modifications automatically {{{2 + " (current buffer only) + " Use :NoAutoChecktime to disable it (uses b:autochecktime) + fun! MyAutoCheckTime() + " only check timestamp for normal files + if &buftype != '' | return | endif + if ! exists('b:autochecktime') || b:autochecktime + checktime % + let b:autochecktime = 1 + endif + endfun + augroup MyAutoChecktime au! - " blacklist certain filetype - let blacklist = ['gitcommit'] - autocmd BufReadPost * - \ if index(blacklist, &ft) < 0 && line("'\"") > 1 && line("'\"") <= line("$") | - \ exe "normal! g`\"" | - \ endif -augroup END - -" Check for file modifications automatically {{{2 -" (current buffer only) -" Use :NoAutoChecktime to disable it (uses b:autochecktime) -fun! MyAutoCheckTime() - " only check timestamp for normal files - if &buftype != '' | return | endif - if ! exists('b:autochecktime') || b:autochecktime - checktime % - let b:autochecktime = 1 - endif -endfun -augroup MyAutoChecktime - au! - au FocusGained,BufEnter,CursorHold,InsertEnter * call MyAutoCheckTime() -augroup END -command! NoAutoChecktime let b:autochecktime=0 -command! ToggleAutoChecktime let b:autochecktime=!get(b:, 'autochecktime', 0) | echom "b:autochecktime:" b:autochecktime + au FocusGained,BufEnter,CursorHold,InsertEnter * call MyAutoCheckTime() + augroup END + command! NoAutoChecktime let b:autochecktime=0 + command! ToggleAutoChecktime let b:autochecktime=!get(b:, 'autochecktime', 0) | echom "b:autochecktime:" b:autochecktime +endif " bindings {{{1