diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-05-31 17:44:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-31 17:44:13 +0200 |
commit | 7380ebfc17723662f6fe1e38372f54b3d67fe082 (patch) | |
tree | e079f85a72f851e5c0a8e08f52db0cb4a8b26163 /test/functional | |
parent | 5d840fa7e6ba7d58a89d3126ee914cb0e42168ca (diff) | |
parent | 46536f53e82967dcac8d030ee3394cdb156f9603 (diff) | |
download | rneovim-7380ebfc17723662f6fe1e38372f54b3d67fe082.tar.gz rneovim-7380ebfc17723662f6fe1e38372f54b3d67fe082.tar.bz2 rneovim-7380ebfc17723662f6fe1e38372f54b3d67fe082.zip |
Merge pull request #18194 from famiu/feat/usercmd_preview
feat: user command "preview" (like inccommand)
Diffstat (limited to 'test/functional')
-rw-r--r-- | test/functional/api/command_spec.lua | 14 | ||||
-rw-r--r-- | test/functional/ui/float_spec.lua | 12 | ||||
-rw-r--r-- | test/functional/ui/inccommand_spec.lua | 36 | ||||
-rw-r--r-- | test/functional/ui/inccommand_user_spec.lua | 329 |
4 files changed, 340 insertions, 51 deletions
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index d6d75e93e4..253371557a 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -16,8 +16,8 @@ local feed = helpers.feed local funcs = helpers.funcs describe('nvim_get_commands', function() - local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, keepscript=false, script_id=0, } - local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, keepscript=false, script_id=0, } + local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', preview=false, range=NIL, register=false, keepscript=false, script_id=0, } + local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', preview=false, range=NIL, register=false, keepscript=false, script_id=0, } before_each(clear) it('gets empty list if no commands were defined', function() @@ -59,11 +59,11 @@ describe('nvim_get_commands', function() end) it('gets various command attributes', function() - local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, keepscript=false, script_id=0, } - local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, keepscript=false, script_id=1, } - local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, keepscript=false, script_id=2, } - local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, } - local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, } + local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', preview=false, range='10', register=false, keepscript=false, script_id=0, } + local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', preview=false, range=NIL, register=false, keepscript=false, script_id=1, } + local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', preview=false, range=NIL, register=false, keepscript=false, script_id=2, } + local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', preview=false, range=NIL, register=false, keepscript=false, script_id=3, } + local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', preview=false, range=NIL, register=true, keepscript=false, script_id=4, } source([[ let s:foo = 1 command -complete=custom,ListUsers -nargs=+ Finger !finger <args> diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index ca5e269f92..fdd1504b13 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -3660,10 +3660,10 @@ describe('float window', function() screen:expect{grid=[[ ## grid 1 [2:----------------------------------------]| - {5:[No Name] }| - [5:----------------------------------------]| - [5:----------------------------------------]| - [5:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| {5:[Preview] }| [3:----------------------------------------]| ## grid 2 @@ -3674,10 +3674,6 @@ describe('float window', function() {17:f}{1:oo }| {17:b}{1:ar }| {1: }| - ## grid 5 - |1| {17:f}oo | - |2| {17:b}ar | - {0:~ }| ]], float_pos=expected_pos} else screen:expect([[ diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index a1ff778da1..a95cb0e83a 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -255,42 +255,6 @@ describe(":substitute, 'inccommand' preserves", function() end) end - for _, case in pairs{"", "split", "nosplit"} do - it("visual selection for non-previewable command (inccommand="..case..") #5888", function() - local screen = Screen.new(30,10) - common_setup(screen, case, default_text) - feed('1G2V') - - feed(':s') - screen:expect([[ - {vis:Inc substitution on} | - t{vis:wo lines} | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :'<,'>s^ | - ]]) - - feed('o') - screen:expect([[ - {vis:Inc substitution on} | - t{vis:wo lines} | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :'<,'>so^ | - ]]) - end) - end - for _, case in ipairs({'', 'split', 'nosplit'}) do it('previous substitute string ~ (inccommand='..case..') #12109', function() local screen = Screen.new(30,10) diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua new file mode 100644 index 0000000000..e2cd82943e --- /dev/null +++ b/test/functional/ui/inccommand_user_spec.lua @@ -0,0 +1,329 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local insert = helpers.insert +local feed = helpers.feed +local command = helpers.command + +-- Implements a :Replace command that works like :substitute. +local setup_replace_cmd = [[ + local function show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches) + -- Find the width taken by the largest line number, used for padding the line numbers + local highest_lnum = math.max(matches[#matches][1], 1) + local highest_lnum_width = math.floor(math.log10(highest_lnum)) + local preview_buf_line = 0 + + vim.g.prevns = preview_ns + vim.g.prevbuf = preview_buf + + for _, match in ipairs(matches) do + local lnum = match[1] + local line_matches = match[2] + local prefix + + if use_preview_win then + prefix = string.format( + '|%s%d| ', + string.rep(' ', highest_lnum_width - math.floor(math.log10(lnum))), + lnum + ) + + vim.api.nvim_buf_set_lines( + preview_buf, + preview_buf_line, + preview_buf_line, + 0, + { prefix .. vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] } + ) + end + + for _, line_match in ipairs(line_matches) do + vim.api.nvim_buf_add_highlight( + buf, + preview_ns, + 'Substitute', + lnum - 1, + line_match[1], + line_match[2] + ) + + if use_preview_win then + vim.api.nvim_buf_add_highlight( + preview_buf, + preview_ns, + 'Substitute', + preview_buf_line, + #prefix + line_match[1], + #prefix + line_match[2] + ) + end + end + + preview_buf_line = preview_buf_line + 1 + end + + if use_preview_win then + return 2 + else + return 1 + end + end + + local function do_replace(opts, preview, preview_ns, preview_buf) + local pat1 = opts.fargs[1] or '' + local pat2 = opts.fargs[2] or '' + 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 matches = {} + + for i, line in ipairs(lines) do + local startidx, endidx = 0, 0 + local line_matches = {} + local num = 1 + + while startidx ~= -1 do + local match = vim.fn.matchstrpos(line, pat1, 0, num) + startidx, endidx = match[2], match[3] + + if startidx ~= -1 then + line_matches[#line_matches+1] = { startidx, endidx } + end + + num = num + 1 + end + + if #line_matches > 0 then + matches[#matches+1] = { line1 + i - 1, line_matches } + end + end + + local new_lines = {} + + for _, match in ipairs(matches) do + local lnum = match[1] + local line_matches = match[2] + local line = lines[lnum - line1 + 1] + local pat_width_differences = {} + + -- If previewing, only replace the text in current buffer if pat2 isn't empty + -- Otherwise, always replace the text + if pat2 ~= '' or not preview then + if preview then + for _, line_match in ipairs(line_matches) do + local startidx, endidx = unpack(line_match) + local pat_match = line:sub(startidx + 1, endidx) + + pat_width_differences[#pat_width_differences+1] = + #vim.fn.substitute(pat_match, pat1, pat2, 'g') - #pat_match + end + end + + new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g') + end + + -- Highlight the matches if previewing + if preview then + local idx_offset = 0 + for i, line_match in ipairs(line_matches) do + local startidx, endidx = unpack(line_match) + -- Starting index of replacement text + local repl_startidx = startidx + idx_offset + -- Ending index of the replacement text (if pat2 isn't empty) + local repl_endidx + + if pat2 ~= '' then + repl_endidx = endidx + idx_offset + pat_width_differences[i] + else + repl_endidx = endidx + idx_offset + end + + if pat2 ~= '' then + idx_offset = idx_offset + pat_width_differences[i] + end + + line_matches[i] = { repl_startidx, repl_endidx } + end + end + end + + for lnum, line in pairs(new_lines) do + vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line }) + end + + if preview then + local lnum = vim.api.nvim_win_get_cursor(0)[1] + -- Use preview window only if preview buffer is provided and range isn't just the current line + local use_preview_win = (preview_buf ~= nil) and (line1 ~= lnum or line2 ~= lnum) + return show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches) + end + end + + local function replace(opts) + do_replace(opts, false) + end + + local function replace_preview(opts, preview_ns, preview_buf) + return do_replace(opts, true, preview_ns, preview_buf) + end + + -- ":<range>Replace <pat1> <pat2>" + -- Replaces all occurences of <pat1> in <range> with <pat2> + vim.api.nvim_create_user_command( + 'Replace', + replace, + { nargs = '*', range = '%', addr = 'lines', + preview = replace_preview } + ) +]] + +describe("'inccommand' for user commands", function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 17) + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.Yellow1}, + [2] = {foreground = Screen.colors.Blue1, bold = true}, + [3] = {reverse = true}, + [4] = {reverse = true, bold = true} + }) + screen:attach() + exec_lua(setup_replace_cmd) + command('set cmdwinheight=5') + insert[[ + text on line 1 + more text on line 2 + oh no, even more text + will the text ever stop + oh well + did the text stop + why won't it stop + make the text stop + ]] + end) + + it('works with inccommand=nosplit', function() + command('set inccommand=nosplit') + feed(':Replace text cats') + screen:expect([[ + {1:cats} on line 1 | + more {1:cats} on line 2 | + oh no, even more {1:cats} | + will the {1:cats} ever stop | + oh well | + did the {1:cats} stop | + why won't it stop | + make the {1:cats} stop | + | + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + :Replace text cats^ | + ]]) + end) + + it('works with inccommand=split', function() + command('set inccommand=split') + feed(':Replace text cats') + screen:expect([[ + {1:cats} on line 1 | + more {1:cats} on line 2 | + oh no, even more {1:cats} | + will the {1:cats} ever stop | + oh well | + did the {1:cats} stop | + why won't it stop | + make the {1:cats} stop | + | + {4:[No Name] [+] }| + |1| {1:cats} on line 1 | + |2| more {1:cats} on line 2 | + |3| oh no, even more {1:cats} | + |4| will the {1:cats} ever stop | + |6| did the {1:cats} stop | + {3:[Preview] }| + :Replace text cats^ | + ]]) + end) + + it('properly closes preview when inccommand=split', function() + command('set inccommand=split') + feed(':Replace text cats<Esc>') + screen:expect([[ + text on line 1 | + more text on line 2 | + oh no, even more text | + will the text ever stop | + oh well | + did the text stop | + why won't it stop | + make the text stop | + ^ | + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]) + end) + + it('properly executes command when inccommand=split', function() + command('set inccommand=split') + feed(':Replace text cats<CR>') + screen:expect([[ + cats on line 1 | + more cats on line 2 | + oh no, even more cats | + will the cats ever stop | + oh well | + did the cats stop | + why won't it stop | + make the cats stop | + ^ | + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + :Replace text cats | + ]]) + end) + + it('shows preview window only when range is not current line', function() + command('set inccommand=split') + feed('gg:.Replace text cats') + screen:expect([[ + {1:cats} on line 1 | + more text on line 2 | + oh no, even more text | + will the text ever stop | + oh well | + did the text stop | + why won't it stop | + make the text stop | + | + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + :.Replace text cats^ | + ]]) + end) +end) |