local Util = require("lazyvim.util") return { -- file explorer { "nvim-neo-tree/neo-tree.nvim", branch = "v3.x", cmd = "Neotree", keys = { { "fe", function() require("neo-tree.command").execute({ toggle = true, dir = Util.root() }) end, desc = "Explorer NeoTree (root dir)", }, { "fE", function() require("neo-tree.command").execute({ toggle = true, dir = vim.loop.cwd() }) end, desc = "Explorer NeoTree (cwd)", }, { "e", "fe", desc = "Explorer NeoTree (root dir)", remap = true }, { "E", "fE", desc = "Explorer NeoTree (cwd)", remap = true }, { "ge", function() require("neo-tree.command").execute({ source = "git_status", toggle = true }) end, desc = "Git explorer", }, { "be", function() require("neo-tree.command").execute({ source = "buffers", toggle = true }) end, desc = "Buffer explorer", }, }, deactivate = function() vim.cmd([[Neotree close]]) end, init = function() if vim.fn.argc(-1) == 1 then local stat = vim.loop.fs_stat(vim.fn.argv(0)) if stat and stat.type == "directory" then require("neo-tree") end end end, opts = { sources = { "filesystem", "buffers", "git_status", "document_symbols" }, open_files_do_not_replace_types = { "terminal", "Trouble", "trouble", "qf", "Outline" }, filesystem = { bind_to_cwd = false, follow_current_file = { enabled = true }, use_libuv_file_watcher = true, }, window = { mappings = { [""] = "none", }, }, default_component_configs = { indent = { with_expanders = true, -- if nil and file nesting is enabled, will enable expanders expander_collapsed = "", expander_expanded = "", expander_highlight = "NeoTreeExpander", }, }, }, config = function(_, opts) local function on_move(data) Util.lsp.on_rename(data.source, data.destination) end local events = require("neo-tree.events") opts.event_handlers = opts.event_handlers or {} vim.list_extend(opts.event_handlers, { { event = events.FILE_MOVED, handler = on_move }, { event = events.FILE_RENAMED, handler = on_move }, }) require("neo-tree").setup(opts) vim.api.nvim_create_autocmd("TermClose", { pattern = "*lazygit", callback = function() if package.loaded["neo-tree.sources.git_status"] then require("neo-tree.sources.git_status").refresh() end end, }) end, }, -- search/replace in multiple files { "nvim-pack/nvim-spectre", build = false, cmd = "Spectre", opts = { open_cmd = "noswapfile vnew" }, -- stylua: ignore keys = { { "sr", function() require("spectre").open() end, desc = "Replace in files (Spectre)" }, }, }, -- Fuzzy finder. -- The default key bindings to find files will use Telescope's -- `find_files` or `git_files` depending on whether the -- directory is a git repo. { "nvim-telescope/telescope.nvim", cmd = "Telescope", version = false, -- telescope did only one release, so use HEAD for now dependencies = { { "nvim-telescope/telescope-fzf-native.nvim", build = "make", enabled = vim.fn.executable("make") == 1, config = function() Util.on_load("telescope.nvim", function() require("telescope").load_extension("fzf") end) end, }, }, keys = { { ",", "Telescope buffers sort_mru=true sort_lastused=true", desc = "Switch Buffer", }, { "/", Util.telescope("live_grep"), desc = "Grep (root dir)" }, { ":", "Telescope command_history", desc = "Command History" }, { "", Util.telescope("files"), desc = "Find Files (root dir)" }, -- find { "fb", "Telescope buffers sort_mru=true sort_lastused=true", desc = "Buffers" }, { "fc", Util.telescope.config_files(), desc = "Find Config File" }, { "ff", Util.telescope("files"), desc = "Find Files (root dir)" }, { "fF", Util.telescope("files", { cwd = false }), desc = "Find Files (cwd)" }, { "fr", "Telescope oldfiles", desc = "Recent" }, { "fR", Util.telescope("oldfiles", { cwd = vim.loop.cwd() }), desc = "Recent (cwd)" }, -- git { "gc", "Telescope git_commits", desc = "commits" }, { "gs", "Telescope git_status", desc = "status" }, -- search { 's"', "Telescope registers", desc = "Registers" }, { "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", Util.telescope("live_grep"), desc = "Grep (root dir)" }, { "sG", Util.telescope("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", Util.telescope("grep_string", { word_match = "-w" }), desc = "Word (root dir)" }, { "sW", Util.telescope("grep_string", { cwd = false, word_match = "-w" }), desc = "Word (cwd)" }, { "sw", Util.telescope("grep_string"), mode = "v", desc = "Selection (root dir)" }, { "sW", Util.telescope("grep_string", { cwd = false }), mode = "v", desc = "Selection (cwd)" }, { "uC", Util.telescope("colorscheme", { enable_preview = true }), desc = "Colorscheme with preview" }, { "ss", function() require("telescope.builtin").lsp_document_symbols({ symbols = require("lazyvim.config").get_kind_filter(), }) end, desc = "Goto Symbol", }, { "sS", function() require("telescope.builtin").lsp_dynamic_workspace_symbols({ symbols = require("lazyvim.config").get_kind_filter(), }) end, desc = "Goto Symbol (Workspace)", }, }, opts = function() local actions = require("telescope.actions") local open_with_trouble = function(...) return require("trouble.providers.telescope").open_with_trouble(...) end local open_selected_with_trouble = function(...) return require("trouble.providers.telescope").open_selected_with_trouble(...) end local find_files_no_ignore = function() local action_state = require("telescope.actions.state") local line = action_state.get_current_line() Util.telescope("find_files", { no_ignore = true, default_text = line })() end local find_files_with_hidden = function() local action_state = require("telescope.actions.state") local line = action_state.get_current_line() Util.telescope("find_files", { hidden = true, default_text = line })() end return { defaults = { prompt_prefix = " ", selection_caret = " ", -- open files in the first window that is an actual file. -- use the current window if no other window is available. get_selection_window = function() local wins = vim.api.nvim_list_wins() table.insert(wins, 1, vim.api.nvim_get_current_win()) for _, win in ipairs(wins) do local buf = vim.api.nvim_win_get_buf(win) if vim.bo[buf].buftype == "" then return win end end return 0 end, mappings = { i = { [""] = open_with_trouble, [""] = open_selected_with_trouble, [""] = find_files_no_ignore, [""] = find_files_with_hidden, [""] = actions.cycle_history_next, [""] = actions.cycle_history_prev, [""] = actions.preview_scrolling_down, [""] = actions.preview_scrolling_up, }, n = { ["q"] = actions.close, }, }, }, } end, }, -- Flash enhances the built-in search functionality by showing labels -- at the end of each match, letting you quickly jump to a specific -- location. { "folke/flash.nvim", 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" }, }, }, -- Flash Telescope config { "nvim-telescope/telescope.nvim", optional = true, opts = function(_, opts) if not Util.has("flash.nvim") then return end local function flash(prompt_bufnr) 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 ~= "TelescopeResults" end, }, }, action = function(match) local picker = require("telescope.actions.state").get_current_picker(prompt_bufnr) picker:set_selection(match.pos[1] - 1) end, }) end opts.defaults = vim.tbl_deep_extend("force", opts.defaults or {}, { mappings = { n = { s = flash }, i = { [""] = flash } }, }) end, }, -- which-key helps you remember key bindings by showing a popup -- with the active keybindings of the command you started typing. { "folke/which-key.nvim", event = "VeryLazy", opts = { plugins = { spelling = true }, defaults = { mode = { "n", "v" }, ["g"] = { name = "+goto" }, ["gs"] = { name = "+surround" }, ["]"] = { name = "+next" }, ["["] = { name = "+prev" }, [""] = { name = "+tabs" }, ["b"] = { name = "+buffer" }, ["c"] = { name = "+code" }, ["f"] = { name = "+file/find" }, ["g"] = { name = "+git" }, ["gh"] = { name = "+hunks" }, ["q"] = { name = "+quit/session" }, ["s"] = { name = "+search" }, ["u"] = { name = "+ui" }, ["w"] = { name = "+windows" }, ["x"] = { name = "+diagnostics/quickfix" }, }, }, config = function(_, opts) local wk = require("which-key") wk.setup(opts) wk.register(opts.defaults) end, }, -- git signs highlights text that has changed since the list -- git commit, and also lets you interactively stage & unstage -- hunks in a commit. { "lewis6991/gitsigns.nvim", event = "LazyFile", opts = { signs = { add = { text = "▎" }, change = { text = "▎" }, delete = { text = "" }, topdelete = { text = "" }, changedelete = { text = "▎" }, untracked = { 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", gs.next_hunk, "Next Hunk") map("n", "[h", gs.prev_hunk, "Prev 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, "Preview Hunk") map("n", "ghb", function() gs.blame_line({ full = true }) end, "Blame Line") 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") end, }, }, -- Automatically highlights other instances of the word under your cursor. -- This works with LSP, Treesitter, and regexp matching to find the other -- instances. { "RRethy/vim-illuminate", event = "LazyFile", opts = { delay = 200, large_file_cutoff = 2000, large_file_overrides = { providers = { "lsp" }, }, }, config = function(_, opts) require("illuminate").configure(opts) local function map(key, dir, buffer) vim.keymap.set("n", key, function() require("illuminate")["goto_" .. dir .. "_reference"](false) end, { desc = dir:sub(1, 1):upper() .. dir:sub(2) .. " Reference", buffer = buffer }) end map("]]", "next") map("[[", "prev") -- also set it after loading ftplugins, since a lot overwrite [[ and ]] vim.api.nvim_create_autocmd("FileType", { callback = function() local buffer = vim.api.nvim_get_current_buf() map("]]", "next", buffer) map("[[", "prev", buffer) end, }) end, keys = { { "]]", desc = "Next Reference" }, { "[[", desc = "Prev Reference" }, }, }, -- buffer remove { "echasnovski/mini.bufremove", keys = { { "bd", function() local bd = require("mini.bufremove").delete if vim.bo.modified then local choice = vim.fn.confirm(("Save changes to %q?"):format(vim.fn.bufname()), "&Yes\n&No\n&Cancel") if choice == 1 then -- Yes vim.cmd.write() bd(0) elseif choice == 2 then -- No bd(0, true) end else bd(0) end end, desc = "Delete Buffer", }, -- stylua: ignore { "bD", function() require("mini.bufremove").delete(0, true) end, desc = "Delete Buffer (Force)" }, }, }, -- better diagnostics list and others { "folke/trouble.nvim", cmd = { "TroubleToggle", "Trouble" }, opts = { use_diagnostic_signs = true }, keys = { { "xx", "TroubleToggle document_diagnostics", desc = "Document Diagnostics (Trouble)" }, { "xX", "TroubleToggle workspace_diagnostics", desc = "Workspace Diagnostics (Trouble)" }, { "xL", "TroubleToggle loclist", desc = "Location List (Trouble)" }, { "xQ", "TroubleToggle quickfix", desc = "Quickfix List (Trouble)" }, { "[q", function() if require("trouble").is_open() then require("trouble").previous({ skip_groups = true, jump = true }) else local ok, err = pcall(vim.cmd.cprev) if not ok then vim.notify(err, vim.log.levels.ERROR) end end end, desc = "Previous trouble/quickfix item", }, { "]q", function() if require("trouble").is_open() then require("trouble").next({ skip_groups = true, jump = true }) else local ok, err = pcall(vim.cmd.cnext) if not ok then vim.notify(err, vim.log.levels.ERROR) end end end, desc = "Next trouble/quickfix item", }, }, }, -- Finds and lists all of the TODO, HACK, BUG, etc comment -- in your project and loads them into a browsable list. { "folke/todo-comments.nvim", cmd = { "TodoTrouble", "TodoTelescope" }, event = "LazyFile", config = true, -- 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", "TodoTrouble", desc = "Todo (Trouble)" }, { "xT", "TodoTrouble keywords=TODO,FIX,FIXME", desc = "Todo/Fix/Fixme (Trouble)" }, { "st", "TodoTelescope", desc = "Todo" }, { "sT", "TodoTelescope keywords=TODO,FIX,FIXME", desc = "Todo/Fix/Fixme" }, }, }, }