return { { "nvim-lua/plenary.nvim", lazy = true }, { "nvim-tree/nvim-web-devicons", lazy = true, opts = {} }, { 'nvim-mini/mini.extra', lazy = true, }, { 'nvim-mini/mini.ai', lazy = false, version = '*', dependencies = { 'nvim-treesitter/nvim-treesitter-textobjects', }, config = function() local MiniExtra = require('mini.extra') local ai = require('mini.ai') local spec_treesitter = ai.gen_spec.treesitter ai.setup({ custom_textobjects = { B = MiniExtra.gen_ai_spec.buffer(), I = MiniExtra.gen_ai_spec.indent(), L = MiniExtra.gen_ai_spec.line(), N = MiniExtra.gen_ai_spec.number(), F = spec_treesitter({ a = '@function.outer', i = '@function.inner' }), C = spec_treesitter({ a = '@comment.outer', i = '@comment.inner' }), P = spec_treesitter({ a = '@parameter.outer', i = '@parameter.inner' }), O = spec_treesitter({ a = '@block.outter', i = '@block.inner' }), k = spec_treesitter({ a = '@class.outer', i = '@class.inner' }), o = spec_treesitter({ a = { '@conditional.outer', '@loop.outer' }, i = { '@conditional.inner', '@loop.inner' }, }) } }) end }, { 'nvim-mini/mini.move', lazy = false, version = '*', opts = {} }, { 'nvim-mini/mini.align', lazy = false, version = '*', opts = {} }, { 'nvim-mini/mini.bracketed', lazy = false, version = '*', opts = {} }, { 'nvim-mini/mini.operators', lazy = false, version = '*', opts = { evaluate = { prefix = 'g|', func = nil, }, exchange = { prefix = 'g.', reindent_linewise = true, }, multiply = { prefix = 'gm', func = nil, }, replace = { prefix = 'gk', reindent_linewise = true, }, sort = { prefix = 'gs', func = nil, } } }, { 'nvim-mini/mini.surround', lazy = false, version = '*', opts = { -- Add custom surroundings to be used on top of builtin ones. For more -- information with examples, see `:h MiniSurround.config`. -- custom_surroundings = { -- s = { input = { '%[%[().-()%]%]' }, output = { left = '[[', right = ']]' } }, -- }, mappings = { add = 'sa', -- Add surrounding in Normal and Visual modes delete = 'sd', -- Delete surrounding find = 'sf', -- Find surrounding (to the right) find_left = 'sF', -- Find surrounding (to the left) highlight = 'sh', -- Highlight surrounding replace = 'sr', -- Replace surrounding update_n_lines = 'sn', -- Update `n_lines` suffix_last = 'l', -- Suffix to search with "prev" method suffix_next = 'n', -- Suffix to search with "next" method }, } }, { 'nvim-mini/mini.splitjoin', lazy = false, version = false, config = function() require('mini.splitjoin').setup() end, }, { "nvim-mini/mini.hipatterns", lazy = false, event = "BufReadPre", config = function() local MiniExtra = require("mini.extra") local hi_words = MiniExtra.gen_highlighter.words require('mini.hipatterns').setup({ highlighters = { -- Highlight standalone 'FIXME', 'HACK', 'TODO', 'NOTE' fixme = hi_words({ 'FIXME', 'Fixme', 'fixme' }, "MiniHipatternsFixme"), hack = hi_words({ 'HACK', 'Hack', 'hack' }, "MiniHipatternsHack"), todo = hi_words({ 'TODO', 'Todo', 'todo' }, "MiniHipatternsTodo"), note = hi_words({ 'NOTE', 'Note', 'note' }, "MiniHipatternsNote"), done = hi_words({ 'DONE', 'Done', 'done' }, "MiniHipatternsFixme"), -- Highlight hex color strings (`#rrggbb`) using that color -- hex_color = hipatterns.gen_highlighter.hex_color(), }, }) end, }, { 'nvim-mini/mini.files', event = 'VeryLazy', version = '*', keys = { { "-", function() require("mini.files").open(vim.api.nvim_buf_get_name(0)) end, desc = "Open parent directory", mode = "n" }, { "-", function() require("mini.files").open() end, desc = "File Explore", mode = "n" }, }, opts = { mappings = { go_in = "", -- Map both Enter and L to enter directories or open files go_in_plus = "L", go_out = "-", go_out_plus = "H", close = "", }, } }, { 'nvim-mini/mini.sessions', version = '*', lazy = false, config = function() require('mini.sessions').setup() end, }, { "folke/snacks.nvim", priority = 1000, lazy = false, ---@type snacks.Config opts = { animate = { enabled = false, duration = { step = 1, total = 1 }, easing = "linear", }, bigfile = { enabled = true }, dashboard = { enabled = false }, explorer = { enabled = false }, image = { enabled = true, inline = false, scale = 3, -- nil | 1 (default) | 2 | 3 | ... doc = { inline = true, enabled = true, max_width = nil, max_height = nil, conceal = true, }, math = { enabled = true, } }, terminal = { bo = { filetype = "snacks_terminal", }, win = { style = "terminal" }, wo = {}, keys = { q = "hide", gf = function(self) local f = vim.fn.findfile(vim.fn.expand(""), "**") if f == "" then Snacks.notify.warn("No file under cursor") else self:hide() vim.schedule(function() vim.cmd("e " .. f) end) end end, term_normal = { "", function(self) self.esc_timer = self.esc_timer or (vim.uv or vim.loop).new_timer() if self.esc_timer:is_active() then self.esc_timer:stop() vim.cmd("stopinsert") else self.esc_timer:start(250, 0, function() end) return "" end end, mode = "t", expr = true, desc = "Double escape to normal mode", }, }, }, indent = { enabled = false }, notifier = { enabled = true, timeout = 1000, }, picker = { enabled = true, layout = { preset = "ivy", layout = { position = "bottom" } }, frecency = true, history_bonus = true, projects = { dev = { "~/Projects", }, }, win = { input = { keys = { [''] = { 'layout_right', mode = { 'i', 'n' } }, }, }, }, sources = { files = { -- add a custom action actions = { create_if_missing = function(picker, item) -- When typing in the input, item may be nil. -- We'll use the input text as the intended file path. local input = picker.input and picker.input:get() or "" if input == "" then return end -- Make it relative to picker cwd if it’s not absolute local cwd = picker.opts.cwd or vim.loop.cwd() local path = input if not path:match("^/") and not path:match("^%a:[/\\]") then path = cwd .. "/" .. path end -- Normalize path (optional) path = vim.fn.fnamemodify(path, ":p") -- If file exists, just open it if vim.loop.fs_stat(path) then picker:close() vim.cmd.edit(path) return end -- Ensure parent dirs exist (optional but nice) vim.fn.mkdir(vim.fn.fnamemodify(path, ":h"), "p") -- Open a new buffer at that path (creates buffer even if file missing) picker:close() vim.cmd.edit(path) -- At this point buffer is new + unnamed-on-disk until you :w end, }, -- bind the action to a key in the picker UI win = { input = { keys = { [""] = { "create_if_missing", mode = { "i", "n" }, desc = "Create file from input" }, }, }, list = { keys = { [""] = { "create_if_missing", mode = { "n" }, desc = "Create file from input" }, }, }, }, }, }, }, scope = { enabled = false }, scratch = { ft = "markdown", }, statuscolumn = { enabled = true }, styles = { notification = { -- wo = { wrap = true } -- Wrap notifications } } }, keys = { -- Org Search { "o/", function() Snacks.picker.grep({dirs = { os.getenv("HOME") .. "/Documents/notes" }}) end, desc = "Search Org Files" }, -- { "o/", function() Snacks.picker.files({ cwd = os.getenv("HOME") .. "/Documents/notes" }) end, desc = "Find Document" }, -- Top Pickers & Explorer { "", function() Snacks.picker.smart() end, desc = "Smart Find Files" }, { "j", function() Snacks.picker.files({args = { "--no-require-git" }}) end, desc = "Find Files" }, { ",", 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" }, { "i", function() Snacks.picker.lsp_symbols() end, desc = "Symbols" }, { "I", function() Snacks.picker.lsp_workspace_symbols() end, desc = "LSP Workspace Symbols" }, -- { "e", function() Snacks.explorer() end, desc = "File Explorer" }, -- find { "fb", function() Snacks.picker.buffers() end, desc = "Buffers" }, { "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 Git Files" }, { "fp", function() Snacks.picker.projects() end, desc = "Projects" }, { "fr", function() Snacks.picker.recent() end, desc = "Recent" }, -- git { "gb", function() Snacks.picker.git_branches() end, desc = "Git Branches" }, { "gl", function() Snacks.picker.git_log() end, desc = "Git Log" }, { "gL", function() Snacks.picker.git_log_line() end, desc = "Git Log Line" }, { "gs", function() Snacks.picker.git_status() end, desc = "Git Status" }, { "gS", function() Snacks.picker.git_stash() end, desc = "Git Stash" }, { "gd", function() Snacks.picker.git_diff() end, desc = "Git Diff (Hunks)" }, { "gf", function() Snacks.picker.git_log_file() end, desc = "Git Log File" }, -- 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" }, { "sw", function() Snacks.picker.grep_word() end, desc = "Visual selection or word", mode = { "n", "x" } }, -- search { 's"', function() Snacks.picker.registers() end, desc = "Registers" }, { 's/', function() Snacks.picker.search_history() end, desc = "Search History" }, { "sa", function() Snacks.picker.autocmds() end, desc = "Autocmds" }, { "sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" }, { "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" }, { "sD", function() Snacks.picker.diagnostics_buffer() end, desc = "Buffer 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.marks() end, desc = "Marks" }, { "sM", function() Snacks.picker.man() end, desc = "Man Pages" }, { "sp", function() Snacks.picker.lazy() end, desc = "Search for Plugin Spec" }, { "sq", function() Snacks.picker.qflist() end, desc = "Quickfix List" }, { "sR", function() Snacks.picker.resume() end, desc = "Resume" }, { "su", function() Snacks.picker.undo() end, desc = "Undo History" }, { "sz", function() Snacks.picker.zoxide() end, desc = "Zoxide" }, { "uC", function() Snacks.picker.colorschemes() end, desc = "Colorschemes" }, -- LSP { "gd", function() Snacks.picker.lsp_definitions() end, desc = "Goto Definition" }, { "gD", function() Snacks.picker.lsp_declarations() end, desc = "Goto Declaration" }, { "grr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" }, { "gri", 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() end, desc = "LSP Symbols" }, { "sS", function() Snacks.picker.lsp_workspace_symbols() end, desc = "LSP Workspace Symbols" }, -- Other { "Z", function() Snacks.zen() end, desc = "Toggle Zen Mode" }, { "z", function() Snacks.zen.zoom() end, desc = "Toggle Zoom" }, { ".", function() Snacks.scratch() end, desc = "Toggle Scratch Buffer" }, { "S", function() Snacks.scratch.select() end, desc = "Select Scratch Buffer" }, -- { "n", function() Snacks.notifier.show_history() end, desc = "Notification History" }, { "bd", function() Snacks.bufdelete() end, desc = "Delete Buffer" }, { "bR", function() Snacks.rename.rename_file() end, desc = "Rename File" }, { "gB", function() Snacks.gitbrowse() end, desc = "Git Browse", mode = { "n", "v" } }, { "gg", function() Snacks.lazygit() end, desc = "Lazygit" }, { "un", function() Snacks.notifier.hide() end, desc = "Dismiss All Notifications" }, { "", function() Snacks.terminal.toggle() end, desc = "Toggle Terminal", mode = { "n", "t" } }, -- { "", "", desc = "Exit Terminal Mode", mode = { "t" } }, { "]]", function() Snacks.words.jump(vim.v.count1) end, desc = "Next Reference", mode = { "n", "t" } }, { "[[", function() Snacks.words.jump(-vim.v.count1) end, desc = "Prev Reference", mode = { "n", "t" } }, { "xN", desc = "Neovim News", function() Snacks.win({ file = vim.api.nvim_get_runtime_file("doc/news.txt", false)[1], width = 0.6, height = 0.6, wo = { spell = false, wrap = false, signcolumn = "yes", statuscolumn = " ", conceallevel = 3, }, }) end, } }, init = function() vim.api.nvim_create_autocmd("User", { pattern = "VeryLazy", callback = function() -- Setup some globals for debugging (lazy-loaded) _G.dd = function(...) Snacks.debug.inspect(...) end _G.bt = function() Snacks.debug.backtrace() end vim.print = _G.dd -- Override print to use snacks for `:=` command -- Create some toggle mappings 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 }):map("uc") Snacks.toggle.treesitter():map("uT") Snacks.toggle.option("background", { off = "light", on = "dark", name = "Dark Background" }):map("ub") Snacks.toggle.inlay_hints():map("uh") Snacks.toggle.indent():map("ug") Snacks.toggle.dim():map("uD") end, }) end, }, }