Merge pull request #17932 from zeertzjq/vim-8.1.2145

vim-patch:8.1.{2145,2159,2165,2167,2333,2346,2350},8.2.{0839,0851,0855,0867,0916,0919,2084,2728,3595,4504,4819,4824,4827,4828,4829,4833,4837}
This commit is contained in:
zeertzjq 2022-04-29 17:13:28 +08:00 committed by GitHub
commit 35a7b0f9b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1154 additions and 601 deletions

View File

@ -67,12 +67,19 @@ CTRL-V Insert next non-digit literally. Up to three digits form the
decimal value of a single byte. The non-digit and the three
digits are not considered for mapping. This works the same
way as in Insert mode (see above, |i_CTRL-V|).
For special keys, the CTRL modifier may be included into the
key to produce a control character. If there is no control
character for the key then its |key-notation| is inserted.
Note: Under Windows CTRL-V is often mapped to paste text.
Use CTRL-Q instead then.
*c_CTRL-Q*
CTRL-Q Same as CTRL-V. But with some terminals it is used for
control flow, it doesn't work then.
CTRL-SHIFT-V *c_CTRL-SHIFT-V* *c_CTRL-SHIFT-Q*
CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
modifier into the key.
*c_<Left>* *c_Left*
<Left> cursor left
*c_<Right>* *c_Right*

View File

@ -1325,6 +1325,9 @@ A string constant accepts these special characters:
To use the double quote character it must be escaped: "<M-\">".
Don't use <Char-xxxx> to get a UTF-8 character, use \uxxxx as
mentioned above.
\<*xxx> Like \<xxx> but prepends a modifier instead of including it in the
character. E.g. "\<C-w>" is one character 0x17 while "\<*C-w>" is four
bytes: 3 for the CTRL modifier and then character "W".
Note that "\xff" is stored as the byte 255, which may be invalid in some
encodings. Use "\u00ff" to store character 255 correctly as UTF-8.

View File

@ -192,12 +192,14 @@ CTRL-D Delete one shiftwidth of indent at the start of the current
label.
*i_CTRL-V*
CTRL-V Insert next non-digit literally. For special keys, the
terminal code is inserted. It's also possible to enter the
decimal, octal or hexadecimal value of a character
CTRL-V Insert next non-digit literally. It's also possible to enter
the decimal, octal or hexadecimal value of a character
|i_CTRL-V_digit|.
The characters typed right after CTRL-V are not considered for
mapping.
For special keys, the CTRL modifier may be included into the
key to produce a control character. If there is no control
character for the key then its |key-notation| is inserted.
Note: When CTRL-V is mapped (e.g., to paste text) you can
often use CTRL-Q instead |i_CTRL-Q|.
@ -206,6 +208,10 @@ CTRL-Q Same as CTRL-V.
Note: Some terminal connections may eat CTRL-Q, it doesn't
work then. It does work in the GUI.
CTRL-SHIFT-V *i_CTRL-SHIFT-V* *i_CTRL-SHIFT-Q*
CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
modifier into the key.
CTRL-X Enter CTRL-X mode. This is a sub-mode where commands can
be given to complete words or scroll the window. See
|i_CTRL-X| and |ins-completion|.

View File

@ -4483,8 +4483,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that typing <F10> in paste mode inserts "<F10>", since in paste
mode everything is inserted literally, except the 'pastetoggle' key
sequence.
No timeout is used, this means that a multi-key 'pastetoggle' can not
be triggered manually.
When the value has several bytes 'ttimeoutlen' applies.
*'pex'* *'patchexpr'*
'patchexpr' 'pex' string (default "")

View File

@ -365,6 +365,10 @@ Macro/|recording| behavior
macros and 'keymap' at the same time. This also means you can use |:imap| on
the results of keys from 'keymap'.
Mappings:
Creating a mapping for a simplifiable key (e.g. <C-I>) doesn't replace an
existing mapping for its simplified form (e.g. <Tab>).
Motion:
The |jumplist| avoids useless/phantom jumps.

View File

@ -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);

View File

