]> granicus.if.org Git - vim/commitdiff
patch 8.2.1634: loop to handle keys for the command line is too long v8.2.1634
authorBram Moolenaar <Bram@vim.org>
Mon, 7 Sep 2020 20:05:28 +0000 (22:05 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 7 Sep 2020 20:05:28 +0000 (22:05 +0200)
Problem:    Loop to handle keys for the command line is too long.
Solution:   Move a few more parts to separate functions. (Yegappan Lakshmanan,
            closes #6895)

src/ex_getln.c
src/testdir/test_cmdline.vim
src/version.c

index ba10af3a4eaec8e51c93a8e7a31be9a3833584d4..2c157ee52f3d95508f512592f3080fcc3209341c 100644 (file)
@@ -21,6 +21,7 @@
 #define CMDLINE_NOT_CHANGED    1
 #define CMDLINE_CHANGED                2
 #define GOTO_NORMAL_MODE       3
+#define PROCESS_NEXT_KEY       4
 
 // The current cmdline_info.  It is initialized in getcmdline() and after that
 // used by other functions.  When invoking getcmdline() recursively it needs
@@ -39,7 +40,7 @@ static int    extra_char_shift;
 static int     cmd_hkmap = 0;  // Hebrew mapping during command line
 #endif
 
-static char_u  *getcmdline_int(int firstc, long count, int indent, int init_ccline);
+static char_u  *getcmdline_int(int firstc, long count, int indent, int clear_ccline);
 static int     cmdline_charsize(int idx);
 static void    set_cmdspos(void);
 static void    set_cmdspos_cursor(void);
@@ -767,6 +768,220 @@ cmdline_init(void)
     CLEAR_FIELD(ccline);
 }
 
+/*
+ * Handle the backslash key pressed in the command-line mode.  CTRL-\ CTRL-N
+ * goes to Normal mode, CTRL-\ CTRL-G goes to Insert mode when 'insertmode' is
+ * set, CTRL-\ e prompts for an expression.
+ */
+    static int
+cmdline_handle_backslash_key(int c, int *gotesc)
+{
+    ++no_mapping;
+    ++allow_keys;
+    c = plain_vgetc();
+    --no_mapping;
+    --allow_keys;
+
+    // CTRL-\ e doesn't work when obtaining an expression, unless it
+    // is in a mapping.
+    if (c != Ctrl_N && c != Ctrl_G && (c != 'e'
+               || (ccline.cmdfirstc == '=' && KeyTyped)
+#ifdef FEAT_EVAL
+               || cmdline_star > 0
+#endif
+               ))
+    {
+       vungetc(c);
+       return PROCESS_NEXT_KEY;
+    }
+
+#ifdef FEAT_EVAL
+    if (c == 'e')
+    {
+       char_u  *p = NULL;
+       int     len;
+
+       /*
+        * Replace the command line with the result of an expression.
+        * Need to save and restore the current command line, to be
+        * able to enter a new one...
+        */
+       if (ccline.cmdpos == ccline.cmdlen)
+           new_cmdpos = 99999; // keep it at the end
+       else
+           new_cmdpos = ccline.cmdpos;
+
+       c = get_expr_register();
+       if (c == '=')
+       {
+           // Need to save and restore ccline.  And set "textwinlock"
+           // to avoid nasty things like going to another buffer when
+           // evaluating an expression.
+           ++textwinlock;
+           p = get_expr_line();
+           --textwinlock;
+
+           if (p != NULL)
+           {
+               len = (int)STRLEN(p);
+               if (realloc_cmdbuff(len + 1) == OK)
+               {
+                   ccline.cmdlen = len;
+                   STRCPY(ccline.cmdbuff, p);
+                   vim_free(p);
+
+                   // Restore the cursor or use the position set with
+                   // set_cmdline_pos().
+                   if (new_cmdpos > ccline.cmdlen)
+                       ccline.cmdpos = ccline.cmdlen;
+                   else
+                       ccline.cmdpos = new_cmdpos;
+
+                   KeyTyped = FALSE;   // Don't do p_wc completion.
+                   redrawcmd();
+                   return CMDLINE_CHANGED;
+               }
+               vim_free(p);
+           }
+       }
+       beep_flush();
+       got_int = FALSE;        // don't abandon the command line
+       did_emsg = FALSE;
+       emsg_on_display = FALSE;
+       redrawcmd();
+       return CMDLINE_NOT_CHANGED;
+    }
+#endif
+
+    if (c == Ctrl_G && p_im && restart_edit == 0)
+       restart_edit = 'a';
+    *gotesc = TRUE;    // will free ccline.cmdbuff after putting it
+                       // in history
+    return GOTO_NORMAL_MODE;
+}
+
+/*
+ * Completion for 'wildchar' or 'wildcharm' key.
+ * - hitting <ESC> twice means: abandon command line.
+ * - wildcard expansion is only done when the 'wildchar' key is really
+ *   typed, not when it comes from a macro
+ * Returns CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED.
+ */
+    static int
+cmdline_wildchar_complete(
+       int             c,
+       int             escape,
+       int             *did_wild_list,
+       int             *wim_index_p,
+       expand_T        *xp,
+       int             *gotesc)
+{
+    int                wim_index = *wim_index_p;
+    int                res;
+    int                j;
+    int                options = WILD_NO_BEEP;
+
+    if (wim_flags[wim_index] & WIM_BUFLASTUSED)
+       options |= WILD_BUFLASTUSED;
+    if (xp->xp_numfiles > 0)   // typed p_wc at least twice
+    {
+       // if 'wildmode' contains "list" may still need to list
+       if (xp->xp_numfiles > 1
+               && !*did_wild_list
+               && (wim_flags[wim_index] & WIM_LIST))
+       {
+           (void)showmatches(xp, FALSE);
+           redrawcmd();
+           *did_wild_list = TRUE;
+       }
+       if (wim_flags[wim_index] & WIM_LONGEST)
+           res = nextwild(xp, WILD_LONGEST, options, escape);
+       else if (wim_flags[wim_index] & WIM_FULL)
+           res = nextwild(xp, WILD_NEXT, options, escape);
+       else
+           res = OK;       // don't insert 'wildchar' now
+    }
+    else                   // typed p_wc first time
+    {
+       wim_index = 0;
+       j = ccline.cmdpos;
+       // if 'wildmode' first contains "longest", get longest
+       // common part
+       if (wim_flags[0] & WIM_LONGEST)
+           res = nextwild(xp, WILD_LONGEST, options, escape);
+       else
+           res = nextwild(xp, WILD_EXPAND_KEEP, options, escape);
+
+       // if interrupted while completing, behave like it failed
+       if (got_int)
+       {
+           (void)vpeekc();     // remove <C-C> from input stream
+           got_int = FALSE;    // don't abandon the command line
+           (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
+#ifdef FEAT_WILDMENU
+           xp->xp_context = EXPAND_NOTHING;
+#endif
+           *wim_index_p = wim_index;
+           return CMDLINE_CHANGED;
+       }
+
+       // when more than one match, and 'wildmode' first contains
+       // "list", or no change and 'wildmode' contains "longest,list",
+       // list all matches
+       if (res == OK && xp->xp_numfiles > 1)
+       {
+           // a "longest" that didn't do anything is skipped (but not
+           // "list:longest")
+           if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j)
+               wim_index = 1;
+           if ((wim_flags[wim_index] & WIM_LIST)
+#ifdef FEAT_WILDMENU
+                   || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0)
+#endif
+              )
+           {
+               if (!(wim_flags[0] & WIM_LONGEST))
+               {
+#ifdef FEAT_WILDMENU
+                   int p_wmnu_save = p_wmnu;
+                   p_wmnu = 0;
+#endif
+                   // remove match
+                   nextwild(xp, WILD_PREV, 0, escape);
+#ifdef FEAT_WILDMENU
+                   p_wmnu = p_wmnu_save;
+#endif
+               }
+#ifdef FEAT_WILDMENU
+               (void)showmatches(xp, p_wmnu
+                       && ((wim_flags[wim_index] & WIM_LIST) == 0));
+#else
+               (void)showmatches(xp, FALSE);
+#endif
+               redrawcmd();
+               *did_wild_list = TRUE;
+               if (wim_flags[wim_index] & WIM_LONGEST)
+                   nextwild(xp, WILD_LONGEST, options, escape);
+               else if (wim_flags[wim_index] & WIM_FULL)
+                   nextwild(xp, WILD_NEXT, options, escape);
+           }
+           else
+               vim_beep(BO_WILD);
+       }
+#ifdef FEAT_WILDMENU
+       else if (xp->xp_numfiles == -1)
+           xp->xp_context = EXPAND_NOTHING;
+#endif
+    }
+    if (wim_index < 3)
+       ++wim_index;
+    if (c == ESC)
+       *gotesc = TRUE;
+
+    *wim_index_p = wim_index;
+    return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
+}
+
 /*
  * Handle backspace, delete and CTRL-W keys in the command-line mode.
  * Returns:
@@ -1244,6 +1459,41 @@ done:
     return res;
 }
 
+/*
+ * Initialize the current command-line info.
+ */
+    static int
+init_ccline(int firstc, int indent)
+{
+    ccline.overstrike = FALSE;             // always start in insert mode
+
+    /*
+     * set some variables for redrawcmd()
+     */
+    ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
+    ccline.cmdindent = (firstc > 0 ? indent : 0);
+
+    // alloc initial ccline.cmdbuff
+    alloc_cmdbuff(exmode_active ? 250 : indent + 1);
+    if (ccline.cmdbuff == NULL)
+       return FAIL;
+    ccline.cmdlen = ccline.cmdpos = 0;
+    ccline.cmdbuff[0] = NUL;
+    sb_text_start_cmdline();
+
+    // autoindent for :insert and :append
+    if (firstc <= 0)
+    {
+       vim_memset(ccline.cmdbuff, ' ', indent);
+       ccline.cmdbuff[indent] = NUL;
+       ccline.cmdpos = indent;
+       ccline.cmdspos = indent;
+       ccline.cmdlen = indent;
+    }
+
+    return OK;
+}
+
 /*
  * getcmdline() - accept a command line starting with firstc.
  *
@@ -1278,7 +1528,7 @@ getcmdline_int(
     int                firstc,
     long       count UNUSED,   // only used for incremental search
     int                indent,         // indent for inside conditionals
-    int                init_ccline)    // clear ccline first
+    int                clear_ccline)   // clear ccline first
 {
     int                c;
     int                i;
@@ -1316,7 +1566,7 @@ getcmdline_int(
        save_cmdline(&save_ccline);
        did_save_ccline = TRUE;
     }
-    if (init_ccline)
+    if (clear_ccline)
        CLEAR_FIELD(ccline);
 
 #ifdef FEAT_EVAL
@@ -1332,35 +1582,12 @@ getcmdline_int(
        cmd_hkmap = 0;
 #endif
 
-    ccline.overstrike = FALSE;             // always start in insert mode
-
 #ifdef FEAT_SEARCH_EXTRA
     init_incsearch_state(&is_state);
 #endif
 
-    /*
-     * set some variables for redrawcmd()
-     */
-    ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
-    ccline.cmdindent = (firstc > 0 ? indent : 0);
-
-    // alloc initial ccline.cmdbuff
-    alloc_cmdbuff(exmode_active ? 250 : indent + 1);
-    if (ccline.cmdbuff == NULL)
+    if (init_ccline(firstc, indent) != OK)
        goto theend;    // out of memory
