-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.lua
More file actions
369 lines (329 loc) · 11.7 KB
/
init.lua
File metadata and controls
369 lines (329 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
local notify = require("peekstack.util.notify")
local M = {}
---@type table<string, fun(ctx: PeekstackProviderContext, cb: fun(locations: PeekstackLocation[]))>
local providers = {}
---@class PeekstackPicker
---@field pick fun(locations: PeekstackLocation[], opts?: table, cb: fun(location: PeekstackLocation))
---@type table<string, PeekstackPicker>
local pickers = {}
local function set_hl()
vim.api.nvim_set_hl(0, "PeekstackOrigin", { default = true, link = "IncSearch" })
vim.api.nvim_set_hl(0, "PeekstackStackViewIndex", { default = true, link = "LineNr" })
vim.api.nvim_set_hl(0, "PeekstackStackViewPinned", { default = true, link = "DiagnosticWarn" })
vim.api.nvim_set_hl(0, "PeekstackStackViewTree", { default = true, link = "Comment" })
vim.api.nvim_set_hl(0, "PeekstackStackViewProvider", { default = true, link = "Type" })
vim.api.nvim_set_hl(0, "PeekstackStackViewPath", { default = true, link = "Directory" })
vim.api.nvim_set_hl(0, "PeekstackStackViewFocused", { default = true, link = "Type" })
vim.api.nvim_set_hl(0, "PeekstackStackViewPreview", { default = true, link = "Comment" })
vim.api.nvim_set_hl(0, "PeekstackStackViewFilter", { default = true, link = "Search" })
vim.api.nvim_set_hl(0, "PeekstackStackViewHeader", { default = true, link = "Title" })
vim.api.nvim_set_hl(0, "PeekstackStackViewEmpty", { default = true, link = "Comment" })
vim.api.nvim_set_hl(0, "PeekstackStackViewCursorLine", { default = true, link = "CursorLine" })
vim.api.nvim_set_hl(0, "PeekstackInlinePreview", { default = true, link = "Comment" })
vim.api.nvim_set_hl(0, "PeekstackTitleProvider", { default = true, link = "Type" })
vim.api.nvim_set_hl(0, "PeekstackTitlePath", { default = true, link = "Directory" })
vim.api.nvim_set_hl(0, "PeekstackTitleIcon", { default = true, link = "Special" })
vim.api.nvim_set_hl(0, "PeekstackTitleLine", { default = true, link = "LineNr" })
vim.api.nvim_set_hl(0, "PeekstackStackViewIcon", { default = true, link = "Special" })
vim.api.nvim_set_hl(0, "PeekstackStackViewLine", { default = true, link = "LineNr" })
vim.api.nvim_set_hl(0, "PeekstackPopupBorder", { default = true, link = "FloatBorder" })
vim.api.nvim_set_hl(0, "PeekstackPopupBorderFocused", { default = true, link = "Function" })
vim.api.nvim_set_hl(0, "PeekstackPopupBorderZoomed", { default = true, link = "WarningMsg" })
vim.api.nvim_set_hl(0, "PeekstackTitleKindError", { default = true, link = "DiagnosticError" })
vim.api.nvim_set_hl(0, "PeekstackTitleKindWarn", { default = true, link = "DiagnosticWarn" })
vim.api.nvim_set_hl(0, "PeekstackTitleKindInfo", { default = true, link = "DiagnosticInfo" })
vim.api.nvim_set_hl(0, "PeekstackTitleKindHint", { default = true, link = "DiagnosticHint" })
end
---@param name string
---@param fn fun(ctx: PeekstackProviderContext, cb: fun(locations: PeekstackLocation[]))
function M.register_provider(name, fn)
providers[name] = fn
end
---@return string[]
function M.list_providers()
local names = vim.tbl_keys(providers)
table.sort(names)
return names
end
---@param name string
---@param fn PeekstackPicker
function M.register_picker(name, fn)
pickers[name] = fn
end
---@param prefix string
---@param provider_mod table
---@param names string[]
local function register_providers(prefix, provider_mod, names)
for _, name in ipairs(names) do
local fn = provider_mod[name]
if fn then
M.register_provider(prefix .. name, fn)
end
end
end
---@param name string
---@return fun(ctx: PeekstackProviderContext, cb: fun(locations: PeekstackLocation[]))?
local function ensure_provider(name)
return providers[name]
end
---@return PeekstackPicker
local function pick_backend()
local config = require("peekstack.config")
local backend = config.get().picker.backend
return pickers[backend] or pickers.builtin
end
---@param name string?
---@return boolean
local function is_lsp_provider(name)
return type(name) == "string" and name:match("^lsp%.") ~= nil
end
---Normalize location and set provider from opts if needed
---@param loc table
---@param opts? table
---@return PeekstackLocation?
local function prepare_location(loc, opts)
local location = require("peekstack.core.location")
local provider = opts and opts.provider
if loc.provider == nil and provider then
loc.provider = provider
end
local normalized = location.normalize(loc, loc.provider)
if normalized then
return normalized
end
notify.warn("Invalid location payload: expected uri/range")
return nil
end
---@param loc table
---@param opts? table
function M.peek_location(loc, opts)
if not loc then
return
end
local normalized = prepare_location(loc, opts)
if not normalized then
return
end
local mode = opts and opts.mode
if mode == "inline" then
local cfg = require("peekstack.config").get()
if cfg.ui.inline_preview and cfg.ui.inline_preview.enabled then
local inline_preview = require("peekstack.ui.inline_preview")
inline_preview.open(normalized, opts)
return
end
-- Fallback to stack mode when inline preview is disabled
mode = nil
end
if mode == "quick" then
local stack = require("peekstack.core.stack")
stack.push(normalized, vim.tbl_extend("force", opts, { stack = false }))
else
local stack = require("peekstack.core.stack")
stack.push(normalized, opts)
end
end
---@param locations? PeekstackLocation[]
---@param opts? table
function M.peek_locations(locations, opts)
if not locations or #locations == 0 then
notify.info("No locations")
return
end
if #locations == 1 then
M.peek_location(locations[1], opts)
return
end
local picker = pick_backend()
picker.pick(locations, opts or {}, function(choice)
if choice then
M.peek_location(choice, opts)
end
end)
end
--- Peek a location by provider name. This is the core dispatch function.
---@param provider string
---@param opts? table
local function peek_by_provider(provider, opts)
local fn = ensure_provider(provider)
if not fn then
notify.warn("Unknown provider: " .. tostring(provider))
return
end
local context = require("peekstack.core.context")
local ctx = context.current()
local merged = vim.tbl_extend("force", opts or {}, { provider = provider })
fn(ctx, function(locations)
local filtered = locations or {}
if is_lsp_provider(provider) and locations and #locations > 0 then
local location_mod = require("peekstack.core.location")
local pos = ctx.position or {}
local uri = nil
if ctx.bufnr and vim.api.nvim_buf_is_valid(ctx.bufnr) then
uri = vim.uri_from_bufnr(ctx.bufnr)
end
if uri and pos.line ~= nil and pos.character ~= nil then
local realpath_cache = {}
filtered = {}
for _, loc in ipairs(locations) do
local normalized = location_mod.normalize(loc, provider)
if
normalized
and not location_mod.is_same_position(normalized, uri, pos.line, pos.character, {
realpath_cache = realpath_cache,
})
then
table.insert(filtered, normalized)
end
end
end
end
M.peek_locations(filtered, merged)
end)
end
---@class PeekstackPeekMethods
---@field definition fun(opts?: table)
---@field implementation fun(opts?: table)
---@field references fun(opts?: table)
---@field type_definition fun(opts?: table)
---@field declaration fun(opts?: table)
---@field symbols_document fun(opts?: table)
---@field diagnostics_cursor fun(opts?: table)
---@field diagnostics_buffer fun(opts?: table)
---@field file_under_cursor fun(opts?: table)
---@field grep fun(opts?: table)
---@field marks_buffer fun(opts?: table)
---@field marks_global fun(opts?: table)
---@field marks_all fun(opts?: table)
---@alias PeekstackPeekCallable fun(provider: string, opts?: table)
--- `M.peek` is a callable table: call `M.peek("provider", opts)` directly,
--- or use convenience shortcuts like `M.peek.definition(opts)`.
---@type PeekstackPeekMethods|PeekstackPeekCallable
M.peek = setmetatable({
definition = function(opts)
return peek_by_provider("lsp.definition", opts)
end,
implementation = function(opts)
return peek_by_provider("lsp.implementation", opts)
end,
references = function(opts)
return peek_by_provider("lsp.references", opts)
end,
type_definition = function(opts)
return peek_by_provider("lsp.type_definition", opts)
end,
declaration = function(opts)
return peek_by_provider("lsp.declaration", opts)
end,
symbols_document = function(opts)
return peek_by_provider("lsp.symbols_document", opts)
end,
diagnostics_cursor = function(opts)
return peek_by_provider("diagnostics.under_cursor", opts)
end,
diagnostics_buffer = function(opts)
return peek_by_provider("diagnostics.in_buffer", opts)
end,
file_under_cursor = function(opts)
return peek_by_provider("file.under_cursor", opts)
end,
grep = function(opts)
return peek_by_provider("grep.search", opts)
end,
marks_buffer = function(opts)
return peek_by_provider("marks.buffer", opts)
end,
marks_global = function(opts)
return peek_by_provider("marks.global", opts)
end,
marks_all = function(opts)
return peek_by_provider("marks.all", opts)
end,
}, {
__call = function(_, provider, opts)
return peek_by_provider(provider, opts)
end,
})
---@param opts? table
function M.setup(opts)
local config = require("peekstack.config")
local events = require("peekstack.core.events")
local commands = require("peekstack.commands")
local stack_view = require("peekstack.ui.stack_view")
local persist_auto = require("peekstack.persist.auto")
providers = {}
pickers = {}
config.setup(opts)
set_hl()
events.setup()
commands.setup()
stack_view.setup()
local cfg = config.get()
M.register_picker("builtin", require("peekstack.picker.builtin"))
local backend = cfg.picker.backend
if backend ~= "builtin" then
local picker_map = {
telescope = "peekstack.picker.telescope",
["fzf-lua"] = "peekstack.picker.fzf_lua",
snacks = "peekstack.picker.snacks",
}
local mod_name = picker_map[backend]
if mod_name then
local ok, picker_mod = pcall(require, mod_name)
if ok then
M.register_picker(backend, picker_mod)
end
end
end
if cfg.providers.lsp.enable then
local lsp_provider = require("peekstack.providers.lsp")
register_providers("lsp.", lsp_provider, {
"definition",
"implementation",
"references",
"type_definition",
"declaration",
"symbols_document",
})
end
if cfg.providers.diagnostics.enable then
local diag_provider = require("peekstack.providers.diagnostics")
register_providers("diagnostics.", diag_provider, {
"under_cursor",
"in_buffer",
})
end
if cfg.providers.file.enable then
local file_provider = require("peekstack.providers.file")
register_providers("file.", file_provider, { "under_cursor" })
end
local grep_provider = require("peekstack.providers.grep")
register_providers("grep.", grep_provider, { "search" })
if cfg.providers.marks.enable then
local marks_provider = require("peekstack.providers.marks")
register_providers("marks.", marks_provider, { "buffer", "global", "all" })
end
persist_auto.setup()
end
---Proxy table for `peekstack.core.stack`.
---@type table
M.stack = setmetatable({}, {
__index = function(_, k)
return require("peekstack.core.stack")[k]
end,
})
---Proxy table for `peekstack.persist`.
---@type table
M.persist = setmetatable({}, {
__index = function(_, k)
return require("peekstack.persist")[k]
end,
})
---Proxy table for `peekstack.extensions`.
---@type table
M.extensions = setmetatable({}, {
__index = function(_, k)
return require("peekstack.extensions")[k]
end,
})
return M