@ -403,9 +403,19 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
return (String) { .data = NULL, .size = 0 };
}
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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -5180,8 +5180,7 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo
char_u *rep_buf = NULL;
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);

View File

@ -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

View File

@ -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) {
*keylenp = keylen;
return map_result_get; // get character from typeahead
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_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,300 +3078,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 (keyround == 2) {
if (!did_simplify) {
break;
}
lhs = (char_u *)&args->alt_lhs;
len = (int)args->alt_lhs_len;
} else if (did_simplify && do_print) {
// when printing always use the not-simplified map
lhs = (char_u *)&args->alt_lhs;
len = (int)args->alt_lhs_len;
}
if (is_abbrev && maptype != 1) {
//
// 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) {
// 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
}
}
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();
}
// 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;
}
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);
// 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 (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 (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];
}
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;
}
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;
// 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;
}
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
mpp = abbr_table;
} else {
mpp = &(map_table[hash]);
}
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 (!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 (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;
}
if (!has_lhs || !has_rhs) {
// print entries
if (!did_it && !did_local) {
if (is_abbrev) {
msg(_("No abbreviation found"));
} else {
msg(_("No mapping found"));
}
}
goto theend; // listing finished
}
if (did_it) {
continue; // have added the new entry already
}
// Get here when adding a new entry to the maphash[] list or abbrlist.
mp = xmalloc(sizeof(mapblock_T));
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*lhs == Ctrl_C) {
if (map_table == buf->b_maphash) {
buf->b_mapped_ctrl_c &= ~mode;
buf->b_mapped_ctrl_c |= mode;
} else {
mapped_ctrl_c &= ~mode;
mapped_ctrl_c |= mode;
}
}
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"));
}
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);
}
goto theend; // listing finished
}
if (did_it) { // have added the new entry already
goto theend;
}
// 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;
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbrev) {
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
mapped_ctrl_c |= mode;
n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
}
mp->m_keys = vim_strsave(lhs);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_luaref = args->rhs_lua;
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = args->nowait;
mp->m_silent = args->silent;
mp->m_mode = mode;
mp->m_expr = args->expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
nlua_set_sctx(&mp->m_script_ctx);
mp->m_desc = NULL;
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
}
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbrev) {
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
theend:
return retval;
}
@ -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 { \

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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 Dont 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;

View File

@ -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, ///< dont 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

View File

@ -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;

View File

@ -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
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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",

View File

@ -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\"']")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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", "")', ''))

View File

@ -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

View File

@ -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 {

View File

@ -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 '}':

View File

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

View File

@ -213,11 +213,11 @@ describe("'langmap'", function()
iii]])
end)
local function testrecording(command_string, expect_string, setup_function)
local function testrecording(command_string, expect_string, setup_function, expect_macro)
if setup_function then setup_function() end
feed('qa' .. command_string .. 'q')
expect(expect_string)
eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
eq(expect_macro or helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
eval('@a'))
if setup_function then setup_function() end
-- n.b. may need nvim_replace_termcodes() here.
@ -273,8 +273,8 @@ describe("'langmap'", function()
it('treats control modified keys as characters', function()
command('nnoremap <C-w> iw<esc>')
command('nnoremap <C-i> ii<esc>')
testrecording('<C-w>', 'whello', local_setup)
testrecording('<C-i>', 'ihello', local_setup)
testrecording('<C-w>', 'whello', local_setup, eval([["\<*C-w>"]]))
testrecording('<C-i>', 'ihello', local_setup, eval([["\<*C-i>"]]))
end)
end)

View File

