# HG changeset patch # User Bram Moolenaar # Date 1662986704 -7200 # Node ID b6b803ed4a53a450f88a1ee6a93d822b5fc3f4e4 # Parent 51f58c2d23fa0afbaa455da02223e932d94151c4 patch 9.0.0449: there is no easy way to translate a key code into a string Commit: https://github.com/vim/vim/commit/cdc839353f68ca43db6446e1b727fc7ba657b738 Author: zeertzjq Date: Mon Sep 12 13:38:41 2022 +0100 patch 9.0.0449: there is no easy way to translate a key code into a string Problem: There is no easy way to translate a string with a key code into a readable string. Solution: Add the keytrans() function. (closes #11114) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -325,6 +325,8 @@ js_encode({expr}) String encode JS styl json_decode({string}) any decode JSON json_encode({expr}) String encode JSON keys({dict}) List keys in {dict} +keytrans({string}) String translate internal keycodes to a form + that can be used by |:map| len({expr}) Number the length of {expr} libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg} libcallnr({lib}, {func}, {arg}) Number idem, but return a Number @@ -5205,6 +5207,16 @@ keys({dict}) *keys()* Can also be used as a |method|: > mydict->keys() +keytrans({string}) *keytrans()* + Turn the internal byte representation of keys into a form that + can be used for |:map|. E.g. > + :let xx = "\" + :echo keytrans(xx) +< + + Can also be used as a |method|: > + "\"->keytrans() + < *len()* *E701* len({expr}) The result is a Number, which is the length of the argument. When {expr} is a String or a Number the length in bytes is diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -89,6 +89,7 @@ static void f_inputsecret(typval_T *argv static void f_interrupt(typval_T *argvars, typval_T *rettv); static void f_invert(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); +static void f_keytrans(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); static void f_libcallnr(typval_T *argvars, typval_T *rettv); @@ -2058,6 +2059,8 @@ static funcentry_T global_functions[] = ret_string, f_json_encode}, {"keys", 1, 1, FEARG_1, arg1_dict_any, ret_list_string, f_keys}, + {"keytrans", 1, 1, FEARG_1, arg1_string, + ret_string, f_keytrans}, {"last_buffer_nr", 0, 0, 0, NULL, // obsolete ret_number, f_last_buffer_nr}, {"len", 1, 1, FEARG_1, arg1_len, @@ -7136,6 +7139,24 @@ f_islocked(typval_T *argvars, typval_T * } /* + * "keytrans()" function + */ + static void +f_keytrans(typval_T *argvars, typval_T *rettv) +{ + char_u *escaped; + + rettv->v_type = VAR_STRING; + if (check_for_string_arg(argvars, 0) == FAIL + || argvars[0].vval.v_string == NULL) + return; + // Need to escape K_SPECIAL and CSI for mb_unescape(). + escaped = vim_strsave_escape_csi(argvars[0].vval.v_string); + rettv->vval.v_string = str2special_save(escaped, TRUE, TRUE); + vim_free(escaped); +} + +/* * "last_buffer_nr()" function. */ static void diff --git a/src/map.c b/src/map.c --- a/src/map.c +++ b/src/map.c @@ -2317,7 +2317,7 @@ mapblock2dict( int buffer_local, // false if not buffer local mapping int abbr) // true if abbreviation { - char_u *lhs = str2special_save(mp->m_keys, TRUE); + char_u *lhs = str2special_save(mp->m_keys, TRUE, FALSE); char_u *mapmode = map_mode_to_chars(mp->m_mode); dict_add_string(dict, "lhs", lhs); @@ -2409,7 +2409,7 @@ get_maparg(typval_T *argvars, typval_T * if (*rhs == NUL) rettv->vval.v_string = vim_strsave((char_u *)""); else - rettv->vval.v_string = str2special_save(rhs, FALSE); + rettv->vval.v_string = str2special_save(rhs, FALSE, FALSE); } } @@ -2478,7 +2478,7 @@ f_maplist(typval_T *argvars UNUSED, typv keys_buf = NULL; did_simplify = FALSE; - lhs = str2special_save(mp->m_keys, TRUE); + lhs = str2special_save(mp->m_keys, TRUE, FALSE); (void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify); vim_free(lhs); diff --git a/src/menu.c b/src/menu.c --- a/src/menu.c +++ b/src/menu.c @@ -2890,7 +2890,7 @@ menuitem_getinfo(char_u *menu_name, vimm *menu->strings[bit] == NUL ? (char_u *)"" : (tofree = str2special_save( - menu->strings[bit], FALSE))); + menu->strings[bit], FALSE, FALSE))); vim_free(tofree); } if (status == OK) diff --git a/src/message.c b/src/message.c --- a/src/message.c +++ b/src/message.c @@ -1759,7 +1759,7 @@ msg_outtrans_special( ++str; } else - text = (char *)str2special(&str, from); + text = (char *)str2special(&str, from, FALSE); if (text[0] != NUL && text[1] == NUL) // single-byte character or illegal byte text = (char *)transchar_byte((char_u)text[0]); @@ -1782,14 +1782,16 @@ msg_outtrans_special( char_u * str2special_save( char_u *str, - int is_lhs) // TRUE for lhs, FALSE for rhs + int replace_spaces, // TRUE to replace " " with "". + // used for the lhs of mapping and keytrans(). + int replace_lt) // TRUE to replace "<" with "". { garray_T ga; char_u *p = str; ga_init2(&ga, 1, 40); while (*p != NUL) - ga_concat(&ga, str2special(&p, is_lhs)); + ga_concat(&ga, str2special(&p, replace_spaces, replace_lt)); ga_append(&ga, NUL); return (char_u *)ga.ga_data; } @@ -1804,7 +1806,9 @@ str2special_save( char_u * str2special( char_u **sp, - int from) // TRUE for lhs of mapping + int replace_spaces, // TRUE to replace " " with "". + // used for the lhs of mapping and keytrans(). + int replace_lt) // TRUE to replace "<" with "". { int c; static char_u buf[7]; @@ -1861,8 +1865,10 @@ str2special( *sp = str + (*str == NUL ? 0 : 1); // Make special keys and C0 control characters in <> form, also . - // Use only for lhs of a mapping. - if (special || c < ' ' || (from && c == ' ')) + if (special + || c < ' ' + || (replace_spaces && c == ' ') + || (replace_lt && c == '<')) return get_special_key_name(c, modifiers); buf[0] = c; buf[1] = NUL; @@ -1880,7 +1886,7 @@ str2specialbuf(char_u *sp, char_u *buf, *buf = NUL; while (*sp) { - s = str2special(&sp, FALSE); + s = str2special(&sp, FALSE, FALSE); if ((int)(STRLEN(s) + STRLEN(buf)) < len) STRCAT(buf, s); } diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -3994,7 +3994,8 @@ get_option_value( if (stringval != NULL) { if ((char_u **)varp == &p_pt) // 'pastetoggle' - *stringval = str2special_save(*(char_u **)(varp), FALSE); + *stringval = str2special_save(*(char_u **)(varp), FALSE, + FALSE); #ifdef FEAT_CRYPT // never return the value of the crypt key else if ((char_u **)varp == &curbuf->b_p_key @@ -4879,7 +4880,7 @@ put_setstring( { s = *valuep; while (*s != NUL) - if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL) + if (put_escstr(fd, str2special(&s, FALSE, FALSE), 2) == FAIL) return FAIL; } // expand the option value, replace $HOME by ~ diff --git a/src/proto/message.pro b/src/proto/message.pro --- a/src/proto/message.pro +++ b/src/proto/message.pro @@ -37,8 +37,8 @@ char_u *msg_outtrans_one(char_u *p, int int msg_outtrans_len_attr(char_u *msgstr, int len, int attr); void msg_make(char_u *arg); int msg_outtrans_special(char_u *strstart, int from, int maxlen); -char_u *str2special_save(char_u *str, int is_lhs); -char_u *str2special(char_u **sp, int from); +char_u *str2special_save(char_u *str, int replace_spaces, int replace_lt); +char_u *str2special(char_u **sp, int replace_spaces, int replace_lt); void str2specialbuf(char_u *sp, char_u *buf, int len); void msg_prt_line(char_u *s, int list); void msg_puts(char *s); diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -2764,6 +2764,32 @@ func Test_eval() call assert_fails("call eval('5 a')", 'E488:') endfunc +" Test for the keytrans() function +func Test_keytrans() + call assert_equal('', keytrans(' ')) + call assert_equal('', keytrans('<')) + call assert_equal('Tab>', keytrans('')) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\")) + call assert_equal('', keytrans("\<*M-Space>")) + call assert_equal('', "\<*M-x>"->keytrans()) + call assert_equal('', "\<*C-I>"->keytrans()) + call assert_equal('', "\<*S-3>"->keytrans()) + call assert_equal('π', 'π'->keytrans()) + call assert_equal('', "\"->keytrans()) + call assert_equal('ě', 'ě'->keytrans()) + call assert_equal('', "\"->keytrans()) + call assert_equal('', ''->keytrans()) + call assert_equal('', test_null_string()->keytrans()) + call assert_fails('call keytrans(1)', 'E1174:') + call assert_fails('call keytrans()', 'E119:') +endfunc + " Test for the nr2char() function func Test_nr2char() set encoding=latin1 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 449, +/**/ 448, /**/ 447,