diff options
Diffstat (limited to 'src')
76 files changed, 2649 insertions, 326 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index de4af8e77b..360993de68 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -222,31 +222,17 @@ function(get_preproc_output varname iname)    endif()  endfunction() -# Handle generating version from Git. -set(use_git_version 0) -if(NVIM_VERSION_MEDIUM) -  message(STATUS "NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}") -elseif(EXISTS ${PROJECT_SOURCE_DIR}/.git) -  find_program(GIT_EXECUTABLE git) -  if(GIT_EXECUTABLE) -    message(STATUS "Using NVIM_VERSION_MEDIUM from Git") -    set(use_git_version 1) -  else() -    message(STATUS "Skipping version-string generation (cannot find git)") -  endif() -endif() -if(use_git_version) -  # Create a update_version_stamp target to update the version during build. -  file(RELATIVE_PATH relbuild "${PROJECT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") -  add_custom_target(update_version_stamp ALL -    COMMAND ${LUA_PRG} scripts/update_version_stamp.lua -      ${relbuild}/cmake.config/auto/versiondef_git.h -      "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}" -    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -    BYPRODUCTS ${CMAKE_BINARY_DIR}/cmake.config/auto/versiondef_git.h) -else() -  file(WRITE ${CMAKE_BINARY_DIR}/cmake.config/auto/versiondef_git.h "") -endif() +set(NVIM_VERSION_GIT_H ${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef_git.h) +add_custom_target(update_version_stamp +  COMMAND ${CMAKE_COMMAND} +    -DNVIM_VERSION_MAJOR=${NVIM_VERSION_MAJOR} +    -DNVIM_VERSION_MINOR=${NVIM_VERSION_MINOR} +    -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH} +    -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE} +    -DOUTPUT=${NVIM_VERSION_GIT_H} +    -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake +  WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +  BYPRODUCTS ${NVIM_VERSION_GIT_H})  # NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers  # NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources @@ -280,9 +266,9 @@ foreach(sfile ${NVIM_SOURCES}    get_preproc_output(PREPROC_OUTPUT ${gf_i})    set(depends "${HEADER_GENERATOR}" "${sfile}") -  if(use_git_version AND "${f}" STREQUAL "version.c") +  if("${f}" STREQUAL "version.c")      # Ensure auto/versiondef_git.h exists after "make clean". -    list(APPEND depends update_version_stamp) +    list(APPEND depends "${NVIM_VERSION_GIT_H}")    endif()    add_custom_command(      OUTPUT "${gf_c_h}" "${gf_h_h}" diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7e1eae9632..f937450107 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -94,11 +94,19 @@ static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use")  // Number of times free_buffer() was called.  static int buf_free_count = 0; +static int top_file_num = 1;            ///< highest file number +  typedef enum {    kBffClearWinInfo = 1,    kBffInitChangedtick = 2,  } BufFreeFlags; +/// @return  the highest possible buffer number +int get_highest_fnum(void) +{ +  return top_file_num - 1; +} +  /// Read data from buffer for retrying.  ///  /// @param read_stdin  read file from stdin, otherwise fifo @@ -443,6 +451,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i      return false;    } +  // check no autocommands closed the window    if (win != NULL  // Avoid bogus clang warning.        && win_valid_any_tab(win)) {      // Set b_last_cursor when closing the last window for the buffer. @@ -1643,8 +1652,6 @@ void no_write_message_nobang(const buf_T *const buf)  // functions for dealing with the buffer list  // -static int top_file_num = 1;            ///< highest file number -  /// Initialize b:changedtick and changedtick_val attribute  ///  /// @param[out]  buf  Buffer to initialize for. @@ -3136,18 +3143,16 @@ void maketitle(void)      if (*p_titlestring != NUL) {        if (stl_syntax & STL_IN_TITLE) {          int use_sandbox = false; -        int save_called_emsg = called_emsg; +        const int called_emsg_before = called_emsg;          use_sandbox = was_set_insecurely(curwin, "titlestring", 0); -        called_emsg = false;          build_stl_str_hl(curwin, buf, sizeof(buf),                           (char *)p_titlestring, use_sandbox,                           0, maxlen, NULL, NULL);          title_str = buf; -        if (called_emsg) { +        if (called_emsg > called_emsg_before) {            set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);          } -        called_emsg |= save_called_emsg;        } else {          title_str = (char *)p_titlestring;        } @@ -3252,17 +3257,15 @@ void maketitle(void)      if (*p_iconstring != NUL) {        if (stl_syntax & STL_IN_ICON) {          int use_sandbox = false; -        int save_called_emsg = called_emsg; +        const int called_emsg_before = called_emsg;          use_sandbox = was_set_insecurely(curwin, "iconstring", 0); -        called_emsg = false;          build_stl_str_hl(curwin, icon_str, sizeof(buf),                           (char *)p_iconstring, use_sandbox,                           0, 0, NULL, NULL); -        if (called_emsg) { +        if (called_emsg > called_emsg_before) {            set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);          } -        called_emsg |= save_called_emsg;        } else {          icon_str = (char *)p_iconstring;        } @@ -5268,9 +5271,9 @@ bool bt_terminal(const buf_T *const buf)    return buf != NULL && buf->b_p_bt[0] == 't';  } -/// @return  true if "buf" is a "nofile", "acwrite", "terminal" or "prompt" / +/// @return  true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"  ///          buffer.  This means the buffer name is not a file name. -bool bt_nofile(const buf_T *const buf) +bool bt_nofilename(const buf_T *const buf)    FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT  {    return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f') @@ -5279,6 +5282,13 @@ bool bt_nofile(const buf_T *const buf)                           || buf->b_p_bt[0] == 'p');  } +/// @return  true if "buf" has 'buftype' set to "nofile". +bool bt_nofile(const buf_T *const buf) +  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ +  return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f'; +} +  /// @return  true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"  ///          buffer.  bool bt_dontwrite(const buf_T *const buf) @@ -5330,7 +5340,7 @@ char *buf_spname(buf_T *buf)    }    // There is no _file_ when 'buftype' is "nofile", b_sfname    // contains the name as specified by the user. -  if (bt_nofile(buf)) { +  if (bt_nofilename(buf)) {      if (buf->b_fname != NULL) {        return buf->b_fname;      } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c7173c2078..096fcba981 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6554,7 +6554,8 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const    const char *prompt = "";    const char *defstr = ""; -  const char *cancelreturn = NULL; +  typval_T *cancelreturn = NULL; +  typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;    const char *xp_name = NULL;    Callback input_callback = { .type = kCallbackNone };    char prompt_buf[NUMBUFLEN]; @@ -6576,13 +6577,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const      if (defstr == NULL) {        return;      } -    cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"), -                                              cancelreturn_buf, def); -    if (cancelreturn == NULL) {  // error -      return; -    } -    if (*cancelreturn == NUL) { -      cancelreturn = NULL; +    dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); +    if (cancelreturn_di != NULL) { +      cancelreturn = &cancelreturn_di->di_tv;      }      xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),                                           xp_name_buf, def); @@ -6606,15 +6603,16 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const          return;        }        if (argvars[2].v_type != VAR_UNKNOWN) { -        const char *const arg2 = tv_get_string_buf_chk(&argvars[2], -                                                       cancelreturn_buf); -        if (arg2 == NULL) { +        const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); +        if (strarg2 == NULL) {            return;          }          if (inputdialog) { -          cancelreturn = arg2; +          cancelreturn_strarg2.v_type = VAR_STRING; +          cancelreturn_strarg2.vval.v_string = (char *)strarg2; +          cancelreturn = &cancelreturn_strarg2;          } else { -          xp_name = arg2; +          xp_name = strarg2;          }        }      } @@ -6662,7 +6660,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const    callback_free(&input_callback);    if (rettv->vval.v_string == NULL && cancelreturn != NULL) { -    rettv->vval.v_string = xstrdup(cancelreturn); +    tv_copy(cancelreturn, rettv);    }    xfree(xp_arg); @@ -7296,7 +7294,7 @@ void timer_due_cb(TimeWatcher *tw, void *data)  {    timer_T *timer = (timer_T *)data;    int save_did_emsg = did_emsg; -  int save_called_emsg = called_emsg; +  const int called_emsg_before = called_emsg;    const bool save_ex_pressedreturn = get_pressedreturn();    if (timer->stopped || timer->paused) { @@ -7313,19 +7311,17 @@ void timer_due_cb(TimeWatcher *tw, void *data)    argv[0].v_type = VAR_NUMBER;    argv[0].vval.v_number = timer->timer_id;    typval_T rettv = TV_INITIAL_VALUE; -  called_emsg = false;    callback_call(&timer->callback, 1, argv, &rettv);    // Handle error message -  if (called_emsg && did_emsg) { +  if (called_emsg > called_emsg_before && did_emsg) {      timer->emsg_count++;      if (current_exception != NULL) {        discard_current_exception();      }    }    did_emsg = save_did_emsg; -  called_emsg = save_called_emsg;    set_pressedreturn(save_ex_pressedreturn);    if (timer->emsg_count >= 3) { diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6466e06010..f231146d7c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -482,7 +482,7 @@ static buf_T *find_buffer(typval_T *avar)         * buffer, these don't use the full path. */        FOR_ALL_BUFFERS(bp) {          if (bp->b_fname != NULL -            && (path_with_url(bp->b_fname) || bt_nofile(bp)) +            && (path_with_url(bp->b_fname) || bt_nofilename(bp))              && STRCMP(bp->b_fname, avar->vval.v_string) == 0) {            buf = bp;            break; @@ -3902,15 +3902,14 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)    typval_T argv = TV_INITIAL_VALUE;    typval_T exprval = TV_INITIAL_VALUE;    bool error = false; -  int save_called_emsg = called_emsg; -  called_emsg = false; +  const int called_emsg_before = called_emsg;    LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout,                              eval_expr_typval(&expr, &argv, 0, &exprval) != OK                              || tv_get_number_chk(&exprval, &error) -                            || called_emsg || error || got_int); +                            || called_emsg > called_emsg_before || error || got_int); -  if (called_emsg || error) { +  if (called_emsg > called_emsg_before || error) {      rettv->vval.v_number = -3;    } else if (got_int) {      got_int = false; @@ -3920,8 +3919,6 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)      rettv->vval.v_number = 0;    } -  called_emsg = save_called_emsg; -    // Stop dummy timer    time_watcher_stop(tw);    time_watcher_close(tw, dummy_timer_close_cb); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1955a0b3d0..28e1893b31 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1980,17 +1980,14 @@ theend:  /// @return  OK if it's OK, FAIL if it is not.  int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int other)  { -  /* -   * write to other file or b_flags set or not writing the whole file: -   * overwriting only allowed with '!' -   */ +  // Write to another file or b_flags set or not writing the whole file: +  // overwriting only allowed with '!'    if ((other         || (buf->b_flags & BF_NOTEDITED)         || ((buf->b_flags & BF_NEW)             && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)         || (buf->b_flags & BF_READERR))        && !p_wa -      && !bt_nofile(buf)        && os_path_exists((char_u *)ffname)) {      if (!eap->forceit && !eap->append) {  #ifdef UNIX @@ -3874,6 +3871,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T                  curwin->w_cursor.col = 0;                }                getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); +              curwin->w_cursor.col = regmatch.startpos[0].col;                if (subflags.do_number || curwin->w_p_nu) {                  int numw = number_width(curwin) + 1;                  sc += numw; @@ -3883,7 +3881,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T                prompt = xmallocz((size_t)ec + 1);                memset(prompt, ' ', (size_t)sc);                memset(prompt + sc, '^', (size_t)(ec - sc) + 1); -              resp = getcmdline_prompt((char)(-1), prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE); +              resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);                msg_putchar('\n');                xfree(prompt);                if (resp != NULL) { @@ -4861,8 +4859,7 @@ void ex_help(exarg_T *eap)     * Re-use an existing help window or open a new one.     * Always open a new one for ":tab help".     */ -  if (!bt_help(curwin->w_buffer) -      || cmdmod.cmod_tab != 0) { +  if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {      if (cmdmod.cmod_tab != 0) {        wp = NULL;      } else { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 897928abec..e57dc5d13f 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1018,8 +1018,7 @@ void check_arg_idx(win_T *win)      // We are editing the current entry in the argument list.      // Set "arg_had_last" if it's also the last one      win->w_arg_idx_invalid = false; -    if (win->w_arg_idx == WARGCOUNT(win) - 1 -        && win->w_alist == &global_alist) { +    if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {        arg_had_last = true;      }    } @@ -1150,8 +1149,7 @@ void do_argfile(exarg_T *eap, int argn)      }      curwin->w_arg_idx = argn; -    if (argn == ARGCOUNT - 1 -        && curwin->w_alist == &global_alist) { +    if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {        arg_had_last = true;      } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2899e17039..4e6846bf21 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -227,6 +227,8 @@ void do_exmode(void)          emsg(_(e_emptybuf));        } else {          if (ex_pressedreturn) { +          // Make sure the message overwrites the right line and isn't throttled. +          msg_scroll_flush();            // go up one line, to overwrite the ":<CR>" line, so the            // output doesn't contain empty lines.            msg_row = prev_msg_row; @@ -908,6 +910,15 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)    msg_list = saved_msg_list; +  // Cleanup if "cs_emsg_silent_list" remains. +  if (cstack.cs_emsg_silent_list != NULL) { +    eslist_T *elem, *temp; +    for (elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) { +      temp = elem->next; +      xfree(elem); +    } +  } +    /*     * If there was too much output to fit on the command line, ask the user to     * hit return before redrawing the screen. With the ":global" command we do @@ -4605,8 +4616,9 @@ char *invalid_range(exarg_T *eap)        }        break;      case ADDR_BUFFERS: -      if (eap->line1 < firstbuf->b_fnum -          || eap->line2 > lastbuf->b_fnum) { +      // Only a boundary check, not whether the buffers actually +      // exist. +      if (eap->line1 < 1 || eap->line2 > get_highest_fnum()) {          return _(e_invrange);        }        break; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a5783f4ced..92c9d83045 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2646,7 +2646,7 @@ char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_  /// @param[in]  highlight_callback  Callback used for highlighting user input.  ///  /// @return [allocated] Command line or NULL. -char *getcmdline_prompt(const char firstc, const char *const prompt, const int attr, +char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr,                          const int xp_context, const char *const xp_arg,                          const Callback highlight_callback)    FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index a7f4080b22..6ca6da9cd0 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -187,7 +187,7 @@ static int ses_do_win(win_T *wp)    }    if (wp->w_buffer->b_fname == NULL        // When 'buftype' is "nofile" can't restore the window contents. -      || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) { +      || (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {      return ssop_flags & SSOP_BLANK;    }    if (bt_help(wp->w_buffer)) { @@ -363,7 +363,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr          return FAIL;        }      } else if (wp->w_buffer->b_ffname != NULL -               && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)) { +               && (!bt_nofilename(wp->w_buffer) || wp->w_buffer->terminal)) {        // Load the file.        // Editing a file in this buffer: use ":edit file". @@ -706,7 +706,7 @@ static int makeopens(FILE *fd, char_u *dirnow)        if (ses_do_win(wp)            && wp->w_buffer->b_ffname != NULL            && !bt_help(wp->w_buffer) -          && !bt_nofile(wp->w_buffer)) { +          && !bt_nofilename(wp->w_buffer)) {          if (need_tabnext && put_line(fd, "tabnext") == FAIL) {            return FAIL;          } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index febde53ce2..6782465ef1 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2286,7 +2286,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en        && reset_changed        && whole        && buf == curbuf -      && !bt_nofile(buf) +      && !bt_nofilename(buf)        && !filtering        && (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)        && vim_strchr(p_cpo, CPO_FNAMEW) != NULL) { @@ -2360,22 +2360,22 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en      if (append) {        if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD, -                                           sfname, sfname, FALSE, curbuf, eap))) { -        if (overwriting && bt_nofile(curbuf)) { -          nofile_err = TRUE; +                                           sfname, sfname, false, curbuf, eap))) { +        if (overwriting && bt_nofilename(curbuf)) { +          nofile_err = true;          } else {            apply_autocmds_exarg(EVENT_FILEAPPENDPRE, -                               sfname, sfname, FALSE, curbuf, eap); +                               sfname, sfname, false, curbuf, eap);          }        }      } else if (filtering) {        apply_autocmds_exarg(EVENT_FILTERWRITEPRE, -                           NULL, sfname, FALSE, curbuf, eap); +                           NULL, sfname, false, curbuf, eap);      } else if (reset_changed && whole) {        int was_changed = curbufIsChanged();        did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD, -                                     sfname, sfname, FALSE, curbuf, eap); +                                     sfname, sfname, false, curbuf, eap);        if (did_cmd) {          if (was_changed && !curbufIsChanged()) {            /* Written everything correctly and BufWriteCmd has reset @@ -2385,21 +2385,21 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en            u_update_save_nr(curbuf);          }        } else { -        if (overwriting && bt_nofile(curbuf)) { -          nofile_err = TRUE; +        if (overwriting && bt_nofilename(curbuf)) { +          nofile_err = true;          } else {            apply_autocmds_exarg(EVENT_BUFWRITEPRE, -                               sfname, sfname, FALSE, curbuf, eap); +                               sfname, sfname, false, curbuf, eap);          }        }      } else {        if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD, -                                           sfname, sfname, FALSE, curbuf, eap))) { -        if (overwriting && bt_nofile(curbuf)) { -          nofile_err = TRUE; +                                           sfname, sfname, false, curbuf, eap))) { +        if (overwriting && bt_nofilename(curbuf)) { +          nofile_err = true;          } else {            apply_autocmds_exarg(EVENT_FILEWRITEPRE, -                               sfname, sfname, FALSE, curbuf, eap); +                               sfname, sfname, false, curbuf, eap);          }        }      } @@ -4306,7 +4306,7 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)    char *p;    if (buf->b_fname != NULL -      && !bt_nofile(buf) +      && !bt_nofilename(buf)        && !path_with_url(buf->b_fname)        && (force            || buf->b_sfname == NULL diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 01d4ab086e..8d896aef31 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -229,7 +229,7 @@ EXTERN bool did_emsg;                       // set by emsg() when the message  EXTERN bool called_vim_beep;                // set if vim_beep() is called  EXTERN bool did_emsg_syntax;                // did_emsg set because of a                                              // syntax error -EXTERN int called_emsg;                     // always set by emsg() +EXTERN int called_emsg;                     // always incremented by emsg()  EXTERN int ex_exitval INIT(= 0);            // exit value for ex mode  EXTERN bool emsg_on_display INIT(= false);  // there is an error message  EXTERN bool rc_did_emsg INIT(= false);      // vim_regcomp() called emsg() diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 9a4db520a6..7f4df66b4d 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2125,6 +2125,10 @@ void init_default_mappings(void)    add_map("<C-W>", "<C-G>u<C-W>", MODE_INSERT, false);    add_map("*", "y/\\\\V<C-R>\"<CR>", MODE_VISUAL, false);    add_map("#", "y?\\\\V<C-R>\"<CR>", MODE_VISUAL, false); + +  // Use : instead of <Cmd> so that ranges are supported (e.g. 3& repeats the substitution on the +  // next 3 lines) +  add_map("&", ":&&<CR>", MODE_NORMAL, false);  }  /// Add a mapping. Unlike @ref do_map this copies the string arguments, so diff --git a/src/nvim/match.c b/src/nvim/match.c index d5e3d8cddc..e17a95569c 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -398,7 +398,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_    linenr_T l;    colnr_T matchcol;    long nmatched = 0; -  int save_called_emsg = called_emsg; +  const int called_emsg_before = called_emsg;    // for :{range}s/pat only highlight inside the range    if (lnum < search_first_line || lnum > search_last_line) { @@ -421,7 +421,6 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_    // Repeat searching for a match until one is found that includes "mincol"    // or none is found in this line. -  called_emsg = false;    for (;;) {      // Stop searching after passing the time limit.      if (profile_passed_limit(shl->tm)) { @@ -468,7 +467,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_        if (regprog_is_copy) {          cur->match.regprog = cur->hl.rm.regprog;        } -      if (called_emsg || got_int || timed_out) { +      if (called_emsg > called_emsg_before || got_int || timed_out) {          // Error while handling regexp: stop using this regexp.          if (shl == search_hl) {            // don't free regprog in the match list, it's a copy @@ -495,9 +494,6 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_        break;                            // useful match found      }    } - -  // Restore called_emsg for assert_fails(). -  called_emsg = save_called_emsg;  }  /// Advance to the match in window "wp" line "lnum" or past it. diff --git a/src/nvim/message.c b/src/nvim/message.c index 6ab0a5d3e6..2c96613bb3 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -637,7 +637,7 @@ static bool emsg_multiline(const char *s, bool multiline)      return true;    } -  called_emsg = true; +  called_emsg++;    // If "emsg_severe" is true: When an error exception is to be thrown,    // prefer this message over previous messages for the same command. diff --git a/src/nvim/move.c b/src/nvim/move.c index 99ca5060cd..bd68ad6f97 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -786,8 +786,7 @@ void curs_columns(win_T *wp, int may_scroll)      } else {        wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows;      } -  } else if (wp->w_p_wrap -             && wp->w_width_inner != 0) { +  } else if (wp->w_p_wrap && wp->w_width_inner != 0) {      width = textwidth + win_col_off2(wp);      // long line wrapping, adjust wp->w_wrow @@ -1083,8 +1082,7 @@ bool scrolldown(long line_count, int byfold)     * and move the cursor onto the displayed part of the window.     */    int wrow = curwin->w_wrow; -  if (curwin->w_p_wrap -      && curwin->w_width_inner != 0) { +  if (curwin->w_p_wrap && curwin->w_width_inner != 0) {      validate_virtcol();      validate_cheight();      wrow += curwin->w_cline_height - 1 - diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0eb8a8f59b..b675abfb7d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6012,8 +6012,7 @@ static void nv_g_home_m_cmd(cmdarg_T *cap)    cap->oap->motion_type = kMTCharWise;    cap->oap->inclusive = false; -  if (curwin->w_p_wrap -      && curwin->w_width_inner != 0) { +  if (curwin->w_p_wrap && curwin->w_width_inner != 0) {      int width1 = curwin->w_width_inner - curwin_col_off();      int width2 = width1 + curwin_col_off2(); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ba151b5ce2..90c0b6f5c6 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -726,8 +726,7 @@ static int pum_set_selected(int n, int repeat)          if (!resized              && (curbuf->b_nwindows == 1)              && (curbuf->b_fname == NULL) -            && (curbuf->b_p_bt[0] == 'n') -            && (curbuf->b_p_bt[2] == 'f') +            && bt_nofile(curbuf)              && (curbuf->b_p_bh[0] == 'w')) {            // Already a "wipeout" buffer, make it empty.            while (!buf_is_empty(curbuf)) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index bb6cbc35bc..4c49d30819 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2327,7 +2327,6 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)  {    regprog_T *prog = NULL;    char_u *expr = (char_u *)expr_arg; -  int save_called_emsg;    regexp_engine = p_re; @@ -2360,8 +2359,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)    //    // First try the NFA engine, unless backtracking was requested.    // -  save_called_emsg = called_emsg; -  called_emsg = false; +  const int called_emsg_before = called_emsg;    if (regexp_engine != BACKTRACKING_ENGINE) {      prog = nfa_regengine.regcomp(expr,                                   re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0)); @@ -2388,13 +2386,12 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)      // also fails for patterns that it can't handle well but are still valid      // patterns, thus a retry should work.      // But don't try if an error message was given. -    if (regexp_engine == AUTOMATIC_ENGINE && !called_emsg) { +    if (regexp_engine == AUTOMATIC_ENGINE && called_emsg == called_emsg_before) {        regexp_engine = BACKTRACKING_ENGINE;        report_re_switch(expr);        prog = bt_regengine.regcomp(expr, re_flags);      }    } -  called_emsg |= save_called_emsg;    if (prog != NULL) {      // Store the info needed to call regcomp() again when the engine turns out diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3d69d317fd..de837720c1 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5316,7 +5316,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)      }      fillchar = wp->w_p_fcs_chars.wbr; -    attr = (wp == curwin) ? HL_ATTR(HLF_WBR) : HL_ATTR(HLF_WBRNC); +    attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);      maxwidth = wp->w_width_inner;      use_sandbox = was_set_insecurely(wp, "winbar", 0); @@ -6533,13 +6533,11 @@ static void win_redr_ruler(win_T *wp, bool always)    }    if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) { -    int save_called_emsg = called_emsg; -    called_emsg = false; +    const int called_emsg_before = called_emsg;      win_redr_custom(wp, false, true); -    if (called_emsg) { +    if (called_emsg > called_emsg_before) {        set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);      } -    called_emsg |= save_called_emsg;      return;    } diff --git a/src/nvim/search.c b/src/nvim/search.c index a915594e26..f3061b4dc4 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -556,7 +556,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,    long nmatched;    int submatch = 0;    bool first_match = true; -  int save_called_emsg = called_emsg; +  const int called_emsg_before = called_emsg;    bool break_loop = false;    linenr_T stop_lnum = 0;  // stop after this line number when != 0    proftime_T *tm = NULL;   // timeout limit or NULL @@ -579,7 +579,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,    /*     * find the string     */ -  called_emsg = FALSE;    do {  // loop for count      // When not accepting a match at the start position set "extra_col" to a      // non-zero value.  Don't do that when starting at MAXCOL, since MAXCOL + 1 @@ -651,7 +650,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,            break;          }          // Abort searching on an error (e.g., out of stack). -        if (called_emsg || (timed_out != NULL && *timed_out)) { +        if (called_emsg > called_emsg_before || (timed_out != NULL && *timed_out)) {            break;          }          if (nmatched > 0) { @@ -908,7 +907,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,        // Stop the search if wrapscan isn't set, "stop_lnum" is        // specified, after an interrupt, after a match and after looping        // twice. -      if (!p_ws || stop_lnum != 0 || got_int || called_emsg +      if (!p_ws || stop_lnum != 0 || got_int +          || called_emsg > called_emsg_before            || (timed_out != NULL && *timed_out)            || break_loop            || found || loop) { @@ -933,7 +933,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,          extra_arg->sa_wrapped = true;        }      } -    if (got_int || called_emsg +    if (got_int || called_emsg > called_emsg_before          || (timed_out != NULL && *timed_out)          || break_loop) {        break; @@ -942,8 +942,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,    vim_regfree(regmatch.regprog); -  called_emsg |= save_called_emsg; -    if (!found) {             // did not find it      if (got_int) {        emsg(_(e_interr)); @@ -4409,7 +4407,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct    int nmatched = 0;    int result = -1;    pos_T pos; -  int save_called_emsg = called_emsg; +  const int called_emsg_before = called_emsg;    int flag = 0;    if (pattern == NULL) { @@ -4435,7 +4433,6 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct                 SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) {      // Zero-width pattern should match somewhere, then we can check if      // start and end are in the same position. -    called_emsg = false;      do {        regmatch.startpos[0].col++;        nmatched = vim_regexec_multi(®match, curwin, curbuf, @@ -4449,14 +4446,13 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct               ? regmatch.startpos[0].col < pos.col               : regmatch.startpos[0].col > pos.col); -    if (!called_emsg) { +    if (called_emsg == called_emsg_before) {        result = (nmatched != 0                  && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum                  && regmatch.startpos[0].col == regmatch.endpos[0].col);      }    } -  called_emsg |= save_called_emsg;    vim_regfree(regmatch.regprog);    return result;  } @@ -5303,6 +5299,16 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr)    do_fuzzymatch(argvars, rettv, true);  } +/// Get line "lnum" and copy it into "buf[LSIZE]". +/// The copy is made because the regexp may make the line invalid when using a +/// mark. +static char_u *get_line_and_copy(linenr_T lnum, char_u *buf) +{ +  char_u *line = ml_get(lnum); +  STRLCPY(buf, line, LSIZE); +  return buf; +} +  /// Find identifiers or defines in included files.  /// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.  /// @@ -5399,7 +5405,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo    if (lnum > end_lnum) {                // do at least one line      lnum = end_lnum;    } -  line = ml_get(lnum); +  line = get_line_and_copy(lnum, file_line);    for (;;) {      if (incl_regmatch.regprog != NULL @@ -5687,7 +5693,7 @@ search_line:              if (lnum >= end_lnum) {                goto exit_matched;              } -            line = ml_get(++lnum); +            line = get_line_and_copy(++lnum, file_line);            } else if (vim_fgets(line = file_line,                                 LSIZE, files[depth].fp)) {              goto exit_matched; @@ -5879,7 +5885,7 @@ exit_matched:        if (++lnum > end_lnum) {          break;        } -      line = ml_get(lnum); +      line = get_line_and_copy(lnum, file_line);      }      already = NULL;    } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 04eb9dd2bc..9d2fd2637d 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -3905,12 +3905,12 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin)  /// Return true if "word" contains valid word characters.  /// Control characters and trailing '/' are invalid.  Space is OK. -static bool valid_spell_word(const char_u *word) +static bool valid_spell_word(const char_u *word, const char_u *end)  { -  if (!utf_valid_string(word, NULL)) { +  if (!utf_valid_string(word, end)) {      return false;    } -  for (const char_u *p = word; *p != NUL; p += utfc_ptr2len((const char *)p)) { +  for (const char_u *p = word; *p != NUL && p < end; p += utfc_ptr2len((const char *)p)) {      if (*p < ' ' || (p[0] == '/' && p[1] == NUL)) {        return false;      } @@ -3939,7 +3939,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co    int res = OK;    // Avoid adding illegal bytes to the word tree. -  if (!valid_spell_word(word)) { +  if (!valid_spell_word(word, word + len)) {      return FAIL;    } @@ -5536,7 +5536,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo    int i;    char_u *spf; -  if (!valid_spell_word(word)) { +  if (!valid_spell_word(word, word + len)) {      emsg(_(e_illegal_character_in_word));      return;    } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 5b62d11bc9..28b3b6c1ef 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1863,6 +1863,7 @@ parse_line:          // For "normal" tags: Do a quick check if the tag matches.          // This speeds up tag searching a lot!          if (orgpat.headlen) { +          memset(&tagp, 0, sizeof(tagp));            tagp.tagname = lbuf;            tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);            if (tagp.tagname_end == NULL) { @@ -2784,7 +2785,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)      // A :ta from a help file will keep the b_help flag set.  For ":ptag"      // we need to use the flag from the window where we came from.      if (l_g_do_tagpreview != 0) { -      keep_help_flag = curwin_save->w_buffer->b_help; +      keep_help_flag = bt_help(curwin_save->w_buffer);      } else {        keep_help_flag = curbuf->b_help;      } diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index d0a666a049..6b16e888a9 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -153,6 +153,9 @@ func RunTheTest(test)    " directory after executing the test.    let save_cwd = getcwd() +  " Align Nvim defaults to Vim. +  source setup.vim +    if exists("*SetUp")      try        call SetUp() @@ -191,9 +194,6 @@ func RunTheTest(test)      endtry    endif -  " Align Nvim defaults to Vim. -  source setup.vim -    " Clear any autocommands and put back the catch-all for SwapExists.    au!    au SwapExists * call HandleSwapExists() @@ -364,24 +364,25 @@ let s:flaky_tests = [        \ 'Test_cursorhold_insert()',        \ 'Test_exit_callback_interval()',        \ 'Test_map_timeout_with_timer_interrupt()', -      \ 'Test_oneshot()',        \ 'Test_out_cb()', -      \ 'Test_paused()',        \ 'Test_popup_and_window_resize()',        \ 'Test_quoteplus()',        \ 'Test_quotestar()',        \ 'Test_reltime()', -      \ 'Test_repeat_many()', -      \ 'Test_repeat_three()',        \ 'Test_state()', -      \ 'Test_stop_all_in_callback()',        \ 'Test_term_mouse_double_click_to_create_tab()',        \ 'Test_term_mouse_multiple_clicks_to_visually_select()',        \ 'Test_terminal_composing_unicode()',        \ 'Test_terminal_redir_file()',        \ 'Test_terminal_tmap()', +      \ 'Test_timer_oneshot()', +      \ 'Test_timer_paused()', +      \ 'Test_timer_repeat_many()', +      \ 'Test_timer_repeat_three()', +      \ 'Test_timer_stop_all_in_callback()', +      \ 'Test_timer_stop_in_callback()', +      \ 'Test_timer_with_partial_callback()',        \ 'Test_termwinscroll()', -      \ 'Test_with_partial_callback()',        \ ]  " Locate Test_ functions and execute them. diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index f8db2ea120..e6c0762729 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -1,33 +1,34 @@ -" Align Nvim defaults to Vim. -set backspace= -set complete=.,w,b,u,t,i -set directory& -set directory^=. -set fillchars=vert:\|,fold:- -set formatoptions=tcq -set fsync -set laststatus=1 -set listchars=eol:$ -set joinspaces -set nohidden nosmarttab noautoindent noautoread noruler noshowcmd -set nohlsearch noincsearch -set nrformats=bin,octal,hex -set shortmess=filnxtToOS -set sidescroll=0 -set tags=./tags,tags -set undodir& -set undodir^=. -set wildoptions= -set startofline -set sessionoptions& -set sessionoptions+=options -set viewoptions& -set viewoptions+=options -set switchbuf= - -" Make "Q" switch to Ex mode. -" This does not work for all tests. -nnoremap Q gQ +if exists('s:did_load') +  " Align Nvim defaults to Vim. +  set backspace= +  set complete=.,w,b,u,t,i +  set directory& +  set directory^=. +  set fillchars=vert:\|,fold:- +  set formatoptions=tcq +  set fsync +  set laststatus=1 +  set listchars=eol:$ +  set joinspaces +  set nohidden nosmarttab noautoindent noautoread noruler noshowcmd +  set nohlsearch noincsearch +  set nrformats=bin,octal,hex +  set shortmess=filnxtToOS +  set sidescroll=0 +  set tags=./tags,tags +  set undodir& +  set undodir^=. +  set wildoptions= +  set startofline +  set sessionoptions& +  set sessionoptions+=options +  set viewoptions& +  set viewoptions+=options +  set switchbuf= +  " Make "Q" switch to Ex mode. +  " This does not work for all tests. +  nnoremap Q gQ +endif  " Common preparations for running tests. diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 164149476f..ca7c8574cb 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -1,5 +1,8 @@  " Test argument list commands +source shared.vim +source term_util.vim +  func Reset_arglist()    args a | %argd  endfunc @@ -510,3 +513,42 @@ func Test_argdo()    call assert_equal(['Xa.c', 'Xb.c', 'Xc.c'], l)    bwipe Xa.c Xb.c Xc.c  endfunc + +" Test for quiting Vim with unedited files in the argument list +func Test_quit_with_arglist() +  if !CanRunVimInTerminal() +    throw 'Skipped: cannot run vim in terminal' +  endif +  let buf = RunVimInTerminal('', {'rows': 6}) +  call term_sendkeys(buf, ":set nomore\n") +  call term_sendkeys(buf, ":args a b c\n") +  call term_sendkeys(buf, ":quit\n") +  call term_wait(buf) +  call WaitForAssert({-> assert_match('^E173:', term_getline(buf, 6))}) +  call StopVimInTerminal(buf) + +  " Try :confirm quit with unedited files in arglist +  let buf = RunVimInTerminal('', {'rows': 6}) +  call term_sendkeys(buf, ":set nomore\n") +  call term_sendkeys(buf, ":args a b c\n") +  call term_sendkeys(buf, ":confirm quit\n") +  call term_wait(buf) +  call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$', +        \ term_getline(buf, 6))}) +  call term_sendkeys(buf, "N") +  call term_wait(buf) +  call term_sendkeys(buf, ":confirm quit\n") +  call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$', +        \ term_getline(buf, 6))}) +  call term_sendkeys(buf, "Y") +  call term_wait(buf) +  call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))}) +  only! +  " When this test fails, swap files are left behind which breaks subsequent +  " tests +  call delete('.a.swp') +  call delete('.b.swp') +  call delete('.c.swp') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 9d0d5b3e70..fdd8b0bef6 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -50,6 +50,26 @@ func Test_assert_equal()    call remove(v:errors, 0)  endfunc +func Test_assert_equal_dict() +  call assert_equal(0, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 1})) + +  call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 3})) +  call assert_match("Expected {'one': 1} but got {'one': 3} - 1 equal item omitted", v:errors[0]) +  call remove(v:errors, 0) + +  call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 22, one: 11})) +  call assert_match("Expected {'one': 1, 'two': 2} but got {'one': 11, 'two': 22}", v:errors[0]) +  call remove(v:errors, 0) + +  call assert_equal(1, assert_equal(#{}, #{two: 2, one: 1})) +  call assert_match("Expected {} but got {'one': 1, 'two': 2}", v:errors[0]) +  call remove(v:errors, 0) + +  call assert_equal(1, assert_equal(#{two: 2, one: 1}, #{})) +  call assert_match("Expected {'one': 1, 'two': 2} but got {}", v:errors[0]) +  call remove(v:errors, 0) +endfunc +  func Test_assert_equalfile()    call assert_equal(1, assert_equalfile('abcabc', 'xyzxyz'))    call assert_match("E485: Can't read file abcabc", v:errors[0]) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 1936832400..438851a0ad 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2226,7 +2226,7 @@ func Test_autocmd_bufreadpre()    " (even though the position will be invalid, this should make Vim reset the    " cursor position in the other window.    wincmd p -  1 +  1 " set cpo+=g    " won't do anything, but try to set the cursor on an invalid lnum    autocmd BufReadPre <buffer> :norm! 70gg    " triggers BufReadPre, should not move the cursor in either window @@ -2241,8 +2241,11 @@ func Test_autocmd_bufreadpre()    close    close    call delete('XAutocmdBufReadPre.txt') +  " set cpo-=g  endfunc +" FileChangedShell tested in test_filechanged.vim +  " Tests for the following autocommands:  " - FileWritePre	writing a compressed file  " - FileReadPost	reading a compressed file @@ -2560,7 +2563,29 @@ func Test_BufWrite_lockmarks()    call delete('Xtest2')  endfunc -" FileChangedShell tested in test_filechanged.vim +" Test closing a window or editing another buffer from a FileChangedRO handler +" in a readonly buffer +func Test_FileChangedRO_winclose() +  augroup FileChangedROTest +    au! +    autocmd FileChangedRO * quit +  augroup END +  new +  set readonly +  call assert_fails('normal i', 'E788:') +  close +  augroup! FileChangedROTest + +  augroup FileChangedROTest +    au! +    autocmd FileChangedRO * edit Xfile +  augroup END +  new +  set readonly +  call assert_fails('normal i', 'E788:') +  close +  augroup! FileChangedROTest +endfunc  func LogACmd()    call add(g:logged, line('$')) diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim index 02360b4e75..67be3e6747 100644 --- a/src/nvim/testdir/test_buffer.vim +++ b/src/nvim/testdir/test_buffer.vim @@ -105,9 +105,9 @@ func Test_buflist_browse()    call assert_equal(b2, bufnr())    call assert_equal(1, line('.')) -  brewind +/foo3 +  brewind +    call assert_equal(b1, bufnr()) -  call assert_equal(3, line('.')) +  call assert_equal(4, line('.'))    blast +/baz2    call assert_equal(b3, bufnr()) @@ -146,6 +146,7 @@ endfunc  func Test_bdelete_cmd()    %bwipe!    call assert_fails('bdelete 5', 'E516:') +  call assert_fails('1,1bdelete 1 2', 'E488:')    " Deleting a unlisted and unloaded buffer    edit Xfile1 diff --git a/src/nvim/testdir/test_changelist.vim b/src/nvim/testdir/test_changelist.vim index ce77c1f3c7..3741f32e69 100644 --- a/src/nvim/testdir/test_changelist.vim +++ b/src/nvim/testdir/test_changelist.vim @@ -46,3 +46,5 @@ func Test_getchangelist()    call delete('Xfile1.txt')    call delete('Xfile2.txt')  endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index 6f09e85a42..683bcabe34 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -1,3 +1,4 @@ +" Test for character search commands - t, T, f, F, ; and ,  func Test_charsearch()    enew! @@ -60,3 +61,16 @@ func Test_search_cmds()    call assert_equal('ddd yee y', getline(6))    enew!  endfunc + +" Test for character search in virtual edit mode with <Tab> +func Test_csearch_virtualedit() +  new +  set virtualedit=all +  call setline(1, "a\tb") +  normal! tb +  call assert_equal([0, 1, 2, 6], getpos('.')) +  set virtualedit& +  close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index 922803438f..db62fe5fa6 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -39,6 +39,8 @@ func Test_client_server()    call remote_send(name, ":let testvar = 'yes'\<CR>")    call WaitFor('remote_expr("' . name . '", "exists(\"testvar\") ? testvar : \"\"", "", 1) == "yes"')    call assert_equal('yes', remote_expr(name, "testvar", "", 2)) +  call assert_fails("let x=remote_expr(name, '2+x')", 'E449:') +  call assert_fails("let x=remote_expr('[], '2+2')", 'E116:')    if has('unix') && has('gui') && !has('gui_running')      " Running in a terminal and the GUI is available: Tell the server to open @@ -75,6 +77,7 @@ func Test_client_server()      eval 'MYSELF'->remote_startserver()      " May get MYSELF1 when running the test again.      call assert_match('MYSELF', v:servername) +    call assert_fails("call remote_startserver('MYSELF')", 'E941:')    endif    let g:testvar = 'myself'    call assert_equal('myself', remote_expr(v:servername, 'testvar')) @@ -107,7 +110,12 @@ func Test_client_server()        call job_stop(job, 'kill')      endif    endtry + +  call assert_fails("let x=remote_peek([])", 'E730:') +  call assert_fails("let x=remote_read('vim10')", 'E277:')  endfunc  " Uncomment this line to get a debugging log  " call ch_logfile('channellog', 'w') + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 9885d356fd..276bb7fb71 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -603,12 +603,22 @@ func Test_cmdline_paste()    call feedkeys(":\"one\<C-R>\<C-X>two\<CR>", 'xt')    call assert_equal('"onetwo', @:) +  " Test for pasting register containing CTRL-H using CTRL-R and CTRL-R CTRL-R    let @a = "xy\<C-H>z"    call feedkeys(":\"\<C-R>a\<CR>", 'xt')    call assert_equal('"xz', @:) +  call feedkeys(":\"\<C-R>\<C-R>a\<CR>", 'xt') +  call assert_equal("\"xy\<C-H>z", @:)    call feedkeys(":\"\<C-R>\<C-O>a\<CR>", 'xt')    call assert_equal("\"xy\<C-H>z", @:) +  " Test for pasting register containing CTRL-V using CTRL-R and CTRL-R CTRL-R +  let @a = "xy\<C-V>z" +  call feedkeys(":\"\<C-R>=@a\<CR>\<cr>", 'xt') +  call assert_equal('"xyz', @:) +  call feedkeys(":\"\<C-R>\<C-R>=@a\<CR>\<cr>", 'xt') +  call assert_equal("\"xy\<C-V>z", @:) +    call assert_beeps('call feedkeys(":\<C-R>=\<C-R>=\<Esc>", "xt")')    bwipe! @@ -1268,6 +1278,10 @@ endfunc  func Test_cmdwin_feedkeys()    " This should not generate E488    call feedkeys("q:\<CR>", 'x') +  " Using feedkeys with q: only should automatically close the cmd window +  call feedkeys('q:', 'xt') +  call assert_equal(1, winnr('$')) +  call assert_equal('', getcmdwintype())  endfunc  " Tests for the issues fixed in 7.4.441. @@ -1312,22 +1326,6 @@ func Test_cmdwin_autocmd()    augroup! CmdWin  endfunc -func Test_cmdwin_jump_to_win() -  call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:') -  new -  set modified -  call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:') -  close! -  call feedkeys("q/:close\<CR>", "xt") -  call assert_equal(1, winnr('$')) -  call feedkeys("q/:exit\<CR>", "xt") -  call assert_equal(1, winnr('$')) - -  " opening command window twice should fail -  call assert_beeps('call feedkeys("q:q:\<CR>\<CR>", "xt")') -  call assert_equal(1, winnr('$')) -endfunc -  func Test_cmdlineclear_tabenter()    " See test/functional/legacy/cmdline_spec.lua    CheckScreendump @@ -1365,6 +1363,22 @@ func Test_cmdline_expand_special()    call delete('Xfile.java')  endfunc +func Test_cmdwin_jump_to_win() +  call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:') +  new +  set modified +  call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:') +  close! +  call feedkeys("q/:close\<CR>", "xt") +  call assert_equal(1, winnr('$')) +  call feedkeys("q/:exit\<CR>", "xt") +  call assert_equal(1, winnr('$')) + +  " opening command window twice should fail +  call assert_beeps('call feedkeys("q:q:\<CR>\<CR>", "xt")') +  call assert_equal(1, winnr('$')) +endfunc +  " Test for backtick expression in the command line  func Test_cmd_backtick()    %argd @@ -1385,6 +1399,35 @@ func Test_cmdwin_tabpage()    tabclose!  endfunc +" Test for the :! command +func Test_cmd_bang() +  if !has('unix') +    return +  endif + +  let lines =<< trim [SCRIPT] +    " Test for no previous command +    call assert_fails('!!', 'E34:') +    set nomore +    " Test for cmdline expansion with :! +    call setline(1, 'foo!') +    silent !echo <cWORD> > Xfile.out +    call assert_equal(['foo!'], readfile('Xfile.out')) +    " Test for using previous command +    silent !echo \! ! +    call assert_equal(['! echo foo!'], readfile('Xfile.out')) +    call writefile(v:errors, 'Xresult') +    call delete('Xfile.out') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc +  " Test error: "E135: *Filter* Autocommands must not change current buffer"  func Test_cmd_bang_E135()    new @@ -1529,6 +1572,7 @@ endfunc  func Test_cmdline_edit()    let str = ":one two\<C-U>"    let str ..= "one two\<C-W>\<C-W>" +  let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"    let str ..= "\<Left>five\<Right>"    let str ..= "\<Home>two "    let str ..= "\<C-Left>one " @@ -1547,6 +1591,7 @@ func Test_cmdline_edit_rightleft()    set rightleftcmd=search    let str = "/one two\<C-U>"    let str ..= "one two\<C-W>\<C-W>" +  let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"    let str ..= "\<Right>five\<Left>"    let str ..= "\<Home>two "    let str ..= "\<C-Right>one " @@ -1574,6 +1619,63 @@ func Test_cmdline_expr()    call assert_equal("\"e \<C-\>\<C-Y>", @:)  endfunc +" Test for 'imcmdline' and 'imsearch' +" This test doesn't actually test the input method functionality. +func Test_cmdline_inputmethod() +  new +  call setline(1, ['', 'abc', '']) +  set imcmdline + +  call feedkeys(":\"abc\<CR>", 'xt') +  call assert_equal("\"abc", @:) +  call feedkeys(":\"\<C-^>abc\<C-^>\<CR>", 'xt') +  call assert_equal("\"abc", @:) +  call feedkeys("/abc\<CR>", 'xt') +  call assert_equal([2, 1], [line('.'), col('.')]) +  call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt') +  call assert_equal([2, 1], [line('.'), col('.')]) + +  " set imsearch=2 +  call cursor(1, 1) +  call feedkeys("/abc\<CR>", 'xt') +  call assert_equal([2, 1], [line('.'), col('.')]) +  call cursor(1, 1) +  call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt') +  call assert_equal([2, 1], [line('.'), col('.')]) +  set imdisable +  call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt') +  call assert_equal([2, 1], [line('.'), col('.')]) +  set imdisable& +  set imsearch& + +  set imcmdline& +  %bwipe! +endfunc + +" Test for recursively getting multiple command line inputs +func Test_cmdwin_multi_input() +  call feedkeys(":\<C-R>=input('P: ')\<CR>\"cyan\<CR>\<CR>", 'xt') +  call assert_equal('"cyan', @:) +endfunc + +" Test for using CTRL-_ in the command line with 'allowrevins' +func Test_cmdline_revins() +  CheckNotMSWindows +  CheckFeature rightleft +  call feedkeys(":\"abc\<c-_>\<cr>", 'xt') +  call assert_equal("\"abc\<c-_>", @:) +  set allowrevins +  call feedkeys(":\"abc\<c-_>xyz\<c-_>\<CR>", 'xt') +  call assert_equal('"abcñèæ', @:) +  set allowrevins& +endfunc + +" Test for typing UTF-8 composing characters in the command line +func Test_cmdline_composing_chars() +  call feedkeys(":\"\<C-V>u3046\<C-V>u3099\<CR>", 'xt') +  call assert_equal('"ゔ', @:) +endfunc +  " Test for normal mode commands not supported in the cmd window  func Test_cmdwin_blocked_commands()    call assert_fails('call feedkeys("q:\<C-T>\<CR>", "xt")', 'E11:') @@ -1582,6 +1684,36 @@ func Test_cmdwin_blocked_commands()    call assert_fails('call feedkeys("q:Q\<CR>", "xt")', 'E11:')    call assert_fails('call feedkeys("q:Z\<CR>", "xt")', 'E11:')    call assert_fails('call feedkeys("q:\<F1>\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>s\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>v\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>^\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>n\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>z\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>o\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>w\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>j\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>k\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>h\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>l\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>T\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>x\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>r\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>R\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>K\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>}\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>]\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>f\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>d\<CR>", "xt")', 'E11:') +  call assert_fails('call feedkeys("q:\<C-W>g\<CR>", "xt")', 'E11:') +endfunc + +" Close the Cmd-line window in insert mode using CTRL-C +func Test_cmdwin_insert_mode_close() +  %bw! +  let s = '' +  exe "normal q:a\<C-C>let s='Hello'\<CR>" +  call assert_equal('Hello', s) +  call assert_equal(1, winnr('$'))  endfunc  " test that ";" works to find a match at the start of the first line diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index c0c572ce65..3dc8710d63 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -68,5 +68,9 @@ func Test_compiler_completion()  endfunc  func Test_compiler_error() +  let g:current_compiler = 'abc'    call assert_fails('compiler doesnotexist', 'E666:') +  call assert_equal('abc', g:current_compiler) +  call assert_fails('compiler! doesnotexist', 'E666:') +  unlet! g:current_compiler  endfunc diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 7b6bc940d3..acc34e5e7c 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -466,10 +466,12 @@ endfunc  func Test_digraph_cmndline()    " Create digraph on commandline -  " This is a hack, to let Vim create the digraph in commandline mode -  let s = '' -  exe "sil! norm! :let s.='\<c-k>Eu'\<cr>" -  call assert_equal("€", s) +  call feedkeys(":\"\<c-k>Eu\<cr>", 'xt') +  call assert_equal('"€', @:) + +  " Canceling a CTRL-K on the cmdline +  call feedkeys(":\"a\<c-k>\<esc>b\<cr>", 'xt') +  call assert_equal('"ab', @:)  endfunc  func Test_show_digraph() diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 19b088959b..65194f49dd 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -286,7 +286,7 @@ func Test_edit_11()    call cursor(2, 1)    call feedkeys("i\<c-f>int c;\<esc>", 'tnix')    call cursor(3, 1) -  call feedkeys("i/* comment */", 'tnix') +  call feedkeys("\<Insert>/* comment */", 'tnix')    call assert_equal(['{', "\<tab>int c;", "/* comment */"], getline(1, '$'))    " added changed cindentkeys slightly    set cindent cinkeys+=*/ @@ -1617,6 +1617,22 @@ func Test_edit_startinsert()    bwipe!  endfunc +" Test for :startreplace and :startgreplace +func Test_edit_startreplace() +  new +  call setline(1, 'abc') +  call feedkeys("l:startreplace\<CR>xyz\e", 'xt') +  call assert_equal('axyz', getline(1)) +  call feedkeys("0:startreplace!\<CR>abc\e", 'xt') +  call assert_equal('axyzabc', getline(1)) +  call setline(1, "a\tb") +  call feedkeys("0l:startgreplace\<CR>xyz\e", 'xt') +  call assert_equal("axyz\tb", getline(1)) +  call feedkeys("0i\<C-R>=execute('startreplace')\<CR>12\e", 'xt') +  call assert_equal("12axyz\tb", getline(1)) +  close! +endfunc +  func Test_edit_noesckeys()    CheckNotGui    new @@ -1637,6 +1653,58 @@ func Test_edit_noesckeys()    " set esckeys  endfunc +" Test for running an invalid ex command in insert mode using CTRL-O +" Note that vim has a hard-coded sleep of 3 seconds. So this test will take +" more than 3 seconds to complete. +func Test_edit_ctrl_o_invalid_cmd() +  new +  set showmode showcmd +  let caught_e492 = 0 +  try +    call feedkeys("i\<C-O>:invalid\<CR>abc\<Esc>", "xt") +  catch /E492:/ +    let caught_e492 = 1 +  endtry +  call assert_equal(1, caught_e492) +  call assert_equal('abc', getline(1)) +  set showmode& showcmd& +  close! +endfunc + +" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions') +func Test_edit_cpo_H() +  throw 'Skipped: Nvim does not support cpoptions flag "H"' +  new +  call setline(1, '    ') +  normal! Ia +  call assert_equal('    a', getline(1)) +  set cpo+=H +  call setline(1, '    ') +  normal! Ia +  call assert_equal('   a ', getline(1)) +  set cpo-=H +  close! +endfunc + +" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions') +func Test_edit_cpo_L() +  new +  call setline(1, 'abcdefghijklmnopqr') +  exe "normal 0gR\<Tab>" +  call assert_equal("\<Tab>ijklmnopqr", getline(1)) +  set cpo+=L +  set list +  call setline(1, 'abcdefghijklmnopqr') +  exe "normal 0gR\<Tab>" +  call assert_equal("\<Tab>cdefghijklmnopqr", getline(1)) +  set nolist +  call setline(1, 'abcdefghijklmnopqr') +  exe "normal 0gR\<Tab>" +  call assert_equal("\<Tab>ijklmnopqr", getline(1)) +  set cpo-=L +  %bw! +endfunc +  " Test for editing a directory  func Test_edit_is_a_directory()    CheckEnglish diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index cff78620d1..b478332c79 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -64,6 +64,66 @@ func Test_ex_mode()    let &encoding = encoding_save  endfunc +" Test substitute confirmation prompt :%s/pat/str/c in Ex mode +func Test_Ex_substitute() +  CheckRunVimInTerminal +  let buf = RunVimInTerminal('', {'rows': 6}) + +  call term_sendkeys(buf, ":call setline(1, ['foo foo', 'foo foo', 'foo foo'])\<CR>") +  call term_sendkeys(buf, ":set number\<CR>") +  call term_sendkeys(buf, "gQ") +  call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000) + +  call term_sendkeys(buf, "%s/foo/bar/gc\<CR>") +  call WaitForAssert({-> assert_match('  1 foo foo', term_getline(buf, 5))}, +        \ 1000) +  call WaitForAssert({-> assert_match('    ^^^', term_getline(buf, 6))}, 1000) +  call term_sendkeys(buf, "N\<CR>") +  call term_wait(buf) +  call WaitForAssert({-> assert_match('    ^^^', term_getline(buf, 6))}, 1000) +  call term_sendkeys(buf, "n\<CR>") +  call WaitForAssert({-> assert_match('        ^^^', term_getline(buf, 6))}, +        \ 1000) +  call term_sendkeys(buf, "y\<CR>") + +  call term_sendkeys(buf, "q\<CR>") +  call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000) + +  " Pressing enter in ex mode should print the current line +  call term_sendkeys(buf, "\<CR>") +  call WaitForAssert({-> assert_match('  3 foo foo', +        \ term_getline(buf, 5))}, 1000) + +  call term_sendkeys(buf, ":vi\<CR>") +  call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000) + +  call term_sendkeys(buf, ":q!\n") +  call StopVimInTerminal(buf) +endfunc + +" Test for displaying lines from an empty buffer in Ex mode +func Test_Ex_emptybuf() +  new +  call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E749:') +  call setline(1, "abc") +  call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E501:') +  call assert_fails('call feedkeys("Q%d\<CR>", "xt")', 'E749:') +  close! +endfunc + +" Test for the :open command +func Test_open_command() +  throw 'Skipped: Nvim does not have :open' +  new +  call setline(1, ['foo foo', 'foo bar', 'foo baz']) +  call feedkeys("Qopen\<CR>j", 'xt') +  call assert_equal('foo bar', getline('.')) +  call feedkeys("Qopen /bar/\<CR>", 'xt') +  call assert_equal(5, col('.')) +  call assert_fails('call feedkeys("Qopen /baz/\<CR>", "xt")', 'E479:') +  close! +endfunc +  " Test for :g/pat/visual to run vi commands in Ex mode  " This used to hang Vim before 8.2.0274.  func Test_Ex_global() @@ -81,6 +141,31 @@ func Test_Ex_escape_enter()    call assert_equal("a\rb", l)  endfunc +" Test for :append! command in Ex mode +func Test_Ex_append() +  throw 'Skipped: Nvim only supports Vim Ex mode' +  new +  call setline(1, "\t   abc") +  call feedkeys("Qappend!\npqr\nxyz\n.\nvisual\n", 'xt') +  call assert_equal(["\t   abc", "\t   pqr", "\t   xyz"], getline(1, '$')) +  close! +endfunc + +" In Ex-mode, backslashes at the end of a command should be halved. +func Test_Ex_echo_backslash() +  throw 'Skipped: Nvim only supports Vim Ex mode' +  " This test works only when the language is English +  if v:lang != "C" && v:lang !~ '^[Ee]n' +    return +  endif +  let bsl = '\\\\' +  let bsl2 = '\\\' +  call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")', +        \ "E15: Invalid expression: \\\\") +  call assert_fails('call feedkeys("Qecho " .. bsl2 .. "\nm\nvisual\n", "xt")', +        \ "E15: Invalid expression: \\\nm") +endfunc +  func Test_ex_mode_errors()    " Not allowed to enter ex mode when text is locked    au InsertCharPre <buffer> normal! gQ<CR> diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index df2cf97633..6858efeaa9 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -22,6 +22,7 @@ func Test_range_error()    call assert_fails(':\/echo 1', 'E481:')    normal vv    call assert_fails(":'<,'>echo 1", 'E481:') +  call assert_fails(":\\xcenter", 'E10:')  endfunc  func Test_buffers_lastused() @@ -65,6 +66,10 @@ func Test_copy()    1,3copy 2    call assert_equal(['L1', 'L2', 'L1', 'L2', 'L3', 'L3', 'L4'], getline(1, 7)) +  " Specifying a count before using : to run an ex-command +  exe "normal! gg4:yank\<CR>" +  call assert_equal("L1\nL2\nL1\nL2\n", @") +    close!  endfunc @@ -222,12 +227,24 @@ func Test_change_cmd()    close!  endfunc +" Test for the :language command +func Test_language_cmd() +  CheckNotMSWindows  " FIXME: why does this fail on Windows CI? +  CheckNotBSD  " FIXME: why does this fail on OpenBSD CI? +  CheckFeature multi_lang + +  call assert_fails('language ctype non_existing_lang', 'E197:') +  call assert_fails('language time non_existing_lang', 'E197:') +endfunc +  " Test for the :confirm command dialog  func Test_confirm_cmd()    CheckNotGui    CheckRunVimInTerminal +    call writefile(['foo1'], 'foo')    call writefile(['bar1'], 'bar') +    " Test for saving all the modified buffers    let buf = RunVimInTerminal('', {'rows': 20})    call term_sendkeys(buf, ":set nomore\n") @@ -240,8 +257,10 @@ func Test_confirm_cmd()    call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)    call term_sendkeys(buf, "A")    call StopVimInTerminal(buf) +    call assert_equal(['foo2'], readfile('foo'))    call assert_equal(['bar2'], readfile('bar')) +    " Test for discarding all the changes to modified buffers    let buf = RunVimInTerminal('', {'rows': 20})    call term_sendkeys(buf, ":set nomore\n") @@ -254,8 +273,10 @@ func Test_confirm_cmd()    call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)    call term_sendkeys(buf, "D")    call StopVimInTerminal(buf) +    call assert_equal(['foo2'], readfile('foo'))    call assert_equal(['bar2'], readfile('bar')) +    " Test for saving and discarding changes to some buffers    let buf = RunVimInTerminal('', {'rows': 20})    call term_sendkeys(buf, ":set nomore\n") @@ -270,6 +291,7 @@ func Test_confirm_cmd()    call WaitForAssert({-> assert_match('\[Y\]es, (N)o, (C)ancel: ', term_getline(buf, 20))}, 1000)    call term_sendkeys(buf, "Y")    call StopVimInTerminal(buf) +    call assert_equal(['foo4'], readfile('foo'))    call assert_equal(['bar2'], readfile('bar')) @@ -391,6 +413,11 @@ func Test_confirm_write_partial_file()    call delete('Xscript')  endfunc +" Test for the :print command +func Test_print_cmd() +  call assert_fails('print', 'E749:') +endfunc +  " Test for the :winsize command  func Test_winsize_cmd()    call assert_fails('winsize 1', 'E465:') @@ -399,6 +426,67 @@ func Test_winsize_cmd()    " Actually changing the window size would be flaky.  endfunc +" Test for the :redir command +func Test_redir_cmd() +  call assert_fails('redir @@', 'E475:') +  call assert_fails('redir abc', 'E475:') +  if has('unix') +    call mkdir('Xdir') +    call assert_fails('redir > Xdir', 'E17:') +    call delete('Xdir', 'd') +  endif +  if !has('bsd') +    call writefile([], 'Xfile') +    call setfperm('Xfile', 'r--r--r--') +    call assert_fails('redir! > Xfile', 'E190:') +    call delete('Xfile') +  endif + +  " Test for redirecting to a register +  redir @q> | echon 'clean ' | redir END +  redir @q>> | echon 'water' | redir END +  call assert_equal('clean water', @q) + +  " Test for redirecting to a variable +  redir => color | echon 'blue ' | redir END +  redir =>> color | echon 'sky' | redir END +  call assert_equal('blue sky', color) +endfunc + +" Test for the :filetype command +func Test_filetype_cmd() +  call assert_fails('filetype abc', 'E475:') +endfunc + +" Test for the :mode command +func Test_mode_cmd() +  call assert_fails('mode abc', 'E359:') +endfunc + +" Test for the :sleep command +func Test_sleep_cmd() +  call assert_fails('sleep x', 'E475:') +endfunc + +" Test for the :read command +func Test_read_cmd() +  call writefile(['one'], 'Xfile') +  new +  call assert_fails('read', 'E32:') +  edit Xfile +  read +  call assert_equal(['one', 'one'], getline(1, '$')) +  close! +  new +  read Xfile +  call assert_equal(['', 'one'], getline(1, '$')) +  call deletebufline('', 1, '$') +  call feedkeys("Qr Xfile\<CR>visual\<CR>", 'xt') +  call assert_equal(['one'], getline(1, '$')) +  close! +  call delete('Xfile') +endfunc +  " Test for running Ex commands when text is locked.  " <C-\>e in the command line is used to lock the text  func Test_run_excmd_with_text_locked() @@ -423,6 +511,107 @@ func Test_run_excmd_with_text_locked()    call assert_fails("call feedkeys(\":\<C-R>=execute('bnext')\<CR>\", 'xt')", 'E565:')  endfunc +" Test for the :verbose command +func Test_verbose_cmd() +  call assert_equal(['  verbose=1'], split(execute('verbose set vbs'), "\n")) +  call assert_equal(['  verbose=0'], split(execute('0verbose set vbs'), "\n")) +  let l = execute("4verbose set verbose | set verbose") +  call assert_equal(['  verbose=4', '  verbose=0'], split(l, "\n")) +endfunc + +" Test for the :delete command and the related abbreviated commands +func Test_excmd_delete() +  new +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['^Ibar$'], split(execute('dl'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['^Ibar$'], split(execute('dell'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['^Ibar$'], split(execute('delel'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['^Ibar$'], split(execute('deletl'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['^Ibar$'], split(execute('deletel'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('dp'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('dep'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('delp'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('delep'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('deletp'), "\n")) +  call setline(1, ['foo', "\tbar"]) +  call assert_equal(['        bar'], split(execute('deletep'), "\n")) +  close! +endfunc + +" Test for commands that are blocked in a sandbox +func Sandbox_tests() +  call assert_fails("call histadd(':', 'ls')", 'E48:') +  call assert_fails("call mkdir('Xdir')", 'E48:') +  call assert_fails("call rename('a', 'b')", 'E48:') +  call assert_fails("call setbufvar(1, 'myvar', 1)", 'E48:') +  call assert_fails("call settabvar(1, 'myvar', 1)", 'E48:') +  call assert_fails("call settabwinvar(1, 1, 'myvar', 1)", 'E48:') +  call assert_fails("call setwinvar(1, 'myvar', 1)", 'E48:') +  call assert_fails("call timer_start(100, '')", 'E48:') +  if has('channel') +    call assert_fails("call prompt_setcallback(1, '')", 'E48:') +    call assert_fails("call prompt_setinterrupt(1, '')", 'E48:') +    call assert_fails("call prompt_setprompt(1, '')", 'E48:') +  endif +  call assert_fails("let $TESTVAR=1", 'E48:') +  call assert_fails("call feedkeys('ivim')", 'E48:') +  call assert_fails("source! Xfile", 'E48:') +  call assert_fails("call delete('Xfile')", 'E48:') +  call assert_fails("call writefile([], 'Xfile')", 'E48:') +  call assert_fails('!ls', 'E48:') +  " call assert_fails('shell', 'E48:') +  call assert_fails('stop', 'E48:') +  call assert_fails('exe "normal \<C-Z>"', 'E48:') +  " set insertmode +  " call assert_fails('call feedkeys("\<C-Z>", "xt")', 'E48:') +  " set insertmode& +  call assert_fails('suspend', 'E48:') +  call assert_fails('call system("ls")', 'E48:') +  call assert_fails('call systemlist("ls")', 'E48:') +  if has('clientserver') +    call assert_fails('let s=remote_expr("gvim", "2+2")', 'E48:') +    if !has('win32') +      " remote_foreground() doesn't thrown an error message on MS-Windows +      call assert_fails('call remote_foreground("gvim")', 'E48:') +    endif +    call assert_fails('let s=remote_peek("gvim")', 'E48:') +    call assert_fails('let s=remote_read("gvim")', 'E48:') +    call assert_fails('let s=remote_send("gvim", "abc")', 'E48:') +    call assert_fails('let s=server2client("gvim", "abc")', 'E48:') +  endif +  if has('terminal') +    call assert_fails('terminal', 'E48:') +    call assert_fails('call term_start("vim")', 'E48:') +    call assert_fails('call term_dumpwrite(1, "Xfile")', 'E48:') +  endif +  if has('channel') +    call assert_fails("call ch_logfile('chlog')", 'E48:') +    call assert_fails("call ch_open('localhost:8765')", 'E48:') +  endif +  if has('job') +    call assert_fails("call job_start('vim')", 'E48:') +  endif +  if has('unix') && has('libcall') +    call assert_fails("echo libcall('libc.so', 'getenv', 'HOME')", 'E48:') +  endif +  if has('unix') +    call assert_fails('cd `pwd`', 'E48:') +  endif +endfunc + +func Test_sandbox() +  sandbox call Sandbox_tests() +endfunc +  func Test_not_break_expression_register()    call setreg('=', '1+1')    if 0 diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim index 48dce25bb3..d86fea4f45 100644 --- a/src/nvim/testdir/test_expand.vim +++ b/src/nvim/testdir/test_expand.vim @@ -1,5 +1,7 @@  " Test for expanding file names +source shared.vim +  func Test_with_directories()    call mkdir('Xdir1')    call mkdir('Xdir2') @@ -79,5 +81,48 @@ func Test_expandcmd()    call assert_fails('call expandcmd("make <afile>")', 'E495:')    enew    call assert_fails('call expandcmd("make %")', 'E499:') -  close +  let $FOO="blue\tsky" +  call setline(1, "$FOO") +  call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>')) +  unlet $FOO +  close! +endfunc + +" Test for expanding <sfile>, <slnum> and <sflnum> outside of sourcing a script +func Test_source_sfile() +  let lines =<< trim [SCRIPT] +    :call assert_fails('echo expandcmd("<sfile>")', 'E498:') +    :call assert_fails('echo expandcmd("<slnum>")', 'E842:') +    :call assert_fails('echo expandcmd("<sflnum>")', 'E961:') +    :call assert_fails('call expandcmd("edit <cfile>")', 'E446:') +    :call assert_fails('call expandcmd("edit #")', 'E194:') +    :call assert_fails('call expandcmd("edit #<2")', 'E684:') +    :call assert_fails('call expandcmd("edit <cword>")', 'E348:') +    :call assert_fails('call expandcmd("edit <cexpr>")', 'E348:') +    :call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:') +    :call writefile(v:errors, 'Xresult') +    :qall! + +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -s Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc + +" Test for expanding filenames multiple times in a command line +func Test_expand_filename_multicmd() +  edit foo +  call setline(1, 'foo!') +  new +  call setline(1, 'foo!') +  new <cword> | new <cWORD> +  call assert_equal(4, winnr('$')) +  call assert_equal('foo!', bufname(winbufnr(1))) +  call assert_equal('foo', bufname(winbufnr(2))) +  %bwipe!  endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index 1b0f49ca51..c6e781a1ef 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -237,7 +237,7 @@ func Test_file_changed_dialog()    sleep 2    silent !touch Xchanged_d    let v:warningmsg = '' -  checktime +  checktime Xchanged_d    call assert_equal('', v:warningmsg)    call assert_equal(1, line('$'))    call assert_equal('new line', getline(1)) diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 00ba548e97..dee04e66dc 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -757,6 +757,30 @@ func Test_setfiletype_completion()    call assert_equal('"setfiletype java javacc javascript javascriptreact', @:)  endfunc +" Test for ':filetype detect' command for a buffer without a file +func Test_emptybuf_ftdetect() +  new +  call setline(1, '#!/bin/sh') +  call assert_equal('', &filetype) +  filetype detect +  call assert_equal('sh', &filetype) +  close! +endfunc + +" Test for ':filetype indent on' and ':filetype indent off' commands +func Test_filetype_indent_off() +  new Xtest.vim +  filetype indent on +  call assert_equal(1, g:did_indent_on) +  call assert_equal(['filetype detection:ON  plugin:OFF  indent:ON'], +        \ execute('filetype')->split("\n")) +  filetype indent off +  call assert_equal(0, exists('g:did_indent_on')) +  call assert_equal(['filetype detection:ON  plugin:OFF  indent:OFF'], +        \ execute('filetype')->split("\n")) +  close +endfunc +  """""""""""""""""""""""""""""""""""""""""""""""""  " Tests for specific extensions and filetypes.  " Keep sorted. diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index d465e48c7b..dae164b11c 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -45,6 +45,14 @@ func Test_filter_fails()    call assert_fails('filter /pat', 'E476:')    call assert_fails('filter /pat/', 'E476:')    call assert_fails('filter /pat/ asdf', 'E492:') +  " Using assert_fails() causes E476 instead of E866. So use a try-catch. +  let caught_e866 = 0 +  try +    filter /\@>b/ ls +  catch /E866:/ +    let caught_e866 = 1 +  endtry +  call assert_equal(1, caught_e866)    call assert_fails('filter!', 'E471:')    call assert_fails('filter! pat', 'E476:') diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index 1684c5d30a..0f4b30aec2 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -193,12 +193,14 @@ func Test_find_cmd()    set path=.,./**/*    call CreateFiles()    cd Xdir1 +    " Test for :find    find foo    call assert_equal('foo', expand('%:.'))    2find foo    call assert_equal('Xdir2/foo', expand('%:.'))    call assert_fails('3find foo', 'E347:') +    " Test for :sfind    enew    sfind barfoo @@ -207,6 +209,7 @@ func Test_find_cmd()    close    call assert_fails('sfind baz', 'E345:')    call assert_equal(2, winnr('$')) +    " Test for :tabfind    enew    tabfind foobar @@ -215,7 +218,8 @@ func Test_find_cmd()    tabclose    call assert_fails('tabfind baz', 'E345:')    call assert_equal(1, tabpagenr('$')) -  " call chdir(save_dir) + +  call chdir(save_dir)    exe 'cd ' . save_dir    call CleanFiles()    let &path = save_path diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index ad561baf4a..feddf85346 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -66,6 +66,18 @@ func Test_global_print()    close!  endfunc +" Test for global command with newline character +func Test_global_newline() +  new +  call setline(1, ['foo']) +  exe "g/foo/s/f/h/\<NL>s/o$/w/" +  call assert_equal('how', getline(1)) +  call setline(1, ["foo\<NL>bar"]) +  exe "g/foo/s/foo\\\<NL>bar/xyz/" +  call assert_equal('xyz', getline(1)) +  close! +endfunc +  func Test_wrong_delimiter()    call assert_fails('g x^bxd', 'E146:')  endfunc diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index ece8ccf215..e84726bbfc 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -125,6 +125,11 @@ func Test_help_tagjump()    call assert_true(getline('.') =~ '\*/\\bar\*')    helpclose +  help \_$ +  call assert_equal("help", &filetype) +  call assert_true(getline('.') =~ '\*/\\_$\*') +  helpclose +    help CTRL-\_CTRL-N    call assert_equal("help", &filetype)    call assert_true(getline('.') =~ '\*CTRL-\\_CTRL-N\*') diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index 96006ac7e7..feb521e232 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -114,6 +114,7 @@ function Test_Search_history_window()    bwipe!  endfunc +" Test for :history command option completion  function Test_history_completion()    call feedkeys(":history \<C-A>\<C-B>\"\<CR>", 'tx')    call assert_equal('"history / : = > ? @ all cmd debug expr input search', @:) @@ -122,8 +123,9 @@ endfunc  " Test for increasing the 'history' option value  func Test_history_size()    let save_histsz = &history -  call histdel(':')    set history=10 +  call histadd(':', 'ls') +  call histdel(':')    for i in range(1, 5)      call histadd(':', 'cmd' .. i)    endfor @@ -173,6 +175,53 @@ func Test_history_search()    call assert_equal(['pat2', 'pat1', ''], g:pat)    cunmap <F2>    delfunc SavePat + +  " Search for a pattern that is not present in the history +  call assert_beeps('call feedkeys("/a1b2\<Up>\<CR>", "xt")') + +  " Recall patterns with 'history' set to 0 +  set history=0 +  let @/ = 'abc' +  let cmd = 'call feedkeys("/\<Up>\<Down>\<S-Up>\<S-Down>\<CR>", "xt")' +  call assert_fails(cmd, 'E486:') +  set history& + +  " Recall patterns till the end of history +  set history=4 +  call histadd('/', 'pat') +  call histdel('/') +  call histadd('/', 'pat1') +  call histadd('/', 'pat2') +  call assert_beeps('call feedkeys("/\<Up>\<Up>\<Up>\<C-U>\<cr>", "xt")') +  call assert_beeps('call feedkeys("/\<Down><cr>", "xt")') + +  " Test for wrapping around the history list +  for i in range(3, 7) +    call histadd('/', 'pat' .. i) +  endfor +  let upcmd = "\<up>\<up>\<up>\<up>\<up>" +  let downcmd = "\<down>\<down>\<down>\<down>\<down>" +  try +    call feedkeys("/" .. upcmd .. "\<cr>", 'xt') +  catch /E486:/ +  endtry +  call assert_equal('pat4', @/) +  try +    call feedkeys("/" .. upcmd .. downcmd .. "\<cr>", 'xt') +  catch /E486:/ +  endtry +  call assert_equal('pat4', @/) + +  " Test for changing the search command separator in the history +  call assert_fails('call feedkeys("/def/\<cr>", "xt")', 'E486:') +  call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:') +  call assert_equal('def?', histget('/', -1)) + +  call assert_fails('call feedkeys("/ghi?\<cr>", "xt")', 'E486:') +  call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:') +  call assert_equal('ghi\?', histget('/', -1)) + +  set history&  endfunc  " Test for making sure the key value is not stored in history diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim index 12fe52f057..2559654f25 100644 --- a/src/nvim/testdir/test_increment.vim +++ b/src/nvim/testdir/test_increment.vim @@ -776,6 +776,14 @@ func Test_increment_empty_line()    call setline(1, ['0', '0', '0', '0', '0', '0', ''])    exe "normal Gvgg\<C-A>"    call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7)) + +  " Ctrl-A/Ctrl-X should do nothing in operator pending mode +  %d +  call setline(1, 'one two') +  exe "normal! c\<C-A>l" +  exe "normal! c\<C-X>l" +  call assert_equal('one two', getline(1)) +    bwipe!  endfunc diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim index ac6ef8f29f..1f7a0825a5 100644 --- a/src/nvim/testdir/test_join.vim +++ b/src/nvim/testdir/test_join.vim @@ -437,5 +437,11 @@ func Test_join_lines()    call setline(1, ['a', 'b', '', 'c', 'd'])    normal 5J    call assert_equal('a b c d', getline(1)) +  call setline(1, ['a', 'b', 'c']) +  2,2join +  call assert_equal(['a', 'b', 'c'], getline(1, '$')) +  call assert_equal(2, line('.')) +  2join +  call assert_equal(['a', 'b c'], getline(1, '$'))    bwipe!  endfunc diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index e2b7554797..a34a950526 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -643,6 +643,13 @@ func Test_map_error()    map <expr> ,f abc    call assert_fails('normal ,f', 'E121:')    unmap <expr> ,f + +  " Recursive use of :normal in a map +  set maxmapdepth=100 +  map gq :normal gq<CR> +  call assert_fails('normal gq', 'E192:') +  unmap gq +  set maxmapdepth&  endfunc  " Test for <special> key mapping diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 608f9883c2..74e63d9d69 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -215,6 +215,10 @@ func Test_mark_error()    call assert_fails('mark', 'E471:')    call assert_fails('mark xx', 'E488:')    call assert_fails('mark _', 'E191:') +  call assert_beeps('normal! m~') + +  call setpos("'k", [0, 100, 1, 0]) +  call assert_fails("normal 'k", 'E19:')  endfunc  " Test for :lockmarks when pasting content @@ -241,6 +245,29 @@ func Test_marks_k_cmd()    close!  endfunc +" Test for file marks (A-Z) +func Test_file_mark() +  new Xone +  call setline(1, ['aaa', 'bbb']) +  norm! G$mB +  w! +  new Xtwo +  call setline(1, ['ccc', 'ddd']) +  norm! GmD +  w! + +  enew +  normal! `B +  call assert_equal('Xone', bufname()) +  call assert_equal([2, 3], [line('.'), col('.')]) +  normal! 'D +  call assert_equal('Xtwo', bufname()) +  call assert_equal([2, 1], [line('.'), col('.')]) + +  call delete('Xone') +  call delete('Xtwo') +endfunc +  " Test for the getmarklist() function  func Test_getmarklist()    new diff --git a/src/nvim/testdir/test_move.vim b/src/nvim/testdir/test_move.vim index f666a904b0..8c40369dbd 100644 --- a/src/nvim/testdir/test_move.vim +++ b/src/nvim/testdir/test_move.vim @@ -38,6 +38,7 @@ func Test_move()    call assert_fails("move -100", 'E16:')    call assert_fails("move +100", 'E16:')    call assert_fails('move', 'E16:') +  call assert_fails("move 'r", 'E20:')    %bwipeout!  endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index f146726b7b..2d5b66df26 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1,6 +1,8 @@  " Test for various Normal mode commands  source shared.vim +source check.vim +source view_util.vim  func Setup_NewWindow()    10new @@ -53,7 +55,7 @@ func OpfuncDummy(type, ...)    let g:bufnr=bufnr('%')  endfunc -fun! Test_normal00_optrans() +func Test_normal00_optrans()    new    call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line'])    1 @@ -95,6 +97,12 @@ func Test_normal01_keymodel()    50    call feedkeys("\<S-Up>y", 'tx')    call assert_equal(['49', '5'], getreg(0, 0, 1)) +  " Use the different Shift special keys +  50 +  call feedkeys("\<S-Right>\<S-Left>\<S-Up>\<S-Down>\<S-Home>\<S-End>y", 'tx') +  call assert_equal(['50'], getline("'<", "'>")) +  call assert_equal(['50', ''], getreg(0, 0, 1)) +    " Do not start visual mode when keymodel=    set keymodel=    50 @@ -486,8 +494,8 @@ func Test_normal11_showcmd()    bw!  endfunc +" Test for nv_error and normal command errors  func Test_normal12_nv_error() -  " Test for nv_error    10new    call setline(1, range(1,5))    " should not do anything, just beep @@ -497,6 +505,22 @@ func Test_normal12_nv_error()    call assert_beeps("normal! g\<C-A>")    call assert_beeps("normal! g\<C-X>")    call assert_beeps("normal! g\<C-B>") +  " call assert_beeps("normal! vQ\<Esc>") +  call assert_beeps("normal! 2[[") +  call assert_beeps("normal! 2]]") +  call assert_beeps("normal! 2[]") +  call assert_beeps("normal! 2][") +  call assert_beeps("normal! 4[z") +  call assert_beeps("normal! 4]z") +  call assert_beeps("normal! 4[c") +  call assert_beeps("normal! 4]c") +  call assert_beeps("normal! 200%") +  call assert_beeps("normal! %") +  call assert_beeps("normal! 2{") +  call assert_beeps("normal! 2}") +  call assert_beeps("normal! r\<Right>") +  call assert_beeps("normal! 8ry") +  call assert_beeps('normal! "@')    bw!  endfunc @@ -658,6 +682,13 @@ func Test_normal16_z_scroll_hor()    $put =lineB    1d +  " Test for zl and zh with a count +  norm! 0z10l +  call assert_equal([11, 1], [col('.'), wincol()]) +  norm! z4h +  call assert_equal([11, 5], [col('.'), wincol()]) +  normal! 2gg +    " Test for zl    1    norm! 5zl @@ -780,6 +811,27 @@ func Test_normal17_z_scroll_hor2()    bw!  endfunc +" Test for H, M and L commands with folds +func Test_scroll_cmds() +  15new +  call setline(1, range(1, 100)) +  exe "normal! 30ggz\<CR>" +  set foldenable +  33,36fold +  40,43fold +  46,49fold +  let h = winheight(0) +  " Top of the screen = 30 +  " Folded lines = 9 +  " Bottom of the screen = 30 + h + 9 - 1 +  normal! 4L +  call assert_equal(35 + h, line('.')) +  normal! 4H +  call assert_equal(33, line('.')) +  set foldenable& +  close! +endfunc +  func Test_normal18_z_fold()    " basic tests for foldopen/folddelete    if !has("folding") @@ -789,6 +841,9 @@ func Test_normal18_z_fold()    50    setl foldenable fdm=marker foldlevel=5 +  call assert_beeps('normal! zj') +  call assert_beeps('normal! zk') +    " Test for zF    " First fold    norm! 4zF @@ -1221,6 +1276,9 @@ func Test_normal22_zet()    let a = readfile('Xfile_Test_normal22_zet')    call assert_equal(['1', '2'], a) +  " Unsupported Z command +  call assert_beeps('normal! ZW') +    " Nvim: This sometimes hangs the TSAN build.    " for file in ['Xfile_Test_normal22_zet']    "   call delete(file) @@ -1289,6 +1347,15 @@ func Test_normal23_K()      call assert_match("man --pager=cat 'man'", a)    endif +  " Error cases +  call setline(1, '#$#') +  call assert_fails('normal! ggK', 'E349:') +  call setline(1, '---') +  call assert_fails('normal! ggv2lK', 'E349:') +  call setline(1, ['abc', 'xyz']) +  call assert_fails("normal! gg2lv2h\<C-]>", 'E426:') +  call assert_beeps("normal! ggVjK") +    " clean up    let &keywordprg = k    bw! @@ -1499,12 +1566,27 @@ func Test_normal28_parenthesis()    norm! $d(    call assert_equal(['With some sentences!', '', ' ', '', 'This is a long sentence', ''], getline(1, '$')) +  " It is an error if a next sentence is not found +  %d +  call setline(1, '.SH') +  call assert_beeps('normal )') + +  " Jumping to a fold should open the fold +  call setline(1, ['', '', 'one', 'two', 'three']) +  set foldenable +  2,$fold +  call feedkeys(')', 'xt') +  call assert_equal(3, line('.')) +  call assert_equal(1, foldlevel('.')) +  call assert_equal(-1, foldclosed('.')) +  set foldenable& +    " clean up    bw!  endfunc -fun! Test_normal29_brace() -  " basic test for { and } movements +" Test for { and } paragraph movements +func Test_normal29_brace()    let text =<< trim [DATA]      A paragraph begins after each empty line, and also at each of a set of      paragraph macros, specified by the pairs of characters in the 'paragraphs' @@ -1657,12 +1739,24 @@ fun! Test_normal29_brace()    " [DATA]    " call assert_equal(expected, getline(1, '$')) +  " Jumping to a fold should open the fold +  " %d +  " call setline(1, ['', 'one', 'two', '']) +  " set foldenable +  " 2,$fold +  " call feedkeys('}', 'xt') +  " call assert_equal(4, line('.')) +  " call assert_equal(1, foldlevel('.')) +  " call assert_equal(-1, foldclosed('.')) +  " set foldenable& +    " clean up    set cpo-={    bw!  endfunc -fun! Test_normal30_changecase() +" Test for ~ command +func Test_normal30_changecase()    new    call append(0, 'This is a simple test: äüöß')    norm! 1ggVu @@ -1682,8 +1776,23 @@ fun! Test_normal30_changecase()    norm! V~    call assert_equal('THIS IS A simple test: äüöss', getline('.')) -  " Turkish ASCII turns to multi-byte.  On some systems Turkish locale -  " is available but toupper()/tolower() don't do the right thing. +  " Test for changing case across lines using 'whichwrap' +  call setline(1, ['aaaaaa', 'aaaaaa']) +  normal! gg10~ +  call assert_equal(['AAAAAA', 'aaaaaa'], getline(1, 2)) +  set whichwrap+=~ +  normal! gg10~ +  call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2)) +  set whichwrap& + +  " clean up +  bw! +endfunc + +" Turkish ASCII turns to multi-byte.  On some systems Turkish locale +" is available but toupper()/tolower() don't do the right thing. +func Test_normal_changecase_turkish() +  new    try      lang tr_TR.UTF-8      set casemap= @@ -1727,21 +1836,11 @@ fun! Test_normal30_changecase()      " can't use Turkish locale      throw 'Skipped: Turkish locale not available'    endtry - -  call setline(1, ['aaaaaa', 'aaaaaa']) -  normal! gg10~ -  call assert_equal(['AAAAAA', 'aaaaaa'], getline(1, 2)) -  set whichwrap+=~ -  normal! gg10~ -  call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2)) -  set whichwrap& - -  " clean up -  bw! +  close!  endfunc -fun! Test_normal31_r_cmd() -  " Test for r command +" Test for r (replace) command +func Test_normal31_r_cmd()    new    call append(0, 'This is a simple test: abcd')    exe "norm! 1gg$r\<cr>" @@ -1760,6 +1859,22 @@ fun! Test_normal31_r_cmd()    exe "norm! 1gg05rf"    call assert_equal('fffffis a', getline(1)) +  " When replacing characters, copy characters from above and below lines +  " using CTRL-Y and CTRL-E. +  " Different code paths are used for utf-8 and latin1 encodings +  set showmatch +  for enc in ['latin1', 'utf-8'] +    enew! +    let &encoding = enc +    call setline(1, [' {a}', 'xxxxxxxxxx', '      [b]']) +    exe "norm! 2gg5r\<C-Y>l5r\<C-E>" +    call assert_equal(' {a}x [b]x', getline(2)) +  endfor +  set showmatch& + +  " r command should fail in operator pending mode +  call assert_beeps('normal! cr') +    " clean up    set noautoindent    bw! @@ -1783,7 +1898,7 @@ endfunc  " Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,  " gi and gI commands -fun! Test_normal33_g_cmd2() +func Test_normal33_g_cmd2()    if !has("jumplist")      return    endif @@ -1832,6 +1947,16 @@ fun! Test_normal33_g_cmd2()    norm! g&    call assert_equal(['11', '22', '33', '44', '55', '66', '77', '88', '9', '110', 'a', 'b', 'c', 'dd'], getline(1, '$')) +  " Jumping to a fold using gg should open the fold +  set foldenable +  set foldopen+=jump +  5,8fold +  call feedkeys('6gg', 'xt') +  call assert_equal(1, foldlevel('.')) +  call assert_equal(-1, foldclosed('.')) +  set foldopen-=jump +  set foldenable& +    " Test for gv    %d    call append('$', repeat(['abcdefgh'], 8)) @@ -1975,6 +2100,10 @@ fun! Test_normal33_g_cmd2()    call assert_equal('foo       first line', getline(1))    set virtualedit& +  " Test for aboring a g command using CTRL-\ CTRL-G +  exe "normal! g\<C-\>\<C-G>" +  call assert_equal('foo       first line', getline('.')) +    " clean up    bw!  endfunc @@ -1995,6 +2124,10 @@ func Test_g_ctrl_g()    let a = execute(":norm! g\<c-g>")    call assert_equal("\n--No lines in buffer--", a) +  " Test for CTRL-G (same as :file) +  let a = execute(":norm! \<c-g>") +  call assert_equal("\n\n\"[No Name]\" --No lines in buffer--", a) +    call setline(1, ['first line', 'second line'])    " Test g CTRL-g with dos, mac and unix file type. @@ -2063,7 +2196,7 @@ func Test_g_ctrl_g()  endfunc  " Test for g8 -fun! Test_normal34_g_cmd3() +func Test_normal34_g_cmd3()    new    let a=execute(':norm! 1G0g8')    call assert_equal("\nNUL", a) @@ -2113,7 +2246,7 @@ func Test_normal_8g8()  endfunc  " Test for g< -fun! Test_normal35_g_cmd4() +func Test_normal35_g_cmd4()    " Cannot capture its output,    " probably a bug, therefore, test disabled:    throw "Skipped: output of g< can't be tested currently" @@ -2123,7 +2256,7 @@ fun! Test_normal35_g_cmd4()  endfunc  " Test for gp gP go -fun! Test_normal36_g_cmd5() +func Test_normal36_g_cmd5()    new    call append(0, 'abcdefghijklmnopqrstuvwxyz')    set ff=unix @@ -2162,7 +2295,7 @@ fun! Test_normal36_g_cmd5()  endfunc  " Test for gt and gT -fun! Test_normal37_g_cmd6() +func Test_normal37_g_cmd6()    tabnew 1.txt    tabnew 2.txt    tabnew 3.txt @@ -2189,7 +2322,7 @@ fun! Test_normal37_g_cmd6()  endfunc  " Test for <Home> and <C-Home> key -fun! Test_normal38_nvhome() +func Test_normal38_nvhome()    new    call setline(1, range(10))    $ @@ -2211,8 +2344,21 @@ fun! Test_normal38_nvhome()    bw!  endfunc +" Test for <End> and <C-End> keys +func Test_normal_nvend() +  new +  call setline(1, map(range(1, 10), '"line" .. v:val')) +  exe "normal! \<End>" +  call assert_equal(5, col('.')) +  exe "normal! 4\<End>" +  call assert_equal([4, 5], [line('.'), col('.')]) +  exe "normal! \<C-End>" +  call assert_equal([10, 6], [line('.'), col('.')]) +  close! +endfunc +  " Test for cw cW ce -fun! Test_normal39_cw() +func Test_normal39_cw()    " Test for cw and cW on whitespace    " and cpo+=w setting    new @@ -2253,7 +2399,7 @@ fun! Test_normal39_cw()  endfunc  " Test for CTRL-\ commands -fun! Test_normal40_ctrl_bsl() +func Test_normal40_ctrl_bsl()    new    call append(0, 'here      are   some words')    exe "norm! 1gg0a\<C-\>\<C-N>" @@ -2271,14 +2417,19 @@ fun! Test_normal40_ctrl_bsl()    exe ":norm! \<c-\>\<c-n>dw"    " set noim    call assert_equal('are   some words', getline(1)) -  " call assert_false(&insertmode) +  call assert_false(&insertmode) +  call assert_beeps("normal! \<C-\>\<C-A>") + +  " Using CTRL-\ CTRL-N in cmd window should close the window +  call feedkeys("q:\<C-\>\<C-N>", 'xt') +  call assert_equal('', getcmdwintype())    " clean up    bw!  endfunc  " Test for <c-r>=, <c-r><c-r>= and <c-r><c-o>= in insert mode -fun! Test_normal41_insert_reg() +func Test_normal41_insert_reg()    new    set sts=2 sw=2 ts=8 tw=0    call append(0, ["aaa\tbbb\tccc", '', '', '']) @@ -2334,7 +2485,7 @@ func Test_normal42_halfpage()  endfunc  " Tests for text object aw -fun! Test_normal43_textobject1() +func Test_normal43_textobject1()    new    call append(0, ['foobar,eins,foobar', 'foo,zwei,foo    '])    " diw @@ -2505,7 +2656,7 @@ func Test_normal49_counts()  endfunc  func Test_normal50_commandline() -  if !has("timers") || !has("cmdline_hist") || !has("vertsplit") +  if !has("timers") || !has("cmdline_hist")      return    endif    func! DoTimerWork(id) @@ -2567,6 +2718,8 @@ func Test_normal52_rl()    call assert_equal(19, col('.'))    call feedkeys("\<right>", 'tx')    call assert_equal(18, col('.')) +  call feedkeys("\<left>", 'tx') +  call assert_equal(19, col('.'))    call feedkeys("\<s-right>", 'tx')    call assert_equal(13, col('.'))    call feedkeys("\<c-right>", 'tx') @@ -2690,6 +2843,8 @@ func Test_changelist()    normal g;    call assert_equal([2, 2], [line('.'), col('.')])    call assert_fails('normal g;', 'E662:') +  new +  call assert_fails('normal g;', 'E664:')    %bwipe!    let &ul = save_ul  endfunc @@ -2736,6 +2891,10 @@ endfunc  " Jumping to beginning and end of methods in Java-like languages  func Test_java_motion()    new +  call assert_beeps('normal! [m') +  call assert_beeps('normal! ]m') +  call assert_beeps('normal! [M') +  call assert_beeps('normal! ]M')    a  Piece of Java  { @@ -2810,7 +2969,7 @@ Piece of Java    close!  endfunc -fun! Test_normal_gdollar_cmd() +func Test_normal_gdollar_cmd()    if !has("jumplist")      return    endif @@ -2935,6 +3094,29 @@ func Test_normal_cpo_minus()    close!  endfunc +" Test for displaying dollar when changing text ('$' flag in 'cpoptions') +func Test_normal_cpo_dollar() +  throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua' +  new +  let g:Line = '' +  func SaveFirstLine() +    let g:Line = Screenline(1) +    return '' +  endfunc +  inoremap <expr> <buffer> <F2> SaveFirstLine() +  call test_override('redraw_flag', 1) +  set cpo+=$ +  call setline(1, 'one two three') +  redraw! +  exe "normal c2w\<F2>vim" +  call assert_equal('one tw$ three', g:Line) +  call assert_equal('vim three', getline(1)) +  set cpo-=$ +  call test_override('ALL', 0) +  delfunc SaveFirstLine +  %bw! +endfunc +  " Test for using : to run a multi-line Ex command in operator pending mode  func Test_normal_yank_with_excmd()    new @@ -2975,12 +3157,28 @@ func Test_wincmd_with_count()  endfunc  " Test for 'b', 'B' 'ge' and 'gE' commands -func Test_backward_motion() +func Test_horiz_motion() +  new    normal! gg    call assert_beeps('normal! b')    call assert_beeps('normal! B')    call assert_beeps('normal! gE')    call assert_beeps('normal! ge') +  " <S-Backspace> moves one word left and <C-Backspace> moves one WORD left +  call setline(1, 'one ,two ,three') +  exe "normal! $\<S-BS>" +  call assert_equal(11, col('.')) +  exe "normal! $\<C-BS>" +  call assert_equal(10, col('.')) +  close! +endfunc + +" Test for using a : command in operator pending mode +func Test_normal_colon_op() +  new +  call setline(1, ['one', 'two']) +  call assert_beeps("normal! Gc:d\<CR>") +  close!  endfunc  " Some commands like yy, cc, dd, >>, << and !! accept a count after diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 7b6cfa6bb4..2840378b97 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -290,9 +290,10 @@ func Test_set_errors()    call assert_fails('set regexpengine=3', 'E474:')    call assert_fails('set history=10001', 'E474:')    call assert_fails('set numberwidth=21', 'E474:') -  call assert_fails('set colorcolumn=-a') -  call assert_fails('set colorcolumn=a') -  call assert_fails('set colorcolumn=1,') +  call assert_fails('set colorcolumn=-a', 'E474:') +  call assert_fails('set colorcolumn=a', 'E474:') +  call assert_fails('set colorcolumn=1,', 'E474:') +  call assert_fails('set colorcolumn=1;', 'E474:')    call assert_fails('set cmdheight=-1', 'E487:')    call assert_fails('set cmdwinheight=-1', 'E487:')    if has('conceal') @@ -343,9 +344,13 @@ func Test_set_errors()      call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:')      call assert_fails('set guicursor=i-ci', 'E545:')      call assert_fails('set guicursor=x', 'E545:') +    call assert_fails('set guicursor=x:', 'E546:')      call assert_fails('set guicursor=r-cr:horx', 'E548:')      call assert_fails('set guicursor=r-cr:hor0', 'E549:')    endif +  if has('mouseshape') +    call assert_fails('se mouseshape=i-r:x', 'E547:') +  endif    call assert_fails('set backupext=~ patchmode=~', 'E589:')    call assert_fails('set winminheight=10 winheight=9', 'E591:')    call assert_fails('set winminwidth=10 winwidth=9', 'E592:') @@ -736,7 +741,26 @@ func Test_buftype()    call setline(1, ['L1'])    set buftype=nowrite    call assert_fails('write', 'E382:') -  close! + +  " for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'terminal', 'prompt', 'popup'] +  for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'prompt'] +    exe 'set buftype=' .. val +    call writefile(['something'], 'XBuftype') +    call assert_fails('write XBuftype', 'E13:', 'with buftype=' .. val) +  endfor + +  call delete('XBuftype') +  bwipe! +endfunc + +" Test for the 'shell' option +func Test_shell() +  throw 'Skipped: Nvim does not have :shell' +  CheckUnix +  let save_shell = &shell +  set shell= +  call assert_fails('shell', 'E91:') +  let &shell = save_shell  endfunc  " Test for the 'shellquote' option diff --git a/src/nvim/testdir/test_plus_arg_edit.vim b/src/nvim/testdir/test_plus_arg_edit.vim index 64533e71cf..c52044d064 100644 --- a/src/nvim/testdir/test_plus_arg_edit.vim +++ b/src/nvim/testdir/test_plus_arg_edit.vim @@ -18,7 +18,7 @@ func Test_edit_bad()    e! ++enc=utf8 Xfile    call assert_equal('[?][?][???][??]', getline(1)) -  e! ++enc=utf8 ++bad=_ Xfile +  e! ++encoding=utf8 ++bad=_ Xfile    call assert_equal('[_][_][___][__]', getline(1))    e! ++enc=utf8 ++bad=drop Xfile diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index a46ef8b3fe..97af3699a8 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -114,6 +114,16 @@ func Test_put_p_indent_visual()    bwipe!  endfunc +" Test for deleting all the contents of a buffer with a put +func Test_put_visual_delete_all_lines() +  new +  call setline(1, ['one', 'two', 'three']) +  let @r = '' +  normal! VG"rgp +  call assert_equal(1, line('$')) +  close! +endfunc +  func Test_gp_with_count_leaves_cursor_at_end()    new    call setline(1, '<---->') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 4f83c45770..5649652fd2 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -499,6 +499,7 @@ func Xtest_browse(cchar)  		\ 'RegularLine2']    Xfirst +  call assert_fails('-5Xcc', 'E16:')    call assert_fails('Xprev', 'E553')    call assert_fails('Xpfile', 'E553')    Xnfile @@ -2891,6 +2892,21 @@ func Test_vimgrep_incsearch()    set noincsearch  endfunc +" Test vimgrep with the last search pattern not set +func Test_vimgrep_with_no_last_search_pat() +  let lines =<< trim [SCRIPT] +    call assert_fails('vimgrep // *', 'E35:') +    call writefile(v:errors, 'Xresult') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc +  " Test vimgrep without swap file  func Test_vimgrep_without_swap_file()    let lines =<< trim [SCRIPT] @@ -4400,6 +4416,20 @@ func Test_splitview()    call assert_equal(0, getloclist(0, {'winid' : 0}).winid)    new | only +  " Using :split or :vsplit from a quickfix window should behave like a :new +  " or a :vnew command +  copen +  split +  call assert_equal(3, winnr('$')) +  let l = getwininfo() +  call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix]) +  close +  copen +  vsplit +  let l = getwininfo() +  call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix]) +  new | only +    call delete('Xtestfile1')    call delete('Xtestfile2')  endfunc diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 45e60a6d44..82d250e8b3 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -122,7 +122,10 @@ endfunc  " Tests for regexp patterns without multi-byte support.  func Test_regexp_single_line_pat()    " tl is a List of Lists with: -  "    regexp engine +  "    regexp engines to test +  "       0 - test with 'regexpengine' values 0 and 1 +  "       1 - test with 'regexpengine' values 0 and 2 +  "       2 - test with 'regexpengine' values 0, 1 and 2    "    regexp pattern    "    text to test the pattern on    "    expected match (optional) @@ -143,6 +146,8 @@ func Test_regexp_single_line_pat()    call add(tl, [2, 'c*', 'abdef', ''])    call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])    call add(tl, [2, 'bc\+', 'abdef']) " no match +  " match newline character in a string +  call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])    " operator \|    call add(tl, [2, 'a\|ab', 'cabd', 'a']) " alternation is ordered @@ -566,6 +571,9 @@ func Test_regexp_single_line_pat()    " Test \%V atom    call add(tl, [2, '\%>70vGesamt', 'Jean-Michel Charlier & Victor Hubinon\Gesamtausgabe [Salleck]    Buck Danny {Jean-Michel Charlier & Victor Hubinon}\Gesamtausgabe', 'Gesamt']) +  " Test for ignoring case and matching repeated characters +  call add(tl, [2, '\cb\+', 'aAbBbBcC', 'bBbB']) +    " Run the tests    for t in tl      let re = t[0] @@ -625,6 +633,14 @@ endfunc  " Tests for multi-line regexp patterns without multi-byte support.  func Test_regexp_multiline_pat() +  " tl is a List of Lists with: +  "    regexp engines to test +  "       0 - test with 'regexpengine' values 0 and 1 +  "       1 - test with 'regexpengine' values 0 and 2 +  "       2 - test with 'regexpengine' values 0, 1 and 2 +  "    regexp pattern +  "    List with text to test the pattern on +  "    List with the expected match    let tl = []    " back references @@ -634,6 +650,70 @@ func Test_regexp_multiline_pat()    " line breaks    call add(tl, [2, '\S.*\nx', ['abc', 'def', 'ghi', 'xjk', 'lmn'], ['abc', 'def', 'XXjk', 'lmn']]) +  " Any single character or end-of-line +  call add(tl, [2, '\_.\+', ['a', 'b', 'c'], ['XX']]) +  " Any identifier or end-of-line +  call add(tl, [2, '\_i\+', ['a', 'b', ';', '2'], ['XX;XX']]) +  " Any identifier but excluding digits or end-of-line +  call add(tl, [2, '\_I\+', ['a', 'b', ';', '2'], ['XX;XX2XX']]) +  " Any keyword or end-of-line +  call add(tl, [2, '\_k\+', ['a', 'b', '=', '2'], ['XX=XX']]) +  " Any keyword but excluding digits or end-of-line +  call add(tl, [2, '\_K\+', ['a', 'b', '=', '2'], ['XX=XX2XX']]) +  " Any filename character or end-of-line +  call add(tl, [2, '\_f\+', ['a', 'b', '.', '5'], ['XX']]) +  " Any filename character but excluding digits or end-of-line +  call add(tl, [2, '\_F\+', ['a', 'b', '.', '5'], ['XX5XX']]) +  " Any printable character or end-of-line +  call add(tl, [2, '\_p\+', ['a', 'b', '=', '4'], ['XX']]) +  " Any printable character excluding digits or end-of-line +  call add(tl, [2, '\_P\+', ['a', 'b', '=', '4'], ['XX4XX']]) +  " Any whitespace character or end-of-line +  call add(tl, [2, '\_s\+', [' ', ' ', 'a', 'b'], ['XXaXXbXX']]) +  " Any non-whitespace character or end-of-line +  call add(tl, [2, '\_S\+', [' ', ' ', 'a', 'b'], [' XX XX']]) +  " Any decimal digit or end-of-line +  call add(tl, [2, '\_d\+', ['1', 'a', '2', 'b', '3'], ['XXaXXbXX']]) +  " Any non-decimal digit or end-of-line +  call add(tl, [2, '\_D\+', ['1', 'a', '2', 'b', '3'], ['1XX2XX3XX']]) +  " Any hexadecimal digit or end-of-line +  call add(tl, [2, '\_x\+', ['1', 'a', 'g', '9', '8'], ['XXgXX']]) +  " Any non-hexadecimal digit or end-of-line +  call add(tl, [2, '\_X\+', ['1', 'a', 'g', '9', '8'], ['1XXaXX9XX8XX']]) +  " Any octal digit or end-of-line +  call add(tl, [2, '\_o\+', ['0', '7', '8', '9', '0'], ['XX8XX9XX']]) +  " Any non-octal digit or end-of-line +  call add(tl, [2, '\_O\+', ['0', '7', '8', '9', '0'], ['0XX7XX0XX']]) +  " Any word character or end-of-line +  call add(tl, [2, '\_w\+', ['A', 'B', '=', 'C', 'D'], ['XX=XX']]) +  " Any non-word character or end-of-line +  call add(tl, [2, '\_W\+', ['A', 'B', '=', 'C', 'D'], ['AXXBXXCXXDXX']]) +  " Any head-of-word character or end-of-line +  call add(tl, [2, '\_h\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']]) +  " Any non-head-of-word character or end-of-line +  call add(tl, [2, '\_H\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']]) +  " Any alphabetic character or end-of-line +  call add(tl, [2, '\_a\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']]) +  " Any non-alphabetic character or end-of-line +  call add(tl, [2, '\_A\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']]) +  " Any lowercase character or end-of-line +  call add(tl, [2, '\_l\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']]) +  " Any non-lowercase character or end-of-line +  call add(tl, [2, '\_L\+', ['a', 'A', 'b', 'B'], ['aXXbXX']]) +  " Any uppercase character or end-of-line +  call add(tl, [2, '\_u\+', ['a', 'A', 'b', 'B'], ['aXXbXX']]) +  " Any non-uppercase character or end-of-line +  call add(tl, [2, '\_U\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']]) +  " Collection or end-of-line +  call add(tl, [2, '\_[a-z]\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']]) +  " start of line anywhere in the text +  call add(tl, [2, 'one\zs\_s*\_^\zetwo', +        \ ['', 'one', ' two', 'one', '', 'two'], +        \ ['', 'one', ' two', 'oneXXtwo']]) +  " end of line anywhere in the text +  call add(tl, [2, 'one\zs\_$\_s*two', +        \ ['', 'one', ' two', 'one', '', 'two'], ['', 'oneXX', 'oneXX']]) +    " Check that \_[0-9] matching EOL does not break a following \>    call add(tl, [2, '\<\(\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\.\)\{3\}\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\>', ['', 'localnet/192.168.0.1', ''], ['', 'localnet/XX', '']]) @@ -649,7 +729,7 @@ func Test_regexp_multiline_pat()      let before = t[2]      let after = t[3]      for engine in [0, 1, 2] -      if engine == 2 && re == 0 || engine == 1 && re ==1 +      if engine == 2 && re == 0 || engine == 1 && re == 1          continue        endif        let ®expengine = engine @@ -697,9 +777,8 @@ func Test_lookbehind_across_line()    bwipe!  endfunc -" Check matching Visual area -func Test_matching_visual_area() -  new +" Test for the \%V atom (match inside the visual area) +func Regex_Match_Visual_Area()    call append(0, ['Visual:', 'thexe the thexethe', 'andaxand andaxand',          \ 'oooxofor foroxooo', 'oooxofor foroxooo'])    call cursor(1, 1) @@ -708,12 +787,22 @@ func Test_matching_visual_area()    exe "normal jfx\<C-V>fxj:s/\\%Vo/O/g\<CR>"    call assert_equal(['Visual:', 'thexE thE thExethe', 'AndAxAnd AndAxAnd',          \ 'oooxOfOr fOrOxooo', 'oooxOfOr fOrOxooo', ''], getline(1, '$')) +  %d +endfunc + +" Check matching Visual area +func Test_matching_visual_area() +  new +  set regexpengine=1 +  call Regex_Match_Visual_Area() +  set regexpengine=2 +  call Regex_Match_Visual_Area() +  set regexpengine&    bwipe!  endfunc  " Check matching marks -func Test_matching_marks() -  new +func Regex_Mark()    call append(0, ['', '', '', 'Marks:', 'asdfSasdfsadfEasdf', 'asdfSas',          \ 'dfsadfEasdf', '', '', '', '', ''])    call cursor(4, 1) @@ -721,6 +810,15 @@ func Test_matching_marks()    exe "normal jfSmsj0fEme:.-4,.+6s/.\\%>'s\\_.*\\%<'e../again/\<CR>"    call assert_equal(['', '', '', 'Marks:', 'asdfhereasdf', 'asdfagainasdf',          \ '', '', '', '', '', ''], getline(1, '$')) +  %d +endfunc + +func Test_matching_marks() +  new +  set regexpengine=1 +  call Regex_Mark() +  set regexpengine=2 +  call Regex_Mark()    bwipe!  endfunc @@ -761,8 +859,7 @@ func Test_matching_curpos()  endfunc  " Test for matching the start and end of a buffer -func Test_start_end_of_buffer_match() -  new +func Regex_start_end_buffer()    call setline(1, repeat(['vim edit'], 20))    /\%^    call assert_equal([0, 1, 1, 0], getpos('.')) @@ -772,6 +869,15 @@ func Test_start_end_of_buffer_match()    call assert_equal([0, 20, 8, 0], getpos('.'))    exe "normal 6gg/..\\%$\<CR>"    call assert_equal([0, 20, 7, 0], getpos('.')) +  %d +endfunc + +func Test_start_end_of_buffer_match() +  new +  set regexpengine=1 +  call Regex_start_end_buffer() +  set regexpengine=2 +  call Regex_start_end_buffer()    bwipe!  endfunc @@ -784,10 +890,20 @@ endfunc  " Check for detecting error  func Test_regexp_error() -  set regexpengine=2 -  call assert_fails("call matchlist('x x', ' \\ze*')", 'E888:') -  call assert_fails("call matchlist('x x', ' \\zs*')", 'E888:') -  set re& +  call assert_fails("call matchlist('x x', '\\%#=1 \\zs*')", 'E888:') +  call assert_fails("call matchlist('x x', '\\%#=1 \\ze*')", 'E888:') +  call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:') +  call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:') +  call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:') +endfunc + +" Test for using the last substitute string pattern (~) +func Test_regexp_last_subst_string() +  new +  s/bar/baz/e +  call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=1\~"), "baz") +  call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=2\~"), "baz") +  close!  endfunc  " Check patterns matching cursor position. diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index b852cfd22f..abe28b77cd 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -416,6 +416,20 @@ func Test_put_reg_restart_mode()    bwipe!  endfunc +" Test for executing a register using :@ command +func Test_execute_register() +  call setreg('r', []) +  call assert_beeps('@r') +  let i = 1 +  let @q = 'let i+= 1' +  @q +  @ +  call assert_equal(3, i) + +  " cannot execute a register in operator pending mode +  call assert_beeps('normal! c@r') +endfunc +  " Test for getting register info  func Test_get_reginfo()    enew diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 454c956996..747fb0e384 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -19,9 +19,9 @@ func Test_search_cmdline()    set noincsearch    :1    call feedkeys("/foobar\<cr>", 'tx') -  call feedkeys("/the\<cr>",'tx') +  call feedkeys("/the\<cr>", 'tx')    call assert_equal('the', @/) -  call feedkeys("/thes\<C-P>\<C-P>\<cr>",'tx') +  call feedkeys("/thes\<C-P>\<C-P>\<cr>", 'tx')    call assert_equal('foobar', @/)    " Test 2 @@ -655,10 +655,49 @@ func Test_search_cmdline7()    bw!  endfunc -" Tests for regexp with various magic settings -func Test_search_regexp() -  enew! +func Test_search_cmdline8() +  " Highlighting is cleared in all windows +  " since hls applies to all windows +  CheckOption incsearch +  CheckFeature terminal +  CheckNotGui +  if has("win32") +    throw "Skipped: Bug with sending <ESC> to terminal window not fixed yet" +  endif + +  let h = winheight(0) +  if h < 3 +    return +  endif +  " Prepare buffer text +  let lines = ['abb vim vim vi', 'vimvivim'] +  call writefile(lines, 'Xsearch.txt') +  let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', 'Xsearch.txt'], {'term_rows': 3}) +  call WaitForAssert({-> assert_equal(lines, [term_getline(buf, 1), term_getline(buf, 2)])}) + +  call term_sendkeys(buf, ":set incsearch hlsearch\<cr>") +  call term_sendkeys(buf, ":14vsp\<cr>") +  call term_sendkeys(buf, "/vim\<cr>") +  call term_sendkeys(buf, "/b\<esc>") +  call term_sendkeys(buf, "gg0") +  call TermWait(buf, 250) +  let screen_line = term_scrape(buf, 1) +  let [a0,a1,a2,a3] = [screen_line[3].attr, screen_line[4].attr, +        \ screen_line[18].attr, screen_line[19].attr] +  call assert_notequal(a0, a1) +  call assert_notequal(a0, a3) +  call assert_notequal(a1, a2) +  call assert_equal(a0, a2) +  call assert_equal(a1, a3) +  " clean up +  call delete('Xsearch.txt') + +  bwipe! +endfunc + +" Tests for regexp with various magic settings +func Run_search_regexp_magic_opt()    put ='1 a aa abb abbccc'    exe 'normal! /a*b\{2}c\+/e' . "\<CR>"    call assert_equal([0, 2, 17, 0], getpos('.')) @@ -693,6 +732,18 @@ func Test_search_regexp()    exe 'normal! /\V[ab]\(\[xy]\)\1' . "\<CR>"    call assert_equal([0, 9, 7, 0], getpos('.')) +  %d +endfunc + +func Test_search_regexp() +  enew! + +  set regexpengine=1 +  call Run_search_regexp_magic_opt() +  set regexpengine=2 +  call Run_search_regexp_magic_opt() +  set regexpengine& +    set undolevels=100    put ='9 foobar'    put ='' @@ -700,12 +751,12 @@ func Test_search_regexp()    normal G    exe 'normal! dv?bar?' . "\<CR>"    call assert_equal('9 foo', getline('.')) -  call assert_equal([0, 10, 5, 0], getpos('.')) -  call assert_equal(10, line('$')) +  call assert_equal([0, 2, 5, 0], getpos('.')) +  call assert_equal(2, line('$'))    normal u    call assert_equal('9 foobar', getline('.')) -  call assert_equal([0, 10, 6, 0], getpos('.')) -  call assert_equal(11, line('$')) +  call assert_equal([0, 2, 6, 0], getpos('.')) +  call assert_equal(3, line('$'))    set undolevels&    enew! @@ -1433,7 +1484,7 @@ func Test_large_hex_chars2()  endfunc  func Test_one_error_msg() -  " This  was also giving an internal error +  " This was also giving an internal error    call assert_fails('call search(" \\((\\v[[=P=]]){185}+             ")', 'E871:')  endfunc @@ -1478,6 +1529,20 @@ func Test_search_match_at_curpos()    close!  endfunc +" Test for error cases with the search() function +func Test_search_errors() +  call assert_fails("call search('pat', [])", 'E730:') +  call assert_fails("call search('pat', 'b', {})", 'E728:') +  call assert_fails("call search('pat', 'b', 1, [])", 'E745:') +  call assert_fails("call search('pat', 'ns')", 'E475:') +  call assert_fails("call search('pat', 'mr')", 'E475:') + +  new +  call setline(1, ['foo', 'bar']) +  call assert_fails('call feedkeys("/foo/;/bar/;\<CR>", "tx")', 'E386:') +  bwipe! +endfunc +  func Test_search_display_pattern()    new    call setline(1, ['foo', 'bar', 'foobar']) @@ -1541,6 +1606,132 @@ func Test_search_special()    exe "norm /\x80PS"  endfunc +" Test for command failures when the last search pattern is not set. +" Need to run this in a new vim instance where last search pattern is not set. +func Test_search_with_no_last_pat() +  let lines =<< trim [SCRIPT] +    call assert_fails("normal i\<C-R>/\e", 'E35:') +    call assert_fails("exe '/'", 'E35:') +    call assert_fails("exe '?'", 'E35:') +    call assert_fails("/", 'E35:') +    call assert_fails("?", 'E35:') +    call assert_fails("normal n", 'E35:') +    call assert_fails("normal N", 'E35:') +    call assert_fails("normal gn", 'E35:') +    call assert_fails("normal gN", 'E35:') +    call assert_fails("normal cgn", 'E35:') +    call assert_fails("normal cgN", 'E35:') +    let p = [] +    let p = @/ +    call assert_equal('', p) +    call assert_fails("normal :\<C-R>/", 'E35:') +    call assert_fails("//p", 'E35:') +    call assert_fails(";//p", 'E35:') +    call assert_fails("??p", 'E35:') +    call assert_fails(";??p", 'E35:') +    call assert_fails('g//p', 'E476:') +    call assert_fails('v//p', 'E476:') +    call writefile(v:errors, 'Xresult') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') + +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc + +" Test for using tilde (~) atom in search. This should use the last used +" substitute pattern +func Test_search_tilde_pat() +  let lines =<< trim [SCRIPT] +    set regexpengine=1 +    call assert_fails('exe "normal /~\<CR>"', 'E33:') +    call assert_fails('exe "normal ?~\<CR>"', 'E33:') +    set regexpengine=2 +    call assert_fails('exe "normal /~\<CR>"', 'E383:') +    call assert_fails('exe "normal ?~\<CR>"', 'E383:') +    set regexpengine& +    call writefile(v:errors, 'Xresult') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc + +" Test for searching a pattern that is not present with 'nowrapscan' +func Test_search_pat_not_found() +  new +  set nowrapscan +  let @/ = '1abcxyz2' +  call assert_fails('normal n', 'E385:') +  call assert_fails('normal N', 'E384:') +  set wrapscan& +  close +endfunc + +" Test for v:searchforward variable +func Test_searchforward_var() +  new +  call setline(1, ['foo', '', 'foo']) +  call cursor(2, 1) +  let @/ = 'foo' +  let v:searchforward = 0 +  normal N +  call assert_equal(3, line('.')) +  call cursor(2, 1) +  let v:searchforward = 1 +  normal N +  call assert_equal(1, line('.')) +  close! +endfunc + +" Test for invalid regular expressions +func Test_invalid_regexp() +  set regexpengine=1 +  call assert_fails("call search(repeat('\\(.\\)', 10))", 'E51:') +  call assert_fails("call search('\\%(')", 'E53:') +  call assert_fails("call search('\\(')", 'E54:') +  call assert_fails("call search('\\)')", 'E55:') +  call assert_fails("call search('x\\@#')", 'E59:') +  call assert_fails('call search(''\v%(%(%(%(%(%(%(%(%(%(%(a){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}'')', 'E60:') +  call assert_fails("call search('a\\+*')", 'E61:') +  call assert_fails("call search('\\_m')", 'E63:') +  call assert_fails("call search('\\+')", 'E64:') +  call assert_fails("call search('\\1')", 'E65:') +  call assert_fails("call search('\\z\\(\\)')", 'E66:') +  call assert_fails("call search('\\z2')", 'E67:') +  call assert_fails("call search('\\zx')", 'E68:') +  call assert_fails("call search('\\%[ab')", 'E69:') +  call assert_fails("call search('\\%[]')", 'E70:') +  call assert_fails("call search('\\%a')", 'E71:') +  call assert_fails("call search('ab\\%[\\(cd\\)]')", 'E369:') +  call assert_fails("call search('ab\\%[\\%(cd\\)]')", 'E369:') +  set regexpengine=2 +  call assert_fails("call search('\\_')", 'E865:') +  call assert_fails("call search('\\+')", 'E866:') +  call assert_fails("call search('\\zx')", 'E867:') +  call assert_fails("call search('\\%a')", 'E867:') +  call assert_fails("call search('x\\@#')", 'E869:') +  call assert_fails("call search(repeat('\\(.\\)', 10))", 'E872:') +  call assert_fails("call search('\\_m')", 'E877:') +  call assert_fails("call search('\\%(')", 'E53:') +  call assert_fails("call search('\\(')", 'E54:') +  call assert_fails("call search('\\)')", 'E55:') +  call assert_fails("call search('\\z\\(\\)')", 'E66:') +  call assert_fails("call search('\\%[ab')", 'E69:') +  call assert_fails("call search('\\%[]')", 'E70:') +  call assert_fails("call search('\\%9999999999999999999999999999v')", 'E951:') +  set regexpengine& +  call assert_fails("call search('\\%#=3ab')", 'E864:') +endfunc +  " Test 'smartcase' with utf-8.  func Test_search_smartcase_utf8()    new diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim index f3650a9ac4..e2d028e828 100644 --- a/src/nvim/testdir/test_smartindent.vim +++ b/src/nvim/testdir/test_smartindent.vim @@ -21,9 +21,7 @@ endfunc  func Test_smartindent_has_no_effect()    new    exe "normal! i\<Tab>one\<Esc>" -  set noautoindent -  set smartindent -  set indentexpr= +  setlocal noautoindent smartindent indentexpr=    exe "normal! Gotwo\<Esc>"    call assert_equal("\ttwo", getline("$")) @@ -32,16 +30,13 @@ func Test_smartindent_has_no_effect()    call assert_equal("three", getline("$"))    delfunction! MyIndent -  set autoindent& -  set smartindent& -  set indentexpr&    bwipe!  endfunc  " Test for inserting '{' and '} with smartindent  func Test_smartindent_braces()    new -  set smartindent shiftwidth=4 +  setlocal smartindent shiftwidth=4    call setline(1, ['    if (a)', "\tif (b)", "\t    return 1"])    normal 2ggO{    normal 3ggA { @@ -57,7 +52,62 @@ func Test_smartindent_braces()          \ "\t}",          \ '    }'          \ ], getline(1, '$')) -  set si& sw& ai& +  close! +endfunc + +" Test for adding a new line before and after comments with smartindent +func Test_si_add_line_around_comment() +  new +  setlocal smartindent shiftwidth=4 +  call setline(1, ['    A', '# comment1', '# comment2']) +  exe "normal GoC\<Esc>2GOB" +  call assert_equal(['    A', '    B', '# comment1', '# comment2', '    C'], +        \ getline(1, '$')) +  close! +endfunc + +" After a C style comment, indent for a following line should line up with the +" line containing the start of the comment. +func Test_si_indent_after_c_comment() +  new +  setlocal smartindent shiftwidth=4 fo+=ro +  exe "normal i\<C-t>/*\ncomment\n/\n#define FOOBAR\n75\<Esc>ggOabc" +  normal 3jOcont +  call assert_equal(['    abc', '    /*', '     * comment', '     * cont', +        \ '     */', '#define FOOBAR', '    75'], getline(1, '$')) +  close! +endfunc + +" Test for indenting a statement after a if condition split across lines +func Test_si_if_cond_split_across_lines() +  new +  setlocal smartindent shiftwidth=4 +  exe "normal i\<C-t>if (cond1 &&\n\<C-t>cond2) {\ni = 10;\n}" +  call assert_equal(['    if (cond1 &&', "\t    cond2) {", "\ti = 10;", +        \ '    }'], getline(1, '$')) +  close! +endfunc + +" Test for inserting lines before and after a one line comment +func Test_si_one_line_comment() +  new +  setlocal smartindent shiftwidth=4 +  exe "normal i\<C-t>abc;\n\<C-t>/* comment */" +  normal oi = 10; +  normal kOj = 1; +  call assert_equal(['    abc;', "\tj = 1;", "\t/* comment */", "\ti = 10;"], +        \ getline(1, '$')) +  close! +endfunc + +" Test for smartindent with a comment continued across multiple lines +func Test_si_comment_line_continuation() +  new +  setlocal smartindent shiftwidth=4 +  call setline(1, ['# com1', '# com2 \', '    contd', '# com3', '  xyz']) +  normal ggOabc +  call assert_equal(['  abc', '# com1', '# com2 \', '    contd', '# com3', +        \ '  xyz'], getline(1, '$'))    close!  endfunc diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index 540c73a772..9895ad754c 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -1489,6 +1489,22 @@ func Test_sort_last_search_pat()    close!  endfunc +" Test for :sort with no last search pattern +func Test_sort_with_no_last_search_pat() +  let lines =<< trim [SCRIPT] +    call setline(1, ['3b', '1c', '2a']) +    call assert_fails('sort //', 'E35:') +    call writefile(v:errors, 'Xresult') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif +  call delete('Xscript') +  call delete('Xresult') +endfunc +  " Test for retaining marks across a :sort  func Test_sort_with_marks()    new diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index b8fe8422b3..ba6fd5ad95 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -57,3 +57,34 @@ func Test_different_script()    call assert_fails('source XtwoScript', 'E121:')    call delete('XtwoScript')  endfunc + +" When sourcing a vim script, shebang should be ignored. +func Test_source_ignore_shebang() +  call writefile(['#!./xyzabc', 'let g:val=369'], 'Xfile.vim') +  source Xfile.vim +  call assert_equal(g:val, 369) +  call delete('Xfile.vim') +endfunc + +" Test for expanding <sfile> in a autocmd and for <slnum> and <sflnum> +func Test_source_autocmd_sfile() +  let code =<< trim [CODE] +    let g:SfileName = '' +    augroup sfiletest +      au! +      autocmd User UserAutoCmd let g:Sfile = '<sfile>:t' +    augroup END +    doautocmd User UserAutoCmd +    let g:Slnum = expand('<slnum>') +    let g:Sflnum = expand('<sflnum>') +    augroup! sfiletest +  [CODE] +  call writefile(code, 'Xscript.vim') +  source Xscript.vim +  call assert_equal('Xscript.vim', g:Sfile) +  call assert_equal('7', g:Slnum) +  call assert_equal('8', g:Sflnum) +  call delete('Xscript.vim') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 58f0760f48..7744c5bcca 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -827,6 +827,16 @@ func Test_spell_good_word_invalid()    bwipe!  endfunc +func Test_spell_good_word_slash() +  " This caused E1280. +  new +  norm afoo / +  1 +  norm zG + +  bwipe! +endfunc +  func LoadAffAndDic(aff_contents, dic_contents)    throw 'skipped: Nvim does not support enc=latin1'    set enc=latin1 diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index 619b63202a..f795d1c0cf 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -294,7 +294,7 @@ endfunc  " Test for *:s%* on :substitute.  func Test_sub_cmd_6() -  throw "skipped: Nvim removed POSIX-related 'cpoptions' flags" +  throw 'Skipped: Nvim does not support cpoptions flag "/"'    set magic&    set cpo+=/ @@ -808,6 +808,41 @@ func Test_sub_expand_text()    close!  endfunc +" Test for command failures when the last substitute pattern is not set. +func Test_sub_with_no_last_pat() +  let lines =<< trim [SCRIPT] +    call assert_fails('~', 'E33:') +    call assert_fails('s//abc/g', 'E476:') +    call assert_fails('s\/bar', 'E476:') +    call assert_fails('s\&bar&', 'E476:') +    call writefile(v:errors, 'Xresult') +    qall! +  [SCRIPT] +  call writefile(lines, 'Xscript') +  if RunVim([], [], '--clean -S Xscript') +    call assert_equal([], readfile('Xresult')) +  endif + +  " Nvim does not support cpoptions flag "/"' +  " let lines =<< trim [SCRIPT] +  "   set cpo+=/ +  "   call assert_fails('s/abc/%/', 'E33:') +  "   call writefile(v:errors, 'Xresult') +  "   qall! +  " [SCRIPT] +  " call writefile(lines, 'Xscript') +  " if RunVim([], [], '--clean -S Xscript') +  "   call assert_equal([], readfile('Xresult')) +  " endif + +  call delete('Xscript') +  call delete('Xresult') +endfunc + +func Test_substitute() +  call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g')) +endfunc +  func Test_submatch_list_concatenate()    let pat = 'A\(.\)'    let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])} diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index b180f27685..923e1cbf50 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -278,7 +278,6 @@ func Test_swap_recover_ext()      autocmd SwapExists * let v:swapchoice = 'r'    augroup END -    " Create a valid swapfile by editing a file with a special extension.    split Xtest.scr    call setline(1, ['one', 'two', 'three']) @@ -311,6 +310,46 @@ func Test_swap_recover_ext()    augroup! test_swap_recover_ext  endfunc +" Test for closing a split window automatically when a swap file is detected +" and 'Q' is selected in the confirmation prompt. +func Test_swap_split_win() +  autocmd! SwapExists +  augroup test_swap_splitwin +    autocmd! +    autocmd SwapExists * let v:swapchoice = 'q' +  augroup END + +  " Create a valid swapfile by editing a file with a special extension. +  split Xtest.scr +  call setline(1, ['one', 'two', 'three']) +  write  " file is written, not modified +  write  " write again to make sure the swapfile is created +  " read the swapfile as a Blob +  let swapfile_name = swapname('%') +  let swapfile_bytes = readfile(swapfile_name, 'B') + +  " Close and delete the file and recreate the swap file. +  quit +  call delete('Xtest.scr') +  call writefile(swapfile_bytes, swapfile_name) +  " Split edit the file again. This should fail to open the window +  try +    split Xtest.scr +  catch +    " E308 should be caught, not E306. +    call assert_exception('E308:')  " Original file may have been changed +  endtry +  call assert_equal(1, winnr('$')) + +  call delete('Xtest.scr') +  call delete(swapfile_name) + +  augroup test_swap_splitwin +      autocmd! +  augroup END +  augroup! test_swap_splitwin +endfunc +  " Test for selecting 'q' in the attention prompt  func Test_swap_prompt_splitwin()    CheckRunVimInTerminal diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 51ab5c1022..9a115da8d3 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -139,7 +139,11 @@ function Test_tabpage()    call assert_fails("tabmove -99", 'E474:')    call assert_fails("tabmove -3+", 'E474:')    call assert_fails("tabmove $3", 'E474:') +  call assert_fails("%tabonly", 'E16:')    1tabonly! +  tabnew +  call assert_fails("-2tabmove", 'E474:') +  tabonly!  endfunc  " Test autocommands @@ -607,6 +611,16 @@ func Test_tabpage_cmdheight()    call delete('XTest_tabpage_cmdheight')  endfunc +" Test for closing the tab page from a command window +func Test_tabpage_close_cmdwin() +  tabnew +  call feedkeys("q/:tabclose\<CR>\<Esc>", 'xt') +  call assert_equal(2, tabpagenr('$')) +  call feedkeys("q/:tabonly\<CR>\<Esc>", 'xt') +  call assert_equal(2, tabpagenr('$')) +  tabonly +endfunc +  " Return the terminal key code for selecting a tab page from the tabline. This  " sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),  " KS_FILLER (0x58) and then the tab page number. diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 11e32067b2..b1746641ee 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -935,6 +935,11 @@ func Test_tag_multimatch()    tag FIRST    tnext    call assert_equal(2, line('.')) +  tlast +  tprev +  call assert_equal(2, line('.')) +  tNext +  call assert_equal(1, line('.'))    set ignorecase&    call delete('Xtags') @@ -1077,6 +1082,213 @@ Type number and <Enter> (q or empty cancels):    %bwipe  endfunc +" Test for :isearch, :ilist, :ijump and :isplit commands +" Test for [i, ]i, [I, ]I, [ CTRL-I, ] CTRL-I and CTRL-W i commands +func Test_inc_search() +  new +  call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo']) +  call cursor(3, 1) + +  " Test for [i and ]i +  call assert_equal('1:foo', execute('normal [i')) +  call assert_equal('2:foo', execute('normal 2[i')) +  call assert_fails('normal 3[i', 'E387:') +  call assert_equal('3:foo', execute('normal ]i')) +  call assert_equal('4:foo', execute('normal 2]i')) +  call assert_fails('normal 3]i', 'E389:') + +  " Test for :isearch +  call assert_equal('1:foo', execute('isearch foo')) +  call assert_equal('3:foo', execute('isearch 4 /foo/')) +  call assert_fails('isearch 3 foo', 'E387:') +  call assert_equal('3:foo', execute('+1,$isearch foo')) +  call assert_fails('1,.-1isearch 3 foo', 'E389:') +  call assert_fails('isearch bar', 'E389:') +  call assert_fails('isearch /foo/3', 'E488:') + +  " Test for [I and ]I +  call assert_equal([ +        \ '  1:    1 1:foo', +        \ '  2:    2 2:foo', +        \ '  3:    3 foo', +        \ '  4:    4 3:foo', +        \ '  5:    5 4:foo'], split(execute('normal [I'), "\n")) +  call assert_equal([ +        \ '  1:    4 3:foo', +        \ '  2:    5 4:foo'], split(execute('normal ]I'), "\n")) + +  " Test for :ilist +  call assert_equal([ +        \ '  1:    1 1:foo', +        \ '  2:    2 2:foo', +        \ '  3:    3 foo', +        \ '  4:    4 3:foo', +        \ '  5:    5 4:foo'], split(execute('ilist foo'), "\n")) +  call assert_equal([ +        \ '  1:    4 3:foo', +        \ '  2:    5 4:foo'], split(execute('+1,$ilist /foo/'), "\n")) +  call assert_fails('ilist bar', 'E389:') + +  " Test for [ CTRL-I and ] CTRL-I +  exe "normal [\t" +  call assert_equal([1, 3], [line('.'), col('.')]) +  exe "normal 2j4[\t" +  call assert_equal([4, 3], [line('.'), col('.')]) +  call assert_fails("normal k3[\t", 'E387:') +  call assert_fails("normal 6[\t", 'E389:') +  exe "normal ]\t" +  call assert_equal([4, 3], [line('.'), col('.')]) +  exe "normal k2]\t" +  call assert_equal([5, 3], [line('.'), col('.')]) +  call assert_fails("normal 2k3]\t", 'E389:') + +  " Test for :ijump +  call cursor(3, 1) +  ijump foo +  call assert_equal([1, 3], [line('.'), col('.')]) +  call cursor(3, 1) +  ijump 4 /foo/ +  call assert_equal([4, 3], [line('.'), col('.')]) +  call cursor(3, 1) +  call assert_fails('ijump 3 foo', 'E387:') +  +,$ijump 2 foo +  call assert_equal([5, 3], [line('.'), col('.')]) +  call assert_fails('ijump bar', 'E389:') + +  " Test for CTRL-W i +  call cursor(3, 1) +  wincmd i +  call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')]) +  close +  5wincmd i +  call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')]) +  close +  call assert_fails('3wincmd i', 'E387:') +  call assert_fails('6wincmd i', 'E389:') + +  " Test for :isplit +  isplit foo +  call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')]) +  close +  isplit 5 /foo/ +  call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')]) +  close +  call assert_fails('isplit 3 foo', 'E387:') +  call assert_fails('isplit 6 foo', 'E389:') +  call assert_fails('isplit bar', 'E389:') + +  close! +endfunc + +" this was using a line from ml_get() freed by the regexp +func Test_isearch_copy_line() +  new +  norm o +  norm 0 +  0norm o +  sil! norm bc0 +  sil! isearch \%') +  bwipe! +endfunc + +" Test for :dsearch, :dlist, :djump and :dsplit commands +" Test for [d, ]d, [D, ]D, [ CTRL-D, ] CTRL-D and CTRL-W d commands +func Test_macro_search() +  new +  call setline(1, ['#define FOO 1', '#define FOO 2', '#define FOO 3', +        \ '#define FOO 4', '#define FOO 5']) +  call cursor(3, 9) + +  " Test for [d and ]d +  call assert_equal('#define FOO 1', execute('normal [d')) +  call assert_equal('#define FOO 2', execute('normal 2[d')) +  call assert_fails('normal 3[d', 'E387:') +  call assert_equal('#define FOO 4', execute('normal ]d')) +  call assert_equal('#define FOO 5', execute('normal 2]d')) +  call assert_fails('normal 3]d', 'E388:') + +  " Test for :dsearch +  call assert_equal('#define FOO 1', execute('dsearch FOO')) +  call assert_equal('#define FOO 5', execute('dsearch 5 /FOO/')) +  call assert_fails('dsearch 3 FOO', 'E387:') +  call assert_equal('#define FOO 4', execute('+1,$dsearch FOO')) +  call assert_fails('1,.-1dsearch 3 FOO', 'E388:') +  call assert_fails('dsearch BAR', 'E388:') + +  " Test for [D and ]D +  call assert_equal([ +        \ '  1:    1 #define FOO 1', +        \ '  2:    2 #define FOO 2', +        \ '  3:    3 #define FOO 3', +        \ '  4:    4 #define FOO 4', +        \ '  5:    5 #define FOO 5'], split(execute('normal [D'), "\n")) +  call assert_equal([ +        \ '  1:    4 #define FOO 4', +        \ '  2:    5 #define FOO 5'], split(execute('normal ]D'), "\n")) + +  " Test for :dlist +  call assert_equal([ +        \ '  1:    1 #define FOO 1', +        \ '  2:    2 #define FOO 2', +        \ '  3:    3 #define FOO 3', +        \ '  4:    4 #define FOO 4', +        \ '  5:    5 #define FOO 5'], split(execute('dlist FOO'), "\n")) +  call assert_equal([ +        \ '  1:    4 #define FOO 4', +        \ '  2:    5 #define FOO 5'], split(execute('+1,$dlist /FOO/'), "\n")) +  call assert_fails('dlist BAR', 'E388:') + +  " Test for [ CTRL-D and ] CTRL-D +  exe "normal [\<C-D>" +  call assert_equal([1, 9], [line('.'), col('.')]) +  exe "normal 2j4[\<C-D>" +  call assert_equal([4, 9], [line('.'), col('.')]) +  call assert_fails("normal k3[\<C-D>", 'E387:') +  call assert_fails("normal 6[\<C-D>", 'E388:') +  exe "normal ]\<C-D>" +  call assert_equal([4, 9], [line('.'), col('.')]) +  exe "normal k2]\<C-D>" +  call assert_equal([5, 9], [line('.'), col('.')]) +  call assert_fails("normal 2k3]\<C-D>", 'E388:') + +  " Test for :djump +  call cursor(3, 9) +  djump FOO +  call assert_equal([1, 9], [line('.'), col('.')]) +  call cursor(3, 9) +  djump 4 /FOO/ +  call assert_equal([4, 9], [line('.'), col('.')]) +  call cursor(3, 9) +  call assert_fails('djump 3 FOO', 'E387:') +  +,$djump 2 FOO +  call assert_equal([5, 9], [line('.'), col('.')]) +  call assert_fails('djump BAR', 'E388:') + +  " Test for CTRL-W d +  call cursor(3, 9) +  wincmd d +  call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')]) +  close +  5wincmd d +  call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')]) +  close +  call assert_fails('3wincmd d', 'E387:') +  call assert_fails('6wincmd d', 'E388:') + +  " Test for :dsplit +  dsplit FOO +  call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')]) +  close +  dsplit 5 /FOO/ +  call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')]) +  close +  call assert_fails('dsplit 3 FOO', 'E387:') +  call assert_fails('dsplit 6 FOO', 'E388:') +  call assert_fails('dsplit BAR', 'E388:') + +  close! +endfunc +  func Test_define_search()    " this was accessing freed memory    new @@ -1092,6 +1304,37 @@ func Test_define_search()    bwipe!  endfunc +" Test for [*, [/, ]* and ]/ +func Test_comment_search() +  new +  call setline(1, ['', '/*', ' *', ' *', ' */']) +  normal! 4gg[/ +  call assert_equal([2, 1], [line('.'), col('.')]) +  normal! 3gg[* +  call assert_equal([2, 1], [line('.'), col('.')]) +  normal! 3gg]/ +  call assert_equal([5, 3], [line('.'), col('.')]) +  normal! 3gg]* +  call assert_equal([5, 3], [line('.'), col('.')]) +  %d +  call setline(1, ['', '/*', ' *', ' *']) +  call assert_beeps('normal! 3gg]/') +  %d +  call setline(1, ['', ' *', ' *', ' */']) +  call assert_beeps('normal! 4gg[/') +  %d +  call setline(1, '        /* comment */') +  normal! 15|[/ +  call assert_equal(9, col('.')) +  normal! 15|]/ +  call assert_equal(21, col('.')) +  call setline(1, '         comment */') +  call assert_beeps('normal! 15|[/') +  call setline(1, '        /* comment') +  call assert_beeps('normal! 15|]/') +  close! +endfunc +  " Test for the 'taglength' option  func Test_tag_length()    set tags=Xtags diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 748af199b2..f0a0f894c3 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -1137,8 +1137,79 @@ func Test_whichwrap_multi_byte()    bwipe!  endfunc -func Test_substitute() -  call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g')) +" Test for the 'f' flag in 'comments' (only the first line has the comment +" string) +func Test_firstline_comment() +  new +  setlocal comments=f:- fo+=ro +  exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" +  call assert_equal(['A', '- B', '  C', '  D'], getline(1, '$')) +  %d +  setlocal comments=:- +  exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>" +  call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$')) +  %bw! +endfunc + +" Test for the 'r' flag in 'comments' (right align comment) +func Test_comment_rightalign() +  new +  setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro +  exe "normal i=\<C-C>o\t  /***\nD\n/" +  exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG" +  let expected =<< trim END +    = +    A +    	  /*** +    	    ** B +    	    ** C +    	    ** D +    	    ** E +    	    **     F +    	    ******/ +    G +  END +  call assert_equal(expected, getline(1, '$')) +  %bw! +endfunc + +" Test for the 'b' flag in 'comments' +func Test_comment_blank() +  new +  setlocal comments=b:* fo+=ro +  exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD" +  let expected =<< trim END +    A +    *B +    * C +    * D +    * E +    * F +    *G +    H +  END +  call assert_equal(expected, getline(1, '$')) +  %bw! +endfunc + +" Test for the 'n' flag in comments +func Test_comment_nested() +  new +  setlocal comments=n:> fo+=ro +  exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH" +  exe "normal 5GOE\<C-C>6GoG" +  let expected =<< trim END +    > A +    > B +    > C +    > D +    >>>> E +    >>>> F +    >>>> G +    >>>> H +  END +  call assert_equal(expected, getline(1, '$')) +  %bw!  endfunc  " Test for 'a' and 'w' flags in 'formatoptions' diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index e5b4bc23e8..56a5ec96af 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -7,6 +7,10 @@ source shared.vim  source term_util.vim  source load.vim +func SetUp() +  call timer_stopall() +endfunc +  func MyHandler(timer)    let g:val += 1  endfunc @@ -15,7 +19,7 @@ func MyHandlerWithLists(lists, timer)    let x = string(a:lists)  endfunc -func Test_oneshot() +func Test_timer_oneshot()    let g:val = 0    let timer = timer_start(50, 'MyHandler')    let slept = WaitFor('g:val == 1') @@ -27,7 +31,7 @@ func Test_oneshot()    endif  endfunc -func Test_repeat_three() +func Test_timer_repeat_three()    let g:val = 0    let timer = timer_start(50, 'MyHandler', {'repeat': 3})    let slept = WaitFor('g:val == 3') @@ -39,8 +43,7 @@ func Test_repeat_three()    endif  endfunc -func Test_repeat_many() -  call timer_stopall() +func Test_timer_repeat_many()    let g:val = 0    let timer = timer_start(50, 'MyHandler', {'repeat': -1})    if has('mac') @@ -51,7 +54,7 @@ func Test_repeat_many()    call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val)  endfunc -func Test_with_partial_callback() +func Test_timer_with_partial_callback()    let g:val = 0    let meow = {'one': 1}    function meow.bite(...) @@ -68,13 +71,13 @@ func Test_with_partial_callback()    endif  endfunc -func Test_retain_partial() +func Test_timer_retain_partial()    call timer_start(50, function('MyHandlerWithLists', [['a']])) -  call garbagecollect() +  call test_garbagecollect_now()    sleep 100m  endfunc -func Test_info() +func Test_timer_info()    let id = timer_start(1000, 'MyHandler')    let info = id->timer_info()    call assert_equal(id, info[0]['id']) @@ -91,10 +94,11 @@ func Test_info()    call timer_stop(id)    call assert_equal([], timer_info(id)) + +  call assert_fails('call timer_info("abc")', 'E39:')  endfunc -func Test_stopall() -  call timer_stopall() +func Test_timer_stopall()    let id1 = timer_start(1000, 'MyHandler')    let id2 = timer_start(2000, 'MyHandler')    let info = timer_info() @@ -105,7 +109,7 @@ func Test_stopall()    call assert_equal(0, len(info))  endfunc -func Test_paused() +func Test_timer_paused()    let g:val = 0    let id = timer_start(50, 'MyHandler') @@ -129,6 +133,8 @@ func Test_paused()    else      call assert_inrange(0, 10, slept)    endif + +  call assert_fails('call timer_pause("abc", 1)', 'E39:')  endfunc  func StopMyself(timer) @@ -138,7 +144,7 @@ func StopMyself(timer)    endif  endfunc -func Test_delete_myself() +func Test_timer_delete_myself()    let g:called = 0    let t = timer_start(10, 'StopMyself', {'repeat': -1})    call WaitForAssert({-> assert_equal(2, g:called)}) @@ -150,33 +156,45 @@ func StopTimer1(timer)    let g:timer2 = 10->timer_start('StopTimer2')    " avoid maxfuncdepth error    call timer_pause(g:timer1, 1) -  sleep 40m +  sleep 20m  endfunc  func StopTimer2(timer)    call timer_stop(g:timer1)  endfunc -func Test_stop_in_callback() +func Test_timer_stop_in_callback() +  call assert_equal(0, len(timer_info()))    let g:timer1 = timer_start(10, 'StopTimer1') -  sleep 40m +  let slept = 0 +  for i in range(10) +    if len(timer_info()) == 0 +      break +    endif +    sleep 10m +    let slept += 10 +  endfor +  " This should take only 30 msec, but on Mac it's often longer +  call assert_inrange(0, 50, slept)  endfunc  func StopTimerAll(timer)    call timer_stopall()  endfunc -func Test_stop_all_in_callback() -  call timer_stopall() -  let g:timer1 = timer_start(10, 'StopTimerAll') -  let info = timer_info() -  call assert_equal(1, len(info)) -  if has('mac') -    sleep 100m -  endif -  sleep 40m -  let info = timer_info() -  call assert_equal(0, len(info)) +func Test_timer_stop_all_in_callback() +  call assert_equal(0, len(timer_info())) +  call timer_start(10, 'StopTimerAll') +  call assert_equal(1, len(timer_info())) +  let slept = 0 +  for i in range(10) +    if len(timer_info()) == 0 +      break +    endif +    sleep 10m +    let slept += 10 +  endfor +  call assert_inrange(0, 30, slept)  endfunc  func FeedkeysCb(timer) @@ -189,7 +207,7 @@ func InputCb(timer)    call Resume()  endfunc -func Test_input_in_timer() +func Test_timer_input_in_timer()    let g:val = ''    call timer_start(10, 'InputCb')    call Standby(1000) @@ -211,6 +229,10 @@ func Test_timer_errors()    call WaitForAssert({-> assert_equal(3, g:call_count)})    sleep 50m    call assert_equal(3, g:call_count) + +  call assert_fails('call timer_start(100, "MyHandler", "abc")', 'E475:') +  call assert_fails('call timer_start(100, [])', 'E921:') +  call assert_fails('call timer_stop("abc")', 'E39:')  endfunc  func FuncWithCaughtError(timer) @@ -242,7 +264,7 @@ func Interrupt(timer)    call nvim_input("\<C-C>")  endfunc -func Test_peek_and_get_char() +func Test_timer_peek_and_get_char()    if !has('unix') && !has('gui_running')      return    endif @@ -253,7 +275,7 @@ func Test_peek_and_get_char()    eval intr->timer_stop()  endfunc -func Test_getchar_zero() +func Test_timer_getchar_zero()    if has('win32') && !has('gui_running')      " Console: no low-level input      return @@ -271,7 +293,7 @@ func Test_getchar_zero()    call timer_stop(id)  endfunc -func Test_ex_mode() +func Test_timer_ex_mode()    " Function with an empty line.    func Foo(...) @@ -282,9 +304,9 @@ func Test_ex_mode()    call timer_stop(timer)  endfunc -func Test_restore_count() +func Test_timer_restore_count()    if !CanRunVimInTerminal() -    return +    throw 'Skipped: cannot run Vim in a terminal window'    endif    " Check that v:count is saved and restored, not changed by a timer.    call writefile([ @@ -315,7 +337,7 @@ endfunc  " Test that the garbage collector isn't triggered if a timer callback invokes  " vgetc(). -func Test_nocatch_garbage_collect() +func Test_timer_nocatch_garbage_collect()    " skipped: Nvim does not support test_garbagecollect_soon(), test_override()    return    " 'uptimetime. must be bigger than the timer timeout @@ -339,7 +361,7 @@ func Test_nocatch_garbage_collect()    delfunc FeedChar  endfunc -func Test_error_in_timer_callback() +func Test_timer_error_in_timer_callback()    if !has('terminal') || (has('win32') && has('gui_running'))      throw 'Skipped: cannot run Vim in a terminal window'    endif @@ -374,6 +396,15 @@ func Test_error_in_timer_callback()    exe buf .. 'bwipe!'  endfunc +" Test for garbage collection when a timer is still running +func Test_timer_garbage_collect() +  let timer = timer_start(1000, function('MyHandler'), {'repeat' : 10}) +  call test_garbagecollect_now() +  let l = timer_info(timer) +  call assert_equal(function('MyHandler'), l[0].callback) +  call timer_stop(timer) +endfunc +  func Test_timer_invalid_callback()    call assert_fails('call timer_start(0, "0")', 'E921')  endfunc diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 205ed095ea..598402fafe 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -1996,6 +1996,47 @@ func Test_reload_in_try_catch()    call delete('Xreload')  endfunc +" Test for errors with :catch, :throw, :finally                            {{{1 +func Test_try_catch_errors() +  call assert_fails('throw |', 'E471:') +  call assert_fails("throw \n ", 'E471:') +  call assert_fails('catch abc', 'E603:') +  call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:') +  call assert_fails('finally', 'E606:') +  call assert_fails('try | finally | finally | endtry', 'E607:') +  " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here. +  " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:') +  call assert_fails('try | for i in range(5) | endif | endtry', 'E170:') +  call assert_fails('try | while v:true | endtry', 'E170:') +  call assert_fails('try | if v:true | endtry', 'E171:') +endfunc + +" Test for verbose messages with :try :catch, and :finally                 {{{1 +func Test_try_catch_verbose() +  " This test works only when the language is English +  if v:lang != "C" && v:lang !~ '^[Ee]n' +    return +  endif + +  set verbose=14 +  redir => msg +  try +    echo i +  catch /E121:/ +  finally +  endtry +  redir END +  let expected = [ +        \ 'Exception thrown: Vim(echo):E121: Undefined variable: i', +        \ '', +        \ 'Exception caught: Vim(echo):E121: Undefined variable: i', +        \ '', +        \ 'Exception finished: Vim(echo):E121: Undefined variable: i' +        \ ] +  call assert_equal(expected, split(msg, "\n")) +  set verbose& +endfunc +  " Test for using throw in a called function with following error    {{{1  func Test_user_command_throw_in_function_call()    let lines =<< trim END diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index af92328387..da8bf12318 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -299,6 +299,8 @@ func Test_undo_write()    close!    call delete('Xtest')    bwipe! Xtest + +  call assert_fails('earlier xyz', 'E475:')  endfunc  func Test_insert_expr() diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index f93eb6e274..1323288676 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1771,6 +1771,125 @@ func Test_function_defined_line()      call delete('Xtest.vim')  endfunc +" Test for missing :endif, :endfor, :endwhile and :endtry           {{{1 +func Test_missing_end() +  call writefile(['if 2 > 1', 'echo ">"'], 'Xscript') +  call assert_fails('source Xscript', 'E171:') +  call writefile(['for i in range(5)', 'echo i'], 'Xscript') +  call assert_fails('source Xscript', 'E170:') +  call writefile(['while v:true', 'echo "."'], 'Xscript') +  call assert_fails('source Xscript', 'E170:') +  call writefile(['try', 'echo "."'], 'Xscript') +  call assert_fails('source Xscript', 'E600:') +  call delete('Xscript') + +  " Using endfor with :while +  let caught_e732 = 0 +  try +    while v:true +    endfor +  catch /E732:/ +    let caught_e732 = 1 +  endtry +  call assert_equal(1, caught_e732) + +  " Using endwhile with :for +  let caught_e733 = 0 +  try +    for i in range(1) +    endwhile +  catch /E733:/ +    let caught_e733 = 1 +  endtry +  call assert_equal(1, caught_e733) + +  " Missing 'in' in a :for statement +  call assert_fails('for i range(1) | endfor', 'E690:') +endfunc + +" Test for deep nesting of if/for/while/try statements              {{{1 +func Test_deep_nest() +  if !CanRunVimInTerminal() +    throw 'Skipped: cannot run vim in terminal' +  endif + +  let lines =<< trim [SCRIPT] +    " Deep nesting of if ... endif +    func Test1() +      let @a = join(repeat(['if v:true'], 51), "\n") +      let @a ..= "\n" +      let @a ..= join(repeat(['endif'], 51), "\n") +      @a +      let @a = '' +    endfunc + +    " Deep nesting of for ... endfor +    func Test2() +      let @a = join(repeat(['for i in [1]'], 51), "\n") +      let @a ..= "\n" +      let @a ..= join(repeat(['endfor'], 51), "\n") +      @a +      let @a = '' +    endfunc + +    " Deep nesting of while ... endwhile +    func Test3() +      let @a = join(repeat(['while v:true'], 51), "\n") +      let @a ..= "\n" +      let @a ..= join(repeat(['endwhile'], 51), "\n") +      @a +      let @a = '' +    endfunc + +    " Deep nesting of try ... endtry +    func Test4() +      let @a = join(repeat(['try'], 51), "\n") +      let @a ..= "\necho v:true\n" +      let @a ..= join(repeat(['endtry'], 51), "\n") +      @a +      let @a = '' +    endfunc +  [SCRIPT] +  call writefile(lines, 'Xscript') + +  let buf = RunVimInTerminal('-S Xscript', {'rows': 6}) + +  " Deep nesting of if ... endif +  call term_sendkeys(buf, ":call Test1()\n") +  call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))}) + +  " Deep nesting of for ... endfor +  call term_sendkeys(buf, ":call Test2()\n") +  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) + +  " Deep nesting of while ... endwhile +  call term_sendkeys(buf, ":call Test3()\n") +  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) + +  " Deep nesting of try ... endtry +  call term_sendkeys(buf, ":call Test4()\n") +  call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))}) + +  "let l = '' +  "for i in range(1, 6) +  "  let l ..= term_getline(buf, i) . "\n" +  "endfor +  "call assert_report(l) + +  call StopVimInTerminal(buf) +  call delete('Xscript') +endfunc + +" Test for <sfile>, <slnum> in a function                           {{{1 +func Test_sfile_in_function() +  func Xfunc() +    call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>')) +    call assert_equal('2', expand('<slnum>')) +  endfunc +  call Xfunc() +  delfunc Xfunc +endfunc +  func Test_for_over_string()    let res = ''    for c in 'aéc̀d' diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 349c4fde50..f77765d415 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -284,6 +284,15 @@ func Test_virtual_replace2()    call assert_equal(['abcd',          \ 'efgh',          \ 'ijkl'], getline(1, '$')) + +  " Test for truncating spaces in a newly added line using 'autoindent' if +  " characters are not added to that line. +  %d_ +  call setline(1, ['    app', '    bee', '    cat']) +  setlocal autoindent +  exe "normal gg$gRt\n\nr" +  call assert_equal(['    apt', '', '    rat'], getline(1, '$')) +    " clean up    %d_    set bs&vim @@ -1169,8 +1178,8 @@ func Test_exclusive_selection()    close!  endfunc -" Test for starting visual mode with a count -" This test should be run withou any previous visual modes. So this should be +" Test for starting visual mode with a count. +" This test should be run without any previous visual modes. So this should be  " run as a first test.  func Test_AAA_start_visual_mode_with_count()    new diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index d295e520c7..41b0cd874c 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -172,6 +172,35 @@ func Test_window_split_edit_bufnr()    %bw!  endfunc +func Test_window_split_no_room() +  " N horizontal windows need >= 2*N + 1 lines: +  " - 1 line + 1 status line in each window +  " - 1 Ex command line +  " +  " 2*N + 1 <= &lines +  " N <= (lines - 1)/2 +  " +  " Beyond that number of windows, E36: Not enough room is expected. +  let hor_win_count = (&lines - 1)/2 +  let hor_split_count = hor_win_count - 1 +  for s in range(1, hor_split_count) | split | endfor +  call assert_fails('split', 'E36:') + +  " N vertical windows need >= 2*(N - 1) + 1 columns: +  " - 1 column + 1 separator for each window (except last window) +  " - 1 column for the last window which does not have separator +  " +  " 2*(N - 1) + 1 <= &columns +  " 2*N - 1 <= &columns +  " N <= (&columns + 1)/2 +  let ver_win_count = (&columns + 1)/2 +  let ver_split_count = ver_win_count - 1 +  for s in range(1, ver_split_count) | vsplit | endfor +  call assert_fails('vsplit', 'E36:') + +  %bw! +endfunc +  func Test_window_exchange()    e Xa @@ -886,6 +915,155 @@ func Test_floatwin_splitmove()    bwipe  endfunc +" Test for the :only command +func Test_window_only() +  new +  set modified +  new +  call assert_fails('only', 'E445:') +  only! +  " Test for :only with a count +  let wid = win_getid() +  new +  new +  3only +  call assert_equal(1, winnr('$')) +  call assert_equal(wid, win_getid()) +  call assert_fails('close', 'E444:') +  call assert_fails('%close', 'E16:') +endfunc + +" Test for errors with :wincmd +func Test_wincmd_errors() +  call assert_fails('wincmd g', 'E474:') +  call assert_fails('wincmd ab', 'E474:') +endfunc + +" Test for errors with :winpos +func Test_winpos_errors() +  throw 'Skipped: Nvim does not have :winpos' +  if !has("gui_running") && !has('win32') +    call assert_fails('winpos', 'E188:') +  endif +  call assert_fails('winpos 10', 'E466:') +endfunc + +" Test for +cmd in a :split command +func Test_split_cmd() +  split +set\ readonly +  call assert_equal(1, &readonly) +  call assert_equal(2, winnr('$')) +  close +endfunc + +" Create maximum number of horizontally or vertically split windows and then +" run commands that create a new horizontally/vertically split window +func Run_noroom_for_newwindow_test(dir_arg) +  let dir = (a:dir_arg == 'v') ? 'vert ' : '' + +  " Open as many windows as possible +  for i in range(500) +    try +      exe dir . 'new' +    catch /E36:/ +      break +    endtry +  endfor + +  call writefile(['first', 'second', 'third'], 'Xfile1') +  call writefile([], 'Xfile2') +  call writefile([], 'Xfile3') + +  " Argument list related commands +  args Xfile1 Xfile2 Xfile3 +  next +  for cmd in ['sargument 2', 'snext', 'sprevious', 'sNext', 'srewind', +			\ 'sfirst', 'slast'] +    call assert_fails(dir .. cmd, 'E36:') +  endfor +  %argdelete + +  " Buffer related commands +  set modified +  hide enew +  for cmd in ['sbuffer Xfile1', 'sbnext', 'sbprevious', 'sbNext', 'sbrewind', +		\ 'sbfirst', 'sblast', 'sball', 'sbmodified', 'sunhide'] +    call assert_fails(dir .. cmd, 'E36:') +  endfor + +  " Window related commands +  for cmd in ['split', 'split Xfile2', 'new', 'new Xfile3', 'sview Xfile1', +		\ 'sfind runtest.vim'] +    call assert_fails(dir .. cmd, 'E36:') +  endfor + +  " Help +  call assert_fails(dir .. 'help', 'E36:') +  call assert_fails(dir .. 'helpgrep window', 'E36:') + +  " Command-line window +  if a:dir_arg == 'h' +    " Cmd-line window is always a horizontally split window +    call assert_beeps('call feedkeys("q:\<CR>", "xt")') +  endif + +  " Quickfix and location list window +  if has('quickfix') +    cexpr '' +    call assert_fails(dir .. 'copen', 'E36:') +    lexpr '' +    call assert_fails(dir .. 'lopen', 'E36:') + +    " Preview window +    call assert_fails(dir .. 'pedit Xfile2', 'E36:') +    call setline(1, 'abc') +    call assert_fails(dir .. 'psearch abc', 'E36:') +  endif + +  " Window commands (CTRL-W ^ and CTRL-W f) +  if a:dir_arg == 'h' +    call assert_fails('call feedkeys("\<C-W>^", "xt")', 'E36:') +    call setline(1, 'Xfile1') +    call assert_fails('call feedkeys("gg\<C-W>f", "xt")', 'E36:') +  endif +  enew! + +  " Tag commands (:stag, :stselect and :stjump) +  call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", +        \ "second\tXfile1\t2", +        \ "third\tXfile1\t3",], +        \ 'Xtags') +  set tags=Xtags +  call assert_fails(dir .. 'stag second', 'E36:') +  call assert_fails('call feedkeys(":" .. dir .. "stselect second\n1\n", "xt")', 'E36:') +  call assert_fails(dir .. 'stjump second', 'E36:') +  call assert_fails(dir .. 'ptag second', 'E36:') +  set tags& +  call delete('Xtags') + +  " :isplit and :dsplit +  call setline(1, ['#define FOO 1', 'FOO']) +  normal 2G +  call assert_fails(dir .. 'isplit FOO', 'E36:') +  call assert_fails(dir .. 'dsplit FOO', 'E36:') + +  " terminal +  if has('terminal') +    call assert_fails(dir .. 'terminal', 'E36:') +  endif + +  %bwipe! +  call delete('Xfile1') +  call delete('Xfile2') +  call delete('Xfile3') +  only +endfunc + +func Test_split_cmds_with_no_room() +  call Run_noroom_for_newwindow_test('h') +  call Run_noroom_for_newwindow_test('v') +endfunc +  func Test_window_resize()    throw 'Skipped: Nvim supports cmdheight=0'    " Vertical :resize (absolute, relative, min and max size). diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index b42665c9b5..bfbba1f793 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -206,6 +206,8 @@ func Test_write_errors()    call assert_fails('1,2write', 'E140:')    close! +  call assert_fails('w > Xtest', 'E494:') +     " Try to overwrite a directory    if has('unix')      call mkdir('Xdir1') diff --git a/src/nvim/testing.c b/src/nvim/testing.c index 9207ebe73b..69b687e44f 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -110,9 +110,13 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)  /// Fill "gap" with information about an assert error.  static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, -                              typval_T *exp_tv, typval_T *got_tv, assert_type_T atype) +                              typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype)  {    char_u *tofree; +  typval_T *exp_tv = exp_tv_arg; +  typval_T *got_tv = got_tv_arg; +  bool did_copy = false; +  int omitted = 0;    if (opt_msg_tv->v_type != VAR_UNKNOWN) {      tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL); @@ -130,6 +134,55 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s    }    if (exp_str == NULL) { +    // When comparing dictionaries, drop the items that are equal, so that +    // it's a lot easier to see what differs. +    if (atype != ASSERT_NOTEQUAL +        && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT +        && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL) { +      dict_T *exp_d = exp_tv->vval.v_dict; +      dict_T *got_d = got_tv->vval.v_dict; + +      did_copy = true; +      exp_tv->vval.v_dict = tv_dict_alloc(); +      got_tv->vval.v_dict = tv_dict_alloc(); + +      int todo = (int)exp_d->dv_hashtab.ht_used; +      for (const hashitem_T *hi = exp_d->dv_hashtab.ht_array; todo > 0; hi++) { +        if (!HASHITEM_EMPTY(hi)) { +          dictitem_T *item2 = tv_dict_find(got_d, (const char *)hi->hi_key, -1); +          if (item2 == NULL +              || !tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &item2->di_tv, false, false)) { +            // item of exp_d not present in got_d or values differ. +            const size_t key_len = STRLEN(hi->hi_key); +            tv_dict_add_tv(exp_tv->vval.v_dict, (const char *)hi->hi_key, key_len, +                           &TV_DICT_HI2DI(hi)->di_tv); +            if (item2 != NULL) { +              tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len, +                             &item2->di_tv); +            } +          } else { +            omitted++; +          } +          todo--; +        } +      } + +      // Add items only present in got_d. +      todo = (int)got_d->dv_hashtab.ht_used; +      for (const hashitem_T *hi = got_d->dv_hashtab.ht_array; todo > 0; hi++) { +        if (!HASHITEM_EMPTY(hi)) { +          dictitem_T *item2 = tv_dict_find(exp_d, (const char *)hi->hi_key, -1); +          if (item2 == NULL) { +            // item of got_d not present in exp_d +            const size_t key_len = STRLEN(hi->hi_key); +            tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len, +                           &TV_DICT_HI2DI(hi)->di_tv); +          } +          todo--; +        } +      } +    } +      tofree = (char_u *)encode_tv2string(exp_tv, NULL);      ga_concat_shorten_esc(gap, tofree);      xfree(tofree); @@ -148,6 +201,18 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s      tofree = (char_u *)encode_tv2string(got_tv, NULL);      ga_concat_shorten_esc(gap, tofree);      xfree(tofree); + +    if (omitted != 0) { +      char buf[100]; +      vim_snprintf(buf, sizeof(buf), " - %d equal item%s omitted", omitted, +                   omitted == 1 ? "" : "s"); +      ga_concat(gap, buf); +    } +  } + +  if (did_copy) { +    tv_clear(exp_tv); +    tv_clear(got_tv);    }  } @@ -405,15 +470,15 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)    const char *const cmd = tv_get_string_chk(&argvars[0]);    garray_T ga;    int save_trylevel = trylevel; +  const int called_emsg_before = called_emsg;    // trylevel must be zero for a ":throw" command to be considered failed    trylevel = 0; -  called_emsg = false;    suppress_errthrow = true;    emsg_silent = true;    do_cmdline_cmd(cmd); -  if (!called_emsg) { +  if (called_emsg == called_emsg_before) {      prepare_assert_error(&ga);      ga_concat(&ga, "command did not fail: ");      assert_append_cmd_or_arg(&ga, argvars, cmd); @@ -438,7 +503,6 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)    }    trylevel = save_trylevel; -  called_emsg = false;    suppress_errthrow = false;    emsg_silent = false;    emsg_on_display = false; diff --git a/src/nvim/window.c b/src/nvim/window.c index 7895391697..cf10e635b6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -331,6 +331,7 @@ newwindow:    // move window to new tab page    case 'T': +    CHECK_CMDWIN;      if (one_window(curwin)) {        msg(_(m_onlyone));      } else {  | 
