To: vim_dev@googlegroups.com Subject: Patch 8.2.1634 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 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 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 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 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\\"\", 'xt') call assert_equal('"1,10 | chistory', @:) + + " use as the 'wildchar' for completion + set wildchar= + call feedkeys(":g/a\\xb/clearj\\\"\", 'xt') + call assert_equal('"g/a\xb/clearjumps', @:) + " pressing twice should cancel the command + call feedkeys(":chist\\", '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 -- Bram@Moolenaar.net -- 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 ///