-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathconfigs.lua
478 lines (469 loc) · 13.7 KB
/
configs.lua
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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
local utils = require('plugin.winbar.utils')
local icons = utils.static.icons
local M = {}
---@class winbar_configs_t
M.opts = {
icons = {
kinds = {
symbols = icons.kinds,
},
ui = {
bar = {
separator = vim.g.has_nf and icons.ui.AngleRight or ' > ',
extends = vim.opt.listchars:get().extends
or vim.trim(icons.ui.Ellipsis),
},
menu = {
separator = ' ',
indicator = icons.ui.AngleRight,
},
},
},
symbol = {
---@type fun(symbol: winbar_symbol_t, min_width: integer?, n_clicks: integer?, button: string?, modifiers: string?)|false?
on_click = function(symbol)
-- Update current context highlights if the symbol
-- is shown inside a menu
if symbol.entry and symbol.entry.menu then
symbol.entry.menu:update_current_context_hl(symbol.entry.idx)
elseif symbol.bar then
symbol.bar:update_current_context_hl(symbol.bar_idx)
end
-- Determine menu configs
local prev_win = nil ---@type integer?
local entries_source = nil ---@type winbar_symbol_t[]?
local init_cursor = nil ---@type integer[]?
local win_configs = {}
if symbol.bar then -- If symbol inside a winbar
prev_win = symbol.bar.win
entries_source = symbol.opts.siblings
init_cursor = symbol.opts.sibling_idx
and { symbol.opts.sibling_idx, 0 }
if symbol.bar.in_pick_mode then
---@param tbl number[]
local function tbl_sum(tbl)
local sum = 0
for _, v in ipairs(tbl) do
sum = sum + v
end
return sum
end
win_configs.relative = 'win'
win_configs.win = vim.api.nvim_get_current_win()
win_configs.row = 0
win_configs.col = symbol.bar.padding.left
+ tbl_sum(vim.tbl_map(
function(component)
return component:displaywidth()
+ symbol.bar.separator:displaywidth()
end,
vim.tbl_filter(function(component)
return component.bar_idx < symbol.bar_idx
end, symbol.bar.components)
))
end
elseif symbol.entry and symbol.entry.menu then -- If inside a menu
prev_win = symbol.entry.menu.win
entries_source = symbol.opts.children
end
-- Toggle existing menu
if symbol.menu then
symbol.menu:toggle({
prev_win = prev_win,
win_configs = win_configs,
})
return
end
-- Create a new menu for the symbol
if not entries_source or vim.tbl_isempty(entries_source) then
return
end
local menu = require('plugin.winbar.menu')
local configs = require('plugin.winbar.configs')
symbol.menu = menu.winbar_menu_t:new({
prev_win = prev_win,
cursor = init_cursor,
win_configs = win_configs,
---@param sym winbar_symbol_t
entries = vim.tbl_map(function(sym)
local menu_indicator_icon = configs.opts.icons.ui.menu.indicator
local menu_indicator_on_click = nil
if not sym.children or vim.tbl_isempty(sym.children) then
menu_indicator_icon =
string.rep(' ', vim.fn.strdisplaywidth(menu_indicator_icon))
menu_indicator_on_click = false
end
return menu.winbar_menu_entry_t:new({
components = {
sym:merge({
name = '',
icon = menu_indicator_icon,
icon_hl = 'winbarIconUIIndicator',
on_click = menu_indicator_on_click,
}),
sym:merge({
on_click = function()
local root_menu = symbol.menu and symbol.menu:root()
if root_menu then
root_menu:close(false)
end
sym:jump()
end,
}),
},
})
end, entries_source),
})
symbol.menu:toggle()
end,
preview = {
---Reorient the preview window on previewing a new symbol
---@param win integer source window id
---@param range { start: { line: integer }, end: { line: integer } } 0-indexed
---@diagnostic disable-next-line: unused-local
reorient = function(win, range) end, -- luacheck: ignore 212
},
jump = {
---@param win integer source window id
---@param range { start: { line: integer }, end: { line: integer } } 0-indexed
---@diagnostic disable-next-line: unused-local
reorient = function(win, range) end, -- luacheck: ignore 212
},
},
bar = {
---@type boolean|fun(buf: integer, win: integer): boolean
enable = function(buf, win)
local bt = vim.bo[buf].bt
local ft = vim.bo[buf].ft
return not vim.w[win].winbar_no_attach
and not vim.b[buf].winbar_no_attach
and vim.fn.win_gettype(win) == ''
and bt ~= 'terminal'
and bt ~= 'quickfix'
and bt ~= 'prompt'
and ft ~= 'query'
and ft ~= 'help'
and ft ~= 'diff'
and not vim.startswith(ft, 'git')
and not utils.opt.winbar:was_locally_set({ win = 0 })
and (
ft == 'markdown'
or utils.ts.is_active(buf)
or not vim.tbl_isempty(vim.lsp.get_clients({
bufnr = buf,
method = 'textDocument/documentSymbol',
}))
)
end,
attach_events = {
'BufEnter',
'BufWinEnter',
'BufWritePost',
},
-- Wait for a short time before updating the winbar, if another update
-- request is received within this time, the previous request will be
-- cancelled, this improves the performance when the user is holding
-- down a key (e.g. 'j') to scroll the window
update_debounce = 16,
update_events = {
win = {
'CursorMoved',
'WinResized',
},
buf = {
'BufModifiedSet',
'FileChangedShellPost',
'TextChanged',
'InsertLeave',
},
global = {
'DirChanged',
'VimResized',
},
},
hover = true,
---@type winbar_source_t[]|fun(buf: integer, win: integer): winbar_source_t[]
sources = function(buf)
local sources = require('plugin.winbar.sources')
return vim.bo[buf].ft == 'markdown' and { sources.markdown }
or {
utils.source.fallback({
sources.lsp,
sources.treesitter,
}),
}
end,
padding = {
left = 1,
right = 1,
},
pick = {
pivots = 'abcdefghijklmnopqrstuvwxyz',
},
},
menu = {
-- When on, preview the symbol in the source window
preview = true,
hover = true,
-- When on, set the cursor to the closest clickable component
-- on CursorMoved
quick_navigation = true,
-- Menu scrollbar options
scrollbar = {
enable = true,
},
entry = {
padding = {
left = 1,
right = 1,
},
},
---@type table<string, string|function|table<string, string|function>>
keymaps = {
['q'] = function()
local menu = utils.menu.get_current()
if menu then
menu:close()
end
end,
['<LeftMouse>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
local clicked_menu = utils.menu.get({ win = mouse.winid })
-- If clicked on a menu, invoke the corresponding click action,
-- else close all menus and set the cursor to the clicked window
if clicked_menu then
clicked_menu:click_at({ mouse.line, mouse.column - 1 }, nil, 1, 'l')
return
end
utils.menu.exec('close')
utils.bar.exec('update_current_context_hl')
if vim.api.nvim_win_is_valid(mouse.winid) then
vim.api.nvim_set_current_win(mouse.winid)
end
end,
['<CR>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local cursor = vim.api.nvim_win_get_cursor(menu.win)
local component = menu.entries[cursor[1]]:first_clickable(cursor[2])
if component then
menu:click_on(component, nil, 1, 'l')
end
end,
['<MouseMove>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
if M.opts.menu.hover then
utils.menu.update_hover_hl(mouse)
end
if M.opts.menu.preview then
utils.menu.update_preview(mouse)
end
end,
},
---@alias winbar_menu_win_config_opts_t any|fun(menu: winbar_menu_t):any
---@type table<string, winbar_menu_win_config_opts_t>
---@see vim.api.nvim_open_win
win_configs = {
border = 'none',
style = 'minimal',
relative = 'win',
win = function(menu)
return menu.prev_menu and menu.prev_menu.win
or vim.fn.getmousepos().winid
end,
row = function(menu)
return menu.prev_menu
and menu.prev_menu.clicked_at
and menu.prev_menu.clicked_at[1] - vim.fn.line('w0')
or 0
end,
---@param menu winbar_menu_t
col = function(menu)
if menu.prev_menu then
return menu.prev_menu._win_configs.width
+ (menu.prev_menu.scrollbar and 1 or 0)
end
local mouse = vim.fn.getmousepos()
local bar = utils.bar.get({ win = menu.prev_win })
if not bar then
return mouse.wincol
end
local _, range = bar:get_component_at(math.max(0, mouse.wincol - 1))
return range and range.start or mouse.wincol
end,
height = function(menu)
return math.max(
1,
math.min(
#menu.entries,
vim.go.pumheight ~= 0 and vim.go.pumheight
or math.ceil(vim.go.lines / 4)
)
)
end,
width = function(menu)
local min_width = vim.go.pumwidth ~= 0 and vim.go.pumwidth or 8
if vim.tbl_isempty(menu.entries) then
return min_width
end
return math.max(
min_width,
math.max(unpack(vim.tbl_map(function(entry)
return entry:displaywidth()
end, menu.entries)))
)
end,
zindex = function(menu)
if menu.prev_menu then
if menu.prev_menu.scrollbar and menu.prev_menu.scrollbar.thumb then
return vim.api.nvim_win_get_config(menu.prev_menu.scrollbar.thumb).zindex
end
return vim.api.nvim_win_get_config(menu.prev_win).zindex
end
end,
},
},
sources = {
treesitter = {
max_depth = 12,
-- Vim regex used to extract a short name from the node text
-- word with optional prefix and suffix: [#~!@\*&.]*[[:keyword:]]\+!\?
-- word separators: \(->\)\+\|-\+\|\.\+\|:\+\|\s\+
name_regex = [=[[#~!@\*&.]*[[:keyword:]]\+!\?]=]
.. [=[\(\(\(->\)\+\|-\+\|\.\+\|:\+\|\s\+\)\?[#~!@\*&.]*[[:keyword:]]\+!\?\)*]=],
-- The order matters! The first match is used as the type
-- of the treesitter symbol and used to show the icon
-- Types listed below must have corresponding icons
-- in the `icons.kinds.symbols` table for the icon to be shown
valid_types = {
'break_statement',
'call',
'case_statement',
'class',
'constant',
'constructor',
'continue_statement',
'delete',
'do_statement',
'element',
'enum',
'enum_member',
'event',
'for_statement',
'function',
'h1_marker',
'h2_marker',
'h3_marker',
'h4_marker',
'h5_marker',
'h6_marker',
'if_statement',
'interface',
'keyword',
'macro',
'method',
'namespace',
'null',
'operator',
'package',
'pair',
'property',
'reference',
'repeat',
'rule_set',
'scope',
'specifier',
'struct',
'switch_statement',
'table',
'type',
'type_parameter',
'unit',
'value',
'variable',
'while_statement',
'declaration',
'field',
'identifier',
'object',
'statement',
-- 'boolean',
-- 'module',
-- 'number',
-- 'text',
-- 'string',
-- 'array',
-- 'list',
},
},
lsp = {
max_depth = 12,
valid_symbols = {
'File',
'Module',
'Namespace',
'Package',
'Class',
'Method',
'Property',
'Field',
'Constructor',
'Enum',
'Interface',
'Function',
'Variable',
'Constant',
'String',
'Number',
'Boolean',
'Array',
'Object',
'Keyword',
'Null',
'EnumMember',
'Struct',
'Event',
'Operator',
'TypeParameter',
},
request = {
-- Times to retry a request before giving up
ttl_init = 60,
interval = 1000, -- in ms
},
},
markdown = {
max_depth = 6,
parse = {
-- Number of lines to update when cursor moves out of the parsed range
look_ahead = 200,
},
},
},
}
---Set winbar options
---@param new_opts winbar_configs_t?
function M.set(new_opts)
M.opts = vim.tbl_deep_extend('force', M.opts, new_opts or {})
end
---Evaluate a dynamic option value (with type T|fun(...): T)
---@generic T
---@param opt T|fun(...): T
---@return T
function M.eval(opt, ...)
if opt and vim.is_callable(opt) then
return opt(...)
end
return opt
end
return M