snacks.lua (8453B)
1 local function symbols() 2 local server_ready = not not vim.lsp.buf_notify(0, '$/progress', {}) 3 if server_ready then 4 Snacks.picker.lsp_symbols() 5 else 6 Snacks.picker.treesitter() 7 end 8 end 9 10 local keys = { 11 -- Top Pickers & Explorer 12 { "<leader>s<space>", function() Snacks.picker.smart() end, desc = "Smart Find Files" }, 13 { "<leader>b", function() Snacks.picker.buffers() end, desc = "Buffers" }, 14 { "<leader>sg", function() Snacks.picker.grep() end, desc = "Grep" }, 15 { "<leader>s;", function() Snacks.picker.command_history() end, desc = "Command History" }, 16 { "<leader>sn", function() Snacks.picker.notifications() end, desc = "Notification History" }, 17 { 18 "<leader>f", 19 function() 20 local explorer_pickers = Snacks.picker.get({ source = "explorer" }) 21 for _, v in pairs(explorer_pickers) do 22 if v:is_focused() then 23 v:close() 24 else 25 v:focus() 26 end 27 end 28 if #explorer_pickers == 0 then 29 Snacks.picker.explorer() 30 end 31 end, 32 33 desc = "Explorer" 34 }, 35 -- find 36 { "<leader>sc", function() Snacks.picker.files({ cwd = vim.fn.stdpath("config") }) end, desc = "Find Config File" }, 37 { "<leader>sf", function() Snacks.picker.files() end, desc = "Find Files" }, 38 { "<leader>sv", function() Snacks.picker.git_files() end, desc = "Find Git Files" }, 39 { "<leader>sP", function() Snacks.picker.projects() end, desc = "Projects" }, 40 { "<leader>sp", function() Snacks.picker.recent() end, desc = "Recent" }, 41 -- git 42 { "<leader>gb", function() Snacks.picker.git_branches() end, desc = "Git Branches" }, 43 { "<leader>gl", function() Snacks.picker.git_log() end, desc = "Git Log" }, 44 { "<leader>gL", function() Snacks.picker.git_log_line() end, desc = "Git Log Line" }, 45 { "<leader>gs", function() Snacks.picker.git_status() end, desc = "Git Status" }, 46 { "<leader>gS", function() Snacks.picker.git_stash() end, desc = "Git Stash" }, 47 { "<leader>gd", function() Snacks.picker.git_diff() end, desc = "Git Diff (Hunks)" }, 48 { "<leader>gf", function() Snacks.picker.git_log_file() end, desc = "Git Log File" }, 49 -- Grep 50 { "<leader>sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" }, 51 { "<leader>sB", function() Snacks.picker.grep_buffers() end, desc = "Grep Open Buffers" }, 52 { "<leader>sg", function() Snacks.picker.grep() end, desc = "Grep" }, 53 { "<leader>sw", function() Snacks.picker.grep_word() end, desc = "Visual selection or word", mode = { "n", "x" } }, 54 -- search 55 { '<leader>s"', function() Snacks.picker.registers() end, desc = "Registers" }, 56 { '<leader>s/', function() Snacks.picker.search_history() end, desc = "Search History" }, 57 { "<leader>sa", function() Snacks.picker.autocmds() end, desc = "Autocmds" }, 58 { "<leader>sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" }, 59 { "<leader>sC", function() Snacks.picker.commands() end, desc = "Commands" }, 60 { "<leader>sd", function() Snacks.picker.diagnostics() end, desc = "Diagnostics" }, 61 { "<leader>sD", function() Snacks.picker.diagnostics_buffer() end, desc = "Buffer Diagnostics" }, 62 { "<leader>sh", function() Snacks.picker.help() end, desc = "Help Pages" }, 63 { "<leader>sH", function() Snacks.picker.highlights() end, desc = "Highlights" }, 64 { "<leader>si", function() Snacks.picker.icons() end, desc = "Icons" }, 65 { "<leader>sj", function() Snacks.picker.jumps() end, desc = "Jumps" }, 66 { "<leader>sk", function() Snacks.picker.keymaps() end, desc = "Keymaps" }, 67 { "<leader>sl", function() Snacks.picker.loclist() end, desc = "Location List" }, 68 { "<leader>sm", function() Snacks.picker.marks() end, desc = "Marks" }, 69 { "<leader>sM", function() Snacks.picker.man() end, desc = "Man Pages" }, 70 { "<leader>sq", function() Snacks.picker.qflist() end, desc = "Quickfix List" }, 71 { "<leader>sR", function() Snacks.picker.resume() end, desc = "Resume" }, 72 { "<leader>su", function() Snacks.picker.undo() end, desc = "Undo History" }, 73 { "<leader>uC", function() Snacks.picker.colorschemes() end, desc = "Colorschemes" }, 74 -- LSP 75 { "gd", function() Snacks.picker.lsp_definitions() end, desc = "Goto Definition" }, 76 { "gD", function() Snacks.picker.lsp_declarations() end, desc = "Goto Declaration" }, 77 { "<leader>sr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" }, 78 { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" }, 79 { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" }, 80 { "<leader>ss", symbols, desc = "LSP Symbols" }, 81 { "<leader>sS", function() Snacks.picker.lsp_workspace_symbols() end, desc = "LSP Workspace Symbols" }, 82 -- Other 83 } 84 85 ---@type table<number, {token:lsp.ProgressToken, msg:string, done:boolean}[]> 86 local progress = vim.defaulttable() 87 vim.api.nvim_create_autocmd("LspProgress", { 88 desc = 'Show LSP loading progress', 89 group = vim.api.nvim_create_augroup('LspProgress', { clear = true }), 90 91 ---@param ev {data: {client_id: integer, params: lsp.ProgressParams}} 92 callback = function(ev) 93 local client = vim.lsp.get_client_by_id(ev.data.client_id) 94 local value = ev.data.params 95 .value --[[@as {percentage?: number, title?: string, message?: string, kind: "begin" | "report" | "end"}]] 96 if not client or type(value) ~= "table" then 97 return 98 end 99 local p = progress[client.id] 100 101 for i = 1, #p + 1 do 102 if i == #p + 1 or p[i].token == ev.data.params.token then 103 p[i] = { 104 token = ev.data.params.token, 105 msg = ("[%3d%%] %s%s"):format( 106 value.kind == "end" and 100 or value.percentage or 100, 107 value.title or "", 108 value.message and (" **%s**"):format(value.message) or "" 109 ), 110 done = value.kind == "end", 111 } 112 break 113 end 114 end 115 116 local msg = {} ---@type string[] 117 progress[client.id] = vim.tbl_filter(function(v) 118 return table.insert(msg, v.msg) or not v.done 119 end, p) 120 121 local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } 122 vim.notify(table.concat(msg, "\n"), "info", { 123 id = "lsp_progress", 124 title = client.name, 125 opts = function(notif) 126 notif.icon = #progress[client.id] == 0 and " " 127 or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1] 128 end, 129 }) 130 end, 131 }) 132 133 return { 134 "folke/snacks.nvim", 135 priority = 1000, 136 lazy = false, 137 ---@type snacks.Config 138 opts = { 139 picker = { 140 win = { 141 preview = { 142 wo = { 143 foldlevel = 99, 144 foldenable = false, 145 }, 146 }, 147 }, 148 sources = 149 { 150 ---@class snacks.picker.Explorer.Config 151 explorer = { 152 jump = { close = true }, 153 win = { 154 list = { 155 keys = { 156 ["-"] = "explorer_up", 157 } 158 } 159 }, 160 }, 161 } 162 }, 163 animate = {}, 164 notifier = {}, 165 indent = { 166 enabled = true, 167 indent = { 168 char = '┊', 169 }, 170 }, 171 input = {}, 172 }, 173 keys = keys, 174 }