vim-patch:8.1.2145: cannot map <C-H> when modifyOtherKeys is enabled

Problem:    Cannot map <C-H> when modifyOtherKeys is enabled.
Solution:   Add the <C-H> mapping twice, both with modifier and as 0x08.  Use
            only the first one when modifyOtherKeys has been detected.
459fd785e4

Add REPTERM_NO_SPECIAL instead of REPTERM_SPECIAL because the meaning of
"special" is different between Vim and Nvim.
Omit seenModifyOtherKeys as Nvim supports attaching multiple UIs.
Omit tests as they send terminal codes.
Keep the behavior of API functions.
This commit is contained in:
zeertzjq
2022-03-31 15:47:53 +08:00
parent 188537efb3
commit dde4f09f51
16 changed files with 410 additions and 345 deletions

View File

@@ -632,7 +632,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod
} else { } else {
parsed_args.desc = NULL; parsed_args.desc = NULL;
} }
if (parsed_args.lhs_len > MAXMAPLEN) { if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) {
api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data); api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
goto fail_and_free; goto fail_and_free;
} }
@@ -1128,6 +1128,9 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
for (const mapblock_T *current_maphash = get_maphash(i, buf); for (const mapblock_T *current_maphash = get_maphash(i, buf);
current_maphash; current_maphash;
current_maphash = current_maphash->m_next) { current_maphash = current_maphash->m_next) {
if (current_maphash->m_simplified) {
continue;
}
// Check for correct mode // Check for correct mode
if (int_mode & current_maphash->m_mode) { if (int_mode & current_maphash->m_mode) {
mapblock_fill_dict(dict, current_maphash, buffer_value, false); mapblock_fill_dict(dict, current_maphash, buffer_value, false);

View File

@@ -403,9 +403,19 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
return (String) { .data = NULL, .size = 0 }; return (String) { .data = NULL, .size = 0 };
} }
int flags = 0;
if (from_part) {
flags |= REPTERM_FROM_PART;
}
if (do_lt) {
flags |= REPTERM_DO_LT;
}
if (!special) {
flags |= REPTERM_NO_SPECIAL;
}
char *ptr = NULL; char *ptr = NULL;
replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, flags, NULL, CPO_TO_CPO_FLAGS);
from_part, do_lt, special, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr); return cstr_as_string(ptr);
} }

View File

