From ac086d8ce2bf24ef643aeea428a81f5ec331bec0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 18:05:54 +0300 Subject: mbyte: Refactor mb_unescape Does not alter its usages. --- src/nvim/mbyte.c | 85 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 41 deletions(-) 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; } -- cgit From e9e1668ca6520936eaa0958823325c11a72fa923 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 18:12:46 +0300 Subject: message: Refactor str2special_save and str2special Does not alter their usages as well. --- src/nvim/message.c | 120 +++++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 057ce75f79..2263f9c00d 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1281,90 +1281,94 @@ 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 , normally used for +/// lhs, but not rhs. +/// +/// @return [allocated] Converted string. +char *str2special_save(const char *const str, const bool replace_spaces) + 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)); + } 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 */ -) +/// 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 , normally used for +/// lhs, but not rhs. +/// +/// @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) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { - int c; - static char_u buf[7]; - char_u *str = *sp; - int modifiers = 0; - int special = FALSE; + static char buf[7]; - 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; + // 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 as ^@ or */ + if (c == KS_ZERO) { // display as ^@ or 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 and . - * Use 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 and . + if (special || char2cells(c) > 1 || (replace_spaces && c == ' ')) { + return (const char *)get_special_key_name(c, modifiers); + } buf[0] = c; buf[1] = NUL; return buf; -- cgit From 832c158a663c7acb03a47fbd1e380ab7f835a9cd Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 18:24:16 +0300 Subject: message: Refactor str2specialbuf Does not alter its usages. --- src/nvim/message.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 2263f9c00d..656f1adaf8 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1374,19 +1374,25 @@ const char *str2special(const char **const sp, const bool replace_spaces) 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); + 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; } /* -- cgit From 6140396d97d700ab6390b4ecfc4fd7da0ebdfd9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 18:29:42 +0300 Subject: *: Adjust usages of modified functions --- src/nvim/eval.c | 12 +++++++----- src/nvim/getchar.c | 10 ++++------ src/nvim/message.c | 2 +- src/nvim/option.c | 22 +++++++++++++--------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..c4baf3f653 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); + } } else { tv_dict_alloc_ret(rettv); @@ -12154,7 +12156,7 @@ 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, true); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12168,7 +12170,7 @@ 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("lhs"), lhs); tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 4e42042959..1d1af69c94 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1806,7 +1806,7 @@ static int vgetorpeek(int advance) * 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; @@ -4000,11 +4000,9 @@ int put_escstr(FILE *fd, char_u *strstart, int what) } 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); + // 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/message.c b/src/nvim/message.c index 656f1adaf8..feb2fb214e 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1269,7 +1269,7 @@ msg_outtrans_special ( string = ""; str++; } else { - string = (const char *)str2special((char_u **)&str, from); + string = str2special((const char **)&str, from); } const int len = vim_strsize((char_u *)string); // Highlight special keys diff --git a/src/nvim/option.c b/src/nvim/option.c index b48ffae85b..bc101ba703 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5175,9 +5175,12 @@ 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), 2) + == FAIL) { return FAIL; + } + } } else if (expand) { buf = xmalloc(MAXPATHL); home_replace(NULL, *valuep, buf, MAXPATHL, FALSE); @@ -6173,15 +6176,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); + } } } -- cgit From df040e55fbd3edc5a36462af927a7194d079d0b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:01:09 +0300 Subject: eval/typval: Add tv_dict_add_allocated_str() function --- src/nvim/eval/typval.c | 23 ++++++++++++++++++++++- test/unit/eval/typval_spec.lua | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) 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 @@ -1386,12 +1386,33 @@ int tv_dict_add_str(dict_T *const d, const char *const key, const size_t key_len, 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/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 5d543f914f..33d5bb28be 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -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() -- cgit From 85a6329a2b73924006eaccf8fd9ab59ec5c3ec46 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:02:15 +0300 Subject: eval: Use tv_dict_add_allocated_str() for mapblock_fill_dict --- src/nvim/eval.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c4baf3f653..32ba514d27 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12170,7 +12170,7 @@ 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"), lhs); + tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); @@ -12178,10 +12178,7 @@ void mapblock_fill_dict(dict_T *const dict, 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); } /* -- cgit From 936c070059f8c60085fa83fa7ea2ee4797d69f7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:15:14 +0300 Subject: eval: Make nvim_get_keymap output more robust --- src/nvim/eval.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 32ba514d27..8ea0969dd5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12156,7 +12156,8 @@ void mapblock_fill_dict(dict_T *const dict, bool compatible) FUNC_ATTR_NONNULL_ALL { - char *const lhs = str2special_save((const char *)mp->m_keys, true); + char *const lhs = str2special_save((const char *)mp->m_keys, + compatible ? true : false); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12170,8 +12171,13 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } + 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)); + } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); 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); -- cgit From 4b8bdd953e2b7928af80e8b97118d52f99f0d95a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:21:21 +0300 Subject: functests: Remove local_copy function --- test/functional/api/keymap_spec.lua | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 833e0d2f3c..7d6445f49c 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) @@ -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 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 foo bar') @@ -121,7 +116,7 @@ describe('get_keymap', function() command('nnoremap 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')) -- cgit From a1fee487ba5199ff672a87c5830732c224fa59eb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:28:44 +0300 Subject: functests: Add tests for new behaviour Apparently it is not working yet. --- test/functional/api/keymap_spec.lua | 72 +++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 7d6445f49c..b7858888c4 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -17,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')) @@ -238,4 +238,56 @@ describe('get_keymap', function() eq('', meths.get_keymap('n')[1]['lhs']) eq(':let g:maparg_test_var = 1', 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 \\ \\') + command('nnoremap \\ \\') + + command('set cpo+=B<') + command('xnoremap \\ \\') + command('xnoremap \\ \\') + + command('set cpo-=B<') + command('snoremap \\ \\') + command('snoremap \\ \\') + + command('set cpo-=B cpo+=<') + command('onoremap \\ \\') + command('onoremap \\ \\') + + for _, cmd in ipairs({ + 'set cpo-=B cpo+=<', + 'set cpo-=B<', + 'set cpo+=B<', + 'set cpo-=< cpo+=B', + }) do + command(cmd) + eq({cpomap('\\', '\\', 'n'), cpomap('\\', '\\', 'n')}, + meths.get_keymap('n')) + -- FIXME + -- eq({cpomap('\\', '\\', 'x'), cpomap('\\C-A>', '\\C-B>', 'x')}, + -- meths.get_keymap('x')) + -- eq({cpomap('C-C>', 'C-D>', 's'), cpomap('C-A>', 'C-B>', 's')}, + -- meths.get_keymap('x')) + -- eq({cpomap('C-C>', 'C-D>', 'o'), cpomap('C-A>', 'C-B>', 'o')}, + -- meths.get_keymap('x')) + end + end) end) -- cgit From 24f0056ca5cb392f1e1bf38d648a6037acf1f1ef Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:37:21 +0300 Subject: message: Add support for replacing `<` to str2special --- src/nvim/eval.c | 7 ++++--- src/nvim/message.c | 21 ++++++++++++++------- src/nvim/option.c | 3 ++- test/functional/api/keymap_spec.lua | 13 ++++++------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8ea0969dd5..662270e788 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12120,7 +12120,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) // Return a string. if (rhs != NULL) { rettv->vval.v_string = (char_u *)str2special_save( - (const char *)rhs, false); + (const char *)rhs, false, false); } } else { @@ -12157,7 +12157,7 @@ void mapblock_fill_dict(dict_T *const dict, FUNC_ATTR_NONNULL_ALL { char *const lhs = str2special_save((const char *)mp->m_keys, - compatible ? true : false); + compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12175,7 +12175,8 @@ void mapblock_fill_dict(dict_T *const dict, 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)); + 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); diff --git a/src/nvim/message.c b/src/nvim/message.c index feb2fb214e..8a9d8e1bc6 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1269,7 +1269,7 @@ msg_outtrans_special ( string = ""; str++; } else { - string = str2special((const char **)&str, from); + string = str2special((const char **)&str, from, false); } const int len = vim_strsize((char_u *)string); // Highlight special keys @@ -1286,11 +1286,13 @@ msg_outtrans_special ( /// Used for lhs or rhs of mappings. /// /// @param[in] str String to convert. -/// @param[in] replace_spaces Convert spaces into , normally used for +/// @param[in] replace_spaces Convert spaces into ``, normally used fo /// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into ``. /// /// @return [allocated] Converted string. -char *str2special_save(const char *const str, const bool replace_spaces) +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 { @@ -1299,7 +1301,7 @@ char *str2special_save(const char *const str, const bool replace_spaces) const char *p = str; while (*p != NUL) { - ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces)); + ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces, replace_lt)); } ga_append(&ga, NUL); return (char *)ga.ga_data; @@ -1310,11 +1312,13 @@ char *str2special_save(const char *const str, const bool replace_spaces) /// @param[in,out] sp String to convert. Is advanced to the next key code. /// @param[in] replace_spaces Convert spaces into , normally used for /// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into ``. /// /// @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 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]; @@ -1366,7 +1370,10 @@ const char *str2special(const char **const sp, const bool replace_spaces) } // Make unprintable characters in <> form, also and . - if (special || char2cells(c) > 1 || (replace_spaces && c == ' ')) { + if (special + || char2cells(c) > 1 + || (replace_spaces && c == ' ') + || (replace_lt && c == '<')) { return (const char *)get_special_key_name(c, modifiers); } buf[0] = c; @@ -1383,7 +1390,7 @@ void str2specialbuf(const char *sp, char *buf, size_t len) FUNC_ATTR_NONNULL_ALL { while (*sp) { - const char *s = str2special(&sp, false); + const char *s = str2special(&sp, false, false); const size_t s_len = strlen(s); if (s_len <= len) { break; diff --git a/src/nvim/option.c b/src/nvim/option.c index bc101ba703..7287db6eb8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5176,7 +5176,8 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e if (valuep == &p_pt) { s = *valuep; while (*s != NUL) { - if (put_escstr(fd, (char_u *)str2special((const char **)&s, false), 2) + if (put_escstr(fd, (char_u *)str2special((const char **)&s, false, + false), 2) == FAIL) { return FAIL; } diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index b7858888c4..fc3ab2d179 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -281,13 +281,12 @@ describe('get_keymap', function() command(cmd) eq({cpomap('\\', '\\', 'n'), cpomap('\\', '\\', 'n')}, meths.get_keymap('n')) - -- FIXME - -- eq({cpomap('\\', '\\', 'x'), cpomap('\\C-A>', '\\C-B>', 'x')}, - -- meths.get_keymap('x')) - -- eq({cpomap('C-C>', 'C-D>', 's'), cpomap('C-A>', 'C-B>', 's')}, - -- meths.get_keymap('x')) - -- eq({cpomap('C-C>', 'C-D>', 'o'), cpomap('C-A>', 'C-B>', 'o')}, - -- meths.get_keymap('x')) + eq({cpomap('\\', '\\', 'x'), cpomap('\\C-a>', '\\C-b>', 'x')}, + meths.get_keymap('x')) + eq({cpomap('C-c>', 'C-d>', 's'), cpomap('C-a>', 'C-b>', 's')}, + meths.get_keymap('s')) + eq({cpomap('C-c>', 'C-d>', 'o'), cpomap('C-a>', 'C-b>', 'o')}, + meths.get_keymap('o')) end end) end) -- cgit From 5fe5d712aae512c62083754bc364030848d67987 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:49:40 +0300 Subject: functests: Use more extensive testing Fixes #6937 --- test/functional/api/keymap_spec.lua | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index fc3ab2d179..f1c4d7bdf3 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -257,20 +257,20 @@ describe('get_keymap', function() end command('set cpo-=< cpo+=B') - command('nnoremap \\ \\') - command('nnoremap \\ \\') + command('nnoremap \\C-a>\\ \\C-b>\\') + command('nnoremap \\C-c>\\ \\C-d>\\') command('set cpo+=B<') - command('xnoremap \\ \\') - command('xnoremap \\ \\') + command('xnoremap \\C-a>\\ \\C-b>\\') + command('xnoremap \\C-c>\\ \\C-d>\\') command('set cpo-=B<') - command('snoremap \\ \\') - command('snoremap \\ \\') + command('snoremap \\C-a>\\ \\C-b>\\') + command('snoremap \\C-c>\\ \\C-d>\\') command('set cpo-=B cpo+=<') - command('onoremap \\ \\') - command('onoremap \\ \\') + command('onoremap \\C-a>\\ \\C-b>\\') + command('onoremap \\C-c>\\ \\C-d>\\') for _, cmd in ipairs({ 'set cpo-=B cpo+=<', @@ -279,13 +279,17 @@ describe('get_keymap', function() 'set cpo-=< cpo+=B', }) do command(cmd) - eq({cpomap('\\', '\\', 'n'), cpomap('\\', '\\', 'n')}, + eq({cpomap('\\C-c>\\', '\\C-d>\\', 'n'), + cpomap('\\C-a>\\', '\\C-b>\\', 'n')}, meths.get_keymap('n')) - eq({cpomap('\\', '\\', 'x'), cpomap('\\C-a>', '\\C-b>', 'x')}, + eq({cpomap('\\C-c>\\', '\\C-d>\\', 'x'), + cpomap('\\C-a>C-a>LT>C-a>\\', '\\C-b>C-b>LT>C-b>\\', 'x')}, meths.get_keymap('x')) - eq({cpomap('C-c>', 'C-d>', 's'), cpomap('C-a>', 'C-b>', 's')}, + eq({cpomap('C-c>C-c> ', 'C-d>C-d>', 's'), + cpomap('C-a>C-a> ', 'C-b>C-b>', 's')}, meths.get_keymap('s')) - eq({cpomap('C-c>', 'C-d>', 'o'), cpomap('C-a>', 'C-b>', 'o')}, + eq({cpomap('C-c>C-c> ', 'C-d>C-d>', 'o'), + cpomap('C-a>C-a>LT>C-a> ', 'C-b>C-b>LT>C-b>', 'o')}, meths.get_keymap('o')) end end) -- cgit From b97df0bdad63ba5da87982ea74ca171854e65dee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 19:52:04 +0300 Subject: getchar: Fix linter error --- src/nvim/getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 1d1af69c94..fc1b8ccfcb 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3999,7 +3999,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what) return OK; } - for (; *str != NUL; ++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); -- cgit From d5916a823a37a1c0fb1d3d2f52db7cad4107b924 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 20:08:00 +0300 Subject: functests: Test how spaces appear in get_keymap output --- test/functional/api/keymap_spec.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index f1c4d7bdf3..aa556b563d 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -293,4 +293,20 @@ describe('get_keymap', function() 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 \\| \\| ') + eq({space_table}, meths.get_keymap('n')) + end) end) -- cgit From 35898cff5d1d6dc60e0d7b87bfe106539453b031 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Jul 2017 20:18:15 +0300 Subject: unittests: Fix allocation ordering for tv_dict_add_str() --- test/unit/eval/typval_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 33d5bb28be..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, @@ -2007,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)}) -- cgit