From 8d929f558c981c75f19dfc22e54e36c904be887e Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 2 Sep 2017 23:28:49 +0200 Subject: [PATCH 01/13] Inccommand: Multiline substitutions, highlighting, multibyte. Make inccomand work with multiline patterns and substitutions. Also care for proper highlighting and multibyte characters. --- src/nvim/buffer.c | 37 ++++++++ src/nvim/ex_cmds.c | 218 +++++++++++++++++++++++++++++++-------------- 2 files changed, 190 insertions(+), 65 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9d42fbc1b3..3080d4960d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5278,6 +5278,43 @@ int bufhl_add_hl(buf_T *buf, return src_id; } +/// Add highlighting to a buffer, bounded by two cursor positions, +/// with an offset +/// @param buf The buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Id of the highlight group to use +/// @param lpos_start Cursor position to start the hightlighting at +/// @param lpos_end Cursor position to end the highlighting at +/// @param offset Move the whole highlighting this many columns to the right +void bufhl_add_hl_pos_offset(buf_T *buf, + int src_id, + int hl_id, + lpos_T pos_start, + lpos_T pos_end, + colnr_T offset) +{ + colnr_T hl_start = 0; + colnr_T hl_end = 0; + + for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { + if (pos_start.lnum < lnum && lnum < pos_end.lnum) { + hl_start = offset; + hl_end = MAXCOL; + } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = MAXCOL; + } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { + hl_start = offset; + hl_end = pos_end.col + offset; + } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = pos_end.col + offset; + } + (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end); + } +} + /// Clear bufhl highlights from a given source group and range of lines. /// /// @param buf The buffer to remove highlights from diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 918e7a0c91..fe0c662898 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -90,14 +90,20 @@ typedef struct { SubIgnoreType do_ic; ///< ignore case flag } subflags_T; -/// Lines matched during :substitute. +/// Partial result of a substitution during :substitute. +/// Numbers refer to the buffer _after_ substitution typedef struct { - linenr_T lnum; - long nmatch; - char_u *line; - kvec_t(colnr_T) cols; ///< columns of in-line matches -} MatchedLine; -typedef kvec_t(MatchedLine) MatchedLineVec; + lpos_T start; // start of the match + lpos_T end; // end of the match + linenr_T pre_match; // where to begin showing lines before the match +} SubResult; + +// Collected results of a substitution for showing them in +// the preview window +typedef struct { + kvec_t(SubResult) subresults; + linenr_T lines_needed; // lines neede in the preview window +} PreviewLines; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" @@ -3168,7 +3174,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) linenr_T old_line_count = curbuf->b_ml.ml_line_count; char_u *sub_firstline; // allocated copy of first sub line bool endcolumn = false; // cursor in last column when done - MatchedLineVec matched_lines = KV_INITIAL_VALUE; + PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; + static int pre_src_id = 0; // Source id for the preview highlight + static int pre_hl_id = 0; + buf_T *orig_buf = curbuf; // save to reset highlighting pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; @@ -3336,7 +3345,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; lnum <= line2 && !got_quit && !aborting() - && (!preview || matched_lines.size < (size_t)p_cwh + && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, @@ -3401,8 +3410,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstlnum = lnum; copycol = 0; matchcol = 0; - // the current match - MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE }; /* At first match, remember current cursor position. */ if (!got_match) { @@ -3419,10 +3426,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) * 5. break if there isn't another match in this line */ for (;; ) { + SubResult current_match = { + .start = { 0, 0 }, + .end = { 0, 0 }, + .pre_match = 0, + }; + // lnum is where the match start, but maybe not the pattern match, + // since we can have \n before \zs in the pattern + /* Advance "lnum" to the line where the match starts. The * match does not start in the first line when there is a line * break before \zs. */ if (regmatch.startpos[0].lnum > 0) { + current_match.pre_match = lnum; lnum += regmatch.startpos[0].lnum; sub_firstlnum += regmatch.startpos[0].lnum; nmatch -= regmatch.startpos[0].lnum; @@ -3430,6 +3446,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstline = NULL; } + // Now we're at the line where the pattern match starts + // Note: If not first match on a line, column can't be known here + current_match.start.lnum = sub_firstlnum; + if (sub_firstline == NULL) { sub_firstline = vim_strsave(ml_get(sub_firstlnum)); } @@ -3439,12 +3459,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) curwin->w_cursor.lnum = lnum; do_again = FALSE; - if (preview) { - // Increment the in-line match count and store the column. - matched_line.nmatch++; - kv_push(matched_line.cols, regmatch.startpos[0].col); - } - /* * 1. Match empty string does not count, except for first * match. This reproduces the strange vi behaviour. @@ -3696,8 +3710,17 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) } \ } while (0) + // Save the line numbers for the preview buffer + // NOTE: If the pattern matches a final newline, the next line will + // be shown also, but should not be highlighted. Intentional for now. if (preview && !has_second_delim) { + current_match.start.col = regmatch.startpos[0].col; + current_match.end.lnum = sub_firstlnum + nmatch - 1; + current_match.end.col = regmatch.endpos[0].col; + ADJUST_SUB_FIRSTLNUM(); + lnum += nmatch - 1; + goto skip; } @@ -3747,6 +3770,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) memmove(new_end, sub_firstline + copycol, (size_t)copy_len); new_end += copy_len; + // Finally, at this point we can know where the match actually will + // start in the new text + current_match.start.col = new_end - new_start; + (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, sub, new_end, true, p_magic, true); @@ -3799,6 +3826,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) p1 += (*mb_ptr2len)(p1) - 1; } } + current_match.end.col = STRLEN(new_start); + current_match.end.lnum = lnum; } // 4. If subflags.do_all is set, find next match. @@ -3907,9 +3936,35 @@ skip: * found the match. */ if (nmatch == -1) lnum -= regmatch.startpos[0].lnum; + + // Push the match to preview_lines + // TODO(KillTheMule): Code duplication at line 3961 + linenr_T match_lines = current_match.end.lnum + - current_match.start.lnum +1; + if (preview_lines.subresults.size > 0) { + linenr_T last_lnum = kv_last(preview_lines.subresults).end.lnum; + if (last_lnum == current_match.start.lnum) { + preview_lines.lines_needed += match_lines - 1; + } + } else { + preview_lines.lines_needed += match_lines; + } + kv_push(preview_lines.subresults, current_match); break; } } + // Push the match to preview_lines + linenr_T match_lines = current_match.end.lnum + - current_match.start.lnum +1; + if (preview_lines.subresults.size > 0) { + linenr_T last_lnum = kv_last(preview_lines.subresults).end.lnum; + if (last_lnum == current_match.start.lnum) { + preview_lines.lines_needed += match_lines - 1; + } + } else { + preview_lines.lines_needed += match_lines; + } + kv_push(preview_lines.subresults, current_match); line_breakcheck(); } @@ -3919,12 +3974,6 @@ skip: xfree(new_start); /* for when substitute was cancelled */ xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; - - if (preview) { - matched_line.lnum = lnum; - matched_line.line = vim_strsave(ml_get(lnum)); - kv_push(matched_lines, matched_line); - } } line_breakcheck(); @@ -4003,18 +4052,23 @@ skip: if (got_quit) { // Substitution is too slow, disable 'inccommand'. set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, SID_NONE); - } else if (*p_icm != NUL && matched_lines.size != 0 && pat != NULL) { + } else if (*p_icm != NUL && preview_lines.subresults.size != 0 && pat != NULL) { + if (pre_src_id == 0) { + // Get a unique new src_id, saved in a static + pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0); + } + if (pre_hl_id == 0) { + pre_hl_id = syn_check_group((char_u *)"Substitute", 13); + } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines); + preview_buf = show_sub(eap, old_cursor, pat, sub, &preview_lines, + has_second_delim, pre_hl_id, pre_src_id); + bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, + kv_last(preview_lines.subresults).end.lnum); } } - for (MatchedLine m; kv_size(matched_lines);) { - m = kv_pop(matched_lines); - xfree(m.line); - kv_destroy(m.cols); - } - kv_destroy(matched_lines); + kv_destroy(preview_lines.subresults); return preview_buf; #undef ADJUST_SUB_FIRSTLNUM @@ -6018,7 +6072,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, - MatchedLineVec *matched_lines) + PreviewLines *preview_lines, bool show_hl, int hl_id, + int src_id) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6028,6 +6083,8 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, char_u *save_shm_p = vim_strsave(p_shm); size_t sub_size = mb_string2cells(sub); size_t pat_size = mb_string2cells(pat); + PreviewLines lines = *preview_lines; + buf_T *orig_buf = curbuf; // We keep a special-purpose buffer around, but don't assume it exists. buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; @@ -6046,15 +6103,19 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, } // Place cursor on nearest matching line, to undo do_sub() cursor placement. - for (size_t i = 0; i < matched_lines->size; i++) { - MatchedLine curmatch = matched_lines->items[i]; - if (curmatch.lnum >= old_cusr.lnum) { - curwin->w_cursor.lnum = curmatch.lnum; - curwin->w_cursor.col = curmatch.cols.items[0]; + for (size_t i = 0; i < lines.subresults.size; i++) { + SubResult curres = lines.subresults.items[i]; + if (curres.start.lnum >= old_cusr.lnum) { + curwin->w_cursor.lnum = curres.start.lnum; + curwin->w_cursor.col = curres.start.col; break; } // Else: All matches are above, do_sub() already placed cursor. } + // Width of the "| lnum|..." column which displays the line numbers. + linenr_T highest_num_line = 0; + int col_width = 0; + if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]"); buf_clear(); @@ -6070,43 +6131,70 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, curwin->w_p_fen = false; // Width of the "| lnum|..." column which displays the line numbers. - linenr_T highest_num_line = kv_last(*matched_lines).lnum; - int col_width = log10(highest_num_line) + 1 + 3; + highest_num_line = kv_last(lines.subresults).end.lnum; + col_width = log10(highest_num_line) + 1 + 3; + } - char *str = NULL; - size_t old_line_size = 0; - size_t line_size; - int src_id_highlight = 0; - int hl_id = syn_check_group((char_u *)"Substitute", 13); + char *str = NULL; // construct the line to show in here + size_t old_line_size = 0; + size_t line_size = 0; + linenr_T linenr_preview = 0; // # of last line added to preview buffer + linenr_T linenr_origbuf = 0; // # of last line added to original number + linenr_T next_linenr = 0; // # of the next line to show for the match - // Dump the lines into the preview buffer. - for (size_t line = 0; line < matched_lines->size; line++) { - MatchedLine mat = matched_lines->items[line]; - line_size = mb_string2cells(mat.line) + col_width + 1; + for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) { + SubResult match = lines.subresults.items[matchidx]; - // Reallocate if str not long enough - if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); - old_line_size = line_size; + if (split && preview_buf) { + lpos_T p_start = { 0, match.start.col }; // match starts here in preview + lpos_T p_end = { 0, match.end.col }; // ... and ends here + + if (match.pre_match == 0) { + next_linenr = match.start.lnum; + } else { + next_linenr = match.pre_match; + } + // Don't add a line twice + if (next_linenr == linenr_origbuf) { + next_linenr++; + p_start.lnum = linenr_preview; // might be redefined below + p_end.lnum = linenr_preview; // might be redefined below } - // Put "|lnum| line" into `str` and append it to the preview buffer. - snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line); - ml_append(line, (char_u *)str, (colnr_T)line_size, false); - - // highlight the replaced part - if (sub_size > 0) { - for (size_t i = 0; i < mat.cols.size; i++) { - colnr_T col_start = mat.cols.items[i] + col_width - + i * (sub_size - pat_size) + 1; - colnr_T col_end = col_start - 1 + sub_size; - src_id_highlight = bufhl_add_hl(curbuf, src_id_highlight, hl_id, - line + 1, col_start, col_end); + for (; next_linenr <= match.end.lnum; next_linenr++) { + if (next_linenr == match.start.lnum) { + p_start.lnum = linenr_preview + 1; } + if (next_linenr == match.end.lnum) { + p_end.lnum = linenr_preview + 1; + } + char_u *line = ml_get_buf(orig_buf, next_linenr, false); + line_size = STRLEN(line) + col_width + 1; + + // Reallocate if line not long enough + if (line_size > old_line_size) { + str = xrealloc(str, line_size * sizeof(char)); + old_line_size = line_size; + } + // Put "|lnum| line" into `str` and append it to the preview buffer. + snprintf(str, line_size, "|%*ld| %s", col_width - 3, + next_linenr, line); + ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false); + linenr_preview += 1; + } + linenr_origbuf = match.end.lnum; + + if (show_hl) { + bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, + p_end, col_width); } } - xfree(str); + if (show_hl) { + bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, + match.end, 0); + } } + xfree(str); redraw_later(SOME_VALID); win_enter(save_curwin, false); // Return to original window From 1fcd8389429b51d1d388f7f0a43566b4bcc9b9f4 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sun, 10 Sep 2017 13:23:48 +0200 Subject: [PATCH 02/13] Fix old inccomand tests, and add more for the new functionality. --- test/functional/ui/icmmulti_spec.lua | 692 +++++++++++++++++++++++++ test/functional/ui/inccommand_spec.lua | 48 +- 2 files changed, 716 insertions(+), 24 deletions(-) create mode 100644 test/functional/ui/icmmulti_spec.lua diff --git a/test/functional/ui/icmmulti_spec.lua b/test/functional/ui/icmmulti_spec.lua new file mode 100644 index 0000000000..81ceaec111 --- /dev/null +++ b/test/functional/ui/icmmulti_spec.lua @@ -0,0 +1,692 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths +local eq = helpers.eq +local eval = helpers.eval +local feed_command = helpers.feed_command +local expect = helpers.expect +local feed = helpers.feed +local insert = helpers.insert +local meths = helpers.meths +local neq = helpers.neq +local ok = helpers.ok +local source = helpers.source +local wait = helpers.wait +local nvim = helpers.nvim + +local multiline_text = [[ + 1 2 3 + A B C + 4 5 6 + X Y Z + 7 8 9 +]] + +local multimatch_text = [[ + a bdc eae a fgl lzia r + x +]] + +local multibyte_text = [[ + £ ¥ ѫѫ PEPPERS +£ ¥ ѫfѫ + a£ ѫ¥KOL +£ ¥ libm +£ ¥ +]] + +local long_multiline_text = [[ + 1 2 3 + A B C + 4 5 6 + X Y Z + 7 8 9 + K L M + a b c + d e f + q r s + x y z + £ m n + t œ ¥ +]] +local function common_setup(screen, inccommand, text) + if screen then + command("syntax on") + command("set nohlsearch") + command("hi Substitute guifg=red guibg=yellow") + screen:attach() + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Fuchsia}, + [2] = {foreground = Screen.colors.Brown, bold = true}, + [3] = {foreground = Screen.colors.SlateBlue}, + [4] = {bold = true, foreground = Screen.colors.SlateBlue}, + [5] = {foreground = Screen.colors.DarkCyan}, + [6] = {bold = true}, + [7] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, + [8] = {foreground = Screen.colors.Slateblue, underline = true}, + [9] = {background = Screen.colors.Yellow}, + [10] = {reverse = true}, + [11] = {reverse = true, bold=true}, + [12] = {foreground = Screen.colors.Red, background = Screen.colors.Yellow}, + [13] = {bold = true, foreground = Screen.colors.SeaGreen}, + [14] = {foreground = Screen.colors.White, background = Screen.colors.Red}, + [15] = {bold=true, foreground=Screen.colors.Blue}, + [16] = {background=Screen.colors.Grey90}, -- cursorline + vis = {background=Screen.colors.LightGrey} + }) + end + + command("set inccommand=" .. (inccommand and inccommand or "")) + + if text then + insert(text) + end +end + +describe(":substitute", function() + local screen = Screen.new(30,15) + + before_each(function() + clear() + end) + + it(", inccomand=split, highlights multiline substitutions", function() + common_setup(screen, "split", multiline_text) + feed("gg") + + feed(":%s/2\\_.*X/MMM") + screen:expect([[ + 1 {12:MMM} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| 1 {12:MMM} Y Z | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/2\_.*X/MMM^ | + ]]) + + feed("\\rK\\rLLL") + screen:expect([[ + 1 {12:MMM} | + {12:K} | + {12:LLL} Y Z | + 7 8 9 | + | + {11:[No Name] [+] }| + |1| 1 {12:MMM} | + |2|{12: K} | + |3|{12: LLL} Y Z | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/2\_.*X/MMM\rK\rLLL^ | + ]]) + end) + + it(", inccomand=nosplit, highlights multiline substitutions", function() + common_setup(screen, "nosplit", multiline_text) + feed("gg") + + feed(":%s/2\\_.*X/MMM") + screen:expect([[ + 1 {12:MMM} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/2\_.*X/MMM^ | + ]]) + + feed("\\rK\\rLLL") + screen:expect([[ + 1 {12:MMM} | + {12:K} | + {12:LLL} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/2\_.*X/MMM\rK\rLLL^ | + ]]) + end) + + it(", inccomand=split, highlights multiple matches on a line", function() + common_setup(screen, "split", multimatch_text) + command("set gdefault") + feed("gg") + + feed(":%s/a/XLK") + screen:expect([[ + {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + x | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}| + {12:LK} r | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/a/XLK^ | + ]]) + end) + + it(", inccomand=nosplit, highlights multiple matches on a line", function() + common_setup(screen, "nosplit", multimatch_text) + command("set gdefault") + feed("gg") + + feed(":%s/a/XLK") + screen:expect([[ + {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + x | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/a/XLK^ | + ]]) + end) + + it(", inccomand=split, with \\zs", function() + common_setup(screen, "split", multiline_text) + feed("gg") + + feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") + screen:expect([[ + 1 2 3 | + {12:OKO} B C | + 4 5 6 | + {12:OKO} Y Z | + 7 8 9 | + {11:[No Name] [+] }| + |1| 1 2 3 | + |2| {12:OKO} B C | + |3| 4 5 6 | + |4| {12:OKO} Y Z | + | + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[0-9]\n\zs[A-Z]/OKO^ | + ]]) + end) + + it(", inccomand=nosplit, with \\zs", function() + common_setup(screen, "nosplit", multiline_text) + feed("gg") + + feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") + screen:expect([[ + 1 2 3 | + {12:OKO} B C | + 4 5 6 | + {12:OKO} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/[0-9]\n\zs[A-Z]/OKO^ | + ]]) + end) + + it(", inccomand=split, substitutions of different length", + function() + common_setup(screen, "split", "T T123 T2T TTT T090804\nx") + + feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") + screen:expect([[ + T {12:123123} {12:22}T TTT {12:090804090804} | + x | + {15:~ }| + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| T {12:123123} {12:22}T TTT {12:090804090}| + {12:804} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/T\([0-9]\+\)/\1\1/g^ | + ]]) + end) + + it(", inccomand=nosplit, substitutions of different length", function() + common_setup(screen, "nosplit", "T T123 T2T TTT T090804\nx") + + feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") + screen:expect([[ + T {12:123123} {12:22}T TTT {12:090804090804} | + x | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/T\([0-9]\+\)/\1\1/g^ | + ]]) + end) + + it(", inccomand=split, contraction of lines", function() + local text = [[ + T T123 T T123 T2T TT T23423424 + x + afa Q + adf la;lkd R + alx + ]] + + common_setup(screen, "split", text) + feed(":%s/[QR]\\n") + screen:expect([[ + afa Q | + adf la;lkd R | + alx | + | + {15:~ }| + {11:[No Name] [+] }| + |3| afa Q | + |4| adf la;lkd R | + |5| alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[QR]\n^ | + ]]) + + feed("/KKK") + screen:expect([[ + x | + afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |3| afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[QR]\n/KKK^ | + ]]) + end) + + it(", inccomand=nosplit, contraction of lines", function() + local text = [[ + T T123 T T123 T2T TT T23423424 + x + afa Q + adf la;lkd R + alx + ]] + + common_setup(screen, "nosplit", text) + feed(":%s/[QR]\\n/KKK") + screen:expect([[ + T T123 T T123 T2T TT T23423424| + x | + afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/[QR]\n/KKK^ | + ]]) + end) + + it(", inccommand=split, multibyte text", function() + common_setup(screen, "split", multibyte_text) + feed(":%s/£.*ѫ/X¥¥") + screen:expect([[ + {12:X¥¥} | + a{12:X¥¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {11:[No Name] [+] }| + |1| {12:X¥¥} PEPPERS | + |2| {12:X¥¥} | + |3| a{12:X¥¥}¥KOL | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/£.*ѫ/X¥¥^ | + ]]) + + feed("\\ra££ ¥") + screen:expect([[ + {12:a££ ¥} | + a{12:X¥¥} | + {12:a££ ¥}¥KOL | + £ ¥ libm | + £ ¥ | + {11:[No Name] [+] }| + |1| {12:X¥¥} | + |2|{12: a££ ¥} PEPPERS | + |3| {12:X¥¥} | + |4|{12: a££ ¥} | + |5| a{12:X¥¥} | + |6|{12: a££ ¥}¥KOL | + | + {10:[Preview] }| + :%s/£.*ѫ/X¥¥\ra££ ¥^ | + ]]) + end) + + it(", inccommand=nosplit, multibyte text", function() + common_setup(screen, "nosplit", multibyte_text) + feed(":%s/£.*ѫ/X¥¥") + screen:expect([[ + {12:X¥¥} PEPPERS | + {12:X¥¥} | + a{12:X¥¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/£.*ѫ/X¥¥^ | + ]]) + + feed("\\ra££ ¥") + screen:expect([[ + {12:X¥¥} | + {12:a££ ¥} PEPPERS | + {12:X¥¥} | + {12:a££ ¥} | + a{12:X¥¥} | + {12:a££ ¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/£.*ѫ/X¥¥\ra££ ¥^ | + ]]) + end) + + it(", inccomand=split, small cmdwinheight", function() + common_setup(screen, "split", long_multiline_text) + command("set cmdwinheight=2") + + feed(":%s/[a-z]") + screen:expect([[ + X Y Z | + 7 8 9 | + K L M | + a b c | + d e f | + q r s | + x y z | + £ m n | + t œ ¥ | + | + {11:[No Name] [+] }| + | 7| a b c | + | 8| d e f | + {10:[Preview] }| + :%s/[a-z]^ | + ]]) + + feed("/JLKR £") + screen:expect([[ + X Y Z | + 7 8 9 | + K L M | + {12:JLKR £} b c | + {12:JLKR £} e f | + {12:JLKR £} r s | + {12:JLKR £} y z | + £ {12:JLKR £} n | + {12:JLKR £} œ ¥ | + | + {11:[No Name] [+] }| + | 7| {12:JLKR £} b c | + | 8| {12:JLKR £} e f | + {10:[Preview] }| + :%s/[a-z]/JLKR £^ | + ]]) + + feed("\\rѫ ab \\rXXXX") + screen:expect([[ + 7 8 9 | + K L M | + {12:JLKR £} | + {12:ѫ ab } | + {12:XXXX} b c | + {12:JLKR £} | + {12:ѫ ab } | + {12:XXXX} e f | + {12:JLKR £} | + {11:[No Name] [+] }| + | 7| {12:JLKR £} | + | 8|{12: ѫ ab } | + {10:[Preview] }| + :%s/[a-z]/JLKR £\rѫ ab \rXXX| + X^ | + ]]) + end) + + it(", inccomand=split, large cmdwinheight", function() + common_setup(screen, "split", long_multiline_text) + command("set cmdwinheight=11") + + feed(":%s/. .$") + screen:expect([[ + t œ ¥ | + {11:[No Name] [+] }| + | 1| 1 2 3 | + | 2| A B C | + | 3| 4 5 6 | + | 4| X Y Z | + | 5| 7 8 9 | + | 6| K L M | + | 7| a b c | + | 8| d e f | + | 9| q r s | + |10| x y z | + |11| £ m n | + {10:[Preview] }| + :%s/. .$^ | + ]]) + + feed("/ YYY") + screen:expect([[ + t {12: YYY} | + {11:[No Name] [+] }| + | 1| 1 {12: YYY} | + | 2| A {12: YYY} | + | 3| 4 {12: YYY} | + | 4| X {12: YYY} | + | 5| 7 {12: YYY} | + | 6| K {12: YYY} | + | 7| a {12: YYY} | + | 8| d {12: YYY} | + | 9| q {12: YYY} | + |10| x {12: YYY} | + |11| £ {12: YYY} | + {10:[Preview] }| + :%s/. .$/ YYY^ | + ]]) + + feed("\\r KKK") + screen:expect([[ + a {12: YYY} | + {11:[No Name] [+] }| + | 1| 1 {12: YYY} | + | 2|{12: KKK} | + | 3| A {12: YYY} | + | 4|{12: KKK} | + | 5| 4 {12: YYY} | + | 6|{12: KKK} | + | 7| X {12: YYY} | + | 8|{12: KKK} | + | 9| 7 {12: YYY} | + |10|{12: KKK} | + |11| K {12: YYY} | + {10:[Preview] }| + :%s/. .$/ YYY\r KKK^ | + ]]) + end) + + it(", inccomand=split, lookaround", function() + common_setup(screen, "split", "something\neverything\nsomeone") + feed([[:%s/\(some\)\@=thing/one/]]) + screen:expect([[ + some{12:one} | + everything | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| some{12:one} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/\(some\)\@<=thing/one/^ | + ]]) + feed("") + + feed([[:%s/\(some\)\@!thing/one/]]) + screen:expect([[ + something | + every{12:one} | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |2| every{12:one} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/\(some\)\@]]) + + feed([[:%s/some\(thing\)\@=/every/]]) + screen:expect([[ + {12:every}thing | + everything | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| {12:every}thing | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/some\(thing\)\@=/every/^ | + ]]) + feed([[]]) + + feed([[:%s/some\(thing\)\@!/every/]]) + screen:expect([[ + everything | + {12:every}one | + {15:~ }| + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |3| {12:every}one | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/some\(thing\)\@!/every/^ | + ]]) + end) +end) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index cc023ef10d..e22105b804 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -764,7 +764,7 @@ describe(":substitute, inccommand=split", function() feed("x") screen:expect([[ Inc substitution on | - xo lines | + {12:x}o lines | | {15:~ }| {15:~ }| @@ -805,7 +805,7 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw/XX") screen:expect([[ Inc substitution on | - XXo lines | + {12:XX}o lines | | {15:~ }| {15:~ }| @@ -894,7 +894,7 @@ describe(":substitute, inccommand=split", function() feed('M M M') feed(':%s/M/123/g') screen:expect([[ - 123 123 123 | + {12:123} {12:123} {12:123} | Inc substitution on | two lines | Inc substitution on | @@ -919,15 +919,15 @@ describe(":substitute, inccommand=split", function() insert(string.rep('abc abc abc\n', 20)) feed(':%s/abc/MMM/g') screen:expect([[ - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | - MMM MMM MMM | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | + {12:MMM} {12:MMM} {12:MMM} | {11:[No Name] [+] }| | 1| {12:MMM} {12:MMM} {12:MMM} | | 2| {12:MMM} {12:MMM} {12:MMM} | @@ -969,9 +969,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ BBo lines | Inc substitution on | - Xo lines | + {12:X}o lines | Inc substitution on | - Xo lines | + {12:X}o lines | {11:[No Name] [+] }| |1001| {12:X}o lines | |1003| {12:X}o lines | @@ -1069,8 +1069,8 @@ describe(":substitute, inccommand=split", function() feed(":1,2s/t/X") screen:expect([[ - Inc subsXitution on | - Xwo lines | + Inc subs{12:X}itution on | + {12:X}wo lines | Inc substitution on | two lines | | @@ -1134,7 +1134,7 @@ describe("inccommand=nosplit", function() two lines | Inc substitution on | two lines | - Line *.X | + Line *.{12:X} | {15:~ }| {15:~ }| {15:~ }| @@ -1150,7 +1150,7 @@ describe("inccommand=nosplit", function() two lines | Inc substitution on | two lines | - Line *.X here | + Line *.{12:X} here | {15:~ }| {15:~ }| {15:~ }| @@ -1198,9 +1198,9 @@ describe("inccommand=nosplit", function() feed("/BM") screen:expect([[ Inc substitution on | - BMo lines | + {12:BM}o lines | Inc substitution on | - BMo lines | + {12:BM}o lines | | {15:~ }| {15:~ }| @@ -1212,9 +1212,9 @@ describe("inccommand=nosplit", function() feed("/") screen:expect([[ Inc substitution on | - BMo lines | + {12:BM}o lines | Inc substitution on | - BMo lines | + {12:BM}o lines | | {15:~ }| {15:~ }| @@ -1245,8 +1245,8 @@ describe("inccommand=nosplit", function() feed(":1,2s/t/X") screen:expect([[ - Inc subsXitution on | - Xwo lines | + Inc subs{12:X}itution on | + {12:X}wo lines | Inc substitution on | two lines | | From 35b867d78687484a1153e957748547e4d043934a Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Mon, 18 Sep 2017 22:35:05 +0200 Subject: [PATCH 03/13] Lint --- src/nvim/ex_cmds.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index fe0c662898..022ba6a4dd 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -100,9 +100,9 @@ typedef struct { // Collected results of a substitution for showing them in // the preview window -typedef struct { +typedef struct { kvec_t(SubResult) subresults; - linenr_T lines_needed; // lines neede in the preview window + linenr_T lines_needed; // lines neede in the preview window } PreviewLines; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -3434,9 +3434,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // lnum is where the match start, but maybe not the pattern match, // since we can have \n before \zs in the pattern - /* Advance "lnum" to the line where the match starts. The - * match does not start in the first line when there is a line - * break before \zs. */ + // Advance "lnum" to the line where the match starts. The + // match does not start in the first line when there is a line + // break before \zs. if (regmatch.startpos[0].lnum > 0) { current_match.pre_match = lnum; lnum += regmatch.startpos[0].lnum; @@ -3965,7 +3965,6 @@ skip: preview_lines.lines_needed += match_lines; } kv_push(preview_lines.subresults, current_match); - line_breakcheck(); } @@ -4048,11 +4047,12 @@ skip: // Show 'inccommand' preview if there are matched lines. buf_T *preview_buf = NULL; + size_t subsize = preview_lines.subresults.size; if (preview && !aborting()) { if (got_quit) { // Substitution is too slow, disable 'inccommand'. set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, SID_NONE); - } else if (*p_icm != NUL && preview_lines.subresults.size != 0 && pat != NULL) { + } else if (*p_icm != NUL && subsize != 0 && pat != NULL) { if (pre_src_id == 0) { // Get a unique new src_id, saved in a static pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0); From a4e4f2bd02b02975896ef62ec2abba8e15e85745 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Mon, 18 Sep 2017 22:47:09 +0200 Subject: [PATCH 04/13] Move tests into original file, lint, and add a test --- test/functional/ui/icmmulti_spec.lua | 692 ------------------------- test/functional/ui/inccommand_spec.lua | 665 +++++++++++++++++++++++- 2 files changed, 664 insertions(+), 693 deletions(-) delete mode 100644 test/functional/ui/icmmulti_spec.lua diff --git a/test/functional/ui/icmmulti_spec.lua b/test/functional/ui/icmmulti_spec.lua deleted file mode 100644 index 81ceaec111..0000000000 --- a/test/functional/ui/icmmulti_spec.lua +++ /dev/null @@ -1,692 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local curbufmeths = helpers.curbufmeths -local eq = helpers.eq -local eval = helpers.eval -local feed_command = helpers.feed_command -local expect = helpers.expect -local feed = helpers.feed -local insert = helpers.insert -local meths = helpers.meths -local neq = helpers.neq -local ok = helpers.ok -local source = helpers.source -local wait = helpers.wait -local nvim = helpers.nvim - -local multiline_text = [[ - 1 2 3 - A B C - 4 5 6 - X Y Z - 7 8 9 -]] - -local multimatch_text = [[ - a bdc eae a fgl lzia r - x -]] - -local multibyte_text = [[ - £ ¥ ѫѫ PEPPERS -£ ¥ ѫfѫ - a£ ѫ¥KOL -£ ¥ libm -£ ¥ -]] - -local long_multiline_text = [[ - 1 2 3 - A B C - 4 5 6 - X Y Z - 7 8 9 - K L M - a b c - d e f - q r s - x y z - £ m n - t œ ¥ -]] -local function common_setup(screen, inccommand, text) - if screen then - command("syntax on") - command("set nohlsearch") - command("hi Substitute guifg=red guibg=yellow") - screen:attach() - screen:set_default_attr_ids({ - [1] = {foreground = Screen.colors.Fuchsia}, - [2] = {foreground = Screen.colors.Brown, bold = true}, - [3] = {foreground = Screen.colors.SlateBlue}, - [4] = {bold = true, foreground = Screen.colors.SlateBlue}, - [5] = {foreground = Screen.colors.DarkCyan}, - [6] = {bold = true}, - [7] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, - [8] = {foreground = Screen.colors.Slateblue, underline = true}, - [9] = {background = Screen.colors.Yellow}, - [10] = {reverse = true}, - [11] = {reverse = true, bold=true}, - [12] = {foreground = Screen.colors.Red, background = Screen.colors.Yellow}, - [13] = {bold = true, foreground = Screen.colors.SeaGreen}, - [14] = {foreground = Screen.colors.White, background = Screen.colors.Red}, - [15] = {bold=true, foreground=Screen.colors.Blue}, - [16] = {background=Screen.colors.Grey90}, -- cursorline - vis = {background=Screen.colors.LightGrey} - }) - end - - command("set inccommand=" .. (inccommand and inccommand or "")) - - if text then - insert(text) - end -end - -describe(":substitute", function() - local screen = Screen.new(30,15) - - before_each(function() - clear() - end) - - it(", inccomand=split, highlights multiline substitutions", function() - common_setup(screen, "split", multiline_text) - feed("gg") - - feed(":%s/2\\_.*X/MMM") - screen:expect([[ - 1 {12:MMM} Y Z | - 7 8 9 | - | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |1| 1 {12:MMM} Y Z | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/2\_.*X/MMM^ | - ]]) - - feed("\\rK\\rLLL") - screen:expect([[ - 1 {12:MMM} | - {12:K} | - {12:LLL} Y Z | - 7 8 9 | - | - {11:[No Name] [+] }| - |1| 1 {12:MMM} | - |2|{12: K} | - |3|{12: LLL} Y Z | - | - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/2\_.*X/MMM\rK\rLLL^ | - ]]) - end) - - it(", inccomand=nosplit, highlights multiline substitutions", function() - common_setup(screen, "nosplit", multiline_text) - feed("gg") - - feed(":%s/2\\_.*X/MMM") - screen:expect([[ - 1 {12:MMM} Y Z | - 7 8 9 | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/2\_.*X/MMM^ | - ]]) - - feed("\\rK\\rLLL") - screen:expect([[ - 1 {12:MMM} | - {12:K} | - {12:LLL} Y Z | - 7 8 9 | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/2\_.*X/MMM\rK\rLLL^ | - ]]) - end) - - it(", inccomand=split, highlights multiple matches on a line", function() - common_setup(screen, "split", multimatch_text) - command("set gdefault") - feed("gg") - - feed(":%s/a/XLK") - screen:expect([[ - {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| - x | - | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}| - {12:LK} r | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/a/XLK^ | - ]]) - end) - - it(", inccomand=nosplit, highlights multiple matches on a line", function() - common_setup(screen, "nosplit", multimatch_text) - command("set gdefault") - feed("gg") - - feed(":%s/a/XLK") - screen:expect([[ - {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| - x | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/a/XLK^ | - ]]) - end) - - it(", inccomand=split, with \\zs", function() - common_setup(screen, "split", multiline_text) - feed("gg") - - feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") - screen:expect([[ - 1 2 3 | - {12:OKO} B C | - 4 5 6 | - {12:OKO} Y Z | - 7 8 9 | - {11:[No Name] [+] }| - |1| 1 2 3 | - |2| {12:OKO} B C | - |3| 4 5 6 | - |4| {12:OKO} Y Z | - | - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/[0-9]\n\zs[A-Z]/OKO^ | - ]]) - end) - - it(", inccomand=nosplit, with \\zs", function() - common_setup(screen, "nosplit", multiline_text) - feed("gg") - - feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") - screen:expect([[ - 1 2 3 | - {12:OKO} B C | - 4 5 6 | - {12:OKO} Y Z | - 7 8 9 | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/[0-9]\n\zs[A-Z]/OKO^ | - ]]) - end) - - it(", inccomand=split, substitutions of different length", - function() - common_setup(screen, "split", "T T123 T2T TTT T090804\nx") - - feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") - screen:expect([[ - T {12:123123} {12:22}T TTT {12:090804090804} | - x | - {15:~ }| - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |1| T {12:123123} {12:22}T TTT {12:090804090}| - {12:804} | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/T\([0-9]\+\)/\1\1/g^ | - ]]) - end) - - it(", inccomand=nosplit, substitutions of different length", function() - common_setup(screen, "nosplit", "T T123 T2T TTT T090804\nx") - - feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") - screen:expect([[ - T {12:123123} {12:22}T TTT {12:090804090804} | - x | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/T\([0-9]\+\)/\1\1/g^ | - ]]) - end) - - it(", inccomand=split, contraction of lines", function() - local text = [[ - T T123 T T123 T2T TT T23423424 - x - afa Q - adf la;lkd R - alx - ]] - - common_setup(screen, "split", text) - feed(":%s/[QR]\\n") - screen:expect([[ - afa Q | - adf la;lkd R | - alx | - | - {15:~ }| - {11:[No Name] [+] }| - |3| afa Q | - |4| adf la;lkd R | - |5| alx | - | - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/[QR]\n^ | - ]]) - - feed("/KKK") - screen:expect([[ - x | - afa {12:KKK}adf la;lkd {12:KKK}alx | - | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |3| afa {12:KKK}adf la;lkd {12:KKK}alx | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/[QR]\n/KKK^ | - ]]) - end) - - it(", inccomand=nosplit, contraction of lines", function() - local text = [[ - T T123 T T123 T2T TT T23423424 - x - afa Q - adf la;lkd R - alx - ]] - - common_setup(screen, "nosplit", text) - feed(":%s/[QR]\\n/KKK") - screen:expect([[ - T T123 T T123 T2T TT T23423424| - x | - afa {12:KKK}adf la;lkd {12:KKK}alx | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/[QR]\n/KKK^ | - ]]) - end) - - it(", inccommand=split, multibyte text", function() - common_setup(screen, "split", multibyte_text) - feed(":%s/£.*ѫ/X¥¥") - screen:expect([[ - {12:X¥¥} | - a{12:X¥¥}¥KOL | - £ ¥ libm | - £ ¥ | - | - {11:[No Name] [+] }| - |1| {12:X¥¥} PEPPERS | - |2| {12:X¥¥} | - |3| a{12:X¥¥}¥KOL | - | - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/£.*ѫ/X¥¥^ | - ]]) - - feed("\\ra££ ¥") - screen:expect([[ - {12:a££ ¥} | - a{12:X¥¥} | - {12:a££ ¥}¥KOL | - £ ¥ libm | - £ ¥ | - {11:[No Name] [+] }| - |1| {12:X¥¥} | - |2|{12: a££ ¥} PEPPERS | - |3| {12:X¥¥} | - |4|{12: a££ ¥} | - |5| a{12:X¥¥} | - |6|{12: a££ ¥}¥KOL | - | - {10:[Preview] }| - :%s/£.*ѫ/X¥¥\ra££ ¥^ | - ]]) - end) - - it(", inccommand=nosplit, multibyte text", function() - common_setup(screen, "nosplit", multibyte_text) - feed(":%s/£.*ѫ/X¥¥") - screen:expect([[ - {12:X¥¥} PEPPERS | - {12:X¥¥} | - a{12:X¥¥}¥KOL | - £ ¥ libm | - £ ¥ | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/£.*ѫ/X¥¥^ | - ]]) - - feed("\\ra££ ¥") - screen:expect([[ - {12:X¥¥} | - {12:a££ ¥} PEPPERS | - {12:X¥¥} | - {12:a££ ¥} | - a{12:X¥¥} | - {12:a££ ¥}¥KOL | - £ ¥ libm | - £ ¥ | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - :%s/£.*ѫ/X¥¥\ra££ ¥^ | - ]]) - end) - - it(", inccomand=split, small cmdwinheight", function() - common_setup(screen, "split", long_multiline_text) - command("set cmdwinheight=2") - - feed(":%s/[a-z]") - screen:expect([[ - X Y Z | - 7 8 9 | - K L M | - a b c | - d e f | - q r s | - x y z | - £ m n | - t œ ¥ | - | - {11:[No Name] [+] }| - | 7| a b c | - | 8| d e f | - {10:[Preview] }| - :%s/[a-z]^ | - ]]) - - feed("/JLKR £") - screen:expect([[ - X Y Z | - 7 8 9 | - K L M | - {12:JLKR £} b c | - {12:JLKR £} e f | - {12:JLKR £} r s | - {12:JLKR £} y z | - £ {12:JLKR £} n | - {12:JLKR £} œ ¥ | - | - {11:[No Name] [+] }| - | 7| {12:JLKR £} b c | - | 8| {12:JLKR £} e f | - {10:[Preview] }| - :%s/[a-z]/JLKR £^ | - ]]) - - feed("\\rѫ ab \\rXXXX") - screen:expect([[ - 7 8 9 | - K L M | - {12:JLKR £} | - {12:ѫ ab } | - {12:XXXX} b c | - {12:JLKR £} | - {12:ѫ ab } | - {12:XXXX} e f | - {12:JLKR £} | - {11:[No Name] [+] }| - | 7| {12:JLKR £} | - | 8|{12: ѫ ab } | - {10:[Preview] }| - :%s/[a-z]/JLKR £\rѫ ab \rXXX| - X^ | - ]]) - end) - - it(", inccomand=split, large cmdwinheight", function() - common_setup(screen, "split", long_multiline_text) - command("set cmdwinheight=11") - - feed(":%s/. .$") - screen:expect([[ - t œ ¥ | - {11:[No Name] [+] }| - | 1| 1 2 3 | - | 2| A B C | - | 3| 4 5 6 | - | 4| X Y Z | - | 5| 7 8 9 | - | 6| K L M | - | 7| a b c | - | 8| d e f | - | 9| q r s | - |10| x y z | - |11| £ m n | - {10:[Preview] }| - :%s/. .$^ | - ]]) - - feed("/ YYY") - screen:expect([[ - t {12: YYY} | - {11:[No Name] [+] }| - | 1| 1 {12: YYY} | - | 2| A {12: YYY} | - | 3| 4 {12: YYY} | - | 4| X {12: YYY} | - | 5| 7 {12: YYY} | - | 6| K {12: YYY} | - | 7| a {12: YYY} | - | 8| d {12: YYY} | - | 9| q {12: YYY} | - |10| x {12: YYY} | - |11| £ {12: YYY} | - {10:[Preview] }| - :%s/. .$/ YYY^ | - ]]) - - feed("\\r KKK") - screen:expect([[ - a {12: YYY} | - {11:[No Name] [+] }| - | 1| 1 {12: YYY} | - | 2|{12: KKK} | - | 3| A {12: YYY} | - | 4|{12: KKK} | - | 5| 4 {12: YYY} | - | 6|{12: KKK} | - | 7| X {12: YYY} | - | 8|{12: KKK} | - | 9| 7 {12: YYY} | - |10|{12: KKK} | - |11| K {12: YYY} | - {10:[Preview] }| - :%s/. .$/ YYY\r KKK^ | - ]]) - end) - - it(", inccomand=split, lookaround", function() - common_setup(screen, "split", "something\neverything\nsomeone") - feed([[:%s/\(some\)\@=thing/one/]]) - screen:expect([[ - some{12:one} | - everything | - someone | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |1| some{12:one} | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/\(some\)\@<=thing/one/^ | - ]]) - feed("") - - feed([[:%s/\(some\)\@!thing/one/]]) - screen:expect([[ - something | - every{12:one} | - someone | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |2| every{12:one} | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/\(some\)\@]]) - - feed([[:%s/some\(thing\)\@=/every/]]) - screen:expect([[ - {12:every}thing | - everything | - someone | - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |1| {12:every}thing | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/some\(thing\)\@=/every/^ | - ]]) - feed([[]]) - - feed([[:%s/some\(thing\)\@!/every/]]) - screen:expect([[ - everything | - {12:every}one | - {15:~ }| - {15:~ }| - {15:~ }| - {11:[No Name] [+] }| - |3| {12:every}one | - | - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {15:~ }| - {10:[Preview] }| - :%s/some\(thing\)\@!/every/^ | - ]]) - end) -end) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index e22105b804..25466974a9 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -21,6 +21,42 @@ local default_text = [[ two lines ]] +local multiline_text = [[ + 1 2 3 + A B C + 4 5 6 + X Y Z + 7 8 9 +]] + +local multimatch_text = [[ + a bdc eae a fgl lzia r + x +]] + +local multibyte_text = [[ + £ ¥ ѫѫ PEPPERS +£ ¥ ѫfѫ + a£ ѫ¥KOL +£ ¥ libm +£ ¥ +]] + +local long_multiline_text = [[ + 1 2 3 + A B C + 4 5 6 + X Y Z + 7 8 9 + K L M + a b c + d e f + q r s + x y z + £ m n + t œ ¥ +]] + local function common_setup(screen, inccommand, text) if screen then command("syntax on") @@ -1142,7 +1178,6 @@ describe("inccommand=nosplit", function() :%smagic/3.*/X^ | ]]) - feed([[]]) -- cancel feed(":%snomagic/3.*/X") -- start :snomagic command screen:expect([[ @@ -1178,6 +1213,28 @@ describe("inccommand=nosplit", function() feed('') end) + it("does not show window after toggling :set inccomand", function() + feed(":%s/tw/OKOK") + feed("") + command("set icm=split") + feed(":%s/tw/OKOK") + feed("") + command("set icm=nosplit") + feed(":%s/tw/OKOK") + screen:expect([[ + Inc substitution on | + {12:OKOK}o lines | + Inc substitution on | + {12:OKOK}o lines | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/tw/OKOK^ | + ]]) + end) + it('never shows preview buffer', function() feed_command("set hlsearch") @@ -1712,3 +1769,609 @@ describe("'inccommand' with 'gdefault'", function() eq({mode='n', blocking=false}, nvim("get_mode")) end) end) + +describe(":substitute", function() + local screen = Screen.new(30,15) + + before_each(function() + clear() + end) + + it(", inccomand=split, highlights multiline substitutions", function() + common_setup(screen, "split", multiline_text) + feed("gg") + + feed(":%s/2\\_.*X/MMM") + screen:expect([[ + 1 {12:MMM} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| 1 {12:MMM} Y Z | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/2\_.*X/MMM^ | + ]]) + + feed("\\rK\\rLLL") + screen:expect([[ + 1 {12:MMM} | + {12:K} | + {12:LLL} Y Z | + 7 8 9 | + | + {11:[No Name] [+] }| + |1| 1 {12:MMM} | + |2|{12: K} | + |3|{12: LLL} Y Z | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/2\_.*X/MMM\rK\rLLL^ | + ]]) + end) + + it(", inccomand=nosplit, highlights multiline substitutions", function() + common_setup(screen, "nosplit", multiline_text) + feed("gg") + + feed(":%s/2\\_.*X/MMM") + screen:expect([[ + 1 {12:MMM} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/2\_.*X/MMM^ | + ]]) + + feed("\\rK\\rLLL") + screen:expect([[ + 1 {12:MMM} | + {12:K} | + {12:LLL} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/2\_.*X/MMM\rK\rLLL^ | + ]]) + end) + + it(", inccomand=split, highlights multiple matches on a line", function() + common_setup(screen, "split", multimatch_text) + command("set gdefault") + feed("gg") + + feed(":%s/a/XLK") + screen:expect([[ + {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + x | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}| + {12:LK} r | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/a/XLK^ | + ]]) + end) + + it(", inccomand=nosplit, highlights multiple matches on a line", function() + common_setup(screen, "nosplit", multimatch_text) + command("set gdefault") + feed("gg") + + feed(":%s/a/XLK") + screen:expect([[ + {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + x | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/a/XLK^ | + ]]) + end) + + it(", inccomand=split, with \\zs", function() + common_setup(screen, "split", multiline_text) + feed("gg") + + feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") + screen:expect([[ + 1 2 3 | + {12:OKO} B C | + 4 5 6 | + {12:OKO} Y Z | + 7 8 9 | + {11:[No Name] [+] }| + |1| 1 2 3 | + |2| {12:OKO} B C | + |3| 4 5 6 | + |4| {12:OKO} Y Z | + | + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[0-9]\n\zs[A-Z]/OKO^ | + ]]) + end) + + it(", inccomand=nosplit, with \\zs", function() + common_setup(screen, "nosplit", multiline_text) + feed("gg") + + feed(":%s/[0-9]\\n\\zs[A-Z]/OKO") + screen:expect([[ + 1 2 3 | + {12:OKO} B C | + 4 5 6 | + {12:OKO} Y Z | + 7 8 9 | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/[0-9]\n\zs[A-Z]/OKO^ | + ]]) + end) + + it(", inccomand=split, substitutions of different length", + function() + common_setup(screen, "split", "T T123 T2T TTT T090804\nx") + + feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") + screen:expect([[ + T {12:123123} {12:22}T TTT {12:090804090804} | + x | + {15:~ }| + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| T {12:123123} {12:22}T TTT {12:090804090}| + {12:804} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/T\([0-9]\+\)/\1\1/g^ | + ]]) + end) + + it(", inccomand=nosplit, substitutions of different length", function() + common_setup(screen, "nosplit", "T T123 T2T TTT T090804\nx") + + feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g") + screen:expect([[ + T {12:123123} {12:22}T TTT {12:090804090804} | + x | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/T\([0-9]\+\)/\1\1/g^ | + ]]) + end) + + it(", inccomand=split, contraction of lines", function() + local text = [[ + T T123 T T123 T2T TT T23423424 + x + afa Q + adf la;lkd R + alx + ]] + + common_setup(screen, "split", text) + feed(":%s/[QR]\\n") + screen:expect([[ + afa Q | + adf la;lkd R | + alx | + | + {15:~ }| + {11:[No Name] [+] }| + |3| afa Q | + |4| adf la;lkd R | + |5| alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[QR]\n^ | + ]]) + + feed("/KKK") + screen:expect([[ + x | + afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |3| afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/[QR]\n/KKK^ | + ]]) + end) + + it(", inccomand=nosplit, contraction of lines", function() + local text = [[ + T T123 T T123 T2T TT T23423424 + x + afa Q + adf la;lkd R + alx + ]] + + common_setup(screen, "nosplit", text) + feed(":%s/[QR]\\n/KKK") + screen:expect([[ + T T123 T T123 T2T TT T23423424| + x | + afa {12:KKK}adf la;lkd {12:KKK}alx | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/[QR]\n/KKK^ | + ]]) + end) + + it(", inccommand=split, multibyte text", function() + common_setup(screen, "split", multibyte_text) + feed(":%s/£.*ѫ/X¥¥") + screen:expect([[ + {12:X¥¥} | + a{12:X¥¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {11:[No Name] [+] }| + |1| {12:X¥¥} PEPPERS | + |2| {12:X¥¥} | + |3| a{12:X¥¥}¥KOL | + | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/£.*ѫ/X¥¥^ | + ]]) + + feed("\\ra££ ¥") + screen:expect([[ + {12:a££ ¥} | + a{12:X¥¥} | + {12:a££ ¥}¥KOL | + £ ¥ libm | + £ ¥ | + {11:[No Name] [+] }| + |1| {12:X¥¥} | + |2|{12: a££ ¥} PEPPERS | + |3| {12:X¥¥} | + |4|{12: a££ ¥} | + |5| a{12:X¥¥} | + |6|{12: a££ ¥}¥KOL | + | + {10:[Preview] }| + :%s/£.*ѫ/X¥¥\ra££ ¥^ | + ]]) + end) + + it(", inccommand=nosplit, multibyte text", function() + common_setup(screen, "nosplit", multibyte_text) + feed(":%s/£.*ѫ/X¥¥") + screen:expect([[ + {12:X¥¥} PEPPERS | + {12:X¥¥} | + a{12:X¥¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/£.*ѫ/X¥¥^ | + ]]) + + feed("\\ra££ ¥") + screen:expect([[ + {12:X¥¥} | + {12:a££ ¥} PEPPERS | + {12:X¥¥} | + {12:a££ ¥} | + a{12:X¥¥} | + {12:a££ ¥}¥KOL | + £ ¥ libm | + £ ¥ | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/£.*ѫ/X¥¥\ra££ ¥^ | + ]]) + end) + + it(", inccomand=split, small cmdwinheight", function() + common_setup(screen, "split", long_multiline_text) + command("set cmdwinheight=2") + + feed(":%s/[a-z]") + screen:expect([[ + X Y Z | + 7 8 9 | + K L M | + a b c | + d e f | + q r s | + x y z | + £ m n | + t œ ¥ | + | + {11:[No Name] [+] }| + | 7| a b c | + | 8| d e f | + {10:[Preview] }| + :%s/[a-z]^ | + ]]) + + feed("/JLKR £") + screen:expect([[ + X Y Z | + 7 8 9 | + K L M | + {12:JLKR £} b c | + {12:JLKR £} e f | + {12:JLKR £} r s | + {12:JLKR £} y z | + £ {12:JLKR £} n | + {12:JLKR £} œ ¥ | + | + {11:[No Name] [+] }| + | 7| {12:JLKR £} b c | + | 8| {12:JLKR £} e f | + {10:[Preview] }| + :%s/[a-z]/JLKR £^ | + ]]) + + feed("\\rѫ ab \\rXXXX") + screen:expect([[ + 7 8 9 | + K L M | + {12:JLKR £} | + {12:ѫ ab } | + {12:XXXX} b c | + {12:JLKR £} | + {12:ѫ ab } | + {12:XXXX} e f | + {12:JLKR £} | + {11:[No Name] [+] }| + | 7| {12:JLKR £} | + | 8|{12: ѫ ab } | + {10:[Preview] }| + :%s/[a-z]/JLKR £\rѫ ab \rXXX| + X^ | + ]]) + end) + + it(", inccomand=split, large cmdwinheight", function() + common_setup(screen, "split", long_multiline_text) + command("set cmdwinheight=11") + + feed(":%s/. .$") + screen:expect([[ + t œ ¥ | + {11:[No Name] [+] }| + | 1| 1 2 3 | + | 2| A B C | + | 3| 4 5 6 | + | 4| X Y Z | + | 5| 7 8 9 | + | 6| K L M | + | 7| a b c | + | 8| d e f | + | 9| q r s | + |10| x y z | + |11| £ m n | + {10:[Preview] }| + :%s/. .$^ | + ]]) + + feed("/ YYY") + screen:expect([[ + t {12: YYY} | + {11:[No Name] [+] }| + | 1| 1 {12: YYY} | + | 2| A {12: YYY} | + | 3| 4 {12: YYY} | + | 4| X {12: YYY} | + | 5| 7 {12: YYY} | + | 6| K {12: YYY} | + | 7| a {12: YYY} | + | 8| d {12: YYY} | + | 9| q {12: YYY} | + |10| x {12: YYY} | + |11| £ {12: YYY} | + {10:[Preview] }| + :%s/. .$/ YYY^ | + ]]) + + feed("\\r KKK") + screen:expect([[ + a {12: YYY} | + {11:[No Name] [+] }| + | 1| 1 {12: YYY} | + | 2|{12: KKK} | + | 3| A {12: YYY} | + | 4|{12: KKK} | + | 5| 4 {12: YYY} | + | 6|{12: KKK} | + | 7| X {12: YYY} | + | 8|{12: KKK} | + | 9| 7 {12: YYY} | + |10|{12: KKK} | + |11| K {12: YYY} | + {10:[Preview] }| + :%s/. .$/ YYY\r KKK^ | + ]]) + end) + + it(", inccomand=split, lookaround", function() + common_setup(screen, "split", "something\neverything\nsomeone") + feed([[:%s/\(some\)\@=thing/one/]]) + screen:expect([[ + some{12:one} | + everything | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| some{12:one} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/\(some\)\@<=thing/one/^ | + ]]) + feed("") + + feed([[:%s/\(some\)\@!thing/one/]]) + screen:expect([[ + something | + every{12:one} | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |2| every{12:one} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/\(some\)\@]]) + + feed([[:%s/some\(thing\)\@=/every/]]) + screen:expect([[ + {12:every}thing | + everything | + someone | + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |1| {12:every}thing | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/some\(thing\)\@=/every/^ | + ]]) + feed([[]]) + + feed([[:%s/some\(thing\)\@!/every/]]) + screen:expect([[ + everything | + {12:every}one | + {15:~ }| + {15:~ }| + {15:~ }| + {11:[No Name] [+] }| + |3| {12:every}one | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/some\(thing\)\@!/every/^ | + ]]) + end) +end) From 3e5ecd95386da2122e495ed4fda566f6e1997206 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Fri, 22 Sep 2017 21:37:13 +0200 Subject: [PATCH 05/13] Remove pat/sub from show_sub They were only used to not show the preview window when typing "s/" or "s//" only, in which case the previous pattern would be reused. Now the window is shown in that case. --- src/nvim/ex_cmds.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 022ba6a4dd..b74aad46bf 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4061,8 +4061,8 @@ skip: pre_hl_id = syn_check_group((char_u *)"Substitute", 13); } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, pat, sub, &preview_lines, - has_second_delim, pre_hl_id, pre_src_id); + preview_buf = show_sub(eap, old_cursor, &preview_lines, has_second_delim, + pre_hl_id, pre_src_id); bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, kv_last(preview_lines.subresults).end.lnum); } @@ -6071,7 +6071,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, +static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, bool show_hl, int hl_id, int src_id) FUNC_ATTR_NONNULL_ALL @@ -6081,8 +6081,6 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, win_T *save_curwin = curwin; cmdmod_T save_cmdmod = cmdmod; char_u *save_shm_p = vim_strsave(p_shm); - size_t sub_size = mb_string2cells(sub); - size_t pat_size = mb_string2cells(pat); PreviewLines lines = *preview_lines; buf_T *orig_buf = curbuf; @@ -6096,7 +6094,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, bool outside_curline = (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); - bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size); + bool split = outside_curline && (*p_icm != 'n'); if (preview_buf == curbuf) { // Preview buffer cannot preview itself! split = false; preview_buf = NULL; From d8bb1dabb63501fe4075f062d4ed81bbc62170d0 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 23 Sep 2017 22:57:15 +0200 Subject: [PATCH 06/13] Fix the last line in the preview buffer It would always show an empty line at the end that didn't belong. --- src/nvim/ex_cmds.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b74aad46bf..b9f14f27ea 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6177,7 +6177,11 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, // Put "|lnum| line" into `str` and append it to the preview buffer. snprintf(str, line_size, "|%*ld| %s", col_width - 3, next_linenr, line); - ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false); + if (linenr_preview == 0) { + ml_replace(1, (char_u *)str, true); + } else { + ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false); + } linenr_preview += 1; } linenr_origbuf = match.end.lnum; From be20b20cf3a845abf8e62f005a9ccbb0cde03e7d Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 23 Sep 2017 22:57:43 +0200 Subject: [PATCH 07/13] Adjust tests for the new preview window ... that does not have that superflous last line. Also, remove some indeterminism for the freebsd64 tests. Partially, those were suggested by the tests themselves, while successfull. Some of them were added after some testing because the lookaround test would fail on freebsd64 only. --- test/functional/ui/inccommand_spec.lua | 60 ++++++++++++++------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 25466974a9..9196e0548e 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -725,7 +725,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] }| |2| two lines | |4| two lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -767,7 +767,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| two lines | |4| two lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -788,7 +788,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| o lines | |4| o lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -807,7 +807,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| {12:x}o lines | |4| {12:x}o lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -826,7 +826,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| o lines | |4| o lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -848,7 +848,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| {12:XX}o lines | |4| {12:XX}o lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -915,7 +915,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |2| {9:tw}o lines | |4| {9:tw}o lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -937,7 +937,7 @@ describe(":substitute, inccommand=split", function() two lines | {11:[No Name] [+] }| |1| {12:123} {12:123} {12:123} | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1113,7 +1113,7 @@ describe(":substitute, inccommand=split", function() {11:[No Name] [+] }| |1| Inc subs{12:X}itution on | |2| {12:X}wo lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1221,6 +1221,7 @@ describe("inccommand=nosplit", function() feed("") command("set icm=nosplit") feed(":%s/tw/OKOK") + wait() screen:expect([[ Inc substitution on | {12:OKOK}o lines | @@ -1641,7 +1642,7 @@ describe("'inccommand' split windows", function() {15:~ }{10:|}{15:~ }| {10:[No Name] [+] [No Name] [+] }| |2| two lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1680,7 +1681,7 @@ describe("'inccommand' split windows", function() {15:~ }| {10:[No Name] [+] }| |2| two lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1730,7 +1731,7 @@ describe("'inccommand' split windows", function() {15:~ }| {11:[No Name] [+] }| |2| two lines | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1790,7 +1791,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |1| 1 {12:MMM} Y Z | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1811,7 +1812,7 @@ describe(":substitute", function() |1| 1 {12:MMM} | |2|{12: K} | |3|{12: LLL} Y Z | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1878,7 +1879,7 @@ describe(":substitute", function() {11:[No Name] [+] }| |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}| {12:LK} r | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -1929,7 +1930,7 @@ describe(":substitute", function() |2| {12:OKO} B C | |3| 4 5 6 | |4| {12:OKO} Y Z | - | + {15:~ }| {15:~ }| {15:~ }| {10:[Preview] }| @@ -1975,7 +1976,7 @@ describe(":substitute", function() {11:[No Name] [+] }| |1| T {12:123123} {12:22}T TTT {12:090804090}| {12:804} | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2029,7 +2030,7 @@ describe(":substitute", function() |3| afa Q | |4| adf la;lkd R | |5| alx | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2046,7 +2047,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |3| afa {12:KKK}adf la;lkd {12:KKK}alx | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2100,7 +2101,7 @@ describe(":substitute", function() |1| {12:X¥¥} PEPPERS | |2| {12:X¥¥} | |3| a{12:X¥¥}¥KOL | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2122,7 +2123,7 @@ describe(":substitute", function() |4|{12: a££ ¥} | |5| a{12:X¥¥} | |6|{12: a££ ¥}¥KOL | - | + {15:~ }| {10:[Preview] }| :%s/£.*ѫ/X¥¥\ra££ ¥^ | ]]) @@ -2304,7 +2305,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |1| some{12:one} | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2313,8 +2314,9 @@ describe(":substitute", function() {10:[Preview] }| :%s/\(some\)\@<=thing/one/^ | ]]) - feed("") + feed("") + wait() feed([[:%s/\(some\)\@!thing/one/]]) screen:expect([[ something | @@ -2324,7 +2326,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |2| every{12:one} | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2333,8 +2335,9 @@ describe(":substitute", function() {10:[Preview] }| :%s/\(some\)\@]]) + feed([[]]) + wait() feed([[:%s/some\(thing\)\@=/every/]]) screen:expect([[ {12:every}thing | @@ -2344,7 +2347,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |1| {12:every}thing | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| @@ -2353,8 +2356,9 @@ describe(":substitute", function() {10:[Preview] }| :%s/some\(thing\)\@=/every/^ | ]]) - feed([[]]) + feed([[]]) + wait() feed([[:%s/some\(thing\)\@!/every/]]) screen:expect([[ everything | @@ -2364,7 +2368,7 @@ describe(":substitute", function() {15:~ }| {11:[No Name] [+] }| |3| {12:every}one | - | + {15:~ }| {15:~ }| {15:~ }| {15:~ }| From 353c81af1e3cea8d32f834def9ffcbae86c5c727 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 30 Sep 2017 23:20:28 +0200 Subject: [PATCH 08/13] Clear highlight when there's no match --- src/nvim/ex_cmds.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b9f14f27ea..baf2595772 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4052,7 +4052,7 @@ skip: if (got_quit) { // Substitution is too slow, disable 'inccommand'. set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, SID_NONE); - } else if (*p_icm != NUL && subsize != 0 && pat != NULL) { + } else if (*p_icm != NUL && pat != NULL) { if (pre_src_id == 0) { // Get a unique new src_id, saved in a static pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0); @@ -4063,8 +4063,10 @@ skip: curbuf->b_changed = save_b_changed; // preserve 'modified' during preview preview_buf = show_sub(eap, old_cursor, &preview_lines, has_second_delim, pre_hl_id, pre_src_id); - bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, - kv_last(preview_lines.subresults).end.lnum); + if (subsize > 0) { + bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, + kv_last(preview_lines.subresults).end.lnum); + } } } @@ -6128,9 +6130,11 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, curwin->w_p_spell = false; curwin->w_p_fen = false; - // Width of the "| lnum|..." column which displays the line numbers. - highest_num_line = kv_last(lines.subresults).end.lnum; - col_width = log10(highest_num_line) + 1 + 3; + if (lines.subresults.size > 0) { + // Width of the "| lnum|..." column which displays the line numbers. + highest_num_line = kv_last(lines.subresults).end.lnum; + col_width = log10(highest_num_line) + 1 + 3; + } } char *str = NULL; // construct the line to show in here From ab942b7ffb08ffa2f7f417916d8f0a509fd39af4 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 30 Sep 2017 23:20:40 +0200 Subject: [PATCH 09/13] Test for clearing the highlight --- test/functional/ui/inccommand_spec.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 9196e0548e..da0f05bf07 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -948,6 +948,28 @@ describe(":substitute, inccommand=split", function() ]]) end) + it('highlights nothing when there\'s no match', function() + feed('gg') + feed(':%s/Inx') + screen:expect([[ + Inc substitution on | + two lines | + Inc substitution on | + two lines | + | + {11:[No Name] [+] }| + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/Inx^ | + ]]) + end) + it('previews correctly when previewhight is small', function() feed_command('set cwh=3') feed_command('set hls') From 0c358725b189f7d25c15c4fc7c454218316793b5 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sat, 23 Sep 2017 20:38:52 +0200 Subject: [PATCH 10/13] Fix highlighting conflict --- src/nvim/ex_cmds.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index baf2595772..ab4c52a635 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3683,6 +3683,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // go beyond the last line of the buffer. if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; + current_match.end.lnum = sub_firstlnum + nmatch; skip_match = true; } @@ -3715,7 +3716,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // be shown also, but should not be highlighted. Intentional for now. if (preview && !has_second_delim) { current_match.start.col = regmatch.startpos[0].col; - current_match.end.lnum = sub_firstlnum + nmatch - 1; + if (current_match.end.lnum == 0) { + current_match.end.lnum = sub_firstlnum + nmatch - 1; + } current_match.end.col = regmatch.endpos[0].col; ADJUST_SUB_FIRSTLNUM(); @@ -4061,7 +4064,7 @@ skip: pre_hl_id = syn_check_group((char_u *)"Substitute", 13); } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, &preview_lines, has_second_delim, + preview_buf = show_sub(eap, old_cursor, &preview_lines, true, pre_hl_id, pre_src_id); if (subsize > 0) { bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, @@ -6170,13 +6173,18 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, if (next_linenr == match.end.lnum) { p_end.lnum = linenr_preview + 1; } - char_u *line = ml_get_buf(orig_buf, next_linenr, false); - line_size = STRLEN(line) + col_width + 1; + char_u *line; + if (next_linenr == orig_buf->b_ml.ml_line_count + 1) { + line = (char_u *)""; + } else { + line = ml_get_buf(orig_buf, next_linenr, false); + line_size = STRLEN(line) + col_width + 1; - // Reallocate if line not long enough - if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); - old_line_size = line_size; + // Reallocate if line not long enough + if (line_size > old_line_size) { + str = xrealloc(str, line_size * sizeof(char)); + old_line_size = line_size; + } } // Put "|lnum| line" into `str` and append it to the preview buffer. snprintf(str, line_size, "|%*ld| %s", col_width - 3, @@ -6250,7 +6258,11 @@ void ex_substitute(exarg_T *eap) curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' + // Don't show search highlighting during live substitution + bool save_hls = p_hls; + p_hls = false; buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt)); + p_hls = save_hls; if (save_changedtick != curbuf->b_changedtick) { // Undo invisibly. This also moves the cursor! From 369ac900f97059de21d946aa460d4ef2b6899b3e Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sun, 24 Sep 2017 13:57:47 +0200 Subject: [PATCH 11/13] Adjust tests for new highlighting. Also extend an old test to show of the new way. --- test/functional/ui/inccommand_spec.lua | 113 +++++++++++++++---------- 1 file changed, 66 insertions(+), 47 deletions(-) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index da0f05bf07..88de2f07de 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -718,13 +718,13 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw") screen:expect([[ Inc substitution on | - two lines | + {12:tw}o lines | | {15:~ }| {15:~ }| {11:[No Name] }| - |2| two lines | - |4| two lines | + |2| {12:tw}o lines | + |4| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -760,13 +760,13 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw") screen:expect([[ Inc substitution on | - two lines | + {12:tw}o lines | | {15:~ }| {15:~ }| {11:[No Name] [+] }| - |2| two lines | - |4| two lines | + |2| {12:tw}o lines | + |4| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -864,7 +864,7 @@ describe(":substitute, inccommand=split", function() screen:sleep(1) screen:expect([[ Inc substitution on | - two lines | + {12:tw}o lines | Inc substitution on | two lines | | @@ -908,13 +908,13 @@ describe(":substitute, inccommand=split", function() -- 'cursorline' is NOT active during preview. screen:expect([[ Inc substitution on | - {9:tw}o lines | + {12:tw}o lines | Inc substitution on | - {9:tw}o lines | + {12:tw}o lines | | {11:[No Name] [+] }| - |2| {9:tw}o lines | - |4| {9:tw}o lines | + |2| {12:tw}o lines | + |4| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -1264,9 +1264,9 @@ describe("inccommand=nosplit", function() feed(":%s/tw") screen:expect([[ Inc substitution on | - {9:tw}o lines | + {12:tw}o lines | Inc substitution on | - {9:tw}o lines | + {12:tw}o lines | | {15:~ }| {15:~ }| @@ -1643,7 +1643,7 @@ describe("'inccommand' split windows", function() feed(":%s/tw") screen:expect([[ Inc substitution on {10:|}Inc substitution on| - two lines {10:|}two lines | + {12:tw}o lines {10:|}{12:tw}o lines | {10:|} | {15:~ }{10:|}{15:~ }| {15:~ }{10:|}{15:~ }| @@ -1658,12 +1658,12 @@ describe("'inccommand' split windows", function() {15:~ }{10:|}{15:~ }| {11:[No Name] [+] }{10:|}{15:~ }| Inc substitution on {10:|}{15:~ }| - two lines {10:|}{15:~ }| + {12:tw}o lines {10:|}{15:~ }| {10:|}{15:~ }| {15:~ }{10:|}{15:~ }| {15:~ }{10:|}{15:~ }| {10:[No Name] [+] [No Name] [+] }| - |2| two lines | + |2| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -1682,7 +1682,7 @@ describe("'inccommand' split windows", function() feed(":%s/tw") screen:expect([[ Inc substitution on {10:|}Inc substitution on| - two lines {10:|}two lines | + {12:tw}o lines {10:|}{12:tw}o lines | {10:|} | {15:~ }{10:|}{15:~ }| {15:~ }{10:|}{15:~ }| @@ -1697,12 +1697,12 @@ describe("'inccommand' split windows", function() {15:~ }{10:|}{15:~ }| {11:[No Name] [+] }{10:[No Name] [+] }| Inc substitution on | - two lines | + {12:tw}o lines | | {15:~ }| {15:~ }| {10:[No Name] [+] }| - |2| two lines | + |2| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -1732,7 +1732,7 @@ describe("'inccommand' split windows", function() screen:expect([[ Inc substitution on | - two lines | + {12:tw}o lines | | {15:~ }| {15:~ }| @@ -1752,7 +1752,7 @@ describe("'inccommand' split windows", function() {15:~ }| {15:~ }| {11:[No Name] [+] }| - |2| two lines | + |2| {12:tw}o lines | {15:~ }| {15:~ }| {15:~ }| @@ -1804,7 +1804,26 @@ describe(":substitute", function() common_setup(screen, "split", multiline_text) feed("gg") - feed(":%s/2\\_.*X/MMM") + feed(":%s/2\\_.*X") + screen:expect([[ + 1 {12:2 3} | + {12:A B C} | + {12:4 5 6} | + {12:X} Y Z | + 7 8 9 | + {11:[No Name] [+] }| + |1| 1 {12:2 3} | + |2|{12: A B C} | + |3|{12: 4 5 6} | + |4|{12: X} Y Z | + {15:~ }| + {15:~ }| + {15:~ }| + {10:[Preview] }| + :%s/2\_.*X^ | + ]]) + + feed("/MMM") screen:expect([[ 1 {12:MMM} Y Z | 7 8 9 | @@ -2043,15 +2062,15 @@ describe(":substitute", function() common_setup(screen, "split", text) feed(":%s/[QR]\\n") screen:expect([[ - afa Q | - adf la;lkd R | + afa {12:Q} | + adf la;lkd {12:R} | alx | | {15:~ }| {11:[No Name] [+] }| - |3| afa Q | - |4| adf la;lkd R | - |5| alx | + |3| afa {12:Q} | + |4|{12: }adf la;lkd {12:R} | + |5|{12: }alx | {15:~ }| {15:~ }| {15:~ }| @@ -2201,16 +2220,16 @@ describe(":substitute", function() X Y Z | 7 8 9 | K L M | - a b c | - d e f | - q r s | - x y z | - £ m n | - t œ ¥ | + {12:a} b c | + {12:d} e f | + {12:q} r s | + {12:x} y z | + £ {12:m} n | + {12:t} œ ¥ | | {11:[No Name] [+] }| - | 7| a b c | - | 8| d e f | + | 7| {12:a} b c | + | 8| {12:d} e f | {10:[Preview] }| :%s/[a-z]^ | ]]) @@ -2260,19 +2279,19 @@ describe(":substitute", function() feed(":%s/. .$") screen:expect([[ - t œ ¥ | + t {12:œ ¥} | {11:[No Name] [+] }| - | 1| 1 2 3 | - | 2| A B C | - | 3| 4 5 6 | - | 4| X Y Z | - | 5| 7 8 9 | - | 6| K L M | - | 7| a b c | - | 8| d e f | - | 9| q r s | - |10| x y z | - |11| £ m n | + | 1| 1 {12:2 3} | + | 2| A {12:B C} | + | 3| 4 {12:5 6} | + | 4| X {12:Y Z} | + | 5| 7 {12:8 9} | + | 6| K {12:L M} | + | 7| a {12:b c} | + | 8| d {12:e f} | + | 9| q {12:r s} | + |10| x {12:y z} | + |11| £ {12:m n} | {10:[Preview] }| :%s/. .$^ | ]]) From 7b4baad6747a1f812bdf62e5c8c7b59a6977ce44 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sun, 24 Sep 2017 14:17:59 +0200 Subject: [PATCH 12/13] Remove superflous parameter from show_sub --- src/nvim/ex_cmds.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ab4c52a635..85e65f32c2 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4064,7 +4064,7 @@ skip: pre_hl_id = syn_check_group((char_u *)"Substitute", 13); } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, &preview_lines, true, + preview_buf = show_sub(eap, old_cursor, &preview_lines, pre_hl_id, pre_src_id); if (subsize > 0) { bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, @@ -6077,8 +6077,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, - PreviewLines *preview_lines, bool show_hl, int hl_id, - int src_id) + PreviewLines *preview_lines, int hl_id, int src_id) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6198,15 +6197,11 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, } linenr_origbuf = match.end.lnum; - if (show_hl) { - bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, - p_end, col_width); - } - } - if (show_hl) { - bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, - match.end, 0); + bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, + p_end, col_width); } + bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, + match.end, 0); } xfree(str); From 4daf63871adc2713ded2a6b8c07fe0ac4b6b3fc1 Mon Sep 17 00:00:00 2001 From: KillTheMule Date: Sun, 29 Oct 2017 18:21:26 +0100 Subject: [PATCH 13/13] Fix cmd modifier tests for the new highlight --- test/functional/ui/inccommand_spec.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 88de2f07de..ceb2c83dd9 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -740,13 +740,13 @@ describe(":substitute, inccommand=split", function() it("shows preview when cmd modifiers are present", function() -- one modifier feed(':keeppatterns %s/tw/to') - screen:expect([[too lines]], nil, nil, nil, true) + screen:expect([[{12:to}o lines]], nil, nil, nil, true) feed('') screen:expect([[two lines]], nil, nil, nil, true) -- multiple modifiers feed(':keeppatterns silent %s/tw/to') - screen:expect([[too lines]], nil, nil, nil, true) + screen:expect([[{12:to}o lines]], nil, nil, nil, true) feed('') screen:expect([[two lines]], nil, nil, nil, true) @@ -1219,13 +1219,13 @@ describe("inccommand=nosplit", function() it("shows preview when cmd modifiers are present", function() -- one modifier feed(':keeppatterns %s/tw/to') - screen:expect([[too lines]], nil, nil, nil, true) + screen:expect([[{12:to}o lines]], nil, nil, nil, true) feed('') screen:expect([[two lines]], nil, nil, nil, true) -- multiple modifiers feed(':keeppatterns silent %s/tw/to') - screen:expect([[too lines]], nil, nil, nil, true) + screen:expect([[{12:to}o lines]], nil, nil, nil, true) feed('') screen:expect([[two lines]], nil, nil, nil, true)