@ -3,67 +3,74 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, insert, funcs, eq, feed =
helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed
local eval = helpers.eval
local meths = helpers.meths
describe('cmdline CTRL-R', function()
describe('cmdline', function()
before_each(clear)
it('pasting non-special register inserts <CR> *between* lines', function()
insert([[
line1abc
line2somemoretext
]])
-- Yank 2 lines linewise, then paste to cmdline.
feed([[<C-\><C-N>gg0yj:<C-R>0]])
-- <CR> inserted between lines, NOT after the final line.
eq('line1abc\rline2somemoretext', funcs.getcmdline())
describe('Ctrl-R', function()
it('pasting non-special register inserts <CR> *between* lines', function()
insert([[
line1abc
line2somemoretext
]])
-- Yank 2 lines linewise, then paste to cmdline.
feed([[<C-\><C-N>gg0yj:<C-R>0]])
-- <CR> inserted between lines, NOT after the final line.
eq('line1abc\rline2somemoretext', funcs.getcmdline())
-- Yank 2 lines charwise, then paste to cmdline.
feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
-- <CR> inserted between lines, NOT after the final line.
eq('abc\rline2', funcs.getcmdline())
-- Yank 2 lines charwise, then paste to cmdline.
feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
-- <CR> inserted between lines, NOT after the final line.
eq('abc\rline2', funcs.getcmdline())
-- Yank 1 line linewise, then paste to cmdline.
feed([[<C-\><C-N>ggyy:<C-R>0]])
-- No <CR> inserted.
eq('line1abc', funcs.getcmdline())
-- Yank 1 line linewise, then paste to cmdline.
feed([[<C-\><C-N>ggyy:<C-R>0]])
-- No <CR> inserted.
eq('line1abc', funcs.getcmdline())
end)
it('pasting special register inserts <CR>, <NL>', function()
feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
eq('foo\nbar\rbaz', funcs.getcmdline())
end)
end)
it('pasting special register inserts <CR>, <NL>', function()
feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
eq('foo\nbar\rbaz', funcs.getcmdline())
end)
end)
describe('cmdline history', function()
before_each(clear)
it('correctly clears start of the history', function()
-- Regression test: check absence of the memory leak when clearing start of
-- the history using ex_getln.c/clr_history().
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
end)
it('correctly clears end of the history', function()
-- Regression test: check absence of the memory leak when clearing end of
-- the history using ex_getln.c/clr_history().
meths.set_option('history', 1)
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
end)
it('correctly removes item from history', function()
-- Regression test: check that ex_getln.c/del_history_idx() correctly clears
-- history index after removing history entry. If it does not then deleting
-- history will result in a double free.
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histadd(':', 'bar'))
eq(1, funcs.histadd(':', 'baz'))
eq(1, funcs.histdel(':', -2))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
it('Ctrl-Shift-V supports entering unsimplified key notations', function()
feed(':"<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><CR>')
eq('"<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>', eval('@:'))
end)
describe('history', function()
it('correctly clears start of the history', function()
-- Regression test: check absence of the memory leak when clearing start of
-- the history using ex_getln.c/clr_history().
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
end)
it('correctly clears end of the history', function()
-- Regression test: check absence of the memory leak when clearing end of
-- the history using ex_getln.c/clr_history().
meths.set_option('history', 1)
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
end)
it('correctly removes item from history', function()
-- Regression test: check that ex_getln.c/del_history_idx() correctly clears
-- history index after removing history entry. If it does not then deleting
-- history will result in a double free.
eq(1, funcs.histadd(':', 'foo'))
eq(1, funcs.histadd(':', 'bar'))
eq(1, funcs.histadd(':', 'baz'))
eq(1, funcs.histdel(':', -2))
eq(1, funcs.histdel(':'))
eq('', funcs.histget(':', -1))
end)
end)
end)

View File

@ -131,6 +131,11 @@ describe('insert-mode', function()
end)
end)
it('Ctrl-Shift-V supports entering unsimplified key notations', function()
feed('i<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><Esc>')
expect('<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>')
end)
describe([[With 'insertmode', Insert mode is not re-entered immediately after <C-L>]], function()
before_each(function()
command('set insertmode')

View File

@ -46,7 +46,7 @@ describe('eval', function()
command('AR "')
command([[let @" = "abc\n"]])
source('AR "')
command([[let @" = "abc\r"]])
command([[let @" = "abc\<C-m>"]])
command('AR "')
command([[let @= = '"abc"']])
command('AR =')

View File

@ -4,16 +4,14 @@ local clear = helpers.clear
local feed = helpers.feed
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local sleep = helpers.sleep
local expect = helpers.expect
local eval = helpers.eval
local insert = helpers.insert
local meths = helpers.meths
local sleep = helpers.sleep
describe("'pastetoggle' option", function()
before_each(function()
clear()
command('set nopaste')
end)
before_each(clear)
it("toggles 'paste'", function()
command('set pastetoggle=a')
eq(0, eval('&paste'))
@ -22,19 +20,71 @@ describe("'pastetoggle' option", function()
feed('j')
eq(1, eval('&paste'))
end)
describe("multiple key 'pastetoggle'", function()
before_each(function()
eq(0, eval('&paste'))
command('set timeoutlen=1 ttimeoutlen=10000')
end)
it('is waited for when chars are typed', function()
local pastetoggle = 'lllll'
command('set pastetoggle=' .. pastetoggle)
feed(pastetoggle:sub(0, 2))
-- sleep() for long enough that vgetorpeek() is gotten into, but short
-- enough that ttimeoutlen is not reached.
sleep(200)
feed(pastetoggle:sub(3, -1))
-- Need another key so that the vgetorpeek() function returns.
feed('j')
eq(1, eval('&paste'))
end)
it('is not waited for when there are no typed chars after mapped chars', function()
command('set pastetoggle=abc')
command('imap d a')
meths.feedkeys('id', 't', true)
-- sleep() for long enough that vgetorpeek() is gotten into, but short
-- enough that ttimeoutlen is not reached.
sleep(200)
feed('bc')
-- Need another key so that the vgetorpeek() function returns.
feed('j')
-- 'ttimeoutlen' should NOT apply
eq(0, eval('&paste'))
end)
it('does not wait for timeout', function()
command('set pastetoggle=abc')
command('set ttimeoutlen=9999999')
eq(0, eval('&paste'))
-- n.b. need <esc> to return from vgetorpeek()
feed('abc<esc>')
eq(1, eval('&paste'))
feed('ab')
sleep(10)
feed('c<esc>')
expect('bc')
eq(1, eval('&paste'))
it('is waited for when there are typed chars after mapped chars', function()
command('set pastetoggle=abc')
command('imap d a')
meths.feedkeys('idb', 't', true)
-- sleep() for long enough that vgetorpeek() is gotten into, but short
-- enough that ttimeoutlen is not reached.
sleep(200)
feed('c')
-- Need another key so that the vgetorpeek() function returns.
feed('j')
-- 'ttimeoutlen' should apply
eq(1, eval('&paste'))
end)
it('is waited for when there are typed chars after noremapped chars', function()
command('set pastetoggle=abc')
command('inoremap d a')
meths.feedkeys('idb', 't', true)
-- sleep() for long enough that vgetorpeek() is gotten into, but short
-- enough that ttimeoutlen is not reached.
sleep(200)
feed('c')
-- Need another key so that the vgetorpeek() function returns.
feed('j')
-- 'ttimeoutlen' should apply
eq(1, eval('&paste'))
end)
end)
it('does not interfere with character-find', function()
insert('foo,bar')
feed('0')
command('set pastetoggle=,sp')
feed('dt,')
expect(',bar')
end)
end)

View File

@ -8,6 +8,7 @@ local meths = helpers.meths
local exec_lua = helpers.exec_lua
local write_file = helpers.write_file
local funcs = helpers.funcs
local eval = helpers.eval
local Screen = require('test.functional.ui.screen')
before_each(clear)
@ -172,11 +173,20 @@ describe('input pairs', function()
eq('\t\t', curbuf_contents())
end)
it('can be mapped', function()
command('inoremap <tab> TAB!')
command('inoremap <c-i> CTRL-I!')
feed('i<tab><c-i><esc>')
eq('TAB!CTRL-I!', curbuf_contents())
describe('can be mapped separately', function()
it('if <tab> is mapped after <c-i>', function()
command('inoremap <c-i> CTRL-I!')
command('inoremap <tab> TAB!')
feed('i<tab><c-i><esc>')
eq('TAB!CTRL-I!', curbuf_contents())
end)
it('if <tab> is mapped before <c-i>', function()
command('inoremap <tab> TAB!')
command('inoremap <c-i> CTRL-I!')
feed('i<tab><c-i><esc>')
eq('TAB!CTRL-I!', curbuf_contents())
end)
end)
end)
@ -186,11 +196,20 @@ describe('input pairs', function()
eq('unos\ndos\ntres', curbuf_contents())
end)
it('can be mapped', function()
command('inoremap <c-m> SNIPPET!')
command('inoremap <cr> , and then<cr>')
feed('iunos<c-m>dos<cr>tres<esc>')
eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
describe('can be mapped separately', function()
it('if <cr> is mapped after <c-m>', function()
command('inoremap <c-m> SNIPPET!')
command('inoremap <cr> , and then<cr>')
feed('iunos<c-m>dos<cr>tres<esc>')
eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
end)
it('if <cr> is mapped before <c-m>', function()
command('inoremap <cr> , and then<cr>')
command('inoremap <c-m> SNIPPET!')
feed('iunos<c-m>dos<cr>tres<esc>')
eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
end)
end)
end)
@ -200,11 +219,20 @@ describe('input pairs', function()
eq('doubledoublesingle', curbuf_contents())
end)
it('can be mapped', function()
command('inoremap <c-[> HALLOJ!')
command('inoremap <esc> ,<esc>')
feed('2adubbel<c-[>upp<esc>')
eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
describe('can be mapped separately', function()
it('if <esc> is mapped after <c-[>', function()
command('inoremap <c-[> HALLOJ!')
command('inoremap <esc> ,<esc>')
feed('2adubbel<c-[>upp<esc>')
eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
end)
it('if <esc> is mapped before <c-[>', function()
command('inoremap <esc> ,<esc>')
command('inoremap <c-[> HALLOJ!')
feed('2adubbel<c-[>upp<esc>')
eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
end)
end)
end)
end)
@ -216,6 +244,75 @@ it('Ctrl-6 is Ctrl-^ vim-patch:8.1.2333', function()
eq('aaa', funcs.bufname())
end)
it('c_CTRL-R_CTRL-R, i_CTRL-R_CTRL-R, i_CTRL-G_CTRL-K work properly vim-patch:8.1.2346', function()
command('set timeoutlen=10')
command([[let @a = 'aaa']])
feed([[:let x = '<C-R><C-R>a'<CR>]])
eq([[let x = 'aaa']], eval('@:'))
feed('a<C-R><C-R>a<Esc>')
expect('aaa')
command('bwipe!')
feed('axx<CR>yy<C-G><C-K>a<Esc>')
expect([[
axx
yy]])
end)
it('typing a simplifiable key at hit-enter prompt triggers mapping vim-patch:8.2.0839', function()
local screen = Screen.new(60,8)
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
[2] = {bold = true, reverse = true}, -- MsgSeparator
[3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
})
screen:attach()
command([[nnoremap <C-6> <Cmd>echo 'hit ctrl-6'<CR>]])
feed_command('ls')
screen:expect([[
|
{1:~ }|
{1:~ }|
{1:~ }|
{2: }|
:ls |
1 %a "[No Name]" line 1 |
{3:Press ENTER or type command to continue}^ |
]])
feed('<C-6>')
screen:expect([[
^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
hit ctrl-6 |
]])
end)
it('mixing simplified and unsimplified keys can trigger mapping vim-patch:8.2.0916', function()
command('set timeoutlen=10')
command([[imap ' <C-W>]])
command('imap <C-W><C-A> c-a')
feed([[a'<C-A>]])
expect('c-a')
end)
it('unsimplified mapping works when there was a partial match vim-patch:8.2.4504', function()
command('set timeoutlen=10')
command('nnoremap <C-J> a')
command('nnoremap <NL> x')
command('nnoremap <C-J>x <Nop>')
funcs.setline(1, 'x')
-- CTRL-J b should have trigger the <C-J> mapping and then insert "b"
feed('<C-J>b<Esc>')
expect('xb')
end)
describe('input non-printable chars', function()
after_each(function()
os.remove('Xtest-overwrite')

View File

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