@@ -356,6 +356,8 @@ struct mapblock {
LuaRef m_luaref; // lua function reference as rhs LuaRef m_luaref; // lua function reference as rhs
int m_keylen; // strlen(m_keys) int m_keylen; // strlen(m_keys)
int m_mode; // valid mode int m_mode; // valid mode
int m_simplified; // m_keys was simplified, do no use this map
// if keys are typed
int m_noremap; // if non-zero no re-mapping for m_str int m_noremap; // if non-zero no re-mapping for m_str
char m_silent; // <silent> used, don't echo commands char m_silent; // <silent> used, don't echo commands
char m_nowait; // <nowait> used char m_nowait; // <nowait> used

View File

@@ -4965,7 +4965,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// Special key, e.g.: "\<C-W>" // Special key, e.g.: "\<C-W>"
case '<': case '<':
extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true); extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true, true, NULL);
if (extra != 0) { if (extra != 0) {
name += extra; name += extra;
if (name >= rettv->vval.v_string + len) { if (name >= rettv->vval.v_string + len) {

View File

@@ -5684,7 +5684,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
mode = get_map_mode((char_u **)&which, 0); mode = get_map_mode((char_u **)&which, 0);
keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true, keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
xfree(keys_buf); xfree(keys_buf);

View File

@@ -5180,8 +5180,7 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo
char_u *rep_buf = NULL; char_u *rep_buf = NULL;
garray_T *gap; garray_T *gap;
replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true, replace_termcodes(rep, STRLEN(rep), &rep_buf, 0, NULL, CPO_TO_CPO_FLAGS);
CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) { if (rep_buf == NULL) {
// Can't replace termcodes - try using the string as is // Can't replace termcodes - try using the string as is
rep_buf = vim_strsave(rep); rep_buf = vim_strsave(rep);

View File

@@ -2711,11 +2711,12 @@ int fix_input_buffer(char_u *buf, int len)
/// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] orig_rhs_len `strlen` of orig_rhs.
/// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[in] cpo_flags See param docs for @ref replace_termcodes.
/// @param[out] mapargs MapArguments struct holding the replaced strings. /// @param[out] mapargs MapArguments struct holding the replaced strings.
void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs, void set_maparg_lhs_rhs(const char_u *const orig_lhs, const size_t orig_lhs_len,
const size_t orig_rhs_len, LuaRef rhs_lua, int cpo_flags, const char_u *const orig_rhs, const size_t orig_rhs_len,
MapArguments *mapargs) const LuaRef rhs_lua, const int cpo_flags, MapArguments *const mapargs)
{ {
char_u *lhs_buf = NULL; char_u *lhs_buf = NULL;
char_u *alt_lhs_buf = NULL;
char_u *rhs_buf = NULL; char_u *rhs_buf = NULL;
// If mapping has been given as ^V<C_UP> say, then replace the term codes // If mapping has been given as ^V<C_UP> say, then replace the term codes
@@ -2725,10 +2726,22 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const
// replace_termcodes() may move the result to allocated memory, which // replace_termcodes() may move the result to allocated memory, which
// needs to be freed later (*lhs_buf and *rhs_buf). // needs to be freed later (*lhs_buf and *rhs_buf).
// replace_termcodes() also removes CTRL-Vs and sometimes backslashes. // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf, // If something like <C-H> is simplified to 0x08 then mark it as simplified.
true, true, true, cpo_flags); bool did_simplify = false;
const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf, flags, &did_simplify,
cpo_flags);
mapargs->lhs_len = STRLEN(replaced); mapargs->lhs_len = STRLEN(replaced);
STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs)); STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs));
if (did_simplify) {
replaced = replace_termcodes(orig_lhs, orig_lhs_len, &alt_lhs_buf, flags | REPTERM_NO_SIMPLIFY,
NULL, cpo_flags);
mapargs->alt_lhs_len = STRLEN(replaced);
STRLCPY(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs));
} else {
mapargs->alt_lhs_len = 0;
}
mapargs->rhs_lua = rhs_lua; mapargs->rhs_lua = rhs_lua;
if (rhs_lua == LUA_NOREF) { if (rhs_lua == LUA_NOREF) {
@@ -2741,8 +2754,8 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const
mapargs->rhs_len = 0; mapargs->rhs_len = 0;
mapargs->rhs_is_noop = true; mapargs->rhs_is_noop = true;
} else { } else {
replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL,
false, true, true, cpo_flags); cpo_flags);
mapargs->rhs_len = STRLEN(replaced); mapargs->rhs_len = STRLEN(replaced);
mapargs->rhs_is_noop = false; mapargs->rhs_is_noop = false;
mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u)); mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
@@ -2760,6 +2773,7 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const
} }
xfree(lhs_buf); xfree(lhs_buf);
xfree(alt_lhs_buf);
xfree(rhs_buf); xfree(rhs_buf);
} }
@@ -2894,15 +2908,9 @@ int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf) int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
{ {
mapblock_T *mp, **mpp; mapblock_T *mp, **mpp;
char_u *p; const char_u *p;
int n; int n;
int len = 0; // init for GCC
int did_it = false;
int did_local = false;
int round;
int retval = 0; int retval = 0;
int hash;
int new_hash;
mapblock_T **abbr_table; mapblock_T **abbr_table;
mapblock_T **map_table; mapblock_T **map_table;
int noremap; int noremap;
@@ -2929,8 +2937,9 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
validate_maphash(); validate_maphash();
bool has_lhs = (args->lhs[0] != NUL); const bool has_lhs = (args->lhs[0] != NUL);
bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop; const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
const bool do_print = !has_lhs || (maptype != 1 && !has_rhs);
// check for :unmap without argument // check for :unmap without argument
if (maptype == 1 && !has_lhs) { if (maptype == 1 && !has_lhs) {
@@ -2938,300 +2947,325 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
goto theend; goto theend;
} }
char_u *lhs = (char_u *)&args->lhs; const char_u *lhs = (char_u *)&args->lhs;
char_u *rhs = args->rhs; const char_u *const rhs = args->rhs;
char_u *orig_rhs = args->orig_rhs; const char_u *const orig_rhs = args->orig_rhs;
const bool did_simplify = args->alt_lhs_len != 0;
// check arguments and translate function keys // The following is done twice if we have two versions of keys
if (has_lhs) { for (int keyround = 1; keyround <= 2; keyround++) {
len = (int)args->lhs_len; bool did_it = false;
if (len > MAXMAPLEN) { bool did_local = false;
retval = 1; int len = (int)args->lhs_len;
goto theend;
if (keyround == 2) {
if (!did_simplify) {
break;
}
lhs = (char_u *)&args->alt_lhs;
len = (int)args->alt_lhs_len;
} else if (did_simplify && do_print) {
// when printing always use the not-simplified map
lhs = (char_u *)&args->alt_lhs;
len = (int)args->alt_lhs_len;
} }
if (is_abbrev && maptype != 1) { // check arguments and translate function keys
// if (has_lhs) {
// If an abbreviation ends in a keyword character, the if (len > MAXMAPLEN) {
// rest must be all keyword-char or all non-keyword-char.
// Otherwise we won't be able to find the start of it in a
// vi-compatible way.
//
int same = -1;
const int first = vim_iswordp(lhs);
int last = first;
p = lhs + utfc_ptr2len(lhs);
n = 1;
while (p < lhs + len) {
n++; // nr of (multi-byte) chars
last = vim_iswordp(p); // type of last char
if (same == -1 && last != first) {
same = n - 1; // count of same char type
}
p += utfc_ptr2len(p);
}
if (last && n > 2 && same >= 0 && same < n - 1) {
retval = 1; retval = 1;
goto theend; goto theend;
} }
// An abbreviation cannot contain white space.
for (n = 0; n < len; n++) { if (is_abbrev && maptype != 1) {
if (ascii_iswhite(lhs[n])) { //
// If an abbreviation ends in a keyword character, the
// rest must be all keyword-char or all non-keyword-char.
// Otherwise we won't be able to find the start of it in a
// vi-compatible way.
//
int same = -1;
const int first = vim_iswordp(lhs);
int last = first;
p = lhs + utfc_ptr2len(lhs);
n = 1;
while (p < lhs + len) {
n++; // nr of (multi-byte) chars
last = vim_iswordp(p); // type of last char
if (same == -1 && last != first) {
same = n - 1; // count of same char type
}
p += utfc_ptr2len(p);
}
if (last && n > 2 && same >= 0 && same < n - 1) {
retval = 1; retval = 1;
goto theend; goto theend;
} }
} // for // An abbreviation cannot contain white space.
} for (n = 0; n < len; n++) {
} if (ascii_iswhite(lhs[n])) {
retval = 1;
if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation, goto theend;
no_abbr = false; // reset flag that indicates there are no abbreviations }
} } // for
}
if (!has_lhs || (maptype != 1 && !has_rhs)) { }
msg_start();
} if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
no_abbr = false; // reset flag that indicates there are no abbreviations
// Check if a new local mapping wasn't already defined globally. }
if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
// need to loop over all global hash lists if (do_print) {
for (hash = 0; hash < 256 && !got_int; hash++) { msg_start();
if (is_abbrev) { }
if (hash != 0) { // there is only one abbreviation list
break; // Check if a new local mapping wasn't already defined globally.
} if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
mp = first_abbr; // need to loop over all global hash lists
} else { for (int hash = 0; hash < 256 && !got_int; hash++) {
mp = maphash[hash]; if (is_abbrev) {
} if (hash != 0) { // there is only one abbreviation list
for (; mp != NULL && !got_int; mp = mp->m_next) { break;
// check entries with the same mode }
if ((mp->m_mode & mode) != 0 mp = first_abbr;
&& mp->m_keylen == len } else {
&& args->unique mp = maphash[hash];
&& STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) { }
if (is_abbrev) { for (; mp != NULL && !got_int; mp = mp->m_next) {
semsg(_("E224: global abbreviation already exists for %s"), // check entries with the same mode
mp->m_keys); if ((mp->m_mode & mode) != 0
} else { && mp->m_keylen == len
semsg(_("E225: global mapping already exists for %s"), mp->m_keys); && args->unique
&& STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) {
if (is_abbrev) {
semsg(_("E224: global abbreviation already exists for %s"),
mp->m_keys);
} else {
semsg(_("E225: global mapping already exists for %s"), mp->m_keys);
}
retval = 5;
goto theend;
} }
retval = 5;
goto theend;
} }
} }
} }
}
// When listing global mappings, also list buffer-local ones here. // When listing global mappings, also list buffer-local ones here.
if (map_table != buf->b_maphash && !has_rhs && maptype != 1) { if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
// need to loop over all global hash lists // need to loop over all global hash lists
for (hash = 0; hash < 256 && !got_int; hash++) { for (int hash = 0; hash < 256 && !got_int; hash++) {
if (is_abbrev) { if (is_abbrev) {
if (hash != 0) { // there is only one abbreviation list if (hash != 0) { // there is only one abbreviation list
break; break;
}
mp = buf->b_first_abbr;
} else {
mp = buf->b_maphash[hash];
} }
mp = buf->b_first_abbr; for (; mp != NULL && !got_int; mp = mp->m_next) {
} else { // check entries with the same mode
mp = buf->b_maphash[hash]; if ((mp->m_mode & mode) != 0) {
} if (!has_lhs) { // show all entries
for (; mp != NULL && !got_int; mp = mp->m_next) {
// check entries with the same mode
if ((mp->m_mode & mode) != 0) {
if (!has_lhs) { // show all entries
showmap(mp, true);
did_local = true;
} else {
n = mp->m_keylen;
if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
showmap(mp, true); showmap(mp, true);
did_local = true; did_local = true;
} else {
n = mp->m_keylen;
if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
showmap(mp, true);
did_local = true;
}
} }
} }
} }
} }
} }
}
// Find an entry in the maphash[] list that matches. // Find an entry in the maphash[] list that matches.
// For :unmap we may loop two times: once to try to unmap an entry with a // For :unmap we may loop two times: once to try to unmap an entry with a
// matching 'from' part, a second time, if the first fails, to unmap an // matching 'from' part, a second time, if the first fails, to unmap an
// entry with a matching 'to' part. This was done to allow ":ab foo bar" // entry with a matching 'to' part. This was done to allow ":ab foo bar"
// to be unmapped by typing ":unab foo", where "foo" will be replaced by // to be unmapped by typing ":unab foo", where "foo" will be replaced by
// "bar" because of the abbreviation. // "bar" because of the abbreviation.
for (round = 0; (round == 0 || maptype == 1) && round <= 1 for (int round = 0; (round == 0 || maptype == 1) && round <= 1
&& !did_it && !got_int; round++) { && !did_it && !got_int; round++) {
// need to loop over all hash lists // need to loop over all hash lists
for (hash = 0; hash < 256 && !got_int; hash++) { for (int hash = 0; hash < 256 && !got_int; hash++) {
if (is_abbrev) { if (is_abbrev) {
if (hash > 0) { // there is only one abbreviation list if (hash > 0) { // there is only one abbreviation list
break; break;
}
mpp = abbr_table;
} else {
mpp = &(map_table[hash]);
}
for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
if (!(mp->m_mode & mode)) { // skip entries with wrong mode
mpp = &(mp->m_next);
continue;
}
if (!has_lhs) { // show all entries
showmap(mp, map_table != maphash);
did_it = true;
} else { // do we have a match?
if (round) { // second round: Try unmap "rhs" string
n = (int)STRLEN(mp->m_str);
p = mp->m_str;
} else {
n = mp->m_keylen;
p = mp->m_keys;
} }
if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) { mpp = abbr_table;
if (maptype == 1) { // delete entry } else {
// Only accept a full match. For abbreviations we mpp = &(map_table[hash]);
// ignore trailing space when matching with the }
// "lhs", since an abbreviation can't have for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
// trailing space. if (!(mp->m_mode & mode)) { // skip entries with wrong mode
if (n != len && (!is_abbrev || round || n > len mpp = &(mp->m_next);
|| *skipwhite(lhs + n) != NUL)) { continue;
}
if (!has_lhs) { // show all entries
showmap(mp, map_table != maphash);
did_it = true;
} else { // do we have a match?
if (round) { // second round: Try unmap "rhs" string
n = (int)STRLEN(mp->m_str);
p = mp->m_str;
} else {
n = mp->m_keylen;
p = mp->m_keys;
}
if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
if (maptype == 1) {
// Delete entry.
// Only accept a full match. For abbreviations
// we ignore trailing space when matching with
// the "lhs", since an abbreviation can't have
// trailing space.
if (n != len && (!is_abbrev || round || n > len
|| *skipwhite(lhs + n) != NUL)) {
mpp = &(mp->m_next);
continue;
}
// We reset the indicated mode bits. If nothing
// is left the entry is deleted below.
mp->m_mode &= ~mode;
did_it = true; // remember we did something
} else if (!has_rhs) { // show matching entry
showmap(mp, map_table != maphash);
did_it = true;
} else if (n != len) { // new entry is ambiguous
mpp = &(mp->m_next); mpp = &(mp->m_next);
continue; continue;
} } else if (args->unique) {
// We reset the indicated mode bits. If nothing is if (is_abbrev) {
// left the entry is deleted below. semsg(_("E226: abbreviation already exists for %s"), p);
mp->m_mode &= ~mode; } else {
did_it = true; // remember we did something semsg(_("E227: mapping already exists for %s"), p);
} else if (!has_rhs) { // show matching entry }
showmap(mp, map_table != maphash); retval = 5;
did_it = true; goto theend;
} else if (n != len) { // new entry is ambiguous } else {
mpp = &(mp->m_next); // new rhs for existing entry
continue; mp->m_mode &= ~mode; // remove mode bits
} else if (args->unique) { if (mp->m_mode == 0 && !did_it) { // reuse entry
if (is_abbrev) { XFREE_CLEAR(mp->m_str);
semsg(_("E226: abbreviation already exists for %s"), p); XFREE_CLEAR(mp->m_orig_str);
} else { XFREE_CLEAR(mp->m_desc);
semsg(_("E227: mapping already exists for %s"), p); NLUA_CLEAR_REF(mp->m_luaref);
}
retval = 5; mp->m_str = vim_strsave(rhs);
goto theend; mp->m_orig_str = vim_strsave(orig_rhs);
} else { // new rhs for existing entry mp->m_luaref = args->rhs_lua;
mp->m_mode &= ~mode; // remove mode bits mp->m_noremap = noremap;
if (mp->m_mode == 0 && !did_it) { // reuse entry mp->m_nowait = args->nowait;
XFREE_CLEAR(mp->m_str); mp->m_silent = args->silent;
XFREE_CLEAR(mp->m_orig_str); mp->m_mode = mode;
XFREE_CLEAR(mp->m_desc); mp->m_simplified = did_simplify && keyround == 1;
NLUA_CLEAR_REF(mp->m_luaref); mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_str = vim_strsave(rhs); mp->m_script_ctx.sc_lnum += sourcing_lnum;
mp->m_orig_str = vim_strsave(orig_rhs); nlua_set_sctx(&mp->m_script_ctx);
mp->m_luaref = args->rhs_lua; if (args->desc != NULL) {
mp->m_noremap = noremap; mp->m_desc = xstrdup(args->desc);
mp->m_nowait = args->nowait; }
mp->m_silent = args->silent; did_it = true;
mp->m_mode = mode;
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
nlua_set_sctx(&mp->m_script_ctx);
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
} }
did_it = true;
} }
} if (mp->m_mode == 0) { // entry can be deleted
if (mp->m_mode == 0) { // entry can be deleted mapblock_free(mpp);
mapblock_free(mpp); continue; // continue with *mpp
continue; // continue with *mpp }
}
// May need to put this entry into another hash list. // May need to put this entry into another hash list.
new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
if (!is_abbrev && new_hash != hash) { if (!is_abbrev && new_hash != hash) {
*mpp = mp->m_next; *mpp = mp->m_next;
mp->m_next = map_table[new_hash]; mp->m_next = map_table[new_hash];
map_table[new_hash] = mp; map_table[new_hash] = mp;
continue; // continue with *mpp continue; // continue with *mpp
}
} }
} }
mpp = &(mp->m_next);
} }
mpp = &(mp->m_next);
} }
} }
}
if (maptype == 1) { // delete entry if (maptype == 1) {
if (!did_it) { // delete entry
retval = 2; // no match if (!did_it) {
} else if (*lhs == Ctrl_C) { retval = 2; // no match
// If CTRL-C has been unmapped, reuse it for Interrupting. } else if (*lhs == Ctrl_C) {
// If CTRL-C has been unmapped, reuse it for Interrupting.
if (map_table == buf->b_maphash) {
buf->b_mapped_ctrl_c &= ~mode;
} else {
mapped_ctrl_c &= ~mode;
}
}
continue;
}
if (!has_lhs || !has_rhs) {
// print entries
if (!did_it && !did_local) {
if (is_abbrev) {
msg(_("No abbreviation found"));
} else {
msg(_("No mapping found"));
}
}
goto theend; // listing finished
}
if (did_it) {
continue; // have added the new entry already
}
// Get here when adding a new entry to the maphash[] list or abbrlist.
mp = xmalloc(sizeof(mapblock_T));
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*lhs == Ctrl_C) {
if (map_table == buf->b_maphash) { if (map_table == buf->b_maphash) {
buf->b_mapped_ctrl_c &= ~mode; buf->b_mapped_ctrl_c |= mode;
} else { } else {
mapped_ctrl_c &= ~mode; mapped_ctrl_c |= mode;
} }
} }
goto theend;
}
if (!has_lhs || !has_rhs) { // print entries mp->m_keys = vim_strsave(lhs);
if (!did_it && !did_local) { mp->m_str = vim_strsave(rhs);
if (is_abbrev) { mp->m_orig_str = vim_strsave(orig_rhs);
msg(_("No abbreviation found")); mp->m_luaref = args->rhs_lua;
} else { mp->m_keylen = (int)STRLEN(mp->m_keys);
msg(_("No mapping found")); mp->m_noremap = noremap;
} mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
mp->m_mode = mode;
mp->m_simplified = did_simplify && keyround == 1;
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
nlua_set_sctx(&mp->m_script_ctx);
mp->m_desc = NULL;
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
} }
goto theend; // listing finished
}
if (did_it) { // have added the new entry already // add the new entry in front of the abbrlist or maphash[] list
goto theend; if (is_abbrev) {
} mp->m_next = *abbr_table;
*abbr_table = mp;
// Get here when adding a new entry to the maphash[] list or abbrlist.
mp = xmalloc(sizeof(mapblock_T));
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*lhs == Ctrl_C) {
if (map_table == buf->b_maphash) {
buf->b_mapped_ctrl_c |= mode;
} else { } else {
mapped_ctrl_c |= mode; n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
} }
} }
mp->m_keys = vim_strsave(lhs);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_luaref = args->rhs_lua;
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
mp->m_mode = mode;
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
nlua_set_sctx(&mp->m_script_ctx);
mp->m_desc = NULL;
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
}
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbrev) {
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
theend: theend:
return retval; return retval;
} }
@@ -3600,9 +3634,8 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
int retval; int retval;
char_u *buf; char_u *buf;
char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, const char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, REPTERM_DO_LT,
false, true, true, NULL, CPO_TO_CPO_FLAGS);
CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \ #define MAPMODE(mode, modechars, chr, modeflags) \
do { \ do { \

View File

@@ -48,6 +48,10 @@ struct map_arguments {
char_u lhs[MAXMAPLEN + 1]; char_u lhs[MAXMAPLEN + 1];
size_t lhs_len; size_t lhs_len;
/// Unsimplifed {lhs} of the mapping. If no simplification has been done then alt_lhs_len is 0.
char_u alt_lhs[MAXMAPLEN + 1];
size_t alt_lhs_len;
char_u *rhs; /// The {rhs} of the mapping. char_u *rhs; /// The {rhs} of the mapping.
size_t rhs_len; size_t rhs_len;
LuaRef rhs_lua; /// lua function as rhs LuaRef rhs_lua; /// lua function as rhs
@@ -59,7 +63,7 @@ struct map_arguments {
}; };
typedef struct map_arguments MapArguments; typedef struct map_arguments MapArguments;
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \ #define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
{ 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL } { 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
#define KEYLEN_PART_KEY (-1) // keylen value for incomplete key-code #define KEYLEN_PART_KEY (-1) // keylen value for incomplete key-code
#define KEYLEN_PART_MAP (-2) // keylen value for incomplete mapping #define KEYLEN_PART_MAP (-2) // keylen value for incomplete mapping

View File

@@ -570,16 +570,18 @@ char_u *get_special_key_name(int c, int modifiers)
// be at least 19 bytes per "<x>" form. // be at least 19 bytes per "<x>" form.
/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. /// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
/// @param[in] in_string Inside a double quoted string /// @param[in] in_string Inside a double quoted string
/// @param[in] simplify simplify <C-H>, etc.
/// @param[out] did_simplify found <C-H>, etc.
/// ///
/// @return Number of characters added to dst, zero for no match. /// @return Number of characters added to dst, zero for no match.
unsigned int trans_special(const char_u **srcp, const size_t src_len, char_u *const dst, unsigned int trans_special(const char_u **const srcp, const size_t src_len, char_u *const dst,
const bool keycode, const bool in_string) const bool keycode, const bool in_string, const bool simplify,
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT bool *const did_simplify)
FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{ {
int modifiers = 0; int modifiers = 0;
int key; int key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string, simplify,
did_simplify);
key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string);
if (key == 0) { if (key == 0) {
return 0; return 0;
} }
@@ -626,11 +628,14 @@ unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. /// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
/// @param[in] keep_x_key Dont translate xHome to Home key. /// @param[in] keep_x_key Dont translate xHome to Home key.
/// @param[in] in_string In string, double quote is escaped /// @param[in] in_string In string, double quote is escaped
/// @param[in] simplify simplify <C-H>, etc.
/// @param[out] did_simplify found <C-H>, etc.
/// ///
/// @return Key and modifiers or 0 if there is no match. /// @return Key and modifiers or 0 if there is no match.
int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, const bool keycode, int find_special_key(const char_u **const srcp, const size_t src_len, int *const modp,
const bool keep_x_key, const bool in_string) const bool keycode, const bool keep_x_key, const bool in_string,
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL const bool simplify, bool *const did_simplify)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3)
{ {
const char_u *last_dash; const char_u *last_dash;
const char_u *end_of_name; const char_u *end_of_name;
@@ -748,7 +753,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Normal Key with modifier: // Normal Key with modifier:
// Try to make a single byte code (except for Alt/Meta modifiers). // Try to make a single byte code (except for Alt/Meta modifiers).
if (!IS_SPECIAL(key)) { if (!IS_SPECIAL(key)) {
key = extract_modifiers(key, &modifiers); key = extract_modifiers(key, &modifiers, simplify, did_simplify);
} }
*modp = modifiers; *modp = modifiers;
@@ -762,7 +767,10 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
/// Try to include modifiers (except alt/meta) in the key. /// Try to include modifiers (except alt/meta) in the key.
/// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc. /// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc.
static int extract_modifiers(int key, int *modp) /// @param[in] simplify if false, don't do Ctrl
/// @param[out] did_simplify set when it is not NULL and "simplify" is true and
/// Ctrl is removed from modifiers
static int extract_modifiers(int key, int *modp, const bool simplify, bool *const did_simplify)
{ {
int modifiers = *modp; int modifiers = *modp;
@@ -773,15 +781,19 @@ static int extract_modifiers(int key, int *modp)
modifiers &= ~MOD_MASK_SHIFT; modifiers &= ~MOD_MASK_SHIFT;
} }
} }
if ((modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { // <C-H> and <C-h> mean the same thing, always use "H"
if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) {
key = TOUPPER_ASC(key); key = TOUPPER_ASC(key);
int new_key = CTRL_CHR(key); }
if (new_key != TAB && new_key != CAR && new_key != ESC) { if (simplify && (modifiers & MOD_MASK_CTRL)
key = new_key; && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
modifiers &= ~MOD_MASK_CTRL; key = CTRL_CHR(key);
if (key == 0) { // <C-@> is <Nul> modifiers &= ~MOD_MASK_CTRL;
key = K_ZERO; if (key == 0) { // <C-@> is <Nul>
} key = K_ZERO;
}
if (did_simplify != NULL) {
*did_simplify = true;
} }
} }
@@ -853,34 +865,31 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
return 0; // Shouldn't get here return 0; // Shouldn't get here
} }
/// Replace any terminal code strings with the equivalent internal /// Replace any terminal code strings with the equivalent internal representation.
/// representation
/// ///
/// Used for the "from" and "to" part of a mapping, and the "to" part of /// Used for the "from" and "to" part of a mapping, and the "to" part of a menu command.
/// a menu command. Any strings like "<C-UP>" are also replaced, unless /// Any strings like "<C-UP>" are also replaced, unless `special` is false.
/// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL /// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
/// KS_SPECIAL KE_FILLER. ///
/// When "flags" has REPTERM_FROM_PART, trailing <C-v> is included, otherwise it is removed (to make
/// ":map xx ^V" map xx to nothing). When cpo_flags contains FLAG_CPO_BSLASH, a backslash can be
/// used in place of <C-v>. All other <C-v> characters are removed.
/// ///
/// @param[in] from What characters to replace. /// @param[in] from What characters to replace.
/// @param[in] from_len Length of the "from" argument. /// @param[in] from_len Length of the "from" argument.
/// @param[out] bufp Location where results were saved in case of success /// @param[out] bufp Location where results were saved in case of success (allocated).
/// (allocated). Will be set to NULL in case of failure. /// Will be set to NULL in case of failure.
/// @param[in] do_lt If true, also translate <lt>. /// @param[in] flags REPTERM_FROM_PART see above
/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is /// REPTERM_DO_LT also translate <lt>
/// removed (to make ":map xx ^V" map xx to nothing). /// REPTERM_NO_SPECIAL do not accept <key> notation
/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash /// REPTERM_NO_SIMPLIFY do not simplify <C-H> into 0x08, etc.
/// can be used in place of <C-v>. All other <C-v> /// @param[out] did_simplify set when some <C-H> code was simplied, unless it is NULL.
/// characters are removed. /// @param[in] cpo_flags Relevant flags derived from p_cpo, see CPO_TO_CPO_FLAGS.
/// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char.
/// @param[in] cpo_flags Relevant flags derived from p_cpo, see
/// #CPO_TO_CPO_FLAGS.
/// ///
/// @return Pointer to an allocated memory in case of success, "from" in case of /// @return Pointer to an allocated memory, which is also saved to "bufp".
/// failure. In case of success returned pointer is also saved to char_u *replace_termcodes(const char_u *const from, const size_t from_len, char_u **const bufp,
/// "bufp". const int flags, bool *const did_simplify, const int cpo_flags)
char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bufp, FUNC_ATTR_NONNULL_ARG(1, 3)
const bool from_part, const bool do_lt, const bool special, int cpo_flags)
FUNC_ATTR_NONNULL_ALL
{ {
ssize_t i; ssize_t i;
size_t slen; size_t slen;
@@ -888,10 +897,10 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
size_t dlen = 0; size_t dlen = 0;
const char_u *src; const char_u *src;
const char_u *const end = from + from_len - 1; const char_u *const end = from + from_len - 1;
int do_backslash; // backslash is a special character
char_u *result; // buffer for resulting string char_u *result; // buffer for resulting string
do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); const bool do_backslash = !(cpo_flags & FLAG_CPO_BSLASH); // backslash is a special character
const bool do_special = !(flags & REPTERM_NO_SPECIAL);
// Allocate space for the translation. Worst case a single character is // Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end. // replaced by 6 bytes (shifted special key), plus a NUL at the end.
@@ -901,7 +910,7 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
src = from; src = from;
// Check for #n at start only: function key n // Check for #n at start only: function key n
if (from_part && from_len > 1 && src[0] == '#' if ((flags & REPTERM_FROM_PART) && from_len > 1 && src[0] == '#'
&& ascii_isdigit(src[1])) { // function key && ascii_isdigit(src[1])) { // function key
result[dlen++] = K_SPECIAL; result[dlen++] = K_SPECIAL;
result[dlen++] = 'k'; result[dlen++] = 'k';
@@ -916,8 +925,8 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
// Copy each byte from *from to result[dlen] // Copy each byte from *from to result[dlen]
while (src <= end) { while (src <= end) {
// Check for special <> keycodes, like "<C-S-LeftMouse>" // Check for special <> keycodes, like "<C-S-LeftMouse>"
if (special && (do_lt || ((end - src) >= 3 if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3
&& STRNCMP(src, "<lt>", 4) != 0))) { && STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _. // Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
@@ -936,15 +945,15 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
} }
} }
slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true, slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true, false,
false); (flags & REPTERM_NO_SIMPLIFY) == 0, did_simplify);
if (slen) { if (slen) {
dlen += slen; dlen += slen;
continue; continue;
} }
} }
if (special) { if (do_special) {
char_u *p, *s, len; char_u *p, *s, len;
// Replace <Leader> by the value of "mapleader". // Replace <Leader> by the value of "mapleader".
@@ -984,7 +993,7 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
if (key == Ctrl_V || (do_backslash && key == '\\')) { if (key == Ctrl_V || (do_backslash && key == '\\')) {
src++; // skip CTRL-V or backslash src++; // skip CTRL-V or backslash
if (src > end) { if (src > end) {
if (from_part) { if (flags & REPTERM_FROM_PART) {
result[dlen++] = key; result[dlen++] = key;
} }
break; break;

View File

@@ -236,8 +236,8 @@ void ex_menu(exarg_T *eap)
} else if (modes & MENU_TIP_MODE) { } else if (modes & MENU_TIP_MODE) {
map_buf = NULL; // Menu tips are plain text. map_buf = NULL; // Menu tips are plain text.
} else { } else {
map_to = (char *)replace_termcodes((char_u *)map_to, STRLEN(map_to), map_to = (char *)replace_termcodes((char_u *)map_to, STRLEN(map_to), (char_u **)&map_buf,
(char_u **)&map_buf, false, true, true, CPO_TO_CPO_FLAGS); REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
} }
menuarg.modes = modes; menuarg.modes = modes;
menuarg.noremap[0] = noremap; menuarg.noremap[0] = noremap;

View File

@@ -3015,7 +3015,7 @@ ambw_end:
} else if (varp == &p_pt) { } else if (varp == &p_pt) {
// 'pastetoggle': translate key codes like in a mapping // 'pastetoggle': translate key codes like in a mapping
if (*p_pt) { if (*p_pt) {
(void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true, (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
if (p != NULL) { if (p != NULL) {
if (new_value_alloced) { if (new_value_alloced) {
@@ -5222,7 +5222,7 @@ int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
} else if (has_lt) { } else if (has_lt) {
arg--; // put arg at the '<' arg--; // put arg at the '<'
modifiers = 0; modifiers = 0;
key = find_special_key(&arg, len + 1, &modifiers, true, true, false); key = find_special_key(&arg, len + 1, &modifiers, true, true, false, true, NULL);
if (modifiers) { // can't handle modifiers here if (modifiers) { // can't handle modifiers here
key = 0; key = 0;
} }

View File

@@ -239,8 +239,7 @@ size_t input_enqueue(String keys)
// K_SPECIAL(0x80). // K_SPECIAL(0x80).
uint8_t buf[19] = { 0 }; uint8_t buf[19] = { 0 };
unsigned int new_size unsigned int new_size
= trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, false, true, NULL);
false);
if (new_size) { if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size); new_size = handle_mouse_event(&ptr, buf, new_size);

View File

@@ -318,4 +318,10 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#define REPLACE_CR_NCHAR (-1) #define REPLACE_CR_NCHAR (-1)
#define REPLACE_NL_NCHAR (-2) #define REPLACE_NL_NCHAR (-2)
// Flags for replace_termcodes()
#define REPTERM_FROM_PART 1
#define REPTERM_DO_LT 2
#define REPTERM_NO_SPECIAL 4
#define REPTERM_NO_SIMPLIFY 8
#endif // NVIM_VIM_H #endif // NVIM_VIM_H

View File

@@ -1817,9 +1817,8 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
} }
// Special key, e.g.: "\<C-W>" // Special key, e.g.: "\<C-W>"
case '<': { case '<': {
const size_t special_len = ( const size_t special_len = trans_special((const char_u **)&p, (size_t)(e - p),
trans_special((const char_u **)&p, (size_t)(e - p), (char_u *)v_p, true, true, true, NULL);
(char_u *)v_p, true, true));
if (special_len != 0) { if (special_len != 0) {
v_p += special_len; v_p += special_len;
} else { } else {

View File

@@ -582,7 +582,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can set mappings containing literal keycodes', function() it('can set mappings containing literal keycodes', function()
meths.set_keymap('n', '\n\r\n', 'rhs', {}) meths.set_keymap('n', '\n\r\n', 'rhs', {})
local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs') local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
eq(expected, get_mapargs('n', '<C-j><CR><C-j>')) eq(expected, get_mapargs('n', '<NL><CR><NL>'))
end) end)
it('can set mappings whose RHS is a <Nop>', function() it('can set mappings whose RHS is a <Nop>', function()

View File

@@ -6,6 +6,7 @@ local eq = helpers.eq
local neq = helpers.neq local neq = helpers.neq
local keymap = helpers.cimport("./src/nvim/keymap.h") local keymap = helpers.cimport("./src/nvim/keymap.h")
local NULL = helpers.NULL
describe('keymap.c', function() describe('keymap.c', function()
@@ -15,12 +16,12 @@ describe('keymap.c', function()
itp('no keycode', function() itp('no keycode', function()
srcp[0] = 'abc' srcp[0] = 'abc'
eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false)) eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false, false, NULL))
end) end)
itp('keycode with multiple modifiers', function() itp('keycode with multiple modifiers', function()
srcp[0] = '<C-M-S-A>' srcp[0] = '<C-M-S-A>'
neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false)) neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false, false, NULL))
neq(0, modp[0]) neq(0, modp[0])
end) end)
@@ -28,22 +29,22 @@ describe('keymap.c', function()
-- Compare other capitalizations to this. -- Compare other capitalizations to this.
srcp[0] = '<C-A>' srcp[0] = '<C-A>'
local all_caps_key = local all_caps_key =
keymap.find_special_key(srcp, 5, modp, false, false, false) keymap.find_special_key(srcp, 5, modp, false, false, false, false, NULL)
local all_caps_mod = modp[0] local all_caps_mod = modp[0]
srcp[0] = '<C-a>' srcp[0] = '<C-a>'
eq(all_caps_key, eq(all_caps_key,
keymap.find_special_key(srcp, 5, modp, false, false, false)) keymap.find_special_key(srcp, 5, modp, false, false, false, false, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
srcp[0] = '<c-A>' srcp[0] = '<c-A>'
eq(all_caps_key, eq(all_caps_key,
keymap.find_special_key(srcp, 5, modp, false, false, false)) keymap.find_special_key(srcp, 5, modp, false, false, false, false, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
srcp[0] = '<c-a>' srcp[0] = '<c-a>'
eq(all_caps_key, eq(all_caps_key,
keymap.find_special_key(srcp, 5, modp, false, false, false)) keymap.find_special_key(srcp, 5, modp, false, false, false, false, NULL))
eq(all_caps_mod, modp[0]) eq(all_caps_mod, modp[0])
end) end)
@@ -51,20 +52,20 @@ describe('keymap.c', function()
-- Unescaped with in_string=false -- Unescaped with in_string=false
srcp[0] = '<C-">' srcp[0] = '<C-">'
eq(string.byte('"'), eq(string.byte('"'),
keymap.find_special_key(srcp, 5, modp, false, false, false)) keymap.find_special_key(srcp, 5, modp, false, false, false, false, NULL))
-- Unescaped with in_string=true -- Unescaped with in_string=true
eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true)) eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true, false, NULL))
-- Escaped with in_string=false -- Escaped with in_string=false
srcp[0] = '<C-\\">' srcp[0] = '<C-\\">'
-- Should fail because the key is invalid -- Should fail because the key is invalid
-- (more than 1 non-modifier character). -- (more than 1 non-modifier character).
eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false)) eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false, false, NULL))
-- Escaped with in_string=true -- Escaped with in_string=true
eq(string.byte('"'), eq(string.byte('"'),
keymap.find_special_key(srcp, 6, modp, false, false, true)) keymap.find_special_key(srcp, 6, modp, false, false, true, false, NULL))
end) end)
end) end)