summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt12
-rw-r--r--src/evalfunc.c21
-rw-r--r--src/map.c6
-rw-r--r--src/menu.c2
-rw-r--r--src/message.c20
-rw-r--r--src/option.c5
-rw-r--r--src/proto/message.pro4
-rw-r--r--src/testdir/test_functions.vim26
-rw-r--r--src/version.c2
9 files changed, 83 insertions, 15 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 3448ed585..fb8b11601 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -325,6 +325,8 @@ js_encode({expr}) String encode JS style JSON
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 = "\<C-Home>"
+ :echo keytrans(xx)
+< <C-Home>
+
+ Can also be used as a |method|: >
+ "\<C-Home>"->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
index bc0e23e9c..cd3156294 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -89,6 +89,7 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv);
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 *rettv)
}
/*
+ * "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
index 140cbc84f..d063e053a 100644
--- 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 *rettv, int exact)
if (*rhs == NUL)
rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
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, typval_T *rettv)
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
index 014d0518d..6a93316ab 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -2890,7 +2890,7 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict)
*menu->strings[bit] == NUL
? (char_u *)"<Nop>"
: (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
index b68c08974..57197c1a8 100644
--- 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 "<Space>".
+ // used for the lhs of mapping and keytrans().
+ int replace_lt) // TRUE to replace "<" with "<lt>".
{
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 "<Space>".
+ // used for the lhs of mapping and keytrans().
+ int replace_lt) // TRUE to replace "<" with "<lt>".
{
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 <M-Space>.
- // Use <Space> 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, int len)
*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
index 5ce52184e..37c81aaa4 100644
--- 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
index 3f8a8fe2e..6657a08ec 100644
--- a/src/proto/message.pro
+++ b/src/proto/message.pro
@@ -37,8 +37,8 @@ char_u *msg_outtrans_one(char_u *p, int attr);
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
index cad1d4b43..4c71e227a 100644
--- 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('<Space>', keytrans(' '))
+ call assert_equal('<lt>', keytrans('<'))
+ call assert_equal('<lt>Tab>', keytrans('<Tab>'))
+ call assert_equal('<Tab>', keytrans("\<Tab>"))
+ call assert_equal('<C-V>', keytrans("\<C-V>"))
+ call assert_equal('<BS>', keytrans("\<BS>"))
+ call assert_equal('<Home>', keytrans("\<Home>"))
+ call assert_equal('<C-Home>', keytrans("\<C-Home>"))
+ call assert_equal('<M-Home>', keytrans("\<M-Home>"))
+ call assert_equal('<C-Space>', keytrans("\<C-Space>"))
+ call assert_equal('<M-Space>', keytrans("\<*M-Space>"))
+ call assert_equal('<M-x>', "\<*M-x>"->keytrans())
+ call assert_equal('<C-I>', "\<*C-I>"->keytrans())
+ call assert_equal('<S-3>', "\<*S-3>"->keytrans())
+ call assert_equal('π', 'π'->keytrans())
+ call assert_equal('<M-π>', "\<M-π>"->keytrans())
+ call assert_equal('ě', 'ě'->keytrans())
+ call assert_equal('<M-ě>', "\<M-ě>"->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
index 2764ad9ad..3633b198c 100644
--- 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,