diff options
| -rw-r--r-- | runtime/doc/cmdline.txt | 11 | ||||
| -rw-r--r-- | runtime/doc/eval.txt | 2 | ||||
| -rw-r--r-- | runtime/doc/map.txt | 2 | ||||
| -rw-r--r-- | runtime/doc/starting.txt | 3 | ||||
| -rwxr-xr-x | scripts/vim-patch.sh | 6 | ||||
| -rw-r--r-- | src/nvim/buffer.c | 5 | ||||
| -rw-r--r-- | src/nvim/ex_cmds2.c | 9 | ||||
| -rw-r--r-- | src/nvim/ex_docmd.c | 111 | ||||
| -rw-r--r-- | src/nvim/ex_getln.c | 95 | ||||
| -rw-r--r-- | src/nvim/lua/vim.lua | 4 | ||||
| -rw-r--r-- | src/nvim/normal.c | 158 | ||||
| -rw-r--r-- | src/nvim/ops.c | 96 | ||||
| -rw-r--r-- | src/nvim/search.c | 88 | ||||
| -rw-r--r-- | src/nvim/search.h | 2 | ||||
| -rw-r--r-- | src/nvim/testdir/Makefile | 1 | ||||
| -rw-r--r-- | src/nvim/testdir/test_cmdline.vim | 71 | ||||
| -rw-r--r-- | src/nvim/testdir/test_gn.vim | 46 | ||||
| -rw-r--r-- | src/nvim/testdir/test_normal.vim | 45 | ||||
| -rw-r--r-- | src/nvim/testdir/test_preview.vim | 13 | ||||
| -rw-r--r-- | src/nvim/testdir/test_textobjects.vim | 30 | ||||
| -rw-r--r-- | src/nvim/vim.h | 2 | ||||
| -rw-r--r-- | test/functional/ex_cmds/cmd_map_spec.lua | 28 | ||||
| -rw-r--r-- | test/functional/legacy/003_cindent_spec.lua | 17 | 
23 files changed, 594 insertions, 251 deletions
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index c46a9fc2d8..6e0c3269ab 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -163,12 +163,14 @@ CTRL-R CTRL-F				*c_CTRL-R_CTRL-F* *c_<C-R>_<C-F>*  CTRL-R CTRL-P				*c_CTRL-R_CTRL-P* *c_<C-R>_<C-P>*  CTRL-R CTRL-W				*c_CTRL-R_CTRL-W* *c_<C-R>_<C-W>*  CTRL-R CTRL-A				*c_CTRL-R_CTRL-A* *c_<C-R>_<C-A>* +CTRL-R CTRL-L				*c_CTRL-R_CTRL-L* *c_<C-R>_<C-L>*  		Insert the object under the cursor:  			CTRL-F	the Filename under the cursor  			CTRL-P	the Filename under the cursor, expanded with  				'path' as in |gf|  			CTRL-W	the Word under the cursor  			CTRL-A	the WORD under the cursor; see |WORD| +			CTRL-L	the line under the cursor  		When 'incsearch' is set the cursor position at the end of the  		currently displayed match is used.  With CTRL-W the part of @@ -176,8 +178,8 @@ CTRL-R CTRL-A				*c_CTRL-R_CTRL-A* *c_<C-R>_<C-A>*  					*c_CTRL-R_CTRL-R* *c_<C-R>_<C-R>*  					*c_CTRL-R_CTRL-O* *c_<C-R>_<C-O>* -CTRL-R CTRL-R {0-9a-z"%#:-=. CTRL-F CTRL-P CTRL-W CTRL-A} -CTRL-R CTRL-O {0-9a-z"%#:-=. CTRL-F CTRL-P CTRL-W CTRL-A} +CTRL-R CTRL-R {0-9a-z"%#:-=. CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L} +CTRL-R CTRL-O {0-9a-z"%#:-=. CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L}  		Insert register or object under the cursor.  Works like  		|c_CTRL-R| but inserts the text literally.  For example, if  		register a contains "xy^Hz" (where ^H is a backspace), @@ -786,6 +788,11 @@ Also see |`=|.  Note: these are typed literally, they are not special keys!  	<cword>    is replaced with the word under the cursor (like |star|)  	<cWORD>    is replaced with the WORD under the cursor (see |WORD|) +	<cexpr>    is replaced with the word under the cursor, including more +		   to form a C expression.  E.g., when the cursor is on "arg" +		   of "ptr->arg" then the result is "ptr->arg"; when the +		   cursor is on "]" of "list[idx]" then the result is +		   "list[idx]".  This is used for |v:beval_text|.  	<cfile>    is replaced with the path name under the cursor (like what  		   |gf| uses)  	<afile>    When executing autocommands, is replaced with the file name diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 493345edee..594b67b5c8 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4080,6 +4080,7 @@ getcompletion({pat}, {type} [, {filtered}])		*getcompletion()*  		specifies what for.  The following completion types are  		supported: +		arglist		file names in argument list  		augroup		autocmd groups  		buffer		buffer names  		behave		:behave suboptions @@ -4100,6 +4101,7 @@ getcompletion({pat}, {type} [, {filtered}])		*getcompletion()*  		highlight	highlight groups  		history		:history suboptions  		locale		locale names (as output of locale -a) +		mapclear        buffer argument  		mapping		mapping name  		menu		menus  		messages	|:messages| suboptions diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 804b7410f6..d630bf5652 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1208,6 +1208,7 @@ By default, the arguments of user defined commands do not undergo completion.  However, by specifying one or the other of the following attributes, argument  completion can be enabled: +	-complete=arglist	file names in argument list  	-complete=augroup	autocmd groups  	-complete=buffer	buffer names  	-complete=behave	:behave suboptions @@ -1227,6 +1228,7 @@ completion can be enabled:  	-complete=highlight	highlight groups  	-complete=history	:history suboptions  	-complete=locale	locale names (as output of locale -a) +	-complete=mapclear	buffer argument  	-complete=mapping	mapping name  	-complete=menu		menus  	-complete=messages	|:messages| suboptions diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 24fb87a6b4..c9ce2b9dc1 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -408,9 +408,6 @@ accordingly.  Vim proceeds in this order:  		Windows		$XDG_CONFIG_HOME/nvim/init.vim  				(default for $XDG_CONFIG_HOME is ~/AppData/Local) -	The files are searched in the order specified above and only the first -	one that is found is read. -  	RECOMMENDATION: Put all your Vim configuration stuff in the   	$HOME/.config/nvim/ directory. That makes it easy to copy it to   	another system. diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 6dd411a34e..9ea43383b6 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -85,12 +85,12 @@ get_vim_sources() {      git clone https://github.com/vim/vim.git "${VIM_SOURCE_DIR}"      cd "${VIM_SOURCE_DIR}"    else -    if [[ ! -d "${VIM_SOURCE_DIR}/.git" ]]; then +    cd "${VIM_SOURCE_DIR}" +    if [[ "$(git rev-parse --show-toplevel)" != "${VIM_SOURCE_DIR}" ]]; then        msg_err "${VIM_SOURCE_DIR} does not appear to be a git repository."        echo "  Please remove it and try again."        exit 1      fi -    cd "${VIM_SOURCE_DIR}"      echo "Updating Vim sources: ${VIM_SOURCE_DIR}"      git pull &&        msg_ok "Updated Vim sources." || @@ -202,7 +202,7 @@ get_vimpatch() {    printf "Pre-processing patch...\n"    preprocess_patch "${NVIM_SOURCE_DIR}/${patch_file}" -  msg_ok "Saved patch to '${NVIM_SOURCE_DIR}/${patch_file}'.\n" +  msg_ok "Saved patch to '${NVIM_SOURCE_DIR}/${patch_file}'."  }  stage_patch() { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 00d472b4c8..64569c294b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2659,9 +2659,8 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)   * Get alternate file name for current window.   * Return NULL if there isn't any, and give error message if requested.   */ -char_u * -getaltfname ( -    int errmsg                     /* give error message */ +char_u * getaltfname( +    bool errmsg                   // give error message  )  {    char_u      *fname; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 120278d3fb..c384d253b9 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2254,6 +2254,15 @@ static int alist_add_list(int count, char_u **files, int after)    }  } +// Function given to ExpandGeneric() to obtain the possible arguments of the +// argedit and argdelete commands. +char_u *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ +  if (idx >= ARGCOUNT) { +    return NULL; +  } +  return alist_name(&ARGLIST[idx]); +}  /// ":compiler[!] {name}"  void ex_compiler(exarg_T *eap) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c31242f2ac..20c188ed55 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2665,8 +2665,8 @@ const char * set_one_cmd_context(    size_t len = 0;    exarg_T ea;    int context = EXPAND_NOTHING; -  int forceit = false; -  int usefilter = false;  // Filter instead of file name. +  bool forceit = false; +  bool usefilter = false;  // Filter instead of file name.    ExpandInit(xp);    xp->xp_pattern = (char_u *)buff; @@ -2786,9 +2786,9 @@ const char * set_one_cmd_context(    xp->xp_context = EXPAND_NOTHING;   /* Default now that we're past command */ -  if (*p == '!') {                  /* forced commands */ -    forceit = TRUE; -    ++p; +  if (*p == '!') {                  // forced commands +    forceit = true; +    p++;    }    /* @@ -2813,10 +2813,10 @@ const char * set_one_cmd_context(    }    if (ea.cmdidx == CMD_read) { -    usefilter = forceit;                        /* :r! filter if forced */ -    if (*arg == '!') {                          /* :r !filter */ -      ++arg; -      usefilter = TRUE; +    usefilter = forceit;                        // :r! filter if forced +    if (*arg == '!') {                          // :r !filter +      arg++; +      usefilter = true;      }    } @@ -2978,7 +2978,7 @@ const char * set_one_cmd_context(        // A full match ~user<Tab> will be replaced by user's home        // directory i.e. something like ~user<Tab> -> /home/user/        if (*p == NUL && p > (const char *)xp->xp_pattern + 1 -          && match_user(xp->xp_pattern + 1) == 1) { +          && match_user(xp->xp_pattern + 1) >= 1) {          xp->xp_context = EXPAND_USER;          ++xp->xp_pattern;        } @@ -3350,6 +3350,19 @@ const char * set_one_cmd_context(    case CMD_xunmap:      return (const char *)set_context_in_map_cmd(          xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); +  case CMD_mapclear: +  case CMD_nmapclear: +  case CMD_vmapclear: +  case CMD_omapclear: +  case CMD_imapclear: +  case CMD_cmapclear: +  case CMD_lmapclear: +  case CMD_smapclear: +  case CMD_xmapclear: +    xp->xp_context = EXPAND_MAPCLEAR; +    xp->xp_pattern = (char_u *)arg; +    break; +    case CMD_abbreviate:    case CMD_noreabbrev:    case CMD_cabbrev:       case CMD_cnoreabbrev:    case CMD_iabbrev:       case CMD_inoreabbrev: @@ -3441,6 +3454,13 @@ const char * set_one_cmd_context(      xp->xp_pattern = (char_u *)arg;      break; +  case CMD_argdelete: +    while ((xp->xp_pattern = vim_strchr((const char_u *)arg, ' ')) != NULL) { +      arg = (const char *)(xp->xp_pattern + 1); +    } +    xp->xp_context = EXPAND_ARGLIST; +    xp->xp_pattern = (char_u *)arg; +    break;    default:      break; @@ -4846,6 +4866,7 @@ static struct {   */  static const char *command_complete[] =  { +  [EXPAND_ARGLIST] = "arglist",    [EXPAND_AUGROUP] = "augroup",    [EXPAND_BEHAVE] = "behave",    [EXPAND_BUFFERS] = "buffer", @@ -4870,6 +4891,7 @@ static const char *command_complete[] =  #ifdef HAVE_WORKING_LIBINTL    [EXPAND_LOCALES] = "locale",  #endif +  [EXPAND_MAPCLEAR] = "mapclear",    [EXPAND_MAPPINGS] = "mapping",    [EXPAND_MENUS] = "menu",    [EXPAND_MESSAGES] = "messages", @@ -8370,23 +8392,25 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)      "%",  #define SPEC_PERC   0      "#", -#define SPEC_HASH   1 -    "<cword>",                          /* cursor word */ -#define SPEC_CWORD  2 -    "<cWORD>",                          /* cursor WORD */ -#define SPEC_CCWORD 3 -    "<cfile>",                          /* cursor path name */ -#define SPEC_CFILE  4 -    "<sfile>",                          /* ":so" file name */ -#define SPEC_SFILE  5 -    "<slnum>",                          /* ":so" file line number */ -#define SPEC_SLNUM  6 -    "<afile>",                          /* autocommand file name */ -# define SPEC_AFILE 7 -    "<abuf>",                           /* autocommand buffer number */ -# define SPEC_ABUF  8 -    "<amatch>",                         /* autocommand match name */ -# define SPEC_AMATCH 9 +#define SPEC_HASH   (SPEC_PERC + 1) +    "<cword>",                          // cursor word +#define SPEC_CWORD  (SPEC_HASH + 1) +    "<cWORD>",                          // cursor WORD +#define SPEC_CCWORD (SPEC_CWORD + 1) +    "<cexpr>",                          // expr under cursor +#define SPEC_CEXPR  (SPEC_CCWORD + 1) +    "<cfile>",                          // cursor path name +#define SPEC_CFILE  (SPEC_CEXPR + 1) +    "<sfile>",                          // ":so" file name +#define SPEC_SFILE  (SPEC_CFILE + 1) +    "<slnum>",                          // ":so" file line number +#define SPEC_SLNUM  (SPEC_SFILE + 1) +    "<afile>",                          // autocommand file name +#define SPEC_AFILE  (SPEC_SLNUM + 1) +    "<abuf>",                           // autocommand buffer number +#define SPEC_ABUF   (SPEC_AFILE + 1) +    "<amatch>",                         // autocommand match name +#define SPEC_AMATCH (SPEC_ABUF + 1)    };    for (size_t i = 0; i < ARRAY_SIZE(spec_str); ++i) { @@ -8467,10 +8491,16 @@ eval_vars (    /*     * word or WORD under cursor     */ -  if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD) { -    resultlen = find_ident_under_cursor(&result, (spec_idx == SPEC_CWORD -                                                  ? (FIND_IDENT|FIND_STRING) -                                                  : FIND_STRING)); +  if (spec_idx == SPEC_CWORD +      || spec_idx == SPEC_CCWORD +      || spec_idx == SPEC_CEXPR) { +    resultlen = find_ident_under_cursor( +        &result, +        spec_idx == SPEC_CWORD +        ? (FIND_IDENT | FIND_STRING) +        : (spec_idx == SPEC_CEXPR +           ? (FIND_IDENT | FIND_STRING | FIND_EVAL) +           : FIND_STRING));      if (resultlen == 0) {        *errormsg = (char_u *)"";        return NULL; @@ -8507,9 +8537,13 @@ eval_vars (        if (*s == '<')                    /* "#<99" uses v:oldfiles */          ++s;        i = getdigits_int(&s); -      *usedlen = (size_t)(s - src);           /* length of what we expand */ +      if (s == src + 2 && src[1] == '-') { +        // just a minus sign, don't skip over it +        s--; +      } +      *usedlen = (size_t)(s - src);           // length of what we expand -      if (src[1] == '<') { +      if (src[1] == '<' && i != 0) {          if (*usedlen < 2) {            /* Should we give an error message for #<text? */            *usedlen = 1; @@ -8522,6 +8556,9 @@ eval_vars (            return NULL;          }        } else { +        if (i == 0 && src[1] == '<' && *usedlen > 1) { +          *usedlen = 1; +        }          buf = buflist_findnr(i);          if (buf == NULL) {            *errormsg = (char_u *)_( @@ -9655,6 +9692,14 @@ char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)    return NULL;  } +char_u *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ +  if (idx == 0) { +    return (char_u *)"<buffer>"; +  } +  return NULL; +} +  static TriState filetype_detect = kNone;  static TriState filetype_plugin = kNone;  static TriState filetype_indent = kNone; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1810056a4a..da1681520b 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -177,10 +177,6 @@ typedef struct command_line_state {    int break_ctrl_c;    expand_T xpc;    long *b_im_ptr; -  // Everything that may work recursively should save and restore the -  // current command line in save_ccline.  That includes update_screen(), a -  // custom status line may invoke ":normal". -  struct cmdline_info save_ccline;  } CommandLineState;  typedef struct cmdline_info CmdlineInfo; @@ -225,11 +221,19 @@ static bool need_cursor_update = false;  # include "ex_getln.c.generated.h"  #endif -static int cmd_hkmap = 0;       /* Hebrew mapping during command line */ +static int cmd_hkmap = 0;  // Hebrew mapping during command line +static int cmd_fkmap = 0;  // Farsi mapping during command line -static int cmd_fkmap = 0;       /* Farsi mapping during command line */ +/// Internal entry point for cmdline mode. +/// +/// caller must use save_cmdline and restore_cmdline. Best is to use +/// getcmdline or getcmdline_prompt, instead of calling this directly.  static uint8_t *command_line_enter(int firstc, long count, int indent)  { +  // can be invoked recursively, identify each level +  static int cmdline_level = 0; +  cmdline_level++; +    CommandLineState state, *s = &state;    memset(s, 0, sizeof(CommandLineState));    s->firstc = firstc; @@ -257,7 +261,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)    }    ccline.prompt_id = last_prompt_id++; -  ccline.level++; +  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 @@ -489,19 +493,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)    sb_text_end_cmdline(); -  { -    char_u *p = ccline.cmdbuff; - -    // Make ccline empty, getcmdline() may try to use it. -    ccline.cmdbuff = NULL; +  char_u *p = ccline.cmdbuff; -    if (ui_is_external(kUICmdline)) { -      ccline.redraw_state = kCmdRedrawNone; -      ui_call_cmdline_hide(ccline.level); -    } -    ccline.level--; -    return p; +  if (ui_is_external(kUICmdline)) { +    ui_call_cmdline_hide(ccline.level);    } + +  cmdline_level--; +  return p;  }  static int command_line_check(VimState *state) @@ -641,9 +640,7 @@ static int command_line_execute(VimState *state, int key)          p_ls = save_p_ls;          p_wmh = save_p_wmh;          last_status(false); -        save_cmdline(&s->save_ccline);          update_screen(VALID);                 // redraw the screen NOW -        restore_cmdline(&s->save_ccline);          redrawcmd();          save_p_ls = -1;          wild_menu_showing = 0; @@ -818,18 +815,17 @@ static int command_line_execute(VimState *state, int key)          new_cmdpos = ccline.cmdpos;        } -      save_cmdline(&s->save_ccline);        s->c = get_expr_register(); -      restore_cmdline(&s->save_ccline);        if (s->c == '=') {          // Need to save and restore ccline.  And set "textlock"          // to avoid nasty things like going to another buffer when          // evaluating an expression. -        save_cmdline(&s->save_ccline); -        ++textlock; +        CmdlineInfo save_ccline; +        save_cmdline(&save_ccline); +        textlock++;          p = get_expr_line(); -        --textlock; -        restore_cmdline(&s->save_ccline); +        textlock--; +        restore_cmdline(&save_ccline);          if (p != NULL) {            len = (int)STRLEN(p); @@ -1362,9 +1358,10 @@ static int command_line_handle_key(CommandLineState *s)          beep_flush();          s->c = ESC;        } else { -        save_cmdline(&s->save_ccline); +        CmdlineInfo save_ccline; +        save_cmdline(&save_ccline);          s->c = get_expr_register(); -        restore_cmdline(&s->save_ccline); +        restore_cmdline(&save_ccline);        }      } @@ -1899,9 +1896,7 @@ static int command_line_changed(CommandLineState *s)        curwin->w_redr_status = true;      } -    save_cmdline(&s->save_ccline);      update_screen(SOME_VALID); -    restore_cmdline(&s->save_ccline);      restore_last_search_pattern();      // Leave it at the end to make CTRL-R CTRL-W work. @@ -1996,7 +1991,14 @@ getcmdline (      int indent               // indent for inside conditionals  )  { -  return command_line_enter(firstc, count, indent); +  // Be prepared for situations where cmdline can be invoked recursively. +  // That includes cmd mappings, event handlers, as well as update_screen() +  // (custom status line eval), which all may invoke ":normal :". +  CmdlineInfo save_ccline; +  save_cmdline(&save_ccline); +  char_u *retval = command_line_enter(firstc, count, indent); +  restore_cmdline(&save_ccline); +  return retval;  }  /// Get a command line with a prompt @@ -2020,7 +2022,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,  {    const int msg_col_save = msg_col; -  struct cmdline_info save_ccline; +  CmdlineInfo save_ccline;    save_cmdline(&save_ccline);    ccline.prompt_id = last_prompt_id++; @@ -2034,7 +2036,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,    int msg_silent_saved = msg_silent;    msg_silent = 0; -  char *const ret = (char *)getcmdline(firstc, 1L, 0); +  char *const ret = (char *)command_line_enter(firstc, 1L, 0);    restore_cmdline(&save_ccline);    msg_silent = msg_silent_saved; @@ -2176,6 +2178,7 @@ getexline (    /* When executing a register, remove ':' that's in front of each line. */    if (exec_from_reg && vpeekc() == ':')      (void)vgetc(); +    return getcmdline(c, 1L, indent);  } @@ -3019,16 +3022,16 @@ void cmdline_screen_cleared(void)    }    int prev_level = ccline.level-1; -  CmdlineInfo *prev_ccline = ccline.prev_ccline; -  while (prev_level > 0 && prev_ccline) { -    if (prev_ccline->level == prev_level) { +  CmdlineInfo *line = ccline.prev_ccline; +  while (prev_level > 0 && line) { +    if (line->level == prev_level) {        // don't redraw a cmdline already shown in the cmdline window        if (prev_level != cmdwin_level) { -        prev_ccline->redraw_state = kCmdRedrawAll; +        line->redraw_state = kCmdRedrawAll;        }        prev_level--;      } -    prev_ccline = prev_ccline->prev_ccline; +    line = line->prev_ccline;    }    need_cursor_update = true; @@ -3244,6 +3247,7 @@ static void save_cmdline(struct cmdline_info *ccp)    ccline.cmdprompt = NULL;    ccline.xpc = NULL;    ccline.special_char = NUL; +  ccline.level = 0;  }  /* @@ -3286,17 +3290,18 @@ void restore_cmdline_alloc(char_u *p)  /// @returns FAIL for failure, OK otherwise  static bool cmdline_paste(int regname, bool literally, bool remcr)  { -  long i;    char_u              *arg;    char_u              *p; -  int allocated; +  bool allocated;    struct cmdline_info save_ccline;    /* check for valid regname; also accept special characters for CTRL-R in     * the command line */    if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W -      && regname != Ctrl_A && !valid_yank_reg(regname, false)) +      && regname != Ctrl_A && regname != Ctrl_L +      && !valid_yank_reg(regname, false)) {      return FAIL; +  }    /* A register containing CTRL-R can cause an endless loop.  Allow using     * CTRL-C to break the loop. */ @@ -3308,9 +3313,9 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)    /* Need to save and restore ccline.  And set "textlock" to avoid nasty     * things like going to another buffer when evaluating an expression. */    save_cmdline(&save_ccline); -  ++textlock; -  i = get_spec_reg(regname, &arg, &allocated, TRUE); -  --textlock; +  textlock++; +  const bool i = get_spec_reg(regname, &arg, &allocated, true); +  textlock--;    restore_cmdline(&save_ccline);    if (i) { @@ -4756,6 +4761,7 @@ ExpandFromContext (      } tab[] = {        { EXPAND_COMMANDS, get_command_name, false, true },        { EXPAND_BEHAVE, get_behave_arg, true, true }, +      { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },        { EXPAND_MESSAGES, get_messages_arg, true, true },        { EXPAND_HISTORY, get_history_arg, true, true },        { EXPAND_USER_COMMANDS, get_user_commands, false, true }, @@ -4783,6 +4789,7 @@ ExpandFromContext (  #endif        { EXPAND_ENV_VARS, get_env_name, true, true },        { EXPAND_USER, get_users, true, false }, +      { EXPAND_ARGLIST, get_arglist_name, true, false },      };      int i; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index e1bbb03d3f..47f40da72e 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -13,7 +13,7 @@ local function _os_proc_info(pid)    if pid == nil or pid <= 0 or type(pid) ~= 'number' then      error('invalid pid')    end -  local cmd = { 'ps', '-p', pid, '-o', 'ucomm=', } +  local cmd = { 'ps', '-p', pid, '-o', 'comm=', }    local err, name = _system(cmd)    if 1 == err and string.gsub(name, '%s*', '') == '' then      return {}  -- Process not found. @@ -23,7 +23,7 @@ local function _os_proc_info(pid)    end    local _, ppid = _system({ 'ps', '-p', pid, '-o', 'ppid=', })    -- Remove trailing whitespace. -  name = string.gsub(name, '%s+$', '') +  name = string.gsub(string.gsub(name, '%s+$', ''), '^.*/', '')    ppid = string.gsub(ppid, '%s+$', '')    ppid = tonumber(ppid) == nil and -1 or tonumber(ppid)    return { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 225fe4aa9a..afddd548a3 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2990,6 +2990,43 @@ void reset_VIsual(void)    }  } +// Check for a balloon-eval special item to include when searching for an +// identifier.  When "dir" is BACKWARD "ptr[-1]" must be valid! +// Returns true if the character at "*ptr" should be included. +// "dir" is FORWARD or BACKWARD, the direction of searching. +// "*colp" is in/decremented if "ptr[-dir]" should also be included. +// "bnp" points to a counter for square brackets. +static bool find_is_eval_item( +    const char_u *const ptr, +    int *const colp, +    int *const bnp, +    const int dir) +{ +  // Accept everything inside []. +  if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) { +    *bnp += 1; +  } +  if (*bnp > 0) { +    if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD)) { +      *bnp -= 1; +    } +    return true; +  } + +  // skip over "s.var" +  if (*ptr == '.') { +    return true; +  } + +  // two-character item: s->var +  if (ptr[dir == BACKWARD ? 0 : 1] == '>' +      && ptr[dir == BACKWARD ? -1 : 0] == '-') { +    *colp += dir; +    return true; +  } +  return false; +} +  /*   * Find the identifier under or to the right of the cursor.   * "find_type" can have one of three values: @@ -3030,6 +3067,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,    int this_class = 0;    int prev_class;    int prevcol; +  int bn = 0;                       // bracket nesting    /*     * if i == 0: try to find an identifier @@ -3041,71 +3079,62 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,       * 1. skip to start of identifier/string       */      col = startcol; -    if (has_mbyte) { -      while (ptr[col] != NUL) { -        this_class = mb_get_class(ptr + col); -        if (this_class != 0 && (i == 1 || this_class != 1)) -          break; -        col += (*mb_ptr2len)(ptr + col); +    while (ptr[col] != NUL) { +      // Stop at a ']' to evaluate "a[x]". +      if ((find_type & FIND_EVAL) && ptr[col] == ']') { +        break;        } -    } else -      while (ptr[col] != NUL -             && (i == 0 ? !vim_iswordc(ptr[col]) : ascii_iswhite(ptr[col])) -             ) -        ++col; +      this_class = mb_get_class(ptr + col); +      if (this_class != 0 && (i == 1 || this_class != 1)) { +        break; +      } +      col += utfc_ptr2len(ptr + col); +    } +    // When starting on a ']' count it, so that we include the '['. +    bn = ptr[col] == ']';      /*       * 2. Back up to start of identifier/string.       */ -    if (has_mbyte) { -      /* Remember class of character under cursor. */ +    // Remember class of character under cursor. +    if ((find_type & FIND_EVAL) && ptr[col] == ']') { +      this_class = mb_get_class((char_u *)"a"); +    } else {        this_class = mb_get_class(ptr + col); -      while (col > 0 && this_class != 0) { -        prevcol = col - 1 - (*mb_head_off)(ptr, ptr + col - 1); -        prev_class = mb_get_class(ptr + prevcol); -        if (this_class != prev_class -            && (i == 0 -                || prev_class == 0 -                || (find_type & FIND_IDENT)) -            ) -          break; -        col = prevcol; -      } - -      /* If we don't want just any old string, or we've found an -       * identifier, stop searching. */ -      if (this_class > 2) -        this_class = 2; -      if (!(find_type & FIND_STRING) || this_class == 2) +    } +    while (col > 0 && this_class != 0) { +      prevcol = col - 1 - utf_head_off(ptr, ptr + col - 1); +      prev_class = mb_get_class(ptr + prevcol); +      if (this_class != prev_class +          && (i == 0 +              || prev_class == 0 +              || (find_type & FIND_IDENT)) +          && (!(find_type & FIND_EVAL) +              || prevcol == 0 +              || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD))) {          break; -    } else { -      while (col > 0 -             && ((i == 0 -                  ? vim_iswordc(ptr[col - 1]) -                  : (!ascii_iswhite(ptr[col - 1]) -                     && (!(find_type & FIND_IDENT) -                         || !vim_iswordc(ptr[col - 1])))) -                 )) -        --col; +      } +      col = prevcol; +    } -      /* If we don't want just any old string, or we've found an -       * identifier, stop searching. */ -      if (!(find_type & FIND_STRING) || vim_iswordc(ptr[col])) -        break; +    // If we don't want just any old string, or we've found an +    // identifier, stop searching. +    if (this_class > 2) { +      this_class = 2; +    } +    if (!(find_type & FIND_STRING) || this_class == 2) { +      break;      }    } -  if (ptr[col] == NUL || (i == 0 && ( -                            has_mbyte ? this_class != 2 : -                            !vim_iswordc(ptr[col])))) { -    /* -     * didn't find an identifier or string -     */ -    if (find_type & FIND_STRING) +  if (ptr[col] == NUL || (i == 0 && this_class != 2)) { +    // didn't find an identifier or string +    if (find_type & FIND_STRING) {        EMSG(_("E348: No string under cursor")); -    else +    } else {        EMSG(_(e_noident)); +    }      return 0;    }    ptr += col; @@ -3114,21 +3143,20 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,    /*     * 3. Find the end if the identifier/string.     */ +  bn = 0; +  startcol -= col;    col = 0; -  if (has_mbyte) { -    /* Search for point of changing multibyte character class. */ -    this_class = mb_get_class(ptr); -    while (ptr[col] != NUL -           && ((i == 0 ? mb_get_class(ptr + col) == this_class -                : mb_get_class(ptr + col) != 0) -               )) -      col += (*mb_ptr2len)(ptr + col); -  } else -    while ((i == 0 ? vim_iswordc(ptr[col]) -            : (ptr[col] != NUL && !ascii_iswhite(ptr[col]))) -           ) { -      ++col; -    } +  // Search for point of changing multibyte character class. +  this_class = mb_get_class(ptr); +  while (ptr[col] != NUL +         && ((i == 0 +              ? mb_get_class(ptr + col) == this_class +              : mb_get_class(ptr + col) != 0) +             || ((find_type & FIND_EVAL) +                 && col <= (int)startcol +                 && find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) { +    col += utfc_ptr2len(ptr + col); +  }    assert(col >= 0);    return (size_t)col; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 7defde731a..ff02d82ff3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1110,7 +1110,7 @@ int insert_reg(  )  {    int retval = OK; -  int allocated; +  bool allocated;    /*     * It is possible to get into an endless loop by having CTRL-R a in @@ -1187,82 +1187,92 @@ static void stuffescaped(const char *arg, int literally)    }  } -/* - * If "regname" is a special register, return TRUE and store a pointer to its - * value in "argp". - */ -int get_spec_reg( +// If "regname" is a special register, return true and store a pointer to its +// value in "argp". +bool get_spec_reg(      int regname,      char_u **argp, -    int *allocated,         /* return: TRUE when value was allocated */ -    int errmsg                     /* give error message when failing */ +    bool *allocated,        // return: true when value was allocated +    bool errmsg             // give error message when failing  )  {    size_t cnt;    *argp = NULL; -  *allocated = FALSE; +  *allocated = false;    switch (regname) {    case '%':                     /* file name */      if (errmsg)        check_fname();            /* will give emsg if not set */      *argp = curbuf->b_fname; -    return TRUE; +    return true; -  case '#':                     /* alternate file name */ -    *argp = getaltfname(errmsg);                /* may give emsg if not set */ -    return TRUE; +  case '#':                       // alternate file name +    *argp = getaltfname(errmsg);  // may give emsg if not set +    return true;    case '=':                     /* result of expression */      *argp = get_expr_line(); -    *allocated = TRUE; -    return TRUE; +    *allocated = true; +    return true;    case ':':                     /* last command line */      if (last_cmdline == NULL && errmsg)        EMSG(_(e_nolastcmd));      *argp = last_cmdline; -    return TRUE; +    return true;    case '/':                     /* last search-pattern */      if (last_search_pat() == NULL && errmsg)        EMSG(_(e_noprevre));      *argp = last_search_pat(); -    return TRUE; +    return true;    case '.':                     /* last inserted text */      *argp = get_last_insert_save(); -    *allocated = TRUE; -    if (*argp == NULL && errmsg) +    *allocated = true; +    if (*argp == NULL && errmsg) {        EMSG(_(e_noinstext)); -    return TRUE; +    } +    return true; -  case Ctrl_F:                  /* Filename under cursor */ -  case Ctrl_P:                  /* Path under cursor, expand via "path" */ -    if (!errmsg) -      return FALSE; -    *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP -        | (regname == Ctrl_P ? FNAME_EXP : 0), 1L, NULL); -    *allocated = TRUE; -    return TRUE; +  case Ctrl_F:                  // Filename under cursor +  case Ctrl_P:                  // Path under cursor, expand via "path" +    if (!errmsg) { +      return false; +    } +    *argp = file_name_at_cursor( +        FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0), +        1L, NULL); +    *allocated = true; +    return true; -  case Ctrl_W:                  /* word under cursor */ -  case Ctrl_A:                  /* WORD (mnemonic All) under cursor */ -    if (!errmsg) -      return FALSE; +  case Ctrl_W:                  // word under cursor +  case Ctrl_A:                  // WORD (mnemonic All) under cursor +    if (!errmsg) { +      return false; +    }      cnt = find_ident_under_cursor(argp, (regname == Ctrl_W                                           ? (FIND_IDENT|FIND_STRING)                                           : FIND_STRING));      *argp = cnt ? vim_strnsave(*argp, cnt) : NULL; -    *allocated = TRUE; -    return TRUE; +    *allocated = true; +    return true; + +  case Ctrl_L:                  // Line under cursor +    if (!errmsg) { +      return false; +    } + +    *argp = ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false); +    return true;    case '_':                     /* black hole: always empty */      *argp = (char_u *)""; -    return TRUE; +    return true;    } -  return FALSE; +  return false;  }  /// Paste a yank register into the command line. @@ -2652,7 +2662,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)    int lendiff = 0;    pos_T old_pos;    char_u      *insert_string = NULL; -  int allocated = FALSE; +  bool allocated = false;    long cnt;    if (flags & PUT_FIXINDENT) @@ -2748,9 +2758,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)     * For special registers '%' (file name), '#' (alternate file name) and     * ':' (last command line), etc. we have to create a fake yank register.     */ -  if (get_spec_reg(regname, &insert_string, &allocated, TRUE)) { -    if (insert_string == NULL) +  if (get_spec_reg(regname, &insert_string, &allocated, true)) { +    if (insert_string == NULL) {        return; +    }    }    if (!curbuf->terminal) { @@ -4907,10 +4918,11 @@ void *get_reg_contents(int regname, int flags)      return NULL;    char_u *retval; -  int allocated; -  if (get_spec_reg(regname, &retval, &allocated, FALSE)) { -    if (retval == NULL) +  bool allocated; +  if (get_spec_reg(regname, &retval, &allocated, false)) { +    if (retval == NULL) {        return NULL; +    }      if (allocated) {        return get_reg_wrap_one_line(retval, flags);      } diff --git a/src/nvim/search.c b/src/nvim/search.c index 95929f0eb4..7be7dc2187 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -521,7 +521,7 @@ int searchit(                                         buffer without a window! */      buf_T       *buf,      pos_T       *pos, -    int dir, +    Direction dir,      char_u      *pat,      long count,      int options, @@ -570,8 +570,12 @@ int searchit(                 && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count                 && pos->col < MAXCOL - 2) {        // Watch out for the "col" being MAXCOL - 2, used in a closed fold. -      ptr = ml_get_buf(buf, pos->lnum, false) + pos->col; -      start_char_len = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr); +      ptr = ml_get_buf(buf, pos->lnum, false); +      if ((int)STRLEN(ptr) <= pos->col) { +        start_char_len = 1; +      } else { +        start_char_len = utfc_ptr2len(ptr + pos->col); +      }      } else {        start_char_len = 1;      } @@ -1849,7 +1853,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)        } else {    /* Searching backwards */          /*           * A comment may contain / * or / /, it may also start or end -         * with / * /.	Ignore a / * after / /. +         * with / * /. Ignore a / * after / / and after *.           */          if (pos.col == 0)            continue; @@ -1874,6 +1878,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)            }          } else if (  linep[pos.col - 1] == '/'                     && linep[pos.col] == '*' +                   && (pos.col == 1 || linep[pos.col - 2] != '*')                     && (int)pos.col < comment_col) {            count++;            match_pos = pos; @@ -3300,7 +3305,7 @@ int  current_tagblock(      oparg_T *oap,      long count_arg, -    int include                    /* TRUE == include white space */ +    bool include                  // true == include white space  )  {    long count = count_arg; @@ -3314,7 +3319,7 @@ current_tagblock(    char_u      *cp;    int len;    int r; -  int do_include = include; +  bool do_include = include;    bool save_p_ws = p_ws;    int retval = FAIL;    int is_inclusive = true; @@ -3440,10 +3445,12 @@ again:        }      curwin->w_cursor = end_pos; -    /* If we now have the same text as before reset "do_include" and try -     * again. */ -    if (equalpos(start_pos, old_start) && equalpos(end_pos, old_end)) { -      do_include = TRUE; +    // If we are in Visual mode and now have the same text as before set +    // "do_include" and try again. +    if (VIsual_active +        && equalpos(start_pos, old_start) +        && equalpos(end_pos, old_end)) { +      do_include = true;        curwin->w_cursor = old_start;        count = count_arg;        goto again; @@ -3952,8 +3959,10 @@ current_search(    if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))      dec_cursor(); -  pos_T orig_pos;               /* position of the cursor at beginning */ -  pos_T pos;                    /* position after the pattern */ +  pos_T orig_pos;               // position of the cursor at beginning +  pos_T first_match;            // position of first match +  pos_T pos;                    // position after the pattern +  int result;                   // result of various function calls    if (VIsual_active) {      orig_pos = pos = curwin->w_cursor; @@ -3968,8 +3977,9 @@ current_search(      orig_pos = pos = curwin->w_cursor;    } -  // Is the pattern is zero-width? -  int one_char = is_one_char(spats[last_idx].pat, true, &curwin->w_cursor); +  // Is the pattern is zero-width?, this time, don't care about the direction +  int one_char = is_one_char(spats[last_idx].pat, true, &curwin->w_cursor, +                             FORWARD);    if (one_char == -1) {      p_ws = old_p_ws;      return FAIL;      /* pattern not found */ @@ -3987,9 +3997,9 @@ current_search(      if (!dir && !one_char)        flags = SEARCH_END; -    int result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), -        spats[last_idx].pat, i ? count : 1, -        SEARCH_KEEP | flags, RE_SEARCH, 0, NULL); +    result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), +                      spats[last_idx].pat, i ? count : 1, +                      SEARCH_KEEP | flags, RE_SEARCH, 0, NULL);      /* First search may fail, but then start searching from the       * beginning of the file (cursor might be on the search match) @@ -4011,15 +4021,19 @@ current_search(              ml_get(curwin->w_buffer->b_ml.ml_line_count));        }      } +    if (i == 0) { +      first_match = pos; +    }      p_ws = old_p_ws;    } -  int flags = forward ? SEARCH_END : 0; +  const int flags = forward ? SEARCH_END : SEARCH_START;    pos_T start_pos = pos; +  const Direction direction = forward ? FORWARD : BACKWARD;    // Check again from the current cursor position,    // since the next match might actually be only one char wide -  one_char = is_one_char(spats[last_idx].pat, false, &pos); +  one_char = is_one_char(spats[last_idx].pat, false, &pos, direction);    if (one_char < 0) {      // search failed, abort      return FAIL; @@ -4027,9 +4041,26 @@ current_search(    /* move to match, except for zero-width matches, in which case, we are     * already on the next match */ -  if (!one_char) -    searchit(curwin, curbuf, &pos, (forward ? FORWARD : BACKWARD), -        spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, 0, NULL); +  if (!one_char) { +    p_ws = false; +    for (int i = 0; i < 2; i++) { +      result = searchit(curwin, curbuf, &pos, direction, +                        spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, +                        0, NULL); +      // Search successfull, break out from the loop +      if (result) { +        break; +      } +      // search failed, try again from the last search position match +      pos = first_match; +    } +  } + +  p_ws = old_p_ws; +  // not found +  if (!result) { +    return FAIL; +  }    if (!VIsual_active)      VIsual = start_pos; @@ -4063,8 +4094,10 @@ current_search(  /// Check if the pattern is one character long or zero-width.  /// If move is true, check from the beginning of the buffer,  /// else from position "cur". +/// "direction" is FORWARD or BACKWARD.  /// Returns TRUE, FALSE or -1 for failure. -static int is_one_char(char_u *pattern, bool move, pos_T *cur) +static int is_one_char(char_u *pattern, bool move, pos_T *cur, +                       Direction direction)  {    regmmatch_T regmatch;    int nmatched = 0; @@ -4091,7 +4124,7 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur)      // accept a match at the cursor position      flag = SEARCH_START;    } -  if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1, +  if (searchit(curwin, curbuf, &pos, direction, pattern, 1,                 SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) {      // Zero-width pattern should match somewhere, then we can check if      // start and end are in the same position. @@ -4103,7 +4136,9 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur)        if (!nmatched) {          break;        } -    } while (regmatch.startpos[0].col < pos.col); +    } while (direction == FORWARD +             ? regmatch.startpos[0].col < pos.col +             : regmatch.startpos[0].col > pos.col);      if (!called_emsg) {        result = (nmatched != 0 @@ -4603,7 +4638,7 @@ search_line:            if (depth == -1) {              // match in current file              if (l_g_do_tagpreview != 0) { -              if (!GETFILE_SUCCESS(getfile(0, curwin_save->w_buffer->b_fname, +              if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL,                                             NULL, true, lnum, false))) {                  break;    // failed to jump to file                } @@ -4611,6 +4646,7 @@ search_line:                setpcmark();              }              curwin->w_cursor.lnum = lnum; +            check_cursor();            } else {              if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true,                                           files[depth].lnum, false))) { diff --git a/src/nvim/search.h b/src/nvim/search.h index cb50742990..cb094aab8c 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -4,7 +4,7 @@  #include <stdbool.h>  #include <stdint.h> -#include "nvim/types.h" +#include "nvim/vim.h"  #include "nvim/buffer_defs.h"  #include "nvim/eval/typval.h"  #include "nvim/normal.h" diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 1e3dc04049..5057c8eb0a 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -88,6 +88,7 @@ NEW_TESTS ?= \  	    test_normal.res \  	    test_number.res \  	    test_options.res \ +	    test_preview.res \  	    test_profile.res \  	    test_put.res \  	    test_python2.res \ diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 5a43838218..c302948ba3 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -137,6 +137,11 @@ func Test_getcompletion()    let l = getcompletion('v:notexists', 'var')    call assert_equal([], l) +  args a.c b.c +  let l = getcompletion('', 'arglist') +  call assert_equal(['a.c', 'b.c'], l) +  %argdelete +    let l = getcompletion('', 'augroup')    call assert_true(index(l, 'END') >= 0)    let l = getcompletion('blahblah', 'augroup') @@ -222,6 +227,11 @@ func Test_getcompletion()    let l = getcompletion('not', 'messages')    call assert_equal([], l) +  let l = getcompletion('', 'mapclear') +  call assert_true(index(l, '<buffer>') >= 0) +  let l = getcompletion('not', 'mapclear') +  call assert_equal([], l) +    if has('cscope')      let l = getcompletion('', 'cscope')      let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show'] @@ -311,6 +321,9 @@ func Test_paste_in_cmdline()    call feedkeys("ft:aaa \<C-R>\<C-F> bbb\<C-B>\"\<CR>", 'tx')    call assert_equal('"aaa /tmp/some bbb', @:) +  call feedkeys(":aaa \<C-R>\<C-L> bbb\<C-B>\"\<CR>", 'tx') +  call assert_equal('"aaa '.getline(1).' bbb', @:) +    set incsearch    call feedkeys("fy:aaa veryl\<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')    call assert_equal('"aaa verylongword bbb', @:) @@ -375,6 +388,27 @@ func Test_cmdline_complete_user_cmd()    delcommand Foo  endfunc +func Test_cmdline_write_alternatefile() +  new +  call setline('.', ['one', 'two']) +  f foo.txt +  new +  f #-A +  call assert_equal('foo.txt-A', expand('%')) +  f #<-B.txt +  call assert_equal('foo-B.txt', expand('%')) +  f %< +  call assert_equal('foo-B', expand('%')) +  new +  call assert_fails('f #<', 'E95') +  bw! +  f foo-B.txt +  f %<-A +  call assert_equal('foo-B-A', expand('%')) +  bw! +  bw! +endfunc +  " using a leading backslash here  set cpo+=C @@ -430,6 +464,22 @@ func Test_getcmdtype()    cunmap <F6>  endfunc +func Test_getcmdwintype() +  call feedkeys("q/:let a = getcmdwintype()\<CR>:q\<CR>", 'x!') +  call assert_equal('/', a) + +  call feedkeys("q?:let a = getcmdwintype()\<CR>:q\<CR>", 'x!') +  call assert_equal('?', a) + +  call feedkeys("q::let a = getcmdwintype()\<CR>:q\<CR>", 'x!') +  call assert_equal(':', a) + +  call feedkeys(":\<C-F>:let a = getcmdwintype()\<CR>:q\<CR>", 'x!') +  call assert_equal(':', a) + +  call assert_equal('', getcmdwintype()) +endfunc +  func Test_verbosefile()    set verbosefile=Xlog    echomsg 'foo' @@ -440,4 +490,25 @@ func Test_verbosefile()    call delete('Xlog')  endfunc +func Test_setcmdpos() +  func InsertTextAtPos(text, pos) +    call assert_equal(0, setcmdpos(a:pos)) +    return a:text +  endfunc + +  " setcmdpos() with position in the middle of the command line. +  call feedkeys(":\"12\<C-R>=InsertTextAtPos('a', 3)\<CR>b\<CR>", 'xt') +  call assert_equal('"1ab2', @:) + +  call feedkeys(":\"12\<C-R>\<C-R>=InsertTextAtPos('a', 3)\<CR>b\<CR>", 'xt') +  call assert_equal('"1b2a', @:) + +  " setcmdpos() with position beyond the end of the command line. +  call feedkeys(":\"12\<C-B>\<C-R>=InsertTextAtPos('a', 10)\<CR>b\<CR>", 'xt') +  call assert_equal('"12ab', @:) + +  " setcmdpos() returns 1 when not editing the command line. +  call assert_equal(1, setcmdpos(3)) +endfunc +  set cpo& diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim index b2a2937d88..405425a42b 100644 --- a/src/nvim/testdir/test_gn.vim +++ b/src/nvim/testdir/test_gn.vim @@ -5,51 +5,51 @@ func Test_gn_command()    noautocmd new    " replace a single char by itsself quoted:    call setline('.', 'abc x def x ghi x jkl') -  let @/='x' +  let @/ = 'x'    exe "norm! cgn'x'\<esc>.."    call assert_equal("abc 'x' def 'x' ghi 'x' jkl", getline('.'))    sil! %d_    " simple search match    call setline('.', 'foobar') -  let @/='foobar' +  let @/ = 'foobar'    exe "norm! gncsearchmatch"    call assert_equal('searchmatch', getline('.'))    sil! %d _    " replace a multi-line match    call setline('.', ['', 'one', 'two']) -  let @/='one\_s*two\_s' +  let @/ = 'one\_s*two\_s'    exe "norm! gnceins\<CR>zwei"    call assert_equal(['','eins','zwei'], getline(1,'$'))    sil! %d _    " test count argument    call setline('.', ['', 'abcdx | abcdx | abcdx']) -  let @/='[a]bcdx' +  let @/ = '[a]bcdx'    exe "norm! 2gnd"    call assert_equal(['','abcdx |  | abcdx'], getline(1,'$'))    sil! %d _    " join lines    call setline('.', ['join ', 'lines']) -  let @/='$' +  let @/ = '$'    exe "norm! 0gnd"    call assert_equal(['join lines'], getline(1,'$'))    sil! %d _    " zero-width match    call setline('.', ['', 'zero width pattern']) -  let @/='\>\zs' +  let @/ = '\>\zs'    exe "norm! 0gnd"    call assert_equal(['', 'zerowidth pattern'], getline(1,'$'))    sil! %d _    " delete first and last chars    call setline('.', ['delete first and last chars']) -  let @/='^' +  let @/ = '^'    exe "norm! 0gnd$" -  let @/='\zs' +  let @/ = '\zs'    exe "norm! gnd"    call assert_equal(['elete first and last char'], getline(1,'$'))    sil! %d _ @@ -62,14 +62,14 @@ func Test_gn_command()    " backwards search    call setline('.', ['my very excellent mother just served us nachos']) -  let @/='mother' +  let @/ = 'mother'    exe "norm! $cgNmongoose"    call assert_equal(['my very excellent mongoose just served us nachos'], getline(1,'$'))    sil! %d _    " search for single char    call setline('.', ['','for (i=0; i<=10; i++)']) -  let @/='i' +  let @/ = 'i'    exe "norm! cgnj"    call assert_equal(['','for (j=0; i<=10; i++)'], getline(1,'$'))    sil! %d _ @@ -77,28 +77,28 @@ func Test_gn_command()    " search hex char    call setline('.', ['','Y'])    set noignorecase -  let @/='\%x59' +  let @/ = '\%x59'    exe "norm! gnd"    call assert_equal(['',''], getline(1,'$'))    sil! %d _    " test repeating gdn    call setline('.', ['', '1', 'Johnny', '2', 'Johnny', '3']) -  let @/='Johnny' +  let @/ = 'Johnny'    exe "norm! dgn."    call assert_equal(['','1', '', '2', '', '3'], getline(1,'$'))    sil! %d _    " test repeating gUgn    call setline('.', ['', '1', 'Depp', '2', 'Depp', '3']) -  let @/='Depp' +  let @/ = 'Depp'    exe "norm! gUgn."    call assert_equal(['', '1', 'DEPP', '2', 'DEPP', '3'], getline(1,'$'))    sil! %d _    " test using look-ahead assertions    call setline('.', ['a:10', '', 'a:1', '', 'a:20']) -  let @/='a:0\@!\zs\d\+' +  let @/ = 'a:0\@!\zs\d\+'    exe "norm! 2nygno\<esc>p"    call assert_equal(['a:10', '', 'a:1', '1', '', 'a:20'], getline(1,'$'))    sil! %d _ @@ -111,6 +111,24 @@ func Test_gn_command()    call assert_equal(['foo  baz'], getline(1,'$'))    sil! %d_ +  " search upwards with nowrapscan set +  call setline('.', ['foo', 'bar', 'foo', 'baz']) +  set nowrapscan +  let @/ = 'foo' +  $ +  norm! dgN +  call assert_equal(['foo', 'bar', '', 'baz'], getline(1,'$')) +  sil! %d_ + +  " search using the \zs atom +  call setline(1, [' nnoremap', '' , 'nnoremap']) +  set wrapscan&vim +  let @/ = '\_s\zsnnoremap' +  $ +  norm! cgnmatch +  call assert_equal([' nnoremap', '', 'match'], getline(1,'$')) +  sil! %d_ +    set wrapscan&vim    set belloff&vim  endfu diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 86b3e352bd..3ff1e98caf 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -392,10 +392,31 @@ func! Test_normal10_expand()    call setline(1, ['1', 'ifooar,,cbar'])    2    norm! $ -  let a=expand('<cword>') -  let b=expand('<cWORD>') -  call assert_equal('cbar', a) -  call assert_equal('ifooar,,cbar', b) +  call assert_equal('cbar', expand('<cword>')) +  call assert_equal('ifooar,,cbar', expand('<cWORD>')) + +  call setline(1, ['prx = list[idx];']) +  1 +  let expected = ['', 'prx', 'prx', 'prx', +	\ 'list', 'list', 'list', 'list', 'list', 'list', 'list', +	\ 'idx', 'idx', 'idx', 'idx', +	\ 'list[idx]', +	\ '];', +	\ ] +  for i in range(1, 16) +    exe 'norm ' . i . '|' +    call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i) +  endfor + +  if executable('echo') +    " Test expand(`...`) i.e. backticks command expansion. +    " MS-Windows has a trailing space. +    call assert_match('^abcde *$', expand('`echo abcde`')) +  endif + +  " Test expand(`=...`) i.e. backticks expression expansion +  call assert_equal('5', expand('`=2+3`')) +    " clean up    bw!  endfunc @@ -1536,12 +1557,12 @@ fun! Test_normal29_brace()    \ 'the ''{'' flag is in ''cpoptions'' then ''{'' in the first column is used as a',    \ 'paragraph boundary |posix|.',    \ '{', -  \ 'This is no paragaraph', +  \ 'This is no paragraph',    \ 'unless the ''{'' is set',    \ 'in ''cpoptions''',    \ '}',    \ '.IP', -  \ 'The nroff macros IP seperates a paragraph', +  \ 'The nroff macros IP separates a paragraph',    \ 'That means, it must be a ''.''',    \ 'followed by IP',    \ '.LPIt does not matter, if afterwards some', @@ -1556,7 +1577,7 @@ fun! Test_normal29_brace()    1    norm! 0d2}    call assert_equal(['.IP', -    \  'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', 'followed by IP', +    \  'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''', 'followed by IP',      \ '.LPIt does not matter, if afterwards some', 'more characters follow.', '.SHAlso section boundaries from the nroff',      \  'macros terminate a paragraph. That means', 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))    norm! 0d} @@ -1576,21 +1597,21 @@ fun! Test_normal29_brace()    " set cpo+={    " 1    " norm! 0d2} -  " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', -  "   \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', +  " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', +  "   \ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',    "   \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',    "   \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',    "   \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))    " $    " norm! d} -  " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', -  "   \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', +  " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', +  "   \ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',    "   \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',    "   \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',    "   \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))    " norm! gg}    " norm! d5} -  " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$')) +  " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$'))    " clean up    set cpo-={ diff --git a/src/nvim/testdir/test_preview.vim b/src/nvim/testdir/test_preview.vim new file mode 100644 index 0000000000..91923fb1e9 --- /dev/null +++ b/src/nvim/testdir/test_preview.vim @@ -0,0 +1,13 @@ +" Tests for the preview window + +func Test_Psearch() +  " this used to cause ml_get errors +  help +  let wincount = winnr('$') +  0f +  ps. +  call assert_equal(wincount + 1, winnr('$')) +  pclose +  call assert_equal(wincount, winnr('$')) +  bwipe +endfunc diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index 684f197f5f..52c0c3698e 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -121,6 +121,23 @@ func Test_string_html_objects()    enew!  endfunc +func Test_empty_html_tag() +  new +  call setline(1, '<div></div>') +  normal 0citxxx +  call assert_equal('<div>xxx</div>', getline(1)) + +  call setline(1, '<div></div>') +  normal 0f<cityyy +  call assert_equal('<div>yyy</div>', getline(1)) + +  call setline(1, '<div></div>') +  normal 0f<vitsaaa +  call assert_equal('aaa', getline(1)) + +  bwipe! +endfunc +  " Tests for match() and matchstr()  func Test_match()    call assert_equal("b", matchstr("abcd", ".", 0, 2)) @@ -152,3 +169,16 @@ func Test_match()    call assert_equal(3 , match('abc', '\zs', 3, 1))    call assert_equal(-1, match('abc', '\zs', 4, 1))  endfunc + +" This was causing an illegal memory access +func Test_inner_tag() +  new +  norm ixxx +  call feedkeys("v", 'xt') +  insert +x +x +. +  norm it +  q! +endfunc diff --git a/src/nvim/vim.h b/src/nvim/vim.h index bddf092789..767936ecee 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -155,6 +155,8 @@ enum {    EXPAND_USER_ADDR_TYPE,    EXPAND_PACKADD,    EXPAND_MESSAGES, +  EXPAND_MAPCLEAR, +  EXPAND_ARGLIST,    EXPAND_CHECKHEALTH,  }; diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index 77d025dcc7..acb76e382d 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -656,7 +656,6 @@ describe('mappings with <Cmd>', function()    end)    it('works in cmdline mode', function() -    cmdmap('<F2>', 'call setcmdpos(2)')      feed(':text<F3>')      eq('c', eval('m'))      -- didn't leave cmdline mode @@ -768,5 +767,32 @@ describe('mappings with <Cmd>', function()    end) +  it("doesn't crash when invoking cmdline mode recursively #8859", function() +    cmdmap('<F2>', 'norm! :foo') +    feed(':bar') +    screen:expect([[ +      some short lines                                                 | +      of test text                                                     | +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      :bar^                                                             | +    ]]) + +    feed('<f2>x') +    screen:expect([[ +      some short lines                                                 | +      of test text                                                     | +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      {1:~                                                                }| +      :barx^                                                            | +    ]]) +  end) +  end) diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 1cede8a7d7..061904c42f 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -4754,4 +4754,21 @@ describe('cindent', function()        	4        /* end of define */]=])    end) + +  it('* immediately follows comment / vim-patch 8.0.1291', function() +    insert_([=[ +      { +        a = second/*bug*/*line; +      }]=]) + +    feed_command('set cin cino&') +    feed_command('/a = second') +    feed('ox') + +    expect([=[ +      { +        a = second/*bug*/*line; +        x +      }]=]) +  end)  end)  | 
