mirror of
https://github.com/neovim/neovim.git
synced 2025-09-22 11:18:19 +00:00
Merge pull request #12721 from aufarg/vim-8.1.0265
[RDY] vim-patch:8.1.{265,271,273,274,275,277,278,279,280,281,282,284,286,291,295,296,320,321,339,351,392,399,552}
This commit is contained in:
@@ -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"
|
||||
|
@@ -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, ®_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);
|
||||
|
||||
// Update locals from parse_one_cmd()
|
||||
errormsg = parsed.errormsg;
|
||||
p = parsed.parsed_upto;
|
||||
|
||||
if (!parse_success) {
|
||||
// "#!anything" is handled like a comment.
|
||||
if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') {
|
||||
goto doend;
|
||||
}
|
||||
|
||||
// 1. Skip comment lines and leading white space and colons.
|
||||
// 2. Handle command modifiers.
|
||||
|
||||
// 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, ®_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;
|
||||
|
@@ -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)) {
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -21,6 +21,7 @@ describe('search cmdline', function()
|
||||
err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
|
||||
more = { bold = true, foreground = Screen.colors.SeaGreen4 },
|
||||
tilde = { bold = true, foreground = Screen.colors.Blue1 },
|
||||
hl = { background = Screen.colors.Yellow },
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -570,4 +571,72 @@ describe('search cmdline', function()
|
||||
?the |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('incsearch works with :sort', function()
|
||||
-- oldtest: Test_incsearch_sort_dump().
|
||||
screen:try_resize(20, 4)
|
||||
command('set incsearch hlsearch scrolloff=0')
|
||||
funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
|
||||
|
||||
feed(':sort ni u /on')
|
||||
screen:expect([[
|
||||
another {inc:on}e 2 |
|
||||
that {hl:on}e 3 |
|
||||
the {hl:on}e 1 |
|
||||
:sort ni u /on^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
end)
|
||||
|
||||
it('incsearch works with :vimgrep family', function()
|
||||
-- oldtest: Test_incsearch_vimgrep_dump().
|
||||
screen:try_resize(30, 4)
|
||||
command('set incsearch hlsearch scrolloff=0')
|
||||
funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'})
|
||||
|
||||
feed(':vimgrep on')
|
||||
screen:expect([[
|
||||
another {inc:on}e 2 |
|
||||
that {hl:on}e 3 |
|
||||
the {hl:on}e 1 |
|
||||
:vimgrep on^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
|
||||
feed(':vimg /on/ *.txt')
|
||||
screen:expect([[
|
||||
another {inc:on}e 2 |
|
||||
that {hl:on}e 3 |
|
||||
the {hl:on}e 1 |
|
||||
:vimg /on/ *.txt^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
|
||||
feed(':vimgrepadd "\\<LT>on')
|
||||
screen:expect([[
|
||||
another {inc:on}e 2 |
|
||||
that {hl:on}e 3 |
|
||||
the {hl:on}e 1 |
|
||||
:vimgrepadd "\<on^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
|
||||
feed(':lv "tha')
|
||||
screen:expect([[
|
||||
another one 2 |
|
||||
{inc:tha}t one 3 |
|
||||
the one 1 |
|
||||
:lv "tha^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
|
||||
feed(':lvimgrepa "the" **/*.txt')
|
||||
screen:expect([[
|
||||
ano{inc:the}r one 2 |
|
||||
that one 3 |
|
||||
{hl:the} one 1 |
|
||||
:lvimgrepa "the" **/*.txt^ |
|
||||
]])
|
||||
feed('<esc>')
|
||||
end)
|
||||
end)
|
||||
|
@@ -162,7 +162,7 @@ describe(":substitute, 'inccommand' preserves", function()
|
||||
insert(default_text)
|
||||
feed_command("set inccommand=" .. case)
|
||||
|
||||
local delims = { '/', '#', ';', '%', ',', '@', '!', ''}
|
||||
local delims = { '/', '#', ';', '%', ',', '@', '!' }
|
||||
for _,delim in pairs(delims) do
|
||||
feed_command("%s"..delim.."lines"..delim.."LINES"..delim.."g")
|
||||
expect([[
|
||||
|
Reference in New Issue
Block a user