-    ccline.cmdlen = ccline.cmdpos = 0;
-    ccline.cmdbuff[0] = NUL;
-    sb_text_start_cmdline();
-
-    // autoindent for :insert and :append
-    if (firstc <= 0)
-    {
-       vim_memset(ccline.cmdbuff, ' ', indent);
-       ccline.cmdbuff[indent] = NUL;
-       ccline.cmdpos = indent;
-       ccline.cmdspos = indent;
-       ccline.cmdlen = indent;
-    }
 
     ExpandInit(&xpc);
     ccline.xpc = &xpc;
@@ -1572,88 +1799,15 @@ getcmdline_int(
        // mode when 'insertmode' is set, CTRL-\ e prompts for an expression.
        if (c == Ctrl_BSL)
        {
-           ++no_mapping;
-           ++allow_keys;
-           c = plain_vgetc();
-           --no_mapping;
-           --allow_keys;
-           // CTRL-\ e doesn't work when obtaining an expression, unless it
-           // is in a mapping.
-           if (c != Ctrl_N && c != Ctrl_G && (c != 'e'
-                                   || (ccline.cmdfirstc == '=' && KeyTyped)
-#ifdef FEAT_EVAL
-                                   || cmdline_star > 0
-#endif
-                                   ))
-           {
-               vungetc(c);
-               c = Ctrl_BSL;
-           }
-#ifdef FEAT_EVAL
-           else if (c == 'e')
-           {
-               char_u  *p = NULL;
-               int     len;
-
-               /*
-                * Replace the command line with the result of an expression.
-                * Need to save and restore the current command line, to be
-                * able to enter a new one...
-                */
-               if (ccline.cmdpos == ccline.cmdlen)
-                   new_cmdpos = 99999; // keep it at the end
-               else
-                   new_cmdpos = ccline.cmdpos;
-
-               c = get_expr_register();
-               if (c == '=')
-               {
-                   // Need to save and restore ccline.  And set "textwinlock"
-                   // to avoid nasty things like going to another buffer when
-                   // evaluating an expression.
-                   ++textwinlock;
-                   p = get_expr_line();
-                   --textwinlock;
-
-                   if (p != NULL)
-                   {
-                       len = (int)STRLEN(p);
-                       if (realloc_cmdbuff(len + 1) == OK)
-                       {
-                           ccline.cmdlen = len;
-                           STRCPY(ccline.cmdbuff, p);
-                           vim_free(p);
-
-                           // Restore the cursor or use the position set with
-                           // set_cmdline_pos().
-                           if (new_cmdpos > ccline.cmdlen)
-                               ccline.cmdpos = ccline.cmdlen;
-                           else
-                               ccline.cmdpos = new_cmdpos;
-
-                           KeyTyped = FALSE;   // Don't do p_wc completion.
-                           redrawcmd();
-                           goto cmdline_changed;
-                       }
-                       vim_free(p);
-                   }
-               }
-               beep_flush();
-               got_int = FALSE;        // don't abandon the command line
-               did_emsg = FALSE;
-               emsg_on_display = FALSE;
-               redrawcmd();
+           res = cmdline_handle_backslash_key(c, &gotesc);
+           if (res == CMDLINE_CHANGED)
+               goto cmdline_changed;
+           else if (res == CMDLINE_NOT_CHANGED)
                goto cmdline_not_changed;
-           }
-#endif
-           else
-           {
-               if (c == Ctrl_G && p_im && restart_edit == 0)
-                   restart_edit = 'a';
-               gotesc = TRUE;  // will free ccline.cmdbuff after putting it
-                               // in history
-               goto returncmd; // back to Normal mode
-           }
+           else if (res == GOTO_NORMAL_MODE)
+               goto returncmd;         // back to cmd mode
+           c = Ctrl_BSL;               // backslash key not processed by
+                                       // cmdline_handle_backslash_key()
        }
 
 #ifdef FEAT_CMDWIN
@@ -1705,115 +1859,12 @@ getcmdline_int(
            }
        }
 
