Patch 8.2.1634
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)
Files:      src/ex_getln.c, src/testdir/test_cmdline.vim


*** ../vim-8.2.1633/src/ex_getln.c      2020-09-06 15:53:56.627809181 +0200
--- src/ex_getln.c      2020-09-07 22:01:37.510195897 +0200
***************
*** 21,26 ****
--- 21,27 ----
  #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,45 ****
  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 int    cmdline_charsize(int idx);
  static void   set_cmdspos(void);
  static void   set_cmdspos_cursor(void);
--- 40,46 ----
  static int    cmd_hkmap = 0;  // Hebrew mapping during command line
  #endif
  
! 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);
***************
*** 768,773 ****
--- 769,988 ----
  }
  
  /*
+  * 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:
   *  CMDLINE_NOT_CHANGED - if the command line is not changed
***************
*** 1245,1250 ****
--- 1460,1500 ----
  }
  
  /*
+  * 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.
   *
   * firstc == ':'          get ":" command line.
***************
*** 1278,1284 ****
      int               firstc,
      long      count UNUSED,   // only used for incremental search
      int               indent,         // indent for inside conditionals
!     int               init_ccline)    // clear ccline first
  {
      int               c;
      int               i;
--- 1528,1534 ----
      int               firstc,
      long      count UNUSED,   // only used for incremental search
      int               indent,         // indent for inside conditionals
!     int               clear_ccline)   // clear ccline first
  {
      int               c;
      int               i;
***************
*** 1316,1322 ****
        save_cmdline(&save_ccline);
        did_save_ccline = TRUE;
      }
!     if (init_ccline)
        CLEAR_FIELD(ccline);
  
  #ifdef FEAT_EVAL
--- 1566,1572 ----
        save_cmdline(&save_ccline);
        did_save_ccline = TRUE;
      }
!     if (clear_ccline)
        CLEAR_FIELD(ccline);
  
  #ifdef FEAT_EVAL
***************
*** 1332,1366 ****
        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)
        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;
--- 1582,1593 ----
        cmd_hkmap = 0;
  #endif
  
  #ifdef FEAT_SEARCH_EXTRA
      init_incsearch_state(&is_state);
  #endif
  
!     if (init_ccline(firstc, indent) != OK)
        goto theend;    // out of memory
  
      ExpandInit(&xpc);
      ccline.xpc = &xpc;
***************
*** 1572,1659 ****
        // 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();
                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
!           }
        }
  
  #ifdef FEAT_CMDWIN
--- 1799,1813 ----
        // mode when 'insertmode' is set, CTRL-\ e prompts for an expression.
        if (c == Ctrl_BSL)
        {
!           res = cmdline_handle_backslash_key(c, &gotesc);
!           if (res == CMDLINE_CHANGED)
!               goto cmdline_changed;
!           else if (res == CMDLINE_NOT_CHANGED)
                goto cmdline_not_changed;
!           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,1819 ****
            }
        }
  
!       /*
!        * 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
!        */
        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)
                goto cmdline_changed;
        }
  
--- 1859,1870 ----
            }
        }
  
!       // Completion for 'wildchar' or 'wildcharm' key.
        if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm)
        {
!           res = cmdline_wildchar_complete(c, firstc != '@', &did_wild_list,
!                   &wim_index, &xpc, &gotesc);
!           if (res == CMDLINE_CHANGED)
                goto cmdline_changed;
        }
  
*** ../vim-8.2.1633/src/testdir/test_cmdline.vim        2020-09-04 
21:18:40.480161935 +0200
--- src/testdir/test_cmdline.vim        2020-09-07 21:58:07.854738838 +0200
***************
*** 829,834 ****
--- 829,843 ----
    " 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()
*** ../vim-8.2.1633/src/version.c       2020-09-07 18:53:18.387974569 +0200
--- src/version.c       2020-09-07 21:59:37.598506235 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1634,
  /**/

-- 
Q: How do you tell the difference between a female cat and a male cat?
A: You ask it a question and if HE answers, it's a male but, if SHE
   answers, it's a female.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202009072005.087K5tEi1275567%40masaka.moolenaar.net.

Raspunde prin e-mail lui