diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 4cb90a4588..c1a2183f24 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -1688,7 +1688,7 @@ def filter_source(filename, keep_tmpfiles): else: """Filters the source to fix macros that confuse Doxygen.""" with open(filename, 'rt') as fp: - print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))', + print(re.sub(r'^(ArrayOf|DictionaryOf|Dict)(\(.*?\))', lambda m: m.group(1)+'_'.join( re.split(r'[^\w]+', m.group(2))), fp.read(), flags=re.M)) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 4e84b41a02..0948abf065 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -105,73 +105,81 @@ bool ns_initialized(uint32_t ns) return ns < (uint32_t)next_namespace_id; } -Array virt_text_to_array(VirtText vt, bool hl_name) +Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena) { - Array chunks = ARRAY_DICT_INIT; - Array hl_array = ARRAY_DICT_INIT; + Array chunks = arena_array(arena, kv_size(vt)); for (size_t i = 0; i < kv_size(vt); i++) { + size_t j = i; + for (; j < kv_size(vt); j++) { + if (kv_A(vt, j).text != NULL) { + break; + } + } + + Array hl_array = arena_array(arena, i < j ? j - i + 1 : 0); + for (; i < j; i++) { + int hl_id = kv_A(vt, i).hl_id; + if (hl_id > 0) { + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); + } + } + char *text = kv_A(vt, i).text; int hl_id = kv_A(vt, i).hl_id; - if (text == NULL) { - if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); - } - continue; - } - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, CSTR_TO_OBJ(text)); + Array chunk = arena_array(arena, 2); + ADD_C(chunk, CSTR_AS_OBJ(text)); if (hl_array.size > 0) { if (hl_id > 0) { - ADD(hl_array, hl_group_name(hl_id, hl_name)); + ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } - ADD(chunk, ARRAY_OBJ(hl_array)); - hl_array = (Array)ARRAY_DICT_INIT; + ADD_C(chunk, ARRAY_OBJ(hl_array)); } else if (hl_id > 0) { - ADD(chunk, hl_group_name(hl_id, hl_name)); + ADD_C(chunk, hl_group_name(hl_id, hl_name)); } - ADD(chunks, ARRAY_OBJ(chunk)); + ADD_C(chunks, ARRAY_OBJ(chunk)); } - assert(hl_array.size == 0); return chunks; } -static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name) +static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name, Arena *arena) { MTKey start = extmark.start; - Array rv = ARRAY_DICT_INIT; + Array rv = arena_array(arena, 4); if (id) { - ADD(rv, INTEGER_OBJ((Integer)start.id)); + ADD_C(rv, INTEGER_OBJ((Integer)start.id)); } - ADD(rv, INTEGER_OBJ(start.pos.row)); - ADD(rv, INTEGER_OBJ(start.pos.col)); + ADD_C(rv, INTEGER_OBJ(start.pos.row)); + ADD_C(rv, INTEGER_OBJ(start.pos.col)); if (add_dict) { - Dictionary dict = ARRAY_DICT_INIT; + // TODO(bfredl): coding the size like this is a bit fragile. + // We want ArrayOf(Dict(set_extmark)) as the return type.. + Dictionary dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table)); - PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); + PUT_C(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); - PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); + PUT_C(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start))); if (mt_paired(start)) { - PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); - PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); - PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); + PUT_C(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row)); + PUT_C(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col)); + PUT_C(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); } if (mt_no_undo(start)) { - PUT(dict, "undo_restore", BOOLEAN_OBJ(false)); + PUT_C(dict, "undo_restore", BOOLEAN_OBJ(false)); } if (mt_invalidate(start)) { - PUT(dict, "invalidate", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalidate", BOOLEAN_OBJ(true)); } if (mt_invalid(start)) { - PUT(dict, "invalid", BOOLEAN_OBJ(true)); + PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); } - decor_to_dict_legacy(&dict, mt_decor(start), hl_name); + decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); - ADD(rv, DICTIONARY_OBJ(dict)); + ADD_C(rv, DICTIONARY_OBJ(dict)); } return rv; @@ -190,7 +198,7 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na /// absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, Integer id, Dict(get_extmark) *opts, - Error *err) + Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -213,7 +221,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, if (extmark.start.pos.row < 0) { return rv; } - return extmark_to_array(extmark, false, details, hl_name); + return extmark_to_array(extmark, false, details, hl_name, arena); } /// Gets |extmarks| in "traversal order" from a |charwise| region defined by @@ -272,7 +280,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// @param[out] err Error details, if any /// @return List of [extmark_id, row, col] tuples in "traversal order". Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, - Dict(get_extmarks) *opts, Error *err) + Dict(get_extmarks) *opts, Arena *arena, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -336,8 +344,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row, u_col, (int64_t)limit, reverse, type, opts->overlap); + rv = arena_array(arena, kv_size(marks)); for (size_t i = 0; i < kv_size(marks); i++) { - ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name))); + ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); } kv_destroy(marks); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9ce1786fa0..cc95f46baf 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1014,6 +1014,54 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error return true; } +Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena) +{ + Dictionary rv = arena_dict(arena, max_size); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + Object val = NIL; + + if (field->type == kObjectTypeNil) { + val = *(Object *)mem; + } else if (field->type == kObjectTypeInteger) { + val = INTEGER_OBJ(*(Integer *)mem); + } else if (field->type == kObjectTypeFloat) { + val = FLOAT_OBJ(*(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + val = BOOLEAN_OBJ(*(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + val = STRING_OBJ(*(String *)mem); + } else if (field->type == kObjectTypeArray) { + val = ARRAY_OBJ(*(Array *)mem); + } else if (field->type == kObjectTypeDictionary) { + val = DICTIONARY_OBJ(*(Dictionary *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + val.data.integer = *(Integer *)mem; + val.type = field->type; + } else if (field->type == kObjectTypeLuaRef) { + // do nothing + } else { + abort(); + } + + PUT_C(rv, field->str, val); + } + + return rv; +} + void api_free_keydict(void *dict, KeySetLink *table) { for (size_t i = 0; table[i].str; i++) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8a56d1704f..0dbe3d2fb6 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -32,6 +32,7 @@ #define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) +#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(arena_string(arena, cstr_as_string(s))) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ @@ -70,6 +71,9 @@ #define PUT_C(dict, k, v) \ kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) +#define PUT_KEY(d, typ, key, v) \ + do { (d).is_set__##typ##_ |= (1 << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0) + #define ADD(array, item) \ kv_push(array, item) diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 041aaed976..a9d7a8d05c 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -539,30 +539,24 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) #undef HAS_KEY_X } -static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, - BorderTextType bordertext_type) +#define PUT_KEY_X(d, key, value) PUT_KEY(d, float_config, key, value) +static void config_put_bordertext(Dict(float_config) *config, FloatConfig *fconfig, + BorderTextType bordertext_type, Arena *arena) { VirtText vt; AlignTextPos align; - char *field_name; - char *field_pos_name; switch (bordertext_type) { case kBorderTextTitle: vt = fconfig->title_chunks; align = fconfig->title_pos; - field_name = "title"; - field_pos_name = "title_pos"; break; case kBorderTextFooter: vt = fconfig->footer_chunks; align = fconfig->footer_pos; - field_name = "footer"; - field_pos_name = "footer_pos"; break; } - Array bordertext = virt_text_to_array(vt, true); - PUT(config, field_name, ARRAY_OBJ(bordertext)); + Array bordertext = virt_text_to_array(vt, true, arena); char *pos; switch (align) { @@ -576,9 +570,16 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, pos = "right"; break; } - PUT(config, field_pos_name, CSTR_TO_OBJ(pos)); - return config; + switch (bordertext_type) { + case kBorderTextTitle: + PUT_KEY_X(*config, title, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, title_pos, cstr_as_string(pos)); + break; + case kBorderTextFooter: + PUT_KEY_X(*config, footer, ARRAY_OBJ(bordertext)); + PUT_KEY_X(*config, footer_pos, cstr_as_string(pos)); + } } /// Gets window configuration. @@ -590,7 +591,7 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return Map defining the window configuration, see |nvim_open_win()| -Dictionary nvim_win_get_config(Window window, Error *err) +Dict(float_config) nvim_win_get_config(Window window, Arena *arena, Error *err) FUNC_API_SINCE(6) { /// Keep in sync with FloatRelative in buffer_defs.h @@ -599,7 +600,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) /// Keep in sync with WinSplit in buffer_defs.h static const char *const win_split_str[] = { "left", "right", "above", "below" }; - Dictionary rv = ARRAY_DICT_INIT; + Dict(float_config) rv = { 0 }; win_T *wp = find_window_by_handle(window, err); if (!wp) { @@ -608,65 +609,62 @@ Dictionary nvim_win_get_config(Window window, Error *err) FloatConfig *config = &wp->w_float_config; - PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); - PUT(rv, "external", BOOLEAN_OBJ(config->external)); - PUT(rv, "hide", BOOLEAN_OBJ(config->hide)); + PUT_KEY_X(rv, focusable, config->focusable); + PUT_KEY_X(rv, external, config->external); + PUT_KEY_X(rv, hide, config->hide); if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(config->width)); - PUT(rv, "height", INTEGER_OBJ(config->height)); + PUT_KEY_X(rv, width, config->width); + PUT_KEY_X(rv, height, config->height); if (!config->external) { if (config->relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(config->window)); + PUT_KEY_X(rv, win, config->window); if (config->bufpos.lnum >= 0) { - Array pos = ARRAY_DICT_INIT; - ADD(pos, INTEGER_OBJ(config->bufpos.lnum)); - ADD(pos, INTEGER_OBJ(config->bufpos.col)); - PUT(rv, "bufpos", ARRAY_OBJ(pos)); + Array pos = arena_array(arena, 2); + ADD_C(pos, INTEGER_OBJ(config->bufpos.lnum)); + ADD_C(pos, INTEGER_OBJ(config->bufpos.col)); + PUT_KEY_X(rv, bufpos, pos); } } - PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor])); - PUT(rv, "row", FLOAT_OBJ(config->row)); - PUT(rv, "col", FLOAT_OBJ(config->col)); - PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); + PUT_KEY_X(rv, anchor, cstr_as_string((char *)float_anchor_str[config->anchor])); + PUT_KEY_X(rv, row, config->row); + PUT_KEY_X(rv, col, config->col); + PUT_KEY_X(rv, zindex, config->zindex); } if (config->border) { - Array border = ARRAY_DICT_INIT; + Array border = arena_array(arena, 8); for (size_t i = 0; i < 8; i++) { - Array tuple = ARRAY_DICT_INIT; - - String s = cstrn_to_string(config->border_chars[i], MAX_SCHAR_SIZE); + String s = cstrn_as_string(config->border_chars[i], MAX_SCHAR_SIZE); int hi_id = config->border_hl_ids[i]; char *hi_name = syn_id2name(hi_id); if (hi_name[0]) { - ADD(tuple, STRING_OBJ(s)); - ADD(tuple, CSTR_TO_OBJ(hi_name)); - ADD(border, ARRAY_OBJ(tuple)); + Array tuple = arena_array(arena, 2); + ADD_C(tuple, STRING_OBJ(s)); + ADD_C(tuple, CSTR_AS_OBJ(hi_name)); + ADD_C(border, ARRAY_OBJ(tuple)); } else { - ADD(border, STRING_OBJ(s)); + ADD_C(border, STRING_OBJ(s)); } } - PUT(rv, "border", ARRAY_OBJ(border)); + PUT_KEY_X(rv, border, ARRAY_OBJ(border)); if (config->title) { - rv = config_put_bordertext(rv, config, kBorderTextTitle); + config_put_bordertext(&rv, config, kBorderTextTitle, arena); } if (config->footer) { - rv = config_put_bordertext(rv, config, kBorderTextFooter); + config_put_bordertext(&rv, config, kBorderTextFooter, arena); } } } else if (!config->external) { - PUT(rv, "width", INTEGER_OBJ(wp->w_width)); - PUT(rv, "height", INTEGER_OBJ(wp->w_height)); + PUT_KEY_X(rv, width, wp->w_width); + PUT_KEY_X(rv, height, wp->w_height); WinSplit split = win_split_dir(wp); - PUT(rv, "split", CSTR_TO_OBJ(win_split_str[split])); + PUT_KEY_X(rv, split, cstr_as_string((char *)win_split_str[split])); } - if (wp->w_floating && !config->external) { - PUT(rv, "relative", CSTR_TO_OBJ(float_relative_str[config->relative])); - } else { - PUT(rv, "relative", CSTR_TO_OBJ("")); - } + const char *rel = (wp->w_floating && !config->external + ? float_relative_str[config->relative] : ""); + PUT_KEY_X(rv, relative, cstr_as_string((char *)rel)); return rv; } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index b13ec25f50..a593b8eda1 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -919,7 +919,9 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo } /// This assumes maximum one entry of each kind, which will not always be the case. -void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) +/// +/// NB: assumes caller has allocated enough space in dict for all fields! +void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name, Arena *arena) { DecorSignHighlight sh_hl = DECOR_SIGN_HIGHLIGHT_INIT; DecorSignHighlight sh_sign = DECOR_SIGN_HIGHLIGHT_INIT; @@ -953,58 +955,58 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) } if (sh_hl.hl_id) { - PUT(*dict, "hl_group", hl_group_name(sh_hl.hl_id, hl_name)); - PUT(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol)); + PUT_C(*dict, "hl_group", hl_group_name(sh_hl.hl_id, hl_name)); + PUT_C(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol)); priority = sh_hl.priority; } if (sh_hl.flags & kSHConceal) { char buf[MAX_SCHAR_SIZE]; schar_get(buf, sh_hl.text[0]); - PUT(*dict, "conceal", CSTR_TO_OBJ(buf)); + PUT_C(*dict, "conceal", CSTR_TO_ARENA_OBJ(arena, buf)); } if (sh_hl.flags & kSHSpellOn) { - PUT(*dict, "spell", BOOLEAN_OBJ(true)); + PUT_C(*dict, "spell", BOOLEAN_OBJ(true)); } else if (sh_hl.flags & kSHSpellOff) { - PUT(*dict, "spell", BOOLEAN_OBJ(false)); + PUT_C(*dict, "spell", BOOLEAN_OBJ(false)); } if (sh_hl.flags & kSHUIWatched) { - PUT(*dict, "ui_watched", BOOLEAN_OBJ(true)); + PUT_C(*dict, "ui_watched", BOOLEAN_OBJ(true)); } if (sh_hl.url != NULL) { - PUT(*dict, "url", STRING_OBJ(cstr_to_string(sh_hl.url))); + PUT_C(*dict, "url", STRING_OBJ(cstr_as_string((char *)sh_hl.url))); } if (virt_text) { if (virt_text->hl_mode) { - PUT(*dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[virt_text->hl_mode])); + PUT_C(*dict, "hl_mode", CSTR_AS_OBJ((char *)hl_mode_str[virt_text->hl_mode])); } - Array chunks = virt_text_to_array(virt_text->data.virt_text, hl_name); - PUT(*dict, "virt_text", ARRAY_OBJ(chunks)); - PUT(*dict, "virt_text_hide", BOOLEAN_OBJ(virt_text->flags & kVTHide)); - PUT(*dict, "virt_text_repeat_linebreak", BOOLEAN_OBJ(virt_text->flags & kVTRepeatLinebreak)); + Array chunks = virt_text_to_array(virt_text->data.virt_text, hl_name, arena); + PUT_C(*dict, "virt_text", ARRAY_OBJ(chunks)); + PUT_C(*dict, "virt_text_hide", BOOLEAN_OBJ(virt_text->flags & kVTHide)); + PUT_C(*dict, "virt_text_repeat_linebreak", BOOLEAN_OBJ(virt_text->flags & kVTRepeatLinebreak)); if (virt_text->pos == kVPosWinCol) { - PUT(*dict, "virt_text_win_col", INTEGER_OBJ(virt_text->col)); + PUT_C(*dict, "virt_text_win_col", INTEGER_OBJ(virt_text->col)); } - PUT(*dict, "virt_text_pos", CSTR_TO_OBJ(virt_text_pos_str[virt_text->pos])); + PUT_C(*dict, "virt_text_pos", CSTR_AS_OBJ((char *)virt_text_pos_str[virt_text->pos])); priority = virt_text->priority; } if (virt_lines) { - Array all_chunks = ARRAY_DICT_INIT; + Array all_chunks = arena_array(arena, kv_size(virt_lines->data.virt_lines)); bool virt_lines_leftcol = false; for (size_t i = 0; i < kv_size(virt_lines->data.virt_lines); i++) { virt_lines_leftcol = kv_A(virt_lines->data.virt_lines, i).left_col; - Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name); + Array chunks = virt_text_to_array(kv_A(virt_lines->data.virt_lines, i).line, hl_name, arena); ADD(all_chunks, ARRAY_OBJ(chunks)); } - PUT(*dict, "virt_lines", ARRAY_OBJ(all_chunks)); - PUT(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove)); - PUT(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); + PUT_C(*dict, "virt_lines", ARRAY_OBJ(all_chunks)); + PUT_C(*dict, "virt_lines_above", BOOLEAN_OBJ(virt_lines->flags & kVTLinesAbove)); + PUT_C(*dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); priority = virt_lines->priority; } @@ -1012,11 +1014,11 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) if (sh_sign.text[0]) { char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; describe_sign_text(buf, sh_sign.text); - PUT(*dict, "sign_text", CSTR_TO_OBJ(buf)); + PUT_C(*dict, "sign_text", CSTR_TO_ARENA_OBJ(arena, buf)); } if (sh_sign.sign_name) { - PUT(*dict, "sign_name", CSTR_TO_OBJ(sh_sign.sign_name)); + PUT_C(*dict, "sign_name", CSTR_AS_OBJ(sh_sign.sign_name)); } // uncrustify:off @@ -1033,14 +1035,14 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name) for (int j = 0; hls[j].name; j++) { if (hls[j].val) { - PUT(*dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); + PUT_C(*dict, hls[j].name, hl_group_name(hls[j].val, hl_name)); } } priority = sh_sign.priority; } if (priority != -1) { - PUT(*dict, "priority", INTEGER_OBJ(priority)); + PUT_C(*dict, "priority", INTEGER_OBJ(priority)); } } @@ -1068,7 +1070,7 @@ uint16_t decor_type_flags(DecorInline decor) Object hl_group_name(int hl_id, bool hl_name) { if (hl_name) { - return CSTR_TO_OBJ(syn_id2name(hl_id)); + return CSTR_AS_OBJ(syn_id2name(hl_id)); } else { return INTEGER_OBJ(hl_id); } diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 2eee1724c0..14b4ecb1a3 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -212,6 +212,9 @@ for _, f in ipairs(functions) do end f_exported.parameters[i] = param end + if startswith(f.return_type, 'Dict(') then + f_exported.return_type = 'Dictionary' + end exported_functions[#exported_functions + 1] = f_exported end end @@ -279,7 +282,7 @@ for _, k in ipairs(keysets) do return k.name .. '_table[' .. idx .. '].str' end) - keysets_defs:write('extern KeySetLink ' .. k.name .. '_table[];\n') + keysets_defs:write('extern KeySetLink ' .. k.name .. '_table[' .. (1 + #neworder) .. '];\n') local function typename(type) if type == 'HLGroupID' then @@ -596,7 +599,17 @@ for i = 1, #functions do output:write(');\n') end - if fn.return_type ~= 'void' then + local ret_type = real_type(fn.return_type) + if string.match(ret_type, '^KeyDict_') then + local table = string.sub(ret_type, 9) .. '_table' + output:write( + '\n ret = DICTIONARY_OBJ(api_keydict_to_dict(&rv, ' + .. table + .. ', ARRAY_SIZE(' + .. table + .. '), arena));' + ) + elseif ret_type ~= 'void' then output:write('\n ret = ' .. string.upper(real_type(fn.return_type)) .. '_OBJ(rv);') end output:write('\n\ncleanup:') @@ -653,11 +666,11 @@ local function include_headers(output_handle, headers_to_include) end end -local function write_shifted_output(_, str) +local function write_shifted_output(str, ...) str = str:gsub('\n ', '\n') str = str:gsub('^ ', '') str = str:gsub(' +$', '') - output:write(str) + output:write(string.format(str, ...)) end -- start building lua output @@ -688,9 +701,7 @@ local lua_c_functions = {} local function process_function(fn) local lua_c_function_name = ('nlua_api_%s'):format(fn.name) write_shifted_output( - output, - string.format( - [[ + [[ static int %s(lua_State *lstate) { @@ -701,11 +712,10 @@ local function process_function(fn) goto exit_0; } ]], - lua_c_function_name, - #fn.parameters, - #fn.parameters, - (#fn.parameters == 1) and '' or 's' - ) + lua_c_function_name, + #fn.parameters, + #fn.parameters, + (#fn.parameters == 1) and '' or 's' ) lua_c_functions[#lua_c_functions + 1] = { binding = lua_c_function_name, @@ -714,38 +724,29 @@ local function process_function(fn) if not fn.fast then write_shifted_output( - output, - string.format( - [[ + [[ if (!nlua_is_deferred_safe()) { return luaL_error(lstate, e_luv_api_disabled, "%s"); } ]], - fn.name - ) + fn.name ) end if fn.textlock then - write_shifted_output( - output, - [[ + write_shifted_output([[ if (text_locked()) { - api_set_error(&err, kErrorTypeException, "%s", get_text_locked_msg()); + api_set_error(&err, kErrorTypeException, "%%s", get_text_locked_msg()); goto exit_0; } - ]] - ) + ]]) elseif fn.textlock_allow_cmdwin then - write_shifted_output( - output, - [[ + write_shifted_output([[ if (textlock != 0 || expr_map_locked()) { - api_set_error(&err, kErrorTypeException, "%s", e_textlock); + api_set_error(&err, kErrorTypeException, "%%s", e_textlock); goto exit_0; } - ]] - ) + ]]) end local cparams = '' @@ -763,44 +764,37 @@ local function process_function(fn) local seterr = '' if string.match(param_type, '^KeyDict_') then write_shifted_output( - output, - string.format( - [[ - %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err);]], - param_type, - cparam, - cparam, - param_type - ) + [[ + %s %s = { 0 }; + nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err); + ]], + param_type, + cparam, + cparam, + param_type ) cparam = '&' .. cparam errshift = 1 -- free incomplete dict on error else write_shifted_output( - output, - string.format( - [[ - const %s %s = nlua_pop_%s(lstate, %s&err);]], - param[1], - cparam, - param_type, - extra - ) + [[ + const %s %s = nlua_pop_%s(lstate, %s&err);]], + param[1], + cparam, + param_type, + extra ) - seterr = [[ - err_param = "]] .. param[2] .. [[";]] + seterr = '\n err_param = "' .. param[2] .. '";' end - write_shifted_output( - output, - string.format([[ + write_shifted_output([[ if (ERROR_SET(&err)) {]] .. seterr .. [[ + goto exit_%u; } ]], #fn.parameters - j + errshift) - ) free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) cparams = cparam .. ', ' .. cparams end @@ -809,12 +803,7 @@ local function process_function(fn) end if fn.arena_return then cparams = cparams .. '&arena, ' - write_shifted_output( - output, - [[ - Arena arena = ARENA_EMPTY; - ]] - ) + write_shifted_output(' Arena arena = ARENA_EMPTY;\n') end if fn.has_lua_imp then @@ -831,27 +820,27 @@ local function process_function(fn) local rev_i = #free_code - i + 1 local code = free_code[rev_i] if i == 1 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then - free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) + free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) else - free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format(rev_i, code) + free_at_exit_code = free_at_exit_code .. ('\nexit_%u:\n %s'):format(rev_i, code) end end local err_throw_code = [[ - exit_0: - if (ERROR_SET(&err)) { - luaL_where(lstate, 1); - if (err_param) { - lua_pushstring(lstate, "Invalid '"); - lua_pushstring(lstate, err_param); - lua_pushstring(lstate, "': "); - } - lua_pushstring(lstate, err.msg); - api_clear_error(&err); - lua_concat(lstate, err_param ? 5 : 2); - return lua_error(lstate); +exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + if (err_param) { + lua_pushstring(lstate, "Invalid '"); + lua_pushstring(lstate, err_param); + lua_pushstring(lstate, "': "); } - ]] + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, err_param ? 5 : 2); + return lua_error(lstate); + } +]] local return_type if fn.return_type ~= 'void' then if fn.return_type:match('^ArrayOf') then @@ -861,86 +850,62 @@ local function process_function(fn) end local free_retval if fn.arena_return then - free_retval = 'arena_mem_free(arena_finish(&arena));' + free_retval = ' arena_mem_free(arena_finish(&arena));' else - free_retval = 'api_free_' .. return_type:lower() .. '(ret);' + free_retval = ' api_free_' .. return_type:lower() .. '(ret);' end - write_shifted_output( - output, - string.format( - [[ - const %s ret = %s(%s); - ]], - fn.return_type, - fn.name, - cparams - ) - ) + write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams) + local ret_type = real_type(fn.return_type) if fn.has_lua_imp then -- only push onto the Lua stack if we haven't already - write_shifted_output( - output, - string.format( - [[ + write_shifted_output(string.format( + [[ if (lua_gettop(lstate) == 0) { nlua_push_%s(lstate, ret, true); } ]], - return_type - ) + return_type + )) + elseif string.match(ret_type, '^KeyDict_') then + write_shifted_output( + ' nlua_push_keydict(lstate, &ret, %s_table);\n', + string.sub(ret_type, 9) ) else local special = (fn.since ~= nil and fn.since < 11) - write_shifted_output( - output, - string.format( - [[ - nlua_push_%s(lstate, ret, %s); - ]], - return_type, - tostring(special) - ) - ) + write_shifted_output(' nlua_push_%s(lstate, ret, %s);\n', return_type, tostring(special)) end write_shifted_output( - output, - string.format( - [[ + + [[ %s %s %s return 1; ]], - free_retval, - free_at_exit_code, - err_throw_code - ) + free_retval, + free_at_exit_code, + err_throw_code ) else write_shifted_output( - output, - string.format( - [[ + [[ %s(%s); %s %s return 0; ]], - fn.name, - cparams, - free_at_exit_code, - err_throw_code - ) + fn.name, + cparams, + free_at_exit_code, + err_throw_code ) end - write_shifted_output( - output, - [[ + write_shifted_output([[ } - ]] - ) + ]]) end for _, fn in ipairs(functions) do diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 91f2db3ac9..3cf5b0e94f 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1360,3 +1360,46 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_ lua_pop(L, 1); // [] } + +void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) +{ + lua_createtable(L, 0, 0); + for (size_t i = 0; table[i].str; i++) { + KeySetLink *field = &table[i]; + bool is_set = true; + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)value; + is_set = ks->is_set_ & (1ULL << field->opt_index); + } + + if (!is_set) { + continue; + } + + char *mem = ((char *)value + field->ptr_off); + + lua_pushstring(L, field->str); + if (field->type == kObjectTypeNil) { + nlua_push_Object(L, *(Object *)mem, false); + } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer + || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { + lua_pushinteger(L, *(Integer *)mem); + } else if (field->type == kObjectTypeFloat) { + lua_pushnumber(L, *(Float *)mem); + } else if (field->type == kObjectTypeBoolean) { + lua_pushboolean(L, *(Boolean *)mem); + } else if (field->type == kObjectTypeString) { + nlua_push_String(L, *(String *)mem, false); + } else if (field->type == kObjectTypeArray) { + nlua_push_Array(L, *(Array *)mem, false); + } else if (field->type == kObjectTypeDictionary) { + nlua_push_Dictionary(L, *(Dictionary *)mem, false); + } else if (field->type == kObjectTypeLuaRef) { + nlua_pushref(L, *(LuaRef *)mem); + } else { + abort(); + } + + lua_rawset(L, -3); + } +} diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index f79e829a7f..7ed840c2f9 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -665,7 +665,7 @@ static void ui_ext_tabline_update(void) win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff)))); + PUT_C(tab_info, "name", CSTR_TO_ARENA_OBJ(&arena, NameBuff)); ADD_C(tabs, DICTIONARY_OBJ(tab_info)); } @@ -686,7 +686,7 @@ static void ui_ext_tabline_update(void) PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string(NameBuff)))); + PUT_C(buffer_info, "name", CSTR_TO_ARENA_OBJ(&arena, NameBuff)); ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 397c1f6132..60ba9f3958 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1101,6 +1101,15 @@ describe('float window', function() local win = api.nvim_open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5, zindex=60}) local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20, zindex=60, hide=false} eq(expected, api.nvim_win_get_config(win)) + eq(true, exec_lua([[ + local expected, win = ... + local actual = vim.api.nvim_win_get_config(win) + for k,v in pairs(expected) do + if v ~= actual[k] then + error(k) + end + end + return true]], expected, win)) eq({external=false, focusable=true, hide=false, relative='',split="left",width=40,height=6}, api.nvim_win_get_config(0))