-       /*
-        * Completion for 'wildchar' or 'wildcharm' key.
-        * - hitting <ESC> twice means: abandon command line.
-        * - wildcard expansion is only done when the 'wildchar' key is really
-        *   typed, not when it comes from a macro
-        */
+       // Completion for 'wildchar' or 'wildcharm' key.
        if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm)
        {
-           int options = WILD_NO_BEEP;
-
-           if (wim_flags[wim_index] & WIM_BUFLASTUSED)
-               options |= WILD_BUFLASTUSED;
-           if (xpc.xp_numfiles > 0)   // typed p_wc at least twice
-           {
-               // if 'wildmode' contains "list" may still need to list
-               if (xpc.xp_numfiles > 1
-                       && !did_wild_list
-                       && (wim_flags[wim_index] & WIM_LIST))
-               {
-                   (void)showmatches(&xpc, FALSE);
-                   redrawcmd();
-                   did_wild_list = TRUE;
-               }
-               if (wim_flags[wim_index] & WIM_LONGEST)
-                   res = nextwild(&xpc, WILD_LONGEST, options,
-                                                              firstc != '@');
-               else if (wim_flags[wim_index] & WIM_FULL)
-                   res = nextwild(&xpc, WILD_NEXT, options, firstc != '@');
-               else
-                   res = OK;       // don't insert 'wildchar' now
-           }
-           else                    // typed p_wc first time
-           {
-               wim_index = 0;
-               j = ccline.cmdpos;
-               // if 'wildmode' first contains "longest", get longest
-               // common part
-               if (wim_flags[0] & WIM_LONGEST)
-                   res = nextwild(&xpc, WILD_LONGEST, options, firstc != '@');
-               else
-                   res = nextwild(&xpc, WILD_EXPAND_KEEP, options,
-                                                               firstc != '@');
-
-               // if interrupted while completing, behave like it failed
-               if (got_int)
-               {
-                   (void)vpeekc();     // remove <C-C> from input stream
-                   got_int = FALSE;    // don't abandon the command line
-                   (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE);
-#ifdef FEAT_WILDMENU
-                   xpc.xp_context = EXPAND_NOTHING;
-#endif
-                   goto cmdline_changed;
-               }
-
-               // when more than one match, and 'wildmode' first contains
-               // "list", or no change and 'wildmode' contains "longest,list",
-               // list all matches
-               if (res == OK && xpc.xp_numfiles > 1)
-               {
-                   // a "longest" that didn't do anything is skipped (but not
-                   // "list:longest")
-                   if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j)
-                       wim_index = 1;
-                   if ((wim_flags[wim_index] & WIM_LIST)
-#ifdef FEAT_WILDMENU
-                         || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0)
-#endif
-                           )
-                   {
-                       if (!(wim_flags[0] & WIM_LONGEST))
-                       {
-#ifdef FEAT_WILDMENU
-                           int p_wmnu_save = p_wmnu;
-                           p_wmnu = 0;
-#endif
-                           // remove match
-                           nextwild(&xpc, WILD_PREV, 0, firstc != '@');
-#ifdef FEAT_WILDMENU
-                           p_wmnu = p_wmnu_save;
-#endif
-                       }
-#ifdef FEAT_WILDMENU
-                       (void)showmatches(&xpc, p_wmnu
-                               && ((wim_flags[wim_index] & WIM_LIST) == 0));
-#else
-                       (void)showmatches(&xpc, FALSE);
-#endif
-                       redrawcmd();
-                       did_wild_list = TRUE;
-                       if (wim_flags[wim_index] & WIM_LONGEST)
-                           nextwild(&xpc, WILD_LONGEST, options,
-                                                              firstc != '@');
-                       else if (wim_flags[wim_index] & WIM_FULL)
-                           nextwild(&xpc, WILD_NEXT, options, firstc != '@');
-                   }
-                   else
-                       vim_beep(BO_WILD);
-               }
-#ifdef FEAT_WILDMENU
-               else if (xpc.xp_numfiles == -1)
-                   xpc.xp_context = EXPAND_NOTHING;
-#endif
-           }
-           if (wim_index < 3)
-               ++wim_index;
-           if (c == ESC)
-               gotesc = TRUE;
-           if (res == OK)
+           res = cmdline_wildchar_complete(c, firstc != '@', &did_wild_list,
+                   &wim_index, &xpc, &gotesc);
+           if (res == CMDLINE_CHANGED)
                goto cmdline_changed;
        }
 
index 628db67a60187caec53aeeb6377d5c38ece3ebe7..a547ac4e632cdbd0fefe1c96f5b6ac9d3883f435 100644 (file)
@@ -829,6 +829,15 @@ func Test_cmdline_complete_various()
   " completion after a range followed by a pipe (|) character
   call feedkeys(":1,10 | chist\t\<C-B>\"\<CR>", 'xt')
   call assert_equal('"1,10 | chistory', @:)
+
+  " use <Esc> as the 'wildchar' for completion
+  set wildchar=<Esc>
+  call feedkeys(":g/a\\xb/clearj\<Esc>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"g/a\xb/clearjumps', @:)
+  " pressing <esc> twice should cancel the command
+  call feedkeys(":chist\<Esc>\<Esc>", 'xt')
+  call assert_equal('"g/a\xb/clearjumps', @:)
+  set wildchar&
 endfunc
 
 func Test_cmdline_write_alternatefile()
index 1b91560bba117d60f627190677d66ff6efd00be1..d941c1611760093f3f96572211b634bd9626ecbd 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1634,
 /**/
     1633,
 /**/