Merge pull request #19360 from famiu/feat/multibuffer-inccommand

feat: multibuffer preview support for inccommand
This commit is contained in:
bfredl 2022-08-19 17:21:18 +02:00 committed by GitHub
commit 3c545b9c62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 435 additions and 170 deletions

View File

@ -1466,10 +1466,10 @@ 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
1. Modify the target buffers 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
3. Add required highlights to the target buffers. 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
@ -1480,8 +1480,8 @@ Your command preview routine must implement this protocol:
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.
After preview ends, Nvim discards all changes to all buffers made during the
preview and clears all highlights in the preview namespace.
Here's an example of a command to trim trailing whitespace from lines that
supports incremental command preview:

View File

@ -148,7 +148,7 @@ struct cmdline_info {
/// Last value of prompt_id, incremented when doing new prompt
static unsigned last_prompt_id = 0;
// Struct to store the viewstate during 'incsearch' highlighting.
// Struct to store the viewstate during 'incsearch' highlighting and 'inccommand' preview.
typedef struct {
colnr_T vs_curswant;
colnr_T vs_leftcol;
@ -200,6 +200,32 @@ typedef struct command_line_state {
long *b_im_ptr;
} CommandLineState;
typedef struct cmdpreview_win_info {
win_T *win;
pos_T save_w_cursor;
viewstate_T save_viewstate;
int save_w_p_cul;
int save_w_p_cuc;
} CpWinInfo;
typedef struct cmdpreview_buf_info {
buf_T *buf;
time_t save_b_u_time_cur;
long save_b_u_seq_cur;
u_header_T *save_b_u_newhead;
long save_b_p_ul;
int save_b_changed;
varnumber_T save_changedtick;
} CpBufInfo;
typedef struct cmdpreview_info {
kvec_t(CpWinInfo) win_info;
kvec_t(CpBufInfo) buf_info;
bool save_hls;
cmdmod_T save_cmdmod;
garray_T save_view;
} CpInfo;
typedef struct cmdline_info CmdlineInfo;
/// The current cmdline_info. It is initialized in getcmdline() and after that
@ -242,26 +268,26 @@ static long cmdpreview_ns = 0;
static int cmd_hkmap = 0; // Hebrew mapping during command line
static void save_viewstate(viewstate_T *vs)
static void save_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL
{
vs->vs_curswant = curwin->w_curswant;
vs->vs_leftcol = curwin->w_leftcol;
vs->vs_topline = curwin->w_topline;
vs->vs_topfill = curwin->w_topfill;
vs->vs_botline = curwin->w_botline;
vs->vs_empty_rows = curwin->w_empty_rows;
vs->vs_curswant = wp->w_curswant;
vs->vs_leftcol = wp->w_leftcol;
vs->vs_topline = wp->w_topline;
vs->vs_topfill = wp->w_topfill;
vs->vs_botline = wp->w_botline;
vs->vs_empty_rows = wp->w_empty_rows;
}
static void restore_viewstate(viewstate_T *vs)
static void restore_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL
{
curwin->w_curswant = vs->vs_curswant;
curwin->w_leftcol = vs->vs_leftcol;
curwin->w_topline = vs->vs_topline;
curwin->w_topfill = vs->vs_topfill;
curwin->w_botline = vs->vs_botline;
curwin->w_empty_rows = vs->vs_empty_rows;
wp->w_curswant = vs->vs_curswant;
wp->w_leftcol = vs->vs_leftcol;
wp->w_topline = vs->vs_topline;
wp->w_topfill = vs->vs_topfill;
wp->w_botline = vs->vs_botline;
wp->w_empty_rows = vs->vs_empty_rows;
}
static void init_incsearch_state(incsearch_state_T *s)
@ -273,8 +299,8 @@ static void init_incsearch_state(incsearch_state_T *s)
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
s->search_start = curwin->w_cursor;
save_viewstate(&s->init_viewstate);
save_viewstate(&s->old_viewstate);
save_viewstate(curwin, &s->init_viewstate);
save_viewstate(curwin, &s->old_viewstate);
}
/// Completion for |:checkhealth| command.
@ -554,7 +580,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
// first restore the old curwin values, so the screen is
// positioned in the same way as the actual search command
restore_viewstate(&s->old_viewstate);
restore_viewstate(curwin, &s->old_viewstate);
changed_cline_bef_curs();
update_topline(curwin);
@ -664,7 +690,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
}
curwin->w_cursor = s->search_start; // -V519
}
restore_viewstate(&s->old_viewstate);
restore_viewstate(curwin, &s->old_viewstate);
highlight_match = false;
// by default search all lines
@ -1663,7 +1689,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
update_topline(curwin);
validate_cursor();
highlight_match = true;
save_viewstate(&s->old_viewstate);
save_viewstate(curwin, &s->old_viewstate);
update_screen(NOT_VALID);
highlight_match = false;
redrawcmdline();
@ -2395,6 +2421,126 @@ static void cmdpreview_close_win(void)
}
}
/// Save current state and prepare windows and buffers for command preview.
static void cmdpreview_prepare(CpInfo *cpinfo)
{
kv_init(cpinfo->buf_info);
kv_init(cpinfo->win_info);
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
buf_T *buf = win->w_buffer;
// Don't save state of command preview buffer or preview window.
if (buf->handle == cmdpreview_bufnr) {
continue;
}
CpBufInfo cp_bufinfo;
cp_bufinfo.buf = buf;
cp_bufinfo.save_b_u_time_cur = buf->b_u_time_cur;
cp_bufinfo.save_b_u_seq_cur = buf->b_u_seq_cur;
cp_bufinfo.save_b_u_newhead = buf->b_u_newhead;
cp_bufinfo.save_b_p_ul = buf->b_p_ul;
cp_bufinfo.save_b_changed = buf->b_changed;
cp_bufinfo.save_changedtick = buf_get_changedtick(buf);
kv_push(cpinfo->buf_info, cp_bufinfo);
buf->b_p_ul = LONG_MAX; // Make sure we can undo all changes
CpWinInfo cp_wininfo;
cp_wininfo.win = win;
// Save window cursor position and viewstate
cp_wininfo.save_w_cursor = win->w_cursor;
save_viewstate(win, &cp_wininfo.save_viewstate);
// Save 'cursorline' and 'cursorcolumn'
cp_wininfo.save_w_p_cul = win->w_p_cul;
cp_wininfo.save_w_p_cuc = win->w_p_cuc;
kv_push(cpinfo->win_info, cp_wininfo);
win->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights
win->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights
}
cpinfo->save_hls = p_hls;
cpinfo->save_cmdmod = cmdmod;
win_size_save(&cpinfo->save_view);
save_search_patterns();
p_hls = false; // Don't show search highlighting during live substitution
cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers
cmdmod.cmod_tab = 0; // Disable :tab modifier
cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer
}
// Restore the state of buffers and windows before command preview.
static void cmdpreview_restore_state(CpInfo *cpinfo)
{
for (size_t i = 0; i < cpinfo->buf_info.size; i++) {
CpBufInfo cp_bufinfo = cpinfo->buf_info.items[i];
buf_T *buf = cp_bufinfo.buf;
buf->b_changed = cp_bufinfo.save_b_changed;
if (buf->b_u_seq_cur != cp_bufinfo.save_b_u_seq_cur) {
int count = 0;
// Calculate how many undo steps are necessary to restore earlier state.
for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead;
uhp != NULL && uhp->uh_seq > cp_bufinfo.save_b_u_seq_cur;
uhp = uhp->uh_next.ptr, ++count) {}
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
// Undo invisibly. This also moves the cursor!
if (!u_undo_and_forget(count)) {
abort();
}
aucmd_restbuf(&aco);
// Restore newhead. It is meaningless when curhead is valid, but we must
// restore it so that undotree() is identical before/after the preview.
buf->b_u_newhead = cp_bufinfo.save_b_u_newhead;
buf->b_u_time_cur = cp_bufinfo.save_b_u_time_cur;
}
if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) {
buf_set_changedtick(buf, cp_bufinfo.save_changedtick);
}
buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
// Clear preview highlights.
extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
}
for (size_t i = 0; i < cpinfo->win_info.size; i++) {
CpWinInfo cp_wininfo = cpinfo->win_info.items[i];
win_T *win = cp_wininfo.win;
// Restore window cursor position and viewstate
win->w_cursor = cp_wininfo.save_w_cursor;
restore_viewstate(win, &cp_wininfo.save_viewstate);
// Restore 'cursorline' and 'cursorcolumn'
win->w_p_cul = cp_wininfo.save_w_p_cul;
win->w_p_cuc = cp_wininfo.save_w_p_cuc;
update_topline(win);
}
cmdmod = cpinfo->save_cmdmod; // Restore cmdmod
p_hls = cpinfo->save_hls; // Restore 'hlsearch'
restore_search_patterns(); // Restore search patterns
win_size_restore(&cpinfo->save_view); // Restore window sizes
ga_clear(&cpinfo->save_view);
kv_destroy(cpinfo->win_info);
kv_destroy(cpinfo->buf_info);
}
/// Show 'inccommand' preview if command is previewable. It works like this:
/// 1. Store current undo information so we can revert to current state later.
/// 2. Execute the preview callback with the parsed command, preview buffer number and preview
@ -2439,35 +2585,18 @@ static bool cmdpreview_may_show(CommandLineState *s)
ea.line2 = lnum;
}
time_t save_b_u_time_cur = curbuf->b_u_time_cur;
long save_b_u_seq_cur = curbuf->b_u_seq_cur;
u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
long save_b_p_ul = curbuf->b_p_ul;
int save_b_changed = curbuf->b_changed;
int save_w_p_cul = curwin->w_p_cul;
int save_w_p_cuc = curwin->w_p_cuc;
bool save_hls = p_hls;
varnumber_T save_changedtick = buf_get_changedtick(curbuf);
CpInfo cpinfo;
bool icm_split = *p_icm == 's'; // inccommand=split
buf_T *cmdpreview_buf;
win_T *cmdpreview_win;
cmdmod_T save_cmdmod = cmdmod;
cmdpreview = true;
emsg_silent++; // Block error reporting as the command may be incomplete,
// but still update v:errmsg
msg_silent++; // Block messages, namely ones that prompt
block_autocmds(); // Block events
garray_T save_view;
win_size_save(&save_view); // Save current window sizes
save_search_patterns(); // Save search patterns
curbuf->b_p_ul = LONG_MAX; // Make sure we can undo all changes
curwin->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights
curwin->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights
p_hls = false; // Don't show search highlighting during live substitution
cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers
cmdmod.cmod_tab = 0; // Disable :tab modifier
cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer
// Save current state and prepare for command preview.
cmdpreview_prepare(&cpinfo);
// Open preview buffer if inccommand=split.
if (!icm_split) {
@ -2475,12 +2604,14 @@ static bool cmdpreview_may_show(CommandLineState *s)
} else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) {
abort();
}
// Setup preview namespace if it's not already set.
if (!cmdpreview_ns) {
cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT);
}
// Set cmdpreview state.
cmdpreview = true;
// Execute the preview callback and use its return value to determine whether to show preview or
// open the preview window. The preview callback also handles doing the changes and highlights for
// the preview.
@ -2499,7 +2630,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
cmdpreview_type = 1;
}
// If preview callback is nonzero, update screen now.
// If preview callback return value is nonzero, update screen now.
if (cmdpreview_type != 0) {
int save_rd = RedrawingDisabled;
RedrawingDisabled = 0;
@ -2511,44 +2642,13 @@ static bool cmdpreview_may_show(CommandLineState *s)
if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) {
cmdpreview_close_win();
}
// Clear preview highlights.
extmark_clear(curbuf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
curbuf->b_changed = save_b_changed; // Preserve 'modified' during preview
// Restore state.
cmdpreview_restore_state(&cpinfo);
if (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
// Undo invisibly. This also moves the cursor!
while (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
if (!u_undo_and_forget(1)) {
abort();
}
}
// Restore newhead. It is meaningless when curhead is valid, but we must
// restore it so that undotree() is identical before/after the preview.
curbuf->b_u_newhead = save_b_u_newhead;
curbuf->b_u_time_cur = save_b_u_time_cur;
}
if (save_changedtick != buf_get_changedtick(curbuf)) {
buf_set_changedtick(curbuf, save_changedtick);
}
cmdmod = save_cmdmod; // Restore cmdmod
p_hls = save_hls; // Restore 'hlsearch'
curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
curbuf->b_p_ul = save_b_p_ul; // Restore 'undolevels'
restore_search_patterns(); // Restore search patterns
win_size_restore(&save_view); // Restore window sizes
ga_clear(&save_view);
unblock_autocmds(); // Unblock events
msg_silent--; // Unblock messages
emsg_silent--; // Unblock error reporting
// Restore the window "view".
curwin->w_cursor = s->is_state.save_cursor;
restore_viewstate(&s->is_state.old_viewstate);
update_topline(curwin);
redrawcmdline();
end:
xfree(cmdline);

View File

@ -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
local buf = match[1]
local buf_matches = match[2]
if use_preview_win then
prefix = string.format(
'|%s%d| ',
string.rep(' ', highest_lnum_width - math.floor(math.log10(lnum))),
lnum
)
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_ns,
'Substitute',
preview_buf_line,
#prefix + line_match[1],
#prefix + line_match[2]
preview_buf_line,
0,
{ prefix .. vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] }
)
end
end
preview_buf_line = preview_buf_line + 1
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
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 }
return false
end,
vim.api.nvim_list_bufs()
)
for _, buf in ipairs(buffers) do
local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, false)
local buf_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
num = num + 1
if #line_matches > 0 then
buf_matches[#buf_matches+1] = { line1 + i - 1, line_matches }
end
end
if #line_matches > 0 then
matches[#matches+1] = { line1 + i - 1, line_matches }
end
end
local new_lines = {}
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 = {}
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)
-- 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
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
for _, line_match in ipairs(line_matches) do
local idx_offset = 0
for i, line_match in ipairs(line_matches) do
local startidx, endidx = unpack(line_match)
local pat_match = line:sub(startidx + 1, endidx)
-- 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
pat_width_differences[#pat_width_differences+1] =
#vim.fn.substitute(pat_match, pat1, pat2, 'g') - #pat_match
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
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
for lnum, line in pairs(new_lines) do
vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line })
end
end
for lnum, line in pairs(new_lines) do
vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line })
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)