diff options
author | James McCoy <jamessan@jamessan.com> | 2016-08-21 15:06:58 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-21 15:06:58 -0400 |
commit | a26d52ea328e64ab08dae369e5a7c551bb05abf7 (patch) | |
tree | ea2900718c45a8b56450ea3f5ba0e9396eb46ff6 /src | |
parent | 71b3e20d0fba20d4957c1949352bde1744a0a947 (diff) | |
parent | c317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba (diff) | |
download | rneovim-a26d52ea328e64ab08dae369e5a7c551bb05abf7.tar.gz rneovim-a26d52ea328e64ab08dae369e5a7c551bb05abf7.tar.bz2 rneovim-a26d52ea328e64ab08dae369e5a7c551bb05abf7.zip |
Merge pull request #5231 from jamessan/vim-7.4.1700
vim-patch:7.4.1700,7.4.2219
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/ex_cmds.c | 541 | ||||
-rw-r--r-- | src/nvim/testdir/test_alot.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_regexp_utf8.vim | 41 | ||||
-rw-r--r-- | src/nvim/version.c | 3 |
4 files changed, 350 insertions, 236 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5de9ac0523..b7691997d7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -64,6 +64,24 @@ */ typedef struct sign sign_T; +/// Case matching style to use for :substitute +typedef enum { + kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options + kSubIgnoreCase, ///< Ignore case of the search + kSubMatchCase, ///< Match case of the search +} SubIgnoreType; + +/// Flags kept between calls to :substitute. +typedef struct { + bool do_all; ///< do multiple substitutions per line + bool do_ask; ///< ask for confirmation + bool do_count; ///< count only + bool do_error; ///< if false, ignore errors + bool do_print; ///< print last line with subs + bool do_list; ///< list last line with subs + bool do_number; ///< list last line with line nr + SubIgnoreType do_ic; ///< ignore case flag +} subflags_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" @@ -2901,6 +2919,159 @@ void sub_set_replacement(SubReplacementString sub) old_sub = sub; } +/// Recognize ":%s/\n//" and turn it into a join command, which is much +/// more efficient. +/// +/// @param[in] eap Ex arguments +/// @param[in] pat Search pattern +/// @param[in] sub Replacement string +/// @param[in] cmd Command from :s_flags +/// +/// @returns true if :substitute can be replaced with a join command +static bool sub_joining_lines(exarg_T *eap, char_u *pat, + char_u *sub, char_u *cmd) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) +{ + // TODO(vim): find a generic solution to make line-joining operations more + // efficient, avoid allocating a string that grows in size. + if (pat != NULL + && strcmp((const char *)pat, "\\n") == 0 + && *sub == NUL + && (*cmd == NUL || (cmd[1] == NUL + && (*cmd == 'g' + || *cmd == 'l' + || *cmd == 'p' + || *cmd == '#')))) { + curwin->w_cursor.lnum = eap->line1; + if (*cmd == 'l') { + eap->flags = EXFLAG_LIST; + } else if (*cmd == '#') { + eap->flags = EXFLAG_NR; + } else if (*cmd == 'p') { + eap->flags = EXFLAG_PRINT; + } + + // The number of lines joined is the number of lines in the range + linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 + // plus one extra line if not at the end of file. + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); + if (joined_lines_count > 1) { + do_join(joined_lines_count, FALSE, TRUE, FALSE, true); + sub_nsubs = joined_lines_count - 1; + sub_nlines = 1; + do_sub_msg(false); + ex_may_print(eap); + } + + if (!cmdmod.keeppatterns) { + save_re_pat(RE_SUBST, pat, p_magic); + } + add_to_history(HIST_SEARCH, pat, TRUE, NUL); + + return true; + } + + return false; +} + +/// Allocate memory to store the replacement text for :substitute. +/// +/// Slightly more memory that is strictly necessary is allocated to reduce the +/// frequency of memory (re)allocation. +/// +/// @param[in,out] new_start pointer to the memory for the replacement text +/// @param[in] needed_len amount of memory needed +/// +/// @returns pointer to the end of the allocated memory +static char_u *sub_grow_buf(char_u **new_start, int needed_len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET +{ + int new_start_len = 0; + char_u *new_end; + if (*new_start == NULL) { + // Get some space for a temporary buffer to do the + // substitution into (and some extra space to avoid + // too many calls to xmalloc()/free()). + new_start_len = needed_len + 50; + *new_start = xmalloc(new_start_len); + **new_start = NUL; + new_end = *new_start; + } else { + // Check if the temporary buffer is long enough to do the + // substitution into. If not, make it larger (with a bit + // extra to avoid too many calls to xmalloc()/free()). + size_t len = STRLEN(*new_start); + needed_len += len; + if (needed_len > new_start_len) { + new_start_len = needed_len + 50; + *new_start = xrealloc(*new_start, new_start_len); + } + new_end = *new_start + len; + } + + return new_end; +} + +/// Parse cmd string for :substitute's {flags} and update subflags accordingly +/// +/// @param[in] cmd command string +/// @param[in,out] subflags current flags defined for the :substitute command +/// @param[in,out] which_pat pattern type from which to get default search +/// +/// @returns pointer to the end of the flags, which may be the end of the string +static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, + int *which_pat) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + // Find trailing options. When '&' is used, keep old options. + if (*cmd == '&') { + cmd++; + } else { + subflags->do_all = p_gd; + subflags->do_ask = false; + subflags->do_error = true; + subflags->do_print = false; + subflags->do_count = false; + subflags->do_number = false; + subflags->do_ic = kSubHonorOptions; + } + while (*cmd) { + // Note that 'g' and 'c' are always inverted. + // 'r' is never inverted. + if (*cmd == 'g') { + subflags->do_all = !subflags->do_all; + } else if (*cmd == 'c') { + subflags->do_ask = !subflags->do_ask; + } else if (*cmd == 'n') { + subflags->do_count = true; + } else if (*cmd == 'e') { + subflags->do_error = !subflags->do_error; + } else if (*cmd == 'r') { // use last used regexp + *which_pat = RE_LAST; + } else if (*cmd == 'p') { + subflags->do_print = true; + } else if (*cmd == '#') { + subflags->do_print = true; + subflags->do_number = true; + } else if (*cmd == 'l') { + subflags->do_print = true; + subflags->do_list = true; + } else if (*cmd == 'i') { // ignore case + subflags->do_ic = kSubIgnoreCase; + } else if (*cmd == 'I') { // don't ignore case + subflags->do_ic = kSubMatchCase; + } else { + break; + } + cmd++; + } + if (subflags->do_count) { + subflags->do_ask = false; + } + + return cmd; +} + /* do_sub() * * Perform a substitution from line eap->line1 to line eap->line2 using the @@ -2912,41 +3083,35 @@ void sub_set_replacement(SubReplacementString sub) */ void do_sub(exarg_T *eap) { - linenr_T lnum; long i = 0; regmmatch_T regmatch; - static int do_all = FALSE; /* do multiple substitutions per line */ - static int do_ask = FALSE; /* ask for confirmation */ - static bool do_count = false; /* count only */ - static int do_error = TRUE; /* if false, ignore errors */ - static int do_print = FALSE; /* print last line with subs. */ - static int do_list = FALSE; /* list last line with subs. */ - static int do_number = FALSE; /* list last line with line nr*/ - static int do_ic = 0; /* ignore case flag */ - int save_do_all; // remember user specified 'g' flag - int save_do_ask; // remember user specified 'c' flag - char_u *pat = NULL, *sub = NULL; /* init for GCC */ + static subflags_T subflags = { + .do_all = false, + .do_ask = false, + .do_count = false, + .do_error = true, + .do_print = false, + .do_list = false, + .do_number = false, + .do_ic = kSubHonorOptions + }; + char_u *pat = NULL, *sub = NULL; // init for GCC int delimiter; int sublen; - int got_quit = FALSE; - int got_match = FALSE; - int temp; + int got_quit = false; + int got_match = false; int which_pat; - char_u *cmd; - int save_State; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line= 0; /* below last changed line AFTER the - * change */ + char_u *cmd = eap->arg; + linenr_T first_line = 0; // first changed line + linenr_T last_line= 0; // below last changed line AFTER the + // change linenr_T old_line_count = curbuf->b_ml.ml_line_count; - linenr_T line2; - long nmatch; /* number of lines in match */ - char_u *sub_firstline; /* allocated copy of first sub line */ - int endcolumn = FALSE; /* cursor in last column when done */ + char_u *sub_firstline; // allocated copy of first sub line + bool endcolumn = false; // cursor in last column when done pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; - cmd = eap->arg; if (!global_busy) { sub_nsubs = 0; sub_nlines = 0; @@ -3028,104 +3193,20 @@ void do_sub(exarg_T *eap) endcolumn = (curwin->w_curswant == MAXCOL); } - // Recognize ":%s/\n//" and turn it into a join command, which is much - // more efficient. - // TODO: find a generic solution to make line-joining operations more - // efficient, avoid allocating a string that grows in size. - if (pat != NULL - && strcmp((const char *)pat, "\\n") == 0 - && *sub == NUL - && (*cmd == NUL || (cmd[1] == NUL - && (*cmd == 'g' - || *cmd == 'l' - || *cmd == 'p' - || *cmd == '#')))) { - curwin->w_cursor.lnum = eap->line1; - if (*cmd == 'l') { - eap->flags = EXFLAG_LIST; - } else if (*cmd == '#') { - eap->flags = EXFLAG_NR; - } else if (*cmd == 'p') { - eap->flags = EXFLAG_PRINT; - } - - // The number of lines joined is the number of lines in the range - linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 - // plus one extra line if not at the end of file. - + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); - if (joined_lines_count > 1) { - do_join(joined_lines_count, FALSE, TRUE, FALSE, true); - sub_nsubs = joined_lines_count - 1; - sub_nlines = 1; - do_sub_msg(false); - ex_may_print(eap); - } - - if (!cmdmod.keeppatterns) { - save_re_pat(RE_SUBST, pat, p_magic); - } - add_to_history(HIST_SEARCH, pat, TRUE, NUL); - + if (sub_joining_lines(eap, pat, sub, cmd)) { return; } - /* - * Find trailing options. When '&' is used, keep old options. - */ - if (*cmd == '&') { - ++cmd; - } else { - // default is global on - do_all = p_gd ? TRUE : FALSE; - - do_ask = FALSE; - do_error = TRUE; - do_print = FALSE; - do_count = false; - do_number = FALSE; - do_ic = 0; - } - while (*cmd) { - // Note that 'g' and 'c' are always inverted. - // 'r' is never inverted. - if (*cmd == 'g') - do_all = !do_all; - else if (*cmd == 'c') - do_ask = !do_ask; - else if (*cmd == 'n') - do_count = true; - else if (*cmd == 'e') - do_error = !do_error; - else if (*cmd == 'r') /* use last used regexp */ - which_pat = RE_LAST; - else if (*cmd == 'p') - do_print = TRUE; - else if (*cmd == '#') { - do_print = TRUE; - do_number = TRUE; - } else if (*cmd == 'l') { - do_print = TRUE; - do_list = TRUE; - } else if (*cmd == 'i') /* ignore case */ - do_ic = 'i'; - else if (*cmd == 'I') /* don't ignore case */ - do_ic = 'I'; - else - break; - ++cmd; - } - if (do_count) { - do_ask = FALSE; - } + cmd = sub_parse_flags(cmd, &subflags, &which_pat); - save_do_all = do_all; - save_do_ask = do_ask; + bool save_do_all = subflags.do_all; // remember user specified 'g' flag + bool save_do_ask = subflags.do_ask; // remember user specified 'c' flag // check for a trailing count cmd = skipwhite(cmd); if (ascii_isdigit(*cmd)) { i = getdigits_long(&cmd); - if (i <= 0 && !eap->skip && do_error) { + if (i <= 0 && !eap->skip && subflags.do_error) { EMSG(_(e_zerocount)); return; } @@ -3150,24 +3231,25 @@ void do_sub(exarg_T *eap) if (eap->skip) /* not executing commands, only parsing */ return; - if (!do_count && !MODIFIABLE(curbuf)) { - /* Substitution is not allowed in non-'modifiable' buffer */ + if (!subflags.do_count && !MODIFIABLE(curbuf)) { + // Substitution is not allowed in non-'modifiable' buffer EMSG(_(e_modifiable)); return; } - if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, - ®match) == FAIL) { - if (do_error) + if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) { + if (subflags.do_error) { EMSG(_(e_invcmd)); + } return; } - /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ - if (do_ic == 'i') - regmatch.rmm_ic = TRUE; - else if (do_ic == 'I') - regmatch.rmm_ic = FALSE; + // the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' + if (subflags.do_ic == kSubIgnoreCase) { + regmatch.rmm_ic = true; + } else if (subflags.do_ic == kSubMatchCase) { + regmatch.rmm_ic = false; + } sub_firstline = NULL; @@ -3179,29 +3261,25 @@ void do_sub(exarg_T *eap) if (!(sub[0] == '\\' && sub[1] == '=')) sub = regtilde(sub, p_magic); - /* - * Check for a match on each line. - */ - line2 = eap->line2; - for (lnum = eap->line1; lnum <= line2 && !(got_quit - || aborting() - ); ++lnum) { - nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + // Check for a match on each line. + linenr_T line2 = eap->line2; + for (linenr_T lnum = eap->line1; + lnum <= line2 && !(got_quit || aborting()); + lnum++) { + long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, + (colnr_T)0, NULL); if (nmatch) { colnr_T copycol; colnr_T matchcol; colnr_T prev_matchcol = MAXCOL; char_u *new_end, *new_start = NULL; - unsigned new_start_len = 0; char_u *p1; int did_sub = FALSE; int lastone; - int len, copy_len, needed_len; - long nmatch_tl = 0; /* nr of lines matched below lnum */ - int do_again; /* do it again after joining lines */ - int skip_match = FALSE; - linenr_T sub_firstlnum; /* nr of first sub line */ + long nmatch_tl = 0; // nr of lines matched below lnum + int do_again; // do it again after joining lines + int skip_match = false; + linenr_T sub_firstlnum; // nr of first sub line /* * The new text is build up step by step, to avoid too much @@ -3241,8 +3319,7 @@ void do_sub(exarg_T *eap) * accordingly. * * The new text is built up in new_start[]. It has some extra - * room to avoid using xmalloc()/free() too often. new_start_len is - * the length of the allocated memory at new_start. + * room to avoid using xmalloc()/free() too often. * * Make a copy of the old line, so it won't be taken away when * updating the screen or handling a multi-line match. The "old_" @@ -3261,9 +3338,9 @@ void do_sub(exarg_T *eap) /* * Loop until nothing more to replace in this line. * 1. Handle match with empty string. - * 2. If do_ask is set, ask for confirmation. + * 2. If subflags.do_ask is set, ask for confirmation. * 3. substitute the string. - * 4. if do_all is set, find next match + * 4. if subflags.do_all is set, find next match * 5. break if there isn't another match in this line */ for (;; ) { @@ -3314,15 +3391,13 @@ void do_sub(exarg_T *eap) matchcol = regmatch.endpos[0].col; prev_matchcol = matchcol; - /* - * 2. If do_count is set only increase the counter. - * If do_ask is set, ask for confirmation. - */ - if (do_count) { - /* For a multi-line match, put matchcol at the NUL at - * the end of the line and set nmatch to one, so that - * we continue looking for a match on the next line. - * Avoids that ":s/\nB\@=//gc" get stuck. */ + // 2. If subflags.do_count is set only increase the counter. + // If do_ask is set, ask for confirmation. + if (subflags.do_count) { + // For a multi-line match, put matchcol at the NUL at + // the end of the line and set nmatch to one, so that + // we continue looking for a match on the next line. + // Avoids that ":s/\nB\@=//gc" get stuck. if (nmatch > 1) { matchcol = (colnr_T)STRLEN(sub_firstline); nmatch = 1; @@ -3336,12 +3411,12 @@ void do_sub(exarg_T *eap) goto skip; } - if (do_ask) { + if (subflags.do_ask) { int typed = 0; /* change State to CONFIRM, so that the mouse works * properly */ - save_State = State; + int save_State = State; State = CONFIRM; setmouse(); /* disable mouse in xterm */ curwin->w_cursor.col = regmatch.startpos[0].col; @@ -3354,17 +3429,17 @@ void do_sub(exarg_T *eap) /* * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. */ - while (do_ask) { + while (subflags.do_ask) { if (exmode_active) { char_u *resp; colnr_T sc, ec; - print_line_no_prefix(lnum, do_number, do_list); + print_line_no_prefix(lnum, subflags.do_number, subflags.do_list); getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL); curwin->w_cursor.col = regmatch.endpos[0].col - 1; getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); - if (do_number || curwin->w_p_nu) { + if (subflags.do_number || curwin->w_p_nu) { int numw = number_width(curwin) + 1; sc += numw; ec += numw; @@ -3388,7 +3463,7 @@ void do_sub(exarg_T *eap) curwin->w_p_fen = FALSE; /* Invert the matched string. * Remove the inversion afterwards. */ - temp = RedrawingDisabled; + int temp = RedrawingDisabled; RedrawingDisabled = 0; if (new_start != NULL) { @@ -3468,13 +3543,13 @@ void do_sub(exarg_T *eap) if (typed == 'y') break; if (typed == 'l') { - /* last: replace and then stop */ - do_all = FALSE; + // last: replace and then stop + subflags.do_all = false; line2 = lnum; break; } if (typed == 'a') { - do_ask = FALSE; + subflags.do_ask = false; break; } if (typed == Ctrl_E) @@ -3510,19 +3585,26 @@ void do_sub(exarg_T *eap) /* * 3. substitute the string. */ - if (do_count) { - /* prevent accidentally changing the buffer by a function */ + if (subflags.do_count) { + // prevent accidentally changing the buffer by a function save_ma = curbuf->b_p_ma; - curbuf->b_p_ma = FALSE; + curbuf->b_p_ma = false; sandbox++; } - /* get length of substitution part */ + // Save flags for recursion. They can change for e.g. + // :s/^/\=execute("s#^##gn") + subflags_T subflags_save = subflags; + // get length of substitution part sublen = vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, sub_firstline, FALSE, p_magic, TRUE); - if (do_count) { + sub_firstlnum - regmatch.startpos[0].lnum, + sub, sub_firstline, false, p_magic, true); + // Don't keep flags set by a recursive call + subflags = subflags_save; + if (subflags.do_count) { curbuf->b_p_ma = save_ma; - sandbox--; + if (sandbox > 0) { + sandbox--; + } goto skip; } @@ -3545,33 +3627,10 @@ void do_sub(exarg_T *eap) p1 = ml_get(sub_firstlnum + nmatch - 1); nmatch_tl += nmatch - 1; } - copy_len = regmatch.startpos[0].col - copycol; - needed_len = copy_len + ((unsigned)STRLEN(p1) - - regmatch.endpos[0].col) + sublen + 1; - if (new_start == NULL) { - /* - * Get some space for a temporary buffer to do the - * substitution into (and some extra space to avoid - * too many calls to xmalloc()/free()). - */ - new_start_len = needed_len + 50; - new_start = xmalloc(new_start_len); - *new_start = NUL; - new_end = new_start; - } else { - /* - * Check if the temporary buffer is long enough to do the - * substitution into. If not, make it larger (with a bit - * extra to avoid too many calls to xmalloc()/free()). - */ - len = (unsigned)STRLEN(new_start); - needed_len += len; - if (needed_len > (int)new_start_len) { - new_start_len = needed_len + 50; - new_start = xrealloc(new_start, new_start_len); - } - new_end = new_start + len; - } + size_t copy_len = regmatch.startpos[0].col - copycol; + new_end = sub_grow_buf(&new_start, + copy_len + (STRLEN(p1) - regmatch.endpos[0].col) + + sublen + 1); /* * copy the text up to the part that matched @@ -3595,11 +3654,12 @@ void do_sub(exarg_T *eap) 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 - do_all = FALSE; + // 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. */ @@ -3630,11 +3690,12 @@ void do_sub(exarg_T *eap) ml_append(lnum - 1, new_start, (colnr_T)(p1 - new_start + 1), FALSE); mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); - if (do_ask) + if (subflags.do_ask) { appended_lines(lnum - 1, 1L); - else { - if (first_line == 0) + } else { + if (first_line == 0) { first_line = lnum; + } last_line = lnum + 1; } /* All line numbers increase. */ @@ -3651,12 +3712,10 @@ void do_sub(exarg_T *eap) p1 += (*mb_ptr2len)(p1) - 1; } - /* - * 4. If do_all is set, find next match. - * Prevent endless loop with patterns that match empty - * strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g. - * But ":s/\n/#/" is OK. - */ + // 4. If subflags.do_all is set, find next match. + // Prevent endless loop with patterns that match empty + // strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g. + // But ":s/\n/#/" is OK. skip: /* We already know that we did the last subst when we are at * the end of the line, except that a pattern like @@ -3667,7 +3726,7 @@ skip: || got_int || got_quit || lnum > line2 - || !(do_all || do_again) + || !(subflags.do_all || do_again) || (sub_firstline[matchcol] == NUL && nmatch <= 1 && !re_multiline(regmatch.regprog))); nmatch = -1; @@ -3717,21 +3776,23 @@ skip: for (i = 0; i < nmatch_tl; ++i) ml_delete(lnum, (int)FALSE); mark_adjust(lnum, lnum + nmatch_tl - 1, - (long)MAXLNUM, -nmatch_tl); - if (do_ask) + (long)MAXLNUM, -nmatch_tl); + if (subflags.do_ask) { deleted_lines(lnum, nmatch_tl); - --lnum; - line2 -= nmatch_tl; /* nr of lines decreases */ + } + lnum--; + line2 -= nmatch_tl; // nr of lines decreases nmatch_tl = 0; } /* When asking, undo is saved each time, must also set * changed flag each time. */ - if (do_ask) + if (subflags.do_ask) { changed_bytes(lnum, 0); - else { - if (first_line == 0) + } else { + if (first_line == 0) { first_line = lnum; + } last_line = lnum + 1; } @@ -3784,9 +3845,10 @@ skip: xfree(sub_firstline); /* may have to free allocated copy of the line */ - /* ":s/pat//n" doesn't move the cursor */ - if (do_count) + // ":s/pat//n" doesn't move the cursor + if (subflags.do_count) { curwin->w_cursor = old_cursor; + } if (sub_nsubs > start_nsubs) { /* Set the '[ and '] marks. */ @@ -3795,28 +3857,37 @@ skip: curbuf->b_op_start.col = curbuf->b_op_end.col = 0; if (!global_busy) { - if (!do_ask) { /* when interactive leave cursor on the match */ - if (endcolumn) + // when interactive leave cursor on the match + if (!subflags.do_ask) { + if (endcolumn) { coladvance((colnr_T)MAXCOL); - else + } else { beginline(BL_WHITE | BL_FIX); + } } - if (!do_sub_msg(do_count) && do_ask) + if (!do_sub_msg(subflags.do_count) && subflags.do_ask) { MSG(""); - } else - global_need_beginline = TRUE; - if (do_print) - print_line(curwin->w_cursor.lnum, do_number, do_list); + } + } else { + global_need_beginline = true; + } + if (subflags.do_print) { + print_line(curwin->w_cursor.lnum, subflags.do_number, subflags.do_list); + } } else if (!global_busy) { - if (got_int) /* interrupted */ + if (got_int) { + // interrupted EMSG(_(e_interr)); - else if (got_match) /* did find something but nothing substituted */ + } else if (got_match) { + // did find something but nothing substituted MSG(""); - else if (do_error) /* nothing found */ + } else if (subflags.do_error) { + // nothing found EMSG2(_(e_patnotf2), get_search_pat()); + } } - if (do_ask && hasAnyFolding(curwin)) { + if (subflags.do_ask && hasAnyFolding(curwin)) { // Cursor position may require updating changed_window_setting(); } @@ -3824,9 +3895,9 @@ skip: vim_regfree(regmatch.regprog); // Restore the flag values, they can be used for ":&&". - do_all = save_do_all; - do_ask = save_do_ask; -} + subflags.do_all = save_do_all; + subflags.do_ask = save_do_ask; +} // NOLINT(readability/fn_size) /* * Give message for number of substitutions. diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 30b8a9ceb8..daf6f026ba 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -6,4 +6,5 @@ source test_cursor_func.vim source test_cmdline.vim source test_menu.vim source test_popup.vim +source test_regexp_utf8.vim source test_unlet.vim diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim new file mode 100644 index 0000000000..38f9ed41d5 --- /dev/null +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -0,0 +1,41 @@ +" Tests for regexp in utf8 encoding +scriptencoding utf-8 + +func s:equivalence_test() + let str = "AÀÁÂÃÄÅĀĂĄǍǞǠẢ BḂḆ CÇĆĈĊČ DĎĐḊḎḐ EÈÉÊËĒĔĖĘĚẺẼ FḞ GĜĞĠĢǤǦǴḠ HĤĦḢḦḨ IÌÍÎÏĨĪĬĮİǏỈ JĴ KĶǨḰḴ LĹĻĽĿŁḺ MḾṀ NÑŃŅŇṄṈ OÒÓÔÕÖØŌŎŐƠǑǪǬỎ PṔṖ Q RŔŖŘṘṞ SŚŜŞŠṠ TŢŤŦṪṮ UÙÚÛÜŨŪŬŮŰŲƯǓỦ VṼ WŴẀẂẄẆ XẊẌ YÝŶŸẎỲỶỸ ZŹŻŽƵẐẔ aàáâãäåāăąǎǟǡả bḃḇ cçćĉċč dďđḋḏḑ eèéêëēĕėęěẻẽ fḟ gĝğġģǥǧǵḡ hĥħḣḧḩẖ iìíîïĩīĭįǐỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṁ nñńņňʼnṅṉ oòóôõöøōŏőơǒǫǭỏ pṕṗ q rŕŗřṙṟ sśŝşšṡ tţťŧṫṯẗ uùúûüũūŭůűųưǔủ vṽ wŵẁẃẅẇẘ xẋẍ yýÿŷẏẙỳỷỹ zźżžƶẑẕ" + let groups = split(str) + for group1 in groups + for c in split(group1, '\zs') + " next statement confirms that equivalence class matches every + " character in group + call assert_match('^[[=' . c . '=]]*$', group1) + for group2 in groups + if group2 != group1 + " next statement converts that equivalence class doesn't match + " character in any other group + call assert_equal(-1, match(group2, '[[=' . c . '=]]')) + endif + endfor + endfor + endfor +endfunc + +func Test_equivalence_re1() + set re=1 + call s:equivalence_test() + set re=0 +endfunc + +func Test_equivalence_re2() + set re=2 + call s:equivalence_test() + set re=0 +endfunc + +func Test_recursive_substitute() + new + s/^/\=execute("s#^##gn") + " check we are now not in the sandbox + call setwinvar(1, 'myvar', 1) + bwipe! +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 1cb8d128aa..978bc2e228 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -75,6 +75,7 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 2219, // 2200, // 2199, // 2198, @@ -577,7 +578,7 @@ static int included_patches[] = { 1703, // 1702, // 1701, - // 1700, + 1700, // 1699, // 1698 NA // 1697, |