diff options
author | Famiu Haque <famiuhaque@protonmail.com> | 2022-07-14 14:37:00 +0600 |
---|---|---|
committer | Famiu Haque <famiuhaque@protonmail.com> | 2022-08-19 09:27:52 +0600 |
commit | c2f7a2a18daa7e1d80285818065b2376683dcb21 (patch) | |
tree | c6531a580f1ec3dcc27750001247852739674bcb /test/functional/ui/inccommand_user_spec.lua | |
parent | 9a3877ff9d4db15189b171b4a487d57768abf0a9 (diff) | |
download | rneovim-c2f7a2a18daa7e1d80285818065b2376683dcb21.tar.gz rneovim-c2f7a2a18daa7e1d80285818065b2376683dcb21.tar.bz2 rneovim-c2f7a2a18daa7e1d80285818065b2376683dcb21.zip |
feat: multibuffer preview support for inccommand
Allows preview callbacks to modify multiple buffers in order to show the
preview. Previously, if multiple buffers were modified, only the current
buffer would have its state restored. After this change, all buffers
have their state restored after preview.
Closes #19103.
Diffstat (limited to 'test/functional/ui/inccommand_user_spec.lua')
-rw-r--r-- | test/functional/ui/inccommand_user_spec.lua | 347 |
1 files changed, 256 insertions, 91 deletions
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua index b5816f6fe6..0b25d4f8d2 100644 --- a/test/functional/ui/inccommand_user_spec.lua +++ b/test/functional/ui/inccommand_user_spec.lua @@ -7,61 +7,82 @@ local feed = helpers.feed local command = helpers.command local assert_alive = helpers.assert_alive --- Implements a :Replace command that works like :substitute. +-- Implements a :Replace command that works like :substitute and has multibuffer support. local setup_replace_cmd = [[ - local function show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches) + local function show_replace_preview(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 + local multibuffer = #matches > 1 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 - ) + local buf = match[1] + local buf_matches = match[2] + + if multibuffer and #buf_matches > 0 and use_preview_win then + local bufname = vim.api.nvim_buf_get_name(buf) + + if bufname == "" then + bufname = string.format("Buffer #%d", buf) + end 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] } + { bufname .. ':' } ) + + preview_buf_line = preview_buf_line + 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] - ) + for _, buf_match in ipairs(buf_matches) do + local lnum = buf_match[1] + local line_matches = buf_match[2] + local prefix if use_preview_win then - vim.api.nvim_buf_add_highlight( + 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', - preview_buf_line, - #prefix + line_match[1], - #prefix + line_match[2] + 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 - end - preview_buf_line = preview_buf_line + 1 + preview_buf_line = preview_buf_line + 1 + end end if use_preview_win then @@ -72,94 +93,121 @@ local setup_replace_cmd = [[ end local function do_replace(opts, preview, preview_ns, preview_buf) - local pat1 = opts.fargs[1] or '' + local pat1 = opts.fargs[1] + + if not pat1 then return end + 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 + -- Get list of valid and listed buffers + local buffers = vim.tbl_filter( + function(buf) + if not (vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buflisted and buf ~= preview_buf) + then + return false + end - while startidx ~= -1 do - local match = vim.fn.matchstrpos(line, pat1, 0, num) - startidx, endidx = match[2], match[3] + -- Check if there's at least one window using the buffer + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_get_buf(win) == buf then + return true + end + end - if startidx ~= -1 then - line_matches[#line_matches+1] = { startidx, endidx } - end + return false + end, + vim.api.nvim_list_bufs() + ) - num = num + 1 - end + for _, buf in ipairs(buffers) do + local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, false) + local buf_matches = {} - if #line_matches > 0 then - matches[#matches+1] = { line1 + i - 1, line_matches } - end - end + for i, line in ipairs(lines) do + local startidx, endidx = 0, 0 + local line_matches = {} + local num = 1 - local new_lines = {} + while startidx ~= -1 do + local match = vim.fn.matchstrpos(line, pat1, 0, num) + startidx, endidx = match[2], match[3] - 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 + if startidx ~= -1 then + line_matches[#line_matches+1] = { startidx, endidx } end + + num = num + 1 end - new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g') + if #line_matches > 0 then + buf_matches[#buf_matches+1] = { line1 + i - 1, line_matches } + end 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 + local new_lines = {} + + for _, buf_match in ipairs(buf_matches) do + local lnum = buf_match[1] + local line_matches = buf_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 - if pat2 ~= '' then - idx_offset = idx_offset + pat_width_differences[i] - end + new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g') + end - line_matches[i] = { repl_startidx, repl_endidx } + -- 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 - end - for lnum, line in pairs(new_lines) do - vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line }) + for lnum, line in pairs(new_lines) do + vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line }) + end + + matches[#matches+1] = { buf, buf_matches } 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) + return show_replace_preview(use_preview_win, preview_ns, preview_buf, matches) end end @@ -354,3 +402,120 @@ describe("'inccommand' for user commands", function() assert_alive() end) end) + +describe("'inccommand' with multiple buffers", 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=10') + insert[[ + foo bar baz + bar baz foo + baz foo bar + ]] + command('vsplit | enew') + insert[[ + bar baz foo + baz foo bar + foo bar baz + ]] + end) + + it('works', function() + command('set inccommand=nosplit') + feed(':Replace foo bar') + screen:expect([[ + bar baz {1:bar} │ {1:bar} bar baz | + baz {1:bar} bar │ bar baz {1:bar} | + {1:bar} bar baz │ baz {1:bar} bar | + │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar^ | + ]]) + feed('<CR>') + screen:expect([[ + bar baz bar │ bar bar baz | + baz bar bar │ bar baz bar | + bar bar baz │ baz bar bar | + ^ │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar | + ]]) + end) + + it('works with inccommand=split', function() + command('set inccommand=split') + feed(':Replace foo bar') + screen:expect([[ + bar baz {1:bar} │ {1:bar} bar baz | + baz {1:bar} bar │ bar baz {1:bar} | + {1:bar} bar baz │ baz {1:bar} bar | + │ | + {4:[No Name] [+] }{3:[No Name] [+] }| + Buffer #1: | + |1| {1:bar} bar baz | + |2| bar baz {1:bar} | + |3| baz {1:bar} bar | + Buffer #2: | + |1| bar baz {1:bar} | + |2| baz {1:bar} bar | + |3| {1:bar} bar baz | + | + {2:~ }| + {3:[Preview] }| + :Replace foo bar^ | + ]]) + feed('<CR>') + screen:expect([[ + bar baz bar │ bar bar baz | + baz bar bar │ bar baz bar | + bar bar baz │ baz bar bar | + ^ │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar | + ]]) + end) +end) |