changeset 17594:3b18d5341c03 v8.1.1794

patch 8.1.1794: tests are flaky commit https://github.com/vim/vim/commit/f2d8b7a0a69fd71018341755da5ce55d067b5923 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Aug 2 22:46:11 2019 +0200 patch 8.1.1794: tests are flaky Problem: Tests are flaky. Solution: Undo the change to vgetorpeek().
author Bram Moolenaar <Bram@vim.org>
date Fri, 02 Aug 2019 23:00:07 +0200
parents 8746304ac416
children 50cc81726702
files src/getchar.c src/version.c
diffstat 2 files changed, 509 insertions(+), 553 deletions(-) [+]
line wrap: on
line diff
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1900,530 +1900,6 @@ char_avail(void)
     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,
-	    int *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;
-#ifdef FEAT_LANGMAP
-    int		nolmaplen;
-#endif
-    int		keylen;
-    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 && is_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
-#ifdef FEAT_INS_EXPAND
-	    && !((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)))
-#endif
-	    )
-    {
-#ifdef FEAT_LANGMAP
-	if (tb_c1 == K_SPECIAL)
-	    nolmaplen = 2;
-	else
-	{
-	    LANGMAP_ADJUST(tb_c1,
-			   (State & (CMDLINE | INSERT)) == 0
-			   && get_real_state() != SELECTMODE);
-	    nolmaplen = 0;
-	}
-#endif
-	// First try buffer-local mappings.
-	mp = get_buf_maphash_list(local_State, tb_c1);
-	mp2 = get_maphash_list(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))
-	    {
-#ifdef FEAT_LANGMAP
-		int	nomap = nolmaplen;
-		int	c2;
-#endif
-		// find the match length of this mapping
-		for (mlen = 1; mlen < typebuf.tb_len; ++mlen)
-		{
-#ifdef FEAT_LANGMAP
-		    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)
-#else
-		    if (mp->m_keys[mlen] !=
-			typebuf.tb_buf[typebuf.tb_off + mlen])
-#endif
-			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 = mb_unescape(&p1);
-
-		    if (has_mbyte && p2 != NULL
-			  && MB_BYTE2LEN(tb_c1) > MB_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]
-					      != (int)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)
-		    {
-			// 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)))
-    {
-	for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen];
-						       ++mlen)
-	    if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off
-						      + mlen])
-		    break;
-	if (p_pt[mlen] == NUL)	// match
-	{
-	    // write chars to script file(s)
-	    if (mlen > typebuf.tb_maplen)
-		gotchars(typebuf.tb_buf + typebuf.tb_off
-					  + typebuf.tb_maplen,
-				    mlen - typebuf.tb_maplen);
-
-	    del_typebuf(mlen, 0); // remove the chars
-	    set_option_value((char_u *)"paste",
-				     (long)!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)
-    {
-	int	save_keylen = keylen;
-
-	/*
-	 * When no matching mapping found or found a
-	 * non-matching mapping that matches at least what the
-	 * matching mapping matched:
-	 * Check if we have a terminal code, when:
-	 *  mapping is allowed,
-	 *  keys have not been mapped,
-	 *  and not an ESC sequence, not in insert mode or
-	 *	p_ek is on,
-	 *  and when not timed out,
-	 */
-	if ((no_mapping == 0 || allow_keys != 0)
-		&& (typebuf.tb_maplen == 0
-		    || (p_remap && typebuf.tb_noremap[
-				   typebuf.tb_off] == RM_YES))
-		&& !*timedout)
-	{
-	    keylen = check_termcode(max_mlen + 1,
-					       NULL, 0, NULL);
-
-	    // If no termcode matched but 'pastetoggle'
-	    // matched partially it's like an incomplete key
-	    // sequence.
-	    if (keylen == 0 && save_keylen == KEYLEN_PART_KEY)
-		keylen = KEYLEN_PART_KEY;
-
-	    // When getting a partial match, but the last
-	    // characters were not typed, don't wait for a
-	    // typed character to complete the termcode.
-	    // This helps a lot when a ":normal" command ends
-	    // in an ESC.
-	    if (keylen < 0
-		       && typebuf.tb_len == typebuf.tb_maplen)
-		keylen = 0;
-	}
-	else
-	    keylen = 0;
-	if (keylen == 0)	// no matching terminal code
-	{
-#ifdef AMIGA			// check for window bounds report
-	    if (typebuf.tb_maplen == 0 && (typebuf.tb_buf[
-			       typebuf.tb_off] & 0xff) == CSI)
-	    {
-		char_u *s;
-
-		for (s = typebuf.tb_buf + typebuf.tb_off + 1;
-			s < typebuf.tb_buf + typebuf.tb_off
-					      + typebuf.tb_len
-		   && (VIM_ISDIGIT(*s) || *s == ';'
-						|| *s == ' ');
-			++s)
-		    ;
-		if (*s == 'r' || *s == '|') // found one
-		{
-		    del_typebuf((int)(s + 1 -
-		       (typebuf.tb_buf + typebuf.tb_off)), 0);
-		    // get size and redraw screen
-		    shell_resized();
-		    *keylenp = keylen;
-		    return map_result_retry;
-		}
-		if (*s == NUL)	    // need more characters
-		    keylen = KEYLEN_PART_KEY;
-	    }
-	    if (keylen >= 0)
-#endif
-	      // When there was a matching mapping and no
-	      // termcode could be replaced after another one,
-	      // use that mapping (loop around). 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;	    // got character, break for loop
-	      }
-	}
-
-	if (keylen > 0)	    // full matching terminal code
-	{
-#if defined(FEAT_GUI) && defined(FEAT_MENU)
-	    if (typebuf.tb_len >= 2
-		&& typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL
-			 && typebuf.tb_buf[typebuf.tb_off + 1]
-						   == KS_MENU)
-	    {
-		int	idx;
-
-		// Using a menu may cause a break in undo!
-		// It's like using gotchars(), but without
-		// recording or writing to a script file.
-		may_sync_undo();
-		del_typebuf(3, 0);
-		idx = get_menu_index(current_menu, local_State);
-		if (idx != MENU_INDEX_INVALID)
-		{
-		    // In Select mode and a Visual mode menu
-		    // is used:  Switch to Visual mode
-		    // temporarily.  Append K_SELECT to switch
-		    // back to Select mode.
-		    if (VIsual_active && VIsual_select
-			    && (current_menu->modes & VISUAL))
-		    {
-			VIsual_select = FALSE;
-			(void)ins_typebuf(K_SELECT_STRING,
-				  REMAP_NONE, 0, TRUE, FALSE);
-		    }
-		    ins_typebuf(current_menu->strings[idx],
-				current_menu->noremap[idx],
-				0, TRUE,
-				   current_menu->silent[idx]);
-		}
-	    }
-#endif // FEAT_GUI && FEAT_MENU
-	    *keylenp = keylen;
-	    return map_result_retry;	// try mapping again
-	}
-
-	// Partial match: get some more characters.  When a
-	// matching mapping was found use that one.
-	if (mp == NULL || keylen < 0)
-	    keylen = KEYLEN_PART_KEY;
-	else
-	    keylen = mp_match_len;
-    }
-
-    /*
-     * complete match
-     */
-    if (keylen >= 0 && keylen <= typebuf.tb_len)
-    {
-	char_u *map_str;
-
-#ifdef FEAT_EVAL
-	int save_m_expr;
-	int save_m_noremap;
-	int save_m_silent;
-	char_u *save_m_keys;
-	char_u *save_m_str;
-#else
-# define save_m_noremap mp->m_noremap
-# define save_m_silent mp->m_silent
-#endif
-
-	// write chars to script file(s)
-	if (keylen > typebuf.tb_maplen)
-	    gotchars(typebuf.tb_buf + typebuf.tb_off
-					  + typebuf.tb_maplen,
-				  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);
-	}
-
-#ifdef FEAT_EVAL
-	// 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;
-	save_m_keys = NULL;  // only saved when needed
-	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;
-	    int 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
-#endif
-	    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 (save_m_noremap != REMAP_YES)
-		noremap = save_m_noremap;
-	    else if (
-#ifdef FEAT_EVAL
-		STRNCMP(map_str, save_m_keys != NULL
-				   ? save_m_keys : mp->m_keys,
-					 (size_t)keylen)
-#else
-		STRNCMP(map_str, mp->m_keys, (size_t)keylen)
-#endif
-		   != 0)
-		noremap = REMAP_YES;
-	    else
-		noremap = REMAP_SKIP;
-	    i = ins_typebuf(map_str, noremap,
-					 0, TRUE, cmd_silent || save_m_silent);
-#ifdef FEAT_EVAL
-	    if (save_m_expr)
-		vim_free(map_str);
-#endif
-	}
-#ifdef FEAT_EVAL
-	vim_free(save_m_keys);
-	vim_free(save_m_str);
-#endif
-	*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!)
  */
