diff src/cmdexpand.c @ 22075:eb878f85967e v8.2.1587

patch 8.2.1587: loop for handling keys for the command line is too long Commit: https://github.com/vim/vim/commit/eadee486c70946ad0e1746d77898d6f4f4acc817 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Sep 4 15:37:31 2020 +0200 patch 8.2.1587: loop for handling keys for the command line is too long Problem: Loop for handling keys for the command line is too long. Solution: Move wild menu handling to separate functions. (Yegappan Lakshmanan, closes #6856)
author Bram Moolenaar <Bram@vim.org>
date Fri, 04 Sep 2020 15:45:03 +0200
parents 8a0362947c3a
children 3785043f8768
line wrap: on
line diff
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -2614,6 +2614,255 @@ globpath(
     vim_free(buf);
 }
 
+#ifdef FEAT_WILDMENU
+
+/*
+ * Translate some keys pressed when 'wildmenu' is used.
+ */
+    int
+wildmenu_translate_key(
+	cmdline_info_T	*cclp,
+	int		key,
+	expand_T	*xp,
+	int		did_wild_list)
+{
+    int c = key;
+
+    if (did_wild_list && p_wmnu)
+    {
+	if (c == K_LEFT)
+	    c = Ctrl_P;
+	else if (c == K_RIGHT)
+	    c = Ctrl_N;
+    }
+    // Hitting CR after "emenu Name.": complete submenu
+    if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
+	    && cclp->cmdpos > 1
+	    && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
+	    && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
+	    && (c == '\n' || c == '\r' || c == K_KENTER))
+	c = K_DOWN;
+
+    return c;
+}
+
+/*
+ * Delete characters on the command line, from "from" to the current
+ * position.
+ */
+    static void
+cmdline_del(cmdline_info_T *cclp, int from)
+{
+    mch_memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
+	    (size_t)(cclp->cmdlen - cclp->cmdpos + 1));
+    cclp->cmdlen -= cclp->cmdpos - from;
+    cclp->cmdpos = from;
+}
+
+/*
+ * Handle a key pressed when wild menu is displayed
+ */
+    int
+wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
+{
+    int		c = key;
+    int		i;
+    int		j;
+
+    if (!p_wmnu)
+	return c;
+
+    // Special translations for 'wildmenu'
+    if (xp->xp_context == EXPAND_MENUNAMES)
+    {
+	// Hitting <Down> after "emenu Name.": complete submenu
+	if (c == K_DOWN && cclp->cmdpos > 0
+		&& cclp->cmdbuff[cclp->cmdpos - 1] == '.')
+	    c = p_wc;
+	else if (c == K_UP)
+	{
+	    // Hitting <Up>: Remove one submenu name in front of the
+	    // cursor
+	    int found = FALSE;
+
+	    j = (int)(xp->xp_pattern - cclp->cmdbuff);
+	    i = 0;
+	    while (--j > 0)
+	    {
+		// check for start of menu name
+		if (cclp->cmdbuff[j] == ' '
+			&& cclp->cmdbuff[j - 1] != '\\')
+		{
+		    i = j + 1;
+		    break;
+		}
+		// check for start of submenu name
+		if (cclp->cmdbuff[j] == '.'
+			&& cclp->cmdbuff[j - 1] != '\\')
+		{
+		    if (found)
+		    {
+			i = j + 1;
+			break;
+		    }
+		    else
+			found = TRUE;
+		}
+	    }
+	    if (i > 0)
+		cmdline_del(cclp, i);
+	    c = p_wc;
+	    xp->xp_context = EXPAND_NOTHING;
+	}
+    }
+    if ((xp->xp_context == EXPAND_FILES
+		|| xp->xp_context == EXPAND_DIRECTORIES
+		|| xp->xp_context == EXPAND_SHELLCMD))
+    {
+	char_u upseg[5];
+
+	upseg[0] = PATHSEP;
+	upseg[1] = '.';
+	upseg[2] = '.';
+	upseg[3] = PATHSEP;
+	upseg[4] = NUL;
+
+	if (c == K_DOWN
+		&& cclp->cmdpos > 0
+		&& cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
+		&& (cclp->cmdpos < 3
+		    || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
+		    || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
+	{
+	    // go down a directory
+	    c = p_wc;
+	}
+	else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
+	{
+	    // If in a direct ancestor, strip off one ../ to go down
+	    int found = FALSE;
+
+	    j = cclp->cmdpos;
+	    i = (int)(xp->xp_pattern - cclp->cmdbuff);
+	    while (--j > i)
+	    {
+		if (has_mbyte)
+		    j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
+		if (vim_ispathsep(cclp->cmdbuff[j]))
+		{
+		    found = TRUE;
+		    break;
+		}
+	    }
+	    if (found
+		    && cclp->cmdbuff[j - 1] == '.'
+		    && cclp->cmdbuff[j - 2] == '.'
+		    && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
+	    {
+		cmdline_del(cclp, j - 2);
+		c = p_wc;
+	    }
+	}
+	else if (c == K_UP)
+	{
+	    // go up a directory
+	    int found = FALSE;
+
+	    j = cclp->cmdpos - 1;
+	    i = (int)(xp->xp_pattern - cclp->cmdbuff);
+	    while (--j > i)
+	    {
+		if (has_mbyte)
+		    j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
+		if (vim_ispathsep(cclp->cmdbuff[j])
+# ifdef BACKSLASH_IN_FILENAME
+			&& vim_strchr((char_u *)" *?[{`$%#",
+			    cclp->cmdbuff[j + 1]) == NULL
+# endif
+		   )
+		{
+		    if (found)
+		    {
+			i = j + 1;
+			break;
+		    }
+		    else
+			found = TRUE;
+		}
+	    }
+
+	    if (!found)
+		j = i;
+	    else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
+		j += 4;
+	    else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
+		    && j == i)
+		j += 3;
+	    else
+		j = 0;
+	    if (j > 0)
+	    {
+		// TODO this is only for DOS/UNIX systems - need to put in
+		// machine-specific stuff here and in upseg init
+		cmdline_del(cclp, j);
+		put_on_cmdline(upseg + 1, 3, FALSE);
+	    }
+	    else if (cclp->cmdpos > i)
+		cmdline_del(cclp, i);
+
+	    // Now complete in the new directory. Set KeyTyped in case the
+	    // Up key came from a mapping.
+	    c = p_wc;
+	    KeyTyped = TRUE;
+	}
+    }
+
+    return c;
+}
+
+/*
+ * Free expanded names when finished walking through the matches
+ */
+    void
+wildmenu_cleanup(cmdline_info_T *cclp)
+{
+    int skt = KeyTyped;
+    int old_RedrawingDisabled = RedrawingDisabled;
+
+    if (!p_wmnu || wild_menu_showing == 0)
+	return;
+
+    if (cclp->input_fn)
+	RedrawingDisabled = 0;
+
+    if (wild_menu_showing == WM_SCROLLED)
+    {
+	// Entered command line, move it up
+	cmdline_row--;
+	redrawcmd();
+    }
+    else if (save_p_ls != -1)
+    {
+	// restore 'laststatus' and 'winminheight'
+	p_ls = save_p_ls;
+	p_wmh = save_p_wmh;
+	last_status(FALSE);
+	update_screen(VALID);	// redraw the screen NOW
+	redrawcmd();
+	save_p_ls = -1;
+    }
+    else
+    {
+	win_redraw_last_status(topframe);
+	redraw_statuslines();
+    }
+    KeyTyped = skt;
+    wild_menu_showing = 0;
+    if (cclp->input_fn)
+	RedrawingDisabled = old_RedrawingDisabled;
+}
+#endif
+
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
  * "getcompletion()" function