diff options
Diffstat (limited to 'src/nvim/ex_getln.c')
| -rw-r--r-- | src/nvim/ex_getln.c | 1853 |
1 files changed, 1070 insertions, 783 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a0bcccb5be..76c3680742 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1,50 +1,49 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -/* - * ex_getln.c: Functions for entering and editing an Ex command line. - */ +// ex_getln.c: Functions for entering and editing an Ex command line. #include <assert.h> #include <inttypes.h> +#include <limits.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> +#include <time.h> +#include "klib/kvec.h" #include "nvim/api/extmark.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/arabic.h" #include "nvim/ascii.h" -#include "nvim/assert.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" -#include "nvim/cursor_shape.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval.h" -#include "nvim/event/loop.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" -#include "nvim/fileio.h" -#include "nvim/func_attr.h" +#include "nvim/extmark.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/grid.h" -#include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" -#include "nvim/indent.h" #include "nvim/keycodes.h" -#include "nvim/lib/kvec.h" -#include "nvim/log.h" -#include "nvim/main.h" +#include "nvim/macros.h" #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -53,15 +52,18 @@ #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" +#include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/optionstr.h" #include "nvim/os/input.h" -#include "nvim/os/time.h" +#include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/popupmenu.h" +#include "nvim/pos.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/state.h" #include "nvim/strings.h" @@ -96,7 +98,7 @@ typedef struct { pos_T match_end; bool did_incsearch; bool incsearch_postponed; - int magic_save; + optmagic_T magic_overruled_save; } incsearch_state_T; typedef struct command_line_state { @@ -105,9 +107,9 @@ typedef struct command_line_state { long count; int indent; int c; - int gotesc; // TRUE when <ESC> just typed - int do_abbr; // when TRUE check for abbr. - char_u *lookfor; // string to match + int gotesc; // true when <ESC> just typed + int do_abbr; // when true check for abbr. + char *lookfor; // string to match int hiscnt; // current history line in use int save_hiscnt; // history line before attempting // to jump to next match @@ -115,10 +117,9 @@ typedef struct command_line_state { incsearch_state_T is_state; int did_wild_list; // did wild_list() recently int wim_index; // index in wim_flags[] - int res; int save_msg_scroll; int save_State; // remember State when called - char_u *save_p_icm; + char *save_p_icm; int some_key_typed; // one of the keys was typed // mouse drag and release events are ignored, unless they are // preceded with a mouse down event @@ -138,6 +139,7 @@ typedef struct cmdpreview_win_info { typedef struct cmdpreview_buf_info { buf_T *buf; + bool save_b_u_synced; time_t save_b_u_time_cur; long save_b_u_seq_cur; u_header_T *save_b_u_newhead; @@ -154,6 +156,14 @@ typedef struct cmdpreview_info { garray_T save_view; } CpInfo; +/// Return value when handling keys in command-line mode. +enum { + CMDLINE_NOT_CHANGED = 1, + CMDLINE_CHANGED = 2, + GOTO_NORMAL_MODE = 3, + 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 /// to be saved with save_cmdline() and restored with restore_cmdline(). @@ -170,6 +180,8 @@ static Array cmdline_block = ARRAY_DICT_INIT; /// user interrupting highlight function to not interrupt command-line. static bool getln_interrupted_highlight = false; +static int cedit_key = -1; ///< key value of 'cedit' option + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" #endif @@ -206,7 +218,7 @@ static void init_incsearch_state(incsearch_state_T *s) s->match_start = curwin->w_cursor; s->did_incsearch = false; s->incsearch_postponed = false; - s->magic_save = p_magic; + s->magic_overruled_save = magic_overruled; clearpos(&s->match_end); s->save_cursor = curwin->w_cursor; // may be restored later s->search_start = curwin->w_cursor; @@ -229,6 +241,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s pos_T save_cursor; bool use_last_pat; bool retval = false; + magic_T magic = 0; *skiplen = 0; *patlen = ccline.cmdlen; @@ -253,7 +266,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s exarg_T ea = { .line1 = 1, .line2 = 1, - .cmd = (char *)ccline.cmdbuff, + .cmd = ccline.cmdbuff, .addr_type = ADDR_LINES, }; @@ -261,7 +274,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true); cmd = skip_range(ea.cmd, NULL); - if (vim_strchr("sgvl", *cmd) == NULL) { + if (vim_strchr("sgvl", (uint8_t)(*cmd)) == NULL) { goto theend; } @@ -271,16 +284,16 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s goto theend; } - if (STRNCMP(cmd, "substitute", p - cmd) == 0 - || STRNCMP(cmd, "smagic", p - cmd) == 0 - || STRNCMP(cmd, "snomagic", MAX(p - cmd, 3)) == 0 - || STRNCMP(cmd, "vglobal", p - cmd) == 0) { + if (strncmp(cmd, "substitute", (size_t)(p - cmd)) == 0 + || strncmp(cmd, "smagic", (size_t)(p - cmd)) == 0 + || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0 + || strncmp(cmd, "vglobal", (size_t)(p - cmd)) == 0) { if (*cmd == 's' && cmd[1] == 'm') { - p_magic = true; + magic_overruled = OPTION_MAGIC_ON; } else if (*cmd == 's' && cmd[1] == 'n') { - p_magic = false; + magic_overruled = OPTION_MAGIC_OFF; } - } else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) { + } else if (strncmp(cmd, "sort", (size_t)MAX(p - cmd, 3)) == 0) { // skip over ! and flags if (*p == '!') { p = skipwhite(p + 1); @@ -291,11 +304,11 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s if (*p == NUL) { goto theend; } - } else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0 - || STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0 - || STRNCMP(cmd, "lvimgrep", MAX(p - cmd, 2)) == 0 - || STRNCMP(cmd, "lvimgrepadd", MAX(p - cmd, 9)) == 0 - || STRNCMP(cmd, "global", p - cmd) == 0) { + } else if (strncmp(cmd, "vimgrep", (size_t)MAX(p - cmd, 3)) == 0 + || strncmp(cmd, "vimgrepadd", (size_t)MAX(p - cmd, 8)) == 0 + || strncmp(cmd, "lvimgrep", (size_t)MAX(p - cmd, 2)) == 0 + || strncmp(cmd, "lvimgrepadd", (size_t)MAX(p - cmd, 9)) == 0 + || strncmp(cmd, "global", (size_t)(p - cmd)) == 0) { // skip over "!/". if (*p == '!') { p++; @@ -311,9 +324,9 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s } p = skipwhite(p); - delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++; + delim = (delim_optional && vim_isIDc((uint8_t)(*p))) ? ' ' : *p++; *search_delim = delim; - end = (char *)skip_regexp((char_u *)p, delim, p_magic, NULL); + end = skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic); use_last_pat = end == p && *end == delim; if (end == p && !use_last_pat) { @@ -323,10 +336,8 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s // Don't do 'hlsearch' highlighting if the pattern matches everything. if (!use_last_pat) { char c = *end; - int empty; - *end = NUL; - empty = empty_pattern((char_u *)p); + bool empty = empty_pattern_magic(p, strlen(p), magic); *end = c; if (empty) { goto theend; @@ -334,7 +345,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s } // found a non-empty pattern or // - *skiplen = (int)((char_u *)p - ccline.cmdbuff); + *skiplen = (int)(p - ccline.cmdbuff); *patlen = (int)(end - p); // parse the address range @@ -369,8 +380,8 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat pos_T end_pos; proftime_T tm; int skiplen, patlen; - char_u next_char; - char_u use_last_pat; + char next_char; + bool use_last_pat; int search_delim; // Parsing range may already set the last search pattern. @@ -415,7 +426,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat if (patlen == 0 && !use_last_pat) { found = 0; set_no_hlsearch(true); // turn off previous highlight - redraw_all_later(SOME_VALID); + redraw_all_later(UPD_SOME_VALID); } else { int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; ui_busy_start(); @@ -481,14 +492,14 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat } else { end_pos = curwin->w_cursor; // shutup gcc 4 } - // + // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". if (!use_last_pat) { next_char = ccline.cmdbuff[skiplen + patlen]; ccline.cmdbuff[skiplen + patlen] = NUL; - if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) { - redraw_all_later(SOME_VALID); + if (empty_pattern(ccline.cmdbuff + skiplen, search_delim) && !no_hlsearch) { + redraw_all_later(UPD_SOME_VALID); set_no_hlsearch(true); } ccline.cmdbuff[skiplen + patlen] = next_char; @@ -500,7 +511,8 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat curwin->w_redr_status = true; } - update_screen(SOME_VALID); + redraw_later(curwin, UPD_SOME_VALID); + update_screen(); highlight_match = false; restore_last_search_pattern(); @@ -550,7 +562,7 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) *c = mb_tolower(*c); } if (*c == search_delim - || vim_strchr((p_magic ? "\\~^$.*[" : "\\^$"), *c) != NULL) { + || vim_strchr((magic_isset() ? "\\~^$.*[" : "\\^$"), *c) != NULL) { // put a backslash before special characters stuffcharReadbuff(*c); *c = '\\'; @@ -563,60 +575,74 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool call_update_screen) { - if (s->did_incsearch) { - s->did_incsearch = false; - if (gotesc) { + if (!s->did_incsearch) { + return; + } + + s->did_incsearch = false; + if (gotesc) { + curwin->w_cursor = s->save_cursor; + } else { + if (!equalpos(s->save_cursor, s->search_start)) { + // put the '" mark at the original position curwin->w_cursor = s->save_cursor; - } else { - if (!equalpos(s->save_cursor, s->search_start)) { - // put the '" mark at the original position - curwin->w_cursor = s->save_cursor; - setpcmark(); - } - curwin->w_cursor = s->search_start; // -V519 + setpcmark(); } - restore_viewstate(curwin, &s->old_viewstate); - highlight_match = false; + curwin->w_cursor = s->search_start; // -V519 + } + restore_viewstate(curwin, &s->old_viewstate); + highlight_match = false; - // by default search all lines - search_first_line = 0; - search_last_line = MAXLNUM; + // by default search all lines + search_first_line = 0; + search_last_line = MAXLNUM; - p_magic = s->magic_save; + magic_overruled = s->magic_overruled_save; - validate_cursor(); // needed for TAB - redraw_all_later(SOME_VALID); - if (call_update_screen) { - update_screen(SOME_VALID); - } + validate_cursor(); // needed for TAB + redraw_all_later(UPD_SOME_VALID); + if (call_update_screen) { + update_screen(); } } -/// Internal entry point for cmdline mode. -/// -/// @param count only used for incremental search -/// @param indent indent for inside conditionals -/// @param init_ccline clear ccline first -static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) +/// Initialize the current command-line info. +static void init_ccline(int firstc, int indent) { - const bool cmdheight0 = !ui_has_messages(); + ccline.overstrike = false; // always start in insert mode - if (cmdheight0) { - const long save_so = lastwin->w_p_so; + assert(indent >= 0); - // If cmdheight is 0, cmdheight must be set to 1 when we enter the - // command line. Set "made_cmdheight_nonzero" and reset 'scrolloff' to - // avoid scrolling the last window. - made_cmdheight_nonzero = true; + // set some variables for redrawcmd() + ccline.cmdfirstc = (firstc == '@' ? 0 : firstc); + ccline.cmdindent = (firstc > 0 ? indent : 0); - lastwin->w_p_so = 0; - set_option_value("ch", 1L, NULL, 0); - update_screen(VALID); // redraw the screen NOW + // alloc initial ccline.cmdbuff + alloc_cmdbuff(indent + 50); + ccline.cmdlen = ccline.cmdpos = 0; + ccline.cmdbuff[0] = NUL; - made_cmdheight_nonzero = false; - lastwin->w_p_so = save_so; + ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL, + .colors = KV_INITIAL_VALUE }; + sb_text_start_cmdline(); + + // autoindent for :insert and :append + if (firstc <= 0) { + memset(ccline.cmdbuff, ' ', (size_t)indent); + ccline.cmdbuff[indent] = NUL; + ccline.cmdpos = indent; + ccline.cmdspos = indent; + ccline.cmdlen = indent; } +} +/// Internal entry point for cmdline mode. +/// +/// @param count only used for incremental search +/// @param indent indent for inside conditionals +/// @param clear_ccline clear ccline first +static uint8_t *command_line_enter(int firstc, long count, int indent, bool clear_ccline) +{ // can be invoked recursively, identify each level static int cmdline_level = 0; cmdline_level++; @@ -632,20 +658,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init .ignore_drag_release = true, }; CommandLineState *s = &state; - s->save_p_icm = vim_strsave(p_icm); + s->save_p_icm = xstrdup(p_icm); init_incsearch_state(&s->is_state); CmdlineInfo save_ccline; bool did_save_ccline = false; if (ccline.cmdbuff != NULL) { - // Currently ccline can never be in use if init_ccline is false. + // Currently ccline can never be in use if clear_ccline is false. // Some changes will be needed if this is no longer the case. - assert(init_ccline); + assert(clear_ccline); // Being called recursively. Since ccline is global, we need to save // the current buffer and restore it when returning. save_cmdline(&save_ccline); did_save_ccline = true; - } else if (init_ccline) { + } else if (clear_ccline) { CLEAR_FIELD(ccline); } @@ -659,33 +685,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init cmd_hkmap = 0; } + init_ccline(s->firstc, s->indent); ccline.prompt_id = last_prompt_id++; ccline.level = cmdline_level; - ccline.overstrike = false; // always start in insert mode - - assert(indent >= 0); - - // set some variables for redrawcmd() - ccline.cmdfirstc = (s->firstc == '@' ? 0 : s->firstc); - ccline.cmdindent = (s->firstc > 0 ? s->indent : 0); - - // alloc initial ccline.cmdbuff - alloc_cmdbuff(indent + 50); - ccline.cmdlen = ccline.cmdpos = 0; - ccline.cmdbuff[0] = NUL; - - ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL, - .colors = KV_INITIAL_VALUE }; - sb_text_start_cmdline(); - - // autoindent for :insert and :append - if (s->firstc <= 0) { - memset(ccline.cmdbuff, ' ', (size_t)s->indent); - ccline.cmdbuff[s->indent] = NUL; - ccline.cmdpos = s->indent; - ccline.cmdspos = s->indent; - ccline.cmdlen = s->indent; - } if (cmdline_level == 50) { // Somehow got into a loop recursively calling getcmdline(), bail out. @@ -722,8 +724,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init if (ccline.input_fn) { s->xpc.xp_context = ccline.xp_context; - s->xpc.xp_pattern = (char *)ccline.cmdbuff; - s->xpc.xp_arg = (char *)ccline.xp_arg; + s->xpc.xp_pattern = ccline.cmdbuff; + s->xpc.xp_arg = ccline.xp_arg; } // Avoid scrolling when called by a recursive do_cmdline(), e.g. when @@ -751,13 +753,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init TryState tstate; Error err = ERROR_INIT; bool tl_ret = true; - save_v_event_T save_v_event; - dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); firstcbuf[1] = 0; if (has_event(EVENT_CMDLINEENTER)) { + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); + // set v:event to a dictionary with information about the commandline tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); @@ -770,7 +773,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { msg_putchar('\n'); - msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + msg_scroll = true; + msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST); api_clear_error(&err); redrawcmd(); } @@ -791,11 +795,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init // Redraw the statusline in case it uses the current mode using the mode() // function. - if (!cmd_silent && msg_scrolled == 0) { + if (!cmd_silent) { bool found_one = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (*p_stl != NUL || *wp->w_p_stl != NUL) { + if (*p_stl != NUL || *wp->w_p_stl != NUL || *p_wbr != NUL || *wp->w_p_wbr != NUL) { wp->w_redr_status = true; found_one = true; } @@ -819,6 +823,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init state_enter(&s->state); if (has_event(EVENT_CMDLINELEAVE)) { + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); tv_dict_set_keys_readonly(dict); @@ -852,7 +859,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init s->histype == HIST_SEARCH ? s->firstc : NUL); if (s->firstc == ':') { xfree(new_last_cmdline); - new_last_cmdline = vim_strsave(ccline.cmdbuff); + new_last_cmdline = xstrdup(ccline.cmdbuff); } } @@ -865,12 +872,18 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init // If the line is too long, clear it, so ruler and shown command do // not get printed in the middle of it. msg_check(); + if (p_ch == 0 && !ui_has(kUIMessages)) { + if (must_redraw < UPD_VALID) { + must_redraw = UPD_VALID; + } + } msg_scroll = s->save_msg_scroll; redir_off = false; if (!tl_ret && ERROR_SET(&err)) { msg_putchar('\n'); - msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + emsg(err.msg); + did_emsg = false; api_clear_error(&err); } @@ -879,11 +892,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init need_wait_return = false; } - set_string_option_direct("icm", -1, (char *)s->save_p_icm, OPT_FREE, SID_NONE); + set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE, SID_NONE); State = s->save_State; if (cmdpreview != save_cmdpreview) { cmdpreview = save_cmdpreview; // restore preview state - redraw_all_later(SOME_VALID); + redraw_all_later(UPD_SOME_VALID); } may_trigger_modechanged(); setmouse(); @@ -895,7 +908,7 @@ theend: xfree(ccline.last_colors.cmdbuff); kv_destroy(ccline.last_colors.colors); - char_u *p = ccline.cmdbuff; + char *p = ccline.cmdbuff; if (ui_has(kUICmdline)) { ui_call_cmdline_hide(ccline.level); @@ -910,18 +923,7 @@ theend: ccline.cmdbuff = NULL; } - if (cmdheight0) { - made_cmdheight_nonzero = true; - - // Restore cmdheight - set_option_value("ch", 0L, NULL, 0); - // Redraw is needed for command line completion - redraw_all_later(CLEAR); - - made_cmdheight_nonzero = false; - } - - return p; + return (uint8_t *)p; } static int command_line_check(VimState *state) @@ -940,6 +942,191 @@ static int command_line_check(VimState *state) return 1; } +/// Handle CTRL-\ pressed in Command-line mode: +/// - CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode. +/// - CTRL-\ e prompts for an expression. +static int command_line_handle_ctrl_bsl(CommandLineState *s) +{ + no_mapping++; + allow_keys++; + s->c = plain_vgetc(); + no_mapping--; + allow_keys--; + + // CTRL-\ e doesn't work when obtaining an expression, unless it + // is in a mapping. + if (s->c != Ctrl_N + && s->c != Ctrl_G + && (s->c != 'e' + || (ccline.cmdfirstc == '=' && KeyTyped) + || cmdline_star > 0)) { + vungetc(s->c); + return PROCESS_NEXT_KEY; + } + + if (s->c == 'e') { + // Replace the command line with the result of an expression. + // This will call getcmdline() recursively in get_expr_register(). + if (ccline.cmdpos == ccline.cmdlen) { + new_cmdpos = 99999; // keep it at the end + } else { + new_cmdpos = ccline.cmdpos; + } + + s->c = get_expr_register(); + if (s->c == '=') { + // Evaluate the expression. Set "textlock" to avoid nasty things + // like going to another buffer. + textlock++; + char *p = get_expr_line(); + textlock--; + + if (p != NULL) { + int len = (int)strlen(p); + realloc_cmdbuff(len + 1); + ccline.cmdlen = len; + STRCPY(ccline.cmdbuff, p); + xfree(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; + } + } + beep_flush(); + got_int = false; // don't abandon the command line + did_emsg = false; + emsg_on_display = false; + redrawcmd(); + return CMDLINE_NOT_CHANGED; + } + + s->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 +/// @return CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED. +static int command_line_wildchar_complete(CommandLineState *s) +{ + int res; + int options = WILD_NO_BEEP; + if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) { + options |= WILD_BUFLASTUSED; + } + if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice + // if 'wildmode' contains "list" may still need to list + if (s->xpc.xp_numfiles > 1 + && !s->did_wild_list + && ((wim_flags[s->wim_index] & WIM_LIST) + || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) { + (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + redrawcmd(); + s->did_wild_list = true; + } + + if (wim_flags[s->wim_index] & WIM_LONGEST) { + res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@'); + } else if (wim_flags[s->wim_index] & WIM_FULL) { + res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@'); + } else { + res = OK; // don't insert 'wildchar' now + } + } else { // typed p_wc first time + s->wim_index = 0; + int j = ccline.cmdpos; + + // if 'wildmode' first contains "longest", get longest + // common part + if (wim_flags[0] & WIM_LONGEST) { + res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@'); + } else { + res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->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(&s->xpc, NULL, NULL, 0, WILD_FREE); + s->xpc.xp_context = EXPAND_NOTHING; + 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 && s->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) { + s->wim_index = 1; + } + if ((wim_flags[s->wim_index] & WIM_LIST) + || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) { + if (!(wim_flags[0] & WIM_LONGEST)) { + int p_wmnu_save = p_wmnu; + p_wmnu = 0; + // remove match + nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@'); + p_wmnu = p_wmnu_save; + } + + (void)showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + redrawcmd(); + s->did_wild_list = true; + + if (wim_flags[s->wim_index] & WIM_LONGEST) { + nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@'); + } else if (wim_flags[s->wim_index] & WIM_FULL) { + nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@'); + } + } else { + vim_beep(BO_WILD); + } + } else if (s->xpc.xp_numfiles == -1) { + s->xpc.xp_context = EXPAND_NOTHING; + } + } + + if (s->wim_index < 3) { + s->wim_index++; + } + + if (s->c == ESC) { + s->gotesc = true; + } + + return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED; +} + +static void command_line_end_wildmenu(CommandLineState *s) +{ + if (cmdline_pum_active()) { + cmdline_pum_remove(); + } + if (s->xpc.xp_numfiles != -1) { + (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + } + s->did_wild_list = false; + if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { + s->xpc.xp_context = EXPAND_NOTHING; + } + s->wim_index = 0; + wildmenu_cleanup(&ccline); +} + static int command_line_execute(VimState *state, int key) { if (key == K_IGNORE || key == K_NOP) { @@ -958,6 +1145,19 @@ static int command_line_execute(VimState *state, int key) map_execute_lua(); } + // nvim_select_popupmenu_item() can be called from the handling of + // K_EVENT, K_COMMAND, or K_LUA. + if (pum_want.active) { + if (cmdline_pum_active()) { + nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@'); + if (pum_want.finish) { + nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@'); + command_line_end_wildmenu(s); + } + } + pum_want.active = false; + } + if (!cmdline_was_last_drawn) { redrawcmdline(); } @@ -1026,100 +1226,49 @@ static int command_line_execute(VimState *state, int key) } if (cmdline_pum_active() || s->did_wild_list) { + // Ctrl-Y: Accept the current selection and close the popup menu. + // Ctrl-E: cancel the cmdline popup menu and return the original text. if (s->c == Ctrl_E || s->c == Ctrl_Y) { const int wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; - s->res = nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); + (void)nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); s->c = Ctrl_E; } } + // The wildmenu is cleared if the pressed key is not used for + // navigating the wild menu (i.e. the key is not 'wildchar' or + // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L). + // If the popup menu is displayed, then PageDown and PageUp keys are + // also used to navigate the menu. + bool end_wildmenu = (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z + && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A + && s->c != Ctrl_L); + end_wildmenu = end_wildmenu && (!cmdline_pum_active() + || (s->c != K_PAGEDOWN && s->c != K_PAGEUP + && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP)); + // free expanded names when finished walking through matches - if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z - && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A - && s->c != Ctrl_L) { - if (cmdline_pum_active()) { - cmdline_pum_remove(); - } - if (s->xpc.xp_numfiles != -1) { - (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); - } - s->did_wild_list = false; - if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { - s->xpc.xp_context = EXPAND_NOTHING; - } - s->wim_index = 0; - wildmenu_cleanup(&ccline); + if (end_wildmenu) { + command_line_end_wildmenu(s); } if (p_wmnu) { s->c = wildmenu_process_key(&ccline, s->c, &s->xpc); } - // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ e prompts for an expression. + // CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode, + // CTRL-\ e prompts for an expression. if (s->c == Ctrl_BSL) { - no_mapping++; - allow_keys++; - s->c = plain_vgetc(); - no_mapping--; - allow_keys--; - // CTRL-\ e doesn't work when obtaining an expression, unless it - // is in a mapping. - if (s->c != Ctrl_N - && s->c != Ctrl_G - && (s->c != 'e' - || (ccline.cmdfirstc == '=' && KeyTyped) - || cmdline_star > 0)) { - vungetc(s->c); - s->c = Ctrl_BSL; - } else if (s->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; - } - - s->c = get_expr_register(); - if (s->c == '=') { - textlock++; - p = get_expr_line(); - textlock--; - - if (p != NULL) { - len = (int)STRLEN(p); - realloc_cmdbuff(len + 1); - ccline.cmdlen = len; - STRCPY(ccline.cmdbuff, p); - xfree(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 command_line_changed(s); - } - } - beep_flush(); - got_int = false; // don't abandon the command line - did_emsg = false; - emsg_on_display = false; - redrawcmd(); + switch (command_line_handle_ctrl_bsl(s)) { + case CMDLINE_CHANGED: + return command_line_changed(s); + case CMDLINE_NOT_CHANGED: return command_line_not_changed(s); - } else { - s->gotesc = true; // will free ccline.cmdbuff after putting it - // in history - return 0; // back to Normal mode + case GOTO_NORMAL_MODE: + return 0; // back to cmd mode + default: + s->c = Ctrl_BSL; // backslash key not processed by + // command_line_handle_ctrl_bsl() } } @@ -1167,107 +1316,8 @@ static int command_line_execute(VimState *state, int key) } // 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 ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm - || s->c == Ctrl_Z) { - int options = WILD_NO_BEEP; - if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) { - options |= WILD_BUFLASTUSED; - } - if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice - // if 'wildmode' contains "list" may still need to list - if (s->xpc.xp_numfiles > 1 - && !s->did_wild_list - && ((wim_flags[s->wim_index] & WIM_LIST) - || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) { - (void)showmatches(&s->xpc, p_wmnu - && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); - redrawcmd(); - s->did_wild_list = true; - } - - if (wim_flags[s->wim_index] & WIM_LONGEST) { - s->res = nextwild(&s->xpc, WILD_LONGEST, options, - s->firstc != '@'); - } else if (wim_flags[s->wim_index] & WIM_FULL) { - s->res = nextwild(&s->xpc, WILD_NEXT, options, - s->firstc != '@'); - } else { - s->res = OK; // don't insert 'wildchar' now - } - } else { // typed p_wc first time - s->wim_index = 0; - int j = ccline.cmdpos; - - // if 'wildmode' first contains "longest", get longest - // common part - if (wim_flags[0] & WIM_LONGEST) { - s->res = nextwild(&s->xpc, WILD_LONGEST, options, - s->firstc != '@'); - } else { - s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, - s->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(&s->xpc, NULL, NULL, 0, WILD_FREE); - s->xpc.xp_context = EXPAND_NOTHING; - return command_line_changed(s); - }< |