diff options
-rw-r--r-- | src/nvim/eval.c | 26 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 23 | ||||
-rw-r--r-- | src/nvim/getchar.c | 12 | ||||
-rw-r--r-- | src/nvim/mbyte.c | 85 | ||||
-rw-r--r-- | src/nvim/message.c | 159 | ||||
-rw-r--r-- | src/nvim/option.c | 23 | ||||
-rw-r--r-- | test/functional/api/keymap_spec.lua | 114 | ||||
-rw-r--r-- | test/unit/eval/typval_spec.lua | 36 |
8 files changed, 313 insertions, 165 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..662270e788 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12117,9 +12117,11 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(keys_buf); if (!get_dict) { - /* Return a string. */ - if (rhs != NULL) - rettv->vval.v_string = str2special_save(rhs, FALSE); + // Return a string. + if (rhs != NULL) { + rettv->vval.v_string = (char_u *)str2special_save( + (const char *)rhs, false, false); + } } else { tv_dict_alloc_ret(rettv); @@ -12154,7 +12156,8 @@ void mapblock_fill_dict(dict_T *const dict, bool compatible) FUNC_ATTR_NONNULL_ALL { - char_u *lhs = str2special_save(mp->m_keys, true); + char *const lhs = str2special_save((const char *)mp->m_keys, + compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12168,18 +12171,21 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } - tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + if (compatible) { + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + } else { + tv_dict_add_allocated_str(dict, S_LEN("rhs"), + str2special_save((const char *)mp->m_str, false, + true)); + } + tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); - tv_dict_add_str(dict, S_LEN("mode"), mapmode); - - xfree(lhs); - xfree(mapmode); + tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } /* diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4521085519..c339a5cdd2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1387,11 +1387,32 @@ int tv_dict_add_str(dict_T *const d, const char *const val) FUNC_ATTR_NONNULL_ALL { + return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val)); +} + +/// Add a string entry to dictionary +/// +/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of +/// creating a new copy. +/// +/// @warning String will be freed even in case addition fails. +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_allocated_str(dict_T *const d, + const char *const key, const size_t key_len, + char *const val) + FUNC_ATTR_NONNULL_ALL +{ dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = (char_u *)xstrdup(val); + item->di_tv.vval.v_string = (char_u *)val; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 4e42042959..fc1b8ccfcb 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1806,7 +1806,7 @@ static int vgetorpeek(int advance) * <M-a> and then changing 'encoding'. Beware * that 0x80 is escaped. */ char_u *p1 = mp->m_keys; - char_u *p2 = mb_unescape(&p1); + char_u *p2 = (char_u *)mb_unescape((const char **)&p1); if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2)) mlen = 0; @@ -3999,12 +3999,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what) return OK; } - for (; *str != NUL; ++str) { - char_u *p; - - /* Check for a multi-byte character, which may contain escaped - * K_SPECIAL and CSI bytes */ - p = mb_unescape(&str); + for (; *str != NUL; str++) { + // Check for a multi-byte character, which may contain escaped + // K_SPECIAL and CSI bytes. + const char *p = mb_unescape((const char **)&str); if (p != NULL) { while (*p != NUL) if (fputc(*p++, fd) < 0) diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index d5907da2ed..5b00a4b9a8 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1739,52 +1739,55 @@ int mb_charlen_len(char_u *str, int len) return count; } -/* - * Try to un-escape a multi-byte character. - * Used for the "to" and "from" part of a mapping. - * Return the un-escaped string if it is a multi-byte character, and advance - * "pp" to just after the bytes that formed it. - * Return NULL if no multi-byte char was found. - */ -char_u * mb_unescape(char_u **pp) -{ - static char_u buf[6]; - int n; - int m = 0; - char_u *str = *pp; - - /* Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI - * KS_EXTRA KE_CSI to CSI. - * Maximum length of a utf-8 character is 4 bytes. */ - for (n = 0; str[n] != NUL && m < 4; ++n) { - if (str[n] == K_SPECIAL - && str[n + 1] == KS_SPECIAL - && str[n + 2] == KE_FILLER) { - buf[m++] = K_SPECIAL; - n += 2; - } else if ((str[n] == K_SPECIAL - ) - && str[n + 1] == KS_EXTRA - && str[n + 2] == (int)KE_CSI) { - buf[m++] = CSI; - n += 2; - } else if (str[n] == K_SPECIAL - ) - break; /* a special key can't be a multibyte char */ - else - buf[m++] = str[n]; - buf[m] = NUL; +/// Try to unescape a multibyte character +/// +/// Used for the rhs and lhs of the mappings. +/// +/// @param[in,out] pp String to unescape. Is advanced to just after the bytes +/// that form a multibyte character. +/// +/// @return Unescaped string if it is a multibyte character, NULL if no +/// multibyte character was found. Returns a static buffer, always one +/// and the same. +const char *mb_unescape(const char **const pp) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static char buf[6]; + size_t buf_idx = 0; + uint8_t *str = (uint8_t *)(*pp); + + // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI + // KS_EXTRA KE_CSI to CSI. + // Maximum length of a utf-8 character is 4 bytes. + for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) { + if (str[str_idx] == K_SPECIAL + && str[str_idx + 1] == KS_SPECIAL + && str[str_idx + 2] == KE_FILLER) { + buf[buf_idx++] = (char)K_SPECIAL; + str_idx += 2; + } else if ((str[str_idx] == K_SPECIAL) + && str[str_idx + 1] == KS_EXTRA + && str[str_idx + 2] == KE_CSI) { + buf[buf_idx++] = (char)CSI; + str_idx += 2; + } else if (str[str_idx] == K_SPECIAL) { + break; // A special key can't be a multibyte char. + } else { + buf[buf_idx++] = (char)str[str_idx]; + } + buf[buf_idx] = NUL; - /* Return a multi-byte character if it's found. An illegal sequence - * will result in a 1 here. */ - if ((*mb_ptr2len)(buf) > 1) { - *pp = str + n + 1; + // Return a multi-byte character if it's found. An illegal sequence + // will result in a 1 here. + if (utf_ptr2len((const char_u *)buf) > 1) { + *pp = (const char *)str + buf_idx + 1; return buf; } - /* Bail out quickly for ASCII. */ - if (buf[0] < 128) + // Bail out quickly for ASCII. + if ((uint8_t)buf[0] < 128) { break; + } } return NULL; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 057ce75f79..8a9d8e1bc6 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1269,7 +1269,7 @@ msg_outtrans_special ( string = "<Space>"; str++; } else { - string = (const char *)str2special((char_u **)&str, from); + string = str2special((const char **)&str, from, false); } const int len = vim_strsize((char_u *)string); // Highlight special keys @@ -1281,108 +1281,125 @@ msg_outtrans_special ( return retval; } -/* - * Return the lhs or rhs of a mapping, with the key codes turned into printable - * strings, in an allocated string. - */ -char_u * -str2special_save ( - char_u *str, - int is_lhs /* TRUE for lhs, FALSE for rhs */ -) +/// Convert string, replacing key codes with printables +/// +/// Used for lhs or rhs of mappings. +/// +/// @param[in] str String to convert. +/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used fo +/// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return [allocated] Converted string. +char *str2special_save(const char *const str, const bool replace_spaces, + const bool replace_lt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_RET { garray_T ga; - char_u *p = str; - ga_init(&ga, 1, 40); - while (*p != NUL) - ga_concat(&ga, str2special(&p, is_lhs)); + + const char *p = str; + while (*p != NUL) { + ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces, replace_lt)); + } ga_append(&ga, NUL); - return (char_u *)ga.ga_data; + return (char *)ga.ga_data; } -/* - * Return the printable string for the key codes at "*sp". - * Used for translating the lhs or rhs of a mapping to printable chars. - * Advances "sp" to the next code. - */ -char_u * -str2special ( - char_u **sp, - int from /* TRUE for lhs of mapping */ -) -{ - int c; - static char_u buf[7]; - char_u *str = *sp; - int modifiers = 0; - int special = FALSE; - - if (has_mbyte) { - char_u *p; - - /* Try to un-escape a multi-byte character. Return the un-escaped - * string if it is a multi-byte character. */ - p = mb_unescape(sp); - if (p != NULL) - return p; +/// Convert character, replacing key one key code with printable representation +/// +/// @param[in,out] sp String to convert. Is advanced to the next key code. +/// @param[in] replace_spaces Convert spaces into <Space>, normally used for +/// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return Converted key code, in a static buffer. Buffer is always one and the +/// same, so save converted string somewhere before running str2special +/// for the second time. +const char *str2special(const char **const sp, const bool replace_spaces, + const bool replace_lt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + static char buf[7]; + + // Try to un-escape a multi-byte character. Return the un-escaped + // string if it is a multi-byte character. + const char *const p = mb_unescape(sp); + if (p != NULL) { + return p; } - c = *str; + const char *str = *sp; + int c = (uint8_t)(*str); + int modifiers = 0; + bool special = false; if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { - if (str[1] == KS_MODIFIER) { - modifiers = str[2]; + if ((uint8_t)str[1] == KS_MODIFIER) { + modifiers = (uint8_t)str[2]; str += 3; - c = *str; + c = (uint8_t)(*str); } if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { - c = TO_SPECIAL(str[1], str[2]); + c = TO_SPECIAL((uint8_t)str[1], (uint8_t)str[2]); str += 2; - if (c == KS_ZERO) /* display <Nul> as ^@ or <Nul> */ + if (c == KS_ZERO) { // display <Nul> as ^@ or <Nul> c = NUL; + } + } + if (IS_SPECIAL(c) || modifiers) { // Special key. + special = true; } - if (IS_SPECIAL(c) || modifiers) /* special key */ - special = TRUE; } - if (has_mbyte && !IS_SPECIAL(c)) { - int len = (*mb_ptr2len)(str); + if (!IS_SPECIAL(c)) { + const int len = utf_ptr2len((const char_u *)str); - /* For multi-byte characters check for an illegal byte. */ - if (has_mbyte && MB_BYTE2LEN(*str) > len) { - transchar_nonprint(buf, c); + // Check for an illegal byte. + if (MB_BYTE2LEN((uint8_t)(*str)) > len) { + transchar_nonprint((char_u *)buf, c); *sp = str + 1; return buf; } - /* Since 'special' is TRUE the multi-byte character 'c' will be - * processed by get_special_key_name() */ - c = (*mb_ptr2char)(str); + // Since 'special' is TRUE the multi-byte character 'c' will be + // processed by get_special_key_name(). + c = utf_ptr2char((const char_u *)str); *sp = str + len; - } else + } else { *sp = str + 1; + } - /* Make unprintable characters in <> form, also <M-Space> and <Tab>. - * Use <Space> only for lhs of a mapping. */ - if (special || char2cells(c) > 1 || (from && c == ' ')) - return get_special_key_name(c, modifiers); + // Make unprintable characters in <> form, also <M-Space> and <Tab>. + if (special + || char2cells(c) > 1 + || (replace_spaces && c == ' ') + || (replace_lt && c == '<')) { + return (const char *)get_special_key_name(c, modifiers); + } buf[0] = c; buf[1] = NUL; return buf; } -/* - * Translate a key sequence into special key names. - */ -void str2specialbuf(char_u *sp, char_u *buf, int len) +/// Convert string, replacing key codes with printables +/// +/// @param[in] str String to convert. +/// @param[out] buf Buffer to save results to. +/// @param[in] len Buffer length. +void str2specialbuf(const char *sp, char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL { - char_u *s; - - *buf = NUL; while (*sp) { - s = str2special(&sp, FALSE); - if ((int)(STRLEN(s) + STRLEN(buf)) < len) - STRCAT(buf, s); + const char *s = str2special(&sp, false, false); + const size_t s_len = strlen(s); + if (s_len <= len) { + break; + } + memcpy(buf, s, s_len); + buf += s_len; + len -= s_len; } + *buf = NUL; } /* diff --git a/src/nvim/option.c b/src/nvim/option.c index b48ffae85b..7287db6eb8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5175,9 +5175,13 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e * CTRL-V or backslash */ if (valuep == &p_pt) { s = *valuep; - while (*s != NUL) - if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL) + while (*s != NUL) { + if (put_escstr(fd, (char_u *)str2special((const char **)&s, false, + false), 2) + == FAIL) { return FAIL; + } + } } else if (expand) { buf = xmalloc(MAXPATHL); home_replace(NULL, *valuep, buf, MAXPATHL, FALSE); @@ -6173,15 +6177,16 @@ option_value2string ( } } else { // P_STRING varp = *(char_u **)(varp); - if (varp == NULL) /* just in case */ + if (varp == NULL) { // Just in case. NameBuff[0] = NUL; - else if (opp->flags & P_EXPAND) - home_replace(NULL, varp, NameBuff, MAXPATHL, FALSE); - /* Translate 'pastetoggle' into special key names */ - else if ((char_u **)opp->var == &p_pt) - str2specialbuf(p_pt, NameBuff, MAXPATHL); - else + } else if (opp->flags & P_EXPAND) { + home_replace(NULL, varp, NameBuff, MAXPATHL, false); + // Translate 'pastetoggle' into special key names. + } else if ((char_u **)opp->var == &p_pt) { + str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL); + } else { STRLCPY(NameBuff, varp, MAXPATHL); + } } } diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 833e0d2f3c..aa556b563d 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -1,5 +1,6 @@ - local helpers = require('test.functional.helpers')(after_each) +local global_helpers = require('test.helpers') + local clear = helpers.clear local command = helpers.command local curbufmeths = helpers.curbufmeths @@ -8,13 +9,7 @@ local funcs = helpers.funcs local meths = helpers.meths local source = helpers.source -local function local_copy(t) - local copy = {} - for k,v in pairs(t) do - copy[k] = v - end - return copy -end +local shallowcopy = global_helpers.shallowcopy describe('get_keymap', function() before_each(clear) @@ -22,16 +17,16 @@ describe('get_keymap', function() -- Basic mapping and table to be used to describe results local foo_bar_string = 'nnoremap foo bar' local foo_bar_map_table = { - lhs='foo', - silent=0, - rhs='bar', - expr=0, - sid=0, - buffer=0, - nowait=0, - mode='n', - noremap=1, - } + lhs='foo', + silent=0, + rhs='bar', + expr=0, + sid=0, + buffer=0, + nowait=0, + mode='n', + noremap=1, + } it('returns empty list when no map', function() eq({}, meths.get_keymap('n')) @@ -50,7 +45,7 @@ describe('get_keymap', function() -- Add another mapping command('nnoremap foo_longer bar_longer') - local foolong_bar_map_table = local_copy(foo_bar_map_table) + local foolong_bar_map_table = shallowcopy(foo_bar_map_table) foolong_bar_map_table['lhs'] = 'foo_longer' foolong_bar_map_table['rhs'] = 'bar_longer' @@ -72,7 +67,7 @@ describe('get_keymap', function() command('inoremap foo bar') -- The table will be the same except for the mode - local insert_table = local_copy(foo_bar_map_table) + local insert_table = shallowcopy(foo_bar_map_table) insert_table['mode'] = 'i' eq({insert_table}, meths.get_keymap('i')) @@ -81,11 +76,11 @@ describe('get_keymap', function() it('considers scope', function() -- change the map slightly command('nnoremap foo_longer bar_longer') - local foolong_bar_map_table = local_copy(foo_bar_map_table) + local foolong_bar_map_table = shallowcopy(foo_bar_map_table) foolong_bar_map_table['lhs'] = 'foo_longer' foolong_bar_map_table['rhs'] = 'bar_longer' - local buffer_table = local_copy(foo_bar_map_table) + local buffer_table = shallowcopy(foo_bar_map_table) buffer_table['buffer'] = 1 command('nnoremap <buffer> foo bar') @@ -98,7 +93,7 @@ describe('get_keymap', function() it('considers scope for overlapping maps', function() command('nnoremap foo bar') - local buffer_table = local_copy(foo_bar_map_table) + local buffer_table = shallowcopy(foo_bar_map_table) buffer_table['buffer'] = 1 command('nnoremap <buffer> foo bar') @@ -121,7 +116,7 @@ describe('get_keymap', function() command('nnoremap <buffer> foo bar') -- Final buffer will have buffer mappings - local buffer_table = local_copy(foo_bar_map_table) + local buffer_table = shallowcopy(foo_bar_map_table) buffer_table['buffer'] = final_buffer eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n')) eq({buffer_table}, meths.buf_get_keymap(0, 'n')) @@ -243,4 +238,75 @@ describe('get_keymap', function() eq('<F12>', meths.get_keymap('n')[1]['lhs']) eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs']) end) + + it('works correctly despite various &cpo settings', function() + local cpo_table = { + silent=0, + expr=0, + sid=0, + buffer=0, + nowait=0, + noremap=1, + } + local function cpomap(lhs, rhs, mode) + local ret = shallowcopy(cpo_table) + ret.lhs = lhs + ret.rhs = rhs + ret.mode = mode + return ret + end + + command('set cpo-=< cpo+=B') + command('nnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\') + command('nnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\') + + command('set cpo+=B<') + command('xnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\') + command('xnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\') + + command('set cpo-=B<') + command('snoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\') + command('snoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\') + + command('set cpo-=B cpo+=<') + command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\') + command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\') + + for _, cmd in ipairs({ + 'set cpo-=B cpo+=<', + 'set cpo-=B<', + 'set cpo+=B<', + 'set cpo-=< cpo+=B', + }) do + command(cmd) + eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'), + cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')}, + meths.get_keymap('n')) + eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'), + cpomap('\\<lt>C-a><lt>C-a><lt>LT>C-a>\\', '\\<lt>C-b><lt>C-b><lt>LT>C-b>\\', 'x')}, + meths.get_keymap('x')) + eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'), + cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')}, + meths.get_keymap('s')) + eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'), + cpomap('<lt>C-a><lt>C-a><lt>LT>C-a> ', '<lt>C-b><lt>C-b><lt>LT>C-b>', 'o')}, + meths.get_keymap('o')) + end + end) + + it('always uses space for space and bar for bar', function() + local space_table = { + lhs='| |', + rhs='| |', + mode='n', + silent=0, + expr=0, + sid=0, + buffer=0, + nowait=0, + noremap=1, + } + command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>') + eq({space_table}, meths.get_keymap('n')) + end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 5d543f914f..bec74f05fc 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1948,8 +1948,8 @@ describe('typval.c', function() eq(OK, lib.tv_dict_add_str(d, 'testt', 3, 'TEST')) local dis = dict_items(d) alloc_log:check({ + a.str(dis.tes.di_tv.vval.v_string, 'TEST'), a.di(dis.tes, 'tes'), - a.str(dis.tes.di_tv.vval.v_string, 'TEST') }) eq({test=10, tes='TEST'}, dct2tbl(d)) eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, @@ -1963,6 +1963,38 @@ describe('typval.c', function() alloc_log:check({}) end) end) + describe('allocated_str()', function() + itp('works', function() + local d = dict({test=10}) + eq({test=10}, dct2tbl(d)) + alloc_log:clear() + local s1 = lib.xstrdup('TEST') + local s2 = lib.xstrdup('TEST') + local s3 = lib.xstrdup('TEST') + alloc_log:check({ + a.str(s1, 'TEST'), + a.str(s2, 'TEST'), + a.str(s3, 'TEST'), + }) + eq(OK, lib.tv_dict_add_allocated_str(d, 'testt', 3, s1)) + local dis = dict_items(d) + alloc_log:check({ + a.di(dis.tes, 'tes'), + }) + eq({test=10, tes='TEST'}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s2) end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_allocated_str(d, 'testt', 3, s3) end, + nil)) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.freed(s3), + }) + end) + end) end) describe('clear()', function() itp('works', function() @@ -1975,7 +2007,7 @@ describe('typval.c', function() local dis = dict_items(d) local di = dis.TES local di_s = di.di_tv.vval.v_string - alloc_log:check({a.di(di), a.str(di_s)}) + alloc_log:check({a.str(di_s), a.di(di)}) eq({TES='tEsT'}, dct2tbl(d)) lib.tv_dict_clear(d) alloc_log:check({a.freed(di_s), a.freed(di)}) |