diff options
Diffstat (limited to 'src')
31 files changed, 857 insertions, 483 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/digraph.c b/src/nvim/digraph.c index 0e148543aa..2a6ccce79e 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1513,8 +1513,10 @@ char_u *get_digraph_for_char(int val_arg) int get_digraph(bool cmdline) { no_mapping++; + allow_keys++; int c = plain_vgetc(); no_mapping--; + allow_keys--; if (c != ESC) { // ESC cancels CTRL-K @@ -1531,8 +1533,10 @@ int get_digraph(bool cmdline) add_to_showcmd(c); } no_mapping++; + allow_keys++; int cc = plain_vgetc(); no_mapping--; + allow_keys--; if (cc != ESC) { // ESC cancels CTRL-K diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 48436f1115..251f55c6e7 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -762,8 +762,10 @@ static int insert_execute(VimState *state, int key) // may need to redraw when no more chars available now ins_redraw(false); no_mapping++; + allow_keys++; s->c = plain_vgetc(); no_mapping--; + allow_keys--; if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) { // it's something else vungetc(s->c); @@ -1587,7 +1589,8 @@ static void ins_ctrl_v(void) add_to_showcmd_c(Ctrl_V); - c = get_literal(); + // Do not include modifiers into the key for CTRL-SHIFT-V. + c = get_literal(mod_mask & MOD_MASK_SHIFT); if (did_putchar) { // when the line fits in 'columns' the '^' is at the start of the next // line and will not removed by the redraw @@ -5612,13 +5615,13 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) return m; } -/* - * Next character is interpreted literally. - * A one, two or three digit decimal number is interpreted as its byte value. - * If one or two digits are entered, the next character is given to vungetc(). - * For Unicode a character > 255 may be returned. - */ -int get_literal(void) +/// Next character is interpreted literally. +/// A one, two or three digit decimal number is interpreted as its byte value. +/// If one or two digits are entered, the next character is given to vungetc(). +/// For Unicode a character > 255 may be returned. +/// +/// @param no_simplify do not include modifiers into the key +int get_literal(bool no_simplify) { int cc; int nc; @@ -5636,6 +5639,9 @@ int get_literal(void) i = 0; for (;;) { nc = plain_vgetc(); + if (!no_simplify) { + nc = merge_modifiers(nc, &mod_mask); + } if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { // A character with non-Shift modifiers should not be a valid // character for i_CTRL-V_digit. @@ -7811,11 +7817,10 @@ static void ins_reg(void) } - /* - * Don't map the register name. This also prevents the mode message to be - * deleted when ESC is hit. - */ - ++no_mapping; + // Don't map the register name. This also prevents the mode message to be + // deleted when ESC is hit. + no_mapping++; + allow_keys++; regname = plain_vgetc(); LANGMAP_ADJUST(regname, TRUE); if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) { @@ -7825,7 +7830,8 @@ static void ins_reg(void) regname = plain_vgetc(); LANGMAP_ADJUST(regname, TRUE); } - --no_mapping; + no_mapping--; + allow_keys--; // Don't call u_sync() while typing the expression or giving an error // message for it. Only call it explicitly. @@ -7893,13 +7899,13 @@ static void ins_ctrl_g(void) // Right after CTRL-X the cursor will be after the ruler. setcursor(); - /* - * Don't map the second key. This also prevents the mode message to be - * deleted when ESC is hit. - */ - ++no_mapping; + // Don't map the second key. This also prevents the mode message to be + // deleted when ESC is hit. + no_mapping++; + allow_keys++; c = plain_vgetc(); - --no_mapping; + no_mapping--; + allow_keys--; switch (c) { // CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col case K_UP: @@ -9233,8 +9239,10 @@ static int ins_digraph(void) // don't map the digraph chars. This also prevents the // mode message to be deleted when ESC is hit no_mapping++; + allow_keys++; c = plain_vgetc(); no_mapping--; + allow_keys--; if (did_putchar) { // when the line fits in 'columns' the '?' is at the start of the next // line and will not be removed by the redraw @@ -9260,8 +9268,10 @@ static int ins_digraph(void) add_to_showcmd_c(c); } no_mapping++; + allow_keys++; cc = plain_vgetc(); no_mapping--; + allow_keys--; if (did_putchar) { // when the line fits in 'columns' the '?' is at the start of the // next line and will not be removed by a redraw diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e192a6de6b..f8e2f913bf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4964,8 +4964,13 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) break; // Special key, e.g.: "\<C-W>" - case '<': - extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true); + case '<': { + int flags = FSK_KEYCODE | FSK_IN_STRING; + + if (p[1] != '*') { + flags |= FSK_SIMPLIFY; + } + extra = trans_special((const char_u **)&p, STRLEN(p), name, flags, NULL); if (extra != 0) { name += extra; if (name >= rettv->vval.v_string + len) { @@ -4973,6 +4978,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) } break; } + } FALLTHROUGH; default: diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f97ceedeb7..dfdabd511e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2975,6 +2975,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) bool error = false; no_mapping++; + allow_keys++; for (;;) { // Position the cursor. Needed after a message that ends in a space, // or if event processing caused a redraw. @@ -3012,6 +3013,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) break; } no_mapping--; + allow_keys--; set_vim_var_nr(VV_MOUSE_WIN, 0); set_vim_var_nr(VV_MOUSE_WINID, 0); @@ -5648,6 +5650,8 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { char_u *keys_buf = NULL; + char_u *alt_keys_buf = NULL; + bool did_simplify = false; char_u *rhs; LuaRef rhs_lua; int mode; @@ -5655,6 +5659,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) int get_dict = FALSE; mapblock_T *mp; int buffer_local; + int flags = REPTERM_FROM_PART | REPTERM_DO_LT; // Return empty string for failure. rettv->v_type = VAR_STRING; @@ -5684,10 +5689,16 @@ 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, - CPO_TO_CPO_FLAGS); - rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); - xfree(keys_buf); + char_u *keys_simplified + = replace_termcodes(keys, STRLEN(keys), &keys_buf, flags, &did_simplify, CPO_TO_CPO_FLAGS); + rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + if (did_simplify) { + // When the lhs is being simplified the not-simplified keys are + // preferred for printing, like in do_map(). + (void)replace_termcodes(keys, STRLEN(keys), &alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL, + CPO_TO_CPO_FLAGS); + rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua); + } if (!get_dict) { // Return a string. @@ -5710,6 +5721,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true); } } + + xfree(keys_buf); + xfree(alt_keys_buf); } /// luaeval() function implementation diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 7564b9326b..f059175c88 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3951,8 +3951,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle RedrawingDisabled = temp; no_mapping++; // don't map this key + allow_keys++; // allow special keys typed = plain_vgetc(); no_mapping--; + allow_keys--; // clear the question msg_didout = false; // don't scroll up 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/ex_getln.c b/src/nvim/ex_getln.c index b7d75855d6..35fd900391 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1335,8 +1335,10 @@ static int command_line_execute(VimState *state, int key) // mode when 'insertmode' is set, CTRL-\ e prompts for an expression. if (s->c == Ctrl_BSL) { no_mapping++; + allow_keys++; s->c = plain_vgetc(); no_mapping--; + allow_keys--; // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N @@ -1889,6 +1891,7 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_R: { // insert register putcmdline('"', true); no_mapping++; + allow_keys++; int i = s->c = plain_vgetc(); // CTRL-R <char> if (i == Ctrl_O) { i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R @@ -1897,7 +1900,8 @@ static int command_line_handle_key(CommandLineState *s) if (i == Ctrl_R) { s->c = plain_vgetc(); // CTRL-R CTRL-R <char> } - --no_mapping; + no_mapping--; + allow_keys--; // Insert the result of an expression. // Need to save the current command line, to be able to enter // a new one... @@ -2208,7 +2212,11 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_Q: s->ignore_drag_release = true; putcmdline('^', true); - s->c = get_literal(); // get next (two) character(s) + + // Get next (two) characters. + // Do not include modifiers into the key for CTRL-SHIFT-V. + s->c = get_literal(mod_mask & MOD_MASK_SHIFT); + s->do_abbr = false; // don't do abbreviation now ccline.special_char = NUL; // may need to remove ^ when composing char was typed diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index f2df7b49fd..58daa9631a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -872,10 +872,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 +899,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; @@ -1442,6 +1442,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 +1500,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 +1511,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 +1630,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 +1749,87 @@ 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 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; @@ -1889,7 +1983,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 +2022,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; } } @@ -2620,7 +2751,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 +2842,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 +2857,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 +2885,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 +2904,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 +3039,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 +3068,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 +3078,341 @@ 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); + 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: @@ -3600,9 +3783,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/globals.h b/src/nvim/globals.h index e07a0e22ca..5983f18bb5 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -129,6 +129,9 @@ typedef off_t off_T; // held down based on the MOD_MASK_* symbols that are read first. EXTERN int mod_mask INIT(= 0); // current key modifiers +// The value of "mod_mask" and the unmodified character before calling merge_modifiers(). +EXTERN int vgetc_mod_mask INIT(= 0); +EXTERN int vgetc_char INIT(= 0); // Cmdline_row is the row where the command line starts, just below the // last window. @@ -649,6 +652,7 @@ EXTERN int reg_recorded INIT(= 0); // last recorded register or zero EXTERN int no_mapping INIT(= false); // currently no mapping allowed EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed +EXTERN int allow_keys INIT(= false); // allow key codes when no_mapping is set EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating // an expression. diff --git a/src/nvim/input.c b/src/nvim/input.c index ff6b559710..6827dcae87 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -42,6 +42,7 @@ int ask_yesno(const char *const str, const bool direct) State = CONFIRM; // Mouse behaves like with :confirm. setmouse(); // Disable mouse in xterm. no_mapping++; + allow_keys++; // no mapping here, but recognize keys int r = ' '; while (r != 'y' && r != 'n') { @@ -62,6 +63,7 @@ int ask_yesno(const char *const str, const bool direct) State = save_State; setmouse(); no_mapping--; + allow_keys--; return r; } @@ -172,6 +174,7 @@ int get_number(int colon, int *mouse_used) } no_mapping++; + allow_keys++; // no mapping here, but recognize keys for (;;) { ui_cursor_goto(msg_row, msg_col); c = safe_vgetc(); @@ -205,6 +208,7 @@ int get_number(int colon, int *mouse_used) } } no_mapping--; + allow_keys--; return n; } diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 3fdc140ebd..db51237771 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -568,23 +568,21 @@ char_u *get_special_key_name(int c, int modifiers) /// @param[in] src_len Length of the srcp. /// @param[out] dst Location where translation result will be kept. It must // 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] flags FSK_ values +/// @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 int flags, 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, flags, did_simplify); if (key == 0) { return 0; } - return special_to_buf(key, modifiers, keycode, dst); + return special_to_buf(key, modifiers, flags & FSK_KEYCODE, dst); } /// Put the character sequence for "key" with "modifiers" into "dst" and return @@ -623,20 +621,20 @@ unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst) /// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. /// @param[in] src_len srcp length. /// @param[out] modp Location where information about modifiers is saved. -/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. -/// @param[in] keep_x_key Don鈥檛 translate xHome to Home key. -/// @param[in] in_string In string, double quote is escaped +/// @param[in] flags FSK_ values +/// @param[out] did_simplify FSK_SIMPLIFY and 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 int flags, 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; const char_u *src; const char_u *bp; const char_u *const end = *srcp + src_len - 1; + const bool in_string = flags & FSK_IN_STRING; int modifiers; int bit; int key; @@ -651,6 +649,9 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (src[0] != '<') { return 0; } + if (src[1] == '*') { // <*xxx>: do not simplify + src++; + } // Find end of modifier list last_dash = src; @@ -662,7 +663,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // Anything accepted, like <C-?>. // <C-"> or <M-"> are not special in strings as " is // the string delimiter. With a backslash it works: <M-\"> - if (end - bp > l && !(in_string && bp[1] == '"') && bp[l+1] == '>') { + if (end - bp > l && !(in_string && bp[1] == '"') && bp[l + 1] == '>') { bp += l; } else if (end - bp > 2 && in_string && bp[1] == '\\' && bp[2] == '"' && bp[3] == '>') { @@ -723,7 +724,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, key = utf_ptr2char(last_dash + off); } else { key = get_special_key_code(last_dash + off); - if (!keep_x_key) { + if (!(flags & FSK_KEEP_X_KEY)) { key = handle_x_keys(key); } } @@ -736,7 +737,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // includes the modifier. key = simplify_key(key, &modifiers); - if (!keycode) { + if (!(flags & FSK_KEYCODE)) { // don't want keycode, use single byte code if (key == K_BS) { key = BS; @@ -748,7 +749,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, flags & FSK_SIMPLIFY, did_simplify); } *modp = modifiers; @@ -762,7 +763,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 +777,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 == NUL) { // <C-@> is <Nul> + key = K_ZERO; + } + if (did_simplify != NULL) { + *did_simplify = true; } } @@ -853,34 +861,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 +893,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 +906,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 +921,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 +941,16 @@ 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, + FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY), + 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 +990,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/keymap.h b/src/nvim/keymap.h index 9dff8ba333..9febd472f9 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -507,6 +507,22 @@ enum key_extra { ? 0 \ : FLAG_CPO_BSLASH) +// Flags for replace_termcodes() +enum { + REPTERM_FROM_PART = 1, + REPTERM_DO_LT = 2, + REPTERM_NO_SPECIAL = 4, + REPTERM_NO_SIMPLIFY = 8, +}; + +// Flags for find_special_key() +enum { + FSK_KEYCODE = 0x01, ///< prefer key code, e.g. K_DEL in place of DEL + FSK_KEEP_X_KEY = 0x02, ///< don鈥檛 translate xHome to Home key + FSK_IN_STRING = 0x04, ///< in string, double quote is escaped + FSK_SIMPLIFY = 0x08, ///< simplify <C-H>, etc. +}; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "keymap.h.generated.h" #endif 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/message.c b/src/nvim/message.c index 6708001495..3660dc20af 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1146,6 +1146,7 @@ void wait_return(int redraw) // Don't do mappings here, we put the character back in the // typeahead buffer. no_mapping++; + allow_keys++; // Temporarily disable Recording. If Recording is active, the // character will be recorded later, since it will be added to the @@ -1159,6 +1160,7 @@ void wait_return(int redraw) got_int = false; } no_mapping--; + allow_keys--; reg_recording = save_reg_recording; scriptout = save_scriptout; @@ -1214,7 +1216,7 @@ void wait_return(int redraw) } else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. - ins_char_typebuf(c, mod_mask); + ins_char_typebuf(vgetc_char, vgetc_mod_mask); do_redraw = true; // need a redraw even though there is // typeahead } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ed5f13d00a..c5d7e603cd 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -638,6 +638,7 @@ static void normal_get_additional_char(NormalState *s) int lang; // getting a text character no_mapping++; + allow_keys++; // no mapping for nchar, but allow key codes // Don't generate a CursorHold event here, most commands can't handle // it, e.g., nv_replace(), nv_csearch(). did_cursorhold = true; @@ -676,6 +677,7 @@ static void normal_get_additional_char(NormalState *s) if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) { // Allow mappings defined with ":lmap". no_mapping--; + allow_keys--; if (repl) { State = LREPLACE; } else { @@ -689,6 +691,7 @@ static void normal_get_additional_char(NormalState *s) if (langmap_active) { // Undo the decrement done above no_mapping++; + allow_keys++; } State = NORMAL_BUSY; s->need_flushbuf |= add_to_showcmd(*cp); @@ -769,6 +772,7 @@ static void normal_get_additional_char(NormalState *s) no_mapping++; } no_mapping--; + allow_keys--; } static void normal_invert_horizontal(NormalState *s) @@ -826,6 +830,7 @@ static bool normal_get_command_count(NormalState *s) if (s->ctrl_w) { no_mapping++; + allow_keys++; // no mapping for nchar, but keys } no_zero_mapping++; // don't map zero here @@ -834,6 +839,7 @@ static bool normal_get_command_count(NormalState *s) no_zero_mapping--; if (s->ctrl_w) { no_mapping--; + allow_keys--; } s->need_flushbuf |= add_to_showcmd(s->c); } @@ -844,9 +850,11 @@ static bool normal_get_command_count(NormalState *s) s->ca.opcount = s->ca.count0; // remember first count s->ca.count0 = 0; no_mapping++; + allow_keys++; // no mapping for nchar, but keys s->c = plain_vgetc(); // get next character LANGMAP_ADJUST(s->c, true); no_mapping--; + allow_keys--; s->need_flushbuf |= add_to_showcmd(s->c); return true; } @@ -995,7 +1003,7 @@ static int normal_execute(VimState *state, int key) // restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - int len = ins_char_typebuf(s->c, mod_mask); + int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask); // When recording and gotchars() was called the character will be // recorded again, remove the previous recording. @@ -3407,9 +3415,11 @@ static void nv_zet(cmdarg_T *cap) n = nchar - '0'; for (;;) { no_mapping++; + allow_keys++; // no mapping for nchar, but allow key codes nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); no_mapping--; + allow_keys--; (void)add_to_showcmd(nchar); if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; @@ -3785,9 +3795,11 @@ dozet: case 'u': // "zug" and "zuw": undo "zg" and "zw" no_mapping++; + allow_keys++; // no mapping for nchar, but allow key codes nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); no_mapping--; + allow_keys--; (void)add_to_showcmd(nchar); if (vim_strchr((char_u *)"gGwW", nchar) == NULL) { clearopbeep(cap->oap); @@ -5154,7 +5166,7 @@ static void nv_replace(cmdarg_T *cap) // get another character if (cap->nchar == Ctrl_V) { had_ctrl_v = Ctrl_V; - cap->nchar = get_literal(); + cap->nchar = get_literal(false); // Don't redo a multibyte character with CTRL-V. if (cap->nchar > DEL) { had_ctrl_v = NUL; @@ -5369,7 +5381,7 @@ static void nv_vreplace(cmdarg_T *cap) emsg(_(e_modifiable)); } else { if (cap->extra_char == Ctrl_V) { // get another character - cap->extra_char = get_literal(); + cap->extra_char = get_literal(false); } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); diff --git a/src/nvim/option.c b/src/nvim/option.c index 49c76d2452..068b779eb6 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,8 @@ 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, + FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY, 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..f68a1c08c8 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -238,9 +238,9 @@ size_t input_enqueue(String keys) // but since the keys are UTF-8, so the first byte cannot be // K_SPECIAL(0x80). uint8_t buf[19] = { 0 }; + // Do not simplify the keys here. Simplification will be done later. 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, FSK_KEYCODE, NULL); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); @@ -488,7 +488,12 @@ static void process_interrupts(void) size_t consume_count = 0; RBUFFER_EACH_REVERSE(input_buffer, c, i) { - if ((uint8_t)c == Ctrl_C) { + if ((uint8_t)c == Ctrl_C + || ((uint8_t)c == 'C' && i >= 3 + && (uint8_t)(*rbuffer_get(input_buffer, i - 3)) == K_SPECIAL + && (uint8_t)(*rbuffer_get(input_buffer, i - 2)) == KS_MODIFIER + && (uint8_t)(*rbuffer_get(input_buffer, i - 1)) == MOD_MASK_CTRL)) { + *rbuffer_get(input_buffer, i) = Ctrl_C; got_int = true; consume_count = i; break; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index fd870361c7..5f536c0b7a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1348,7 +1348,7 @@ static bool send_mouse_event(Terminal *term, int c) } end: - ins_char_typebuf(c, mod_mask); + ins_char_typebuf(vgetc_char, vgetc_mod_mask); return true; } diff --git a/src/nvim/testdir/test_backspace_opt.vim b/src/nvim/testdir/test_backspace_opt.vim index 11459991ea..59e94d2898 100644 --- a/src/nvim/testdir/test_backspace_opt.vim +++ b/src/nvim/testdir/test_backspace_opt.vim @@ -76,7 +76,7 @@ func Test_backspace_ctrl_u() set cpo-=< inoremap <c-u> <left><c-u> - exe "normal Avim3\<C-U>\<Esc>\<CR>" + exe "normal Avim3\<*C-U>\<Esc>\<CR>" iunmap <c-u> exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>" @@ -86,7 +86,7 @@ func Test_backspace_ctrl_u() exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>" inoremap <c-u> <left><c-u> - exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>" + exe "normal A vim7\<*C-U>\<*C-U>\<Esc>\<CR>" call assert_equal([ \ "1 this shouldn't be deleted", diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 12febfeb93..95eccde35c 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -185,7 +185,7 @@ func Test_let_register() call Assert_reg('"', 'v', "abc", "['abc']", "abc", "['abc']") let @" = "abc\n" call Assert_reg('"', 'V', "abc\n", "['abc']", "abc\n", "['abc']") - let @" = "abc\r" + let @" = "abc\<C-m>" call Assert_reg('"', 'V', "abc\r\n", "['abc\r']", "abc\r\n", "['abc\r']") let @= = '"abc"' call Assert_reg('=', 'v', "abc", "['abc']", '"abc"', "['\"abc\"']") diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index a9500f8f77..6bb8d82021 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -62,7 +62,7 @@ func Test_map_ctrl_c_insert() inoremap <c-c> <ctrl-c> cnoremap <c-c> dummy cunmap <c-c> - call feedkeys("GoTEST2: CTRL-C |\<C-C>A|\<Esc>", "xt") + call feedkeys("GoTEST2: CTRL-C |\<*C-C>A|\<Esc>", "xt") call assert_equal('TEST2: CTRL-C |<ctrl-c>A|', getline('$')) unmap! <c-c> set nomodified @@ -71,7 +71,7 @@ endfunc func Test_map_ctrl_c_visual() " mapping of ctrl-c in Visual mode vnoremap <c-c> :<C-u>$put ='vmap works' - call feedkeys("GV\<C-C>\<CR>", "xt") + call feedkeys("GV\<*C-C>\<CR>", "xt") call assert_equal('vmap works', getline('$')) vunmap <c-c> set nomodified @@ -221,7 +221,7 @@ endfunc func Test_map_meta_quotes() imap <M-"> foo - call feedkeys("Go-\<M-\">-\<Esc>", "xt") + call feedkeys("Go-\<*M-\">-\<Esc>", "xt") call assert_equal("-foo-", getline('$')) set nomodified iunmap <M-"> @@ -429,6 +429,28 @@ func Test_error_in_map_expr() exe buf .. 'bwipe!' endfunc +func Test_list_mappings() + " Remove default mappings + imapclear + + " reset 'isident' to check it isn't used + set isident= + inoremap <C-m> CtrlM + inoremap <A-S> AltS + inoremap <S-/> ShiftSlash + set isident& + call assert_equal([ + \ 'i <S-/> * ShiftSlash', + \ 'i <M-S> * AltS', + \ 'i <C-M> * CtrlM', + \], execute('imap')->trim()->split("\n")) + iunmap <C-M> + iunmap <A-S> + call assert_equal(['i <S-/> * ShiftSlash'], execute('imap')->trim()->split("\n")) + iunmap <S-/> + call assert_equal(['No mapping found'], execute('imap')->trim()->split("\n")) +endfunc + func Test_expr_map_gets_cursor() new call setline(1, ['one', 'some w!rd']) @@ -739,4 +761,20 @@ func Test_mouse_drag_insert_map() set mouse& endfunc +func Test_unmap_simplifiable() + map <C-I> foo + map <Tab> bar + call assert_equal('foo', maparg('<C-I>')) + call assert_equal('bar', maparg('<Tab>')) + unmap <C-I> + call assert_equal('', maparg('<C-I>')) + call assert_equal('bar', maparg('<Tab>')) + unmap <Tab> + + map <C-I> foo + unmap <Tab> + " This should not error + unmap <C-I> +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 82c4cc128b..8be0c79499 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -112,6 +112,14 @@ func Test_echospace() set ruler& showcmd& endfunc +func Test_mapping_at_hit_return_prompt() + nnoremap <C-B> :echo "hit ctrl-b"<CR> + call feedkeys(":ls\<CR>", "xt") + call feedkeys("\<*C-B>", "xt") + call assert_match('hit ctrl-b', Screenline(&lines - 1)) + nunmap <C-B> +endfunc + func Test_quit_long_message() CheckScreendump diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim index b0d76a15e2..db16f057c8 100644 --- a/src/nvim/testdir/test_regex_char_classes.vim +++ b/src/nvim/testdir/test_regex_char_classes.vim @@ -66,22 +66,22 @@ func Test_regex_char_classes() let save_enc = &encoding set encoding=utf-8 - let input = "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱" + let input = "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱" " Format is [cmd_to_run, expected_output] let tests = [ \ [':s/\%#=0\d//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\d//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\d//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[0-9]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[0-9]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[0-9]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\D//g', \ "0123456789"], \ [':s/\%#=1\D//g', @@ -95,17 +95,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9]//g', \ "0123456789"], \ [':s/\%#=0\o//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\o//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\o//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[0-7]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[0-7]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[0-7]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\O//g', \ "01234567"], \ [':s/\%#=1\O//g', @@ -119,17 +119,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-7]//g', \ "01234567"], \ [':s/\%#=0\x//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\x//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\x//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[0-9A-Fa-f]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[0-9A-Fa-f]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[0-9A-Fa-f]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\X//g', \ "0123456789ABCDEFabcdef"], \ [':s/\%#=1\X//g', @@ -143,17 +143,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9A-Fa-f]//g', \ "0123456789ABCDEFabcdef"], \ [':s/\%#=0\w//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\w//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\w//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[0-9A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[0-9A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[0-9A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\W//g', \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=1\W//g', @@ -167,17 +167,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9A-Za-z_]//g', \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=0\h//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\h//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\h//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[A-Za-z_]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\H//g', \ "ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=1\H//g', @@ -191,17 +191,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^A-Za-z_]//g', \ "ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=0\a//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\a//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\a//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[A-Za-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[A-Za-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[A-Za-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\A//g', \ "ABCDEFGHIXYZabcdefghiwxyz"], \ [':s/\%#=1\A//g', @@ -215,17 +215,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^A-Za-z]//g', \ "ABCDEFGHIXYZabcdefghiwxyz"], \ [':s/\%#=0\l//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\l//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\l//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[a-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[a-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[a-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\L//g', \ "abcdefghiwxyz"], \ [':s/\%#=1\L//g', @@ -239,17 +239,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^a-z]//g', \ "abcdefghiwxyz"], \ [':s/\%#=0\u//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1\u//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2\u//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[A-Z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[A-Z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[A-Z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0\U//g', \ "ABCDEFGHIXYZ"], \ [':s/\%#=1\U//g', @@ -269,11 +269,11 @@ func Test_regex_char_classes() \ [':s/\%#=2\%' . line('.') . 'l^\t...//g', \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[0-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=1[0-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=2[0-z]//g', - \ "\t\<C-L>\r !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], + \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"], \ [':s/\%#=0[^0-z]//g', \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"], \ [':s/\%#=1[^0-z]//g', @@ -293,3 +293,5 @@ func Test_regex_char_classes() enew! close endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index 86fd0147a5..7a15e9a6f1 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -177,9 +177,9 @@ func Test_sub_cmd_1() \ ['I', 's/I/\lII/', ['iI']], \ ['J', 's/J/\LJ\EJ/', ['jJ']], \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\<C-V>\r/", ["l\<C-V>", 'l']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\<C-V>\r/", ["n\<C-V>", 'n']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], \ ['oOo', 's/O/\n/', ["o\no"]], \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], \ ['qQq', 's/Q/\t/', ["q\tq"]], @@ -208,9 +208,9 @@ func Test_sub_cmd_2() \ ['I', 's/I/\lII/', ['iI']], \ ['J', 's/J/\LJ\EJ/', ['jJ']], \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\<C-V>\r/", ["l\<C-V>", 'l']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\<C-V>\r/", ["n\<C-V>", 'n']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], \ ['oOo', 's/O/\n/', ["o\no"]], \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], \ ['qQq', 's/Q/\t/', ["q\tq"]], @@ -230,9 +230,9 @@ func Test_sub_cmd_3() " List entry format: [input, cmd, output] let tests = [['aAa', "s/A/\\='\\'/", ['a\a']], \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']], - \ ['cCc', "s/C/\\='\<C-V>\r'/", ["c\<C-V>", 'c']], - \ ['dDd', "s/D/\\='\\\<C-V>\r'/", ["d\\\<C-V>", 'd']], - \ ['eEe', "s/E/\\='\\\\\<C-V>\r'/", ["e\\\\\<C-V>", 'e']], + \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']], + \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']], + \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']], \ ['fFf', "s/F/\\='\r'/", ['f', 'f']], \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']], \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']], @@ -254,11 +254,11 @@ func Test_sub_cmd_4() \ ['a\a']], \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/", \ ['b\b']], - \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\r', '')/", + \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/", \ ["c\<C-V>", 'c']], - \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\r', '')/", + \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/", \ ["d\<C-V>", 'd']], - \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\r', '')/", + \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/", \ ["e\\\<C-V>", 'e']], \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/", \ ['f', 'f']], @@ -316,7 +316,7 @@ func Test_sub_cmd_7() set cpo& " List entry format: [input, cmd, output] - let tests = [ ["A\<C-V>\rA", 's/A./\=submatch(0)/', ['A', 'A']], + let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']], \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']], \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]], \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]], @@ -467,11 +467,11 @@ func Test_sub_replace_1() call assert_equal('iI', substitute('I', 'I', '\lII', '')) call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', '')) call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', '')) - call assert_equal("l\<C-V>\rl", - \ substitute('lLl', 'L', "\<C-V>\r", '')) - call assert_equal("m\rm", substitute('mMm', 'M', '\r', '')) - call assert_equal("n\<C-V>\rn", - \ substitute('nNn', 'N', "\\\<C-V>\r", '')) + call assert_equal("l\<C-V>\<C-M>l", + \ substitute('lLl', 'L', "\<C-V>\<C-M>", '')) + call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', '')) + call assert_equal("n\<C-V>\<C-M>n", + \ substitute('nNn', 'N', "\\\<C-V>\<C-M>", '')) call assert_equal("o\no", substitute('oOo', 'O', '\n', '')) call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', '')) call assert_equal("q\tq", substitute('qQq', 'Q', '\t', '')) @@ -480,7 +480,7 @@ func Test_sub_replace_1() call assert_equal("u\nu", substitute('uUu', 'U', "\n", '')) call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", '')) call assert_equal("w\\w", substitute('wWw', 'W', "\\", '')) - call assert_equal("x\rx", substitute('xXx', 'X', "\r", '')) + call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", '')) call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', '')) call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', '')) endfunc @@ -500,17 +500,17 @@ func Test_sub_replace_2() call assert_equal('iI', substitute('I', 'I', '\lII', '')) call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', '')) call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', '')) - call assert_equal("l\<C-V>\rl", - \ substitute('lLl', 'L', "\<C-V>\r", '')) - call assert_equal("m\rm", substitute('mMm', 'M', '\r', '')) - call assert_equal("n\<C-V>\rn", - \ substitute('nNn', 'N', "\\\<C-V>\r", '')) + call assert_equal("l\<C-V>\<C-M>l", + \ substitute('lLl', 'L', "\<C-V>\<C-M>", '')) + call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', '')) + call assert_equal("n\<C-V>\<C-M>n", + \ substitute('nNn', 'N', "\\\<C-V>\<C-M>", '')) call assert_equal("o\no", substitute('oOo', 'O', '\n', '')) call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', '')) call assert_equal("q\tq", substitute('qQq', 'Q', '\t', '')) call assert_equal('r\r', substitute('rRr', 'R', '\\', '')) call assert_equal('scs', substitute('sSs', 'S', '\c', '')) - call assert_equal("t\rt", substitute('tTt', 'T', "\r", '')) + call assert_equal("t\<C-M>t", substitute('tTt', 'T', "\r", '')) call assert_equal("u\nu", substitute('uUu', 'U', "\n", '')) call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", '')) call assert_equal('w\w', substitute('wWw', 'W', "\\", '')) @@ -528,7 +528,7 @@ func Test_sub_replace_3() call assert_equal("e\\\\\re", substitute('eEe', 'E', "\\=\"\\\\\\\\\r\"", '')) call assert_equal('f\rf', substitute('fFf', 'F', '\="\\r"', '')) call assert_equal('j\nj', substitute('jJj', 'J', '\="\\n"', '')) - call assert_equal("k\rk", substitute('kKk', 'K', '\="\r"', '')) + call assert_equal("k\<C-M>k", substitute('kKk', 'K', '\="\r"', '')) call assert_equal("l\nl", substitute('lLl', 'L', '\="\n"', '')) endfunc @@ -540,10 +540,10 @@ func Test_sub_replace_4() \ '\=substitute(submatch(0), ".", "\\", "")', '')) call assert_equal('b\b', substitute('bBb', 'B', \ '\=substitute(submatch(0), ".", "\\\\", "")', '')) - call assert_equal("c\<C-V>\rc", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\<C-V>\r", "")', '')) - call assert_equal("d\<C-V>\rd", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\<C-V>\r", "")', '')) - call assert_equal("e\\\<C-V>\re", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\<C-V>\r", "")', '')) - call assert_equal("f\rf", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', '')) + call assert_equal("c\<C-V>\<C-M>c", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\<C-V>\<C-M>", "")', '')) + call assert_equal("d\<C-V>\<C-M>d", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\<C-V>\<C-M>", "")', '')) + call assert_equal("e\\\<C-V>\<C-M>e", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-M>", "")', '')) + call assert_equal("f\<C-M>f", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', '')) call assert_equal("j\nj", substitute('jJj', 'J', '\=substitute(submatch(0), ".", "\\n", "")', '')) call assert_equal("k\rk", substitute('kKk', 'K', '\=substitute(submatch(0), ".", "\r", "")', '')) call assert_equal("l\nl", substitute('lLl', 'L', '\=substitute(submatch(0), ".", "\n", "")', '')) diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim new file mode 100644 index 0000000000..f3b10a922e --- /dev/null +++ b/src/nvim/testdir/test_termcodes.vim @@ -0,0 +1,26 @@ + +func Test_simplify_ctrl_at() + " feeding unsimplified CTRL-@ should still trigger i_CTRL-@ + call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt') + call assert_equal('foofo', getline(1)) + bw! +endfunc + +func Test_simplify_noremap() + call feedkeys("i\<*C-M>", 'nx') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + bw! +endfunc + +func Test_simplify_timedout() + inoremap <C-M>a b + call feedkeys("i\<*C-M>", 'xt') + call assert_equal('', getline(1)) + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + iunmap <C-M>a + bw! +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index b8b0a38f44..b0fbc586b2 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -1817,9 +1817,13 @@ 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)); + int flags = FSK_KEYCODE | FSK_IN_STRING; + + if (p[1] != '*') { + flags |= FSK_SIMPLIFY; + } + const size_t special_len = trans_special((const char_u **)&p, (size_t)(e - p), + (char_u *)v_p, flags, NULL); if (special_len != 0) { v_p += special_len; } else { diff --git a/src/nvim/window.c b/src/nvim/window.c index f68cfe4c9c..50921c8302 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -534,11 +534,13 @@ wingotofile: case Ctrl_G: CHECK_CMDWIN; no_mapping++; + allow_keys++; // no mapping for xchar, but allow key codes if (xchar == NUL) { xchar = plain_vgetc(); } LANGMAP_ADJUST(xchar, true); no_mapping--; + allow_keys--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': |