diff options
Diffstat (limited to 'src/nvim/getchar.c')
-rw-r--r-- | src/nvim/getchar.c | 820 |
1 files changed, 513 insertions, 307 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index f2df7b49fd..6dd6c51e8f 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -430,8 +430,7 @@ void flush_buffers(flush_buffers_T flush_typeahead) init_typebuf(); start_stuff(); - while (read_readbuffers(TRUE) != NUL) { - } + while (read_readbuffers(true) != NUL) {} if (flush_typeahead == FLUSH_MINIMAL) { // remove mapped characters at the start only @@ -443,8 +442,7 @@ void flush_buffers(flush_buffers_T flush_typeahead) // We have to get all characters, because we may delete the first // part of an escape sequence. In an xterm we get one char at a // time and we have to get them all. - while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) { - } + while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) {} } typebuf.tb_off = MAXMAPLEN; typebuf.tb_len = 0; @@ -494,8 +492,7 @@ void CancelRedo(void) redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; start_stuff(); - while (read_readbuffers(TRUE) != NUL) { - } + while (read_readbuffers(true) != NUL) {} } } @@ -872,10 +869,8 @@ void init_default_mappings(void) int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent) { char_u *s1, *s2; - int newlen; int addlen; int i; - int newoff; int val; int nrm; @@ -901,13 +896,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent // In typebuf.tb_buf there must always be room for 3 * (MAXMAPLEN + 4) // characters. We add some extra room to avoid having to allocate too // often. - newoff = MAXMAPLEN + 4; - newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); - if (newlen < 0) { // string is getting too long + int newoff = MAXMAPLEN + 4; + int extra = addlen + newoff + 4 * (MAXMAPLEN + 4); + if (typebuf.tb_len > 2147483674 - extra) { + // string is getting too long for 32 bit int emsg(_(e_toocompl)); // also calls flush_buffers setcursor(); return FAIL; } + int newlen = typebuf.tb_len + extra; s1 = xmalloc((size_t)newlen); s2 = xmalloc((size_t)newlen); typebuf.tb_buflen = newlen; @@ -1261,9 +1258,9 @@ static int old_KeyStuffed; // whether old_char was stuffed static bool can_get_old_char(void) { - // If the old character was not stuffed and characters have been added to - // the stuff buffer, need to first get the stuffed characters instead. - return old_char != -1 && (old_KeyStuffed || stuff_empty()); + // If the old character was not stuffed and characters have been added to + // the stuff buffer, need to first get the stuffed characters instead. + return old_char != -1 && (old_KeyStuffed || stuff_empty()); } /* @@ -1442,6 +1439,28 @@ static void updatescript(int c) } } +/// Merge "modifiers" into "c_arg". +int merge_modifiers(int c_arg, int *modifiers) +{ + int c = c_arg; + + if (*modifiers & MOD_MASK_CTRL) { + if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { + c &= 0x1f; + if (c == NUL) { + c = K_ZERO; + } + } else if (c == '6') { + // CTRL-6 is equivalent to CTRL-^ + c = 0x1e; + } + if (c != c_arg) { + *modifiers &= ~MOD_MASK_CTRL; + } + } + return c; +} + /// Get the next input character. /// Can return a special key or a multi-byte character. /// Can return NUL when called recursively, use safe_vgetc() if that's not @@ -1478,6 +1497,8 @@ int vgetc(void) static size_t last_vgetc_recorded_len = 0; mod_mask = 0; + vgetc_mod_mask = 0; + vgetc_char = 0; // last_recorded_len can be larger than last_vgetc_recorded_len // if peeking records more @@ -1487,19 +1508,24 @@ int vgetc(void) bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read no_mapping++; + allow_keys++; did_inc = true; // mod_mask may change value } c = vgetorpeek(true); if (did_inc) { no_mapping--; + allow_keys--; } // Get two extra bytes for special keys if (c == K_SPECIAL) { + int save_allow_keys = allow_keys; no_mapping++; + allow_keys = 0; // make sure BS is not found c2 = vgetorpeek(true); // no mapping for these chars c = vgetorpeek(true); no_mapping--; + allow_keys = save_allow_keys; if (c2 == KS_MODIFIER) { mod_mask = c; continue; @@ -1601,20 +1627,9 @@ int vgetc(void) c = utf_ptr2char(buf); } - // A modifier was not used for a mapping, apply it to ASCII - // keys. Shift would already have been applied. - if (mod_mask & MOD_MASK_CTRL) { - if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { - c &= 0x1f; - mod_mask &= ~MOD_MASK_CTRL; - if (c == 0) { - c = K_ZERO; - } - } else if (c == '6') { - // CTRL-6 is equivalent to CTRL-^ - c = 0x1e; - mod_mask &= ~MOD_MASK_CTRL; - } + if (vgetc_char == 0) { + vgetc_mod_mask = mod_mask; + vgetc_char = c; } // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed @@ -1731,11 +1746,101 @@ typedef enum { map_result_nomatch, // no matching mapping, get char } map_result_T; +/// Put "string[new_slen]" in typebuf. +/// Remove "slen" bytes. +/// @return FAIL for error, OK otherwise. +static int put_string_in_typebuf(int offset, int slen, char_u *string, int new_slen) +{ + int extra = new_slen - slen; + string[new_slen] = NUL; + if (extra < 0) { + // remove matched chars, taking care of noremap + del_typebuf(-extra, offset); + } else if (extra > 0) { + // insert the extra space we need + if (ins_typebuf(string + slen, REMAP_YES, offset, false, false) == FAIL) { + return FAIL; + } + } + // Careful: del_typebuf() and ins_typebuf() may have reallocated + // typebuf.tb_buf[]! + memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, (size_t)new_slen); + return OK; +} + +/// Check if the bytes at the start of the typeahead buffer are a character used +/// in Insert mode completion. This includes the form with a CTRL modifier. +static bool at_ins_compl_key(void) +{ + char_u *p = typebuf.tb_buf + typebuf.tb_off; + int c = *p; + + if (typebuf.tb_len > 3 && c == K_SPECIAL && p[1] == KS_MODIFIER && (p[2] & MOD_MASK_CTRL)) { + c = p[3] & 0x1f; + } + return (ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c)) + || ((compl_cont_status & CONT_LOCAL) && (c == Ctrl_N || c == Ctrl_P)); +} + +/// Check if typebuf.tb_buf[] contains a modifer plus key that can be changed +/// into just a key, apply that. +/// Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off + "max_offset"]. +/// @return the length of the replaced bytes, 0 if nothing changed, -1 for error. +static int check_simplify_modifier(int max_offset) +{ + for (int offset = 0; offset < max_offset; offset++) { + if (offset + 3 >= typebuf.tb_len) { + break; + } + char_u *tp = typebuf.tb_buf + typebuf.tb_off + offset; + if (tp[0] == K_SPECIAL && tp[1] == KS_MODIFIER) { + // A modifier was not used for a mapping, apply it to ASCII + // keys. Shift would already have been applied. + int modifier = tp[2]; + int c = tp[3]; + int new_c = merge_modifiers(c, &modifier); + + if (new_c != c) { + if (offset == 0) { + // At the start: remember the character and mod_mask before + // merging, in some cases, e.g. at the hit-return prompt, + // they are put back in the typeahead buffer. + vgetc_char = c; + vgetc_mod_mask = tp[2]; + } + char_u new_string[MB_MAXBYTES]; + int len; + if (IS_SPECIAL(new_c)) { + new_string[0] = K_SPECIAL; + new_string[1] = (char_u)K_SECOND(new_c); + new_string[2] = (char_u)K_THIRD(new_c); + len = 3; + } else { + len = utf_char2bytes(new_c, new_string); + } + if (modifier == 0) { + if (put_string_in_typebuf(offset, 4, new_string, len) == FAIL) { + return -1; + } + } else { + tp[2] = (char_u)modifier; + if (put_string_in_typebuf(offset + 3, 1, new_string, len) == FAIL) { + return -1; + } + } + return len; + } + } + } + return 0; +} + /// Handle mappings in the typeahead buffer. /// - When something was mapped, return map_result_retry for recursive mappings. /// - When nothing mapped and typeahead has a character: return map_result_get. /// - When there is no match yet, return map_result_nomatch, need to get more /// typeahead. +/// - On failure (out of memory) return map_result_fail. static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) { mapblock_T *mp = NULL; @@ -1779,9 +1884,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' ')) && State != ASKMORE && State != CONFIRM - && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1)) - || ((compl_cont_status & CONT_LOCAL) - && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) { + && !at_ins_compl_key()) { if (tb_c1 == K_SPECIAL) { nolmaplen = 2; } else { @@ -1889,7 +1992,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) } // If no partly match found, use the longest full match. - if (keylen != KEYLEN_PART_MAP) { + if (keylen != KEYLEN_PART_MAP && mp_match != NULL) { mp = mp_match; keylen = mp_match_len; } @@ -1928,17 +2031,54 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) } } - if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { - // No matching mapping found or found a non-matching mapping that - // matches at least what the matching mapping matched - keylen = 0; - (void)keylen; // suppress clang/dead assignment - // If there was no mapping, use the character from the typeahead - // buffer right here. Otherwise, use the mapping (loop around). - if (mp == NULL) { + if ((mp == NULL || max_mlen > mp_match_len) && keylen != KEYLEN_PART_MAP) { + // When no matching mapping found or found a non-matching mapping that + // matches at least what the matching mapping matched: + // Try to include the modifier into the key when mapping is allowed. + if (no_mapping == 0 || allow_keys != 0) { + if (tb_c1 == K_SPECIAL + && (typebuf.tb_len < 2 + || (typebuf.tb_buf[typebuf.tb_off + 1] == KS_MODIFIER && typebuf.tb_len < 4))) { + // Incomplete modifier sequence: cannot decide whether to simplify yet. + keylen = KEYLEN_PART_KEY; + } else if (keylen == KEYLEN_PART_KEY && !*timedout) { + // If 'pastetoggle' matched partially, don't simplify. + // When the last characters were not typed, don't wait for a typed character to + // complete 'pastetoggle'. + if (typebuf.tb_len == typebuf.tb_maplen) { + keylen = 0; + } + } else { + // Try to include the modifier into the key. + keylen = check_simplify_modifier(max_mlen + 1); + if (keylen < 0) { + // ins_typebuf() failed + return map_result_fail; + } + } + } else { + keylen = 0; + } + if (keylen == 0) { // no simplication has been done + // If there was no mapping at all use the character from the + // typeahead buffer right here. + if (mp == NULL) { + *keylenp = keylen; + return map_result_get; // get character from typeahead + } + } + + if (keylen > 0) { // keys have been simplified *keylenp = keylen; - return map_result_get; // get character from typeahead + return map_result_retry; // try mapping again + } + + if (keylen < 0) { + // Incomplete key sequence: get some more characters. + assert(keylen == KEYLEN_PART_KEY); } else { + assert(mp != NULL); + // When a matching mapping was found use that one. keylen = mp_match_len; } } @@ -2189,6 +2329,10 @@ static int vgetorpeek(bool advance) // try re-mapping. for (;;) { check_end_reg_executing(advance); + // os_breakcheck() can call input_enqueue() + if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) { + ctrl_c_interrupts = false; + } // os_breakcheck() is slow, don't use it too often when // inside a mapping. But call it each time for typed // characters. @@ -2197,6 +2341,7 @@ static int vgetorpeek(bool advance) } else { os_breakcheck(); // check for CTRL-C } + ctrl_c_interrupts = true; int keylen = 0; if (got_int) { // flush all input @@ -2620,7 +2765,7 @@ int inchar(char_u *buf, int maxlen, long wait_time) for (;;) { len = os_inchar(dum, DUM_LEN, 0L, 0, NULL); - if (len == 0 || (len == 1 && dum[0] == 3)) { + if (len == 0 || (len == 1 && dum[0] == Ctrl_C)) { break; } } @@ -2711,11 +2856,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 +2871,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) { @@ -2742,7 +2900,7 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const mapargs->rhs_is_noop = true; } else { replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, - false, true, true, cpo_flags); + REPTERM_DO_LT | REPTERM_NO_SIMPLIFY, 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 +2918,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 +3053,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 +3082,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 +3092,343 @@ 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; + bool keyround1_simplified = keyround == 1 && did_simplify; + 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_simplified && (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) == 0) { + // 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)) { - 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 + if (!has_lhs) { // show all entries + if (!mp->m_simplified) { 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 { - 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); + } + } 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; + } + // In keyround for simplified keys, don't unmap + // a mapping without m_simplified flag. + if (keyround1_simplified && !mp->m_simplified) { + break; } + // 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 + if (!mp->m_simplified) { + showmap(mp, map_table != maphash); + did_it = true; + } + } else if (n != len) { // new entry is ambiguous + mpp = &(mp->m_next); + continue; + } else if (keyround1_simplified && !mp->m_simplified) { + // In keyround for simplified keys, don't replace + // a mapping without m_simplified flag. did_it = true; + break; + } 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 { + // 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); + if (!mp->m_simplified) { + 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 = keyround1_simplified; + 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 + 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) { + if (!keyround1_simplified) { + 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 = keyround1_simplified; // Notice this when porting patch 8.2.0807 + 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: @@ -3317,7 +3516,9 @@ static void mapblock_free(mapblock_T **mpp) mp = *mpp; xfree(mp->m_keys); - NLUA_CLEAR_REF(mp->m_luaref); + if (!mp->m_simplified) { + NLUA_CLEAR_REF(mp->m_luaref); + } XFREE_CLEAR(mp->m_str); XFREE_CLEAR(mp->m_orig_str); XFREE_CLEAR(mp->m_desc); @@ -3600,9 +3801,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 { \ @@ -4675,15 +4875,21 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) // special case to give nicer error message emsg(e_cmdmap_repeated); aborted = true; - } else if (IS_SPECIAL(c1)) { - if (c1 == K_SNR) { - ga_concat(&line_ga, "<SNR>"); + } else if (c1 == K_SNR) { + ga_concat(&line_ga, "<SNR>"); + } else { + if (cmod != 0) { + ga_append(&line_ga, (char)K_SPECIAL); + ga_append(&line_ga, (char)KS_MODIFIER); + ga_append(&line_ga, (char)cmod); + } + if (IS_SPECIAL(c1)) { + ga_append(&line_ga, (char)K_SPECIAL); + ga_append(&line_ga, (char)K_SECOND(c1)); + ga_append(&line_ga, (char)K_THIRD(c1)); } else { - semsg(e_cmdmap_key, get_special_key_name(c1, cmod)); - aborted = true; + ga_append(&line_ga, (char)c1); } - } else { - ga_append(&line_ga, (char)c1); } cmod = 0; |