snacks.lua (8767B)
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.explorer.reveal() 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 { "<leader>s<", function() Snacks.picker.lsp_incoming_calls() end, nowait = true, desc = "Go to call sites" }, 79 { "<leader>s>", function() Snacks.picker.lsp_outgoing_calls() end, nowait = true, desc = "Go to called functions" }, 80 { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" }, 81 { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" }, 82 { "<leader>ss", symbols, desc = "LSP Symbols" }, 83 { "<leader>sS", function() Snacks.picker.lsp_workspace_symbols() end, desc = "LSP Workspace Symbols" }, 84 -- Other 85 } 86 87 ---@type table<number, {token:lsp.ProgressToken, msg:string, done:boolean}[]> 88 local progress = vim.defaulttable() 89 vim.api.nvim_create_autocmd("LspProgress", { 90 desc = 'Show LSP loading progress', 91 group = vim.api.nvim_create_augroup('LspProgress', { clear = true }), 92 93 ---@param ev {data: {client_id: integer, params: lsp.ProgressParams}} 94 callback = function(ev) 95 local client = vim.lsp.get_client_by_id(ev.data.client_id) 96 local value = ev.data.params 97 .value --[[@as {percentage?: number, title?: string, message?: string, kind: "begin" | "report" | "end"}]] 98 if not client or type(value) ~= "table" then 99 return 100 end 101 local p = progress[client.id] 102 103 for i = 1, #p + 1 do 104 if i == #p + 1 or p[i].token == ev.data.params.token then 105 p[i] = { 106 token = ev.data.params.token, 107 msg = ("[%3d%%] %s%s"):format( 108 value.kind == "end" and 100 or value.percentage or 100, 109 value.title or "", 110 value.message and (" **%s**"):format(value.message) or "" 111 ), 112 done = value.kind == "end", 113 } 114 break 115 end 116 end 117 118 local msg = {} ---@type string[] 119 progress[client.id] = vim.tbl_filter(function(v) 120 return table.insert(msg, v.msg) or not v.done 121 end, p) 122 123 local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } 124 vim.notify(table.concat(msg, "\n"), "info", { 125 id = "lsp_progress", 126 title = client.name, 127 opts = function(notif) 128 notif.icon = #progress[client.id] == 0 and " " 129 or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1] 130 end, 131 }) 132 end, 133 }) 134 135 return { 136 "folke/snacks.nvim", 137 priority = 1000, 138 lazy = false, 139 ---@type snacks.Config 140 opts = { 141 picker = { 142 win = { 143 preview = { 144 wo = { 145 foldlevel = 99, 146 foldenable = false, 147 }, 148 }, 149 }, 150 sources = 151 { 152 ---@class snacks.picker.Explorer.Config 153 explorer = { 154 jump = { close = true }, 155 win = { 156 list = { 157 keys = { 158 ["-"] = "explorer_up", 159 } 160 } 161 }, 162 }, 163 } 164 }, 165 animate = {}, 166 notifier = {}, 167 indent = { 168 enabled = true, 169 indent = { 170 char = '┊', 171 }, 172 }, 173 input = {}, 174 }, 175 keys = keys, 176 }