aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r--src/nvim/ex_cmds.c413
1 files changed, 273 insertions, 140 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 8dcb3ac449..c396b5891a 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"
@@ -1169,7 +1175,7 @@ static void do_filter(
// to read the error messages. Otherwise errors are ignored, so you can see
// the error messages from the command that appear on stdout; use 'u' to fix
// the text.
- // Pass on the kShellDoOut flag when the output is being redirected.
+ // Pass on the kShellOptDoOut flag when the output is being redirected.
if (call_shell(
cmd_buf,
kShellOptFilter | shell_flags,
@@ -1411,7 +1417,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#else
// For shells that don't understand braces around commands, at least allow
// the use of commands in a pipe.
- xstrlcpy(buf, cmd, len);
+ xstrlcpy(buf, (char *)cmd, len);
if (itmp != NULL) {
// If there is a pipe, we have to put the '<' in front of it.
// Don't do this when 'shellquote' is not empty, otherwise the
@@ -2007,9 +2013,10 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum,
} else
other = (fnum != curbuf->b_fnum);
- if (other)
- ++no_wait_return; /* don't wait for autowrite message */
- if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf)
+ if (other) {
+ no_wait_return++; // don't wait for autowrite message
+ }
+ if (other && !forceit && curbuf->b_nwindows == 1 && !buf_hide(curbuf)
&& curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) {
if (p_confirm && p_write)
dialog_changed(curbuf, FALSE);
@@ -2026,17 +2033,19 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum,
if (setpm)
setpcmark();
if (!other) {
- if (lnum != 0)
+ if (lnum != 0) {
curwin->w_cursor.lnum = lnum;
+ }
check_cursor_lnum();
beginline(BL_SOL | BL_FIX);
- retval = 0; /* it's in the same file */
+ retval = 0; // it's in the same file
} else if (do_ecmd(fnum, ffname, sfname, NULL, lnum,
- (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0),
- curwin) == OK)
- retval = -1; /* opened another file */
- else
- retval = 1; /* error encountered */
+ (buf_hide(curbuf) ? ECMD_HIDE : 0)
+ + (forceit ? ECMD_FORCEIT : 0), curwin) == OK) {
+ retval = -1; // opened another file
+ } else {
+ retval = 1; // error encountered
+ }
theend:
xfree(free_me);
@@ -2802,16 +2811,18 @@ void ex_z(exarg_T *eap)
int j;
linenr_T lnum = eap->line2;
- /* Vi compatible: ":z!" uses display height, without a count uses
- * 'scroll' */
- if (eap->forceit)
+ // Vi compatible: ":z!" uses display height, without a count uses
+ // 'scroll'
+ if (eap->forceit) {
bigness = curwin->w_height;
- else if (firstwin == lastwin)
+ } else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2;
- else
+ } else {
bigness = curwin->w_height - 3;
- if (bigness < 1)
+ }
+ if (bigness < 1) {
bigness = 1;
+ }
x = eap->arg;
kind = x;
@@ -3166,7 +3177,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;
@@ -3330,9 +3344,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
sub = regtilde(sub, p_magic);
// Check for a match on each line.
+ // If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2;
for (linenr_T lnum = eap->line1;
- lnum <= line2 && !(got_quit || aborting());
+ lnum <= line2 && !got_quit && !aborting()
+ && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
+ || lnum <= curwin->w_botline);
lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL);
@@ -3396,8 +3413,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) {
@@ -3414,10 +3429,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
* 5. break if there isn't another match in this line
*/
for (;; ) {
- /* 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. */
+ 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;
@@ -3425,6 +3449,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));
}
@@ -3434,12 +3462,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.
@@ -3459,6 +3481,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
else
++matchcol;
}
+ // match will be pushed to preview_lines, bring it into a proper state
+ current_match.start.col = matchcol;
+ current_match.end.lnum = sub_firstlnum;
+ current_match.end.col = matchcol;
goto skip;
}
@@ -3497,6 +3523,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
setmouse(); /* disable mouse in xterm */
curwin->w_cursor.col = regmatch.startpos[0].col;
+ if (curwin->w_p_crb) {
+ do_check_cursorbind();
+ }
+
/* When 'cpoptions' contains "u" don't sync undo when
* asking for confirmation. */
if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
@@ -3514,6 +3544,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = regmatch.endpos[0].col - 1;
+ if (curwin->w_cursor.col < 0) {
+ curwin->w_cursor.col = 0;
+ }
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;
@@ -3656,6 +3689,54 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
* use "\=col("."). */
curwin->w_cursor.col = regmatch.startpos[0].col;
+ // When the match included the "$" of the last line it may
+ // 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;
+ }
+
+#define ADJUST_SUB_FIRSTLNUM() \
+ do { \
+ /* For a multi-line match, make a copy of the last matched */ \
+ /* line and continue in that one. */ \
+ if (nmatch > 1) { \
+ sub_firstlnum += nmatch - 1; \
+ xfree(sub_firstline); \
+ sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \
+ /* When going beyond the last line, stop substituting. */ \
+ if (sub_firstlnum <= line2) { \
+ do_again = true; \
+ } else { \
+ subflags.do_all = false; \
+ } \
+ } \
+ if (skip_match) { \
+ /* Already hit end of the buffer, sub_firstlnum is one */ \
+ /* less than what it ought to be. */ \
+ xfree(sub_firstline); \
+ sub_firstline = vim_strsave((char_u *)""); \
+ copycol = 0; \
+ } \
+ } 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;
+ 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();
+ lnum += nmatch - 1;
+
+ goto skip;
+ }
+
// 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern.
if (!preview || has_second_delim) {
@@ -3682,13 +3763,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
goto skip;
}
- // When the match included the "$" of the last line it may
- // 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;
- skip_match = true;
- }
-
// Need room for:
// - result so far in new_start (not for first sub in line)
// - original text up to match
@@ -3709,6 +3783,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(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
sub, new_end, true, p_magic, true);
@@ -3719,30 +3797,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
// is beyond the end of the line after the substitution.
curwin->w_cursor.col = 0;
- // For a multi-line match, make a copy of the last matched
- // line and continue in that one.
- if (nmatch > 1) {
- sub_firstlnum += nmatch - 1;
- xfree(sub_firstline);
- sub_firstline = vim_strsave(ml_get(sub_firstlnum));
- // When going beyond the last line, stop substituting.
- if (sub_firstlnum <= line2) {
- do_again = true;
- } else {
- subflags.do_all = false;
- }
- }
-
// Remember next character to be copied.
copycol = regmatch.endpos[0].col;
- if (skip_match) {
- // Already hit end of the buffer, sub_firstlnum is one
- // less than what it ought to be.
- xfree(sub_firstline);
- sub_firstline = vim_strsave((char_u *)"");
- copycol = 0;
- }
+ ADJUST_SUB_FIRSTLNUM();
// Now the trick is to replace CTRL-M chars with a real line
// break. This would make it impossible to insert a CTRL-M in
@@ -3781,6 +3839,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.
@@ -3889,9 +3949,30 @@ skip:
* found the match. */
if (nmatch == -1)
lnum -= regmatch.startpos[0].lnum;
+
+#define PUSH_PREVIEW_LINES() \
+ do { \
+ linenr_T match_lines = current_match.end.lnum \
+ - current_match.start.lnum +1; \
+ if (preview_lines.subresults.size > 0) { \
+ linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
+ if (last == 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); \
+ } while (0)
+
+ // Push the match to preview_lines.
+ PUSH_PREVIEW_LINES();
+
break;
}
}
+ // Push the match to preview_lines.
+ PUSH_PREVIEW_LINES();
line_breakcheck();
}
@@ -3901,12 +3982,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();
@@ -3981,24 +4056,34 @@ 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 && matched_lines.size != 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);
+ }
+ if (pre_hl_id == 0) {
+ pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute"));
+ }
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, &preview_lines,
+ pre_hl_id, pre_src_id);
+ if (subsize > 0) {
+ 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
+#undef PUSH_PREVIEW_LINES
} // NOLINT(readability/fn_size)
/*
@@ -4538,18 +4623,20 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la
static char *(mtable[]) = {"*", "g*", "[*", "]*",
"/*", "/\\*", "\"*", "**",
"/\\(\\)", "/\\%(\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??",
"/\\?", "/\\z(\\)", "\\=", ":s\\=",
- "[count]", "[quotex]", "[range]",
+ "[count]", "[quotex]",
+ "[range]", ":[range]",
"[pattern]", "\\|", "\\%$",
"s/\\~", "s/\\U", "s/\\L",
"s/\\1", "s/\\2", "s/\\3", "s/\\9"};
static char *(rtable[]) = {"star", "gstar", "[star", "]star",
"/star", "/\\\\star", "quotestar", "starstar",
"/\\\\(\\\\)", "/\\\\%(\\\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
+ "?", ":?", "?<CR>", "g?", "g?g?", "g??",
"/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
- "\\[count]", "\\[quotex]", "\\[range]",
+ "\\[count]", "\\[quotex]",
+ "\\[range]", ":\\[range]",
"\\[pattern]", "\\\\bar", "/\\\\%\\$",
"s/\\\\\\~", "s/\\\\U", "s/\\\\L",
"s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"};
@@ -4777,7 +4864,9 @@ void fix_help_buffer(void)
char_u *rt;
// Set filetype to "help".
- set_option_value("ft", 0L, "help", OPT_LOCAL);
+ if (STRCMP(curbuf->b_p_ft, "help") != 0) {
+ set_option_value("ft", 0L, "help", OPT_LOCAL);
+ }
if (!syntax_present(curwin)) {
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
@@ -5022,8 +5111,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
- if (!got_int)
- EMSG2("E151: No match: %s", NameBuff);
+ if (!got_int) {
+ EMSG2(_("E151: No match: %s"), NameBuff);
+ }
return;
}
@@ -5212,7 +5302,6 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
if (!add_pathsep((char *)NameBuff)
|| STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
EMSG(_(e_fnametoolong));
- xfree(dirname);
return;
}
@@ -5222,8 +5311,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
- EMSG2("E151: No match: %s", NameBuff);
- xfree(dirname);
+ EMSG2(_("E151: No match: %s"), NameBuff);
return;
}
@@ -5740,7 +5828,8 @@ static void sign_list_defined(sign_T *sp)
}
if (sp->sn_line_hl > 0) {
msg_puts(" linehl=");
- const char *const p = get_highlight_name(NULL, sp->sn_line_hl - 1);
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_line_hl - 1, false);
if (p == NULL) {
msg_puts("NONE");
} else {
@@ -5749,7 +5838,8 @@ static void sign_list_defined(sign_T *sp)
}
if (sp->sn_text_hl > 0) {
msg_puts(" texthl=");
- const char *const p = get_highlight_name(NULL, sp->sn_text_hl - 1);
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_text_hl - 1, false);
if (p == NULL) {
msg_puts("NONE");
} else {
@@ -5997,8 +6087,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)
+static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
+ 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
@@ -6006,8 +6096,8 @@ 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;
// We keep a special-purpose buffer around, but don't assume it exists.
buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0;
@@ -6019,22 +6109,26 @@ 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;
}
// 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();
@@ -6049,44 +6143,77 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
curwin->w_p_spell = false;
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;
-
- 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);
-
- // 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;
-
- // 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 (lines.subresults.size > 0) {
+ 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
+ size_t old_line_size = 0;
+ size_t line_size = 0;
+ linenr_T linenr_preview = 0; // last line added to preview buffer
+ linenr_T linenr_origbuf = 0; // last line added to original buffer
+ linenr_T next_linenr = 0; // next line to show for the match
+
+ for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) {
+ SubResult match = lines.subresults.items[matchidx];
+
+ 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
+ }
+
+ 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 *line;
+ if (next_linenr == orig_buf->b_ml.ml_line_count + 1) {
+ line = "";
+ } else {
+ line = (char *)ml_get_buf(orig_buf, next_linenr, false);
+ line_size = strlen(line) + col_width + 1;
- // 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);
+ // 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);
+ 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;
+
+ bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start,
+ p_end, col_width);
}
- xfree(str);
+ 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
@@ -6136,7 +6263,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!
@@ -6215,7 +6346,6 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
void ex_oldfiles(exarg_T *eap)
{
list_T *l = get_vim_var_list(VV_OLDFILES);
- listitem_T *li;
long nr = 0;
if (l == NULL) {
@@ -6223,19 +6353,22 @@ void ex_oldfiles(exarg_T *eap)
} else {
msg_start();
msg_scroll = true;
- for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) {
+ TV_LIST_ITER(l, li, {
+ if (got_int) {
+ break;
+ }
nr++;
- const char *fname = tv_get_string(&li->li_tv);
+ const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
if (!message_filtered((char_u *)fname)) {
msg_outnum(nr);
MSG_PUTS(": ");
- msg_outtrans((char_u *)tv_get_string(&li->li_tv));
+ msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li)));
msg_clr_eos();
msg_putchar('\n');
ui_flush(); // output one line at a time
os_breakcheck();
}
- }
+ });
// Assume "got_int" was set to truncate the listing.
got_int = false;
@@ -6245,7 +6378,7 @@ void ex_oldfiles(exarg_T *eap)
quit_more = false;
nr = prompt_for_number(false);
msg_starthere();
- if (nr > 0 && nr <= l->lv_len) {
+ if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
if (p == NULL) {
return;