diff options
| -rw-r--r-- | src/nvim/api/private/helpers.h | 6 | ||||
| -rw-r--r-- | src/nvim/buffer.c | 2 | ||||
| -rw-r--r-- | src/nvim/edit.c | 2 | ||||
| -rw-r--r-- | src/nvim/eval.c | 17 | ||||
| -rw-r--r-- | src/nvim/eval/decode.c | 18 | ||||
| -rw-r--r-- | src/nvim/eval/encode.c | 622 | ||||
| -rw-r--r-- | src/nvim/eval/typval_encode.h | 563 | ||||
| -rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
| -rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
| -rw-r--r-- | src/nvim/globals.h | 1 | ||||
| -rw-r--r-- | src/nvim/lib/kvec.h | 257 | ||||
| -rw-r--r-- | src/nvim/lib/queue.h | 158 | ||||
| -rw-r--r-- | src/nvim/main.c | 12 | ||||
| -rw-r--r-- | src/nvim/main.h | 3 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 30 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/server.c | 3 | ||||
| -rw-r--r-- | src/nvim/normal.c | 2 | ||||
| -rw-r--r-- | src/nvim/os/input.c | 12 | ||||
| -rw-r--r-- | src/nvim/os/shell.c | 7 | ||||
| -rw-r--r-- | src/nvim/os/signal.c | 11 | ||||
| -rw-r--r-- | src/nvim/os/time.c | 3 | ||||
| -rw-r--r-- | src/nvim/state.c | 5 | ||||
| -rw-r--r-- | src/nvim/terminal.c | 7 | ||||
| -rw-r--r-- | src/nvim/tui/input.c | 5 | ||||
| -rw-r--r-- | src/nvim/tui/tui.c | 5 | ||||
| -rw-r--r-- | src/nvim/ui_bridge.c | 3 | ||||
| -rw-r--r-- | test/unit/os/shell_spec.lua | 4 | ||||
| -rw-r--r-- | test/unit/strings_spec.lua | 32 | 
28 files changed, 1058 insertions, 736 deletions
| diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a0f14ac7a4..7ed726c7ce 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -62,12 +62,10 @@  #define NIL ((Object) {.type = kObjectTypeNil})  #define PUT(dict, k, v)                                                       \ -  kv_push(KeyValuePair,                                                       \ -          dict,                                                               \ -          ((KeyValuePair) {.key = cstr_to_string(k), .value = v})) +  kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))  #define ADD(array, item)                                                      \ -  kv_push(Object, array, item) +  kv_push(array, item)  #define STATIC_CSTR_AS_STRING(s) ((String) {.data = s, .size = sizeof(s) - 1}) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 72716daf0e..a1f2439e0a 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4950,7 +4950,7 @@ int bufhl_add_hl(buf_T *buf,    bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info,                                                           lnum, true); -  bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); +  bufhl_hl_item_T *hlentry = kv_pushp(*lineinfo);    hlentry->src_id = src_id;    hlentry->hl_id = hl_id;    hlentry->start = col_start; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e131da8fe0..44aaedb9b4 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -961,7 +961,7 @@ static int insert_handle_key(InsertState *s)      break;    case K_EVENT:       // some event -    queue_process_events(loop.events); +    queue_process_events(main_loop.events);      break;    case K_FOCUSGAINED:  // Neovim has been given focus diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 420a712e3e..e6f40a0bb8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -67,6 +67,7 @@  #include "nvim/syntax.h"  #include "nvim/tag.h"  #include "nvim/ui.h" +#include "nvim/main.h"  #include "nvim/mouse.h"  #include "nvim/terminal.h"  #include "nvim/undo.h" @@ -11812,7 +11813,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)    list_T *rv = list_alloc();    ui_busy_start(); -  Queue *waiting_jobs = queue_new_parent(loop_on_put, &loop); +  Queue *waiting_jobs = queue_new_parent(loop_on_put, &main_loop);    // For each item in the input list append an integer to the output list. -3    // is used to represent an invalid job id, -2 is for a interrupted job and    // -1 for jobs that were skipped or timed out. @@ -11890,7 +11891,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)      }      // restore the parent queue for the job      queue_process_events(data->events); -    queue_replace_parent(data->events, loop.events); +    queue_replace_parent(data->events, main_loop.events);    }    queue_free(waiting_jobs); @@ -16534,8 +16535,8 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv)    timer->timer_id = last_timer_id++;    timer->callback = func; -  time_watcher_init(&loop, &timer->tw, timer); -  timer->tw.events = queue_new_child(loop.events); +  time_watcher_init(&main_loop, &timer->tw, timer); +  timer->tw.events = queue_new_child(main_loop.events);    // if main loop is blocked, don't queue up multiple events    timer->tw.blockable = true;    time_watcher_start(&timer->tw, timer_due_cb, timeout, @@ -21712,11 +21713,11 @@ static inline TerminalJobData *common_job_init(char **argv,    data->on_stderr = on_stderr;    data->on_exit = on_exit;    data->self = self; -  data->events = queue_new_child(loop.events); +  data->events = queue_new_child(main_loop.events);    if (pty) { -    data->proc.pty = pty_process_init(&loop, data); +    data->proc.pty = pty_process_init(&main_loop, data);    } else { -    data->proc.uv = libuv_process_init(&loop, data); +    data->proc.uv = libuv_process_init(&main_loop, data);    }    Process *proc = (Process *)&data->proc;    proc->argv = argv; @@ -21814,7 +21815,7 @@ static inline void free_term_job_data(TerminalJobData *data)  {    // data->queue may still be used after this function returns(process_wait), so    // only free in the next event loop iteration -  queue_put(loop.fast_events, free_term_job_data_event, 1, data); +  queue_put(main_loop.fast_events, free_term_job_data_event, 1, data);  }  // vimscript job callbacks must be executed on Nvim main loop diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 0774ef515f..43e9f76c0f 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -101,7 +101,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,    FUNC_ATTR_NONNULL_ALL  {    if (kv_size(*container_stack) == 0) { -    kv_push(ValuesStackItem, *stack, obj); +    kv_push(*stack, obj);      return OK;    }    ContainerStackItem last_container = kv_last(*container_stack); @@ -190,7 +190,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,        *next_map_special = true;        return OK;      } -    kv_push(ValuesStackItem, *stack, obj); +    kv_push(*stack, obj);    }    return OK;  } @@ -628,10 +628,8 @@ int json_decode_string(const char *const buf, const size_t buf_len,    convert_setup(&conv, (char_u *) "utf-8", p_enc);    conv.vc_fail = true;    int ret = OK; -  ValuesStack stack; -  kv_init(stack); -  ContainerStack container_stack; -  kv_init(container_stack); +  ValuesStack stack = KV_INITIAL_VALUE; +  ContainerStack container_stack = KV_INITIAL_VALUE;    rettv->v_type = VAR_UNKNOWN;    bool didcomma = false;    bool didcolon = false; @@ -815,13 +813,13 @@ json_decode_string_cycle_start:            .v_lock = VAR_UNLOCKED,            .vval = { .v_list = list },          }; -        kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { +        kv_push(container_stack, ((ContainerStackItem) {            .stack_index = kv_size(stack),            .s = p,            .container = tv,            .special_val = NULL,          })); -        kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); +        kv_push(stack, OBJ(tv, false, didcomma, didcolon));          break;        }        case '{': { @@ -845,13 +843,13 @@ json_decode_string_cycle_start:              .vval = { .v_dict = dict },            };          } -        kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { +        kv_push(container_stack, ((ContainerStackItem) {            .stack_index = kv_size(stack),            .s = p,            .container = tv,            .special_val = val_list,          })); -        kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); +        kv_push(stack, OBJ(tv, false, didcomma, didcolon));          break;        }        default: { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index c651a50be9..54daf7557e 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -6,6 +6,7 @@  #include <msgpack.h>  #include <inttypes.h> +#include <stddef.h>  #include <assert.h>  #include <math.h> @@ -22,6 +23,7 @@  #include "nvim/ascii.h"  #include "nvim/vim.h"  // For _()  #include "nvim/lib/kvec.h" +#include "nvim/eval/typval_encode.h"  #define ga_concat(a, b) ga_concat(a, (char_u *)b)  #define utf_ptr2char(b) utf_ptr2char((char_u *)b) @@ -32,29 +34,6 @@  #define convert_setup(vcp, from, to) \      (convert_setup(vcp, (char_u *)from, (char_u *)to)) -/// Structure representing current VimL to messagepack conversion state -typedef struct { -  enum { -    kMPConvDict,   ///< Convert dict_T *dictionary. -    kMPConvList,   ///< Convert list_T *list. -    kMPConvPairs,  ///< Convert mapping represented as a list_T* of pairs. -  } type; -  union { -    struct { -      dict_T *dict;    ///< Currently converted dictionary. -      hashitem_T *hi;  ///< Currently converted dictionary item. -      size_t todo;     ///< Amount of items left to process. -    } d;  ///< State of dictionary conversion. -    struct { -      list_T *list;    ///< Currently converted list. -      listitem_T *li;  ///< Currently converted list item. -    } l;  ///< State of list or generic mapping conversion. -  } data;  ///< Data to convert. -} MPConvStackVal; - -/// Stack used to convert VimL values to messagepack. -typedef kvec_t(MPConvStackVal) MPConvStack; -  const char *const encode_special_var_names[] = {    [kSpecialVarNull] = "null",    [kSpecialVarTrue] = "true", @@ -275,368 +254,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,            : OK);  } -/// Code for checking whether container references itself -/// -/// @param[in,out]  val  Container to check. -/// @param  copyID_attr  Name of the container attribute that holds copyID. -///                      After checking whether value of this attribute is -///                      copyID (variable) it is set to copyID. -#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ -    do { \ -      if ((val)->copyID_attr == copyID) { \ -        CONV_RECURSE((val), conv_type); \ -      } \ -      (val)->copyID_attr = copyID; \ -    } while (0) - -#define TV_STRLEN(tv) \ -    (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string)) - -/// Define functions which convert VimL value to something else -/// -/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const -/// tv)` which returns OK or FAIL and helper functions. -/// -/// @param  firstargtype  Type of the first argument. It will be used to return -///                       the results. -/// @param  firstargname  Name of the first argument. -/// @param  name  Name of the target converter. -#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ -static int name##_convert_one_value(firstargtype firstargname, \ -                                    MPConvStack *const mpstack, \ -                                    typval_T *const tv, \ -                                    const int copyID, \ -                                    const char *const objname) \ -  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ -  switch (tv->v_type) { \ -    case VAR_STRING: { \ -      CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \ -      break; \ -    } \ -    case VAR_NUMBER: { \ -      CONV_NUMBER(tv->vval.v_number); \ -      break; \ -    } \ -    case VAR_FLOAT: { \ -      CONV_FLOAT(tv->vval.v_float); \ -      break; \ -    } \ -    case VAR_FUNC: { \ -      CONV_FUNC(tv->vval.v_string); \ -      break; \ -    } \ -    case VAR_LIST: { \ -      if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ -        CONV_EMPTY_LIST(); \ -        break; \ -      } \ -      CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ -      CONV_LIST_START(tv->vval.v_list); \ -      kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ -        .type = kMPConvList, \ -        .data = { \ -          .l = { \ -            .list = tv->vval.v_list, \ -            .li = tv->vval.v_list->lv_first, \ -          }, \ -        }, \ -      })); \ -      break; \ -    } \ -    case VAR_SPECIAL: { \ -      switch (tv->vval.v_special) { \ -        case kSpecialVarNull: { \ -          CONV_NIL(); \ -          break; \ -        } \ -        case kSpecialVarTrue: \ -        case kSpecialVarFalse: { \ -          CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ -          break; \ -        } \ -      } \ -      break; \ -    } \ -    case VAR_DICT: { \ -      if (tv->vval.v_dict == NULL \ -          || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ -        CONV_EMPTY_DICT(); \ -        break; \ -      } \ -      const dictitem_T *type_di; \ -      const dictitem_T *val_di; \ -      if (CONV_ALLOW_SPECIAL \ -          && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ -          && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ -                                  (char_u *) "_TYPE", -1)) != NULL \ -          && type_di->di_tv.v_type == VAR_LIST \ -          && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ -                                 (char_u *) "_VAL", -1)) != NULL) { \ -        size_t i; \ -        for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ -          if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ -            break; \ -          } \ -        } \ -        if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ -          goto name##_convert_one_value_regular_dict; \ -        } \ -        switch ((MessagePackType) i) { \ -          case kMPNil: { \ -            CONV_NIL(); \ -            break; \ -          } \ -          case kMPBoolean: { \ -            if (val_di->di_tv.v_type != VAR_NUMBER) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            CONV_BOOL(val_di->di_tv.vval.v_number); \ -            break; \ -          } \ -          case kMPInteger: { \ -            const list_T *val_list; \ -            varnumber_T sign; \ -            varnumber_T highest_bits; \ -            varnumber_T high_bits; \ -            varnumber_T low_bits; \ -            /* List of 4 integers; first is signed (should be 1 or -1, but */ \ -            /* this is not checked), second is unsigned and have at most */ \ -            /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ -            /* bits is not checked), other unsigned and have at most 31 */ \ -            /* non-zero bits (number of bits is not checked).*/ \ -            if (val_di->di_tv.v_type != VAR_LIST \ -                || (val_list = val_di->di_tv.vval.v_list) == NULL \ -                || val_list->lv_len != 4 \ -                || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ -                || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ -                || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ -                || (highest_bits = \ -                    val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ -                || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ -                || (high_bits = \ -                    val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ -                || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ -                || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ -                               | (uint64_t) (((uint64_t) high_bits) << 31) \ -                               | (uint64_t) low_bits); \ -            if (sign > 0) { \ -              CONV_UNSIGNED_NUMBER(number); \ -            } else { \ -              CONV_NUMBER(-number); \ -            } \ -            break; \ -          } \ -          case kMPFloat: { \ -            if (val_di->di_tv.v_type != VAR_FLOAT) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            CONV_FLOAT(val_di->di_tv.vval.v_float); \ -            break; \ -          } \ -          case kMPString: \ -          case kMPBinary: { \ -            const bool is_string = ((MessagePackType) i == kMPString); \ -            if (val_di->di_tv.v_type != VAR_LIST) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            size_t len; \ -            char *buf; \ -            if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ -                                        &buf)) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            if (is_string) { \ -              CONV_STR_STRING(buf, len); \ -            } else { \ -              CONV_STRING(buf, len); \ -            } \ -            xfree(buf); \ -            break; \ -          } \ -          case kMPArray: { \ -            if (val_di->di_tv.v_type != VAR_LIST) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ -                                 kMPConvList); \ -            CONV_LIST_START(val_di->di_tv.vval.v_list); \ -            kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ -              .type = kMPConvList, \ -              .data = { \ -                .l = { \ -                  .list = val_di->di_tv.vval.v_list, \ -                  .li = val_di->di_tv.vval.v_list->lv_first, \ -                }, \ -              }, \ -            })); \ -            break; \ -          } \ -          case kMPMap: { \ -            if (val_di->di_tv.v_type != VAR_LIST) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            list_T *const val_list = val_di->di_tv.vval.v_list; \ -            if (val_list == NULL || val_list->lv_len == 0) { \ -              CONV_EMPTY_DICT(); \ -              break; \ -            } \ -            for (const listitem_T *li = val_list->lv_first; li != NULL; \ -                 li = li->li_next) { \ -              if (li->li_tv.v_type != VAR_LIST \ -                  || li->li_tv.vval.v_list->lv_len != 2) { \ -                goto name##_convert_one_value_regular_dict; \ -              } \ -            } \ -            CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ -            CONV_DICT_START(val_list->lv_len); \ -            kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ -              .type = kMPConvPairs, \ -              .data = { \ -                .l = { \ -                  .list = val_list, \ -                  .li = val_list->lv_first, \ -                }, \ -              }, \ -            })); \ -            break; \ -          } \ -          case kMPExt: { \ -            const list_T *val_list; \ -            varnumber_T type; \ -            if (val_di->di_tv.v_type != VAR_LIST \ -                || (val_list = val_di->di_tv.vval.v_list) == NULL \ -                || val_list->lv_len != 2 \ -                || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ -                || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ -                || type < INT8_MIN \ -                || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            size_t len; \ -            char *buf; \ -            if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ -                                        &len, &buf)) { \ -              goto name##_convert_one_value_regular_dict; \ -            } \ -            CONV_EXT_STRING(buf, len, type); \ -            xfree(buf); \ -            break; \ -          } \ -        } \ -        break; \ -      } \ -name##_convert_one_value_regular_dict: \ -      CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ -      CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ -      kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ -        .type = kMPConvDict, \ -        .data = { \ -          .d = { \ -            .dict = tv->vval.v_dict, \ -            .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ -            .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ -          }, \ -        }, \ -      })); \ -      break; \ -    } \ -    case VAR_UNKNOWN: { \ -      EMSG2(_(e_intern2), #name "_convert_one_value()"); \ -      return FAIL; \ -    } \ -  } \ -  return OK; \ -} \ -\ -scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ -                               const char *const objname) \ -  FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ -  const int copyID = get_copyID(); \ -  MPConvStack mpstack; \ -  kv_init(mpstack); \ -  if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ -      == FAIL) { \ -    goto encode_vim_to_##name##_error_ret; \ -  } \ -  while (kv_size(mpstack)) { \ -    MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ -    typval_T *cur_tv = NULL; \ -    switch (cur_mpsv->type) { \ -      case kMPConvDict: { \ -        if (!cur_mpsv->data.d.todo) { \ -          (void) kv_pop(mpstack); \ -          cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ -          CONV_DICT_END(); \ -          continue; \ -        } else if (cur_mpsv->data.d.todo \ -                   != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ -          CONV_DICT_BETWEEN_ITEMS(); \ -        } \ -        while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ -          cur_mpsv->data.d.hi++; \ -        } \ -        dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ -        cur_mpsv->data.d.todo--; \ -        cur_mpsv->data.d.hi++; \ -        CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ -        CONV_DICT_AFTER_KEY(); \ -        cur_tv = &di->di_tv; \ -        break; \ -      } \ -      case kMPConvList: { \ -        if (cur_mpsv->data.l.li == NULL) { \ -          (void) kv_pop(mpstack); \ -          cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ -          CONV_LIST_END(cur_mpsv->data.l.list); \ -          continue; \ -        } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ -          CONV_LIST_BETWEEN_ITEMS(); \ -        } \ -        cur_tv = &cur_mpsv->data.l.li->li_tv; \ -        cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ -        break; \ -      } \ -      case kMPConvPairs: { \ -        if (cur_mpsv->data.l.li == NULL) { \ -          (void) kv_pop(mpstack); \ -          cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ -          CONV_DICT_END(); \ -          continue; \ -        } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ -          CONV_DICT_BETWEEN_ITEMS(); \ -        } \ -        const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ -        CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \ -        if (name##_convert_one_value(firstargname, &mpstack, \ -                                     &kv_pair->lv_first->li_tv, copyID, \ -                                     objname) == FAIL) { \ -          goto encode_vim_to_##name##_error_ret; \ -        } \ -        CONV_DICT_AFTER_KEY(); \ -        cur_tv = &kv_pair->lv_last->li_tv; \ -        cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ -        break; \ -      } \ -    } \ -    assert(cur_tv != NULL); \ -    if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ -                                 objname) == FAIL) { \ -      goto encode_vim_to_##name##_error_ret; \ -    } \ -  } \ -  kv_destroy(mpstack); \ -  return OK; \ -encode_vim_to_##name##_error_ret: \ -  kv_destroy(mpstack); \ -  return FAIL; \ -} - -#define CONV_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \      do { \        const char *const buf_ = (const char *) buf; \        if (buf == NULL) { \ @@ -655,19 +273,19 @@ encode_vim_to_##name##_error_ret: \        } \      } while (0) -#define CONV_STR_STRING(buf, len) \ -    CONV_STRING(buf, len) +#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \ +    TYPVAL_ENCODE_CONV_STRING(buf, len) -#define CONV_EXT_STRING(buf, len, type) +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) -#define CONV_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_NUMBER(num) \      do { \        char numbuf[NUMBUFLEN]; \        vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \        ga_concat(gap, numbuf); \      } while (0) -#define CONV_FLOAT(flt) \ +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \      do { \        const float_T flt_ = (flt); \        switch (fpclassify(flt_)) { \ @@ -690,51 +308,51 @@ encode_vim_to_##name##_error_ret: \        } \      } while (0) -#define CONV_FUNC(fun) \ +#define TYPVAL_ENCODE_CONV_FUNC(fun) \      do { \        ga_concat(gap, "function("); \ -      CONV_STRING(fun, STRLEN(fun)); \ +      TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \        ga_append(gap, ')'); \      } while (0) -#define CONV_EMPTY_LIST() \ +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \      ga_concat(gap, "[]") -#define CONV_LIST_START(lst) \ +#define TYPVAL_ENCODE_CONV_LIST_START(len) \      ga_append(gap, '[') -#define CONV_EMPTY_DICT() \ +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \      ga_concat(gap, "{}") -#define CONV_NIL() \ +#define TYPVAL_ENCODE_CONV_NIL() \      ga_concat(gap, "v:null") -#define CONV_BOOL(num) \ +#define TYPVAL_ENCODE_CONV_BOOL(num) \      ga_concat(gap, ((num)? "v:true": "v:false")) -#define CONV_UNSIGNED_NUMBER(num) +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) -#define CONV_DICT_START(len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(len) \      ga_append(gap, '{') -#define CONV_DICT_END() \ +#define TYPVAL_ENCODE_CONV_DICT_END() \      ga_append(gap, '}') -#define CONV_DICT_AFTER_KEY() \ +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \      ga_concat(gap, ": ") -#define CONV_DICT_BETWEEN_ITEMS() \ +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \      ga_concat(gap, ", ") -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair) -#define CONV_LIST_END(lst) \ +#define TYPVAL_ENCODE_CONV_LIST_END() \      ga_append(gap, ']') -#define CONV_LIST_BETWEEN_ITEMS() \ -    CONV_DICT_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \ +    TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() -#define CONV_RECURSE(val, conv_type) \ +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \      do { \        if (!did_echo_string_emsg) { \          /* Only give this message once for a recursive call to avoid */ \ @@ -764,12 +382,12 @@ encode_vim_to_##name##_error_ret: \        return OK; \      } while (0) -#define CONV_ALLOW_SPECIAL false +#define TYPVAL_ENCODE_ALLOW_SPECIALS false -DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap) -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ +#undef TYPVAL_ENCODE_CONV_RECURSE +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \      do { \        char ebuf[NUMBUFLEN + 7]; \        size_t backref = 0; \ @@ -796,10 +414,10 @@ DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)        return OK; \      } while (0) -DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap) +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap) -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ +#undef TYPVAL_ENCODE_CONV_RECURSE +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \      do { \        if (!did_echo_string_emsg) { \          /* Only give this message once for a recursive call to avoid */ \ @@ -811,27 +429,27 @@ DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)        return OK; \      } while (0) -#undef CONV_ALLOW_SPECIAL -#define CONV_ALLOW_SPECIAL true +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#define TYPVAL_ENCODE_ALLOW_SPECIALS true -#undef CONV_NIL -#define CONV_NIL() \ +#undef TYPVAL_ENCODE_CONV_NIL +#define TYPVAL_ENCODE_CONV_NIL() \        ga_concat(gap, "null") -#undef CONV_BOOL -#define CONV_BOOL(num) \ +#undef TYPVAL_ENCODE_CONV_BOOL +#define TYPVAL_ENCODE_CONV_BOOL(num) \        ga_concat(gap, ((num)? "true": "false")) -#undef CONV_UNSIGNED_NUMBER -#define CONV_UNSIGNED_NUMBER(num) \ +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \        do { \          char numbuf[NUMBUFLEN]; \          vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \          ga_concat(gap, numbuf); \        } while (0) -#undef CONV_FLOAT -#define CONV_FLOAT(flt) \ +#undef TYPVAL_ENCODE_CONV_FLOAT +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \      do { \        const float_T flt_ = (flt); \        switch (fpclassify(flt_)) { \ @@ -1019,24 +637,24 @@ static inline int convert_to_json_string(garray_T *const gap,    return OK;  } -#undef CONV_STRING -#define CONV_STRING(buf, len) \ +#undef TYPVAL_ENCODE_CONV_STRING +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \      do { \        if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \          return FAIL; \        } \      } while (0) -#undef CONV_EXT_STRING -#define CONV_EXT_STRING(buf, len, type) \ +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \      do { \        xfree(buf); \        EMSG(_("E474: Unable to convert EXT string to JSON")); \        return FAIL; \      } while (0) -#undef CONV_FUNC -#define CONV_FUNC(fun) \ +#undef TYPVAL_ENCODE_CONV_FUNC +#define TYPVAL_ENCODE_CONV_FUNC(fun) \      return conv_error(_("E474: Error while dumping %s, %s: " \                          "attempt to dump function reference"), \                        mpstack, objname) @@ -1080,38 +698,38 @@ static inline bool check_json_key(const typval_T *const tv)    return true;  } -#undef CONV_SPECIAL_DICT_KEY_CHECK -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \ +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair) \      do { \        if (!check_json_key(&kv_pair->lv_first->li_tv)) { \          EMSG(_("E474: Invalid key in special dictionary")); \ -        goto encode_vim_to_##name##_error_ret; \ +        goto label; \        } \      } while (0) -DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_NIL -#undef CONV_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_SPECIAL_DICT_KEY_CHECK -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap) + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS  /// Return a string with the string representation of a variable.  /// Puts quotes around strings, so that they can be parsed back by eval(). @@ -1181,7 +799,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)    return (char *) ga.ga_data;  } -#define CONV_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \      do { \        if (buf == NULL) { \          msgpack_pack_bin(packer, 0); \ @@ -1192,7 +810,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)        } \      } while (0) -#define CONV_STR_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \      do { \        if (buf == NULL) { \          msgpack_pack_str(packer, 0); \ @@ -1203,7 +821,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)        } \      } while (0) -#define CONV_EXT_STRING(buf, len, type) \ +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \      do { \        if (buf == NULL) { \          msgpack_pack_ext(packer, 0, (int8_t) type); \ @@ -1214,30 +832,30 @@ char *encode_tv2json(typval_T *tv, size_t *len)        } \      } while (0) -#define CONV_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_NUMBER(num) \      msgpack_pack_int64(packer, (int64_t) (num)) -#define CONV_FLOAT(flt) \ +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \      msgpack_pack_double(packer, (double) (flt)) -#define CONV_FUNC(fun) \ +#define TYPVAL_ENCODE_CONV_FUNC(fun) \      return conv_error(_("E951: Error while dumping %s, %s: " \                          "attempt to dump function reference"), \                        mpstack, objname) -#define CONV_EMPTY_LIST() \ +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \      msgpack_pack_array(packer, 0) -#define CONV_LIST_START(lst) \ -    msgpack_pack_array(packer, (size_t) (lst)->lv_len) +#define TYPVAL_ENCODE_CONV_LIST_START(len) \ +    msgpack_pack_array(packer, (size_t) (len)) -#define CONV_EMPTY_DICT() \ +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \      msgpack_pack_map(packer, 0) -#define CONV_NIL() \ +#define TYPVAL_ENCODE_CONV_NIL() \      msgpack_pack_nil(packer) -#define CONV_BOOL(num) \ +#define TYPVAL_ENCODE_CONV_BOOL(num) \      do { \        if ((num)) { \          msgpack_pack_true(packer); \ @@ -1246,51 +864,51 @@ char *encode_tv2json(typval_T *tv, size_t *len)        } \      } while (0) -#define CONV_UNSIGNED_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \      msgpack_pack_uint64(packer, (num)) -#define CONV_DICT_START(len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(len) \      msgpack_pack_map(packer, (size_t) (len)) -#define CONV_DICT_END() +#define TYPVAL_ENCODE_CONV_DICT_END() -#define CONV_DICT_AFTER_KEY() +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() -#define CONV_DICT_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair) -#define CONV_LIST_END(lst) +#define TYPVAL_ENCODE_CONV_LIST_END() -#define CONV_LIST_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() -#define CONV_RECURSE(val, conv_type) \ +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \      return conv_error(_("E952: Unable to dump %s: " \                          "container references itself in %s"), \                        mpstack, objname) -#define CONV_ALLOW_SPECIAL true - -DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_NIL -#undef CONV_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_SPECIAL_DICT_KEY_CHECK -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL +#define TYPVAL_ENCODE_ALLOW_SPECIALS true + +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h new file mode 100644 index 0000000000..f70a6c9e94 --- /dev/null +++ b/src/nvim/eval/typval_encode.h @@ -0,0 +1,563 @@ +/// @file eval/typval_convert.h +/// +/// Contains set of macros used to convert (possibly recursive) typval_T into +/// something else. For these macros to work the following macros must be +/// defined: + +/// @def TYPVAL_ENCODE_CONV_NIL +/// @brief Macros used to convert NIL value +/// +/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS +/// is false) and `v:null`. Accepts no arguments, but still must be +/// a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_BOOL +/// @brief Macros used to convert boolean value +/// +/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS +/// is false) and `v:true`/`v:false`. +/// +/// @param  num  Boolean value to convert. Value is an expression which +///              evaluates to some integer. + +/// @def TYPVAL_ENCODE_CONV_NUMBER +/// @brief Macros used to convert integer +/// +/// @param  num  Integer to convert, must accept both varnumber_T and int64_t. + +/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +/// @brief Macros used to convert unsigned integer +/// +/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be +/// defined. +/// +/// @param  num  Integer to convert, must accept uint64_t. + +/// @def TYPVAL_ENCODE_CONV_FLOAT +/// @brief Macros used to convert floating-point number +/// +/// @param  flt  Number to convert, must accept float_T. + +/// @def TYPVAL_ENCODE_CONV_STRING +/// @brief Macros used to convert plain string +/// +/// Is used to convert VAR_STRING objects as well as BIN strings represented as +/// special dictionary. +/// +/// @param  buf  String to convert. Is a char[] buffer, not NUL-terminated. +/// @param  len  String length. + +/// @def TYPVAL_ENCODE_CONV_STR_STRING +/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings +/// +/// Is used to convert dictionary keys and STR strings represented as special +/// dictionaries. + +/// @def TYPVAL_ENCODE_CONV_EXT_STRING +/// @brief Macros used to convert EXT string +/// +/// Is used to convert EXT strings represented as special dictionaries. Never +/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be +/// defined. +/// +/// @param  buf  String to convert. Is a char[] buffer, not NUL-terminated. +/// @param  len  String length. +/// @param  type  EXT type. + +/// @def TYPVAL_ENCODE_CONV_FUNC +/// @brief Macros used to convert a function reference +/// +/// @param  fun  Function name. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST +/// @brief Macros used to convert an empty list +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT +/// @brief Macros used to convert an empty dictionary +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_LIST_START +/// @brief Macros used before starting to convert non-empty list +/// +/// @param  len  List length. Is an expression which evaluates to an integer. + +/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +/// @brief Macros used after finishing converting non-last list item +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_LIST_END +/// @brief Macros used after converting non-empty list +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_START +/// @brief Macros used before starting to convert non-empty dictionary +/// +/// @param  len  Dictionary length. Is an expression which evaluates to an +///              integer. + +/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +/// @brief Macros used to check special dictionary key +/// +/// @param  label  Label for goto in case check was not successfull. +/// @param  kv_pair  List with two elements: key and value. + +/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +/// @brief Macros used after finishing converting dictionary key +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +/// @brief Macros used after finishing converting non-last dictionary value +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_END +/// @brief Macros used after converting non-empty dictionary +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_RECURSE +/// @brief Macros used when self-containing container is detected +/// +/// @param  val  Container for which this situation was detected. +/// @param  conv_type  Type of the stack entry, @see MPConvStackValType. + +/// @def TYPVAL_ENCODE_ALLOW_SPECIALS +/// @brief Macros that specifies whether special dictionaries are special +/// +/// Must be something that evaluates to boolean, most likely `true` or `false`. +/// If it is false then special dictionaries are not treated specially. +#ifndef NVIM_EVAL_TYPVAL_ENCODE_H +#define NVIM_EVAL_TYPVAL_ENCODE_H + +#include <stddef.h> +#include <inttypes.h> +#include <assert.h> + +#include "nvim/lib/kvec.h" +#include "nvim/eval_defs.h" +#include "nvim/eval/encode.h" +#include "nvim/func_attr.h" + +/// Type of the stack entry +typedef enum { +  kMPConvDict,   ///< Convert dict_T *dictionary. +  kMPConvList,   ///< Convert list_T *list. +  kMPConvPairs,  ///< Convert mapping represented as a list_T* of pairs. +} MPConvStackValType; + +/// Structure representing current VimL to messagepack conversion state +typedef struct { +  MPConvStackValType type;  ///< Type of the stack entry. +  union { +    struct { +      dict_T *dict;    ///< Currently converted dictionary. +      hashitem_T *hi;  ///< Currently converted dictionary item. +      size_t todo;     ///< Amount of items left to process. +    } d;  ///< State of dictionary conversion. +    struct { +      list_T *list;    ///< Currently converted list. +      listitem_T *li;  ///< Currently converted list item. +    } l;  ///< State of list or generic mapping conversion. +  } data;  ///< Data to convert. +} MPConvStackVal; + +/// Stack used to convert VimL values to messagepack. +typedef kvec_t(MPConvStackVal) MPConvStack; + +// Defines for MPConvStack +#define _mp_size kv_size +#define _mp_init kv_init +#define _mp_destroy kv_destroy +#define _mp_push kv_push +#define _mp_pop kv_pop +#define _mp_last kv_last + +/// Code for checking whether container references itself +/// +/// @param[in,out]  val  Container to check. +/// @param  copyID_attr  Name of the container attribute that holds copyID. +///                      After checking whether value of this attribute is +///                      copyID (variable) it is set to copyID. +#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ +    do { \ +      if ((val)->copyID_attr == copyID) { \ +        TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \ +      } \ +      (val)->copyID_attr = copyID; \ +    } while (0) + +/// Length of the string stored in typval_T +/// +/// @param[in]  tv  String for which to compute length for. Must be typval_T +///                 with VAR_STRING. +/// +/// @return Length of the string stored in typval_T, including 0 for NULL +///         string. +static inline size_t tv_strlen(const typval_T *const tv) +  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +  FUNC_ATTR_NONNULL_ALL +{ +  assert(tv->v_type == VAR_STRING); +  return (tv->vval.v_string == NULL +          ? 0 +          : strlen((char *) tv->vval.v_string)); +} + +/// Define functions which convert VimL value to something else +/// +/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const +/// tv)` which returns OK or FAIL and helper functions. +/// +/// @param  scope  Scope of the main function: either nothing or `static`. +/// @param  firstargtype  Type of the first argument. It will be used to return +///                       the results. +/// @param  firstargname  Name of the first argument. +/// @param  name  Name of the target converter. +#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \ +                                            firstargname) \ +static int name##_convert_one_value(firstargtype firstargname, \ +                                    MPConvStack *const mpstack, \ +                                    typval_T *const tv, \ +                                    const int copyID, \ +                                    const char *const objname) \ +  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ +  switch (tv->v_type) { \ +    case VAR_STRING: { \ +      TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \ +      break; \ +    } \ +    case VAR_NUMBER: { \ +      TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \ +      break; \ +    } \ +    case VAR_FLOAT: { \ +      TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \ +      break; \ +    } \ +    case VAR_FUNC: { \ +      TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \ +      break; \ +    } \ +    case VAR_LIST: { \ +      if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ +        TYPVAL_ENCODE_CONV_EMPTY_LIST(); \ +        break; \ +      } \ +      _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \ +                                          kMPConvList); \ +      TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \ +      _mp_push(*mpstack, ((MPConvStackVal) { \ +        .type = kMPConvList, \ +        .data = { \ +          .l = { \ +            .list = tv->vval.v_list, \ +            .li = tv->vval.v_list->lv_first, \ +          }, \ +        }, \ +      })); \ +      break; \ +    } \ +    case VAR_SPECIAL: { \ +      switch (tv->vval.v_special) { \ +        case kSpecialVarNull: { \ +          TYPVAL_ENCODE_CONV_NIL(); \ +          break; \ +        } \ +        case kSpecialVarTrue: \ +        case kSpecialVarFalse: { \ +          TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ +          break; \ +        } \ +      } \ +      break; \ +    } \ +    case VAR_DICT: { \ +      if (tv->vval.v_dict == NULL \ +          || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ +        TYPVAL_ENCODE_CONV_EMPTY_DICT(); \ +        break; \ +      } \ +      const dictitem_T *type_di; \ +      const dictitem_T *val_di; \ +      if (TYPVAL_ENCODE_ALLOW_SPECIALS \ +          && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ +          && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ +                                  (char_u *) "_TYPE", -1)) != NULL \ +          && type_di->di_tv.v_type == VAR_LIST \ +          && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ +                                 (char_u *) "_VAL", -1)) != NULL) { \ +        size_t i; \ +        for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ +          if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ +            break; \ +          } \ +        } \ +        if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ +          goto name##_convert_one_value_regular_dict; \ +        } \ +        switch ((MessagePackType) i) { \ +          case kMPNil: { \ +            TYPVAL_ENCODE_CONV_NIL(); \ +            break; \ +          } \ +          case kMPBoolean: { \ +            if (val_di->di_tv.v_type != VAR_NUMBER) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \ +            break; \ +          } \ +          case kMPInteger: { \ +            const list_T *val_list; \ +            varnumber_T sign; \ +            varnumber_T highest_bits; \ +            varnumber_T high_bits; \ +            varnumber_T low_bits; \ +            /* List of 4 integers; first is signed (should be 1 or -1, but */ \ +            /* this is not checked), second is unsigned and have at most */ \ +            /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ +            /* bits is not checked), other unsigned and have at most 31 */ \ +            /* non-zero bits (number of bits is not checked).*/ \ +            if (val_di->di_tv.v_type != VAR_LIST \ +                || (val_list = val_di->di_tv.vval.v_list) == NULL \ +                || val_list->lv_len != 4 \ +                || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ +                || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ +                || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ +                || (highest_bits = \ +                    val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ +                || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ +                || (high_bits = \ +                    val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ +                || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ +                || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ +                               | (uint64_t) (((uint64_t) high_bits) << 31) \ +                               | (uint64_t) low_bits); \ +            if (sign > 0) { \ +              TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \ +            } else { \ +              TYPVAL_ENCODE_CONV_NUMBER(-number); \ +            } \ +            break; \ +          } \ +          case kMPFloat: { \ +            if (val_di->di_tv.v_type != VAR_FLOAT) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \ +            break; \ +          } \ +          case kMPString: \ +          case kMPBinary: { \ +            const bool is_string = ((MessagePackType) i == kMPString); \ +            if (val_di->di_tv.v_type != VAR_LIST) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            size_t len; \ +            char *buf; \ +            if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ +                                        &buf)) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            if (is_string) { \ +              TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \ +            } else { \ +              TYPVAL_ENCODE_CONV_STRING(buf, len); \ +            } \ +            xfree(buf); \ +            break; \ +          } \ +          case kMPArray: { \ +            if (val_di->di_tv.v_type != VAR_LIST) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \ +                                                lv_copyID, kMPConvList); \ +            TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \ +            _mp_push(*mpstack, ((MPConvStackVal) { \ +              .type = kMPConvList, \ +              .data = { \ +                .l = { \ +                  .list = val_di->di_tv.vval.v_list, \ +                  .li = val_di->di_tv.vval.v_list->lv_first, \ +                }, \ +              }, \ +            })); \ +            break; \ +          } \ +          case kMPMap: { \ +            if (val_di->di_tv.v_type != VAR_LIST) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            list_T *const val_list = val_di->di_tv.vval.v_list; \ +            if (val_list == NULL || val_list->lv_len == 0) { \ +              TYPVAL_ENCODE_CONV_EMPTY_DICT(); \ +              break; \ +            } \ +            for (const listitem_T *li = val_list->lv_first; li != NULL; \ +                 li = li->li_next) { \ +              if (li->li_tv.v_type != VAR_LIST \ +                  || li->li_tv.vval.v_list->lv_len != 2) { \ +                goto name##_convert_one_value_regular_dict; \ +              } \ +            } \ +            _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \ +                                                kMPConvPairs); \ +            TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \ +            _mp_push(*mpstack, ((MPConvStackVal) { \ +              .type = kMPConvPairs, \ +              .data = { \ +                .l = { \ +                  .list = val_list, \ +                  .li = val_list->lv_first, \ +                }, \ +              }, \ +            })); \ +            break; \ +          } \ +          case kMPExt: { \ +            const list_T *val_list; \ +            varnumber_T type; \ +            if (val_di->di_tv.v_type != VAR_LIST \ +                || (val_list = val_di->di_tv.vval.v_list) == NULL \ +                || val_list->lv_len != 2 \ +                || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ +                || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ +                || type < INT8_MIN \ +                || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            size_t len; \ +            char *buf; \ +            if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ +                                        &len, &buf)) { \ +              goto name##_convert_one_value_regular_dict; \ +            } \ +            TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \ +            xfree(buf); \ +            break; \ +          } \ +        } \ +        break; \ +      } \ +name##_convert_one_value_regular_dict: \ +      _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \ +                                          kMPConvDict); \ +      TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ +      _mp_push(*mpstack, ((MPConvStackVal) { \ +        .type = kMPConvDict, \ +        .data = { \ +          .d = { \ +            .dict = tv->vval.v_dict, \ +            .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ +            .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ +          }, \ +        }, \ +      })); \ +      break; \ +    } \ +    case VAR_UNKNOWN: { \ +      EMSG2(_(e_intern2), #name "_convert_one_value()"); \ +      return FAIL; \ +    } \ +  } \ +  return OK; \ +} \ +\ +scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ +                               const char *const objname) \ +  FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ +  const int copyID = get_copyID(); \ +  MPConvStack mpstack; \ +  _mp_init(mpstack); \ +  if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ +      == FAIL) { \ +    goto encode_vim_to_##name##_error_ret; \ +  } \ +  while (_mp_size(mpstack)) { \ +    MPConvStackVal *cur_mpsv = &_mp_last(mpstack); \ +    typval_T *cur_tv = NULL; \ +    switch (cur_mpsv->type) { \ +      case kMPConvDict: { \ +        if (!cur_mpsv->data.d.todo) { \ +          (void) _mp_pop(mpstack); \ +          cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ +          TYPVAL_ENCODE_CONV_DICT_END(); \ +          continue; \ +        } else if (cur_mpsv->data.d.todo \ +                   != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ +          TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \ +        } \ +        while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ +          cur_mpsv->data.d.hi++; \ +        } \ +        dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ +        cur_mpsv->data.d.todo--; \ +        cur_mpsv->data.d.hi++; \ +        TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0], \ +                                      strlen((char *) &di->di_key[0])); \ +        TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \ +        cur_tv = &di->di_tv; \ +        break; \ +      } \ +      case kMPConvList: { \ +        if (cur_mpsv->data.l.li == NULL) { \ +          (void) _mp_pop(mpstack); \ +          cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ +          TYPVAL_ENCODE_CONV_LIST_END(); \ +          continue; \ +        } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ +          TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \ +        } \ +        cur_tv = &cur_mpsv->data.l.li->li_tv; \ +        cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ +        break; \ +      } \ +      case kMPConvPairs: { \ +        if (cur_mpsv->data.l.li == NULL) { \ +          (void) _mp_pop(mpstack); \ +          cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ +          TYPVAL_ENCODE_CONV_DICT_END(); \ +          continue; \ +        } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ +          TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \ +        } \ +        const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ +        TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK( \ +            encode_vim_to_##name##_error_ret, kv_pair); \ +        if (name##_convert_one_value(firstargname, &mpstack, \ +                                     &kv_pair->lv_first->li_tv, copyID, \ +                                     objname) == FAIL) { \ +          goto encode_vim_to_##name##_error_ret; \ +        } \ +        TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \ +        cur_tv = &kv_pair->lv_last->li_tv; \ +        cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ +        break; \ +      } \ +    } \ +    assert(cur_tv != NULL); \ +    if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ +                                 objname) == FAIL) { \ +      goto encode_vim_to_##name##_error_ret; \ +    } \ +  } \ +  _mp_destroy(mpstack); \ +  return OK; \ +encode_vim_to_##name##_error_ret: \ +  _mp_destroy(mpstack); \ +  return FAIL; \ +} + +#endif  // NVIM_EVAL_TYPVAL_ENCODE_H diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 59962c153b..dd096af6b3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6992,7 +6992,7 @@ void do_sleep(long msec)    ui_flush();  // flush before waiting    for (long left = msec; !got_int && left > 0; left -= 1000L) {      int next = left > 1000l ? 1000 : (int)left; -    LOOP_PROCESS_EVENTS_UNTIL(&loop, loop.events, (int)next, got_int); +    LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, (int)next, got_int);      os_breakcheck();    }  } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index db21fddedb..65144dace8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -358,7 +358,7 @@ static int command_line_execute(VimState *state, int key)    s->c = key;    if (s->c == K_EVENT) { -    queue_process_events(loop.events); +    queue_process_events(main_loop.events);      redrawcmdline();      return 1;    } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index dafb75ca87..f618e5ffc4 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1243,7 +1243,6 @@ EXTERN char *ignoredp;  // If a msgpack-rpc channel should be started over stdin/stdout  EXTERN bool embedded_mode INIT(= false); -EXTERN Loop loop;  /// Used to track the status of external functions.  /// Currently only used for iconv(). diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index b41ef0cc9f..36c91c86b2 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -1,59 +1,61 @@ -/* The MIT License - -   Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk> - -   Permission is hereby granted, free of charge, to any person obtaining -   a copy of this software and associated documentation files (the -   "Software"), to deal in the Software without restriction, including -   without limitation the rights to use, copy, modify, merge, publish, -   distribute, sublicense, and/or sell copies of the Software, and to -   permit persons to whom the Software is furnished to do so, subject to -   the following conditions: - -   The above copyright notice and this permission notice shall be -   included in all copies or substantial portions of the Software. - -   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -   SOFTWARE. -*/ - -/* -  An example: - -#include "kvec.h" -int main() { -	kvec_t(int) array; -	kv_init(array); -	kv_push(int, array, 10); // append -	kv_a(int, array, 20) = 5; // dynamic -	kv_A(array, 20) = 4; // static -	kv_destroy(array); -	return 0; -} -*/ - -/* -  2008-09-22 (0.1.0): +// The MIT License +// +// Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk> +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// An example: +// +//     #include "kvec.h" +//     int main() { +//       kvec_t(int) array = KV_INITIAL_VALUE; +//       kv_push(array, 10); // append +//       kv_a(array, 20) = 5; // dynamic +//       kv_A(array, 20) = 4; // static +//       kv_destroy(array); +//       return 0; +//     } + +#ifndef NVIM_LIB_KVEC_H +#define NVIM_LIB_KVEC_H -	* The initial version. +#include <stdlib.h> +#include <string.h> -*/ +#include "nvim/memory.h" -#ifndef AC_KVEC_H -#define AC_KVEC_H +#define kv_roundup32(x) \ +    ((--(x)), \ +     ((x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16), \ +     (++(x))) -#include <stdlib.h> -#include "nvim/memory.h" +#define KV_INITIAL_VALUE { .size = 0, .capacity = 0, .items = NULL } -#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#define kvec_t(type) \ +    struct { \ +      size_t size; \ +      size_t capacity; \ +      type *items; \ +    } -#define kvec_t(type) struct { size_t size, capacity; type *items; }  #define kv_init(v) ((v).size = (v).capacity = 0, (v).items = 0)  #define kv_destroy(v) xfree((v).items)  #define kv_A(v, i) ((v).items[(i)]) @@ -62,31 +64,130 @@ int main() {  #define kv_max(v) ((v).capacity)  #define kv_last(v) kv_A(v, kv_size(v) - 1) -#define kv_resize(type, v, s)  ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity)) - -#define kv_copy(type, v1, v0) do {							\ -		if ((v1).capacity < (v0).size) kv_resize(type, v1, (v0).size);	\ -		(v1).size = (v0).size;									\ -		memcpy((v1).items, (v0).items, sizeof(type) * (v0).size);		\ -	} while (0)												\ - -#define kv_push(type, v, x) do {									\ -		if ((v).size == (v).capacity) {										\ -			(v).capacity = (v).capacity? (v).capacity<<1 : 8;							\ -			(v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity);	\ -		}															\ -		(v).items[(v).size++] = (x);										\ -	} while (0) - -#define kv_pushp(type, v) ((((v).size == (v).capacity)?							\ -						   ((v).capacity = ((v).capacity? (v).capacity<<1 : 8),				\ -							(v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0)	\ -						   : 0), ((v).items + ((v).size++))) - -#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ -						  ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ -						   (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ -						  : (v).size <= (size_t)(i)? (v).size = (i) + 1 \ -						  : 0), (v).items[(i)]) - -#endif +#define kv_resize(v, s) \ +    ((v).capacity = (s), \ +     (v).items = xrealloc((v).items, sizeof((v).items[0]) * (v).capacity)) + +#define kv_resize_full(v) \ +    kv_resize(v, (v).capacity ? (v).capacity << 1 : 8) + +#define kv_copy(v1, v0) \ +    do { \ +      if ((v1).capacity < (v0).size) { \ +        kv_resize(v1, (v0).size); \ +      } \ +      (v1).size = (v0).size; \ +      memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \ +    } while (0) \ + +#define kv_pushp(v) \ +    ((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \ +     ((v).items + ((v).size++))) + +#define kv_push(v, x) \ +    (*kv_pushp(v) = (x)) + +#define kv_a(v, i) \ +    (((v).capacity <= (size_t) (i) \ +      ? ((v).capacity = (v).size = (i) + 1, \ +         kv_roundup32((v).capacity), \ +         kv_resize((v), (v).capacity), 0) \ +      : ((v).size <= (size_t) (i) \ +         ? (v).size = (i) + 1 \ +         : 0)), \ +     (v).items[(i)]) + +/// Type of a vector with a few first members allocated on stack +/// +/// Is compatible with #kv_A, #kv_pop, #kv_size, #kv_max, #kv_last. +/// Is not compatible with #kv_resize, #kv_resize_full, #kv_copy, #kv_push, +/// #kv_pushp, #kv_a, #kv_destroy. +/// +/// @param[in]  type  Type of vector elements. +/// @param[in]  init_size  Number of the elements in the initial array. +#define kvec_withinit_t(type, INIT_SIZE) \ +    struct { \ +      size_t size; \ +      size_t capacity; \ +      type *items; \ +      type init_array[INIT_SIZE]; \ +    } + +/// Initialize vector with preallocated array +/// +/// @param[out]  v  Vector to initialize. +#define kvi_init(v) \ +    ((v).capacity = ARRAY_SIZE((v).init_array), \ +     (v).size = 0, \ +     (v).items = (v).init_array) + +/// Move data to a new destination and free source +static inline void *_memcpy_free(void *const restrict dest, +                                 void *const restrict src, +                                 const size_t size) +  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_ALWAYS_INLINE +{ +  memcpy(dest, src, size); +  xfree(src); +  return dest; +} + +/// Resize vector with preallocated array +/// +/// @param[out]  v  Vector to resize. +/// @param[in]  s  New size. +#define kvi_resize(v, s) \ +    ((v).capacity = ((s) > ARRAY_SIZE((v).init_array) \ +                     ? (s) \ +                     : ARRAY_SIZE((v).init_array)), \ +     (v).items = ((v).capacity == ARRAY_SIZE((v).init_array) \ +                  ? ((v).items == (v).init_array \ +                     ? (v).items \ +                     : _memcpy_free((v).init_array, (v).items, \ +                                    (v).size * sizeof((v).items[0]))) \ +                  : ((v).items == (v).init_array \ +                     ? memcpy(xmalloc((v).capacity * sizeof((v).items[0])), \ +                              (v).items, \ +                              (v).size * sizeof((v).items[0])) \ +                     : xrealloc((v).items, \ +                                (v).capacity * sizeof((v).items[0]))))) + +/// Resize vector with preallocated array when it is full +/// +/// @param[out]  v  Vector to resize. +#define kvi_resize_full(v) \ +    /* ARRAY_SIZE((v).init_array) is the minimal capacity of this vector. */ \ +    /* Thus when vector is full capacity may not be zero and it is safe */ \ +    /* not to bother with checking whether (v).capacity is 0. But now */ \ +    /* capacity is not guaranteed to have size that is a power of 2. */ \ +    kvi_resize(v, ((v).capacity == ARRAY_SIZE((v).init_array) \ +                   ? ((v).capacity++, kv_roundup32((v).capacity)) \ +                   : (v).capacity << 1)) + +/// Get location where to store new element to a vector with preallocated array +/// +/// @param[in,out]  v  Vector to push to. +/// +/// @return Pointer to the place where new value should be stored. +#define kvi_pushp(v) \ +    ((((v).size == (v).capacity) ? (kvi_resize_full(v), 0) : 0), \ +     ((v).items + ((v).size++))) + +/// Push value to a vector with preallocated array +/// +/// @param[out]  v  Vector to push to. +/// @param[in]  x  Value to push. +#define kvi_push(v, x) \ +    (*kvi_pushp(v) = (x)) + +/// Free array of elements of a vector with preallocated array if needed +/// +/// @param[out]  v  Vector to free. +#define kvi_destroy(v) \ +    do { \ +      if ((v).items != (v).init_array) { \ +        xfree((v).items); \ +      } \ +    } while (0) + +#endif  // NVIM_LIB_KVEC_H diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h index fe02b454ea..fc322978b0 100644 --- a/src/nvim/lib/queue.h +++ b/src/nvim/lib/queue.h @@ -1,92 +1,94 @@ -/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ +// Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -#ifndef QUEUE_H_ -#define QUEUE_H_ +#ifndef NVIM_LIB_QUEUE_H +#define NVIM_LIB_QUEUE_H -typedef void *QUEUE[2]; +#include <stddef.h> -/* Private macros. */ -#define QUEUE_NEXT(q)       (*(QUEUE **) &((*(q))[0])) -#define QUEUE_PREV(q)       (*(QUEUE **) &((*(q))[1])) -#define QUEUE_PREV_NEXT(q)  (QUEUE_NEXT(QUEUE_PREV(q))) -#define QUEUE_NEXT_PREV(q)  (QUEUE_PREV(QUEUE_NEXT(q))) +#include "nvim/func_attr.h" -/* Public macros. */ -#define QUEUE_DATA(ptr, type, field)                                          \ -  ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) +typedef struct _queue { +  struct _queue *next; +  struct _queue *prev; +} QUEUE; -#define QUEUE_FOREACH(q, h)                                                   \ -  for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) +// Public macros. +#define QUEUE_DATA(ptr, type, field)  \ +  ((type *)((char *)(ptr) - offsetof(type, field))) -#define QUEUE_EMPTY(q)                                                        \ -  ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) +#define QUEUE_FOREACH(q, h) \ +  for (  /* NOLINT(readability/braces) */ \ +      (q) = (h)->next; (q) != (h); (q) = (q)->next) -#define QUEUE_HEAD(q)                                                         \ -  (QUEUE_NEXT(q)) +// ffi.cdef is unable to swallow `bool` in place of `int` here. +static inline int QUEUE_EMPTY(const QUEUE *const q) +  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ +  return q == q->next; +} -#define QUEUE_INIT(q)                                                         \ -  do {                                                                        \ -    QUEUE_NEXT(q) = (q);                                                      \ -    QUEUE_PREV(q) = (q);                                                      \ -  }                                                                           \ -  while (0) +#define QUEUE_HEAD(q) (q)->next -#define QUEUE_ADD(h, n)                                                       \ -  do {                                                                        \ -    QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n);                                       \ -    QUEUE_NEXT_PREV(n) = QUEUE_PREV(h);                                       \ -    QUEUE_PREV(h) = QUEUE_PREV(n);                                            \ -    QUEUE_PREV_NEXT(h) = (h);                                                 \ -  }                                                                           \ -  while (0) +static inline void QUEUE_INIT(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +{ +  q->next = q; +  q->prev = q; +} -#define QUEUE_SPLIT(h, q, n)                                                  \ -  do {                                                                        \ -    QUEUE_PREV(n) = QUEUE_PREV(h);                                            \ -    QUEUE_PREV_NEXT(n) = (n);                                                 \ -    QUEUE_NEXT(n) = (q);                                                      \ -    QUEUE_PREV(h) = QUEUE_PREV(q);                                            \ -    QUEUE_PREV_NEXT(h) = (h);                                                 \ -    QUEUE_PREV(q) = (n);                                                      \ -  }                                                                           \ -  while (0) +static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n) +  FUNC_ATTR_ALWAYS_INLINE +{ +  h->prev->next = n->next; +  n->next->prev = h->prev; +  h->prev = n->prev; +  h->prev->next = h; +} -#define QUEUE_INSERT_HEAD(h, q)                                               \ -  do {                                                                        \ -    QUEUE_NEXT(q) = QUEUE_NEXT(h);                                            \ -    QUEUE_PREV(q) = (h);                                                      \ -    QUEUE_NEXT_PREV(q) = (q);                                                 \ -    QUEUE_NEXT(h) = (q);                                                      \ -  }                                                                           \ -  while (0) +static inline void QUEUE_SPLIT(QUEUE *const h, QUEUE *const q, QUEUE *const n) +  FUNC_ATTR_ALWAYS_INLINE +{ +  n->prev = h->prev; +  n->prev->next = n; +  n->next = q; +  h->prev = q->prev; +  h->prev->next = h; +  q->prev = n; +} -#define QUEUE_INSERT_TAIL(h, q)                                               \ -  do {                                                                        \ -    QUEUE_NEXT(q) = (h);                                                      \ -    QUEUE_PREV(q) = QUEUE_PREV(h);                                            \ -    QUEUE_PREV_NEXT(q) = (q);                                                 \ -    QUEUE_PREV(h) = (q);                                                      \ -  }                                                                           \ -  while (0) +static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q) +  FUNC_ATTR_ALWAYS_INLINE +{ +  q->next = h->next; +  q->prev = h; +  q->next->prev = q; +  h->next = q; +} -#define QUEUE_REMOVE(q)                                                       \ -  do {                                                                        \ -    QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q);                                       \ -    QUEUE_NEXT_PREV(q) = QUEUE_PREV(q);                                       \ -  }                                                                           \ -  while (0) +static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q) +  FUNC_ATTR_ALWAYS_INLINE +{ +  q->next = h; +  q->prev = h->prev; +  q->prev->next = q; +  h->prev = q; +} -#endif /* QUEUE_H_ */ +static inline void QUEUE_REMOVE(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +{ +  q->prev->next = q->next; +  q->next->prev = q->prev; +} + +#endif  // NVIM_LIB_QUEUE_H diff --git a/src/nvim/main.c b/src/nvim/main.c index 71a972e8f6..2b14e6a777 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -120,6 +120,8 @@ typedef struct {  # include "main.c.generated.h"  #endif +Loop main_loop; +  static char *argv0;  // Error messages @@ -133,7 +135,7 @@ static const char *err_extra_cmd =  void event_init(void)  { -  loop_init(&loop, NULL); +  loop_init(&main_loop, NULL);    // early msgpack-rpc initialization    msgpack_rpc_init_method_table();    msgpack_rpc_helpers_init(); @@ -151,19 +153,19 @@ void event_init(void)  void event_teardown(void)  { -  if (!loop.events) { +  if (!main_loop.events) {      return;    } -  queue_process_events(loop.events); +  queue_process_events(main_loop.events);    input_stop();    channel_teardown(); -  process_teardown(&loop); +  process_teardown(&main_loop);    server_teardown();    signal_teardown();    terminal_teardown(); -  loop_close(&loop); +  loop_close(&main_loop);  }  /// Performs early initialization. diff --git a/src/nvim/main.h b/src/nvim/main.h index 084e247b7e..86d25fe657 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -2,6 +2,9 @@  #define NVIM_MAIN_H  #include "nvim/normal.h" +#include "nvim/event/loop.h" + +extern Loop main_loop;  #ifdef INCLUDE_GENERATED_DECLARATIONS  # include "main.h.generated.h" diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 3a6d7c1434..0d7d5a247e 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -16,6 +16,7 @@  #include "nvim/event/socket.h"  #include "nvim/msgpack_rpc/helpers.h"  #include "nvim/vim.h" +#include "nvim/main.h"  #include "nvim/ascii.h"  #include "nvim/memory.h"  #include "nvim/os_unix.h" @@ -119,7 +120,7 @@ void channel_teardown(void)  uint64_t channel_from_process(char **argv)  {    Channel *channel = register_channel(kChannelTypeProc); -  channel->data.process.uvproc = libuv_process_init(&loop, channel); +  channel->data.process.uvproc = libuv_process_init(&main_loop, channel);    Process *proc = &channel->data.process.uvproc.process;    proc->argv = argv;    proc->in = &channel->data.process.in; @@ -127,7 +128,7 @@ uint64_t channel_from_process(char **argv)    proc->err = &channel->data.process.err;    proc->cb = process_exit;    if (!process_spawn(proc)) { -    loop_poll_events(&loop, 0); +    loop_poll_events(&main_loop, 0);      decref(channel);      return 0;    } @@ -179,7 +180,7 @@ bool channel_send_event(uint64_t id, char *name, Array args)        // Pending request, queue the notification for later sending.        String method = cstr_as_string(name);        WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); -      kv_push(WBuffer *, channel->delayed_notifications, buffer); +      kv_push(channel->delayed_notifications, buffer);      } else {        send_event(channel, name, args);      } @@ -217,10 +218,10 @@ Object channel_send_call(uint64_t id,    send_request(channel, request_id, method_name, args);    // Push the frame -  ChannelCallFrame frame = {request_id, false, false, NIL}; -  kv_push(ChannelCallFrame *, channel->call_stack, &frame); +  ChannelCallFrame frame = { request_id, false, false, NIL }; +  kv_push(channel->call_stack, &frame);    channel->pending_requests++; -  LOOP_PROCESS_EVENTS_UNTIL(&loop, channel->events, -1, frame.returned); +  LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned);    (void)kv_pop(channel->call_stack);    channel->pending_requests--; @@ -316,11 +317,11 @@ void channel_from_stdio(void)    Channel *channel = register_channel(kChannelTypeStdio);    incref(channel);  // stdio channels are only closed on exit    // read stream -  rstream_init_fd(&loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, -      channel); +  rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, +                  channel);    rstream_start(&channel->data.std.in, parse_msgpack);    // write stream -  wstream_init_fd(&loop, &channel->data.std.out, 1, 0, NULL); +  wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0, NULL);  }  static void forward_stderr(Stream *stream, RBuffer *rbuf, size_t count, @@ -574,13 +575,12 @@ static void send_event(Channel *channel,  static void broadcast_event(char *name, Array args)  { -  kvec_t(Channel *) subscribed; -  kv_init(subscribed); +  kvec_t(Channel *) subscribed = KV_INITIAL_VALUE;    Channel *channel;    map_foreach_value(channels, channel, {      if (pmap_has(cstr_t)(channel->subscribed_events, name)) { -      kv_push(Channel *, subscribed, channel); +      kv_push(subscribed, channel);      }    }); @@ -600,7 +600,7 @@ static void broadcast_event(char *name, Array args)    for (size_t i = 0; i < kv_size(subscribed); i++) {      Channel *channel = kv_A(subscribed, i);      if (channel->pending_requests) { -      kv_push(WBuffer *, channel->delayed_notifications, buffer); +      kv_push(channel->delayed_notifications, buffer);      } else {        channel_write(channel, buffer);      } @@ -647,7 +647,7 @@ static void close_channel(Channel *channel)      case kChannelTypeStdio:        stream_close(&channel->data.std.in, NULL);        stream_close(&channel->data.std.out, NULL); -      queue_put(loop.fast_events, exit_event, 1, channel); +      queue_put(main_loop.fast_events, exit_event, 1, channel);        return;      default:        abort(); @@ -692,7 +692,7 @@ static void close_cb(Stream *stream, void *data)  static Channel *register_channel(ChannelType type)  {    Channel *rv = xmalloc(sizeof(Channel)); -  rv->events = queue_new_child(loop.events); +  rv->events = queue_new_child(main_loop.events);    rv->type = type;    rv->refcount = 1;    rv->closed = false; diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 6cc56ba3dd..abbd3e8aff 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -12,6 +12,7 @@  #include "nvim/eval.h"  #include "nvim/garray.h"  #include "nvim/vim.h" +#include "nvim/main.h"  #include "nvim/memory.h"  #include "nvim/log.h"  #include "nvim/fileio.h" @@ -108,7 +109,7 @@ int server_start(const char *endpoint)    }    SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); -  socket_watcher_init(&loop, watcher, endpoint, NULL); +  socket_watcher_init(&main_loop, watcher, endpoint, NULL);    // Check if a watcher for the endpoint already exists    for (int i = 0; i < watchers.ga_len; i++) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 382c4943ff..cc604352e1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7879,7 +7879,7 @@ static void nv_event(cmdarg_T *cap)    // not safe to perform garbage collection because there could be unreferenced    // lists or dicts being used.    may_garbage_collect = false; -  queue_process_events(loop.events); +  queue_process_events(main_loop.events);    cap->retval |= CA_COMMAND_BUSY;       // don't call edit() now  } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7687b14f02..0c46dc96ee 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -60,7 +60,7 @@ void input_start(int fd)    }    global_fd = fd; -  rstream_init_fd(&loop, &read_stream, fd, READ_BUFFER_SIZE, NULL); +  rstream_init_fd(&main_loop, &read_stream, fd, READ_BUFFER_SIZE, NULL);    rstream_start(&read_stream, read_cb);  } @@ -87,8 +87,8 @@ static void create_cursorhold_event(void)    // have been called(inbuf_poll would return kInputAvail)    // TODO(tarruda): Cursorhold should be implemented as a timer set during the    // `state_check` callback for the states where it can be triggered. -  assert(!events_enabled || queue_empty(loop.events)); -  queue_put(loop.events, cursorhold_event, 0); +  assert(!events_enabled || queue_empty(main_loop.events)); +  queue_put(main_loop.events, cursorhold_event, 0);  }  // Low level input function @@ -147,7 +147,7 @@ bool os_char_avail(void)  void os_breakcheck(void)  {    if (!got_int) { -    loop_poll_events(&loop, 0); +    loop_poll_events(&main_loop, 0);    }  } @@ -322,7 +322,7 @@ static bool input_poll(int ms)      prof_inchar_enter();    } -  LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, ms, input_ready() || input_eof); +  LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof);    if (do_profiling == PROF_YES && ms) {      prof_inchar_exit(); @@ -419,5 +419,5 @@ static void read_error_exit(void)  static bool pending_events(void)  { -  return events_enabled && !queue_empty(loop.events); +  return events_enabled && !queue_empty(main_loop.events);  } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f5a1637c94..988620b145 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -14,6 +14,7 @@  #include "nvim/os/shell.h"  #include "nvim/os/signal.h"  #include "nvim/types.h" +#include "nvim/main.h"  #include "nvim/vim.h"  #include "nvim/message.h"  #include "nvim/memory.h" @@ -205,16 +206,16 @@ static int do_os_system(char **argv,    xstrlcpy(prog, argv[0], MAXPATHL);    Stream in, out, err; -  LibuvProcess uvproc = libuv_process_init(&loop, &buf); +  LibuvProcess uvproc = libuv_process_init(&main_loop, &buf);    Process *proc = &uvproc.process; -  Queue *events = queue_new_child(loop.events); +  Queue *events = queue_new_child(main_loop.events);    proc->events = events;    proc->argv = argv;    proc->in = input != NULL ? &in : NULL;    proc->out = &out;    proc->err = &err;    if (!process_spawn(proc)) { -    loop_poll_events(&loop, 0); +    loop_poll_events(&main_loop, 0);      // Failed, probably due to `sh` not being executable      if (!silent) {        MSG_PUTS(_("\nCannot execute ")); diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 0ff6016e32..4abc9cfc36 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -8,6 +8,7 @@  #include "nvim/globals.h"  #include "nvim/memline.h"  #include "nvim/eval.h" +#include "nvim/main.h"  #include "nvim/memory.h"  #include "nvim/misc1.h"  #include "nvim/misc2.h" @@ -28,10 +29,10 @@ static bool rejecting_deadly;  void signal_init(void)  { -  signal_watcher_init(&loop, &spipe, NULL); -  signal_watcher_init(&loop, &shup, NULL); -  signal_watcher_init(&loop, &squit, NULL); -  signal_watcher_init(&loop, &sterm, NULL); +  signal_watcher_init(&main_loop, &spipe, NULL); +  signal_watcher_init(&main_loop, &shup, NULL); +  signal_watcher_init(&main_loop, &squit, NULL); +  signal_watcher_init(&main_loop, &sterm, NULL);  #ifdef SIGPIPE    signal_watcher_start(&spipe, on_signal, SIGPIPE);  #endif @@ -41,7 +42,7 @@ void signal_init(void)  #endif    signal_watcher_start(&sterm, on_signal, SIGTERM);  #ifdef SIGPWR -  signal_watcher_init(&loop, &spwr, NULL); +  signal_watcher_init(&main_loop, &spwr, NULL);    signal_watcher_start(&spwr, on_signal, SIGPWR);  #endif  } diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 188f0802c9..2205ad0958 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -9,6 +9,7 @@  #include "nvim/os/time.h"  #include "nvim/event/loop.h"  #include "nvim/vim.h" +#include "nvim/main.h"  static uv_mutex_t delay_mutex;  static uv_cond_t delay_cond; @@ -43,7 +44,7 @@ void os_delay(uint64_t milliseconds, bool ignoreinput)      if (milliseconds > INT_MAX) {        milliseconds = INT_MAX;      } -    LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, (int)milliseconds, got_int); +    LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int);    } else {      os_microdelay(milliseconds * 1000);    } diff --git a/src/nvim/state.c b/src/nvim/state.c index b2f3f0bebe..30133e2201 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -4,6 +4,7 @@  #include "nvim/state.h"  #include "nvim/vim.h" +#include "nvim/main.h"  #include "nvim/getchar.h"  #include "nvim/ui.h"  #include "nvim/os/input.h" @@ -32,7 +33,7 @@ getkey:        // processing. Characters can come from mappings, scripts and other        // sources, so this scenario is very common.        key = safe_vgetc(); -    } else if (!queue_empty(loop.events)) { +    } else if (!queue_empty(main_loop.events)) {        // Event was made available after the last queue_process_events call        key = K_EVENT;      } else { @@ -45,7 +46,7 @@ getkey:        // directly.        (void)os_inchar(NULL, 0, -1, 0);        input_disable_events(); -      key = !queue_empty(loop.events) ? K_EVENT : safe_vgetc(); +      key = !queue_empty(main_loop.events) ? K_EVENT : safe_vgetc();      }      if (key == K_EVENT) { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 104cc47cda..df5e03880a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -63,6 +63,7 @@  #include "nvim/map.h"  #include "nvim/misc1.h"  #include "nvim/move.h" +#include "nvim/main.h"  #include "nvim/state.h"  #include "nvim/ex_docmd.h"  #include "nvim/ex_cmds.h" @@ -163,9 +164,9 @@ static VTermColor default_vt_bg_rgb;  void terminal_init(void)  {    invalidated_terminals = pmap_new(ptr_t)(); -  time_watcher_init(&loop, &refresh_timer, NULL); +  time_watcher_init(&main_loop, &refresh_timer, NULL);    // refresh_timer_cb will redraw the screen which can call vimscript -  refresh_timer.events = queue_new_child(loop.events); +  refresh_timer.events = queue_new_child(main_loop.events);    // initialize a rgb->color index map for cterm attributes(VTermScreenCell    // only has RGB information and we need color indexes for terminal UIs) @@ -452,7 +453,7 @@ static int terminal_execute(VimState *state, int key)      case K_EVENT:        // We cannot let an event free the terminal yet. It is still needed.        s->term->refcount++; -      queue_process_events(loop.events); +      queue_process_events(main_loop.events);        s->term->refcount--;        if (s->term->buf_handle == 0) {          s->close = true; diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 99eb230a88..f374c6dc7a 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -4,6 +4,7 @@  #include "nvim/api/vim.h"  #include "nvim/api/private/helpers.h"  #include "nvim/ascii.h" +#include "nvim/main.h"  #include "nvim/misc2.h"  #include "nvim/os/os.h"  #include "nvim/os/input.h" @@ -92,7 +93,7 @@ static void flush_input(TermInput *input, bool wait_until_empty)    size_t drain_boundary = wait_until_empty ? 0 : 0xff;    do {      uv_mutex_lock(&input->key_buffer_mutex); -    loop_schedule(&loop, event_create(1, wait_input_enqueue, 1, input)); +    loop_schedule(&main_loop, event_create(1, wait_input_enqueue, 1, input));      input->waiting = true;      while (input->waiting) {        uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); @@ -336,7 +337,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,        stream_close(&input->read_stream, NULL);        queue_put(input->loop->fast_events, restart_reading, 1, input);      } else { -      loop_schedule(&loop, event_create(1, input_done_event, 0)); +      loop_schedule(&main_loop, event_create(1, input_done_event, 0));      }      return;    } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 62bc81ba64..50558e644a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -11,6 +11,7 @@  #include "nvim/vim.h"  #include "nvim/ui.h"  #include "nvim/map.h" +#include "nvim/main.h"  #include "nvim/memory.h"  #include "nvim/api/vim.h"  #include "nvim/api/private/helpers.h" @@ -261,7 +262,7 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)    UI *ui = data;    update_size(ui);    // run refresh_event in nvim main loop -  loop_schedule(&loop, event_create(1, refresh_event, 0)); +  loop_schedule(&main_loop, event_create(1, refresh_event, 0));  }  static bool attrs_differ(HlAttrs a1, HlAttrs a2) @@ -681,7 +682,7 @@ static void invalidate(UI *ui, int top, int bot, int left, int right)      intersects->right = MAX(right, intersects->right);    } else {      // Else just add a new entry; -    kv_push(Rect, data->invalid_regions, ((Rect){top, bot, left, right})); +    kv_push(data->invalid_regions, ((Rect) { top, bot, left, right }));    }  } diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index d17fa4a782..41d35684b1 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -5,6 +5,7 @@  #include <stdio.h>  #include <limits.h> +#include "nvim/main.h"  #include "nvim/vim.h"  #include "nvim/ui.h"  #include "nvim/memory.h" @@ -100,7 +101,7 @@ static void ui_bridge_stop(UI *b)      if (stopped) {        break;      } -    loop_poll_events(&loop, 10); +    loop_poll_events(&main_loop, 10);    }    uv_thread_join(&bridge->ui_thread);    uv_mutex_destroy(&bridge->mutex); diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index 93103e4e8c..906f950308 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -30,10 +30,6 @@ describe('shell functions', function()      cimported.p_shcf = to_cstr('-c')    end) -  teardown(function() -    cimported.event_teardown() -  end) -    local function shell_build_argv(cmd, extra_args)      local res = cimported.shell_build_argv(          cmd and to_cstr(cmd), diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index e935d2af6a..0034670ee8 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -8,6 +8,38 @@ local to_cstr = helpers.to_cstr  local strings = cimport('stdlib.h', './src/nvim/strings.h',                          './src/nvim/memory.h') +describe('vim_strsave_escaped()', function() +  local vim_strsave_escaped = function(s, chars) +    local res = strings.vim_strsave_escaped(to_cstr(s), to_cstr(chars)) +    local ret = ffi.string(res) + +    -- Explicitly free memory so we are sure it is allocated: if it was not it  +    -- will crash. +    strings.xfree(res) +    return ret +  end + +  it('precedes by a backslash all chars from second argument', function() +    eq([[\a\b\c\d]], vim_strsave_escaped('abcd','abcd')) +  end) + +  it('precedes by a backslash chars only from second argument', function() +    eq([[\a\bcd]], vim_strsave_escaped('abcd','ab')) +  end) + +  it('returns a copy of passed string if second argument is empty', function() +    eq('text \n text', vim_strsave_escaped('text \n text','')) +  end) + +  it('returns an empty string if first argument is empty string', function() +    eq('', vim_strsave_escaped('','\r')) +  end) + +  it('returns a copy of passed string if it does not contain chars from 2nd argument', function() +    eq('some text', vim_strsave_escaped('some text', 'a')) +  end) +end) +  describe('vim_strnsave_unquoted()', function()    local vim_strnsave_unquoted = function(s, len)      local res = strings.vim_strnsave_unquoted(to_cstr(s), len or #s) | 
