From 46536f53e82967dcac8d030ee3394cdb156f9603 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 20 Apr 2022 17:02:18 +0600 Subject: feat: add preview functionality to user commands Adds a Lua-only `preview` flag to user commands which allows the command to be incrementally previewed like `:substitute` when 'inccommand' is set. --- runtime/doc/api.txt | 2 + runtime/doc/map.txt | 106 +++++++++++++++++++++++++++++++++++++++++++++++ runtime/doc/options.txt | 10 +++-- runtime/doc/vim_diff.txt | 2 + 4 files changed, 116 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 965b8e6492..6c8c35486e 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -760,6 +760,8 @@ nvim_create_user_command({name}, {command}, {*opts}) when a Lua function is used for {command}. • force: (boolean, default true) Override any previous definition. + • preview: (function) Preview callback for + 'inccommand' |:command-preview| nvim_del_current_line() *nvim_del_current_line()* Deletes the current line. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 98da68b76a..9776304c8e 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1430,6 +1430,112 @@ Possible values are (second column is the short name used in listing): -addr=other ? other kind of range +Incremental preview ~ + *:command-preview* {nvim-api} +Commands can show an 'inccommand' (as-you-type) preview by defining a preview +handler (only from Lua, see |nvim_create_user_command()|). + +The preview callback must be a Lua function with this signature: > + + function cmdpreview(opts, ns, buf) +< +where "opts" has the same form as that given to |nvim_create_user_command()| +callbacks, "ns" is the preview namespace id for highlights, and "buf" is the +buffer that your preview routine will directly modify to show the previewed +results (for "inccommand=split", or nil for "inccommand=nosplit"). + +Your command preview routine must implement this protocol: + +1. Modify the current buffer as required for the preview (see + |nvim_buf_set_text()| and |nvim_buf_set_lines()|). +2. If preview buffer is provided, add necessary text to the preview buffer. +3. Add required highlights to the current buffer. If preview buffer is + provided, add required highlights to the preview buffer as well. All + highlights must be added to the preview namespace which is provided as an + argument to the preview callback (see |nvim_buf_add_highlight()| and + |nvim_buf_set_extmark()| for help on how to add highlights to a namespace). +4. Return an integer (0, 1, 2) which controls how Nvim behaves as follows: + 0: No preview is shown. + 1: Preview is shown without preview window (even with "inccommand=split"). + 2: Preview is shown and preview window is opened (if "inccommand=split"). + For "inccommand=nosplit" this is the same as 1. + +After preview ends, Nvim discards all changes to the buffer and all highlights +in the preview namespace. + +Here's an example of a command to trim trailing whitespace from lines that +supports incremental command preview: +> + -- Trims trailing whitespace in the current buffer. + -- Also performs 'inccommand' preview if invoked as a preview callback + -- (preview_ns is non-nil). + local function trim_space(opts, preview_ns, preview_buf) + local line1 = opts.line1 + local line2 = opts.line2 + local buf = vim.api.nvim_get_current_buf() + local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, 0) + local new_lines = {} + local preview_buf_line = 0 + + for i, line in ipairs(lines) do + local startidx, endidx = string.find(line, '%s+$') + + if startidx ~= nil then + -- Highlight the match if in command preview mode + if preview_ns ~= nil then + vim.api.nvim_buf_add_highlight( + buf, preview_ns, 'Substitute', line1 + i - 2, startidx - 1, + endidx + ) + + -- Add lines and highlight to the preview buffer + -- if inccommand=split + if preview_buf ~= nil then + local prefix = string.format('|%d| ', line1 + i - 1) + + vim.api.nvim_buf_set_lines( + preview_buf, preview_buf_line, preview_buf_line, 0, + { prefix .. line } + ) + vim.api.nvim_buf_add_highlight( + preview_buf, preview_ns, 'Substitute', preview_buf_line, + #prefix + startidx - 1, #prefix + endidx + ) + + preview_buf_line = preview_buf_line + 1 + end + end + end + + if not preview_ns then + new_lines[#new_lines+1] = string.gsub(line, '%s+$', '') + end + end + + -- Don't make any changes to the buffer if previewing + if not preview_ns then + vim.api.nvim_buf_set_lines(buf, line1 - 1, line2, 0, new_lines) + end + + -- When called as a preview callback, return the value of the + -- preview type + if preview_ns ~= nil then + return 2 + end + end + + -- Create the user command + vim.api.nvim_create_user_command( + 'TrimTrailingWhitespace', + trim_space, + { nargs = '?', range = '%', addr = 'lines', preview = trim_space } + ) +< +Note that in the above example, the same function is used as both the command +callback and the preview callback, but you could instead use separate +functions. + + Special cases ~ *:command-bang* *:command-bar* *:command-register* *:command-buffer* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index a21d3bbce7..eebbc3f73a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3266,8 +3266,9 @@ A jump table for the options with a short description can be found at |Q_op|. 'inccommand' 'icm' string (default "nosplit") global - When nonempty, shows the effects of |:substitute|, |:smagic|, and - |:snomagic| as you type. + When nonempty, shows the effects of |:substitute|, |:smagic|, + |:snomagic| and user commands with the |:command-preview| flag as you + type. Possible values: nosplit Shows the effects of a command incrementally in the @@ -3275,8 +3276,9 @@ A jump table for the options with a short description can be found at |Q_op|. split Like "nosplit", but also shows partial off-screen results in a preview window. - If the preview is too slow (exceeds 'redrawtime') then 'inccommand' is - automatically disabled until |Command-line-mode| is done. + If the preview for built-in commands is too slow (exceeds + 'redrawtime') then 'inccommand' is automatically disabled until + |Command-line-mode| is done. *'include'* *'inc'* 'include' 'inc' string (default "^\s*#\s*include") diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index c079b83c29..8e67cb0923 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -183,6 +183,7 @@ Commands: |:sign-define| accepts a `numhl` argument, to highlight the line number |:match| can be invoked before highlight group is defined |:source| works with Lua and anonymous (no file) scripts + User commands can support |:command-preview| to show results as you type Events: |RecordingEnter| @@ -235,6 +236,7 @@ Options: "horizdown", "vertleft", "vertright", "verthoriz" 'foldcolumn' supports up to 9 dynamic/fixed columns 'inccommand' shows interactive results for |:substitute|-like commands + and |:command-preview| commands 'laststatus' global statusline support 'pumblend' pseudo-transparent popupmenu 'scrollback' -- cgit