diff --git a/src/api/helpers.c b/src/api/helpers.c index 2e189861a5..584249fcc6 100644 --- a/src/api/helpers.c +++ b/src/api/helpers.c @@ -20,6 +20,8 @@ KHASH_SET_INIT_INT64(Lookup) /// @return The converted value static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup); +static bool object_to_vim(Object obj, typval_T *tv, Error *err); + void try_start() { ++trylevel; @@ -62,6 +64,76 @@ bool try_end(Error *err) return err->set; } +Object dict_get_value(dict_T *dict, String key, bool pop, Error *err) +{ + Object rv; + hashitem_T *hi; + dictitem_T *di; + char k[key.size + 1]; + // Convert the key + memcpy(k, key.data, key.size); + k[key.size] = NUL; + hi = hash_find(&dict->dv_hashtab, (uint8_t *)k); + + if (HASHITEM_EMPTY(hi)) { + set_api_error("Key not found", err); + return rv; + } + + di = dict_lookup(hi); + rv = vim_to_object(&di->di_tv); + + if (pop) { + if (dict->dv_lock) { + set_api_error("Dictionary is locked", err); + return rv; + } + + hash_remove(&dict->dv_hashtab, hi); + dictitem_free(di); + } + + return rv; +} + +Object dict_set_value(dict_T *dict, String key, Object value, Error *err) +{ + Object rv = {.type = kObjectTypeNil}; + + if (dict->dv_lock) { + set_api_error("Dictionary is locked", err); + return rv; + } + + if (key.size == 0) { + set_api_error("Empty dictionary keys aren't allowed", err); + return rv; + } + + dictitem_T *di = dict_find(dict, (uint8_t *)key.data, key.size); + typval_T tv; + + if (!object_to_vim(value, &tv, err)) { + return rv; + } + + if (di == NULL) { + uint8_t k[key.size + 1]; + memcpy(k, key.data, key.size); + k[key.size] = NUL; + di = dictitem_alloc(k); + dict_add(dict, di); + } else { + rv = vim_to_object(&di->di_tv); + clear_tv(&di->di_tv); + } + + copy_tv(&tv, &di->di_tv); + clear_tv(&tv); + + return rv; +} + Object vim_to_object(typval_T *obj) { Object rv; @@ -73,6 +145,94 @@ Object vim_to_object(typval_T *obj) return rv; } +static bool object_to_vim(Object obj, typval_T *tv, Error *err) +{ + tv->v_type = VAR_UNKNOWN; + tv->v_lock = 0; + + switch (obj.type) { + case kObjectTypeNil: + tv->v_type = VAR_NUMBER; + tv->vval.v_number = 0; + break; + + case kObjectTypeBool: + tv->v_type = VAR_NUMBER; + tv->vval.v_number = obj.data.boolean; + break; + + case kObjectTypeInt: + tv->v_type = VAR_NUMBER; + tv->vval.v_number = obj.data.integer; + break; + + case kObjectTypeFloat: + tv->v_type = VAR_FLOAT; + tv->vval.v_float = obj.data.floating_point; + break; + + case kObjectTypeString: + tv->v_type = VAR_STRING; + tv->vval.v_string = (uint8_t *)xstrndup(obj.data.string.data, + obj.data.string.size); + break; + + case kObjectTypeArray: + tv->v_type = VAR_LIST; + tv->vval.v_list = list_alloc(); + + for (uint32_t i = 0; i < obj.data.array.size; i++) { + Object item = obj.data.array.items[i]; + listitem_T *li = listitem_alloc(); + + if (!object_to_vim(item, &li->li_tv, err)) { + // cleanup + listitem_free(li); + list_free(tv->vval.v_list, true); + return false; + } + + list_append(tv->vval.v_list, li); + } + tv->vval.v_list->lv_refcount++; + break; + + case kObjectTypeDictionary: + tv->v_type = VAR_DICT; + tv->vval.v_dict = dict_alloc(); + + for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { + KeyValuePair item = obj.data.dictionary.items[i]; + String key = item.key; + + if (key.size == 0) { + set_api_error("Empty dictionary keys aren't allowed", err); + // cleanup + dict_free(tv->vval.v_dict, true); + return false; + } + + char k[key.size + 1]; + memcpy(k, key.data, key.size); + k[key.size] = NUL; + dictitem_T *di = dictitem_alloc((uint8_t *)k); + + if (!object_to_vim(item.value, &di->di_tv, err)) { + // cleanup + dictitem_free(di); + dict_free(tv->vval.v_dict, true); + return false; + } + + dict_add(tv->vval.v_dict, di); + } + tv->vval.v_dict->dv_refcount++; + break; + } + + return true; +} + static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup) { Object rv = {.type = kObjectTypeNil}; diff --git a/src/api/helpers.h b/src/api/helpers.h index ddb46eaad4..d839fe30e7 100644 --- a/src/api/helpers.h +++ b/src/api/helpers.h @@ -22,6 +22,24 @@ void try_start(void); /// @return true if an error occurred bool try_end(Error *err); +/// Recursively expands a vimscript value in a dict +/// +/// @param dict The vimscript dict +/// @param key The key +/// @param bool If true it will pop the value from the dict +/// @param[out] err Details of an error that may have occurred +Object dict_get_value(dict_T *dict, String key, bool pop, Error *err); + +/// Set a value in a dict. Objects are recursively expanded into their +/// vimscript equivalents. +/// +/// @param dict The vimscript dict +/// @param key The key +/// @param value The new value +/// @param[out] err Details of an error that may have occurred +/// @return the old value, if any +Object dict_set_value(dict_T *dict, String key, Object value, Error *err); + /// Convert a vim object to an `Object` instance, recursively expanding /// Arrays/Dictionaries. /// diff --git a/src/api/vim.c b/src/api/vim.c index efe37d9e9a..93654da91f 100644 --- a/src/api/vim.c +++ b/src/api/vim.c @@ -127,14 +127,16 @@ void vim_set_current_line(Object line, Error *err) buffer_set_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, line, err); } -Object vim_get_var(bool special, String name, Error *err) +Object vim_get_var(bool special, String name, bool pop, Error *err) { - abort(); + return dict_get_value(special ? &vimvardict : &globvardict, name, + special ? false : pop, + err); } -void vim_set_var(bool special, String name, Object value, Error *err) +Object vim_set_var(String name, Object value, Error *err) { - abort(); + return dict_set_value(&globvardict, name, value, err); } String vim_get_option(String name, Error *err) diff --git a/src/api/vim.h b/src/api/vim.h index 8335081de7..a18f9c69e9 100644 --- a/src/api/vim.h +++ b/src/api/vim.h @@ -62,15 +62,15 @@ void vim_set_current_line(Object line, Error *err); /// @param name The variable name /// @param[out] err Details of an error that may have occurred /// @return The variable value -Object vim_get_var(bool special, String name, Error *err); +Object vim_get_var(bool special, String name, bool pop, Error *err); -/// Sets a global or special variable +/// Sets a global variable /// -/// @param special If it's a special(:v) variable /// @param name The variable name /// @param value The variable value /// @param[out] err Details of an error that may have occurred -void vim_set_var(bool special, String name, Object value, Error *err); +/// @return the old value if any +Object vim_set_var(String name, Object value, Error *err); /// Get an option value string ///