aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ex_cmds_defs.h4
-rw-r--r--src/nvim/ex_docmd.c712
-rw-r--r--src/nvim/ex_getln.c624
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/screen.c6
-rw-r--r--src/nvim/search.c14
-rw-r--r--src/nvim/testdir/test_search.vim382
7 files changed, 1186 insertions, 564 deletions
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 1f0560ae48..ff5088ea5e 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -169,6 +169,10 @@ struct exarg {
LineGetter getline; ///< Function used to get the next line
void *cookie; ///< argument for getline()
cstack_T *cstack; ///< condition stack for ":if" etc.
+ long verbose_save; ///< saved value of p_verbose
+ int save_msg_silent; ///< saved value of msg_silent
+ int did_esilent; ///< how many times emsg_silent was incremented
+ bool did_sandbox; ///< when true did sandbox++
};
#define FORCE_BIN 1 // ":edit ++bin file"
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 1160ab835b..dfebd13868 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -137,32 +137,6 @@ struct dbg_stuff {
except_T *current_exception;
};
-typedef struct {
- // parsed results
- exarg_T *eap;
- char_u *parsed_upto; // local we've parsed up to so far
- char_u *cmd; // start of command
- char_u *after_modifier;
-
- // errors
- char_u *errormsg;
-
- // globals that need to be updated
- cmdmod_T cmdmod;
- int sandbox;
- int msg_silent;
- int emsg_silent;
- bool ex_pressedreturn;
- long p_verbose;
-
- // other side-effects
- bool set_eventignore;
- long verbose_save;
- int save_msg_silent;
- int did_esilent;
- bool did_sandbox;
-} parse_state_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.c.generated.h"
#endif
@@ -1235,292 +1209,6 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite)
return (char_u *)p;
}
-static void parse_state_to_global(const parse_state_T *parse_state)
-{
- cmdmod = parse_state->cmdmod;
- sandbox = parse_state->sandbox;
- msg_silent = parse_state->msg_silent;
- emsg_silent = parse_state->emsg_silent;
- ex_pressedreturn = parse_state->ex_pressedreturn;
- p_verbose = parse_state->p_verbose;
-
- if (parse_state->set_eventignore) {
- set_string_option_direct(
- (char_u *)"ei", -1, (char_u *)"all", OPT_FREE, SID_NONE);
- }
-}
-
-static void parse_state_from_global(parse_state_T *parse_state)
-{
- memset(parse_state, 0, sizeof(*parse_state));
- parse_state->cmdmod = cmdmod;
- parse_state->sandbox = sandbox;
- parse_state->msg_silent = msg_silent;
- parse_state->emsg_silent = emsg_silent;
- parse_state->ex_pressedreturn = ex_pressedreturn;
- parse_state->p_verbose = p_verbose;
-}
-
-//
-// Parse one Ex command.
-//
-// This has no side-effects, except for modifying parameters
-// passed in by pointer.
-//
-// The `out` should be zeroed, and its `ea` member initialised,
-// before calling this function.
-//
-static bool parse_one_cmd(
- char_u **cmdlinep,
- parse_state_T *const out,
- LineGetter fgetline,
- void *fgetline_cookie)
-{
- exarg_T ea = {
- .line1 = 1,
- .line2 = 1,
- };
- *out->eap = ea;
-
- // "#!anything" is handled like a comment.
- if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') {
- return false;
- }
-
- /*
- * Repeat until no more command modifiers are found.
- */
- ea.cmd = *cmdlinep;
- for (;; ) {
- /*
- * 1. Skip comment lines and leading white space and colons.
- */
- while (*ea.cmd == ' '
- || *ea.cmd == '\t'
- || *ea.cmd == ':') {
- ea.cmd++;
- }
-
- // in ex mode, an empty line works like :+
- if (*ea.cmd == NUL && exmode_active
- && (getline_equal(fgetline, fgetline_cookie, getexmodeline)
- || getline_equal(fgetline, fgetline_cookie, getexline))
- && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ea.cmd = (char_u *)"+";
- out->ex_pressedreturn = true;
- }
-
- // ignore comment and empty lines
- if (*ea.cmd == '"') {
- return false;
- }
- if (*ea.cmd == NUL) {
- out->ex_pressedreturn = true;
- return false;
- }
-
- /*
- * 2. Handle command modifiers.
- */
- char_u *p = skip_range(ea.cmd, NULL);
- switch (*p) {
- // When adding an entry, also modify cmd_exists().
- case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3))
- break;
- out->cmdmod.split |= WSP_ABOVE;
- continue;
-
- case 'b': if (checkforcmd(&ea.cmd, "belowright", 3)) {
- out->cmdmod.split |= WSP_BELOW;
- continue;
- }
- if (checkforcmd(&ea.cmd, "browse", 3)) {
- out->cmdmod.browse = true;
- continue;
- }
- if (!checkforcmd(&ea.cmd, "botright", 2)) {
- break;
- }
- out->cmdmod.split |= WSP_BOT;
- continue;
-
- case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4))
- break;
- out->cmdmod.confirm = true;
- continue;
-
- case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) {
- out->cmdmod.keepmarks = true;
- continue;
- }
- if (checkforcmd(&ea.cmd, "keepalt", 5)) {
- out->cmdmod.keepalt = true;
- continue;
- }
- if (checkforcmd(&ea.cmd, "keeppatterns", 5)) {
- out->cmdmod.keeppatterns = true;
- continue;
- }
- if (!checkforcmd(&ea.cmd, "keepjumps", 5)) {
- break;
- }
- out->cmdmod.keepjumps = true;
- continue;
-
- case 'f': { // only accept ":filter {pat} cmd"
- char_u *reg_pat;
-
- if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
- break;
- }
- if (*p == '!') {
- out->cmdmod.filter_force = true;
- p = skipwhite(p + 1);
- if (*p == NUL || ends_excmd(*p)) {
- break;
- }
- }
- p = skip_vimgrep_pat(p, &reg_pat, NULL);
- if (p == NULL || *p == NUL) {
- break;
- }
- out->cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
- if (out->cmdmod.filter_regmatch.regprog == NULL) {
- break;
- }
- ea.cmd = p;
- continue;
- }
-
- // ":hide" and ":hide | cmd" are not modifiers
- case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3)
- || *p == NUL || ends_excmd(*p))
- break;
- ea.cmd = p;
- out->cmdmod.hide = true;
- continue;
-
- case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) {
- out->cmdmod.lockmarks = true;
- continue;
- }
-
- if (!checkforcmd(&ea.cmd, "leftabove", 5)) {
- break;
- }
- out->cmdmod.split |= WSP_ABOVE;
- continue;
-
- case 'n':
- if (checkforcmd(&ea.cmd, "noautocmd", 3)) {
- if (out->cmdmod.save_ei == NULL) {
- // Set 'eventignore' to "all". Restore the
- // existing option value later.
- out->cmdmod.save_ei = vim_strsave(p_ei);
- out->set_eventignore = true;
- }
- continue;
- }
- if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
- break;
- }
- out->cmdmod.noswapfile = true;
- continue;
-
- case 'r': if (!checkforcmd(&ea.cmd, "rightbelow", 6))
- break;
- out->cmdmod.split |= WSP_BELOW;
- continue;
-
- case 's': if (checkforcmd(&ea.cmd, "sandbox", 3)) {
- if (!out->did_sandbox) {
- out->sandbox++;
- }
- out->did_sandbox = true;
- continue;
- }
- if (!checkforcmd(&ea.cmd, "silent", 3)) {
- break;
- }
- if (out->save_msg_silent == -1) {
- out->save_msg_silent = out->msg_silent;
- }
- out->msg_silent++;
- if (*ea.cmd == '!' && !ascii_iswhite(ea.cmd[-1])) {
- // ":silent!", but not "silent !cmd"
- ea.cmd = skipwhite(ea.cmd + 1);
- out->emsg_silent++;
- out->did_esilent++;
- }
- continue;
-
- case 't': if (checkforcmd(&p, "tab", 3)) {
- long tabnr = get_address(
- &ea, &ea.cmd, ADDR_TABS, ea.skip, false, 1);
-
- if (tabnr == MAXLNUM) {
- out->cmdmod.tab = tabpage_index(curtab) + 1;
- } else {
- if (tabnr < 0 || tabnr > LAST_TAB_NR) {
- out->errormsg = (char_u *)_(e_invrange);
- return false;
- }
- out->cmdmod.tab = tabnr + 1;
- }
- ea.cmd = p;
- continue;
- }
- if (!checkforcmd(&ea.cmd, "topleft", 2)) {
- break;
- }
- out->cmdmod.split |= WSP_TOP;
- continue;
-
- case 'u': if (!checkforcmd(&ea.cmd, "unsilent", 3))
- break;
- if (out->save_msg_silent == -1) {
- out->save_msg_silent = out->msg_silent;
- }
- out->msg_silent = 0;
- continue;
-
- case 'v': if (checkforcmd(&ea.cmd, "vertical", 4)) {
- out->cmdmod.split |= WSP_VERT;
- continue;
- }
- if (!checkforcmd(&p, "verbose", 4))
- break;
- if (out->verbose_save < 0) {
- out->verbose_save = out->p_verbose;
- }
- if (ascii_isdigit(*ea.cmd)) {
- out->p_verbose = atoi((char *)ea.cmd);
- } else {
- out->p_verbose = 1;
- }
- ea.cmd = p;
- continue;
- }
- break;
- }
- out->after_modifier = ea.cmd;
-
- // 3. Skip over the range to find the command. Let "p" point to after it.
- //
- // We need the command to know what kind of range it uses.
-
- out->cmd = ea.cmd;
- ea.cmd = skip_range(ea.cmd, NULL);
- if (*ea.cmd == '*') {
- ea.cmd = skipwhite(ea.cmd + 1);
- }
- out->parsed_upto = find_command(&ea, NULL);
-
- *out->eap = ea;
-
- return true;
-}
-
/*
* Execute one Ex command.
*
@@ -1549,12 +1237,16 @@ static char_u * do_one_cmd(char_u **cmdlinep,
linenr_T lnum;
long n;
char_u *errormsg = NULL; // error message
+ char_u *after_modifier = NULL;
exarg_T ea;
int save_msg_scroll = msg_scroll;
- parse_state_T parsed;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
+ char_u *cmd;
+ memset(&ea, 0, sizeof(ea));
+ ea.line1 = 1;
+ ea.line2 = 1;
ex_nesting_level++;
/* When the last file has not been edited :q has to be typed twice. */
@@ -1571,31 +1263,44 @@ static char_u * do_one_cmd(char_u **cmdlinep,
* recursive calls.
*/
save_cmdmod = cmdmod;
- memset(&cmdmod, 0, sizeof(cmdmod));
- parse_state_from_global(&parsed);
- parsed.eap = &ea;
- parsed.verbose_save = -1;
- parsed.save_msg_silent = -1;
- parsed.did_esilent = 0;
- parsed.did_sandbox = false;
- bool parse_success = parse_one_cmd(cmdlinep, &parsed, fgetline, cookie);
- parse_state_to_global(&parsed);
+ // "#!anything" is handled like a comment.
+ if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') {
+ goto doend;
+ }
- // Update locals from parse_one_cmd()
- errormsg = parsed.errormsg;
- p = parsed.parsed_upto;
+ // 1. Skip comment lines and leading white space and colons.
+ // 2. Handle command modifiers.
- if (!parse_success) {
+ // The "ea" structure holds the arguments that can be used.
+ ea.cmd = *cmdlinep;
+ ea.cmdlinep = cmdlinep;
+ ea.getline = fgetline;
+ ea.cookie = cookie;
+ ea.cstack = cstack;
+
+ if (parse_command_modifiers(&ea, &errormsg, false) == FAIL) {
goto doend;
}
+ after_modifier = ea.cmd;
+
ea.skip = (did_emsg
|| got_int
|| current_exception
|| (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
+ // 3. Skip over the range to find the command. Let "p" point to after it.
+ //
+ // We need the command to know what kind of range it uses.
+ cmd = ea.cmd;
+ ea.cmd = skip_range(ea.cmd, NULL);
+ if (*ea.cmd == '*') {
+ ea.cmd = skipwhite(ea.cmd + 1);
+ }
+ p = find_command(&ea, NULL);
+
// Count this line for profiling if skip is TRUE.
if (do_profiling == PROF_YES
&& (!ea.skip || cstack->cs_idx == 0
@@ -1665,8 +1370,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
}
- ea.cmd = parsed.cmd;
- if (parse_cmd_address(&ea, &errormsg) == FAIL) {
+ ea.cmd = cmd;
+ if (parse_cmd_address(&ea, &errormsg, false) == FAIL) {
goto doend;
}
@@ -1746,8 +1451,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (!(flags & DOCMD_VERBOSE)) {
// If the modifier was parsed OK the error must be in the following
// command
- if (parsed.after_modifier != NULL) {
- append_command(parsed.after_modifier);
+ if (after_modifier != NULL) {
+ append_command(after_modifier);
} else {
append_command(*cmdlinep);
}
@@ -2222,22 +1927,15 @@ static char_u * do_one_cmd(char_u **cmdlinep,
// The :try command saves the emsg_silent flag, reset it here when
// ":silent! try" was used, it should only apply to :try itself.
- if (ea.cmdidx == CMD_try && parsed.did_esilent > 0) {
- emsg_silent -= parsed.did_esilent;
+ if (ea.cmdidx == CMD_try && ea.did_esilent > 0) {
+ emsg_silent -= ea.did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
}
- parsed.did_esilent = 0;
+ ea.did_esilent = 0;
}
// 7. Execute the command.
- //
- // The "ea" structure holds the arguments that can be used.
- ea.cmdlinep = cmdlinep;
- ea.getline = fgetline;
- ea.cookie = cookie;
- ea.cstack = cstack;
-
if (IS_USER_CMDIDX(ea.cmdidx)) {
/*
* Execute a user-defined command.
@@ -2293,30 +1991,21 @@ doend:
? cmdnames[(int)ea.cmdidx].cmd_name
: (char_u *)NULL);
- if (parsed.verbose_save >= 0) {
- p_verbose = parsed.verbose_save;
- }
- if (cmdmod.save_ei != NULL) {
- /* Restore 'eventignore' to the value before ":noautocmd". */
- set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
- OPT_FREE, SID_NONE);
- free_string_option(cmdmod.save_ei);
- }
-
- if (cmdmod.filter_regmatch.regprog != NULL) {
- vim_regfree(cmdmod.filter_regmatch.regprog);
+ if (ea.verbose_save >= 0) {
+ p_verbose = ea.verbose_save;
}
+ free_cmdmod();
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
- if (parsed.save_msg_silent != -1) {
+ if (ea.save_msg_silent != -1) {
// messages could be enabled for a serious error, need to check if the
// counters don't become negative
- if (!did_emsg || msg_silent > parsed.save_msg_silent) {
- msg_silent = parsed.save_msg_silent;
+ if (!did_emsg || msg_silent > ea.save_msg_silent) {
+ msg_silent = ea.save_msg_silent;
}
- emsg_silent -= parsed.did_esilent;
+ emsg_silent -= ea.did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
}
@@ -2330,7 +2019,7 @@ doend:
msg_col = 0;
}
- if (parsed.did_sandbox) {
+ if (ea.did_sandbox) {
sandbox--;
}
@@ -2342,9 +2031,279 @@ doend:
return ea.nextcmd;
}
+// Parse and skip over command modifiers:
+// - update eap->cmd
+// - store flags in "cmdmod".
+// - Set ex_pressedreturn for an empty command line.
+// - set msg_silent for ":silent"
+// - set 'eventignore' to "all" for ":noautocmd"
+// - set p_verbose for ":verbose"
+// - Increment "sandbox" for ":sandbox"
+// When "skip_only" is true the global variables are not changed, except for
+// "cmdmod".
+// Return FAIL when the command is not to be executed.
+// May set "errormsg" to an error message.
+int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only)
+{
+ char_u *p;
+
+ memset(&cmdmod, 0, sizeof(cmdmod));
+ eap->verbose_save = -1;
+ eap->save_msg_silent = -1;
+
+ // Repeat until no more command modifiers are found.
+ for (;; ) {
+ while (*eap->cmd == ' '
+ || *eap->cmd == '\t'
+ || *eap->cmd == ':') {
+ eap->cmd++;
+ }
+
+ // in ex mode, an empty line works like :+
+ if (*eap->cmd == NUL && exmode_active
+ && (getline_equal(eap->getline, eap->cookie, getexmodeline)
+ || getline_equal(eap->getline, eap->cookie, getexline))
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ eap->cmd = (char_u *)"+";
+ if (!skip_only) {
+ ex_pressedreturn = true;
+ }
+ }
+
+ // ignore comment and empty lines
+ if (*eap->cmd == '"') {
+ return FAIL;
+ }
+ if (*eap->cmd == NUL) {
+ if (!skip_only) {
+ ex_pressedreturn = true;
+ }
+ return FAIL;
+ }
+
+ p = skip_range(eap->cmd, NULL);
+ switch (*p) {
+ // When adding an entry, also modify cmd_exists().
+ case 'a': if (!checkforcmd(&eap->cmd, "aboveleft", 3))
+ break;
+ cmdmod.split |= WSP_ABOVE;
+ continue;
+
+ case 'b': if (checkforcmd(&eap->cmd, "belowright", 3)) {
+ cmdmod.split |= WSP_BELOW;
+ continue;
+ }
+ if (checkforcmd(&eap->cmd, "browse", 3)) {
+ cmdmod.browse = true;
+ continue;
+ }
+ if (!checkforcmd(&eap->cmd, "botright", 2)) {
+ break;
+ }
+ cmdmod.split |= WSP_BOT;
+ continue;
+
+ case 'c': if (!checkforcmd(&eap->cmd, "confirm", 4))
+ break;
+ cmdmod.confirm = true;
+ continue;
+
+ case 'k': if (checkforcmd(&eap->cmd, "keepmarks", 3)) {
+ cmdmod.keepmarks = true;
+ continue;
+ }
+ if (checkforcmd(&eap->cmd, "keepalt", 5)) {
+ cmdmod.keepalt = true;
+ continue;
+ }
+ if (checkforcmd(&eap->cmd, "keeppatterns", 5)) {
+ cmdmod.keeppatterns = true;
+ continue;
+ }
+ if (!checkforcmd(&eap->cmd, "keepjumps", 5)) {
+ break;
+ }
+ cmdmod.keepjumps = true;
+ continue;
+
+ case 'f': { // only accept ":filter {pat} cmd"
+ char_u *reg_pat;
+
+ if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
+ break;
+ }
+ if (*p == '!') {
+ cmdmod.filter_force = true;
+ p = skipwhite(p + 1);
+ if (*p == NUL || ends_excmd(*p)) {
+ break;
+ }
+ }
+ if (skip_only) {
+ p = skip_vimgrep_pat(p, NULL, NULL);
+ } else {
+ // NOTE: This puts a NUL after the pattern.
+ p = skip_vimgrep_pat(p, &reg_pat, NULL);
+ }
+ if (p == NULL || *p == NUL) {
+ break;
+ }
+ if (!skip_only) {
+ cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
+ if (cmdmod.filter_regmatch.regprog == NULL) {
+ break;
+ }
+ }
+ eap->cmd = p;
+ continue;
+ }
+
+ // ":hide" and ":hide | cmd" are not modifiers
+ case 'h': if (p != eap->cmd || !checkforcmd(&p, "hide", 3)
+ || *p == NUL || ends_excmd(*p))
+ break;
+ eap->cmd = p;
+ cmdmod.hide = true;
+ continue;
+
+ case 'l': if (checkforcmd(&eap->cmd, "lockmarks", 3)) {
+ cmdmod.lockmarks = true;
+ continue;
+ }
+
+ if (!checkforcmd(&eap->cmd, "leftabove", 5)) {
+ break;
+ }
+ cmdmod.split |= WSP_ABOVE;
+ continue;
+
+ case 'n':
+ if (checkforcmd(&eap->cmd, "noautocmd", 3)) {
+ if (cmdmod.save_ei == NULL && !skip_only) {
+ // Set 'eventignore' to "all". Restore the
+ // existing option value later.
+ cmdmod.save_ei = vim_strsave(p_ei);
+ set_string_option_direct((char_u *)"ei", -1,
+ (char_u *)"all", OPT_FREE, SID_NONE);
+ }
+ continue;
+ }
+ if (!checkforcmd(&eap->cmd, "noswapfile", 3)) {
+ break;
+ }
+ cmdmod.noswapfile = true;
+ continue;
+
+ case 'r': if (!checkforcmd(&eap->cmd, "rightbelow", 6))
+ break;
+ cmdmod.split |= WSP_BELOW;
+ continue;
+
+ case 's': if (checkforcmd(&eap->cmd, "sandbox", 3)) {
+ if (!skip_only) {
+ if (!eap->did_sandbox) {
+ sandbox++;
+ }
+ eap->did_sandbox = true;
+ }
+ continue;
+ }
+ if (!checkforcmd(&eap->cmd, "silent", 3)) {
+ break;
+ }
+ if (!skip_only) {
+ if (eap->save_msg_silent == -1) {
+ eap->save_msg_silent = msg_silent;
+ }
+ msg_silent++;
+ }
+ if (*eap->cmd == '!' && !ascii_iswhite(eap->cmd[-1])) {
+ // ":silent!", but not "silent !cmd"
+ eap->cmd = skipwhite(eap->cmd + 1);
+ if (!skip_only) {
+ emsg_silent++;
+ eap->did_esilent++;
+ }
+ }
+ continue;
+
+ case 't': if (checkforcmd(&p, "tab", 3)) {
+ long tabnr = get_address(
+ eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1);
+
+ if (tabnr == MAXLNUM) {
+ cmdmod.tab = tabpage_index(curtab) + 1;
+ } else {
+ if (tabnr < 0 || tabnr > LAST_TAB_NR) {
+ *errormsg = (char_u *)_(e_invrange);
+ return false;
+ }
+ cmdmod.tab = tabnr + 1;
+ }
+ eap->cmd = p;
+ continue;
+ }
+ if (!checkforcmd(&eap->cmd, "topleft", 2)) {
+ break;
+ }
+ cmdmod.split |= WSP_TOP;
+ continue;
+
+ case 'u': if (!checkforcmd(&eap->cmd, "unsilent", 3))
+ break;
+ if (!skip_only) {
+ if (eap->save_msg_silent == -1) {
+ eap->save_msg_silent = msg_silent;
+ }
+ msg_silent = 0;
+ }
+ continue;
+
+ case 'v': if (checkforcmd(&eap->cmd, "vertical", 4)) {
+ cmdmod.split |= WSP_VERT;
+ continue;
+ }
+ if (!checkforcmd(&p, "verbose", 4))
+ break;
+ if (!skip_only) {
+ if (eap->verbose_save < 0) {
+ eap->verbose_save = p_verbose;
+ }
+ if (ascii_isdigit(*eap->cmd)) {
+ p_verbose = atoi((char *)eap->cmd);
+ } else {
+ p_verbose = 1;
+ }
+ }
+ eap->cmd = p;
+ continue;
+ }
+ break;
+ }
+
+ return OK;
+}
+
+// Free contents of "cmdmod".
+static void free_cmdmod(void)
+{
+ if (cmdmod.save_ei != NULL) {
+ /* Restore 'eventignore' to the value before ":noautocmd". */
+ set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
+ OPT_FREE, SID_NONE);
+ free_string_option(cmdmod.save_ei);
+ }
+
+ if (cmdmod.filter_regmatch.regprog != NULL) {
+ vim_regfree(cmdmod.filter_regmatch.regprog);
+ }
+}
+
+
// Parse the address range, if any, in "eap".
+// May set the last search pattern, unless "silent" is true.
// Return FAIL and set "errormsg" or return OK.
-int parse_cmd_address(exarg_T *eap, char_u **errormsg)
+int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent)
FUNC_ATTR_NONNULL_ALL
{
int address_count = 1;
@@ -2382,7 +2341,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg)
break;
}
eap->cmd = skipwhite(eap->cmd);
- lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip,
+ lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
eap->addr_count == 0, address_count++);
if (eap->cmd == NULL) { // error detected
return FAIL;
@@ -3725,18 +3684,18 @@ char_u *skip_range(
return (char_u *)cmd;
}
-/*
- * get a single EX address
- *
- * Set ptr to the next character after the part that was interpreted.
- * Set ptr to NULL when an error is encountered.
- *
- * Return MAXLNUM when no Ex address was found.
- */
+// Get a single EX address
+//
+// Set ptr to the next character after the part that was interpreted.
+// Set ptr to NULL when an error is encountered.
+// This may set the last used search pattern.
+//
+// Return MAXLNUM when no Ex address was found.
static linenr_T get_address(exarg_T *eap,
char_u **ptr,
int addr_type, // flag: one of ADDR_LINES, ...
int skip, // only skip the address, don't use it
+ bool silent, // no errors or side effects
int to_other_file, // flag: may jump to other file
int address_count) // 1 for first, >1 after comma
{
@@ -3868,13 +3827,15 @@ static linenr_T get_address(exarg_T *eap,
if (*cmd == c)
++cmd;
} else {
- pos = curwin->w_cursor; /* save curwin->w_cursor */
- /*
- * When '/' or '?' follows another address, start
- * from there.
- */
- if (lnum != MAXLNUM)
+ int flags;
+
+ pos = curwin->w_cursor; // save curwin->w_cursor
+
+ // When '/' or '?' follows another address, start from
+ // there.
+ if (lnum != MAXLNUM) {
curwin->w_cursor.lnum = lnum;
+ }
// Start a forward search at the end of the line (unless
// before the first line).
@@ -3888,7 +3849,8 @@ static linenr_T get_address(exarg_T *eap,
curwin->w_cursor.col = 0;
}
searchcmdlen = 0;
- if (!do_search(NULL, c, cmd, 1L, SEARCH_HIS | SEARCH_MSG, NULL)) {
+ flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
+ if (!do_search(NULL, c, cmd, 1L, flags, NULL)) {
curwin->w_cursor = pos;
cmd = NULL;
goto error;
@@ -7793,7 +7755,7 @@ static void ex_put(exarg_T *eap)
*/
static void ex_copymove(exarg_T *eap)
{
- long n = get_address(eap, &eap->arg, eap->addr_type, false, false, 1);
+ long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1);
if (eap->arg == NULL) { // error detected
eap->nextcmd = NULL;
return;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 93684e8606..c966c780a0 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -137,6 +137,7 @@ struct cmdline_info {
/// Last value of prompt_id, incremented when doing new prompt
static unsigned last_prompt_id = 0;
+// Struct to store the viewstate during 'incsearch' highlighting.
typedef struct {
colnr_T vs_curswant;
colnr_T vs_leftcol;
@@ -146,6 +147,19 @@ typedef struct {
int vs_empty_rows;
} viewstate_T;
+// Struct to store the state of 'incsearch' highlighting.
+typedef struct {
+ pos_T search_start; // where 'incsearch' starts searching
+ pos_T save_cursor;
+ viewstate_T init_viewstate;
+ viewstate_T old_viewstate;
+ pos_T match_start;
+ pos_T match_end;
+ bool did_incsearch;
+ bool incsearch_postponed;
+ int magic_save;
+} incsearch_state_T;
+
typedef struct command_line_state {
VimState state;
int firstc;
@@ -159,14 +173,7 @@ typedef struct command_line_state {
int save_hiscnt; // history line before attempting
// to jump to next match
int histype; // history type to be used
- pos_T search_start; // where 'incsearch' starts searching
- pos_T save_cursor;
- viewstate_T init_viewstate;
- viewstate_T old_viewstate;
- pos_T match_start;
- pos_T match_end;
- int did_incsearch;
- int incsearch_postponed;
+ incsearch_state_T is_state;
int did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
int res;
@@ -252,6 +259,382 @@ static void restore_viewstate(viewstate_T *vs)
curwin->w_empty_rows = vs->vs_empty_rows;
}
+static void init_incsearch_state(incsearch_state_T *s)
+{
+ s->match_start = curwin->w_cursor;
+ s->did_incsearch = false;
+ s->incsearch_postponed = false;
+ s->magic_save = p_magic;
+ clearpos(&s->match_end);
+ s->save_cursor = curwin->w_cursor; // may be restored later
+ s->search_start = curwin->w_cursor;
+ save_viewstate(&s->init_viewstate);
+ save_viewstate(&s->old_viewstate);
+}
+
+// Return true when 'incsearch' highlighting is to be done.
+// Sets search_first_line and search_last_line to the address range.
+static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
+ int *skiplen, int *patlen)
+{
+ char_u *cmd;
+ cmdmod_T save_cmdmod = cmdmod;
+ char_u *p;
+ bool delim_optional = false;
+ int delim;
+ char_u *end;
+ char_u *dummy;
+ exarg_T ea;
+ pos_T save_cursor;
+ bool use_last_pat;
+
+ *skiplen = 0;
+ *patlen = ccline.cmdlen;
+
+ if (!p_is || cmd_silent) {
+ return false;
+ }
+
+ // by default search all lines
+ search_first_line = 0;
+ search_last_line = MAXLNUM;
+
+ if (firstc == '/' || firstc == '?') {
+ return true;
+ }
+ if (firstc != ':') {
+ return false;
+ }
+
+ memset(&ea, 0, sizeof(ea));
+ ea.line1 = 1;
+ ea.line2 = 1;
+ ea.cmd = ccline.cmdbuff;
+ ea.addr_type = ADDR_LINES;
+
+ parse_command_modifiers(&ea, &dummy, true);
+ cmdmod = save_cmdmod;
+
+ cmd = skip_range(ea.cmd, NULL);
+ if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) {
+ return false;
+ }
+
+ // Skip over "substitute" to find the pattern separator.
+ for (p = cmd; ASCII_ISALPHA(*p); p++) {}
+ if (*skipwhite(p) == NUL) {
+ return false;
+ }
+
+ if (STRNCMP(cmd, "substitute", p - cmd) == 0
+ || STRNCMP(cmd, "smagic", p - cmd) == 0
+ || STRNCMP(cmd, "snomagic", MAX(p - cmd, 3)) == 0
+ || STRNCMP(cmd, "vglobal", p - cmd) == 0) {
+ if (*cmd == 's' && cmd[1] == 'm') {
+ p_magic = true;
+ } else if (*cmd == 's' && cmd[1] == 'n') {
+ p_magic = false;
+ }
+ } else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) {
+ // skip over flags.
+ while (ASCII_ISALPHA(*(p = skipwhite(p)))) {
+ p++;
+ }
+ if (*p == NUL) {
+ return false;
+ }
+ } else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0
+ || STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0
+ || STRNCMP(cmd, "lvimgrep", MAX(p - cmd, 2)) == 0
+ || STRNCMP(cmd, "lvimgrepadd", MAX(p - cmd, 9)) == 0
+ || STRNCMP(cmd, "global", p - cmd) == 0) {
+ // skip over "!/".
+ if (*p == '!') {
+ p++;
+ if (*skipwhite(p) == NUL) {
+ return false;
+ }
+ }
+ if (*cmd != 'g') {
+ delim_optional = true;
+ }
+ } else {
+ return false;
+ }
+
+ p = skipwhite(p);
+ delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
+ end = skip_regexp(p, delim, p_magic, NULL);
+
+ use_last_pat = end == p && *end == delim;
+ if (end == p && !use_last_pat) {
+ return false;
+ }
+
+ // Don't do 'hlsearch' highlighting if the pattern matches everything.
+ if (!use_last_pat) {
+ char_u c = *end;
+ int empty;
+
+ *end = NUL;
+ empty = empty_pattern(p);
+ *end = c;
+ if (empty) {
+ return false;
+ }
+ }
+
+ // found a non-empty pattern or //
+ *skiplen = (int)(p - ccline.cmdbuff);
+ *patlen = (int)(end - p);
+
+ // parse the address range
+ save_cursor = curwin->w_cursor;
+ curwin->w_cursor = s->search_start;
+ parse_cmd_address(&ea, &dummy, true);
+ if (ea.addr_count > 0) {
+ // Allow for reverse match.
+ if (ea.line2 < ea.line1) {
+ search_first_line = ea.line2;
+ search_last_line = ea.line1;
+ } else {
+ search_first_line = ea.line1;
+ search_last_line = ea.line2;
+ }
+ } else if (cmd[0] == 's' && cmd[1] != 'o') {
+ // :s defaults to the current line
+ search_first_line = curwin->w_cursor.lnum;
+ search_last_line = curwin->w_cursor.lnum;
+ }
+
+ curwin->w_cursor = save_cursor;
+ return true;
+}
+
+// May do 'incsearch' highlighting if desired.
+static void may_do_incsearch_highlighting(int firstc, long count,
+ incsearch_state_T *s)
+{
+ pos_T end_pos;
+ proftime_T tm;
+ searchit_arg_T sia;
+ int skiplen, patlen;
+ char_u next_char;
+ char_u use_last_pat;
+
+ // Parsing range may already set the last search pattern.
+ // NOTE: must call restore_last_search_pattern() before returning!
+ save_last_search_pattern();
+
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
+ restore_last_search_pattern();
+ finish_incsearch_highlighting(false, s, true);
+ return;
+ }
+
+ // if there is a character waiting, search and redraw later
+ if (char_avail()) {
+ restore_last_search_pattern();
+ s->incsearch_postponed = true;
+ return;
+ }
+ s->incsearch_postponed = false;
+
+ if (search_first_line == 0) {
+ // start at the original cursor position
+ curwin->w_cursor = s->search_start;
+ } else {
+ // start at the first line in the range
+ curwin->w_cursor.lnum = search_first_line;
+ curwin->w_cursor.col = 0;
+ }
+ int found; // do_search() result
+
+ // Use the previous pattern for ":s//".
+ next_char = ccline.cmdbuff[skiplen + patlen];
+ use_last_pat = patlen == 0 && skiplen > 0
+ && ccline.cmdbuff[skiplen - 1] == next_char;
+
+ // If there is no pattern, don't do anything.
+ if (patlen == 0 && !use_last_pat) {
+ found = 0;
+ set_no_hlsearch(true); // turn off previous highlight
+ redraw_all_later(SOME_VALID);
+ } else {
+ int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
+ ui_busy_start();
+ ui_flush();
+ emsg_off++; // So it doesn't beep if bad expr
+ // Set the time limit to half a second.
+ tm = profile_setlimit(500L);
+ if (!p_hls) {
+ search_flags += SEARCH_KEEP;
+ }
+ if (search_first_line != 0) {
+ search_flags += SEARCH_START;
+ }
+ ccline.cmdbuff[skiplen + patlen] = NUL;
+ memset(&sia, 0, sizeof(sia));
+ sia.sa_tm = &tm;
+ found = do_search(NULL, firstc == ':' ? '/' : firstc,
+ ccline.cmdbuff + skiplen, count,
+ search_flags, &sia);
+ ccline.cmdbuff[skiplen + patlen] = next_char;
+ emsg_off--;
+ if (curwin->w_cursor.lnum < search_first_line
+ || curwin->w_cursor.lnum > search_last_line) {
+ // match outside of address range
+ found = 0;
+ curwin->w_cursor = s->search_start;
+ }
+
+ // if interrupted while searching, behave like it failed
+ if (got_int) {
+ (void)vpeekc(); // remove <C-C> from input stream
+ got_int = false; // don't abandon the command line
+ found = 0;
+ } else if (char_avail()) {
+ // cancelled searching because a char was typed
+ s->incsearch_postponed = true;
+ }
+ ui_busy_stop();
+ }
+
+ if (found != 0) {
+ highlight_match = true; // highlight position
+ } else {
+ highlight_match = false; // remove highlight
+ }
+
+ // first restore the old curwin values, so the screen is
+ // positioned in the same way as the actual search command
+ restore_viewstate(&s->old_viewstate);
+ changed_cline_bef_curs();
+ update_topline();
+
+ if (found != 0) {
+ pos_T save_pos = curwin->w_cursor;
+
+ s->match_start = curwin->w_cursor;
+ set_search_match(&curwin->w_cursor);
+ validate_cursor();
+ end_pos = curwin->w_cursor;
+ s->match_end = end_pos;
+ curwin->w_cursor = save_pos;
+ } else {
+ end_pos = curwin->w_cursor; // shutup gcc 4
+ }
+ //
+ // Disable 'hlsearch' highlighting if the pattern matches
+ // everything. Avoids a flash when typing "foo\|".
+ if (!use_last_pat) {
+ next_char = ccline.cmdbuff[skiplen + patlen];
+ ccline.cmdbuff[skiplen + patlen] = NUL;
+ if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) {
+ redraw_all_later(SOME_VALID);
+ set_no_hlsearch(true);
+ }
+ ccline.cmdbuff[skiplen + patlen] = next_char;
+ }
+
+ validate_cursor();
+ // May redraw the status line to show the cursor position.
+ if (p_ru && curwin->w_status_height > 0) {
+ curwin->w_redr_status = true;
+ }
+
+ update_screen(SOME_VALID);
+ restore_last_search_pattern();
+
+ // Leave it at the end to make CTRL-R CTRL-W work. But not when beyond the
+ // end of the pattern, e.g. for ":s/pat/".
+ if (ccline.cmdbuff[skiplen + patlen] != NUL) {
+ curwin->w_cursor = s->search_start;
+ } else if (found != 0) {
+ curwin->w_cursor = end_pos;
+ }
+
+ msg_starthere();
+ redrawcmdline();
+ s->did_incsearch = true;
+}
+
+// When CTRL-L typed: add character from the match to the pattern.
+// May set "*c" to the added character.
+// Return OK when calling command_line_not_changed.
+static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
+{
+ int skiplen, patlen;
+
+ // Parsing range may already set the last search pattern.
+ // NOTE: must call restore_last_search_pattern() before returning!
+ save_last_search_pattern();
+
+ // Add a character from under the cursor for 'incsearch'
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
+ restore_last_search_pattern();
+ return FAIL;
+ }
+ restore_last_search_pattern();
+
+ if (s->did_incsearch) {
+ curwin->w_cursor = s->match_end;
+ if (!equalpos(curwin->w_cursor, s->search_start)) {
+ *c = gchar_cursor();
+ // If 'ignorecase' and 'smartcase' are set and the
+ // command line has no uppercase characters, convert
+ // the character to lowercase
+ if (p_ic && p_scs
+ && !pat_has_uppercase(ccline.cmdbuff + skiplen)) {
+ *c = mb_tolower(*c);
+ }
+ if (*c != NUL) {
+ if (*c == firstc
+ || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c)
+ != NULL) {
+ // put a backslash before special characters
+ stuffcharReadbuff(*c);
+ *c = '\\';
+ }
+ return FAIL;
+ }
+ }
+ }
+ return OK;
+}
+
+static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s,
+ bool call_update_screen)
+{
+ if (s->did_incsearch) {
+ s->did_incsearch = false;
+ if (gotesc) {
+ curwin->w_cursor = s->save_cursor;
+ } else {
+ if (!equalpos(s->save_cursor, s->search_start)) {
+ // put the '" mark at the original position
+ curwin->w_cursor = s->save_cursor;
+ setpcmark();
+ }
+ curwin->w_cursor = s->search_start; // -V519
+ }
+ restore_viewstate(&s->old_viewstate);
+ highlight_match = false;
+
+ // by default search all lines
+ search_first_line = 0;
+ search_last_line = MAXLNUM;
+
+ p_magic = s->magic_save;
+
+ validate_cursor(); // needed for TAB
+ redraw_all_later(SOME_VALID);
+ if (call_update_screen) {
+ update_screen(SOME_VALID);
+ }
+ }
+}
+
/// Internal entry point for cmdline mode.
///
/// caller must use save_cmdline and restore_cmdline. Best is to use
@@ -269,11 +652,10 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
.save_msg_scroll = msg_scroll,
.save_State = State,
.ignore_drag_release = true,
- .match_start = curwin->w_cursor,
};
CommandLineState *s = &state;
s->save_p_icm = vim_strsave(p_icm);
- save_viewstate(&state.init_viewstate);
+ init_incsearch_state(&s->is_state);
if (s->firstc == -1) {
s->firstc = NUL;
@@ -288,10 +670,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.prompt_id = last_prompt_id++;
ccline.level = cmdline_level;
ccline.overstrike = false; // always start in insert mode
- clearpos(&s->match_end);
- s->save_cursor = curwin->w_cursor; // may be restored later
- s->search_start = curwin->w_cursor;
- save_viewstate(&state.old_viewstate);
assert(indent >= 0);
@@ -455,22 +833,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
close_preview_windows();
}
- if (s->did_incsearch) {
- if (s->gotesc) {
- curwin->w_cursor = s->save_cursor;
- } else {
- if (!equalpos(s->save_cursor, s->search_start)) {
- // put the '" mark at the original position
- curwin->w_cursor = s->save_cursor;
- setpcmark();
- }
- curwin->w_cursor = s->search_start; // -V519
- }
- restore_viewstate(&s->old_viewstate);
- highlight_match = false;
- validate_cursor(); // needed for TAB
- redraw_all_later(SOME_VALID);
- }
+ finish_incsearch_highlighting(s->gotesc, &s->is_state, false);
if (ccline.cmdbuff != NULL) {
// Put line in history buffer (":" and "=" only when it was typed).
@@ -1075,24 +1438,45 @@ static int command_line_execute(VimState *state, int key)
return command_line_handle_key(s);
}
-static void command_line_next_incsearch(CommandLineState *s, bool next_match)
+// May adjust 'incsearch' highlighting for typing CTRL-G and CTRL-T, go to next
+// or previous match.
+// Returns FAIL when calling command_line_not_changed.
+static int may_do_command_line_next_incsearch(int firstc, long count,
+ incsearch_state_T *s,
+ bool next_match)
{
+ int skiplen, patlen;
+
+ // Parsing range may already set the last search pattern.
+ // NOTE: must call restore_last_search_pattern() before returning!
+ save_last_search_pattern();
+
+ if (!do_incsearch_highlighting(firstc, s, &skiplen, &patlen)) {
+ restore_last_search_pattern();
+ return OK;
+ }
+ if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) {
+ restore_last_search_pattern();
+ return FAIL;
+ }
+
ui_busy_start();
ui_flush();
pos_T t;
char_u *pat;
int search_flags = SEARCH_NOOF;
+ char_u save;
- if (s->firstc == ccline.cmdbuff[0]) {
+ if (firstc == ccline.cmdbuff[skiplen]) {
pat = last_search_pattern();
+ skiplen = 0;
+ patlen = (int)STRLEN(pat);
} else {
- pat = ccline.cmdbuff;
+ pat = ccline.cmdbuff + skiplen;
}
- save_last_search_pattern();
-
if (next_match) {
t = s->match_end;
if (lt(s->match_start, s->match_end)) {
@@ -1108,23 +1492,26 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
search_flags += SEARCH_KEEP;
}
emsg_off++;
+ save = pat[patlen];
+ pat[patlen] = NUL;
int found = searchit(curwin, curbuf, &t, NULL,
next_match ? FORWARD : BACKWARD,
- pat, s->count, search_flags,
+ pat, count, search_flags,
RE_SEARCH, NULL);
emsg_off--;
+ pat[patlen] = save;
ui_busy_stop();
if (found) {
s->search_start = s->match_start;
s->match_end = t;
s->match_start = t;
- if (!next_match && s->firstc == '/') {
+ if (!next_match && firstc != '?') {
// move just before the current match, so that
// when nv_search finishes the cursor will be
// put back on the match
s->search_start = t;
(void)decl(&s->search_start);
- } else if (next_match && s->firstc == '?') {
+ } else if (next_match && firstc == '?') {
// move just after the current match, so that
// when nv_search finishes the cursor will be
// put back on the match
@@ -1134,7 +1521,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
if (lt(t, s->search_start) && next_match) {
// wrap around
s->search_start = t;
- if (s->firstc == '?') {
+ if (firstc == '?') {
(void)incl(&s->search_start);
} else {
(void)decl(&s->search_start);
@@ -1154,7 +1541,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
vim_beep(BO_ERROR);
}
restore_last_search_pattern();
- return;
+ return FAIL;
}
static void command_line_next_histidx(CommandLineState *s, bool next_match)
@@ -1265,10 +1652,10 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) {
- s->search_start = s->save_cursor;
+ s->is_state.search_start = s->is_state.save_cursor;
// save view settings, so that the screen won't be restored at the
// wrong position
- s->old_viewstate = s->init_viewstate;
+ s->is_state.old_viewstate = s->is_state.init_viewstate;
}
redrawcmd();
} else if (ccline.cmdlen == 0 && s->c != Ctrl_W
@@ -1287,7 +1674,7 @@ static int command_line_handle_key(CommandLineState *s)
}
msg_putchar(' '); // delete ':'
}
- s->search_start = s->save_cursor;
+ s->is_state.search_start = s->is_state.save_cursor;
redraw_cmdline = true;
return 0; // back to cmd mode
}
@@ -1337,7 +1724,7 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) {
- s->search_start = s->save_cursor;
+ s->is_state.search_start = s->is_state.save_cursor;
}
redrawcmd();
return command_line_changed(s);
@@ -1565,31 +1952,7 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_changed(s);
case Ctrl_L:
- if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
- // Add a character from under the cursor for 'incsearch'
- if (s->did_incsearch) {
- curwin->w_cursor = s->match_end;
- if (!equalpos(curwin->w_cursor, s->search_start)) {
- s->c = gchar_cursor();
- // If 'ignorecase' and 'smartcase' are set and the
- // command line has no uppercase characters, convert
- // the character to lowercase
- if (p_ic && p_scs
- && !pat_has_uppercase(ccline.cmdbuff)) {
- s->c = mb_tolower(s->c);
- }
- if (s->c != NUL) {
- if (s->c == s->firstc
- || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), s->c)
- != NULL) {
- // put a backslash before special characters
- stuffcharReadbuff(s->c);
- s->c = '\\';
- }
- break;
- }
- }
- }
+ if (may_add_char_to_search(s->firstc, &s->c, &s->is_state) == OK) {
return command_line_not_changed(s);
}
@@ -1703,10 +2066,8 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_G: // next match
case Ctrl_T: // previous match
- if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
- if (ccline.cmdlen != 0) {
- command_line_next_incsearch(s, s->c == Ctrl_G);
- }
+ if (may_do_command_line_next_incsearch(s->firstc, s->count, &s->is_state,
+ s->c == Ctrl_G) == FAIL) {
return command_line_not_changed(s);
}
break;
@@ -1791,7 +2152,7 @@ static int command_line_not_changed(CommandLineState *s)
// command line did not change. Then we only search and redraw if something
// changed in the past.
// Enter command_line_changed() when the command line did change.
- if (!s->incsearch_postponed) {
+ if (!s->is_state.incsearch_postponed) {
return 1;
}
return command_line_changed(s);
@@ -1843,108 +2204,13 @@ static int command_line_changed(CommandLineState *s)
}
// 'incsearch' highlighting.
- if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
- pos_T end_pos;
- proftime_T tm;
- searchit_arg_T sia;
-
- // if there is a character waiting, search and redraw later
- if (char_avail()) {
- s->incsearch_postponed = true;
- return 1;
- }
- s->incsearch_postponed = false;
- curwin->w_cursor = s->search_start; // start at old position
- save_last_search_pattern();
- int i;
-
- // If there is no command line, don't do anything
- if (ccline.cmdlen == 0) {
- i = 0;
- set_no_hlsearch(true); // turn off previous highlight
- redraw_all_later(SOME_VALID);
- } else {
- int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
- ui_busy_start();
- ui_flush();
- ++emsg_off; // So it doesn't beep if bad expr
- // Set the time limit to half a second.
- tm = profile_setlimit(500L);
- if (!p_hls) {
- search_flags += SEARCH_KEEP;
- }
- memset(&sia, 0, sizeof(sia));
- sia.sa_tm = &tm;
- i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
- search_flags, &sia);
- emsg_off--;
- // if interrupted while searching, behave like it failed
- if (got_int) {
- (void)vpeekc(); // remove <C-C> from input stream
- got_int = false; // don't abandon the command line
- i = 0;
- } else if (char_avail()) {
- // cancelled searching because a char was typed
- s->incsearch_postponed = true;
- }
- ui_busy_stop();
- }
-
- if (i != 0) {
- highlight_match = true; // highlight position
- } else {
- highlight_match = false; // remove highlight
- }
-
- // first restore the old curwin values, so the screen is
- // positioned in the same way as the actual search command
- restore_viewstate(&s->old_viewstate);
- changed_cline_bef_curs();
- update_topline();
-
- if (i != 0) {
- pos_T save_pos = curwin->w_cursor;
-
- s->match_start = curwin->w_cursor;
- set_search_match(&curwin->w_cursor);
- validate_cursor();
- end_pos = curwin->w_cursor;
- s->match_end = end_pos;
- curwin->w_cursor = save_pos;
- } else {
- end_pos = curwin->w_cursor; // shutup gcc 4
- }
-
- // Disable 'hlsearch' highlighting if the pattern matches
- // everything. Avoids a flash when typing "foo\|".
- if (empty_pattern(ccline.cmdbuff)) {
- set_no_hlsearch(true);
- }
-
- validate_cursor();
- // May redraw the status line to show the cursor position.
- if (p_ru && curwin->w_status_height > 0) {
- curwin->w_redr_status = true;
- }
-
- update_screen(SOME_VALID);
- restore_last_search_pattern();
-
- // Leave it at the end to make CTRL-R CTRL-W work.
- if (i != 0) {
- curwin->w_cursor = end_pos;
- }
-
- msg_starthere();
- redrawcmdline();
- s->did_incsearch = true;
- } else if (s->firstc == ':'
- && current_sctx.sc_sid == 0 // only if interactive
- && *p_icm != NUL // 'inccommand' is set
- && curbuf->b_p_ma // buffer is modifiable
- && cmdline_star == 0 // not typing a password
- && cmd_can_preview(ccline.cmdbuff)
- && !vpeekc_any()) {
+ if (s->firstc == ':'
+ && current_sctx.sc_sid == 0 // only if interactive
+ && *p_icm != NUL // 'inccommand' is set
+ && curbuf->b_p_ma // buffer is modifiable
+ && cmdline_star == 0 // not typing a password
+ && cmd_can_preview(ccline.cmdbuff)
+ && !vpeekc_any()) {
// Show 'inccommand' preview. It works like this:
// 1. Do the command.
// 2. Command implementation detects CMDPREVIEW state, then:
@@ -1958,8 +2224,8 @@ static int command_line_changed(CommandLineState *s)
emsg_silent--; // Unblock error reporting
// Restore the window "view".
- curwin->w_cursor = s->save_cursor;
- restore_viewstate(&s->old_viewstate);
+ curwin->w_cursor = s->is_state.save_cursor;
+ restore_viewstate(&s->is_state.old_viewstate);
update_topline();
redrawcmdline();
@@ -1968,6 +2234,8 @@ static int command_line_changed(CommandLineState *s)
State = (State & ~CMDPREVIEW);
close_preview_windows();
update_screen(SOME_VALID); // Clear 'inccommand' preview.
+ } else {
+ may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
}
if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 91a0eab947..205be4b811 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -353,9 +353,11 @@ EXTERN int t_colors INIT(= 256); // int value of T_CCO
// position. Search_match_lines is the number of lines after the match (0 for
// a match within one line), search_match_endcol the column number of the
// character just after the match in the last line.
-EXTERN int highlight_match INIT(= false); // show search match pos
-EXTERN linenr_T search_match_lines; // lines of of matched string
-EXTERN colnr_T search_match_endcol; // col nr of match end
+EXTERN bool highlight_match INIT(= false); // show search match pos
+EXTERN linenr_T search_match_lines; // lines of of matched string
+EXTERN colnr_T search_match_endcol; // col nr of match end
+EXTERN linenr_T search_first_line INIT(= 0); // for :{FIRST},{last}s/pat
+EXTERN linenr_T search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat
EXTERN int no_smartcase INIT(= false); // don't use 'smartcase' once
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5f3423989e..3c2e1ccaf5 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -5918,6 +5918,12 @@ next_search_hl (
long nmatched = 0;
int save_called_emsg = called_emsg;
+ // for :{range}s/pat only highlight inside the range
+ if (lnum < search_first_line || lnum > search_last_line) {
+ shl->lnum = 0;
+ return;
+ }
+
if (shl->lnum != 0) {
// Check for three situations:
// 1. If the "lnum" is below a previous match, start a new search.
diff --git a/src/nvim/search.c b/src/nvim/search.c
index fc82e81472..23d84038d6 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -99,6 +99,7 @@ static struct spat saved_spats[2];
// copy of spats[RE_SEARCH], for keeping the search patterns while incremental
// searching
static struct spat saved_last_search_spat;
+static int did_save_last_search_spat = 0;
static int saved_last_idx = 0;
static bool saved_no_hlsearch = false;
@@ -316,6 +317,12 @@ void free_search_patterns(void)
/// cancelling incremental searching even if it's called inside user functions.
void save_last_search_pattern(void)
{
+ if (did_save_last_search_spat != 0) {
+ IEMSG("did_save_last_search_spat is not zero");
+ } else {
+ did_save_last_search_spat++;
+ }
+
saved_last_search_spat = spats[RE_SEARCH];
if (spats[RE_SEARCH].pat != NULL) {
saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
@@ -326,8 +333,15 @@ void save_last_search_pattern(void)
void restore_last_search_pattern(void)
{
+ if (did_save_last_search_spat != 1) {
+ IEMSG("did_save_last_search_spat is not one");
+ return;
+ }
+ did_save_last_search_spat--;
+
xfree(spats[RE_SEARCH].pat);
spats[RE_SEARCH] = saved_last_search_spat;
+ saved_last_search_spat.pat = NULL;
set_vv_searchforward();
last_idx = saved_last_idx;
set_no_hlsearch(saved_no_hlsearch);
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 8036dea29f..767cf99be3 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -346,25 +346,104 @@ func Test_searchc()
bw!
endfunc
-func Test_search_cmdline3()
+func Cmdline3_prep()
throw 'skipped: Nvim does not support test_override()'
- if !exists('+incsearch')
- return
- endif
" need to disable char_avail,
" so that expansion of commandline works
call test_override("char_avail", 1)
new
call setline(1, [' 1', ' 2 the~e', ' 3 the theother'])
set incsearch
+endfunc
+
+func Incsearch_cleanup()
+ throw 'skipped: Nvim does not support test_override()'
+ set noincsearch
+ call test_override("char_avail", 0)
+ bw!
+endfunc
+
+func Test_search_cmdline3()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
1
" first match
call feedkeys("/the\<c-l>\<cr>", 'tx')
call assert_equal(' 2 the~e', getline('.'))
- " clean up
- set noincsearch
- call test_override("char_avail", 0)
- bw!
+
+ call Incsearch_cleanup()
+endfunc
+
+func Test_search_cmdline3s()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":%s/the\<c-l>/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxxe', getline('.'))
+ undo
+ call feedkeys(":%subs/the\<c-l>/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxxe', getline('.'))
+ undo
+ call feedkeys(":%substitute/the\<c-l>/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxxe', getline('.'))
+ undo
+ call feedkeys(":%smagic/the.e/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxx', getline('.'))
+ undo
+ call assert_fails(":%snomagic/the.e/xxx\<cr>", 'E486')
+ "
+ call feedkeys(":%snomagic/the\\.e/xxx\<cr>", 'tx')
+ call assert_equal(' 2 xxx', getline('.'))
+
+ call Incsearch_cleanup()
+endfunc
+
+func Test_search_cmdline3g()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":g/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(' 3 the theother', getline(2))
+ undo
+ call feedkeys(":global/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(' 3 the theother', getline(2))
+ undo
+ call feedkeys(":g!/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(1, line('$'))
+ call assert_equal(' 2 the~e', getline(1))
+ undo
+ call feedkeys(":global!/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(1, line('$'))
+ call assert_equal(' 2 the~e', getline(1))
+
+ call Incsearch_cleanup()
+endfunc
+
+func Test_search_cmdline3v()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call Cmdline3_prep()
+ 1
+ call feedkeys(":v/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(1, line('$'))
+ call assert_equal(' 2 the~e', getline(1))
+ undo
+ call feedkeys(":vglobal/the\<c-l>/d\<cr>", 'tx')
+ call assert_equal(1, line('$'))
+ call assert_equal(' 2 the~e', getline(1))
+
+ call Incsearch_cleanup()
endfunc
func Test_search_cmdline4()
@@ -423,6 +502,45 @@ func Test_search_cmdline5()
bw!
endfunc
+func Test_search_cmdline7()
+ throw 'skipped: Nvim does not support test_override()'
+ " Test that an pressing <c-g> in an empty command line
+ " does not move the cursor
+ if !exists('+incsearch')
+ return
+ endif
+ " need to disable char_avail,
+ " so that expansion of commandline works
+ call test_override("char_avail", 1)
+ new
+ let @/ = 'b'
+ call setline(1, [' bbvimb', ''])
+ set incsearch
+ " first match
+ norm! gg0
+ " moves to next match of previous search pattern, just like /<cr>
+ call feedkeys("/\<c-g>\<cr>", 'tx')
+ call assert_equal([0,1,2,0], getpos('.'))
+ " moves to next match of previous search pattern, just like /<cr>
+ call feedkeys("/\<cr>", 'tx')
+ call assert_equal([0,1,3,0], getpos('.'))
+ " moves to next match of previous search pattern, just like /<cr>
+ call feedkeys("/\<c-t>\<cr>", 'tx')
+ call assert_equal([0,1,7,0], getpos('.'))
+
+ " using an offset uses the last search pattern
+ call cursor(1, 1)
+ call setline(1, ['1 bbvimb', ' 2 bbvimb'])
+ let @/ = 'b'
+ call feedkeys("//e\<c-g>\<cr>", 'tx')
+ call assert_equal('1 bbvimb', getline('.'))
+ call assert_equal(4, col('.'))
+
+ set noincsearch
+ call test_override("char_avail", 0)
+ bw!
+endfunc
+
" Tests for regexp with various magic settings
func Test_search_regexp()
enew!
@@ -504,6 +622,7 @@ func Test_incsearch_substitute_dump()
\ 'for n in range(1, 10)',
\ ' call setline(n, "foo " . n)',
\ 'endfor',
+ \ 'call setline(11, "bar 11")',
\ '3',
\ ], 'Xis_subst_script')
let buf = RunVimInTerminal('-S Xis_subst_script', {'rows': 9, 'cols': 70})
@@ -512,6 +631,7 @@ func Test_incsearch_substitute_dump()
sleep 100m
" Need to send one key at a time to force a redraw.
+ " Select three lines at the cursor with typed pattern.
call term_sendkeys(buf, ':.,.+2s/')
sleep 100m
call term_sendkeys(buf, 'f')
@@ -520,12 +640,203 @@ func Test_incsearch_substitute_dump()
sleep 100m
call term_sendkeys(buf, 'o')
call VerifyScreenDump(buf, 'Test_incsearch_substitute_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Select three lines at the cursor using previous pattern.
+ call term_sendkeys(buf, "/foo\<CR>")
+ sleep 100m
+ call term_sendkeys(buf, ':.,.+2s//')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_02', {})
+
+ " Deleting last slash should remove the match.
+ call term_sendkeys(buf, "\<BS>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_03', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Reverse range is accepted
+ call term_sendkeys(buf, ':5,2s/foo')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_04', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " White space after the command is skipped
+ call term_sendkeys(buf, ':2,3sub /fo')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_05', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Command modifiers are skipped
+ call term_sendkeys(buf, ':above below browse botr confirm keepmar keepalt keeppat keepjum filter xxx hide lockm leftabove noau noswap rightbel sandbox silent silent! $tab top unsil vert verbose 4,5s/fo.')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_06', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Cursorline highlighting at match
+ call term_sendkeys(buf, ":set cursorline\<CR>")
+ call term_sendkeys(buf, 'G9G')
+ call term_sendkeys(buf, ':9,11s/bar')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_07', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Cursorline highlighting at cursor when no match
+ call term_sendkeys(buf, ':9,10s/bar')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_08', {})
+ call term_sendkeys(buf, "\<Esc>")
+ " Only \v handled as empty pattern, does not move cursor
+ call term_sendkeys(buf, '3G4G')
+ call term_sendkeys(buf, ":nohlsearch\<CR>")
+ call term_sendkeys(buf, ':6,7s/\v')
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_09', {})
call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ":set nocursorline\<CR>")
+
+ " All matches are highlighted for 'hlsearch' after the incsearch canceled
+ call term_sendkeys(buf, "1G*")
+ call term_sendkeys(buf, ":2,5s/foo")
+ sleep 100m
+ call term_sendkeys(buf, "\<Esc>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_10', {})
+
+ call term_sendkeys(buf, ":split\<CR>")
+ call term_sendkeys(buf, ":let @/ = 'xyz'\<CR>")
+ call term_sendkeys(buf, ":%s/.")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_11', {})
+ call term_sendkeys(buf, "\<BS>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_12', {})
+ call term_sendkeys(buf, "\<Esc>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_13', {})
+
call StopVimInTerminal(buf)
call delete('Xis_subst_script')
endfunc
+" Similar to Test_incsearch_substitute_dump() for :sort
+func Test_incsearch_sort_dump()
+ if !exists('+incsearch')
+ return
+ endif
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot make screendumps'
+ endif
+ call writefile([
+ \ 'set incsearch hlsearch scrolloff=0',
+ \ 'call setline(1, ["another one 2", "that one 3", "the one 1"])',
+ \ ], 'Xis_sort_script')
+ let buf = RunVimInTerminal('-S Xis_sort_script', {'rows': 9, 'cols': 70})
+ " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by
+ " the 'ambiwidth' check.
+ sleep 100m
+
+ " Need to send one key at a time to force a redraw.
+ call term_sendkeys(buf, ':sort ni u /on')
+ call VerifyScreenDump(buf, 'Test_incsearch_sort_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('Xis_sort_script')
+endfunc
+
+" Similar to Test_incsearch_substitute_dump() for :vimgrep famiry
+func Test_incsearch_vimgrep_dump()
+ if !exists('+incsearch')
+ return
+ endif
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot make screendumps'
+ endif
+ call writefile([
+ \ 'set incsearch hlsearch scrolloff=0',
+ \ 'call setline(1, ["another one 2", "that one 3", "the one 1"])',
+ \ ], 'Xis_vimgrep_script')
+ let buf = RunVimInTerminal('-S Xis_vimgrep_script', {'rows': 9, 'cols': 70})
+ " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by
+ " the 'ambiwidth' check.
+ sleep 100m
+
+ " Need to send one key at a time to force a redraw.
+ call term_sendkeys(buf, ':vimgrep on')
+ call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ':vimg /on/ *.txt')
+ call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_02', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ':vimgrepadd "\<on')
+ call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_03', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ':lv "tha')
+ call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_04', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ':lvimgrepa "the" **/*.txt')
+ call VerifyScreenDump(buf, 'Test_incsearch_vimgrep_05', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('Xis_vimgrep_script')
+endfunc
+
+func Test_keep_last_search_pattern()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ new
+ call setline(1, ['foo', 'foo', 'foo'])
+ set incsearch
+ call test_override("char_avail", 1)
+ let @/ = 'bar'
+ call feedkeys(":/foo/s//\<Esc>", 'ntx')
+ call assert_equal('bar', @/)
+
+ " no error message if pattern not found
+ call feedkeys(":/xyz/s//\<Esc>", 'ntx')
+ call assert_equal('bar', @/)
+
+ bwipe!
+ call test_override("ALL", 0)
+ set noincsearch
+endfunc
+
+func Test_word_under_cursor_after_match()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ new
+ call setline(1, 'foo bar')
+ set incsearch
+ call test_override("char_avail", 1)
+ try
+ call feedkeys("/foo\<C-R>\<C-W>\<CR>", 'ntx')
+ catch /E486:/
+ endtry
+ call assert_equal('foobar', @/)
+
+ bwipe!
+ call test_override("ALL", 0)
+ set noincsearch
+endfunc
+
+func Test_subst_word_under_cursor()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ new
+ call setline(1, ['int SomeLongName;', 'for (xxx = 1; xxx < len; ++xxx)'])
+ set incsearch
+ call test_override("char_avail", 1)
+ call feedkeys("/LongName\<CR>", 'ntx')
+ call feedkeys(":%s/xxx/\<C-R>\<C-W>/g\<CR>", 'ntx')
+ call assert_equal('for (SomeLongName = 1; SomeLongName < len; ++SomeLongName)', getline(2))
+
+ bwipe!
+ call test_override("ALL", 0)
+ set noincsearch
+endfunc
+
func Test_incsearch_with_change()
if !has('timers') || !exists('+incsearch') || !CanRunVimInTerminal()
throw 'Skipped: cannot make screendumps and/or timers feature and/or incsearch option missing'
@@ -580,6 +891,61 @@ func Test_incsearch_scrolling()
call delete('Xscript')
endfunc
+func Test_incsearch_search_dump()
+ if !exists('+incsearch')
+ return
+ endif
+ if !CanRunVimInTerminal()
+ return
+ endif
+ call writefile([
+ \ 'set incsearch hlsearch scrolloff=0',
+ \ 'for n in range(1, 8)',
+ \ ' call setline(n, "foo " . n)',
+ \ 'endfor',
+ \ '3',
+ \ ], 'Xis_search_script')
+ let buf = RunVimInTerminal('-S Xis_search_script', {'rows': 9, 'cols': 70})
+ " Give Vim a chance to redraw to get rid of the spaces in line 2 caused by
+ " the 'ambiwidth' check.
+ sleep 100m
+
+ " Need to send one key at a time to force a redraw.
+ call term_sendkeys(buf, '/fo')
+ call VerifyScreenDump(buf, 'Test_incsearch_search_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+ sleep 100m
+
+ call term_sendkeys(buf, '/\v')
+ call VerifyScreenDump(buf, 'Test_incsearch_search_02', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('Xis_search_script')
+endfunc
+
+func Test_incsearch_substitute()
+ throw 'skipped: Nvim does not support test_override()'
+ if !exists('+incsearch')
+ return
+ endif
+ call test_override("char_avail", 1)
+ new
+ set incsearch
+ for n in range(1, 10)
+ call setline(n, 'foo ' . n)
+ endfor
+ 4
+ call feedkeys(":.,.+2s/foo\<BS>o\<BS>o/xxx\<cr>", 'tx')
+ call assert_equal('foo 3', getline(3))
+ call assert_equal('xxx 4', getline(4))
+ call assert_equal('xxx 5', getline(5))
+ call assert_equal('xxx 6', getline(6))
+ call assert_equal('foo 7', getline(7))
+
+ call Incsearch_cleanup()
+endfunc
+
func Test_search_undefined_behaviour()
if !has("terminal")
return