Skip to content

Commit 92cbbd8

Browse files
committed
feat(cmdline): add config.autopeek.predicate
1 parent 10b1cf2 commit 92cbbd8

File tree

4 files changed

+221
-20
lines changed

4 files changed

+221
-20
lines changed

doc/mini-cmdline.txt

Lines changed: 31 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,20 @@ 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+
338+
Parameters ~
339+
{data} `(table)` Input autopeek data. As described in |MiniCmdline.config|.
340+
{opts} `(table|nil)` Options. Reserved for future use.
341+
342+
Return ~
343+
`(boolean)` If command defines |:command-preview| - `false`, otherwise - `true`.
344+
This makes autopeek easier to use for commands like |:substitute|,
345+
especially if |'inccommand'| is set to `split`.
346+
319347
------------------------------------------------------------------------------
320348
*MiniCmdline.default_autopeek_statuscolumn()*
321349
`MiniCmdline.default_autopeek_statuscolumn`({data}, {opts})

lua/mini/cmdline.lua

Lines changed: 69 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,19 @@ 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+
---
367+
---@param data table Input autopeek data. As described in |MiniCmdline.config|.
368+
---@param opts table|nil Options. Reserved for future use.
369+
---
370+
---@return boolean If command defines |:command-preview| - `false`, otherwise - `true`.
371+
--- This makes autopeek easier to use for commands like |:substitute|,
372+
--- especially if |'inccommand'| is set to `split`.
373+
MiniCmdline.default_autopeek_predicate = function(data, opts)
374+
local cmd_preview_map = H.cache.cmd_preview_map or H.get_cmd_preview_map()
375+
return cmd_preview_map[data.cmd] ~= true
376+
end
377+
351378
--- Default autopeek statuscolumn
352379
---
353380
--- - Show signs next to lines depending on their relation to peeked range.
@@ -493,6 +520,7 @@ H.setup_config = function(config)
493520
H.check_type('autopeek', config.autopeek, 'table')
494521
H.check_type('autopeek.enable', config.autopeek.enable, 'boolean')
495522
H.check_type('autopeek.n_context', config.autopeek.n_context, 'number')
523+
H.check_type('autopeek.predicate', config.autopeek.predicate, 'callable', true)
496524
H.check_type('autopeek.window', config.autopeek.window, 'table')
497525
local autopeek_win_config = config.autopeek.window.config
498526
if not (type(autopeek_win_config) == 'table' or vim.is_callable(autopeek_win_config)) then
@@ -588,6 +616,7 @@ H.on_cmdline_enter = function()
588616

589617
H.cache = {
590618
buf_id = vim.api.nvim_get_current_buf(),
619+
cmd_preview_map = H.get_cmd_preview_map(),
591620
cmd_type = vim.fn.getcmdtype(),
592621
config = H.get_config(),
593622
peek = {},
@@ -597,6 +626,7 @@ H.on_cmdline_enter = function()
597626
H.cache.autocomplete_predicate = H.cache.config.autocomplete.predicate or MiniCmdline.default_autocomplete_predicate
598627
H.cache.buf_is_cmdwin = vim.fn.getbufinfo(H.cache.buf_id)[1].command == 1
599628

629+
H.cache.autopeek_predicate = H.cache.config.autopeek.predicate or MiniCmdline.default_autopeek_predicate
600630
MiniCmdline._peek_statuscolumn = H.make_peek_statuscolumn()
601631
if H.cache.config.autopeek.enable then H.autopeek() end
602632
end
@@ -901,26 +931,33 @@ H.autopeek = function(force)
901931
local line = H.cache.state.line
902932
if line:find('%S') == nil then H.peek_hide() end
903933

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

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
938+
-- Normalize range lines
915939
local n_lines = vim.api.nvim_buf_line_count(0)
916940
local left = H.clamp(range[1] or range[2], 1, n_lines)
917941
local right = H.clamp(range[2] or range[1], 1, n_lines)
918942
local from = right < left and right or left
919943
local to = right < left and left or right
944+
local data = { left = left, right = right, cmd = parsed.cmd }
945+
946+
-- Do not peek if predicate says so
947+
if not H.cache.autopeek_predicate(data) then return H.peek_hide() end
948+
949+
-- Force peek update if command line height has changed when typing
950+
local cmdheight = math.ceil((vim.fn.strdisplaywidth(line) + 1) / vim.o.columns)
951+
cmdheight = math.max(cmdheight, vim.o.cmdheight)
952+
force = force or cmdheight ~= H.cache.peek.cmdheight
953+
954+
-- Skip peek update if command line state is the same (for performance)
955+
local cur_data = H.cache.peek.data or {}
956+
local is_data_same = left == cur_data.left and right == cur_data.right and cmd == cur_data.cmd
957+
if not force and is_data_same then return end
920958

921-
H.cache.peek.range = range
922959
H.cache.peek.cmdheight = cmdheight
923-
H.cache.peek.statuscolumn_data = { left = left, right = right }
960+
H.cache.peek.data = data
924961
H.cache.peek.win_id = H.peek_show(from, to)
925962
end
926963

@@ -1051,7 +1088,7 @@ end
10511088

10521089
H.make_peek_statuscolumn = function()
10531090
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
1091+
return function() return statuscolumn(H.cache.peek.data) or '' end
10551092
end
10561093

10571094
-- Utilities ------------------------------------------------------------------
@@ -1079,8 +1116,21 @@ H.fit_to_width = function(text, width)
10791116
return t_width <= width and text or ('' .. vim.fn.strcharpart(text, t_width - width + 1, width - 1))
10801117
end
10811118

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

10851135
-- Try extra parsing to have a result for some edge cases
10861136
-- - Line with only range
@@ -1097,7 +1147,9 @@ H.parse_cmd_range = function(line)
10971147
end
10981148

10991149
-- Treat `range` only as a "line range"
1100-
return (ok and parsed.addr == 'line') and parsed.range or {}
1150+
local range = (ok and parsed.addr == 'line') and parsed.range or {}
1151+
local cmd = needs_reparse and '' or parsed.cmd
1152+
return { range = range, cmd = cmd }
11011153
end
11021154

11031155
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)