Skip to content

Commit 6d003e3

Browse files
committed
feat(cmdline): add config.autopeek.predicate
1 parent 80e7dd1 commit 6d003e3

File tree

4 files changed

+218
-20
lines changed

4 files changed

+218
-20
lines changed

doc/mini-cmdline.txt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ Defaults ~
130130
-- Number of lines to show above and below range lines
131131
n_context = 1,
132132

133+
-- Custom rule of when to show peek window
134+
predicate = nil,
135+
133136
-- Window options
134137
window = {
135138
-- Floating window config
@@ -229,6 +232,19 @@ left) range lines.
229232
The range itself is visualized by default with the statuscolumn signs.
230233
Default: 1.
231234

235+
`autopeek.predicate` defines a condition of whether to show peek window at
236+
the current command line state. Takes a table with input data and should
237+
return `true` to peek and `false` otherwise.
238+
Will be called only if it is possible to parse range from the current command
239+
line text and it is for buffer lines (no command or |:command-addr| is `lines`)
240+
Default: |MiniCmdline.default_autopeek_predicate()|.
241+
242+
Input data fields:
243+
- <left> `(number)` - left range edge. Not necessarily smallest.
244+
- <right> `(number)` - right range edge. Same as `left` for a single line range.
245+
- <cmd> `(string)` - full command name. Can be empty string if no valid
246+
command is (yet) entered.
247+
232248
`autopeek.window` defines behavior of a peek window.
233249
`autopeek.window.config` is a table defining floating window characteristics
234250
or a callable returning such table.
@@ -238,9 +254,7 @@ It should have the same structure as in |nvim_open_win()|.
238254
customize |'statuscolumn'| value for the peek window. Takes a table with input
239255
data and should return a string to display for line |v:lnum|.
240256
Default: |MiniCmdline.default_autopeek_statuscolumn()|.
241-
Input data fields:
242-
- <left> `(number)` - left range edge. Not necessarily smallest.
243-
- <right> `(number)` - right range edge. Same as `left` for a single line range.
257+
Input data fields are the same as for `autopeek.predicate`.
244258

245259
Example of showing `<` and `>` signs on range lines: >lua
246260

@@ -316,6 +330,19 @@ Parameters ~
316330
Return ~
317331
`(string)` Autocorrected word.
318332

333+
------------------------------------------------------------------------------
334+
*MiniCmdline.default_autopeek_predicate()*
335+
`MiniCmdline.default_autopeek_predicate`({data}, {opts})
336+
Default autopeek predicate
337+
Parameters ~
338+
{data} `(table)` Input autopeek data. As described in |MiniCmdline.config|.
339+
{opts} `(table|nil)` Options. Reserved for future use.
340+
341+
Return ~
342+
`(boolean)` If command itself defines |:command-preview| - `false`,
343+
otherwise - `true`. This makes autopeek easier to use for commands
344+
like |:substitute|, especially if |'inccommand'| is set to `split`.
345+
319346
------------------------------------------------------------------------------
320347
*MiniCmdline.default_autopeek_statuscolumn()*
321348
`MiniCmdline.default_autopeek_statuscolumn`({data}, {opts})