@@ -2466,18 +1942,33 @@ vungetc(int c)
 vgetorpeek(int advance)
 {
     int		c, c1;
+    int		keylen;
+    char_u	*s;
+    mapblock_T	*mp;
+    mapblock_T	*mp2;
+    mapblock_T	*mp_match;
+    int		mp_match_len = 0;
     int		timedout = FALSE;	    /* waited for more than 1 second
 						for mapping to complete */
     int		mapdepth = 0;	    /* check for recursive mapping */
     int		mode_deleted = FALSE;   /* set when mode has been deleted */
+    int		local_State;
+    int		mlen;
+    int		max_mlen;
     int		i;
 #ifdef FEAT_CMDL_INFO
     int		new_wcol, new_wrow;
 #endif
 #ifdef FEAT_GUI
+# ifdef FEAT_MENU
+    int		idx;
+# endif
     int		shape_changed = FALSE;  /* adjusted cursor shape */
 #endif
     int		n;
+#ifdef FEAT_LANGMAP
+    int		nolmaplen;
+#endif
     int		old_wcol, old_wrow;
     int		wait_tb_len;
 
@@ -2495,6 +1986,8 @@ vgetorpeek(int advance)
     if (vgetc_busy > 0 && ex_normal_busy == 0)
 	return NUL;
 
+    local_State = get_real_state();
+
     ++vgetc_busy;
 
     if (advance)
@@ -2539,8 +2032,7 @@ vgetorpeek(int advance)
 	     */
 	    for (;;)
 	    {
-		long	wait_time;
-		int	keylen = 0;
+		long	    wait_time;
 
 		/*
 		 * ui_breakcheck() is slow, don't use it too often when
@@ -2551,11 +2043,11 @@ vgetorpeek(int advance)
 		    line_breakcheck();
 		else
 		    ui_breakcheck();		/* check for CTRL-C */
+		keylen = 0;
 		if (got_int)
 		{
 		    /* flush all input */
 		    c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
-
 		    /*
 		     * If inchar() returns TRUE (script file was active) or we
 		     * are inside a mapping, get out of Insert mode.
@@ -2584,46 +2076,508 @@ vgetorpeek(int advance)
 		else if (typebuf.tb_len > 0)
 		{
 		    /*
-		     * Check for a mapping in "typebuf".
+		     * 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
 		     */
-		    map_result_T result = handle_mapping(
-						&keylen, &timedout, &mapdepth);
+		    mp = NULL;
+		    max_mlen = 0;
+		    c1 = typebuf.tb_buf[typebuf.tb_off];
+		    if (no_mapping == 0 && is_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
+#ifdef FEAT_INS_EXPAND
+			    && !((ctrl_x_mode_not_default()
+						      && vim_is_ctrl_x_key(c1))
+				    || ((compl_cont_status & CONT_LOCAL)
+					&& (c1 == Ctrl_N || c1 == Ctrl_P)))
+#endif
+			    )
+		    {
+#ifdef FEAT_LANGMAP
+			if (c1 == K_SPECIAL)
+			    nolmaplen = 2;
+			else
+			{
+			    LANGMAP_ADJUST(c1,
+					   (State & (CMDLINE | INSERT)) == 0
+					   && get_real_state() != SELECTMODE);
+			    nolmaplen = 0;
+			}
+#endif
+			// First try buffer-local mappings.
+			mp = get_buf_maphash_list(local_State, c1);
+			mp2 = get_maphash_list(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))
+			    {
+#ifdef FEAT_LANGMAP
+				int	nomap = nolmaplen;
+				int	c2;
+#endif
+				/* find the match length of this mapping */
+				for (mlen = 1; mlen < typebuf.tb_len; ++mlen)
+				{
+#ifdef FEAT_LANGMAP
+				    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)
+#else
+				    if (mp->m_keys[mlen] !=
+					typebuf.tb_buf[typebuf.tb_off + mlen])
+#endif
+					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 = mb_unescape(&p1);
+
+				    if (has_mbyte && p2 != NULL
+					  && MB_BYTE2LEN(c1) > MB_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]
+							      != (int)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 (result == map_result_retry)
-			// try mapping again
-			continue;
-		    if (result == map_result_fail)
+				    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)
+				    {
+					/* 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)))
 		    {
-			// failed, use the outer loop
-			c = -1;
-			break;
+			for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen];
+								       ++mlen)
+			    if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off
+								      + mlen])
+				    break;
+			if (p_pt[mlen] == NUL)	/* match */
+			{
+			    /* write chars to script file(s) */
+			    if (mlen > typebuf.tb_maplen)
+				gotchars(typebuf.tb_buf + typebuf.tb_off
+							  + typebuf.tb_maplen,
+						    mlen - typebuf.tb_maplen);
+
+			    del_typebuf(mlen, 0); /* remove the chars */
+			    set_option_value((char_u *)"paste",
+						     (long)!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 (result == map_result_get)
+
+		    if ((mp == NULL || max_mlen >= mp_match_len)
+						 && keylen != KEYLEN_PART_MAP)
 		    {
+			int	save_keylen = keylen;
+
+			/*
+			 * When no matching mapping found or found a
+			 * non-matching mapping that matches at least what the
+			 * matching mapping matched:
+			 * Check if we have a terminal code, when:
+			 *  mapping is allowed,
+			 *  keys have not been mapped,
+			 *  and not an ESC sequence, not in insert mode or
+			 *	p_ek is on,
+			 *  and when not timed out,
+			 */
+			if ((no_mapping == 0 || allow_keys != 0)
+				&& (typebuf.tb_maplen == 0
+				    || (p_remap && typebuf.tb_noremap[
+						   typebuf.tb_off] == RM_YES))
+				&& !timedout)
+			{
+			    keylen = check_termcode(max_mlen + 1,
+							       NULL, 0, NULL);
+
+			    /* If no termcode matched but 'pastetoggle'
+			     * matched partially it's like an incomplete key
+			     * sequence. */
+			    if (keylen == 0 && save_keylen == KEYLEN_PART_KEY)
+				keylen = KEYLEN_PART_KEY;
+
+			    /*
+			     * When getting a partial match, but the last
+			     * characters were not typed, don't wait for a
+			     * typed character to complete the termcode.
+			     * This helps a lot when a ":normal" command ends
+			     * in an ESC.
+			     */
+			    if (keylen < 0
+				       && typebuf.tb_len == typebuf.tb_maplen)
+				keylen = 0;
+			}
+			else
+			    keylen = 0;
+			if (keylen == 0)	/* no matching terminal code */
+			{
+#ifdef AMIGA			/* check for window bounds report */
+			    if (typebuf.tb_maplen == 0 && (typebuf.tb_buf[
+					       typebuf.tb_off] & 0xff) == CSI)
+			    {
+				for (s = typebuf.tb_buf + typebuf.tb_off + 1;
+					s < typebuf.tb_buf + typebuf.tb_off
+							      + typebuf.tb_len
+				   && (VIM_ISDIGIT(*s) || *s == ';'
+								|| *s == ' ');
+					++s)
+				    ;
+				if (*s == 'r' || *s == '|') /* found one */
+				{
+				    del_typebuf((int)(s + 1 -
+				       (typebuf.tb_buf + typebuf.tb_off)), 0);
+				    /* get size and redraw screen */
+				    shell_resized();
+				    continue;
+				}
+				if (*s == NUL)	    /* need more characters */
+				    keylen = KEYLEN_PART_KEY;
+			    }
+			    if (keylen >= 0)
+#endif
+			      /* When there was a matching mapping and no
+			       * termcode could be replaced after another one,
+			       * use that mapping (loop around). If there was
+			       * no mapping use the character from the
+			       * typeahead buffer right here. */
+			      if (mp == NULL)
+			      {
 /*
  * get a character: 2. from the typeahead buffer
  */
-			c = typebuf.tb_buf[typebuf.tb_off] & 255;
-			if (advance)	/* remove chars from tb_buf */
+				c = typebuf.tb_buf[typebuf.tb_off] & 255;
+				if (advance)	/* remove chars from tb_buf */
+				{
+				    cmd_silent = (typebuf.tb_silent > 0);
+				    if (typebuf.tb_maplen > 0)
+					KeyTyped = FALSE;
+				    else
+				    {
+					KeyTyped = TRUE;
+					/* write char to script file(s) */
+					gotchars(typebuf.tb_buf
+							 + typebuf.tb_off, 1);
+				    }
+				    KeyNoremap = typebuf.tb_noremap[
+							      typebuf.tb_off];
+				    del_typebuf(1, 0);
+				}
+				break;	    /* got character, break for loop */
+			      }
+			}
+			if (keylen > 0)	    /* full matching terminal code */
 			{
-			    cmd_silent = (typebuf.tb_silent > 0);
-			    if (typebuf.tb_maplen > 0)
-				KeyTyped = FALSE;
-			    else
+#if defined(FEAT_GUI) && defined(FEAT_MENU)
+			    if (typebuf.tb_len >= 2
+				&& typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL
+					 && typebuf.tb_buf[typebuf.tb_off + 1]
+								   == KS_MENU)
 			    {
-				KeyTyped = TRUE;
-				/* write char to script file(s) */
-				gotchars(typebuf.tb_buf
-						 + typebuf.tb_off, 1);
+				/*
+				 * Using a menu may cause a break in undo!
+				 * It's like using gotchars(), but without
+				 * recording or writing to a script file.
+				 */
+				may_sync_undo();
+				del_typebuf(3, 0);
+				idx = get_menu_index(current_menu, local_State);
+				if (idx != MENU_INDEX_INVALID)
+				{
+				    /*
+				     * In Select mode and a Visual mode menu
+				     * is used:  Switch to Visual mode
+				     * temporarily.  Append K_SELECT to switch
+				     * back to Select mode.
+				     */
+				    if (VIsual_active && VIsual_select
+					    && (current_menu->modes & VISUAL))
+				    {
+					VIsual_select = FALSE;
+					(void)ins_typebuf(K_SELECT_STRING,
+						  REMAP_NONE, 0, TRUE, FALSE);
+				    }
+				    ins_typebuf(current_menu->strings[idx],
+						current_menu->noremap[idx],
+						0, TRUE,
+						   current_menu->silent[idx]);
+				}
 			    }
-			    KeyNoremap = typebuf.tb_noremap[
-						      typebuf.tb_off];
-			    del_typebuf(1, 0);
+#endif /* FEAT_GUI && FEAT_MENU */
+			    continue;	/* try mapping again */
 			}
-			break;
+
+			/* Partial match: get some more characters.  When a
+			 * matching mapping was found use that one. */
+			if (mp == NULL || keylen < 0)
+			    keylen = KEYLEN_PART_KEY;
+			else
+			    keylen = mp_match_len;
 		    }
 
-		    // not enough characters, get more
+		    /* complete match */
+		    if (keylen >= 0 && keylen <= typebuf.tb_len)
+		    {
+#ifdef FEAT_EVAL
+			int save_m_expr;
+			int save_m_noremap;
+			int save_m_silent;
+			char_u *save_m_keys;
+			char_u *save_m_str;
+#else
+# define save_m_noremap mp->m_noremap
+# define save_m_silent mp->m_silent
+#endif
+
+			/* write chars to script file(s) */
+			if (keylen > typebuf.tb_maplen)
+			    gotchars(typebuf.tb_buf + typebuf.tb_off
+							  + typebuf.tb_maplen,
+						  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;
+			}
+
+			/*
+			 * 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);
+			}
+
+#ifdef FEAT_EVAL
+			/* 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;
+			save_m_keys = NULL;  /* only saved when needed */
+			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;
+			    int 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
+#endif
+			    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 (save_m_noremap != REMAP_YES)
+				noremap = save_m_noremap;
+			    else if (
+#ifdef FEAT_EVAL
+				STRNCMP(s, save_m_keys != NULL
+						   ? save_m_keys : mp->m_keys,
+							 (size_t)keylen)
+#else
+				STRNCMP(s, mp->m_keys, (size_t)keylen)
+#endif
+				   != 0)
+				noremap = REMAP_YES;
+			    else
+				noremap = REMAP_SKIP;
+			    i = ins_typebuf(s, noremap,
+					0, TRUE, cmd_silent || save_m_silent);
+#ifdef FEAT_EVAL
+			    if (save_m_expr)
+				vim_free(s);
+#endif
+			}
+#ifdef FEAT_EVAL
+			vim_free(save_m_keys);
+			vim_free(save_m_str);
+#endif
+			if (i == FAIL)
+			{
+			    c = -1;
+			    break;
+			}
+			continue;
+		    }
 		}
 
 /*
--- a/src/version.c
+++ b/src/version.c
@@ -774,6 +774,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1794,
+/**/
     1793,
 /**/
     1792,