diff options
Diffstat (limited to 'src')
30 files changed, 1946 insertions, 158 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6f3f654bdc..ba4d005e9a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -380,7 +380,7 @@ static inline void typval_encode_list_start(EncodedData *const edata,    kv_push(edata->stack, ARRAY_OBJ(((Array) {      .capacity = len,      .size = 0, -    .items = xmalloc(len * sizeof(*((Object *)NULL)->data.array.items)), +    .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)),    })));  } @@ -422,7 +422,8 @@ static inline void typval_encode_dict_start(EncodedData *const edata,    kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) {      .capacity = len,      .size = 0, -    .items = xmalloc(len * sizeof(*((Object *)NULL)->data.dictionary.items)), +    .items = xmalloc(len * sizeof( +        *((Object)OBJECT_INIT).data.dictionary.items)),    })));  } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 1089a2dc3b..a471ebf06f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4216,6 +4216,8 @@ do_arg_all (    win_T       *new_curwin = NULL;    tabpage_T   *new_curtab = NULL; +  assert(firstwin != NULL);  // satisfy coverity +    if (ARGCOUNT <= 0) {      /* Don't give an error message.  We don't want it when the ":all"       * command is in the .vimrc. */ @@ -4407,19 +4409,23 @@ do_arg_all (    /* Remove the "lock" on the argument list. */    alist_unlink(alist); -  --autocmd_no_enter; -  /* restore last referenced tabpage's curwin */ +  autocmd_no_enter--; +  // restore last referenced tabpage's curwin    if (last_curtab != new_curtab) { -    if (valid_tabpage(last_curtab)) -      goto_tabpage_tp(last_curtab, TRUE, TRUE); -    if (win_valid(last_curwin)) +    if (valid_tabpage(last_curtab)) { +      goto_tabpage_tp(last_curtab, true, true); +    } +    if (win_valid(last_curwin)) {        win_enter(last_curwin, false); +    } +  } +  // to window with first arg +  if (valid_tabpage(new_curtab)) { +    goto_tabpage_tp(new_curtab, true, true);    } -  /* to window with first arg */ -  if (valid_tabpage(new_curtab)) -    goto_tabpage_tp(new_curtab, TRUE, TRUE); -  if (win_valid(new_curwin)) +  if (win_valid(new_curwin)) {      win_enter(new_curwin, false); +  }    --autocmd_no_leave;    xfree(opened); diff --git a/src/nvim/diff.c b/src/nvim/diff.c index aafd50687e..5940dc55da 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1007,6 +1007,10 @@ void ex_diffsplit(exarg_T *eap)    bufref_T old_curbuf;    set_bufref(&old_curbuf, curbuf); +  // Need to compute w_fraction when no redraw happened yet. +  validate_cursor(); +  set_fraction(curwin); +    // don't use a new tab page, each tab page has its own diffs    cmdmod.tab = 0; @@ -1032,6 +1036,9 @@ void ex_diffsplit(exarg_T *eap)                curwin->w_cursor.lnum);          }        } +      // Now that lines are folded scroll to show the cursor at the same +      // relative position. +      scroll_to_fraction(curwin, curwin->w_height);      }    }  } @@ -1154,10 +1161,13 @@ void ex_diffoff(exarg_T *eap)          }          foldUpdateAll(wp); - -        // make sure topline is not halfway through a fold -        changed_window_setting_win(wp);        } +      // remove filler lines +      wp->w_topfill = 0; + +      // make sure topline is not halfway a fold and cursor is +      // invalidated +      changed_window_setting_win(wp);        // Note: 'sbo' is not restored, it's a global option.        diff_buf_adjust(wp); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1cc84f8c16..bbb6565509 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -498,6 +498,14 @@ static PMap(uint64_t) *jobs = NULL;  static uint64_t last_timer_id = 0;  static PMap(uint64_t) *timers = NULL; +/// Dummy va_list for passing to vim_snprintf +/// +/// Used because: +/// - passing a NULL pointer doesn't work when va_list isn't a pointer +/// - locally in the function results in a "used before set" warning +/// - using va_start() to initialize it gives "function with fixed args" error +static va_list dummy_ap; +  static const char *const msgpack_type_names[] = {    [kMPNil] = "nil",    [kMPBoolean] = "boolean", @@ -12122,6 +12130,16 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what)    }  } +/// "id()" function +static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) +  FUNC_ATTR_NONNULL_ALL +{ +  const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars); +  rettv->v_type = VAR_STRING; +  rettv->vval.v_string = xmalloc(len + 1); +  vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); +} +  /*   * "items(dict)" function   */ @@ -13628,12 +13646,6 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)    rettv->vval.v_number = lnum;  } -/* This dummy va_list is here because: - * - passing a NULL pointer doesn't work when va_list isn't a pointer - * - locally in the function results in a "used before set" warning - * - using va_start() to initialize it gives "function with fixed args" error */ -static va_list ap; -  /*   * "printf()" function   */ @@ -13650,11 +13662,11 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)      /* Get the required length, allocate the buffer and do it for real. */      did_emsg = FALSE;      fmt = (char *)get_tv_string_buf(&argvars[0], buf); -    len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1); +    len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1);      if (!did_emsg) {        char *s = xmalloc(len + 1);        rettv->vval.v_string = (char_u *)s; -      (void)vim_vsnprintf(s, len + 1, fmt, ap, argvars + 1); +      (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1);      }      did_emsg |= saved_did_emsg;    } @@ -23046,8 +23058,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)    data->refcount++;    char *cmd = xstrdup(proc->argv[0]); -  if (!process_spawn(proc)) { -    EMSG2(_(e_jobspawn), cmd); +  int status = process_spawn(proc); +  if (status) { +    EMSG3(_(e_jobspawn), os_strerror(status), cmd);      xfree(cmd);      if (proc->type == kProcessTypePty) {        xfree(data->proc.pty.term_name); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e0b72feb19..964b061e95 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -168,6 +168,7 @@ return {      invert={args=1},      isdirectory={args=1},      islocked={args=1}, +    id={args=1},      items={args=1},      jobclose={args={1, 2}},      jobpid={args=1}, diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 365eb2dd77..34f88cbc98 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -348,6 +348,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(        _mp_push(*mpstack, ((MPConvStackVal) {          .type = kMPConvPartial,          .tv = tv, +        .saved_copyID = copyID - 1,          .data = {            .p = {              .stage = kMPConvPartialArgs, @@ -362,12 +363,15 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(          TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);          break;        } +      const int saved_copyID = tv->vval.v_list->lv_copyID;        _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,                                               kMPConvList);        TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len); +      assert(saved_copyID != copyID && saved_copyID != copyID - 1);        _mp_push(*mpstack, ((MPConvStackVal) {          .type = kMPConvList,          .tv = tv, +        .saved_copyID = saved_copyID,          .data = {            .l = {              .list = tv->vval.v_list, @@ -495,14 +499,17 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(              if (val_di->di_tv.v_type != VAR_LIST) {                goto _convert_one_value_regular_dict;              } +            const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;              _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,                                                     lv_copyID, copyID,                                                     kMPConvList);              TYPVAL_ENCODE_CONV_LIST_START(tv,                                            val_di->di_tv.vval.v_list->lv_len); +            assert(saved_copyID != copyID && saved_copyID != copyID - 1);              _mp_push(*mpstack, ((MPConvStackVal) {                .tv = tv,                .type = kMPConvList, +              .saved_copyID = saved_copyID,                .data = {                  .l = {                    .list = val_di->di_tv.vval.v_list, @@ -528,13 +535,16 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(                  goto _convert_one_value_regular_dict;                }              } +            const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;              _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,                                                     kMPConvPairs);              TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,                                            val_list->lv_len); +            assert(saved_copyID != copyID && saved_copyID != copyID - 1);              _mp_push(*mpstack, ((MPConvStackVal) {                .tv = tv,                .type = kMPConvPairs, +              .saved_copyID = saved_copyID,                .data = {                  .l = {                    .list = val_list, @@ -569,14 +579,17 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(          }          break;        } -_convert_one_value_regular_dict: +_convert_one_value_regular_dict: {} +      const int saved_copyID = tv->vval.v_dict->dv_copyID;        _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,                                               kMPConvDict);        TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,                                      tv->vval.v_dict->dv_hashtab.ht_used); +      assert(saved_copyID != copyID && saved_copyID != copyID - 1);        _mp_push(*mpstack, ((MPConvStackVal) {          .tv = tv,          .type = kMPConvDict, +        .saved_copyID = saved_copyID,          .data = {            .d = {              .dict = tv->vval.v_dict, @@ -638,7 +651,7 @@ typval_encode_stop_converting_one_item:        case kMPConvDict: {          if (!cur_mpsv->data.d.todo) {            (void)_mp_pop(mpstack); -          cur_mpsv->data.d.dict->dv_copyID = copyID - 1; +          cur_mpsv->data.d.dict->dv_copyID = cur_mpsv->saved_copyID;            TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);            continue;          } else if (cur_mpsv->data.d.todo @@ -662,7 +675,7 @@ typval_encode_stop_converting_one_item:        case kMPConvList: {          if (cur_mpsv->data.l.li == NULL) {            (void)_mp_pop(mpstack); -          cur_mpsv->data.l.list->lv_copyID = copyID - 1; +          cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;            TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);            continue;          } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { @@ -675,7 +688,7 @@ typval_encode_stop_converting_one_item:        case kMPConvPairs: {          if (cur_mpsv->data.l.li == NULL) {            (void)_mp_pop(mpstack); -          cur_mpsv->data.l.list->lv_copyID = copyID - 1; +          cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;            TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);            continue;          } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { @@ -711,6 +724,7 @@ typval_encode_stop_converting_one_item:                _mp_push(mpstack, ((MPConvStackVal) {                  .type = kMPConvPartialList,                  .tv = NULL, +                .saved_copyID = copyID - 1,                  .data = {                    .a = {                      .arg = pt->pt_argv, @@ -731,6 +745,7 @@ typval_encode_stop_converting_one_item:                  TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);                  continue;                } +              const int saved_copyID = dict->dv_copyID;                const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(                    TYPVAL_ENCODE_FIRST_ARG_NAME,                    dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict, @@ -744,9 +759,11 @@ typval_encode_stop_converting_one_item:                }                TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,                                              dict->dv_hashtab.ht_used); +              assert(saved_copyID != copyID && saved_copyID != copyID - 1);                _mp_push(mpstack, ((MPConvStackVal) {                  .type = kMPConvDict,                  .tv = NULL, +                .saved_copyID = saved_copyID,                  .data = {                    .d = {                      .dict = dict, diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index ba325b8f55..46145c5d03 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -34,6 +34,7 @@ typedef enum {  typedef struct {    MPConvStackValType type;  ///< Type of the stack entry.    typval_T *tv;  ///< Currently converted typval_T. +  int saved_copyID;  ///< copyID item used to have.    union {      struct {        dict_T *dict;    ///< Currently converted dictionary. diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index f478d19ca1..616c89671b 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -49,7 +49,7 @@ typedef enum {  typedef struct {    VarType v_type;  ///< Variable type.    VarLockStatus v_lock;  ///< Variable lock status. -  union { +  union typval_vval_union {      varnumber_T v_number;  ///< Number, for VAR_NUMBER.      SpecialVarValue v_special;  ///< Special value, for VAR_SPECIAL.      float_T v_float;  ///< Floating-point number, for VAR_FLOAT. diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 907187aa17..3da0c386b4 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -13,7 +13,8 @@  # include "event/libuv_process.c.generated.h"  #endif -bool libuv_process_spawn(LibuvProcess *uvproc) +/// @returns zero on success, or negative error code +int libuv_process_spawn(LibuvProcess *uvproc)    FUNC_ATTR_NONNULL_ALL  {    Process *proc = (Process *)uvproc; @@ -51,11 +52,11 @@ bool libuv_process_spawn(LibuvProcess *uvproc)    int status;    if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {      ELOG("uv_spawn failed: %s", uv_strerror(status)); -    return false; +    return status;    }    proc->pid = uvproc->uv.pid; -  return true; +  return status;  }  void libuv_process_close(LibuvProcess *uvproc) diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index dc7886469b..4429a65f92 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -30,7 +30,8 @@  static bool process_is_tearing_down = false; -bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL +/// @returns zero on success, or negative error code +int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL  {    if (proc->in) {      uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); @@ -44,19 +45,19 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL      uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0);    } -  bool success; +  int status;    switch (proc->type) {      case kProcessTypeUv: -      success = libuv_process_spawn((LibuvProcess *)proc); +      status = libuv_process_spawn((LibuvProcess *)proc);        break;      case kProcessTypePty: -      success = pty_process_spawn((PtyProcess *)proc); +      status = pty_process_spawn((PtyProcess *)proc);        break;      default:        abort();    } -  if (!success) { +  if (status) {      if (proc->in) {        uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL);      } @@ -74,7 +75,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL      }      shell_free_argv(proc->argv);      proc->status = -1; -    return false; +    return status;    }    if (proc->in) { @@ -105,7 +106,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL    proc->internal_close_cb = decref;    proc->refcount++;    kl_push(WatcherPtr, proc->loop->children, proc); -  return true; +  return 0;  }  void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL @@ -155,19 +156,16 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL  /// Synchronously wait for a process to finish  /// -/// @param process The Process instance -/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for -///        waiting until the process quits. -/// @return returns the status code of the exited process. -1 if the process is -///         still running and the `timeout` has expired. Note that this is -///         indistinguishable from the process returning -1 by itself. Which -///         is possible on some OS. Returns -2 if an user has interruped the -///         wait. +/// @param process  Process instance +/// @param ms       Time in milliseconds to wait for the process. +///                 0 for no wait. -1 to wait until the process quits. +/// @return Exit code of the process. +///         -1 if the timeout expired while the process is still running. +///         -2 if the user interruped the wait.  int process_wait(Process *proc, int ms, MultiQueue *events)    FUNC_ATTR_NONNULL_ARG(1)  { -  // The default status is -1, which represents a timeout -  int status = -1; +  int status = -1;  // default    bool interrupted = false;    if (!proc->refcount) {      status = proc->status; @@ -179,8 +177,8 @@ int process_wait(Process *proc, int ms, MultiQueue *events)      events = proc->events;    } -  // Increase refcount to stop the exit callback from being called(and possibly -  // being freed) before we have a chance to get the status. +  // Increase refcount to stop the exit callback from being called (and possibly +  // freed) before we have a chance to get the status.    proc->refcount++;    LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms,                              // Until... diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9da6062ffb..20a00e1d9c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -13,10 +13,6 @@  #include "nvim/types.h"  #include "nvim/event/loop.h" -/* - * definition of global variables - */ -  #define IOSIZE         (1024+1)          // file I/O and sprintf buffer size  #define MAX_MCO        6                 // maximum value for 'maxcombine' @@ -25,11 +21,8 @@  # define MSG_BUF_CLEN  (MSG_BUF_LEN / 6) // cell length (worst case: utf-8                                           // takes 6 bytes for one cell) -/* - * Maximum length of a path (for non-unix systems) Make it a bit long, to stay - * on the safe side.  But not too long to put on the stack. - * TODO(metrix78): Move this to os_defs.h - */ +// Maximum length of a file path.  Make it a bit long, to stay +// on the safe side.  But not too long to put on the stack.  #ifndef MAXPATHL  # ifdef MAXPATHLEN  #  define MAXPATHL  MAXPATHLEN @@ -108,12 +101,9 @@ typedef enum {   * They may have different values when the screen wasn't (re)allocated yet   * after setting Rows or Columns (e.g., when starting up).   */ - -#define DFLT_COLS       80              /* default value for 'columns' */ -#define DFLT_ROWS       24              /* default value for 'lines' */ - +#define DFLT_COLS       80              // default value for 'columns' +#define DFLT_ROWS       24              // default value for 'lines'  EXTERN long Rows INIT(= DFLT_ROWS);     // nr of rows in the screen -  EXTERN long Columns INIT(= DFLT_COLS);  // nr of columns in the screen  /* @@ -889,9 +879,11 @@ EXTERN int swap_exists_action INIT(= SEA_NONE);  EXTERN int swap_exists_did_quit INIT(= FALSE);  /* Selected "quit" at the dialog. */ -EXTERN char_u IObuff[IOSIZE];       /* sprintf's are done in this buffer */ -EXTERN char_u NameBuff[MAXPATHL];   /* buffer for expanding file names */ -EXTERN char_u msg_buf[MSG_BUF_LEN]; /* small buffer for messages */ +EXTERN char_u IObuff[IOSIZE];               ///< Buffer for sprintf, I/O, etc. +EXTERN char_u NameBuff[MAXPATHL];           ///< Buffer for expanding file names +EXTERN char_u msg_buf[MSG_BUF_LEN];         ///< Small buffer for messages +EXTERN char os_buf[MAX(MAXPATHL, IOSIZE)];  ///< Buffer for the os/ layer +  /* When non-zero, postpone redrawing. */  EXTERN int RedrawingDisabled INIT(= 0); @@ -1127,7 +1119,7 @@ EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory"));  EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id"));  EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));  EXTERN char_u e_jobspawn[] INIT(= N_( -      "E903: Process for command \"%s\" could not be spawned")); +        "E903: Process failed to start: %s: \"%s\""));  EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));  EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));  EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s")); diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 0b20647771..9d50058257 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1294,9 +1294,10 @@ static int cs_kill(exarg_T *eap)      }    } -  if (i >= csinfo_size || csinfo[i].fname == NULL) { -    if (p_csverbose) +  if (!killall && (i >= csinfo_size || csinfo[i].fname == NULL)) { +    if (p_csverbose) {        (void)EMSG2(_("E261: cscope connection %s not found"), stok); +    }      return CSCOPE_FAILURE;    } else {      if (killall) { diff --git a/src/nvim/message.c b/src/nvim/message.c index 6104adf2c7..ad1c63eeac 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -11,6 +11,7 @@  #include "nvim/vim.h"  #include "nvim/ascii.h" +#include "nvim/assert.h"  #include "nvim/message.h"  #include "nvim/charset.h"  #include "nvim/eval.h" @@ -3043,6 +3044,38 @@ static char *tv_str(typval_T *tvs, int *idxp)    return s;  } +/// Get pointer argument from the next entry in tvs +/// +/// First entry is 1. Returns NULL for an error. +/// +/// @param[in]  tvs  List of typval_T values. +/// @param[in,out]  idxp  Pointer to the index of the current value. +/// +/// @return Pointer stored in typval_T or NULL. +static const void *tv_ptr(const typval_T *const tvs, int *const idxp) +  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ +#define OFF(attr) offsetof(union typval_vval_union, attr) +  STATIC_ASSERT( +      OFF(v_string) == OFF(v_list) +      && OFF(v_string) == OFF(v_dict) +      && OFF(v_string) == OFF(v_partial) +      && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list) +      && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict) +      && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial), +      "Strings, dictionaries, lists and partials are expected to be pointers, " +      "so that all three of them can be accessed via v_string"); +#undef OFF +  const int idx = *idxp - 1; +  if (tvs[idx].v_type == VAR_UNKNOWN) { +    EMSG(_(e_printf)); +    return NULL; +  } else { +    (*idxp)++; +    return tvs[idx].vval.v_string; +  } +} +  /*   * Get float argument from "idxp" entry in "tvs".  First entry is 1.   */ @@ -3369,11 +3402,11 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,          size_t size_t_arg = 0;          // only defined for p conversion -        void *ptr_arg = NULL; +        const void *ptr_arg = NULL;          if (fmt_spec == 'p') {            length_modifier = '\0'; -          ptr_arg = tvs ? (void *)tv_str(tvs, &arg_idx) : va_arg(ap, void *); +          ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *);            if (ptr_arg) {              arg_sign = 1;            } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9a01891483..d58c8700ca 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4455,12 +4455,14 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)      if (dobin) {        while (col > 0 && ascii_isbdigit(ptr[col])) {          col--; +        col -= utf_head_off(ptr, ptr + col);        }      }      if (dohex) {        while (col > 0 && ascii_isxdigit(ptr[col])) {          col--; +        col -= utf_head_off(ptr, ptr + col);        }      }      if (dobin @@ -4468,6 +4470,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)          && !((col > 0                && (ptr[col] == 'X' || ptr[col] == 'x')                && ptr[col - 1] == '0' +              && !utf_head_off(ptr, ptr + col - 1)                && ascii_isxdigit(ptr[col + 1])))) {          // In case of binary/hexadecimal pattern overlap match, rescan @@ -4475,6 +4478,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)          while (col > 0 && ascii_isdigit(ptr[col])) {            col--; +          col -= utf_head_off(ptr, ptr + col);          }      } @@ -4482,14 +4486,17 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)           && col > 0           && (ptr[col] == 'X' || ptr[col] == 'x')           && ptr[col - 1] == '0' +         && !utf_head_off(ptr, ptr + col - 1)           && ascii_isxdigit(ptr[col + 1]))          || (dobin              && col > 0              && (ptr[col] == 'B' || ptr[col] == 'b')              && ptr[col - 1] == '0' +            && !utf_head_off(ptr, ptr + col - 1)              && ascii_isbdigit(ptr[col + 1]))) {        // Found hexadecimal or binary number, move to its start.          col--; +        col -= utf_head_off(ptr, ptr + col);      } else {        // Search forward and then backward to find the start of number.        col = pos->col; @@ -4511,15 +4518,18 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)    if (visual) {      while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])             && !(doalp && ASCII_ISALPHA(ptr[col]))) { -      col++; -      length--; +      int mb_len = MB_PTR2LEN(ptr + col); + +      col += mb_len; +      length -= mb_len;      }      if (length == 0) {        goto theend;      } -    if (col > pos->col && ptr[col - 1] == '-') { +    if (col > pos->col && ptr[col - 1] == '-' +        && !utf_head_off(ptr, ptr + col - 1)) {        negative = true;        was_positive = false;      } @@ -4565,7 +4575,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)      endpos = curwin->w_cursor;      curwin->w_cursor.col = col;    } else { -    if (col > 0 && ptr[col - 1] == '-' && !visual) { +    if (col > 0 && ptr[col - 1] == '-' +        && !utf_head_off(ptr, ptr + col - 1) && !visual) {        // negative number        col--;        negative = true; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 5a3c1ef2c8..1697d5edb2 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -164,11 +164,11 @@ static char_u   *homedir = NULL;  void init_homedir(void)  { -  // In case we are called a second time (when 'encoding' changes). +  // In case we are called a second time.    xfree(homedir);    homedir = NULL; -  char_u *var = (char_u *)os_getenv("HOME"); +  const char *var = os_getenv("HOME");  #ifdef WIN32    // Typically, $HOME is not defined on Windows, unless the user has @@ -182,10 +182,10 @@ void init_homedir(void)          homepath = "\\";      }      if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) { -      snprintf((char *)NameBuff, MAXPATHL, "%s%s", homedrive, homepath); -      if (NameBuff[0] != NUL) { -        var = NameBuff; -        vim_setenv("HOME", (char *)NameBuff); +      snprintf(os_buf, MAXPATHL, "%s%s", homedrive, homepath); +      if (os_buf[0] != NUL) { +        var = os_buf; +        vim_setenv("HOME", os_buf);        }      }    } @@ -195,17 +195,16 @@ void init_homedir(void)  #ifdef UNIX      // Change to the directory and get the actual path.  This resolves      // links.  Don't do it when we can't return. -    if (os_dirname(NameBuff, MAXPATHL) == OK -        && os_chdir((char *)NameBuff) == 0) { -      if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) { -        var = IObuff; +    if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) { +      if (!os_chdir(var) && os_dirname(IObuff, IOSIZE) == OK) { +        var = (char *)IObuff;        } -      if (os_chdir((char *)NameBuff) != 0) { +      if (os_chdir(os_buf) != 0) {          EMSG(_(e_prev_dir));        }      }  #endif -    homedir = vim_strsave(var); +    homedir = vim_strsave((char_u *)var);    }  } @@ -870,11 +869,12 @@ bool os_setenv_append_path(const char *fname)      return false;    }    const char *tail = (char *)path_tail_with_sep((char_u *)fname); -  const char *dir = (char *)vim_strnsave((char_u *)fname, -                                         (size_t)(tail - fname)); +  size_t dirlen = (size_t)(tail - fname); +  assert(tail >= fname && dirlen + 1 < sizeof(os_buf)); +  xstrlcpy(os_buf, fname, dirlen + 1);    const char *path = os_getenv("PATH");    const size_t pathlen = path ? strlen(path) : 0; -  const size_t newlen = pathlen + strlen(dir) + 2; +  const size_t newlen = pathlen + dirlen + 2;    if (newlen < MAX_ENVPATHLEN) {      char *temp = xmalloc(newlen);      if (pathlen == 0) { @@ -883,7 +883,7 @@ bool os_setenv_append_path(const char *fname)        xstrlcpy(temp, path, newlen);        xstrlcat(temp, ENV_SEPSTR, newlen);      } -    xstrlcat(temp, dir, newlen); +    xstrlcat(temp, os_buf, newlen);      os_setenv("PATH", temp, 1);      xfree(temp);      return true; diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 097c672887..e930561234 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -275,8 +275,8 @@ static bool is_executable(const char *name)  static bool is_executable_ext(char *name, const char *pathext)    FUNC_ATTR_NONNULL_ALL  { -  xstrlcpy((char *)NameBuff, name, sizeof(NameBuff)); -  char *buf_end = xstrchrnul((char *)NameBuff, '\0'); +  xstrlcpy(os_buf, name, sizeof(os_buf)); +  char *buf_end = xstrchrnul(os_buf, '\0');    for (const char *ext = pathext; *ext; ext++) {      // Skip the extension if there is no suffix after a '.'.      if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) { @@ -287,7 +287,7 @@ static bool is_executable_ext(char *name, const char *pathext)      const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR);      STRLCPY(buf_end, ext, ext_end - ext + 1); -    if (is_executable((char *)NameBuff)) { +    if (is_executable(os_buf)) {        return true;      } diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index b57a69b82b..f5ba0c2612 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -33,7 +33,8 @@  # include "os/pty_process_unix.c.generated.h"  #endif -bool pty_process_spawn(PtyProcess *ptyproc) +/// @returns zero on success, or negative error code +int pty_process_spawn(PtyProcess *ptyproc)    FUNC_ATTR_NONNULL_ALL  {    static struct termios termios; @@ -41,6 +42,7 @@ bool pty_process_spawn(PtyProcess *ptyproc)      init_termios(&termios);    } +  int status = 0;  // zero or negative error code (libuv convention)    Process *proc = (Process *)ptyproc;    assert(!proc->err);    uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); @@ -50,8 +52,9 @@ bool pty_process_spawn(PtyProcess *ptyproc)    int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize);    if (pid < 0) { +    status = -errno;      ELOG("forkpty failed: %s", strerror(errno)); -    return false; +    return status;    } else if (pid == 0) {      init_child(ptyproc);      abort(); @@ -60,30 +63,34 @@ bool pty_process_spawn(PtyProcess *ptyproc)    // make sure the master file descriptor is non blocking    int master_status_flags = fcntl(master, F_GETFL);    if (master_status_flags == -1) { +    status = -errno;      ELOG("Failed to get master descriptor status flags: %s", strerror(errno));      goto error;    }    if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) { +    status = -errno;      ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno));      goto error;    } -  if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) { +  if (proc->in +      && (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) {      goto error;    } -  if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) { +  if (proc->out +      && (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) {      goto error;    }    ptyproc->tty_fd = master;    proc->pid = pid; -  return true; +  return 0;  error:    close(master);    kill(pid, SIGKILL);    waitpid(pid, NULL, 0); -  return false; +  return status;  }  void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) @@ -137,9 +144,10 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL      return;    } +  char *prog = ptyproc->process.argv[0];    setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); -  execvp(ptyproc->process.argv[0], ptyproc->process.argv); -  fprintf(stderr, "execvp failed: %s\n", strerror(errno)); +  execvp(prog, ptyproc->process.argv); +  fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog);  }  static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL @@ -197,22 +205,24 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL    termios->c_cc[VTIME]    = 0;  } -static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe) +static int set_duplicating_descriptor(int fd, uv_pipe_t *pipe)    FUNC_ATTR_NONNULL_ALL  { +  int status = 0;  // zero or negative error code (libuv convention)    int fd_dup = dup(fd);    if (fd_dup < 0) { +    status = -errno;      ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); -    return false; +    return status;    } -  int uv_result = uv_pipe_open(pipe, fd_dup); -  if (uv_result) { +  status = uv_pipe_open(pipe, fd_dup); +  if (status) {      ELOG("Failed to set pipe to descriptor %d: %s", -         fd_dup, uv_strerror(uv_result)); +         fd_dup, uv_strerror(status));      close(fd_dup); -    return false; +    return status;    } -  return true; +  return status;  }  static void chld_handler(uv_signal_t *handle, int signum) diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 9514936ad0..a5fd37ed5c 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -124,13 +124,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)    size_t nread; -  int status = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args), -                            input.data, -                            input.len, -                            output_ptr, -                            &nread, -                            emsg_silent, -                            forward_output); +  int exitcode = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args), +                              input.data, input.len, output_ptr, &nread, +                              emsg_silent, forward_output);    xfree(input.data); @@ -139,16 +135,16 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)      xfree(output);    } -  if (!emsg_silent && status != 0 && !(opts & kShellOptSilent)) { +  if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {      MSG_PUTS(_("\nshell returned ")); -    msg_outnum(status); +    msg_outnum(exitcode);      msg_putchar('\n');    }    State = current_state;    signal_accept_deadly(); -  return status; +  return exitcode;  }  /// os_system - synchronously execute a command in the shell @@ -157,7 +153,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)  ///   char *output = NULL;  ///   size_t nread = 0;  ///   char *argv[] = {"ls", "-la", NULL}; -///   int status = os_sytem(argv, NULL, 0, &output, &nread); +///   int exitcode = os_sytem(argv, NULL, 0, &output, &nread);  ///  /// @param argv The commandline arguments to be passed to the shell. `argv`  ///             will be consumed. @@ -218,11 +214,14 @@ static int do_os_system(char **argv,    proc->in = input != NULL ? &in : NULL;    proc->out = &out;    proc->err = &err; -  if (!process_spawn(proc)) { +  int status = process_spawn(proc); +  if (status) {      loop_poll_events(&main_loop, 0); -    // Failed, probably due to 'sh' not being executable +    // Failed, probably 'shell' is not executable.      if (!silent) { -      MSG_PUTS(_("\nCannot execute ")); +      MSG_PUTS(_("\nshell failed to start: ")); +      msg_outtrans((char_u *)os_strerror(status)); +      MSG_PUTS(": ");        msg_outtrans((char_u *)prog);        msg_putchar('\n');      } @@ -262,7 +261,7 @@ static int do_os_system(char **argv,    // busy state.    ui_busy_start();    ui_flush(); -  int status = process_wait(proc, -1, NULL); +  int exitcode = process_wait(proc, -1, NULL);    if (!got_int && out_data_decide_throttle(0)) {      // Last chunk of output was skipped; display it now.      out_data_ring(NULL, SIZE_MAX); @@ -289,7 +288,7 @@ static int do_os_system(char **argv,    assert(multiqueue_empty(events));    multiqueue_free(events); -  return status; +  return exitcode;  }  ///  - ensures at least `desired` bytes in buffer @@ -321,7 +320,7 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count,  /// Tracks output received for the current executing shell command, and displays  /// a pulsing "..." when output should be skipped. Tracking depends on the  /// synchronous/blocking nature of ":!". -// +///  /// Purpose:  ///   1. CTRL-C is more responsive. #1234 #5396  ///   2. Improves performance of :! (UI, esp. TUI, is the bottleneck). diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index 61920697d0..2d99d67099 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -5264,12 +5264,21 @@ msgstr "Vim est un logiciel libre"  msgid "Help poor children in Uganda!"  msgstr "Aidez les enfants pauvres d'Ouganda !" -msgid "type  :help iccf<Enter>       for information " -msgstr "tapez  :help iccf<Entre>       pour plus d'informations          " +msgid "type  :help nvim<Enter>       if you are new! " +msgstr "tapez  :help nvim<Entre>       si vous tes nouveau!             " + +msgid "type  :CheckHealth<Enter>     to optimize Nvim" +msgstr "tapez  :CheckHealth<Entre>     pour optimiser Nvim               "  msgid "type  :q<Enter>               to exit         "  msgstr "tapez  :q<Entre>               pour sortir du programme          " +msgid "type  :help<Enter>            for help        " +msgstr "tapez  :help<Entre>            pour obtenir de l'aide            " + +msgid "type  :help iccf<Enter>       for information " +msgstr "tapez  :help iccf<Entre>       pour plus d'informations          " +  msgid "type  :help<Enter>  or  <F1>  for on-line help"  msgstr "tapez  :help<Entre>  ou  <F1>  pour accder  l'aide en ligne    " diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 7bcc78e58a..721300c334 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -2,12 +2,13 @@  # Makefile to run all tests for Vim  # -export SHELL := sh - -VIMPROG := ../../../build/bin/nvim +NVIM_PRG ?= ../../../build/bin/nvim  SCRIPTSOURCE := ../../../runtime -SCRIPTS := \ +export SHELL := sh +export NVIM_PRG := $(NVIM_PRG) + +SCRIPTS ?= \             test13.out             \             test14.out             \             test17.out             \ @@ -25,17 +26,20 @@ SCRIPTS := \             test79.out             \             test_marks.out         \ -# Tests using runtest.vim.vim. +# Tests using runtest.vim.  # Keep test_alot*.res as the last one, sort the others. -NEW_TESTS = \ +NEW_TESTS ?= \  	    test_bufwintabinfo.res \  	    test_cmdline.res \  	    test_cscope.res \ +	    test_digraph.res \  	    test_diffmode.res \  	    test_gn.res \  	    test_hardcopy.res \  	    test_help_tagjump.res \  	    test_history.res \ +	    test_increment.res \ +	    test_increment_dbcs.res \  	    test_langmap.res \  	    test_match.res \  	    test_matchadd_conceal.res \ @@ -98,13 +102,13 @@ report:  	                 echo ALL DONE;        \  	             fi" -test1.out: $(VIMPROG) +test1.out: $(NVIM_PRG) -$(SCRIPTS) $(SCRIPTS_GUI): $(VIMPROG) test1.out +$(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out  RM_ON_RUN   := test.out X* viminfo  RM_ON_START := test.ok -RUN_VIM     := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(VIMPROG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in +RUN_VIM     := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in  clean:  	-rm -rf *.out          \ @@ -173,7 +177,7 @@ nolog:  # New style of tests uses Vim script with assert calls.  These are easier  # to write and a lot easier to read and debug.  # Limitation: Only works with the +eval feature. -RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin +RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --noplugin  newtests: newtestssilent  	@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \ diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index eb5912086b..316aba968d 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -65,6 +65,7 @@ set shellslash  " Align with vim defaults.  set directory^=.  set nohidden +set backspace=  function RunTheTest(test)    echo 'Executing ' . a:test diff --git a/src/nvim/testdir/samples/memfile_test.c b/src/nvim/testdir/samples/memfile_test.c new file mode 100644 index 0000000000..0fa1e14c40 --- /dev/null +++ b/src/nvim/testdir/samples/memfile_test.c @@ -0,0 +1,143 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved	by Bram Moolenaar + * + * Do ":help uganda"  in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * memfile_test.c: Unittests for memfile.c + * Mostly by Ivan Krasilnikov. + */ + +#undef NDEBUG +#include <assert.h> + +/* Must include main.c because it contains much more than just main() */ +#define NO_VIM_MAIN +#include "main.c" + +/* This file has to be included because the tested functions are static */ +#include "memfile.c" + +#define index_to_key(i) ((i) ^ 15167) +#define TEST_COUNT 50000 + +/* + * Test mf_hash_*() functions. + */ +    static void +test_mf_hash(void) +{ +    mf_hashtab_T   ht; +    mf_hashitem_T  *item; +    blocknr_T      key; +    long_u	   i; +    long_u	   num_buckets; + +    mf_hash_init(&ht); + +    /* insert some items and check invariants */ +    for (i = 0; i < TEST_COUNT; i++) +    { +	assert(ht.mht_count == i); + +	/* check that number of buckets is a power of 2 */ +	num_buckets = ht.mht_mask + 1; +	assert(num_buckets > 0 && (num_buckets & (num_buckets - 1)) == 0); + +	/* check load factor */ +	assert(ht.mht_count <= (num_buckets << MHT_LOG_LOAD_FACTOR)); + +	if (i < (MHT_INIT_SIZE << MHT_LOG_LOAD_FACTOR)) +	{ +	    /* first expansion shouldn't have occurred yet */ +	    assert(num_buckets == MHT_INIT_SIZE); +	    assert(ht.mht_buckets == ht.mht_small_buckets); +	} +	else +	{ +	    assert(num_buckets > MHT_INIT_SIZE); +	    assert(ht.mht_buckets != ht.mht_small_buckets); +	} + +	key = index_to_key(i); +	assert(mf_hash_find(&ht, key) == NULL); + +	/* allocate and add new item */ +	item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE); +	assert(item != NULL); +	item->mhi_key = key; +	mf_hash_add_item(&ht, item); + +	assert(mf_hash_find(&ht, key) == item); + +	if (ht.mht_mask + 1 != num_buckets) +	{ +	    /* hash table was expanded */ +	    assert(ht.mht_mask + 1 == num_buckets * MHT_GROWTH_FACTOR); +	    assert(i + 1 == (num_buckets << MHT_LOG_LOAD_FACTOR)); +	} +    } + +    /* check presence of inserted items */ +    for (i = 0; i < TEST_COUNT; i++) +    { +	key = index_to_key(i); +	item = mf_hash_find(&ht, key); +	assert(item != NULL); +	assert(item->mhi_key == key); +    } + +    /* delete some items */ +    for (i = 0; i < TEST_COUNT; i++) +    { +	if (i % 100 < 70) +	{ +	    key = index_to_key(i); +	    item = mf_hash_find(&ht, key); +	    assert(item != NULL); +	    assert(item->mhi_key == key); + +	    mf_hash_rem_item(&ht, item); +	    assert(mf_hash_find(&ht, key) == NULL); + +	    mf_hash_add_item(&ht, item); +	    assert(mf_hash_find(&ht, key) == item); + +	    mf_hash_rem_item(&ht, item); +	    assert(mf_hash_find(&ht, key) == NULL); + +	    vim_free(item); +	} +    } + +    /* check again */ +    for (i = 0; i < TEST_COUNT; i++) +    { +	key = index_to_key(i); +	item = mf_hash_find(&ht, key); + +	if (i % 100 < 70) +	{ +	    assert(item == NULL); +	} +	else +	{ +	    assert(item != NULL); +	    assert(item->mhi_key == key); +	} +    } + +    /* free hash table and all remaining items */ +    mf_hash_free_all(&ht); +} + +    int +main(void) +{ +    test_mf_hash(); +    return 0; +} diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index edd49a2b63..adbabd61b9 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -456,7 +456,7 @@ function! ExtraVim(...)      " messing up the user's viminfo file.      let redirect = a:0 ?  	\ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : "" -    exec "!echo '" . debug_quits . "q' | ../../../build/bin/nvim -u NONE -N -es" . redirect . +    exec "!echo '" . debug_quits . "q' | $NVIM_PRG -u NONE -N -es" . redirect .  	\ " -c 'debuggreedy|set viminfo+=nviminfo'" .  	\ " -c 'let ExtraVimBegin = " . extra_begin . "'" .  	\ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints . diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim index b6d70f0765..c8d2ebd7da 100644 --- a/src/nvim/testdir/test_cscope.vim +++ b/src/nvim/testdir/test_cscope.vim @@ -1,9 +1,262 @@  " Test for cscope commands. -if !has('cscope') +if !has('cscope') || !executable('cscope') || !has('quickfix')    finish  endif +func CscopeSetupOrClean(setup) +    if a:setup +      noa sp samples/memfile_test.c +      saveas! Xmemfile_test.c +      call system('cscope -bk -fXcscope.out Xmemfile_test.c') +      call system('cscope -bk -fXcscope2.out Xmemfile_test.c') +      cscope add Xcscope.out +      set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i-,a- +    else +      cscope kill -1 +      for file in ['Xcscope.out', 'Xcscope2.out', 'Xmemfile_test.c'] +          call delete(file) +      endfo +    endif +endfunc + +func Test_cscopeWithCscopeConnections() +    call CscopeSetupOrClean(1) +    " Test 0: E568: duplicate cscope database not added +    try +      set nocscopeverbose +      cscope add Xcscope.out +      set cscopeverbose +    catch +      call assert_true(0) +    endtry +    call assert_fails('cscope add', 'E560') +    call assert_fails('cscope add Xcscope.out', 'E568') +    call assert_fails('cscope add doesnotexist.out', 'E563') + +    " Test 1: Find this C-Symbol +    for cmd in ['cs find s main', 'cs find 0 main'] +      let a=execute(cmd) +      " Test 1.1 test where it moves the cursor +      call assert_equal('main(void)', getline('.')) +      " Test 1.2 test the output of the :cs command +      call assert_match('\n(1 of 1): <<main>> main(void )', a) +    endfor + +    " Test 2: Find this definition +    for cmd in ['cs find g test_mf_hash', 'cs find 1 test_mf_hash'] +      exe cmd +      call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', '    static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1)) +    endfor + +    " Test 3: Find functions called by this function +    for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash'] +      let a=execute(cmd) +      call assert_match('\n(1 of 42): <<mf_hash_init>> mf_hash_init(&ht);', a) +      call assert_equal('    mf_hash_init(&ht);', getline('.')) +    endfor + +    " Test 4: Find functions calling this function +    for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash'] +      let a=execute(cmd) +      call assert_match('\n(1 of 1): <<main>> test_mf_hash();', a) +      call assert_equal('    test_mf_hash();', getline('.')) +    endfor + +    " Test 5: Find this text string +    for cmd in ['cs find t Bram', 'cs find 4 Bram'] +      let a=execute(cmd) +      call assert_match('(1 of 1): <<<unknown>>>  \* VIM - Vi IMproved^Iby Bram Moolenaar', a) +      call assert_equal(' * VIM - Vi IMproved	by Bram Moolenaar', getline('.')) +    endfor + +    " Test 6: Find this egrep pattern +    " test all matches returned by cscope +    for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.'] +      let a=execute(cmd) +      call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a) +      call assert_equal('#include <assert.h>', getline('.')) +      cnext +      call assert_equal('#include "main.c"', getline('.')) +      cnext +      call assert_equal('#include "memfile.c"', getline('.')) +      call assert_fails('cnext', 'E553:') +    endfor + +    " Test 7: Find the same egrep pattern using lcscope this time. +    let a=execute('lcs find e ^\#includ.') +    call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a) +    call assert_equal('#include <assert.h>', getline('.')) +    lnext +    call assert_equal('#include "main.c"', getline('.')) +    lnext +    call assert_equal('#include "memfile.c"', getline('.')) +    call assert_fails('lnext', 'E553:') + +    " Test 8: Find this file +    for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c'] +      enew +      let a=execute(cmd) +      call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+C') +      call assert_equal('Xmemfile_test.c', @%) +    endfor + +    " Test 9: Find files #including this file +    for cmd in ['cs find i assert.h', 'cs find 8 assert.h'] +      enew +      let a=execute(cmd) +      let alines = split(a, '\n', 1) +      call assert_equal('', alines[0]) +      call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+C') +      call assert_equal('(1 of 1): <<global>> #include <assert.h>', alines[2]) +      call assert_equal('#include <assert.h>', getline('.')) +    endfor + +    " Test 10: Invalid find command +    call assert_fails('cs find x', 'E560:') + +    " Test 11: Find places where this symbol is assigned a value +    " this needs a cscope >= 15.8 +    " unfortunately, Travis has cscope version 15.7 +    let cscope_version=systemlist('cscope --version')[0] +    let cs_version=str2float(matchstr(cscope_version, '\d\+\(\.\d\+\)\?')) +    if cs_version >= 15.8 +      for cmd in ['cs find a item', 'cs find 9 item'] +        let a=execute(cmd) +        call assert_equal(['', '(1 of 4): <<test_mf_hash>> item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE);'], split(a, '\n', 1)) +        call assert_equal('	item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE);', getline('.')) +        cnext +        call assert_equal('	item = mf_hash_find(&ht, key);', getline('.')) +        cnext +        call assert_equal('	    item = mf_hash_find(&ht, key);', getline('.')) +        cnext +        call assert_equal('	item = mf_hash_find(&ht, key);', getline('.')) +      endfor +    endif + +    " Test 12: leading whitespace is not removed for cscope find text +    let a=execute('cscope find t     test_mf_hash') +    call assert_equal(['', '(1 of 1): <<<unknown>>>     test_mf_hash();'], split(a, '\n', 1)) +    call assert_equal('    test_mf_hash();', getline('.')) + +    " Test 13: test with scscope +    let a=execute('scs find t Bram') +    call assert_match('(1 of 1): <<<unknown>>>  \* VIM - Vi IMproved^Iby Bram Moolenaar', a) +    call assert_equal(' * VIM - Vi IMproved	by Bram Moolenaar', getline('.')) + +    " Test 14: cscope help +    for cmd in ['cs', 'cs help', 'cs xxx'] +      let a=execute(cmd) +      call assert_match('^cscope commands:\n', a) +      call assert_match('\nadd  :', a) +      call assert_match('\nfind :', a) +      call assert_match('\nhelp : Show this message', a) +      call assert_match('\nkill : Kill a connection', a) +      call assert_match('\nreset: Reinit all connections', a) +      call assert_match('\nshow : Show connections', a) +    endfor +    let a=execute('scscope help') +    call assert_match('This cscope command does not support splitting the window\.', a) + +    " Test 15: reset connections +    let a=execute('cscope reset') +    call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a) +    call assert_match('\nAll cscope databases reset', a) + +    " Test 16: cscope show +    let a=execute('cscope show') +    call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a) + +    " Test 17: cstag and 'csto' option +    set csto=0 +    let a=execute('cstag TEST_COUNT') +    call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a) +    call assert_equal('#define TEST_COUNT 50000', getline('.')) +    set csto=1 +    let a=execute('cstag index_to_key') +    call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a) +    call assert_equal('#define index_to_key(i) ((i) ^ 15167)', getline('.')) +    call assert_fails('cstag xxx', 'E257:') +    call assert_fails('cstag', 'E562:') + +    " Test 18: 'cst' option +    set nocst +    call assert_fails('tag TEST_COUNT', 'E426:') +    set cst +    let a=execute('tag TEST_COUNT') +    call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a) +    call assert_equal('#define TEST_COUNT 50000', getline('.')) +    let a=execute('tags') +    call assert_match('1  1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a) + +    " Test 19: this should trigger call to cs_print_tags() +    " Unclear how to check result though, we just exercise the code. +    set cst cscopequickfix=s0 +    call feedkeys(":cs find s main\<CR>", 't') + +    " Test 20: cscope kill +    call assert_fails('cscope kill 2', 'E261:') +    call assert_fails('cscope kill xxx', 'E261:') + +    let a=execute('cscope kill 0') +    call assert_match('cscope connection 0 closed', a) + +    cscope add Xcscope.out +    let a=execute('cscope kill Xcscope.out') +    call assert_match('cscope connection Xcscope.out closed', a) + +    cscope add Xcscope.out . +    let a=execute('cscope kill -1') +    call assert_match('cscope connection .*Xcscope.out closed', a) +    let a=execute('cscope kill -1') +    call assert_equal('', a) + +    " Test 21: 'csprg' option +    call assert_equal('cscope', &csprg) +    set csprg=doesnotexist +    call assert_fails('cscope add Xcscope2.out', 'E609:') +    set csprg=cscope + +    " Test 22: multiple cscope connections +    cscope add Xcscope.out +    cscope add Xcscope2.out . -C +    let a=execute('cscope show') +    call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a) +    call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a) + +    " Test 23: test Ex command line completion +    call feedkeys(":cs \<C-A>\<C-B>\"\<CR>", 'tx') +    call assert_equal('"cs add find help kill reset show', @:) + +    call feedkeys(":scs \<C-A>\<C-B>\"\<CR>", 'tx') +    call assert_equal('"scs find', @:) + +    call feedkeys(":cs find \<C-A>\<C-B>\"\<CR>", 'tx') +    call assert_equal('"cs find a c d e f g i s t', @:) + +    call feedkeys(":cs kill \<C-A>\<C-B>\"\<CR>", 'tx') +    call assert_equal('"cs kill -1 0 1', @:) + +    call feedkeys(":cs add Xcscope\<C-A>\<C-B>\"\<CR>", 'tx') +    call assert_equal('"cs add Xcscope.out Xcscope2.out', @:) + +    " Test 24: cscope_connection() +    call assert_equal(cscope_connection(), 1) +    call assert_equal(cscope_connection(0, 'out'), 1) +    call assert_equal(cscope_connection(0, 'xxx'), 1) +    call assert_equal(cscope_connection(1, 'out'), 1) +    call assert_equal(cscope_connection(1, 'xxx'), 0) +    call assert_equal(cscope_connection(2, 'out'), 0) +    call assert_equal(cscope_connection(3, 'xxx', '..'), 0) +    call assert_equal(cscope_connection(3, 'out', 'xxx'), 0) +    call assert_equal(cscope_connection(3, 'out', '.'), 1) +    call assert_equal(cscope_connection(4, 'out', '.'), 0) + +    " CleanUp +    call CscopeSetupOrClean(0) + +endfunc +  func Test_cscopequickfix()    set cscopequickfix=s-,g-,d+,c-,t+,e-,f0,i-,a-    call assert_equal('s-,g-,d+,c-,t+,e-,f0,i-,a-', &cscopequickfix) @@ -13,3 +266,14 @@ func Test_cscopequickfix()    call assert_fails('set cscopequickfix=s7', 'E474:')    call assert_fails('set cscopequickfix=s-a', 'E474:')  endfunc + +func Test_withoutCscopeConnection() +  call assert_equal(cscope_connection(), 0) + +  call assert_fails('cscope find s main', 'E567:') +  let a=execute('cscope show') +  call assert_match('no cscope connections', a) +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 7666594862..5de394de8e 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -202,3 +202,19 @@ func Test_diffget_diffput()    bwipe!    enew!  endfunc + +func Test_diffoff() +  enew! +  call setline(1, ['Two', 'Three']) +  let normattr = screenattr(1, 1) +  diffthis +  botright vert new +  call setline(1, ['One', '', 'Two', 'Three']) +  diffthis +  redraw +  diffoff! +  redraw +  call assert_equal(normattr, screenattr(1, 1)) +  bwipe! +  bwipe! +endfunc diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim new file mode 100644 index 0000000000..95500853f2 --- /dev/null +++ b/src/nvim/testdir/test_digraph.vim @@ -0,0 +1,461 @@ +" Tests for digraphs + +if !has("digraphs") || !has("multi_byte") +  finish +endif + +func! Put_Dig(chars) +  exe "norm! o\<c-k>".a:chars +endfu + +func! Put_Dig_BS(char1, char2) +  exe "norm! o".a:char1."\<bs>".a:char2 +endfu + +func! Test_digraphs() +  new +  call Put_Dig("00") +  call assert_equal("∞", getline('.')) +  " not a digraph +  call Put_Dig("el") +  call assert_equal("l", getline('.')) +  call Put_Dig("ht") +  call assert_equal("þ", getline('.')) +  " digraph "ab" is the same as "ba" +  call Put_Dig("ab") +  call Put_Dig("ba") +  call assert_equal(["ば","ば"], getline(line('.')-1,line('.'))) +  " Euro sign +  call Put_Dig("e=") +  call Put_Dig("=e") +  call Put_Dig("Eu") +  call Put_Dig("uE") +  call assert_equal(['е']+repeat(["€"],3), getline(line('.')-3,line('.'))) +  " Rouble sign +  call Put_Dig("R=") +  call Put_Dig("=R") +  call Put_Dig("=P") +  call Put_Dig("P=") +  call assert_equal(['Р']+repeat(["₽"],2)+['П'], getline(line('.')-3,line('.'))) +  " Not a digraph +  call Put_Dig("a\<bs>") +  call Put_Dig("\<bs>a") +  call assert_equal(["<BS>", "<BS>a"], getline(line('.')-1,line('.'))) +  " Grave +  call Put_Dig("a!") +  call Put_Dig("!e") +  call Put_Dig("b!") " not defined +  call assert_equal(["à", "è", "!"], getline(line('.')-2,line('.'))) +  " Acute accent +  call Put_Dig("a'") +  call Put_Dig("'e") +  call Put_Dig("b'") " not defined +  call assert_equal(["á", "é", "'"], getline(line('.')-2,line('.'))) +  " Cicumflex +  call Put_Dig("a>") +  call Put_Dig(">e") +  call Put_Dig("b>") " not defined +  call assert_equal(['â', 'ê', '>'], getline(line('.')-2,line('.'))) +  " Tilde +  call Put_Dig("o~") +  call Put_Dig("~u") " not defined +  call Put_Dig("z~") " not defined +  call assert_equal(['õ', 'u', '~'], getline(line('.')-2,line('.'))) +  " Tilde +  call Put_Dig("o?") +  call Put_Dig("?u") +  call Put_Dig("z?") " not defined +  call assert_equal(['õ', 'ũ', '?'], getline(line('.')-2,line('.'))) +  " Macron +  call Put_Dig("o-") +  call Put_Dig("-u") +  call Put_Dig("z-") " not defined +  call assert_equal(['ō', 'ū', '-'], getline(line('.')-2,line('.'))) +  " Breve +  call Put_Dig("o(") +  call Put_Dig("(u") +  call Put_Dig("z(") " not defined +  call assert_equal(['ŏ', 'ŭ', '('], getline(line('.')-2,line('.'))) +  " Dot above +  call Put_Dig("b.") +  call Put_Dig(".e") +  call Put_Dig("a.") " not defined +  call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.'))) +  " Diaresis +  call Put_Dig("a:") +  call Put_Dig(":u") +  call Put_Dig("b:") " not defined +  call assert_equal(['ä', 'ü', ':'], getline(line('.')-2,line('.'))) +  " Cedilla +  call Put_Dig("',") +  call Put_Dig(",C") +  call Put_Dig("b,") " not defined +  call assert_equal(['¸', 'Ç', ','], getline(line('.')-2,line('.'))) +  " Underline +  call Put_Dig("B_") +  call Put_Dig("_t") +  call Put_Dig("a_") " not defined +  call assert_equal(['Ḇ', 'ṯ', '_'], getline(line('.')-2,line('.'))) +  " Stroke +  call Put_Dig("j/") +  call Put_Dig("/l") +  call Put_Dig("b/") " not defined +  call assert_equal(['/', 'ł', '/'], getline(line('.')-2,line('.'))) +  " Double acute +  call Put_Dig('O"') +  call Put_Dig('"y') +  call Put_Dig('b"') " not defined +  call assert_equal(['Ő', 'ÿ', '"'], getline(line('.')-2,line('.'))) +  " Ogonek +  call Put_Dig('u;') +  call Put_Dig(';E') +  call Put_Dig('b;') " not defined +  call assert_equal(['ų', 'Ę', ';'], getline(line('.')-2,line('.'))) +  " Caron +  call Put_Dig('u<') +  call Put_Dig('<E') +  call Put_Dig('b<') " not defined +  call assert_equal(['ǔ', 'Ě', '<'], getline(line('.')-2,line('.'))) +  " Ring above +  call Put_Dig('u0') +  call Put_Dig('0E') " not defined +  call Put_Dig('b0') " not defined +  call assert_equal(['ů', 'E', '0'], getline(line('.')-2,line('.'))) +  " Hook +  call Put_Dig('u2') +  call Put_Dig('2E') +  call Put_Dig('b2') " not defined +  call assert_equal(['ủ', 'Ẻ', '2'], getline(line('.')-2,line('.'))) +  " Horn +  call Put_Dig('u9') +  call Put_Dig('9E') " not defined +  call Put_Dig('b9') " not defined +  call assert_equal(['ư', 'E', '9'], getline(line('.')-2,line('.'))) +  " Cyrillic +  call Put_Dig('u=') +  call Put_Dig('=b') +  call Put_Dig('=_') +  call assert_equal(['у', 'б', '〓'], getline(line('.')-2,line('.'))) +  " Greek +  call Put_Dig('u*') +  call Put_Dig('*b') +  call Put_Dig('*_') +  call assert_equal(['υ', 'β', '々'], getline(line('.')-2,line('.'))) +  " Greek/Cyrillic special +  call Put_Dig('u%') +  call Put_Dig('%b') " not defined +  call Put_Dig('%_') " not defined +  call assert_equal(['ύ', 'b', '_'], getline(line('.')-2,line('.'))) +  " Arabic +  call Put_Dig('u+') +  call Put_Dig('+b') +  call Put_Dig('+_') " japanese industrial symbol +  call assert_equal(['+', 'ب', '〄'], getline(line('.')-2,line('.'))) +  " Hebrew +  call Put_Dig('Q+') +  call Put_Dig('+B') +  call Put_Dig('+X') +  call assert_equal(['ק', 'ב', 'ח'], getline(line('.')-2,line('.'))) +  " Latin +  call Put_Dig('a3') +  call Put_Dig('A3') +  call Put_Dig('3X') +  call assert_equal(['ǣ', 'Ǣ', 'X'], getline(line('.')-2,line('.'))) +  " Bopomofo +  call Put_Dig('a4') +  call Put_Dig('A4') +  call Put_Dig('4X') +  call assert_equal(['ㄚ', '4', 'X'], getline(line('.')-2,line('.'))) +  " Hiragana +  call Put_Dig('a5') +  call Put_Dig('A5') +  call Put_Dig('5X') +  call assert_equal(['あ', 'ぁ', 'X'], getline(line('.')-2,line('.'))) +  " Katakana +  call Put_Dig('a6') +  call Put_Dig('A6') +  call Put_Dig('6X') +  call assert_equal(['ァ', 'ア', 'X'], getline(line('.')-2,line('.'))) +  " Superscripts +  call Put_Dig('1S') +  call Put_Dig('2S') +  call Put_Dig('3S') +  call assert_equal(['¹', '²', '³'], getline(line('.')-2,line('.'))) +  " Subscripts +  call Put_Dig('1s') +  call Put_Dig('2s') +  call Put_Dig('3s') +  call assert_equal(['₁', '₂', '₃'], getline(line('.')-2,line('.'))) +  " Eszet (only lowercase) +  call Put_Dig("ss") +  call Put_Dig("SS") " start of string +  call assert_equal(["ß", ""], getline(line('.')-1,line('.'))) +  " High bit set +  call Put_Dig("a ") +  call Put_Dig(" A") +  call assert_equal(['á', 'Á'], getline(line('.')-1,line('.'))) +  " Escape is not part of a digraph +  call Put_Dig("a\<esc>") +  call Put_Dig("\<esc>A") +  call assert_equal(['', 'A'], getline(line('.')-1,line('.'))) +  " define some custom digraphs +  " old: 00 ∞ +  " old: el l +  digraph 00 9216 +  digraph el 0252 +  call Put_Dig("00") +  call Put_Dig("el") +  " Reset digraphs +  digraph 00 8734 +  digraph el 108 +  call Put_Dig("00") +  call Put_Dig("el") +  call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.'))) +  bw! +endfunc + +func! Test_digraphs_option() +  " reset whichwrap option, so that testing <esc><bs>A works, +  " without moving up a line +  set digraph ww= +  new +  call Put_Dig_BS("0","0") +  call assert_equal("∞", getline('.')) +  " not a digraph +  call Put_Dig_BS("e","l") +  call assert_equal("l", getline('.')) +  call Put_Dig_BS("h","t") +  call assert_equal("þ", getline('.')) +  " digraph "ab" is the same as "ba" +  call Put_Dig_BS("a","b") +  call Put_Dig_BS("b","a") +  call assert_equal(["ば","ば"], getline(line('.')-1,line('.'))) +  " Euro sign +  call Put_Dig_BS("e","=") +  call Put_Dig_BS("=","e") +  call Put_Dig_BS("E","u") +  call Put_Dig_BS("u","E") +  call assert_equal(['е']+repeat(["€"],3), getline(line('.')-3,line('.'))) +  " Rouble sign +  call Put_Dig_BS("R","=") +  call Put_Dig_BS("=","R") +  call Put_Dig_BS("=","P") +  call Put_Dig_BS("P","=") +  call assert_equal(['Р']+repeat(["₽"],2)+['П'], getline(line('.')-3,line('.'))) +  " Not a digraph: this is different from <c-k>! +  call Put_Dig_BS("a","\<bs>") +  call Put_Dig_BS("\<bs>","a") +  call assert_equal(['','a'], getline(line('.')-1,line('.'))) +  " Grave +  call Put_Dig_BS("a","!") +  call Put_Dig_BS("!","e") +  call Put_Dig_BS("b","!") " not defined +  call assert_equal(["à", "è", "!"], getline(line('.')-2,line('.'))) +  " Acute accent +  call Put_Dig_BS("a","'") +  call Put_Dig_BS("'","e") +  call Put_Dig_BS("b","'") " not defined +  call assert_equal(["á", "é", "'"], getline(line('.')-2,line('.'))) +  " Cicumflex +  call Put_Dig_BS("a",">") +  call Put_Dig_BS(">","e") +  call Put_Dig_BS("b",">") " not defined +  call assert_equal(['â', 'ê', '>'], getline(line('.')-2,line('.'))) +  " Tilde +  call Put_Dig_BS("o","~") +  call Put_Dig_BS("~","u") " not defined +  call Put_Dig_BS("z","~") " not defined +  call assert_equal(['õ', 'u', '~'], getline(line('.')-2,line('.'))) +  " Tilde +  call Put_Dig_BS("o","?") +  call Put_Dig_BS("?","u") +  call Put_Dig_BS("z","?") " not defined +  call assert_equal(['õ', 'ũ', '?'], getline(line('.')-2,line('.'))) +  " Macron +  call Put_Dig_BS("o","-") +  call Put_Dig_BS("-","u") +  call Put_Dig_BS("z","-") " not defined +  call assert_equal(['ō', 'ū', '-'], getline(line('.')-2,line('.'))) +  " Breve +  call Put_Dig_BS("o","(") +  call Put_Dig_BS("(","u") +  call Put_Dig_BS("z","(") " not defined +  call assert_equal(['ŏ', 'ŭ', '('], getline(line('.')-2,line('.'))) +  " Dot above +  call Put_Dig_BS("b",".") +  call Put_Dig_BS(".","e") +  call Put_Dig_BS("a",".") " not defined +  call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.'))) +  " Diaresis +  call Put_Dig_BS("a",":") +  call Put_Dig_BS(":","u") +  call Put_Dig_BS("b",":") " not defined +  call assert_equal(['ä', 'ü', ':'], getline(line('.')-2,line('.'))) +  " Cedilla +  call Put_Dig_BS("'",",") +  call Put_Dig_BS(",","C") +  call Put_Dig_BS("b",",") " not defined +  call assert_equal(['¸', 'Ç', ','], getline(line('.')-2,line('.'))) +  " Underline +  call Put_Dig_BS("B","_") +  call Put_Dig_BS("_","t") +  call Put_Dig_BS("a","_") " not defined +  call assert_equal(['Ḇ', 'ṯ', '_'], getline(line('.')-2,line('.'))) +  " Stroke +  call Put_Dig_BS("j","/") +  call Put_Dig_BS("/","l") +  call Put_Dig_BS("b","/") " not defined +  call assert_equal(['/', 'ł', '/'], getline(line('.')-2,line('.'))) +  " Double acute +  call Put_Dig_BS('O','"') +  call Put_Dig_BS('"','y') +  call Put_Dig_BS('b','"') " not defined +  call assert_equal(['Ő', 'ÿ', '"'], getline(line('.')-2,line('.'))) +  " Ogonek +  call Put_Dig_BS('u',';') +  call Put_Dig_BS(';','E') +  call Put_Dig_BS('b',';') " not defined +  call assert_equal(['ų', 'Ę', ';'], getline(line('.')-2,line('.'))) +  " Caron +  call Put_Dig_BS('u','<') +  call Put_Dig_BS('<','E') +  call Put_Dig_BS('b','<') " not defined +  call assert_equal(['ǔ', 'Ě', '<'], getline(line('.')-2,line('.'))) +  " Ring above +  call Put_Dig_BS('u','0') +  call Put_Dig_BS('0','E') " not defined +  call Put_Dig_BS('b','0') " not defined +  call assert_equal(['ů', 'E', '0'], getline(line('.')-2,line('.'))) +  " Hook +  call Put_Dig_BS('u','2') +  call Put_Dig_BS('2','E') +  call Put_Dig_BS('b','2') " not defined +  call assert_equal(['ủ', 'Ẻ', '2'], getline(line('.')-2,line('.'))) +  " Horn +  call Put_Dig_BS('u','9') +  call Put_Dig_BS('9','E') " not defined +  call Put_Dig_BS('b','9') " not defined +  call assert_equal(['ư', 'E', '9'], getline(line('.')-2,line('.'))) +  " Cyrillic +  call Put_Dig_BS('u','=') +  call Put_Dig_BS('=','b') +  call Put_Dig_BS('=','_') +  call assert_equal(['у', 'б', '〓'], getline(line('.')-2,line('.'))) +  " Greek +  call Put_Dig_BS('u','*') +  call Put_Dig_BS('*','b') +  call Put_Dig_BS('*','_') +  call assert_equal(['υ', 'β', '々'], getline(line('.')-2,line('.'))) +  " Greek/Cyrillic special +  call Put_Dig_BS('u','%') +  call Put_Dig_BS('%','b') " not defined +  call Put_Dig_BS('%','_') " not defined +  call assert_equal(['ύ', 'b', '_'], getline(line('.')-2,line('.'))) +  " Arabic +  call Put_Dig_BS('u','+') +  call Put_Dig_BS('+','b') +  call Put_Dig_BS('+','_') " japanese industrial symbol +  call assert_equal(['+', 'ب', '〄'], getline(line('.')-2,line('.'))) +  " Hebrew +  call Put_Dig_BS('Q','+') +  call Put_Dig_BS('+','B') +  call Put_Dig_BS('+','X') +  call assert_equal(['ק', 'ב', 'ח'], getline(line('.')-2,line('.'))) +  " Latin +  call Put_Dig_BS('a','3') +  call Put_Dig_BS('A','3') +  call Put_Dig_BS('3','X') +  call assert_equal(['ǣ', 'Ǣ', 'X'], getline(line('.')-2,line('.'))) +  " Bopomofo +  call Put_Dig_BS('a','4') +  call Put_Dig_BS('A','4') +  call Put_Dig_BS('4','X') +  call assert_equal(['ㄚ', '4', 'X'], getline(line('.')-2,line('.'))) +  " Hiragana +  call Put_Dig_BS('a','5') +  call Put_Dig_BS('A','5') +  call Put_Dig_BS('5','X') +  call assert_equal(['あ', 'ぁ', 'X'], getline(line('.')-2,line('.'))) +  " Katakana +  call Put_Dig_BS('a','6') +  call Put_Dig_BS('A','6') +  call Put_Dig_BS('6','X') +  call assert_equal(['ァ', 'ア', 'X'], getline(line('.')-2,line('.'))) +  " Superscripts +  call Put_Dig_BS('1','S') +  call Put_Dig_BS('2','S') +  call Put_Dig_BS('3','S') +  call assert_equal(['¹', '²', '³'], getline(line('.')-2,line('.'))) +  " Subscripts +  call Put_Dig_BS('1','s') +  call Put_Dig_BS('2','s') +  call Put_Dig_BS('3','s') +  call assert_equal(['₁', '₂', '₃'], getline(line('.')-2,line('.'))) +  " Eszet (only lowercase) +  call Put_Dig_BS("s","s") +  call Put_Dig_BS("S","S") " start of string +  call assert_equal(["ß", ""], getline(line('.')-1,line('.'))) +  " High bit set (different from <c-k>) +  call Put_Dig_BS("a"," ") +  call Put_Dig_BS(" ","A") +  call assert_equal([' ', 'A'], getline(line('.')-1,line('.'))) +  " Escape is not part of a digraph (different from <c-k>) +  call Put_Dig_BS("a","\<esc>") +  call Put_Dig_BS("\<esc>","A") +  call assert_equal(['', ''], getline(line('.')-1,line('.'))) +  " define some custom digraphs +  " old: 00 ∞ +  " old: el l +  digraph 00 9216 +  digraph el 0252 +  call Put_Dig_BS("0","0") +  call Put_Dig_BS("e","l") +  " Reset digraphs +  digraph 00 8734 +  digraph el 108 +  call Put_Dig_BS("0","0") +  call Put_Dig_BS("e","l") +  call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.'))) +  set nodigraph ww&vim +  bw! +endfunc + +func! Test_digraphs_output() +  new +  let out = execute(':digraph') +  call assert_equal('Eu €  8364',  matchstr(out, '\C\<Eu\D*8364\>')) +  call assert_equal('=e €  8364',  matchstr(out, '\C=e\D*8364\>')) +  call assert_equal('=R ₽  8381',  matchstr(out, '\C=R\D*8381\>')) +  call assert_equal('=P ₽  8381',  matchstr(out, '\C=P\D*8381\>')) +  call assert_equal('o: ö  246',   matchstr(out, '\C\<o:\D*246\>')) +  call assert_equal('v4 ㄪ 12586', matchstr(out, '\C\<v4\D*12586\>')) +  call assert_equal("'0 ˚  730",   matchstr(out, '\C''0\D*730\>')) +  call assert_equal('Z% Ж  1046',  matchstr(out, '\C\<Z%\D*1046\>')) +  call assert_equal('u- ū  363',   matchstr(out, '\C\<u-\D*363\>')) +  call assert_equal('SH ^A   1',   matchstr(out, '\C\<SH\D*1\>')) +  bw! +endfunc + +func! Test_loadkeymap() +  if !has('keymap') +    return +  endif +  new +  set keymap=czech +  set iminsert=0 +  call feedkeys("o|\<c-^>|01234567890|\<esc>", 'tx') +  call assert_equal("|'é+ěščřžýáíé'", getline('.')) +  " reset keymap and encoding option +  set keymap= +  bw! +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) +endfunc + +" vim: tabstop=2 shiftwidth=0 sts=-1 expandtab diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim new file mode 100644 index 0000000000..e53b569716 --- /dev/null +++ b/src/nvim/testdir/test_increment.vim @@ -0,0 +1,760 @@ +" Tests for using Ctrl-A/Ctrl-X on visual selections + +func SetUp() +  new dummy +  set nrformats&vim +endfunc + +func TearDown() +  bwipe! +endfunc + +" 1) Ctrl-A on visually selected number +" Text: +" foobar-10 +"     Expected: +"     1)    Ctrl-A on start of line: +"     foobar-9 +"     2)    Ctrl-A on visually selected "-10": +"     foobar-9 +"     3)    Ctrl-A on visually selected "10": +"     foobar-11 +"     4)    Ctrl-X on visually selected "-10" +"     foobar-11 +"     5)    Ctrl-X on visually selected "10" +"     foobar-9 +func Test_visual_increment_01() +  call setline(1, repeat(["foobaar-10"], 5)) + +  call cursor(1, 1) +  exec "norm! \<C-A>" +  call assert_equal("foobaar-9", getline('.')) +  call assert_equal([0, 1, 9, 0], getpos('.')) + +  call cursor(2, 1) +  exec "norm! f-v$\<C-A>" +  call assert_equal("foobaar-9", getline('.')) +  call assert_equal([0, 2, 8, 0], getpos('.')) + +  call cursor(3, 1) +  exec "norm! f1v$\<C-A>" +  call assert_equal("foobaar-11", getline('.')) +  call assert_equal([0, 3, 9, 0], getpos('.')) + +  call cursor(4, 1) +  exec "norm! f-v$\<C-X>" +  call assert_equal("foobaar-11", getline('.')) +  call assert_equal([0, 4, 8, 0], getpos('.')) + +  call cursor(5, 1) +  exec "norm! f1v$\<C-X>" +  call assert_equal("foobaar-9", getline('.')) +  call assert_equal([0, 5, 9, 0], getpos('.')) +endfunc + +" 2) Ctrl-A on visually selected lines +" Text: +" 10 +" 20 +" 30 +" 40 +" +"     Expected: +"     1) Ctrl-A on visually selected lines: +" 11 +" 21 +" 31 +" 41 +" +"     2) Ctrl-X on visually selected lines: +" 9 +" 19 +" 29 +" 39 +func Test_visual_increment_02() +  call setline(1, ["10", "20", "30", "40"]) +  exec "norm! GV3k$\<C-A>" +  call assert_equal(["11", "21", "31", "41"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, ["10", "20", "30", "40"]) +  exec "norm! GV3k$\<C-X>" +  call assert_equal(["9", "19", "29", "39"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 3) g Ctrl-A on visually selected lines, with non-numbers in between +" Text: +" 10 +" +" 20 +" +" 30 +" +" 40 +" +"     Expected: +"     1) 2 g Ctrl-A on visually selected lines: +" 12 +" +" 24 +" +" 36 +" +" 48 +"     2) 2 g Ctrl-X on visually selected lines +" 8 +" +" 16 +" +" 24 +" +" 32 +func Test_visual_increment_03() +  call setline(1, ["10", "", "20", "", "30", "", "40"]) +  exec "norm! GV6k2g\<C-A>" +  call assert_equal(["12", "", "24", "", "36", "", "48"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, ["10", "", "20", "", "30", "", "40"]) +  exec "norm! GV6k2g\<C-X>" +  call assert_equal(["8", "", "16", "", "24", "", "32"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 4) Ctrl-A on non-number +" Text: +" foobar-10 +"     Expected: +"     1) visually select foobar: +"     foobar-10 +func Test_visual_increment_04() +  call setline(1, ["foobar-10"]) +  exec "norm! vf-\<C-A>" +  call assert_equal(["foobar-10"], getline(1, '$')) +  " NOTE: I think this is correct behavior... +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 5) g<Ctrl-A> on letter +" Test: +" a +" a +" a +" a +"     Expected: +"     1) g Ctrl-A on visually selected lines +"     b +"     c +"     d +"     e +func Test_visual_increment_05() +  set nrformats+=alpha +  call setline(1, repeat(["a"], 4)) +  exec "norm! GV3kg\<C-A>" +  call assert_equal(["b", "c", "d", "e"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 6) g<Ctrl-A> on letter +" Test: +" z +" z +" z +" z +"     Expected: +"     1) g Ctrl-X on visually selected lines +"     y +"     x +"     w +"     v +func Test_visual_increment_06() +  set nrformats+=alpha +  call setline(1, repeat(["z"], 4)) +  exec "norm! GV3kg\<C-X>" +  call assert_equal(["y", "x", "w", "v"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 7) <Ctrl-A> on letter +" Test: +" 2 +" 1 +" 0 +" -1 +" -2 +" +"     Expected: +"     1) Ctrl-A on visually selected lines +"     3 +"     2 +"     1 +"     0 +"     -1 +" +"     2) Ctrl-X on visually selected lines +"     1 +"     0 +"     -1 +"     -2 +"     -3 +func Test_visual_increment_07() +  call setline(1, ["2", "1", "0", "-1", "-2"]) +  exec "norm! GV4k\<C-A>" +  call assert_equal(["3", "2", "1", "0", "-1"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, ["2", "1", "0", "-1", "-2"]) +  exec "norm! GV4k\<C-X>" +  call assert_equal(["1", "0", "-1", "-2", "-3"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 8) Block increment on 0x9 +" Text: +" 0x9 +" 0x9 +"     Expected: +"     1) Ctrl-A on visually block selected region (cursor at beginning): +"     0xa +"     0xa +"     2) Ctrl-A on visually block selected region (cursor at end) +"     0xa +"     0xa +func Test_visual_increment_08() +  call setline(1, repeat(["0x9"], 2)) +  exec "norm! \<C-V>j$\<C-A>" +  call assert_equal(["0xa", "0xa"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, repeat(["0x9"], 2)) +  exec "norm! gg$\<C-V>+\<C-A>" +  call assert_equal(["0xa", "0xa"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 9) Increment and redo +" Text: +" 2 +" 2 +" +" 3 +" 3 +" +"     Expected: +"     1) 2 Ctrl-A on first 2 visually selected lines +"     4 +"     4 +"     2) redo (.) on 3 +"     5 +"     5 +func Test_visual_increment_09() +  call setline(1, ["2", "2", "", "3", "3", ""]) +  exec "norm! ggVj2\<C-A>" +  call assert_equal(["4", "4", "", "3", "3", ""], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  exec "norm! 3j." +  call assert_equal(["4", "4", "", "5", "5", ""], getline(1, '$')) +  call assert_equal([0, 4, 1, 0], getpos('.')) +endfunc + +" 10) sequentially decrement 1 +" Text: +" 1 +" 1 +" 1 +" 1 +"     Expected: +"     1) g Ctrl-X on visually selected lines +"     0 +"     -1 +"     -2 +"     -3 +func Test_visual_increment_10() +  call setline(1, repeat(["1"], 4)) +  exec "norm! GV3kg\<C-X>" +  call assert_equal(["0", "-1", "-2", "-3"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 11) visually block selected indented lines +" Text: +"     1 +" 1 +"     1 +"     1 +"     Expexted: +"     1) g Ctrl-A on block selected indented lines +"     2 +" 1 +"     3 +"     4 +func Test_visual_increment_11() +  call setline(1, ["    1", "1", "    1", "    1"]) +  exec "norm! f1\<C-V>3jg\<C-A>" +  call assert_equal(["    2", "1", "    3", "    4"], getline(1, '$')) +  call assert_equal([0, 1, 5, 0], getpos('.')) +endfunc + +" 12) visually selected several columns +" Text: +" 0 0 +" 0 0 +" 0 0 +"     Expected: +"     1) 'v' select last zero and first zeroes +"     0 1 +"     1 0 +"     1 0 +func Test_visual_increment_12() +  call setline(1, repeat(["0 0"], 3)) +  exec "norm! $v++\<C-A>" +  call assert_equal(["0 1", "1 0", "1 0"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) +endfunc + +" 13) visually selected part of columns +" Text: +" max: 100px +" max: 200px +" max: 300px +" max: 400px +"     Expected: +"     1) 'v' on first two numbers Ctrl-A +"     max: 110px +"     max: 220px +"     max: 330px +"     max: 400px +"     2) 'v' on first two numbers Ctrl-X +"     max: 90px +"     max: 190px +"     max: 290px +"     max: 400px +func Test_visual_increment_13() +  call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) +  exec "norm! f1\<C-V>l2j\<C-A>" +  call assert_equal(["max: 110px", "max: 210px", "max: 310px", "max: 400px"], getline(1, '$')) +  call assert_equal([0, 1, 6, 0], getpos('.')) + +  call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"]) +  exec "norm! ggf1\<C-V>l2j\<C-X>" +  call assert_equal(["max: 90px", "max: 190px", "max: 290px", "max: 400px"], getline(1, '$')) +  call assert_equal([0, 1, 6, 0], getpos('.')) +endfunc + +" 14) redo in block mode +" Text: +" 1 1 +" 1 1 +"     Expected: +"     1) Ctrl-a on first column, redo on second column +"     2 2 +"     2 2 +func Test_visual_increment_14() +  call setline(1, repeat(["1 1"], 2)) +  exec "norm! G\<C-V>k\<C-A>w." +  call assert_equal(["2 2", "2 2"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) +endfunc + +" 15) block select single numbers +" Text: +" 101 +"     Expected: +"     1) Ctrl-a on visually selected zero +"     111 +func Test_visual_increment_15() +  call setline(1, ["101"]) +  exec "norm! lv\<C-A>" +  call assert_equal(["111"], getline(1, '$')) +  call assert_equal([0, 1, 2, 0], getpos('.')) +endfunc + +" 16) increment right aligned numbers +" Text: +"    1 +"   19 +"  119 +"     Expected: +"     1) Ctrl-a on line selected region +"        2 +"       20 +"      120 +func Test_visual_increment_16() +  call setline(1, ["   1", "  19", " 119"]) +  exec "norm! VG\<C-A>" +  call assert_equal(["   2", "  20", " 120"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 17) block-wise increment and redo +" Text: +"   100 +"   1 +" +"   100 +"   1 +" +"   Expected: +"   1) Ctrl-V j $ on first block, afterwards '.' on second +"   101 +"   2 +" +"   101 +"   2 +func Test_visual_increment_17() +  call setline(1, [" 100", " 1", "", " 100", " 1"]) +  exec "norm! \<C-V>j$\<C-A>2j." +  call assert_equal([" 101", " 2", "", " 101", " 1"], getline(1, '$')) +  call assert_equal([0, 3, 1, 0], getpos('.')) +endfunc + +" 18) repeat of g<Ctrl-a> +" Text: +"   0 +"   0 +"   0 +"   0 +" +"   Expected: +"   1) V 4j g<ctrl-a>, repeat twice afterwards with . +"   3 +"   6 +"   9 +"   12 +func Test_visual_increment_18() +  call setline(1, repeat(["0"], 4)) +  exec "norm! GV3kg\<C-A>" +  exec "norm! .." +  call assert_equal(["3", "6", "9", "12"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 19) increment on number with nrformat including alpha +" Text: +"  1 +"  1a +" +"  Expected: +"  1) <Ctrl-V>j$ <ctrl-a> +"  2 +"  2a +func Test_visual_increment_19() +  set nrformats+=alpha +  call setline(1, ["1", "1a"]) +  exec "norm! \<C-V>G$\<C-A>" +  call assert_equal(["2", "2a"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 20) increment a single letter +" Text: +"  a +" +"  Expected: +"  1) <Ctrl-a> and cursor is on a +"  b +func Test_visual_increment_20() +  set nrformats+=alpha +  call setline(1, ["a"]) +  exec "norm! \<C-A>" +  call assert_equal(["b"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 21) block-wise increment on part of hexadecimal +" Text: +" 0x123456 +" +"   Expected: +"   1) Ctrl-V f3 <ctrl-a> +" 0x124456 +func Test_visual_increment_21() +  call setline(1, ["0x123456"]) +  exec "norm! \<C-V>f3\<C-A>" +  call assert_equal(["0x124456"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 22) Block increment on 0b0 +" Text: +" 0b1 +" 0b1 +"     Expected: +"     1) Ctrl-A on visually block selected region (cursor at beginning): +"     0b10 +"     0b10 +"     2) Ctrl-A on visually block selected region (cursor at end) +"     0b10 +"     0b10 +func Test_visual_increment_22() +  call setline(1, repeat(["0b1"], 2)) +  exec "norm! \<C-V>j$\<C-A>" +  call assert_equal(repeat(["0b10"], 2), getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, repeat(["0b1"], 2)) +  exec "norm! $\<C-V>+\<C-A>" +  call assert_equal(repeat(["0b10"], 2), getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 23) block-wise increment on part of binary +" Text: +" 0b1001 +" +"   Expected: +"   1) Ctrl-V 5l <ctrl-a> +" 0b1011 +func Test_visual_increment_23() +  call setline(1, ["0b1001"]) +  exec "norm! \<C-V>4l\<C-A>" +  call assert_equal(["0b1011"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 24) increment hexadecimal +" Text: +" 0x0b1001 +" +"   Expected: +"   1) <ctrl-a> +" 0x0b1002 +func Test_visual_increment_24() +  call setline(1, ["0x0b1001"]) +  exec "norm! \<C-V>$\<C-A>" +  call assert_equal(["0x0b1002"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 25) increment binary with nrformats including alpha +" Text: +" 0b1001a +" +"   Expected: +"   1) <ctrl-a> +" 0b1010a +func Test_visual_increment_25() +  set nrformats+=alpha +  call setline(1, ["0b1001a"]) +  exec "norm! \<C-V>$\<C-A>" +  call assert_equal(["0b1010a"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" 26) increment binary with 32 bits +" Text: +" 0b11111111111111111111111111111110 +" +"   Expected: +"   1) <ctrl-a> +" 0b11111111111111111111111111111111 +func Test_visual_increment_26() +  set nrformats+=alpha +  call setline(1, ["0b11111111111111111111111111111110"]) +  exec "norm! \<C-V>$\<C-A>" +  call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +  set nrformats-=alpha +endfunc + +" 27) increment with 'rightreft', if supported +func Test_visual_increment_27() +  if exists('+rightleft') +    set rightleft +    call setline(1, ["1234 56"]) + +    exec "norm! $\<C-A>" +    call assert_equal(["1234 57"], getline(1, '$')) +    call assert_equal([0, 1, 7, 0], getpos('.')) + +    exec "norm! \<C-A>" +    call assert_equal(["1234 58"], getline(1, '$')) +    call assert_equal([0, 1, 7, 0], getpos('.')) +    set norightleft +  endif +endfunc + +" Tab code and linewise-visual inc/dec +func Test_visual_increment_28() +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! Vj\<C-A>" +  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! ggVj\<C-X>" +  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" Tab code and linewise-visual inc/dec with 'nrformats'+=alpha +func Test_visual_increment_29() +  set nrformats+=alpha +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! Vj\<C-A>" +  call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! ggVj\<C-X>" +  call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" Tab code and character-visual inc/dec +func Test_visual_increment_30() +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! f1vjf1\<C-A>" +  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) + +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! ggf1vjf1\<C-X>" +  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) +endfunc + +" Tab code and blockwise-visual inc/dec +func Test_visual_increment_31() +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! f1\<C-V>jl\<C-A>" +  call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) + +  call setline(1, ["x\<TAB>10", "\<TAB>-1"]) +  exec "norm! ggf1\<C-V>jl\<C-X>" +  call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$')) +  call assert_equal([0, 1, 3, 0], getpos('.')) +endfunc + +" Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak' +func Test_visual_increment_32() +  28vnew dummy_31 +  set linebreak showbreak=+ +  call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"]) +  exec "norm! ggf0\<C-V>jg_\<C-X>" +  call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$')) +  call assert_equal([0, 1, 6, 0], getpos('.')) +  bwipe! +endfunc + +" Tab code and blockwise-visual increment with $ +func Test_visual_increment_33() +  call setline(1, ["\<TAB>123", "456"]) +  exec "norm! gg0\<C-V>j$\<C-A>" +  call assert_equal(["\<TAB>124", "457"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" Tab code and blockwise-visual increment and redo +func Test_visual_increment_34() +  call setline(1, ["\<TAB>123", "     456789"]) +  exec "norm! gg0\<C-V>j\<C-A>" +  call assert_equal(["\<TAB>123", "     457789"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  exec "norm! .." +  call assert_equal(["\<TAB>123", "     459789"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" Tab code, spaces and character-visual increment and redo +func Test_visual_increment_35() +  call setline(1, ["\<TAB>123", "        123", "\<TAB>123", "\<TAB>123"]) +  exec "norm! ggvjf3\<C-A>..." +  call assert_equal(["\<TAB>127", "        127", "\<TAB>123", "\<TAB>123"], getline(1, '$')) +  call assert_equal([0, 1, 2, 0], getpos('.')) +endfunc + +" Tab code, spaces and blockwise-visual increment and redo +func Test_visual_increment_36() +  call setline(1, ["           123", "\<TAB>456789"]) +  exec "norm! G0\<C-V>kl\<C-A>" +  call assert_equal(["           123", "\<TAB>556789"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) + +  exec "norm! ..." +  call assert_equal(["           123", "\<TAB>856789"], getline(1, '$')) +  call assert_equal([0, 1, 1, 0], getpos('.')) +endfunc + +" block-wise increment and dot-repeat +" Text: +"   1 23 +"   4 56 +"  +" Expected: +"   1) f2 Ctrl-V jl <ctrl-a>, repeat twice afterwards with . +"   1 26 +"   4 59 +" +" Try with and without indent. +func Test_visual_increment_37() +  call setline(1, ["  1 23", "  4 56"]) +  exec "norm! ggf2\<C-V>jl\<C-A>.." +  call assert_equal(["  1 26", "  4 59"], getline(1, 2)) + +  call setline(1, ["1 23", "4 56"]) +  exec "norm! ggf2\<C-V>jl\<C-A>.." +  call assert_equal(["1 26", "4 59"], getline(1, 2)) +endfunc + +" Check redo after the normal mode increment +func Test_visual_increment_38() +  exec "norm! i10\<ESC>5\<C-A>." +  call assert_equal(["20"], getline(1, '$')) +  call assert_equal([0, 1, 2, 0], getpos('.')) +endfunc + +" Test what patch 7.3.414 fixed. Ctrl-A on "000" drops the leading zeros. +func Test_normal_increment_01() +  call setline(1, "000") +  exec "norm! gg0\<C-A>" +  call assert_equal("001", getline(1)) + +  call setline(1, "000") +  exec "norm! gg$\<C-A>" +  call assert_equal("001", getline(1)) + +  call setline(1, "001") +  exec "norm! gg0\<C-A>" +  call assert_equal("002", getline(1)) + +  call setline(1, "001") +  exec "norm! gg$\<C-A>" +  call assert_equal("002", getline(1)) +endfunc + +" Test a regression of patch 7.4.1087 fixed. +func Test_normal_increment_02() +  call setline(1, ["hello 10", "world"]) +  exec "norm! ggl\<C-A>jx" +  call assert_equal(["hello 11", "worl"], getline(1, '$')) +  call assert_equal([0, 2, 4, 0], getpos('.')) +endfunc + +" The test35 unified to this file. +func Test_normal_increment_03() +  call setline(1, ["100     0x100     077     0", +        \          "100     0x100     077     ", +        \          "100     0x100     077     0xfF     0xFf", +        \          "100     0x100     077     "]) +  set nrformats=octal,hex +  exec "norm! gg\<C-A>102\<C-X>\<C-A>l\<C-X>l\<C-A>64\<C-A>128\<C-X>$\<C-X>" +  set nrformats=octal +  exec "norm! j0\<C-A>102\<C-X>\<C-A>l\<C-X>2\<C-A>w65\<C-A>129\<C-X>blx6lD" +  set nrformats=hex +  exec "norm! j0101\<C-X>l257\<C-X>\<C-A>Txldt \<C-A> \<C-X> \<C-X>" +  set nrformats= +  exec "norm! j0200\<C-X>l100\<C-X>w78\<C-X>\<C-A>k" +  call assert_equal(["0     0x0ff     0000     -1", +        \            "0     1x100     0777777", +        \            "-1     0x0     078     0xFE     0xfe", +        \            "-100     -100x100     000     "], getline(1, '$')) +  call assert_equal([0, 3, 25, 0], getpos('.')) +endfunc + + +" vim: tabstop=2 shiftwidth=2 expandtab diff --git a/src/nvim/testdir/test_increment_dbcs.vim b/src/nvim/testdir/test_increment_dbcs.vim new file mode 100644 index 0000000000..ee286a0a24 --- /dev/null +++ b/src/nvim/testdir/test_increment_dbcs.vim @@ -0,0 +1,29 @@ +" Tests for using Ctrl-A/Ctrl-X using DBCS. +if !has('multi_byte') +  finish +endif +scriptencoding cp932 + +func SetUp() +  new +  set nrformats& +endfunc + +func TearDown() +  bwipe! +endfunc + +func Test_increment_dbcs_1() +  set nrformats+=alpha +  call setline(1, ["R1"]) +  exec "norm! 0\<C-A>" +  call assert_equal(["R2"], getline(1, '$')) +  call assert_equal([0, 1, 4, 0], getpos('.')) + +  call setline(1, ["`ab0xDEe"]) +  exec "norm! 0\<C-X>" +  call assert_equal(["`ab0xDDe"], getline(1, '$')) +  call assert_equal([0, 1, 13, 0], getpos('.')) +endfunc + +" vim: shiftwidth=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index 7adf6781b9..5e33597568 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -145,7 +145,7 @@ static int included_patches[] = {    // 2298 NA    // 2297 NA    // 2296, -  // 2295, +  2295,    2294,    // 2293,    // 2292, @@ -161,11 +161,11 @@ static int included_patches[] = {    // 2282 NA    // 2281 NA    // 2280, -  // 2279, +  2279,    // 2278 NA    2277,    // 2276, -  // 2275, +  2275,    2274,    2273,    2272, @@ -265,7 +265,7 @@ static int included_patches[] = {    // 2178,    // 2177,    // 2176 NA -  // 2175, +  2175,    2174,    // 2173,    // 2172, @@ -281,7 +281,7 @@ static int included_patches[] = {    2162,    // 2161,    2160, -  // 2159, +  2159,    2158,    // 2157 NA    // 2156 NA @@ -292,7 +292,7 @@ static int included_patches[] = {    // 2151,    // 2150 NA    2149, -  // 2148, +  2148,    2147,    2146,    // 2145 NA @@ -352,13 +352,13 @@ static int included_patches[] = {    // 2091 NA    // 2090,    // 2089 NA -  // 2088, -  // 2087, +  2088, +  2087,    2086, -  // 2085, -  // 2084, +  2085, +  2084,    // 2083, -  // 2082, +  2082,    2081,    // 2080,    // 2079 NA @@ -478,7 +478,7 @@ static int included_patches[] = {    // 1965 NA    1964,    // 1963 NA -  // 1962, +  1962,    1961,    1960,    // 1959 NA @@ -486,13 +486,13 @@ static int included_patches[] = {    // 1957 NA    1956,    // 1955 NA -  // 1954, +  1954,    1953,    1952,    // 1951 NA    1950,    1949, -  // 1948, +  1948,    // 1947 NA    // 1946 NA    // 1945 NA diff --git a/src/nvim/window.c b/src/nvim/window.c index 510f182353..28269e8889 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4717,8 +4717,6 @@ void set_fraction(win_T *wp)   */  void win_new_height(win_T *wp, int height)  { -  linenr_T lnum; -  int sline, line_size;    int prev_height = wp->w_height;    /* Don't want a negative height.  Happens when splitting a tiny window. @@ -4745,6 +4743,15 @@ void win_new_height(win_T *wp, int height)    wp->w_height = height;    wp->w_skipcol = 0; +  scroll_to_fraction(wp, prev_height); +} + +void scroll_to_fraction(win_T *wp, int prev_height) +{ +    linenr_T lnum; +    int sline, line_size; +    int height = wp->w_height; +    /* Don't change w_topline when height is zero.  Don't set w_topline when     * 'scrollbind' is set and this isn't the current window. */    if (height > 0  | 