lua/mini/cmdline.lua

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,19 @@ end
203203
--- The range itself is visualized by default with the statuscolumn signs.
204204
--- Default: 1.
205205
---
206+
--- `autopeek.predicate` defines a condition of whether to show peek window at
207+
--- the current command line state. Takes a table with input data and should
208+
--- return `true` to peek and `false` otherwise.
209+
--- Will be called only if it is possible to parse range from the current command
210+
--- line text and it is for buffer lines (no command or |:command-addr| is `lines`)
211+
--- Default: |MiniCmdline.default_autopeek_predicate()|.
212+
---
213+
--- Input data fields:
214+
--- - <left> `(number)` - left range edge. Not necessarily smallest.
215+
--- - <right> `(number)` - right range edge. Same as `left` for a single line range.
216+
--- - <cmd> `(string)` - full command name. Can be empty string if no valid
217+
--- command is (yet) entered.
218+
---
206219
--- `autopeek.window` defines behavior of a peek window.
207220
--- `autopeek.window.config` is a table defining floating window characteristics
208221
--- or a callable returning such table.
@@ -212,9 +225,7 @@ end
212225
--- customize |'statuscolumn'| value for the peek window. Takes a table with input
213226
--- data and should return a string to display for line |v:lnum|.
214227
--- Default: |MiniCmdline.default_autopeek_statuscolumn()|.
215-
--- Input data fields:
216-
--- - <left> `(number)` - left range edge. Not necessarily smallest.
217-
--- - <right> `(number)` - right range edge. Same as `left` for a single line range.
228+
--- Input data fields are the same as for `autopeek.predicate`.
218229
---
219230
--- Example of showing `<` and `>` signs on range lines: >lua
220231
---
@@ -274,6 +285,9 @@ MiniCmdline.config = {
274285
-- Number of lines to show above and below range lines
275286
n_context = 1,
276287

288+
-- Custom rule of when to show peek window
289+
predicate = nil,
290+
277291
-- Window options
278292
window = {
279293
-- Floating window config
@@ -348,6 +362,18 @@ MiniCmdline.default_autocorrect_func = function(data, opts)
348362
return H.get_nearest_abbr(data.word, all, abbr_lens)
349363
end
350364

365+
--- Default autopeek predicate
366+
---@param data table Input autopeek data. As described in |MiniCmdline.config|.
367+
---@param opts table|nil Options. Reserved for future use.
368+
---
369+
---@return boolean If command itself defines |:command-preview| - `false`,
370+
--- otherwise - `true`. This makes autopeek easier to use for commands
371+
--- like |:substitute|, especially if |'inccommand'| is set to `split`.
372+
MiniCmdline.default_autopeek_predicate = function(data, opts)
373+
local cmd_preview_map = H.cache.cmd_preview_map or H.get_cmd_preview_map()
374+
return cmd_preview_map[data.cmd] ~= true
375+
end
376+
351377
--- Default autopeek statuscolumn
352378
---
353379
--- - Show signs next to lines depending on their relation to peeked range.
@@ -493,6 +519,7 @@ H.setup_config = function(config)
493519
H.check_type('autopeek', config.autopeek, 'table')
494520
H.check_type('autopeek.enable', config.autopeek.enable, 'boolean')
495521
H.check_type('autopeek.n_context', config.autopeek.n_context, 'number')
522+
H.check_type('autopeek.predicate', config.autopeek.predicate, 'callable', true)
496523
H.check_type('autopeek.window', config.autopeek.window, 'table')
497524
local autopeek_win_config = config.autopeek.window.config
498525
if not (type(autopeek_win_config) == 'table' or vim.is_callable(autopeek_win_config)) then
@@ -588,6 +615,7 @@ H.on_cmdline_enter = function()
588615

589616
H.cache = {
590617
buf_id = vim.api.nvim_get_current_buf(),
618+
cmd_preview_map = H.get_cmd_preview_map(),
591619
cmd_type = vim.fn.getcmdtype(),
592620
config = H.get_config(),
593621
peek = {},
@@ -597,6 +625,7 @@ H.on_cmdline_enter = function()
597625
H.cache.autocomplete_predicate = H.cache.config.autocomplete.predicate or MiniCmdline.default_autocomplete_predicate
598626
H.cache.buf_is_cmdwin = vim.fn.getbufinfo(H.cache.buf_id)[1].command == 1
599627

628+
H.cache.autopeek_predicate = H.cache.config.autopeek.predicate or MiniCmdline.default_autopeek_predicate
600629
MiniCmdline._peek_statuscolumn = H.make_peek_statuscolumn()
601630
if H.cache.config.autopeek.enable then H.autopeek() end
602631
end
@@ -901,26 +930,33 @@ H.autopeek = function(force)
901930
local line = H.cache.state.line
902931
if line:find('%S') == nil then H.peek_hide() end
903932

904-
local range = H.parse_cmd_range(line)
933+
local parsed = H.parse_cmd(line)
934+
local range, cmd = parsed.range, parsed.cmd
905935
if range[1] == nil and range[2] == nil then return H.peek_hide() end
906936

907-
-- NOTE: Force peek update if command line height has changed when typing
908-
local cmdheight = math.ceil((vim.fn.strdisplaywidth(line) + 1) / vim.o.columns)
909-
cmdheight = math.max(cmdheight, vim.o.cmdheight)
910-
force = force or cmdheight ~= H.cache.peek.cmdheight
911-
local cur_range = H.cache.peek.range or {}
912-
if not force and range[1] == cur_range[1] and range[2] == cur_range[2] then return end
913-
914-
-- Normalize and show range
937+
-- Normalize range lines
915938
local n_lines = vim.api.nvim_buf_line_count(0)
916939
local left = H.clamp(range[1] or range[2], 1, n_lines)
917940
local right = H.clamp(range[2] or range[1], 1, n_lines)
918941
local from = right < left and right or left
919942
local to = right < left and left or right
943+
local data = { left = left, right = right, cmd = parsed.cmd }
944+
945+
-- Do not peek if predicate says so
946+
if not H.cache.autopeek_predicate(data) then return H.peek_hide() end
947+
948+
-- Force peek update if command line height has changed when typing
949+
local cmdheight = math.ceil((vim.fn.strdisplaywidth(line) + 1) / vim.o.columns)
950+
cmdheight = math.max(cmdheight, vim.o.cmdheight)
951+
force = force or cmdheight ~= H.cache.peek.cmdheight
952+
953+
-- Skip peek update if command line state is the same
954+
local cur_data = H.cache.peek.data or {}
955+
local is_data_same = left == cur_data.left and right == cur_data.right and cmd == cur_data.cmd
956+
if not force and is_data_same then return end
920957

921-
H.cache.peek.range = range
922958
H.cache.peek.cmdheight = cmdheight
923-
H.cache.peek.statuscolumn_data = { left = left, right = right }
959+
H.cache.peek.data = data
924960
H.cache.peek.win_id = H.peek_show(from, to)
925961
end
926962

@@ -1051,7 +1087,7 @@ end
10511087

10521088
H.make_peek_statuscolumn = function()
10531089
local statuscolumn = H.cache.config.autopeek.window.statuscolumn or MiniCmdline.default_autopeek_statuscolumn
1054-
return function() return statuscolumn(H.cache.peek.statuscolumn_data) or '' end
1090+
return function() return statuscolumn(H.cache.peek.data) or '' end
10551091
end
10561092

10571093
-- Utilities ------------------------------------------------------------------
@@ -1079,8 +1115,21 @@ H.fit_to_width = function(text, width)
10791115
return t_width <= width and text or ('' .. vim.fn.strcharpart(text, t_width - width + 1, width - 1))
10801116
end
10811117

1082-
H.parse_cmd_range = function(line)
1118+
H.get_cmd_preview_map = function()
1119+
local res = { substitute = true, smagic = true, snomagic = true }
1120+
-- NOTE: Check `name` type as on Neovim<0.11 output can be `{ [true] = 6 }`
1121+
for name, data in pairs(vim.api.nvim_get_commands({})) do
1122+
if type(name) == 'string' and data.preview then res[name] = true end
1123+
end
1124+
for name, data in pairs(vim.api.nvim_buf_get_commands(0, {})) do
1125+
if type(name) == 'string' and data.preview then res[name] = true end
1126+
end
1127+
return res
1128+
end
1129+
1130+
H.parse_cmd = function(line)
10831131
local ok, parsed = pcall(vim.api.nvim_parse_cmd, line, {})
1132+
local needs_reparse = not ok
10841133

10851134
-- Try extra parsing to have a result for some edge cases
10861135
-- - Line with only range
@@ -1097,7 +1146,9 @@ H.parse_cmd_range = function(line)
10971146
end
10981147

10991148
-- Treat `range` only as a "line range"
1100-
return (ok and parsed.addr == 'line') and parsed.range or {}
1149+
local range = (ok and parsed.addr == 'line') and parsed.range or {}
1150+
local cmd = needs_reparse and '' or parsed.cmd
1151+
return { range = range, cmd = cmd }
11011152
end
11021153

11031154
H.getcmdcomplpat = function() return vim.fn.getcmdcomplpat() end

readmes/mini-cmdline.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ Here are code snippets for some common installation methods (use only one):
148148
-- Number of lines to show above and below range lines
149149
n_context = 1,
150150

151+
-- Custom rule of when to show peek window
152+
predicate = nil,
153+
151154
-- Window options
152155
window = {
153156
-- Floating window config

0 commit comments

Comments
 (0)