aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/private/helpers.c5
-rw-r--r--src/nvim/api/vim.c14
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/eval/funcs.c2
-rw-r--r--src/nvim/ex_docmd.c3
-rw-r--r--src/nvim/getchar.c549
-rw-r--r--src/nvim/getchar.h6
-rw-r--r--src/nvim/keymap.c111
-rw-r--r--src/nvim/menu.c4
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/os/input.c3
-rw-r--r--src/nvim/vim.h6
-rw-r--r--src/nvim/viml/parser/expressions.c5
14 files changed, 390 insertions, 326 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 5ba4700f62..8383f29400 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -632,7 +632,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod
} else {
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);
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);
current_maphash;
current_maphash = current_maphash->m_next) {
+ if (current_maphash->m_simplified) {
+ continue;
+ }
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
mapblock_fill_dict(dict, current_maphash, buffer_value, false);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 0f9a4a0e0d..2174cd1620 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -403,9 +403,19 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
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;
- replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
- from_part, do_lt, special, CPO_TO_CPO_FLAGS);
+ replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, flags, NULL, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr);
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 8d2f7c4545..c71d022018 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -356,6 +356,8 @@ struct mapblock {
LuaRef m_luaref; // lua function reference as rhs
int m_keylen; // strlen(m_keys)
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
char m_silent; // <silent> used, don't echo commands
char m_nowait; // <nowait> used
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index e192a6de6b..af6e7e470d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4965,7 +4965,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// Special key, e.g.: "\<C-W>"
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) {
name += extra;
if (name >= rettv->vval.v_string + len) {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index f97ceedeb7..71a090afed 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -5684,7 +5684,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
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);
rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
xfree(keys_buf);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 4562f6c751..f3b11cc103 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -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;
garray_T *gap;
- replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true,
- CPO_TO_CPO_FLAGS);
+ replace_termcodes(rep, STRLEN(rep), &rep_buf, 0, NULL, CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) {
// Can't replace termcodes - try using the string as is
rep_buf = vim_strsave(rep);
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index f2df7b49fd..89a9a1ea95 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2711,11 +2711,12 @@ int fix_input_buffer(char_u *buf, int len)
/// @param[in] orig_rhs_len `strlen` of orig_rhs.
/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
/// @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,
- const size_t orig_rhs_len, LuaRef rhs_lua, int cpo_flags,
- MapArguments *mapargs)
+void set_maparg_lhs_rhs(const char_u *const orig_lhs, const size_t orig_lhs_len,
+ const char_u *const orig_rhs, const size_t orig_rhs_len,
+ const LuaRef rhs_lua, const int cpo_flags, MapArguments *const mapargs)
{
char_u *lhs_buf = NULL;
+ char_u *alt_lhs_buf = NULL;
char_u *rhs_buf = NULL;
// 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
// needs to be freed later (*lhs_buf and *rhs_buf).
// replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
- char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf,
- true, true, true, cpo_flags);
+ // If something like <C-H> is simplified to 0x08 then mark it as simplified.
+ 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);
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;
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_is_noop = true;
} else {
- replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
- false, true, true, cpo_flags);
+ replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL,
+ cpo_flags);
mapargs->rhs_len = STRLEN(replaced);
mapargs->rhs_is_noop = false;
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(alt_lhs_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)
{
mapblock_T *mp, **mpp;
- char_u *p;
+ const char_u *p;
int n;
- int len = 0; // init for GCC
- int did_it = false;
- int did_local = false;
- int round;
int retval = 0;
- int hash;
- int new_hash;
mapblock_T **abbr_table;
mapblock_T **map_table;
int noremap;
@@ -2929,8 +2937,9 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
validate_maphash();
- 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_lhs = (args->lhs[0] != NUL);
+ 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
if (maptype == 1 && !has_lhs) {
@@ -2938,298 +2947,323 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T
goto theend;
}
- char_u *lhs = (char_u *)&args->lhs;
- char_u *rhs = args->rhs;
- char_u *orig_rhs = args->orig_rhs;
+ const char_u *lhs = (char_u *)&args->lhs;
+ const char_u *const rhs = args->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
- if (has_lhs) {
- len = (int)args->lhs_len;
- if (len > MAXMAPLEN) {
- retval = 1;
- goto theend;
- }
+ // The following is done twice if we have two versions of keys
+ for (int keyround = 1; keyround <= 2; keyround++) {
+ bool did_it = false;
+ bool did_local = false;
+ int len = (int)args->lhs_len;
- if (is_abbrev && maptype != 1) {
- //
- // 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 (keyround == 2) {
+ if (!did_simplify) {
+ break;
}
- if (last && n > 2 && same >= 0 && same < n - 1) {
+ 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;
+ }
+
+ // check arguments and translate function keys
+ if (has_lhs) {
+ if (len > MAXMAPLEN) {
retval = 1;
goto theend;
}
- // An abbreviation cannot contain white space.
- for (n = 0; n < len; n++) {
- if (ascii_iswhite(lhs[n])) {
+
+ if (is_abbrev && maptype != 1) {
+ //
+ // 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;
goto theend;
}
- } // for
+ // An abbreviation cannot contain white space.
+ for (n = 0; n < len; n++) {
+ if (ascii_iswhite(lhs[n])) {
+ retval = 1;
+ goto theend;
+ }
+ } // for
+ }
}
- }
- if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
- no_abbr = false; // reset flag that indicates there are no abbreviations
- }
+ if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
+ no_abbr = false; // reset flag that indicates there are no abbreviations
+ }
- if (!has_lhs || (maptype != 1 && !has_rhs)) {
- msg_start();
- }
+ if (do_print) {
+ msg_start();
+ }
- // 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
- for (hash = 0; hash < 256 && !got_int; hash++) {
- 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) {
+ // need to loop over all global hash lists
+ for (int hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
+ break;
+ }
+ mp = first_abbr;
+ } else {
+ mp = maphash[hash];
}
- mp = first_abbr;
- } else {
- mp = maphash[hash];
- }
- for (; mp != NULL && !got_int; mp = mp->m_next) {
- // check entries with the same mode
- if ((mp->m_mode & mode) != 0
- && mp->m_keylen == len
- && 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);
+ for (; mp != NULL && !got_int; mp = mp->m_next) {
+ // check entries with the same mode
+ if ((mp->m_mode & mode) != 0
+ && mp->m_keylen == len
+ && 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.
- if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
- // need to loop over all global hash lists
- for (hash = 0; hash < 256 && !got_int; hash++) {
- if (is_abbrev) {
- if (hash != 0) { // there is only one abbreviation list
- break;
+ // When listing global mappings, also list buffer-local ones here.
+ if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (int hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
+ break;
+ }
+ mp = buf->b_first_abbr;
+ } else {
+ mp = buf->b_maphash[hash];
}
- mp = buf->b_first_abbr;
- } else {
- mp = buf->b_maphash[hash];
- }
- 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) {
+ 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);
+ did_local = true;
+ }
}
}
}
}
}
- }
- // 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
- // 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"
- // to be unmapped by typing ":unab foo", where "foo" will be replaced by
- // "bar" because of the abbreviation.
- for (round = 0; (round == 0 || maptype == 1) && round <= 1
- && !did_it && !got_int; round++) {
- // need to loop over all hash lists
- for (hash = 0; hash < 256 && !got_int; hash++) {
- if (is_abbrev) {
- if (hash > 0) { // there is only one abbreviation list
- 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;
+ // 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
+ // 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"
+ // to be unmapped by typing ":unab foo", where "foo" will be replaced by
+ // "bar" because of the abbreviation.
+ for (int round = 0; (round == 0 || maptype == 1) && round <= 1
+ && !did_it && !got_int; round++) {
+ // need to loop over all hash lists
+ for (int hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash > 0) { // there is only one abbreviation list
+ break;
+ }
+ mpp = abbr_table;
+ } else {
+ mpp = &(map_table[hash]);
}
- 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;
+ 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 (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)) {
+ 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);
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);
- continue;
- } else if (args->unique) {
- if (is_abbrev) {
- semsg(_("E226: abbreviation already exists for %s"), p);
+ } else if (args->unique) {
+ if (is_abbrev) {
+ semsg(_("E226: abbreviation already exists for %s"), p);
+ } else {
+ semsg(_("E227: mapping already exists for %s"), p);
+ }
+ retval = 5;
+ goto theend;
} else {
- semsg(_("E227: mapping already exists for %s"), p);
- }
- retval = 5;
- goto theend;
- } else { // new rhs for existing entry
- mp->m_mode &= ~mode; // remove mode bits
- if (mp->m_mode == 0 && !did_it) { // reuse entry
- XFREE_CLEAR(mp->m_str);
- XFREE_CLEAR(mp->m_orig_str);
- XFREE_CLEAR(mp->m_desc);
- NLUA_CLEAR_REF(mp->m_luaref);
-
- mp->m_str = vim_strsave(rhs);
- mp->m_orig_str = vim_strsave(orig_rhs);
- mp->m_luaref = args->rhs_lua;
- 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);
- if (args->desc != NULL) {
- mp->m_desc = xstrdup(args->desc);
+ // new rhs for existing entry
+ mp->m_mode &= ~mode; // remove mode bits
+ if (mp->m_mode == 0 && !did_it) { // reuse entry
+ XFREE_CLEAR(mp->m_str);
+ XFREE_CLEAR(mp->m_orig_str);
+ XFREE_CLEAR(mp->m_desc);
+ NLUA_CLEAR_REF(mp->m_luaref);
+
+ mp->m_str = vim_strsave(rhs);
+ mp->m_orig_str = vim_strsave(orig_rhs);
+ mp->m_luaref = args->rhs_lua;
+ 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);
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
+ did_it = true;
}
- did_it = true;
}
- }
- if (mp->m_mode == 0) { // entry can be deleted
- mapblock_free(mpp);
- continue; // continue with *mpp
- }
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
+ continue; // continue with *mpp
+ }
- // May need to put this entry into another hash list.
- new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- if (!is_abbrev && new_hash != hash) {
- *mpp = mp->m_next;
- mp->m_next = map_table[new_hash];
- map_table[new_hash] = mp;
+ // May need to put this entry into another hash list.
+ int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ if (!is_abbrev && new_hash != hash) {
+ *mpp = mp->m_next;
+ mp->m_next = map_table[new_hash];
+ 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 (!did_it) {
- retval = 2; // no match
- } 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;
+ if (maptype == 1) {
+ // delete entry
+ if (!did_it) {
+ retval = 2; // no match
+ } 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;
}
- goto theend;
- }
- if (!has_lhs || !has_rhs) { // print entries
- if (!did_it && !did_local) {
- if (is_abbrev) {
- msg(_("No abbreviation found"));
- } else {
- msg(_("No mapping found"));
+ 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
}
- goto theend; // listing finished
- }
- if (did_it) { // have added the new entry already
- goto theend;
- }
+ 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));
+ // 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 {
- mapped_ctrl_c |= mode;
+ // 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 {
+ mapped_ctrl_c |= mode;
+ }
}
- }
- 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;
+ 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_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);
+ }
+
+ // 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:
@@ -3600,9 +3634,8 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
int retval;
char_u *buf;
- char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf,
- false, true, true,
- CPO_TO_CPO_FLAGS);
+ const char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, REPTERM_DO_LT,
+ NULL, CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \
do { \
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 9b8605f1df..237f0632bd 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -48,6 +48,10 @@ struct map_arguments {
char_u lhs[MAXMAPLEN + 1];
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.
size_t rhs_len;
LuaRef rhs_lua; /// lua function as rhs
@@ -59,7 +63,7 @@ struct map_arguments {
};
typedef struct map_arguments MapArguments;
#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_MAP (-2) // keylen value for incomplete mapping
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 3fdc140ebd..07975545be 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -570,16 +570,18 @@ char_u *get_special_key_name(int c, int modifiers)
// be at least 19 bytes per "<x>" form.
/// @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] simplify simplify <C-H>, etc.
+/// @param[out] did_simplify found <C-H>, etc.
///
/// @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,
- const bool keycode, const bool in_string)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+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 simplify,
+ bool *const did_simplify)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
int modifiers = 0;
- int key;
-
- key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string);
+ int key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string, simplify,
+ did_simplify);
if (key == 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] keep_x_key Don’t translate xHome to Home key.
/// @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.
-int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, const bool keycode,
- const bool keep_x_key, const bool in_string)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+int find_special_key(const char_u **const srcp, const size_t src_len, int *const modp,
+ const bool keycode, const bool keep_x_key, const bool in_string,
+ 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 *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:
// Try to make a single byte code (except for Alt/Meta modifiers).
if (!IS_SPECIAL(key)) {
- key = extract_modifiers(key, &modifiers);
+ key = extract_modifiers(key, &modifiers, simplify, did_simplify);
}
*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.
/// 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;
@@ -773,15 +781,19 @@ static int extract_modifiers(int key, int *modp)
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);
- int new_key = CTRL_CHR(key);
- if (new_key != TAB && new_key != CAR && new_key != ESC) {
- key = new_key;
- modifiers &= ~MOD_MASK_CTRL;
- if (key == 0) { // <C-@> is <Nul>
- key = K_ZERO;
- }
+ }
+ if (simplify && (modifiers & MOD_MASK_CTRL)
+ && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
+ key = CTRL_CHR(key);
+ modifiers &= ~MOD_MASK_CTRL;
+ 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
}
-/// Replace any terminal code strings with the equivalent internal
-/// representation
+/// Replace any terminal code strings with the equivalent internal representation.
+///
+/// Used for the "from" and "to" part of a mapping, and the "to" part of a menu command.
+/// Any strings like "<C-UP>" are also replaced, unless `special` is false.
+/// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
///
-/// Used for the "from" and "to" part of a mapping, and the "to" part of
-/// a menu command. Any strings like "<C-UP>" are also replaced, unless
-/// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL
-/// 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_len Length of the "from" argument.
-/// @param[out] bufp Location where results were saved in case of success
-/// (allocated). Will be set to NULL in case of failure.
-/// @param[in] do_lt If true, also translate <lt>.
-/// @param[in] from_part If true, 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] 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.
+/// @param[out] bufp Location where results were saved in case of success (allocated).
+/// Will be set to NULL in case of failure.
+/// @param[in] flags REPTERM_FROM_PART see above
+/// REPTERM_DO_LT also translate <lt>
+/// REPTERM_NO_SPECIAL do not accept <key> notation
+/// REPTERM_NO_SIMPLIFY do not simplify <C-H> into 0x08, etc.
+/// @param[out] did_simplify set when some <C-H> code was simplied, unless it is NULL.
+/// @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
-/// failure. In case of success returned pointer is also saved to
-/// "bufp".
-char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bufp,
- const bool from_part, const bool do_lt, const bool special, int cpo_flags)
- FUNC_ATTR_NONNULL_ALL
+/// @return Pointer to an allocated memory, which is also saved to "bufp".
+char_u *replace_termcodes(const char_u *const from, const size_t from_len, char_u **const bufp,
+ const int flags, bool *const did_simplify, const int cpo_flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
{
ssize_t i;
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;
const char_u *src;
const char_u *const end = from + from_len - 1;
- int do_backslash; // backslash is a special character
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
// 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;
// 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
result[dlen++] = K_SPECIAL;
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]
while (src <= end) {
// Check for special <> keycodes, like "<C-S-LeftMouse>"
- if (special && (do_lt || ((end - src) >= 3
- && STRNCMP(src, "<lt>", 4) != 0))) {
+ if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3
+ && STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
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,
- false);
+ slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true, false,
+ (flags & REPTERM_NO_SIMPLIFY) == 0, did_simplify);
if (slen) {
dlen += slen;
continue;
}
}
- if (special) {
+ if (do_special) {
char_u *p, *s, len;
// 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 == '\\')) {
src++; // skip CTRL-V or backslash
if (src > end) {
- if (from_part) {
+ if (flags & REPTERM_FROM_PART) {
result[dlen++] = key;
}
break;
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index f648a06284..d2293a6923 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -236,8 +236,8 @@ void ex_menu(exarg_T *eap)
} else if (modes & MENU_TIP_MODE) {
map_buf = NULL; // Menu tips are plain text.
} else {
- map_to = (char *)replace_termcodes((char_u *)map_to, STRLEN(map_to),
- (char_u **)&map_buf, false, true, true, CPO_TO_CPO_FLAGS);
+ map_to = (char *)replace_termcodes((char_u *)map_to, STRLEN(map_to), (char_u **)&map_buf,
+ REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
}
menuarg.modes = modes;
menuarg.noremap[0] = noremap;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 49c76d2452..79231385ad 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3015,7 +3015,7 @@ ambw_end:
} else if (varp == &p_pt) {
// 'pastetoggle': translate key codes like in a mapping
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);
if (p != NULL) {
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) {
arg--; // put arg at the '<'
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
key = 0;
}
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 745b888b5e..4c617c9031 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -239,8 +239,7 @@ size_t input_enqueue(String keys)
// K_SPECIAL(0x80).
uint8_t buf[19] = { 0 };
unsigned int new_size
- = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true,
- false);
+ = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, false, true, NULL);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 3c8a865fb1..2672d0b2bc 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -318,4 +318,10 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#define REPLACE_CR_NCHAR (-1)
#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
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index b8b0a38f44..ec747d56d4 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -1817,9 +1817,8 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
}
// Special key, e.g.: "\<C-W>"
case '<': {
- const size_t special_len = (
- trans_special((const char_u **)&p, (size_t)(e - p),
- (char_u *)v_p, true, true));
+ const size_t special_len = trans_special((const char_u **)&p, (size_t)(e - p),
+ (char_u *)v_p, true, true, true, NULL);
if (special_len != 0) {
v_p += special_len;
} else {