mirror of
https://github.com/neovim/neovim.git
synced 2025-11-30 06:00:42 +00:00
Merge pull request #5231 from jamessan/vim-7.4.1700
vim-patch:7.4.1700,7.4.2219
This commit is contained in:
@@ -64,6 +64,24 @@
|
|||||||
*/
|
*/
|
||||||
typedef struct sign sign_T;
|
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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_cmds.c.generated.h"
|
# include "ex_cmds.c.generated.h"
|
||||||
@@ -2901,6 +2919,159 @@ void sub_set_replacement(SubReplacementString sub)
|
|||||||
old_sub = 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()
|
/* do_sub()
|
||||||
*
|
*
|
||||||
* Perform a substitution from line eap->line1 to line eap->line2 using the
|
* 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)
|
void do_sub(exarg_T *eap)
|
||||||
{
|
{
|
||||||
linenr_T lnum;
|
|
||||||
long i = 0;
|
long i = 0;
|
||||||
regmmatch_T regmatch;
|
regmmatch_T regmatch;
|
||||||
static int do_all = FALSE; /* do multiple substitutions per line */
|
static subflags_T subflags = {
|
||||||
static int do_ask = FALSE; /* ask for confirmation */
|
.do_all = false,
|
||||||
static bool do_count = false; /* count only */
|
.do_ask = false,
|
||||||
static int do_error = TRUE; /* if false, ignore errors */
|
.do_count = false,
|
||||||
static int do_print = FALSE; /* print last line with subs. */
|
.do_error = true,
|
||||||
static int do_list = FALSE; /* list last line with subs. */
|
.do_print = false,
|
||||||
static int do_number = FALSE; /* list last line with line nr*/
|
.do_list = false,
|
||||||
static int do_ic = 0; /* ignore case flag */
|
.do_number = false,
|
||||||
int save_do_all; // remember user specified 'g' flag
|
.do_ic = kSubHonorOptions
|
||||||
int save_do_ask; // remember user specified 'c' flag
|
};
|
||||||
char_u *pat = NULL, *sub = NULL; /* init for GCC */
|
char_u *pat = NULL, *sub = NULL; // init for GCC
|
||||||
int delimiter;
|
int delimiter;
|
||||||
int sublen;
|
int sublen;
|
||||||
int got_quit = FALSE;
|
int got_quit = false;
|
||||||
int got_match = FALSE;
|
int got_match = false;
|
||||||
int temp;
|
|
||||||
int which_pat;
|
int which_pat;
|
||||||
char_u *cmd;
|
char_u *cmd = eap->arg;
|
||||||
int save_State;
|
linenr_T first_line = 0; // first changed line
|
||||||
linenr_T first_line = 0; /* first changed line */
|
linenr_T last_line= 0; // below last changed line AFTER the
|
||||||
linenr_T last_line= 0; /* below last changed line AFTER the
|
// change
|
||||||
* change */
|
|
||||||
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
|
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
|
||||||
linenr_T line2;
|
char_u *sub_firstline; // allocated copy of first sub line
|
||||||
long nmatch; /* number of lines in match */
|
bool endcolumn = false; // cursor in last column when done
|
||||||
char_u *sub_firstline; /* allocated copy of first sub line */
|
|
||||||
int endcolumn = FALSE; /* cursor in last column when done */
|
|
||||||
pos_T old_cursor = curwin->w_cursor;
|
pos_T old_cursor = curwin->w_cursor;
|
||||||
int start_nsubs;
|
int start_nsubs;
|
||||||
int save_ma = 0;
|
int save_ma = 0;
|
||||||
|
|
||||||
cmd = eap->arg;
|
|
||||||
if (!global_busy) {
|
if (!global_busy) {
|
||||||
sub_nsubs = 0;
|
sub_nsubs = 0;
|
||||||
sub_nlines = 0;
|
sub_nlines = 0;
|
||||||
@@ -3028,104 +3193,20 @@ void do_sub(exarg_T *eap)
|
|||||||
endcolumn = (curwin->w_curswant == MAXCOL);
|
endcolumn = (curwin->w_curswant == MAXCOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recognize ":%s/\n//" and turn it into a join command, which is much
|
if (sub_joining_lines(eap, pat, sub, cmd)) {
|
||||||
// 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);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
cmd = sub_parse_flags(cmd, &subflags, &which_pat);
|
||||||
* 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;
|
bool save_do_all = subflags.do_all; // remember user specified 'g' flag
|
||||||
do_error = TRUE;
|
bool save_do_ask = subflags.do_ask; // remember user specified 'c' flag
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
save_do_all = do_all;
|
|
||||||
save_do_ask = do_ask;
|
|
||||||
|
|
||||||
// check for a trailing count
|
// check for a trailing count
|
||||||
cmd = skipwhite(cmd);
|
cmd = skipwhite(cmd);
|
||||||
if (ascii_isdigit(*cmd)) {
|
if (ascii_isdigit(*cmd)) {
|
||||||
i = getdigits_long(&cmd);
|
i = getdigits_long(&cmd);
|
||||||
if (i <= 0 && !eap->skip && do_error) {
|
if (i <= 0 && !eap->skip && subflags.do_error) {
|
||||||
EMSG(_(e_zerocount));
|
EMSG(_(e_zerocount));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3150,24 +3231,25 @@ void do_sub(exarg_T *eap)
|
|||||||
if (eap->skip) /* not executing commands, only parsing */
|
if (eap->skip) /* not executing commands, only parsing */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!do_count && !MODIFIABLE(curbuf)) {
|
if (!subflags.do_count && !MODIFIABLE(curbuf)) {
|
||||||
/* Substitution is not allowed in non-'modifiable' buffer */
|
// Substitution is not allowed in non-'modifiable' buffer
|
||||||
EMSG(_(e_modifiable));
|
EMSG(_(e_modifiable));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS,
|
if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) {
|
||||||
®match) == FAIL) {
|
if (subflags.do_error) {
|
||||||
if (do_error)
|
|
||||||
EMSG(_(e_invcmd));
|
EMSG(_(e_invcmd));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
|
// the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase'
|
||||||
if (do_ic == 'i')
|
if (subflags.do_ic == kSubIgnoreCase) {
|
||||||
regmatch.rmm_ic = TRUE;
|
regmatch.rmm_ic = true;
|
||||||
else if (do_ic == 'I')
|
} else if (subflags.do_ic == kSubMatchCase) {
|
||||||
regmatch.rmm_ic = FALSE;
|
regmatch.rmm_ic = false;
|
||||||
|
}
|
||||||
|
|
||||||
sub_firstline = NULL;
|
sub_firstline = NULL;
|
||||||
|
|
||||||
@@ -3179,29 +3261,25 @@ void do_sub(exarg_T *eap)
|
|||||||
if (!(sub[0] == '\\' && sub[1] == '='))
|
if (!(sub[0] == '\\' && sub[1] == '='))
|
||||||
sub = regtilde(sub, p_magic);
|
sub = regtilde(sub, p_magic);
|
||||||
|
|
||||||
/*
|
// Check for a match on each line.
|
||||||
* Check for a match on each line.
|
linenr_T line2 = eap->line2;
|
||||||
*/
|
for (linenr_T lnum = eap->line1;
|
||||||
line2 = eap->line2;
|
lnum <= line2 && !(got_quit || aborting());
|
||||||
for (lnum = eap->line1; lnum <= line2 && !(got_quit
|
lnum++) {
|
||||||
|| aborting()
|
long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum,
|
||||||
); ++lnum) {
|
|
||||||
nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum,
|
|
||||||
(colnr_T)0, NULL);
|
(colnr_T)0, NULL);
|
||||||
if (nmatch) {
|
if (nmatch) {
|
||||||
colnr_T copycol;
|
colnr_T copycol;
|
||||||
colnr_T matchcol;
|
colnr_T matchcol;
|
||||||
colnr_T prev_matchcol = MAXCOL;
|
colnr_T prev_matchcol = MAXCOL;
|
||||||
char_u *new_end, *new_start = NULL;
|
char_u *new_end, *new_start = NULL;
|
||||||
unsigned new_start_len = 0;
|
|
||||||
char_u *p1;
|
char_u *p1;
|
||||||
int did_sub = FALSE;
|
int did_sub = FALSE;
|
||||||
int lastone;
|
int lastone;
|
||||||
int len, copy_len, needed_len;
|
long nmatch_tl = 0; // nr of lines matched below lnum
|
||||||
long nmatch_tl = 0; /* nr of lines matched below lnum */
|
int do_again; // do it again after joining lines
|
||||||
int do_again; /* do it again after joining lines */
|
int skip_match = false;
|
||||||
int skip_match = FALSE;
|
linenr_T sub_firstlnum; // nr of first sub line
|
||||||
linenr_T sub_firstlnum; /* nr of first sub line */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The new text is build up step by step, to avoid too much
|
* The new text is build up step by step, to avoid too much
|
||||||
@@ -3241,8 +3319,7 @@ void do_sub(exarg_T *eap)
|
|||||||
* accordingly.
|
* accordingly.
|
||||||
*
|
*
|
||||||
* The new text is built up in new_start[]. It has some extra
|
* 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
|
* room to avoid using xmalloc()/free() too often.
|
||||||
* the length of the allocated memory at new_start.
|
|
||||||
*
|
*
|
||||||
* Make a copy of the old line, so it won't be taken away when
|
* 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_"
|
* 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.
|
* Loop until nothing more to replace in this line.
|
||||||
* 1. Handle match with empty string.
|
* 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.
|
* 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
|
* 5. break if there isn't another match in this line
|
||||||
*/
|
*/
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
@@ -3314,15 +3391,13 @@ void do_sub(exarg_T *eap)
|
|||||||
matchcol = regmatch.endpos[0].col;
|
matchcol = regmatch.endpos[0].col;
|
||||||
prev_matchcol = matchcol;
|
prev_matchcol = matchcol;
|
||||||
|
|
||||||
/*
|
// 2. If subflags.do_count is set only increase the counter.
|
||||||
* 2. If do_count is set only increase the counter.
|
// If do_ask is set, ask for confirmation.
|
||||||
* If do_ask is set, ask for confirmation.
|
if (subflags.do_count) {
|
||||||
*/
|
// For a multi-line match, put matchcol at the NUL at
|
||||||
if (do_count) {
|
// the end of the line and set nmatch to one, so that
|
||||||
/* For a multi-line match, put matchcol at the NUL at
|
// we continue looking for a match on the next line.
|
||||||
* the end of the line and set nmatch to one, so that
|
// Avoids that ":s/\nB\@=//gc" get stuck.
|
||||||
* we continue looking for a match on the next line.
|
|
||||||
* Avoids that ":s/\nB\@=//gc" get stuck. */
|
|
||||||
if (nmatch > 1) {
|
if (nmatch > 1) {
|
||||||
matchcol = (colnr_T)STRLEN(sub_firstline);
|
matchcol = (colnr_T)STRLEN(sub_firstline);
|
||||||
nmatch = 1;
|
nmatch = 1;
|
||||||
@@ -3336,12 +3411,12 @@ void do_sub(exarg_T *eap)
|
|||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_ask) {
|
if (subflags.do_ask) {
|
||||||
int typed = 0;
|
int typed = 0;
|
||||||
|
|
||||||
/* change State to CONFIRM, so that the mouse works
|
/* change State to CONFIRM, so that the mouse works
|
||||||
* properly */
|
* properly */
|
||||||
save_State = State;
|
int save_State = State;
|
||||||
State = CONFIRM;
|
State = CONFIRM;
|
||||||
setmouse(); /* disable mouse in xterm */
|
setmouse(); /* disable mouse in xterm */
|
||||||
curwin->w_cursor.col = regmatch.startpos[0].col;
|
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.
|
* Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
|
||||||
*/
|
*/
|
||||||
while (do_ask) {
|
while (subflags.do_ask) {
|
||||||
if (exmode_active) {
|
if (exmode_active) {
|
||||||
char_u *resp;
|
char_u *resp;
|
||||||
colnr_T sc, ec;
|
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);
|
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
|
||||||
curwin->w_cursor.col = regmatch.endpos[0].col - 1;
|
curwin->w_cursor.col = regmatch.endpos[0].col - 1;
|
||||||
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
|
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;
|
int numw = number_width(curwin) + 1;
|
||||||
sc += numw;
|
sc += numw;
|
||||||
ec += numw;
|
ec += numw;
|
||||||
@@ -3388,7 +3463,7 @@ void do_sub(exarg_T *eap)
|
|||||||
curwin->w_p_fen = FALSE;
|
curwin->w_p_fen = FALSE;
|
||||||
/* Invert the matched string.
|
/* Invert the matched string.
|
||||||
* Remove the inversion afterwards. */
|
* Remove the inversion afterwards. */
|
||||||
temp = RedrawingDisabled;
|
int temp = RedrawingDisabled;
|
||||||
RedrawingDisabled = 0;
|
RedrawingDisabled = 0;
|
||||||
|
|
||||||
if (new_start != NULL) {
|
if (new_start != NULL) {
|
||||||
@@ -3468,13 +3543,13 @@ void do_sub(exarg_T *eap)
|
|||||||
if (typed == 'y')
|
if (typed == 'y')
|
||||||
break;
|
break;
|
||||||
if (typed == 'l') {
|
if (typed == 'l') {
|
||||||
/* last: replace and then stop */
|
// last: replace and then stop
|
||||||
do_all = FALSE;
|
subflags.do_all = false;
|
||||||
line2 = lnum;
|
line2 = lnum;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (typed == 'a') {
|
if (typed == 'a') {
|
||||||
do_ask = FALSE;
|
subflags.do_ask = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (typed == Ctrl_E)
|
if (typed == Ctrl_E)
|
||||||
@@ -3510,19 +3585,26 @@ void do_sub(exarg_T *eap)
|
|||||||
/*
|
/*
|
||||||
* 3. substitute the string.
|
* 3. substitute the string.
|
||||||
*/
|
*/
|
||||||
if (do_count) {
|
if (subflags.do_count) {
|
||||||
/* prevent accidentally changing the buffer by a function */
|
// prevent accidentally changing the buffer by a function
|
||||||
save_ma = curbuf->b_p_ma;
|
save_ma = curbuf->b_p_ma;
|
||||||
curbuf->b_p_ma = FALSE;
|
curbuf->b_p_ma = false;
|
||||||
sandbox++;
|
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,
|
sublen = vim_regsub_multi(®match,
|
||||||
sub_firstlnum - regmatch.startpos[0].lnum,
|
sub_firstlnum - regmatch.startpos[0].lnum,
|
||||||
sub, sub_firstline, FALSE, p_magic, TRUE);
|
sub, sub_firstline, false, p_magic, true);
|
||||||
if (do_count) {
|
// Don't keep flags set by a recursive call
|
||||||
|
subflags = subflags_save;
|
||||||
|
if (subflags.do_count) {
|
||||||
curbuf->b_p_ma = save_ma;
|
curbuf->b_p_ma = save_ma;
|
||||||
|
if (sandbox > 0) {
|
||||||
sandbox--;
|
sandbox--;
|
||||||
|
}
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3545,33 +3627,10 @@ void do_sub(exarg_T *eap)
|
|||||||
p1 = ml_get(sub_firstlnum + nmatch - 1);
|
p1 = ml_get(sub_firstlnum + nmatch - 1);
|
||||||
nmatch_tl += nmatch - 1;
|
nmatch_tl += nmatch - 1;
|
||||||
}
|
}
|
||||||
copy_len = regmatch.startpos[0].col - copycol;
|
size_t copy_len = regmatch.startpos[0].col - copycol;
|
||||||
needed_len = copy_len + ((unsigned)STRLEN(p1)
|
new_end = sub_grow_buf(&new_start,
|
||||||
- regmatch.endpos[0].col) + sublen + 1;
|
copy_len + (STRLEN(p1) - regmatch.endpos[0].col)
|
||||||
if (new_start == NULL) {
|
+ sublen + 1);
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy the text up to the part that matched
|
* copy the text up to the part that matched
|
||||||
@@ -3595,11 +3654,12 @@ void do_sub(exarg_T *eap)
|
|||||||
sub_firstlnum += nmatch - 1;
|
sub_firstlnum += nmatch - 1;
|
||||||
xfree(sub_firstline);
|
xfree(sub_firstline);
|
||||||
sub_firstline = vim_strsave(ml_get(sub_firstlnum));
|
sub_firstline = vim_strsave(ml_get(sub_firstlnum));
|
||||||
/* When going beyond the last line, stop substituting. */
|
// When going beyond the last line, stop substituting.
|
||||||
if (sub_firstlnum <= line2)
|
if (sub_firstlnum <= line2) {
|
||||||
do_again = TRUE;
|
do_again = true;
|
||||||
else
|
} else {
|
||||||
do_all = FALSE;
|
subflags.do_all = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remember next character to be copied. */
|
/* Remember next character to be copied. */
|
||||||
@@ -3630,11 +3690,12 @@ void do_sub(exarg_T *eap)
|
|||||||
ml_append(lnum - 1, new_start,
|
ml_append(lnum - 1, new_start,
|
||||||
(colnr_T)(p1 - new_start + 1), FALSE);
|
(colnr_T)(p1 - new_start + 1), FALSE);
|
||||||
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
|
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
|
||||||
if (do_ask)
|
if (subflags.do_ask) {
|
||||||
appended_lines(lnum - 1, 1L);
|
appended_lines(lnum - 1, 1L);
|
||||||
else {
|
} else {
|
||||||
if (first_line == 0)
|
if (first_line == 0) {
|
||||||
first_line = lnum;
|
first_line = lnum;
|
||||||
|
}
|
||||||
last_line = lnum + 1;
|
last_line = lnum + 1;
|
||||||
}
|
}
|
||||||
/* All line numbers increase. */
|
/* All line numbers increase. */
|
||||||
@@ -3651,12 +3712,10 @@ void do_sub(exarg_T *eap)
|
|||||||
p1 += (*mb_ptr2len)(p1) - 1;
|
p1 += (*mb_ptr2len)(p1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// 4. If subflags.do_all is set, find next match.
|
||||||
* 4. If do_all is set, find next match.
|
// Prevent endless loop with patterns that match empty
|
||||||
* Prevent endless loop with patterns that match empty
|
// strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
|
||||||
* strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
|
// But ":s/\n/#/" is OK.
|
||||||
* But ":s/\n/#/" is OK.
|
|
||||||
*/
|
|
||||||
skip:
|
skip:
|
||||||
/* We already know that we did the last subst when we are at
|
/* We already know that we did the last subst when we are at
|
||||||
* the end of the line, except that a pattern like
|
* the end of the line, except that a pattern like
|
||||||
@@ -3667,7 +3726,7 @@ skip:
|
|||||||
|| got_int
|
|| got_int
|
||||||
|| got_quit
|
|| got_quit
|
||||||
|| lnum > line2
|
|| lnum > line2
|
||||||
|| !(do_all || do_again)
|
|| !(subflags.do_all || do_again)
|
||||||
|| (sub_firstline[matchcol] == NUL && nmatch <= 1
|
|| (sub_firstline[matchcol] == NUL && nmatch <= 1
|
||||||
&& !re_multiline(regmatch.regprog)));
|
&& !re_multiline(regmatch.regprog)));
|
||||||
nmatch = -1;
|
nmatch = -1;
|
||||||
@@ -3718,20 +3777,22 @@ skip:
|
|||||||
ml_delete(lnum, (int)FALSE);
|
ml_delete(lnum, (int)FALSE);
|
||||||
mark_adjust(lnum, lnum + nmatch_tl - 1,
|
mark_adjust(lnum, lnum + nmatch_tl - 1,
|
||||||
(long)MAXLNUM, -nmatch_tl);
|
(long)MAXLNUM, -nmatch_tl);
|
||||||
if (do_ask)
|
if (subflags.do_ask) {
|
||||||
deleted_lines(lnum, nmatch_tl);
|
deleted_lines(lnum, nmatch_tl);
|
||||||
--lnum;
|
}
|
||||||
line2 -= nmatch_tl; /* nr of lines decreases */
|
lnum--;
|
||||||
|
line2 -= nmatch_tl; // nr of lines decreases
|
||||||
nmatch_tl = 0;
|
nmatch_tl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When asking, undo is saved each time, must also set
|
/* When asking, undo is saved each time, must also set
|
||||||
* changed flag each time. */
|
* changed flag each time. */
|
||||||
if (do_ask)
|
if (subflags.do_ask) {
|
||||||
changed_bytes(lnum, 0);
|
changed_bytes(lnum, 0);
|
||||||
else {
|
} else {
|
||||||
if (first_line == 0)
|
if (first_line == 0) {
|
||||||
first_line = lnum;
|
first_line = lnum;
|
||||||
|
}
|
||||||
last_line = lnum + 1;
|
last_line = lnum + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3784,9 +3845,10 @@ skip:
|
|||||||
|
|
||||||
xfree(sub_firstline); /* may have to free allocated copy of the line */
|
xfree(sub_firstline); /* may have to free allocated copy of the line */
|
||||||
|
|
||||||
/* ":s/pat//n" doesn't move the cursor */
|
// ":s/pat//n" doesn't move the cursor
|
||||||
if (do_count)
|
if (subflags.do_count) {
|
||||||
curwin->w_cursor = old_cursor;
|
curwin->w_cursor = old_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
if (sub_nsubs > start_nsubs) {
|
if (sub_nsubs > start_nsubs) {
|
||||||
/* Set the '[ and '] marks. */
|
/* Set the '[ and '] marks. */
|
||||||
@@ -3795,28 +3857,37 @@ skip:
|
|||||||
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
|
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
|
||||||
|
|
||||||
if (!global_busy) {
|
if (!global_busy) {
|
||||||
if (!do_ask) { /* when interactive leave cursor on the match */
|
// when interactive leave cursor on the match
|
||||||
if (endcolumn)
|
if (!subflags.do_ask) {
|
||||||
|
if (endcolumn) {
|
||||||
coladvance((colnr_T)MAXCOL);
|
coladvance((colnr_T)MAXCOL);
|
||||||
else
|
} else {
|
||||||
beginline(BL_WHITE | BL_FIX);
|
beginline(BL_WHITE | BL_FIX);
|
||||||
}
|
}
|
||||||
if (!do_sub_msg(do_count) && do_ask)
|
}
|
||||||
|
if (!do_sub_msg(subflags.do_count) && subflags.do_ask) {
|
||||||
MSG("");
|
MSG("");
|
||||||
} else
|
}
|
||||||
global_need_beginline = TRUE;
|
} else {
|
||||||
if (do_print)
|
global_need_beginline = true;
|
||||||
print_line(curwin->w_cursor.lnum, do_number, do_list);
|
}
|
||||||
|
if (subflags.do_print) {
|
||||||
|
print_line(curwin->w_cursor.lnum, subflags.do_number, subflags.do_list);
|
||||||
|
}
|
||||||
} else if (!global_busy) {
|
} else if (!global_busy) {
|
||||||
if (got_int) /* interrupted */
|
if (got_int) {
|
||||||
|
// interrupted
|
||||||
EMSG(_(e_interr));
|
EMSG(_(e_interr));
|
||||||
else if (got_match) /* did find something but nothing substituted */
|
} else if (got_match) {
|
||||||
|
// did find something but nothing substituted
|
||||||
MSG("");
|
MSG("");
|
||||||
else if (do_error) /* nothing found */
|
} else if (subflags.do_error) {
|
||||||
|
// nothing found
|
||||||
EMSG2(_(e_patnotf2), get_search_pat());
|
EMSG2(_(e_patnotf2), get_search_pat());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (do_ask && hasAnyFolding(curwin)) {
|
if (subflags.do_ask && hasAnyFolding(curwin)) {
|
||||||
// Cursor position may require updating
|
// Cursor position may require updating
|
||||||
changed_window_setting();
|
changed_window_setting();
|
||||||
}
|
}
|
||||||
@@ -3824,9 +3895,9 @@ skip:
|
|||||||
vim_regfree(regmatch.regprog);
|
vim_regfree(regmatch.regprog);
|
||||||
|
|
||||||
// Restore the flag values, they can be used for ":&&".
|
// Restore the flag values, they can be used for ":&&".
|
||||||
do_all = save_do_all;
|
subflags.do_all = save_do_all;
|
||||||
do_ask = save_do_ask;
|
subflags.do_ask = save_do_ask;
|
||||||
}
|
} // NOLINT(readability/fn_size)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Give message for number of substitutions.
|
* Give message for number of substitutions.
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ source test_cursor_func.vim
|
|||||||
source test_cmdline.vim
|
source test_cmdline.vim
|
||||||
source test_menu.vim
|
source test_menu.vim
|
||||||
source test_popup.vim
|
source test_popup.vim
|
||||||
|
source test_regexp_utf8.vim
|
||||||
source test_unlet.vim
|
source test_unlet.vim
|
||||||
|
|||||||
41
src/nvim/testdir/test_regexp_utf8.vim
Normal file
41
src/nvim/testdir/test_regexp_utf8.vim
Normal file
@@ -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
|
||||||
@@ -75,6 +75,7 @@ static char *features[] = {
|
|||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static int included_patches[] = {
|
static int included_patches[] = {
|
||||||
|
2219,
|
||||||
// 2200,
|
// 2200,
|
||||||
// 2199,
|
// 2199,
|
||||||
// 2198,
|
// 2198,
|
||||||
@@ -577,7 +578,7 @@ static int included_patches[] = {
|
|||||||
1703,
|
1703,
|
||||||
// 1702,
|
// 1702,
|
||||||
// 1701,
|
// 1701,
|
||||||
// 1700,
|
1700,
|
||||||
// 1699,
|
// 1699,
|
||||||
// 1698 NA
|
// 1698 NA
|
||||||
// 1697,
|
// 1697,
|
||||||
|
|||||||
Reference in New Issue
Block a user