mirror of
https://github.com/neovim/neovim.git
synced 2025-09-27 13:38:34 +00:00
vim-patch:8.1.1797: the vgetorpeek() function is too long
Problem: The vgetorpeek() function is too long.
Solution: Split off the part that handles mappings, with fix.
edd680f364
This commit is contained in:
@@ -1680,6 +1680,348 @@ int char_avail(void)
|
|||||||
return retval != NUL;
|
return retval != NUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
map_result_fail, // failed, break loop
|
||||||
|
map_result_get, // get a character from typeahead
|
||||||
|
map_result_retry, // try to map again
|
||||||
|
map_result_nomatch // no matching mapping, get char
|
||||||
|
} map_result_T;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
|
||||||
|
{
|
||||||
|
mapblock_T *mp = NULL;
|
||||||
|
mapblock_T *mp2;
|
||||||
|
mapblock_T *mp_match;
|
||||||
|
int mp_match_len = 0;
|
||||||
|
int max_mlen = 0;
|
||||||
|
int tb_c1;
|
||||||
|
int mlen;
|
||||||
|
int nolmaplen;
|
||||||
|
int keylen = *keylenp;
|
||||||
|
int i;
|
||||||
|
int local_State = get_real_state();
|
||||||
|
|
||||||
|
// Check for a mappable key sequence.
|
||||||
|
// Walk through one maphash[] list until we find an
|
||||||
|
// entry that matches.
|
||||||
|
//
|
||||||
|
// Don't look for mappings if:
|
||||||
|
// - no_mapping set: mapping disabled (e.g. for CTRL-V)
|
||||||
|
// - maphash_valid not set: no mappings present.
|
||||||
|
// - typebuf.tb_buf[typebuf.tb_off] should not be remapped
|
||||||
|
// - in insert or cmdline mode and 'paste' option set
|
||||||
|
// - waiting for "hit return to continue" and CR or SPACE
|
||||||
|
// typed
|
||||||
|
// - waiting for a char with --more--
|
||||||
|
// - in Ctrl-X mode, and we get a valid char for that mode
|
||||||
|
tb_c1 = typebuf.tb_buf[typebuf.tb_off];
|
||||||
|
if (no_mapping == 0 && maphash_valid
|
||||||
|
&& (no_zero_mapping == 0 || tb_c1 != '0')
|
||||||
|
&& (typebuf.tb_maplen == 0
|
||||||
|
|| (p_remap
|
||||||
|
&& (typebuf.tb_noremap[typebuf.tb_off]
|
||||||
|
& (RM_NONE|RM_ABBR)) == 0))
|
||||||
|
&& !(p_paste && (State & (INSERT + CMDLINE)))
|
||||||
|
&& !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' '))
|
||||||
|
&& State != ASKMORE
|
||||||
|
&& State != CONFIRM
|
||||||
|
&& !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1))
|
||||||
|
|| ((compl_cont_status & CONT_LOCAL)
|
||||||
|
&& (tb_c1 == Ctrl_N
|
||||||
|
|| tb_c1 == Ctrl_P)))) {
|
||||||
|
if (tb_c1 == K_SPECIAL) {
|
||||||
|
nolmaplen = 2;
|
||||||
|
} else {
|
||||||
|
LANGMAP_ADJUST(tb_c1, (State & (CMDLINE | INSERT)) == 0
|
||||||
|
&& get_real_state() != SELECTMODE);
|
||||||
|
nolmaplen = 0;
|
||||||
|
}
|
||||||
|
// First try buffer-local mappings.
|
||||||
|
mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)];
|
||||||
|
mp2 = maphash[MAP_HASH(local_State, tb_c1)];
|
||||||
|
if (mp == NULL) {
|
||||||
|
// There are no buffer-local mappings.
|
||||||
|
mp = mp2;
|
||||||
|
mp2 = NULL;
|
||||||
|
}
|
||||||
|
// Loop until a partly matching mapping is found or
|
||||||
|
// all (local) mappings have been checked.
|
||||||
|
// The longest full match is remembered in "mp_match".
|
||||||
|
// A full match is only accepted if there is no partly
|
||||||
|
// match, so "aa" and "aaa" can both be mapped.
|
||||||
|
mp_match = NULL;
|
||||||
|
mp_match_len = 0;
|
||||||
|
for (; mp != NULL;
|
||||||
|
mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
|
||||||
|
(mp = mp->m_next)) {
|
||||||
|
// Only consider an entry if the first character
|
||||||
|
// matches and it is for the current state.
|
||||||
|
// Skip ":lmap" mappings if keys were mapped.
|
||||||
|
if (mp->m_keys[0] == tb_c1
|
||||||
|
&& (mp->m_mode & local_State)
|
||||||
|
&& ((mp->m_mode & LANGMAP) == 0
|
||||||
|
|| typebuf.tb_maplen == 0)) {
|
||||||
|
int nomap = nolmaplen;
|
||||||
|
int c2;
|
||||||
|
// find the match length of this mapping
|
||||||
|
for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
|
||||||
|
c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
|
||||||
|
if (nomap > 0) {
|
||||||
|
nomap--;
|
||||||
|
} else if (c2 == K_SPECIAL) {
|
||||||
|
nomap = 2;
|
||||||
|
} else {
|
||||||
|
LANGMAP_ADJUST(c2, true);
|
||||||
|
}
|
||||||
|
if (mp->m_keys[mlen] != c2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow mapping the first byte(s) of a
|
||||||
|
// multi-byte char. Happens when mapping
|
||||||
|
// <M-a> and then changing 'encoding'. Beware
|
||||||
|
// that 0x80 is escaped.
|
||||||
|
char_u *p1 = mp->m_keys;
|
||||||
|
char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
|
||||||
|
|
||||||
|
if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) {
|
||||||
|
mlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check an entry whether it matches.
|
||||||
|
// - Full match: mlen == keylen
|
||||||
|
// - Partly match: mlen == typebuf.tb_len
|
||||||
|
keylen = mp->m_keylen;
|
||||||
|
if (mlen == keylen
|
||||||
|
|| (mlen == typebuf.tb_len
|
||||||
|
&& typebuf.tb_len < keylen)) {
|
||||||
|
char_u *s;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
// If only script-local mappings are
|
||||||
|
// allowed, check if the mapping starts
|
||||||
|
// with K_SNR.
|
||||||
|
s = typebuf.tb_noremap + typebuf.tb_off;
|
||||||
|
if (*s == RM_SCRIPT
|
||||||
|
&& (mp->m_keys[0] != K_SPECIAL
|
||||||
|
|| mp->m_keys[1] != KS_EXTRA
|
||||||
|
|| mp->m_keys[2]
|
||||||
|
!= KE_SNR)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one of the typed keys cannot be
|
||||||
|
// remapped, skip the entry.
|
||||||
|
for (n = mlen; --n >= 0;) {
|
||||||
|
if (*s++ & (RM_NONE|RM_ABBR)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keylen > typebuf.tb_len) {
|
||||||
|
if (!*timedout && !(mp_match != NULL
|
||||||
|
&& mp_match->m_nowait)) {
|
||||||
|
// break at a partly match
|
||||||
|
keylen = KEYLEN_PART_MAP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (keylen > mp_match_len
|
||||||
|
|| (keylen == mp_match_len
|
||||||
|
&& mp_match != NULL
|
||||||
|
&& (mp_match->m_mode & LANGMAP) == 0
|
||||||
|
&& (mp->m_mode & LANGMAP) != 0)) {
|
||||||
|
// found a longer match
|
||||||
|
mp_match = mp;
|
||||||
|
mp_match_len = keylen;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No match; may have to check for termcode at next character.
|
||||||
|
if (max_mlen < mlen) {
|
||||||
|
max_mlen = mlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no partly match found, use the longest full match.
|
||||||
|
if (keylen != KEYLEN_PART_MAP) {
|
||||||
|
mp = mp_match;
|
||||||
|
keylen = mp_match_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for match with 'pastetoggle'
|
||||||
|
if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
|
||||||
|
bool match = typebuf_match_len(p_pt, &mlen);
|
||||||
|
if (match) {
|
||||||
|
// write chars to script file(s)
|
||||||
|
if (mlen > typebuf.tb_maplen) {
|
||||||
|
gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
|
||||||
|
(size_t)(mlen - typebuf.tb_maplen));
|
||||||
|
}
|
||||||
|
|
||||||
|
del_typebuf(mlen, 0); // remove the chars
|
||||||
|
set_option_value("paste", !p_paste, NULL, 0);
|
||||||
|
if (!(State & INSERT)) {
|
||||||
|
msg_col = 0;
|
||||||
|
msg_row = Rows - 1;
|
||||||
|
msg_clr_eos(); // clear ruler
|
||||||
|
}
|
||||||
|
status_redraw_all();
|
||||||
|
redraw_statuslines();
|
||||||
|
showmode();
|
||||||
|
setcursor();
|
||||||
|
*keylenp = keylen;
|
||||||
|
return map_result_retry;
|
||||||
|
}
|
||||||
|
// Need more chars for partly match.
|
||||||
|
if (mlen == typebuf.tb_len) {
|
||||||
|
keylen = KEYLEN_PART_KEY;
|
||||||
|
} else if (max_mlen < mlen) {
|
||||||
|
// no match, may have to check for termcode at next character
|
||||||
|
max_mlen = mlen + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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; // got character, break for loop
|
||||||
|
} else {
|
||||||
|
keylen = mp_match_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete match
|
||||||
|
if (keylen >= 0 && keylen <= typebuf.tb_len) {
|
||||||
|
char_u *map_str;
|
||||||
|
int save_m_expr;
|
||||||
|
int save_m_noremap;
|
||||||
|
int save_m_silent;
|
||||||
|
|
||||||
|
// Write chars to script file(s).
|
||||||
|
// Note: :lmap mappings are written *after* being applied. #5658
|
||||||
|
if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) {
|
||||||
|
gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
|
||||||
|
(size_t)(keylen - typebuf.tb_maplen));
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_silent = (typebuf.tb_silent > 0);
|
||||||
|
del_typebuf(keylen, 0); // remove the mapped keys
|
||||||
|
|
||||||
|
// Put the replacement string in front of mapstr.
|
||||||
|
// The depth check catches ":map x y" and ":map y x".
|
||||||
|
if (++*mapdepth >= p_mmd) {
|
||||||
|
emsg(_("E223: recursive mapping"));
|
||||||
|
if (State & CMDLINE) {
|
||||||
|
redrawcmdline();
|
||||||
|
} else {
|
||||||
|
setcursor();
|
||||||
|
}
|
||||||
|
flush_buffers(FLUSH_MINIMAL);
|
||||||
|
*mapdepth = 0; // for next one
|
||||||
|
*keylenp = keylen;
|
||||||
|
return map_result_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Select mode and a Visual mode mapping is used:
|
||||||
|
// Switch to Visual mode temporarily. Append K_SELECT
|
||||||
|
// to switch back to Select mode.
|
||||||
|
if (VIsual_active && VIsual_select
|
||||||
|
&& (mp->m_mode & VISUAL)) {
|
||||||
|
VIsual_select = false;
|
||||||
|
(void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the values from *mp that are used, because
|
||||||
|
// evaluating the expression may invoke a function
|
||||||
|
// that redefines the mapping, thereby making *mp
|
||||||
|
// invalid.
|
||||||
|
save_m_expr = mp->m_expr;
|
||||||
|
save_m_noremap = mp->m_noremap;
|
||||||
|
save_m_silent = mp->m_silent;
|
||||||
|
char_u *save_m_keys = NULL; // only saved when needed
|
||||||
|
char_u *save_m_str = NULL; // only saved when needed
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle ":map <expr>": evaluate the {rhs} as an
|
||||||
|
* expression. Also save and restore the command line
|
||||||
|
* for "normal :".
|
||||||
|
*/
|
||||||
|
if (mp->m_expr) {
|
||||||
|
int save_vgetc_busy = vgetc_busy;
|
||||||
|
const bool save_may_garbage_collect = may_garbage_collect;
|
||||||
|
|
||||||
|
vgetc_busy = 0;
|
||||||
|
may_garbage_collect = false;
|
||||||
|
|
||||||
|
save_m_keys = vim_strsave(mp->m_keys);
|
||||||
|
save_m_str = vim_strsave(mp->m_str);
|
||||||
|
map_str = eval_map_expr(save_m_str, NUL);
|
||||||
|
vgetc_busy = save_vgetc_busy;
|
||||||
|
may_garbage_collect = save_may_garbage_collect;
|
||||||
|
} else {
|
||||||
|
map_str = mp->m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the 'to' part in the typebuf.tb_buf.
|
||||||
|
// If 'from' field is the same as the start of the
|
||||||
|
// 'to' field, don't remap the first character (but do
|
||||||
|
// allow abbreviations).
|
||||||
|
// If m_noremap is set, don't remap the whole 'to' part.
|
||||||
|
if (map_str == NULL) {
|
||||||
|
i = FAIL;
|
||||||
|
} else {
|
||||||
|
int noremap;
|
||||||
|
|
||||||
|
// If this is a LANGMAP mapping, then we didn't record the keys
|
||||||
|
// at the start of the function and have to record them now.
|
||||||
|
if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) {
|
||||||
|
gotchars(map_str, STRLEN(map_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save_m_noremap != REMAP_YES) {
|
||||||
|
noremap = save_m_noremap;
|
||||||
|
} else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
|
||||||
|
(size_t)keylen) != 0) {
|
||||||
|
noremap = REMAP_YES;
|
||||||
|
} else {
|
||||||
|
noremap = REMAP_SKIP;
|
||||||
|
}
|
||||||
|
i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent);
|
||||||
|
if (save_m_expr) {
|
||||||
|
xfree(map_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xfree(save_m_keys);
|
||||||
|
xfree(save_m_str);
|
||||||
|
*keylenp = keylen;
|
||||||
|
if (i == FAIL) {
|
||||||
|
return map_result_fail;
|
||||||
|
}
|
||||||
|
return map_result_retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
*keylenp = keylen;
|
||||||
|
return map_result_nomatch;
|
||||||
|
}
|
||||||
|
|
||||||
// unget one character (can only be done once!)
|
// unget one character (can only be done once!)
|
||||||
void vungetc(int c)
|
void vungetc(int c)
|
||||||
{
|
{
|
||||||
@@ -1715,44 +2057,28 @@ void vungetc(int c)
|
|||||||
static int vgetorpeek(bool advance)
|
static int vgetorpeek(bool advance)
|
||||||
{
|
{
|
||||||
int c, c1;
|
int c, c1;
|
||||||
int keylen;
|
bool timedout = false; // waited for more than 1 second for mapping to complete
|
||||||
char_u *s;
|
|
||||||
mapblock_T *mp;
|
|
||||||
mapblock_T *mp2;
|
|
||||||
mapblock_T *mp_match;
|
|
||||||
int mp_match_len = 0;
|
|
||||||
bool timedout = false; // waited for more than 1 second
|
|
||||||
// for mapping to complete
|
|
||||||
int mapdepth = 0; // check for recursive mapping
|
int mapdepth = 0; // check for recursive mapping
|
||||||
bool mode_deleted = false; // set when mode has been deleted
|
bool mode_deleted = false; // set when mode has been deleted
|
||||||
int local_State;
|
|
||||||
int mlen;
|
|
||||||
int max_mlen;
|
|
||||||
int i;
|
int i;
|
||||||
int new_wcol, new_wrow;
|
int new_wcol, new_wrow;
|
||||||
int n;
|
int n;
|
||||||
int nolmaplen;
|
|
||||||
int old_wcol, old_wrow;
|
int old_wcol, old_wrow;
|
||||||
int wait_tb_len;
|
int wait_tb_len;
|
||||||
|
|
||||||
/*
|
// This function doesn't work very well when called recursively. This may
|
||||||
* This function doesn't work very well when called recursively. This may
|
// happen though, because of:
|
||||||
* happen though, because of:
|
// 1. The call to add_to_showcmd(). char_avail() is then used to check if
|
||||||
* 1. The call to add_to_showcmd(). char_avail() is then used to check if
|
// there is a character available, which calls this function. In that
|
||||||
* there is a character available, which calls this function. In that
|
// case we must return NUL, to indicate no character is available.
|
||||||
* case we must return NUL, to indicate no character is available.
|
// 2. A GUI callback function writes to the screen, causing a
|
||||||
* 2. A GUI callback function writes to the screen, causing a
|
// wait_return().
|
||||||
* wait_return().
|
// Using ":normal" can also do this, but it saves the typeahead buffer,
|
||||||
* Using ":normal" can also do this, but it saves the typeahead buffer,
|
// thus it should be OK. But don't get a key from the user then.
|
||||||
* thus it should be OK. But don't get a key from the user then.
|
if (vgetc_busy > 0 && ex_normal_busy == 0) {
|
||||||
*/
|
|
||||||
if (vgetc_busy > 0
|
|
||||||
&& ex_normal_busy == 0) {
|
|
||||||
return NUL;
|
return NUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_State = get_real_state();
|
|
||||||
|
|
||||||
++vgetc_busy;
|
++vgetc_busy;
|
||||||
|
|
||||||
if (advance) {
|
if (advance) {
|
||||||
@@ -1765,9 +2091,7 @@ static int vgetorpeek(bool advance)
|
|||||||
reg_executing = 0;
|
reg_executing = 0;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
/*
|
// get a character: 1. from the stuffbuffer
|
||||||
* get a character: 1. from the stuffbuffer
|
|
||||||
*/
|
|
||||||
if (typeahead_char != 0) {
|
if (typeahead_char != 0) {
|
||||||
c = typeahead_char;
|
c = typeahead_char;
|
||||||
if (advance) {
|
if (advance) {
|
||||||
@@ -1787,12 +2111,10 @@ static int vgetorpeek(bool advance)
|
|||||||
typebuf.tb_no_abbr_cnt = 1; // no abbreviations now
|
typebuf.tb_no_abbr_cnt = 1; // no abbreviations now
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*
|
// Loop until we either find a matching mapped key, or we
|
||||||
* Loop until we either find a matching mapped key, or we
|
// are sure that it is not a mapped key.
|
||||||
* are sure that it is not a mapped key.
|
// If a mapped key sequence is found we go back to the start to
|
||||||
* If a mapped key sequence is found we go back to the start to
|
// try re-mapping.
|
||||||
* try re-mapping.
|
|
||||||
*/
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// os_breakcheck() is slow, don't use it too often when
|
// os_breakcheck() is slow, don't use it too often when
|
||||||
// inside a mapping. But call it each time for typed
|
// inside a mapping. But call it each time for typed
|
||||||
@@ -1802,10 +2124,11 @@ static int vgetorpeek(bool advance)
|
|||||||
} else {
|
} else {
|
||||||
os_breakcheck(); // check for CTRL-C
|
os_breakcheck(); // check for CTRL-C
|
||||||
}
|
}
|
||||||
keylen = 0;
|
int keylen = 0;
|
||||||
if (got_int) {
|
if (got_int) {
|
||||||
// flush all input
|
// flush all input
|
||||||
c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
|
c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
|
||||||
|
|
||||||
// If inchar() returns TRUE (script file was active) or we
|
// If inchar() returns TRUE (script file was active) or we
|
||||||
// are inside a mapping, get out of Insert mode.
|
// are inside a mapping, get out of Insert mode.
|
||||||
// Otherwise we behave like having gotten a CTRL-C.
|
// Otherwise we behave like having gotten a CTRL-C.
|
||||||
@@ -1829,209 +2152,21 @@ static int vgetorpeek(bool advance)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
} else if (typebuf.tb_len > 0) {
|
} else if (typebuf.tb_len > 0) {
|
||||||
/*
|
// Check for a mapping in "typebuf".
|
||||||
* Check for a mappable key sequence.
|
map_result_T result = (map_result_T)handle_mapping(&keylen, &timedout, &mapdepth);
|
||||||
* Walk through one maphash[] list until we find an
|
|
||||||
* entry that matches.
|
|
||||||
*
|
|
||||||
* Don't look for mappings if:
|
|
||||||
* - no_mapping set: mapping disabled (e.g. for CTRL-V)
|
|
||||||
* - maphash_valid not set: no mappings present.
|
|
||||||
* - typebuf.tb_buf[typebuf.tb_off] should not be remapped
|
|
||||||
* - in insert or cmdline mode and 'paste' option set
|
|
||||||
* - waiting for "hit return to continue" and CR or SPACE
|
|
||||||
* typed
|
|
||||||
* - waiting for a char with --more--
|
|
||||||
* - in Ctrl-X mode, and we get a valid char for that mode
|
|
||||||
*/
|
|
||||||
mp = NULL;
|
|
||||||
max_mlen = 0;
|
|
||||||
c1 = typebuf.tb_buf[typebuf.tb_off];
|
|
||||||
if (no_mapping == 0 && maphash_valid
|
|
||||||
&& (no_zero_mapping == 0 || c1 != '0')
|
|
||||||
&& (typebuf.tb_maplen == 0
|
|
||||||
|| (p_remap
|
|
||||||
&& (typebuf.tb_noremap[typebuf.tb_off]
|
|
||||||
& (RM_NONE|RM_ABBR)) == 0))
|
|
||||||
&& !(p_paste && (State & (INSERT + CMDLINE)))
|
|
||||||
&& !(State == HITRETURN && (c1 == CAR || c1 == ' '))
|
|
||||||
&& State != ASKMORE
|
|
||||||
&& State != CONFIRM
|
|
||||||
&& !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1))
|
|
||||||
|| ((compl_cont_status & CONT_LOCAL)
|
|
||||||
&& (c1 == Ctrl_N
|
|
||||||
|| c1 == Ctrl_P)))) {
|
|
||||||
if (c1 == K_SPECIAL) {
|
|
||||||
nolmaplen = 2;
|
|
||||||
} else {
|
|
||||||
LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0
|
|
||||||
&& get_real_state() != SELECTMODE);
|
|
||||||
nolmaplen = 0;
|
|
||||||
}
|
|
||||||
// First try buffer-local mappings.
|
|
||||||
mp = curbuf->b_maphash[MAP_HASH(local_State, c1)];
|
|
||||||
mp2 = maphash[MAP_HASH(local_State, c1)];
|
|
||||||
if (mp == NULL) {
|
|
||||||
// There are no buffer-local mappings.
|
|
||||||
mp = mp2;
|
|
||||||
mp2 = NULL;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Loop until a partly matching mapping is found or
|
|
||||||
* all (local) mappings have been checked.
|
|
||||||
* The longest full match is remembered in "mp_match".
|
|
||||||
* A full match is only accepted if there is no partly
|
|
||||||
* match, so "aa" and "aaa" can both be mapped.
|
|
||||||
*/
|
|
||||||
mp_match = NULL;
|
|
||||||
mp_match_len = 0;
|
|
||||||
for (; mp != NULL;
|
|
||||||
mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
|
|
||||||
(mp = mp->m_next)) {
|
|
||||||
/*
|
|
||||||
* Only consider an entry if the first character
|
|
||||||
* matches and it is for the current state.
|
|
||||||
* Skip ":lmap" mappings if keys were mapped.
|
|
||||||
*/
|
|
||||||
if (mp->m_keys[0] == c1
|
|
||||||
&& (mp->m_mode & local_State)
|
|
||||||
&& ((mp->m_mode & LANGMAP) == 0
|
|
||||||
|| typebuf.tb_maplen == 0)) {
|
|
||||||
int nomap = nolmaplen;
|
|
||||||
int c2;
|
|
||||||
// find the match length of this mapping
|
|
||||||
for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
|
|
||||||
c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
|
|
||||||
if (nomap > 0) {
|
|
||||||
--nomap;
|
|
||||||
} else if (c2 == K_SPECIAL) {
|
|
||||||
nomap = 2;
|
|
||||||
} else {
|
|
||||||
LANGMAP_ADJUST(c2, TRUE);
|
|
||||||
}
|
|
||||||
if (mp->m_keys[mlen] != c2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't allow mapping the first byte(s) of a
|
if (result == map_result_retry) {
|
||||||
* multi-byte char. Happens when mapping
|
// try mapping again
|
||||||
* <M-a> and then changing 'encoding'. Beware
|
|
||||||
* that 0x80 is escaped. */
|
|
||||||
char_u *p1 = mp->m_keys;
|
|
||||||
char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
|
|
||||||
|
|
||||||
if (p2 != NULL && MB_BYTE2LEN(c1) > utfc_ptr2len(p2)) {
|
|
||||||
mlen = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check an entry whether it matches.
|
|
||||||
// - Full match: mlen == keylen
|
|
||||||
// - Partly match: mlen == typebuf.tb_len
|
|
||||||
keylen = mp->m_keylen;
|
|
||||||
if (mlen == keylen
|
|
||||||
|| (mlen == typebuf.tb_len
|
|
||||||
&& typebuf.tb_len < keylen)) {
|
|
||||||
/*
|
|
||||||
* If only script-local mappings are
|
|
||||||
* allowed, check if the mapping starts
|
|
||||||
* with K_SNR.
|
|
||||||
*/
|
|
||||||
s = typebuf.tb_noremap + typebuf.tb_off;
|
|
||||||
if (*s == RM_SCRIPT
|
|
||||||
&& (mp->m_keys[0] != K_SPECIAL
|
|
||||||
|| mp->m_keys[1] != KS_EXTRA
|
|
||||||
|| mp->m_keys[2]
|
|
||||||
!= KE_SNR)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If one of the typed keys cannot be
|
|
||||||
* remapped, skip the entry.
|
|
||||||
*/
|
|
||||||
for (n = mlen; --n >= 0;) {
|
|
||||||
if (*s++ & (RM_NONE|RM_ABBR)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (n >= 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keylen > typebuf.tb_len) {
|
if (result == map_result_fail) {
|
||||||
if (!timedout && !(mp_match != NULL
|
// failed, use the outer loop
|
||||||
&& mp_match->m_nowait)) {
|
c = -1;
|
||||||
// break at a partly match
|
|
||||||
keylen = KEYLEN_PART_MAP;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (keylen > mp_match_len
|
|
||||||
|| (keylen == mp_match_len
|
|
||||||
&& mp_match != NULL
|
|
||||||
&& (mp_match->m_mode & LANGMAP) == 0
|
|
||||||
&& (mp->m_mode & LANGMAP) != 0)) {
|
|
||||||
// found a longer match
|
|
||||||
mp_match = mp;
|
|
||||||
mp_match_len = keylen;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No match; may have to check for termcode at next character.
|
|
||||||
if (max_mlen < mlen) {
|
|
||||||
max_mlen = mlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If no partly match found, use the longest full
|
if (result == map_result_get) {
|
||||||
* match. */
|
|
||||||
if (keylen != KEYLEN_PART_MAP) {
|
|
||||||
mp = mp_match;
|
|
||||||
keylen = mp_match_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
|
|
||||||
bool match = typebuf_match_len(p_pt, &mlen);
|
|
||||||
if (match) {
|
|
||||||
// write chars to script file(s)
|
|
||||||
if (mlen > typebuf.tb_maplen) {
|
|
||||||
gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
|
|
||||||
(size_t)(mlen - typebuf.tb_maplen));
|
|
||||||
}
|
|
||||||
|
|
||||||
del_typebuf(mlen, 0); // Remove the chars.
|
|
||||||
set_option_value("paste", !p_paste, NULL, 0);
|
|
||||||
if (!(State & INSERT)) {
|
|
||||||
msg_col = 0;
|
|
||||||
msg_row = Rows - 1;
|
|
||||||
msg_clr_eos(); // clear ruler
|
|
||||||
}
|
|
||||||
status_redraw_all();
|
|
||||||
redraw_statuslines();
|
|
||||||
showmode();
|
|
||||||
setcursor();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Need more chars for partly match.
|
|
||||||
if (mlen == typebuf.tb_len) {
|
|
||||||
keylen = KEYLEN_PART_KEY;
|
|
||||||
} else if (max_mlen < mlen) {
|
|
||||||
// no match, may have to check for termcode at
|
|
||||||
// next character
|
|
||||||
max_mlen = mlen + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// get a character: 2. from the typeahead buffer
|
// get a character: 2. from the typeahead buffer
|
||||||
c = typebuf.tb_buf[typebuf.tb_off] & 255;
|
c = typebuf.tb_buf[typebuf.tb_off] & 255;
|
||||||
if (advance) { // remove chars from tb_buf
|
if (advance) { // remove chars from tb_buf
|
||||||
@@ -2046,144 +2181,20 @@ static int vgetorpeek(bool advance)
|
|||||||
KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
|
KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
|
||||||
del_typebuf(1, 0);
|
del_typebuf(1, 0);
|
||||||
}
|
}
|
||||||
break; // got character, break for loop
|
|
||||||
} else {
|
|
||||||
keylen = mp_match_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// complete match
|
|
||||||
if (keylen >= 0 && keylen <= typebuf.tb_len) {
|
|
||||||
int save_m_expr;
|
|
||||||
int save_m_noremap;
|
|
||||||
int save_m_silent;
|
|
||||||
|
|
||||||
// Write chars to script file(s)
|
|
||||||
// Note: :lmap mappings are written *after* being applied. #5658
|
|
||||||
if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) {
|
|
||||||
gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
|
|
||||||
(size_t)(keylen - typebuf.tb_maplen));
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_silent = (typebuf.tb_silent > 0);
|
|
||||||
del_typebuf(keylen, 0); // remove the mapped keys
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Put the replacement string in front of mapstr.
|
|
||||||
* The depth check catches ":map x y" and ":map y x".
|
|
||||||
*/
|
|
||||||
if (++mapdepth >= p_mmd) {
|
|
||||||
emsg(_("E223: recursive mapping"));
|
|
||||||
if (State & CMDLINE) {
|
|
||||||
redrawcmdline();
|
|
||||||
} else {
|
|
||||||
setcursor();
|
|
||||||
}
|
|
||||||
flush_buffers(FLUSH_MINIMAL);
|
|
||||||
mapdepth = 0; // for next one
|
|
||||||
c = -1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// not enough characters, get more
|
||||||
* In Select mode and a Visual mode mapping is used:
|
|
||||||
* Switch to Visual mode temporarily. Append K_SELECT
|
|
||||||
* to switch back to Select mode.
|
|
||||||
*/
|
|
||||||
if (VIsual_active && VIsual_select
|
|
||||||
&& (mp->m_mode & VISUAL)) {
|
|
||||||
VIsual_select = false;
|
|
||||||
(void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the values from *mp that are used, because
|
// get a character: 3. from the user - handle <Esc> in Insert mode
|
||||||
* evaluating the expression may invoke a function
|
|
||||||
* that redefines the mapping, thereby making *mp
|
|
||||||
* invalid. */
|
|
||||||
save_m_expr = mp->m_expr;
|
|
||||||
save_m_noremap = mp->m_noremap;
|
|
||||||
save_m_silent = mp->m_silent;
|
|
||||||
char_u *save_m_keys = NULL; // only saved when needed
|
|
||||||
char_u *save_m_str = NULL; // only saved when needed
|
|
||||||
|
|
||||||
/*
|
// special case: if we get an <ESC> in insert mode and there
|
||||||
* Handle ":map <expr>": evaluate the {rhs} as an
|
// are no more characters at once, we pretend to go out of
|
||||||
* expression. Also save and restore the command line
|
// insert mode. This prevents the one second delay after
|
||||||
* for "normal :".
|
// typing an <ESC>. If we get something after all, we may
|
||||||
*/
|
// have to redisplay the mode. That the cursor is in the wrong
|
||||||
if (mp->m_expr) {
|
// place does not matter.
|
||||||
int save_vgetc_busy = vgetc_busy;
|
|
||||||
const bool save_may_garbage_collect = may_garbage_collect;
|
|
||||||
|
|
||||||
vgetc_busy = 0;
|
|
||||||
may_garbage_collect = false;
|
|
||||||
|
|
||||||
save_m_keys = vim_strsave(mp->m_keys);
|
|
||||||
save_m_str = vim_strsave(mp->m_str);
|
|
||||||
s = eval_map_expr(save_m_str, NUL);
|
|
||||||
vgetc_busy = save_vgetc_busy;
|
|
||||||
may_garbage_collect = save_may_garbage_collect;
|
|
||||||
} else {
|
|
||||||
s = mp->m_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Insert the 'to' part in the typebuf.tb_buf.
|
|
||||||
* If 'from' field is the same as the start of the
|
|
||||||
* 'to' field, don't remap the first character (but do
|
|
||||||
* allow abbreviations).
|
|
||||||
* If m_noremap is set, don't remap the whole 'to'
|
|
||||||
* part.
|
|
||||||
*/
|
|
||||||
if (s == NULL) {
|
|
||||||
i = FAIL;
|
|
||||||
} else {
|
|
||||||
int noremap;
|
|
||||||
|
|
||||||
// If this is a LANGMAP mapping, then we didn't record the keys
|
|
||||||
// at the start of the function and have to record them now.
|
|
||||||
if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) {
|
|
||||||
gotchars(s, STRLEN(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save_m_noremap != REMAP_YES) {
|
|
||||||
noremap = save_m_noremap;
|
|
||||||
} else if (
|
|
||||||
STRNCMP(s, save_m_keys != NULL
|
|
||||||
? save_m_keys : mp->m_keys,
|
|
||||||
(size_t)keylen)
|
|
||||||
!= 0) {
|
|
||||||
noremap = REMAP_YES;
|
|
||||||
} else {
|
|
||||||
noremap = REMAP_SKIP;
|
|
||||||
}
|
|
||||||
i = ins_typebuf(s, noremap,
|
|
||||||
0, TRUE, cmd_silent || save_m_silent);
|
|
||||||
if (save_m_expr) {
|
|
||||||
xfree(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xfree(save_m_keys);
|
|
||||||
xfree(save_m_str);
|
|
||||||
if (i == FAIL) {
|
|
||||||
c = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get a character: 3. from the user - handle <Esc> in Insert mode
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* special case: if we get an <ESC> in insert mode and there
|
|
||||||
* are no more characters at once, we pretend to go out of
|
|
||||||
* insert mode. This prevents the one second delay after
|
|
||||||
* typing an <ESC>. If we get something after all, we may
|
|
||||||
* have to redisplay the mode. That the cursor is in the wrong
|
|
||||||
* place does not matter.
|
|
||||||
*/
|
|
||||||
c = 0;
|
c = 0;
|
||||||
new_wcol = curwin->w_wcol;
|
new_wcol = curwin->w_wcol;
|
||||||
new_wrow = curwin->w_wrow;
|
new_wrow = curwin->w_wrow;
|
||||||
@@ -2213,11 +2224,9 @@ static int vgetorpeek(bool advance)
|
|||||||
if (curwin->w_cursor.col != 0) {
|
if (curwin->w_cursor.col != 0) {
|
||||||
if (curwin->w_wcol > 0) {
|
if (curwin->w_wcol > 0) {
|
||||||
if (did_ai) {
|
if (did_ai) {
|
||||||
/*
|
// We are expecting to truncate the trailing
|
||||||
* We are expecting to truncate the trailing
|
// white-space, so find the last non-white
|
||||||
* white-space, so find the last non-white
|
// character -- webb
|
||||||
* character -- webb
|
|
||||||
*/
|
|
||||||
col = vcol = curwin->w_wcol = 0;
|
col = vcol = curwin->w_wcol = 0;
|
||||||
ptr = get_cursor_line_ptr();
|
ptr = get_cursor_line_ptr();
|
||||||
while (col < curwin->w_cursor.col) {
|
while (col < curwin->w_cursor.col) {
|
||||||
@@ -2305,9 +2314,8 @@ static int vgetorpeek(bool advance)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// get a character: 3. from the user - update display
|
||||||
* get a character: 3. from the user - update display
|
|
||||||
*/
|
|
||||||
// In insert mode a screen update is skipped when characters
|
// In insert mode a screen update is skipped when characters
|
||||||
// are still available. But when those available characters
|
// are still available. But when those available characters
|
||||||
// are part of a mapping, and we are going to do a blocking
|
// are part of a mapping, and we are going to do a blocking
|
||||||
@@ -2321,11 +2329,9 @@ static int vgetorpeek(bool advance)
|
|||||||
setcursor(); // put cursor back where it belongs
|
setcursor(); // put cursor back where it belongs
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// If we have a partial match (and are going to wait for more
|
||||||
* If we have a partial match (and are going to wait for more
|
// input from the user), show the partially matched characters
|
||||||
* input from the user), show the partially matched characters
|
// to the user with showcmd.
|
||||||
* to the user with showcmd.
|
|
||||||
*/
|
|
||||||
i = 0;
|
i = 0;
|
||||||
c1 = 0;
|
c1 = 0;
|
||||||
if (typebuf.tb_len > 0 && advance && !exmode_active) {
|
if (typebuf.tb_len > 0 && advance && !exmode_active) {
|
||||||
@@ -2350,8 +2356,7 @@ static int vgetorpeek(bool advance)
|
|||||||
i = typebuf.tb_len - SHOWCMD_COLS;
|
i = typebuf.tb_len - SHOWCMD_COLS;
|
||||||
}
|
}
|
||||||
while (i < typebuf.tb_len) {
|
while (i < typebuf.tb_len) {
|
||||||
(void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off
|
(void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + i++]);
|
||||||
+ i++]);
|
|
||||||
}
|
}
|
||||||
curwin->w_wcol = old_wcol;
|
curwin->w_wcol = old_wcol;
|
||||||
curwin->w_wrow = old_wrow;
|
curwin->w_wrow = old_wrow;
|
||||||
@@ -2367,9 +2372,7 @@ static int vgetorpeek(bool advance)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// get a character: 3. from the user - get it
|
||||||
* get a character: 3. from the user - get it
|
|
||||||
*/
|
|
||||||
if (typebuf.tb_len == 0) {
|
if (typebuf.tb_len == 0) {
|
||||||
// timedout may have been set while waiting for a mapping
|
// timedout may have been set while waiting for a mapping
|
||||||
// that has a <Nop> RHS.
|
// that has a <Nop> RHS.
|
||||||
@@ -2431,11 +2434,9 @@ static int vgetorpeek(bool advance)
|
|||||||
// if advance is false don't loop on NULs
|
// if advance is false don't loop on NULs
|
||||||
} while (c < 0 || (advance && c == NUL));
|
} while (c < 0 || (advance && c == NUL));
|
||||||
|
|
||||||
/*
|
// The "INSERT" message is taken care of here:
|
||||||
* The "INSERT" message is taken care of here:
|
// if we return an ESC to exit insert mode, the message is deleted
|
||||||
* if we return an ESC to exit insert mode, the message is deleted
|
// if we don't return an ESC but deleted the message before, redisplay it
|
||||||
* if we don't return an ESC but deleted the message before, redisplay it
|
|
||||||
*/
|
|
||||||
if (advance && p_smd && msg_silent == 0 && (State & INSERT)) {
|
if (advance && p_smd && msg_silent == 0 && (State & INSERT)) {
|
||||||
if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) {
|
if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) {
|
||||||
if (typebuf.tb_len && !KeyTyped) {
|
if (typebuf.tb_len && !KeyTyped) {
|
||||||
|
Reference in New Issue
Block a user