diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/ex_getln.c | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
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); - } - - // when more than one match, and 'wildmode' first contains - // "list", or no change and 'wildmode' contains "longest,list", - // list all matches - if (s->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; - } - - if (s->res == OK) { + if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm || s->c == Ctrl_Z) { + if (command_line_wildchar_complete(s) == CMDLINE_CHANGED) { return command_line_changed(s); } } @@ -1277,8 +1327,11 @@ static int command_line_execute(VimState *state, int key) // <S-Tab> goes to last match, in a clumsy way if (s->c == K_S_TAB && KeyTyped) { if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) { - // Trigger the popup menu when wildoptions=pum - showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + if (s->xpc.xp_numfiles > 1 + && ((!s->did_wild_list && (wim_flags[s->wim_index] & WIM_LIST)) || p_wmnu)) { + // Trigger the popup menu when wildoptions=pum + showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0)); + } nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@'); nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@'); return command_line_changed(s); @@ -1321,9 +1374,9 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ ui_flush(); pos_T t; - char_u *pat; + char *pat; int search_flags = SEARCH_NOOF; - char_u save; + char save; if (search_delim == ccline.cmdbuff[skiplen]) { pat = last_search_pattern(); @@ -1332,7 +1385,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ return FAIL; } skiplen = 0; - patlen = (int)STRLEN(pat); + patlen = (int)strlen(pat); } else { pat = ccline.cmdbuff + skiplen; } @@ -1395,7 +1448,8 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ validate_cursor(); highlight_match = true; save_viewstate(curwin, &s->old_viewstate); - update_screen(NOT_VALID); + redraw_later(curwin, UPD_NOT_VALID); + update_screen(); highlight_match = false; redrawcmdline(); curwin->w_cursor = s->match_end; @@ -1406,9 +1460,202 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ return FAIL; } +/// Handle backspace, delete and CTRL-W keys in the command-line mode. +static int command_line_erase_chars(CommandLineState *s) +{ + if (s->c == K_KDEL) { + s->c = K_DEL; + } + + // Delete current character is the same as backspace on next + // character, except at end of line + if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) { + ccline.cmdpos++; + } + + if (s->c == K_DEL) { + ccline.cmdpos += mb_off_next(ccline.cmdbuff, + ccline.cmdbuff + ccline.cmdpos); + } + + if (ccline.cmdpos > 0) { + char *p; + + int j = ccline.cmdpos; + p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j); + + if (s->c == Ctrl_W) { + while (p > ccline.cmdbuff && ascii_isspace(*p)) { + p = mb_prevptr(ccline.cmdbuff, p); + } + + int i = mb_get_class(p); + while (p > ccline.cmdbuff && mb_get_class(p) == i) { + p = mb_prevptr(ccline.cmdbuff, p); + } + + if (mb_get_class(p) != i) { + p += utfc_ptr2len(p); + } + } + + ccline.cmdpos = (int)(p - ccline.cmdbuff); + ccline.cmdlen -= j - ccline.cmdpos; + int i = ccline.cmdpos; + + while (i < ccline.cmdlen) { + ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; + } + + // Truncate at the end, required for multi-byte chars. + ccline.cmdbuff[ccline.cmdlen] = NUL; + if (ccline.cmdlen == 0) { + s->is_state.search_start = s->is_state.save_cursor; + // save view settings, so that the screen won't be restored at the + // wrong position + s->is_state.old_viewstate = s->is_state.init_viewstate; + } + redrawcmd(); + } else if (ccline.cmdlen == 0 && s->c != Ctrl_W + && ccline.cmdprompt == NULL && s->indent == 0) { + // In ex and debug mode it doesn't make sense to return. + if (exmode_active || ccline.cmdfirstc == '>') { + return CMDLINE_NOT_CHANGED; + } + + XFREE_CLEAR(ccline.cmdbuff); // no commandline to return + if (!cmd_silent && !ui_has(kUICmdline)) { + if (cmdmsg_rl) { + msg_col = Columns; + } else { + msg_col = 0; + } + msg_putchar(' '); // delete ':' + } + s->is_state.search_start = s->is_state.save_cursor; + redraw_cmdline = true; + return GOTO_NORMAL_MODE; + } + return CMDLINE_CHANGED; +} + +/// Handle the CTRL-^ key in the command-line mode and toggle the use of the +/// language :lmap mappings and/or Input Method. +static void command_line_toggle_langmap(CommandLineState *s) +{ + if (map_to_exists_mode("", MODE_LANGMAP, false)) { + // ":lmap" mappings exists, toggle use of mappings. + State ^= MODE_LANGMAP; + if (s->b_im_ptr != NULL) { + if (State & MODE_LANGMAP) { + *s->b_im_ptr = B_IMODE_LMAP; + } else { + *s->b_im_ptr = B_IMODE_NONE; + } + } + } + + if (s->b_im_ptr != NULL) { + if (s->b_im_ptr == &curbuf->b_p_iminsert) { + set_iminsert_global(curbuf); + } else { + set_imsearch_global(curbuf); + } + } + ui_cursor_shape(); // may show different cursor shape + // Show/unshow value of 'keymap' in status lines later. + status_redraw_curbuf(); +} + +/// Handle the CTRL-R key in the command-line mode and insert the contents of a +/// numbered or named register. +static int command_line_insert_reg(CommandLineState *s) +{ + const int save_new_cmdpos = new_cmdpos; + + putcmdline('"', true); + no_mapping++; + allow_keys++; + int i = s->c = plain_vgetc(); // CTRL-R <char> + if (i == Ctrl_O) { + i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R + } + + if (i == Ctrl_R) { + s->c = plain_vgetc(); // CTRL-R CTRL-R <char> + } + no_mapping--; + allow_keys--; + // Insert the result of an expression. + new_cmdpos = -1; + if (s->c == '=') { + if (ccline.cmdfirstc == '=' // can't do this recursively + || cmdline_star > 0) { // or when typing a password + beep_flush(); + s->c = ESC; + } else { + s->c = get_expr_register(); + } + } + + if (s->c != ESC) { // use ESC to cancel inserting register + cmdline_paste(s->c, i == Ctrl_R, false); + + // When there was a serious error abort getting the + // command line. + if (aborting()) { + s->gotesc = true; // will free ccline.cmdbuff after + // putting it in history + return GOTO_NORMAL_MODE; + } + KeyTyped = false; // Don't do p_wc completion. + if (new_cmdpos >= 0) { + // set_cmdline_pos() was used + if (new_cmdpos > ccline.cmdlen) { + ccline.cmdpos = ccline.cmdlen; + } else { + ccline.cmdpos = new_cmdpos; + } + } + } + new_cmdpos = save_new_cmdpos; + + // remove the double quote + ccline.special_char = NUL; + redrawcmd(); + + // The text has been stuffed, the command line didn't change yet. + return CMDLINE_NOT_CHANGED; +} + +/// Handle the Left and Right mouse clicks in the command-line mode. +static void command_line_left_right_mouse(CommandLineState *s) +{ + if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) { + s->ignore_drag_release = true; + } else { + s->ignore_drag_release = false; + } + + ccline.cmdspos = cmd_startcol(); + for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; + ccline.cmdpos++) { + int cells = cmdline_charsize(ccline.cmdpos); + if (mouse_row <= cmdline_row + ccline.cmdspos / Columns + && mouse_col < ccline.cmdspos % Columns + cells) { + break; + } + + // Count ">" for double-wide char that doesn't fit. + correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos); + ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; + ccline.cmdspos += cells; + } +} + static void command_line_next_histidx(CommandLineState *s, bool next_match) { - int j = (int)STRLEN(s->lookfor); + int j = (int)strlen(s->lookfor); for (;;) { // one step backwards if (!next_match) { @@ -1451,96 +1698,118 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match) if ((s->c != K_UP && s->c != K_DOWN) || s->hiscnt == s->save_hiscnt - || STRNCMP(get_histentry(s->histype)[s->hiscnt].hisstr, + || strncmp(get_histentry(s->histype)[s->hiscnt].hisstr, s->lookfor, (size_t)j) == 0) { break; } } } -static int command_line_handle_key(CommandLineState *s) +/// Handle the Up, Down, Page Up, Page down, CTRL-N and CTRL-P key in the +/// command-line mode. +static int command_line_browse_history(CommandLineState *s) { - // Big switch for a typed command line character. - switch (s->c) { - case K_BS: - case Ctrl_H: - case K_DEL: - case K_KDEL: - case Ctrl_W: - if (s->c == K_KDEL) { - s->c = K_DEL; - } + if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) { + // no history + return CMDLINE_NOT_CHANGED; + } - // delete current character is the same as backspace on next - // character, except at end of line - if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) { - ++ccline.cmdpos; - } + s->save_hiscnt = s->hiscnt; - if (s->c == K_DEL) { - ccline.cmdpos += mb_off_next(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos); - } + // save current command string so it can be restored later + if (s->lookfor == NULL) { + s->lookfor = xstrdup(ccline.cmdbuff); + s->lookfor[ccline.cmdpos] = NUL; + } - if (ccline.cmdpos > 0) { - char_u *p; + bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N + || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN); + command_line_next_histidx(s, next_match); - int j = ccline.cmdpos; - p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j); + if (s->hiscnt != s->save_hiscnt) { + // jumped to other entry + char *p; + int len = 0; + int old_firstc; - if (s->c == Ctrl_W) { - while (p > ccline.cmdbuff && ascii_isspace(*p)) { - p = mb_prevptr(ccline.cmdbuff, p); - } + XFREE_CLEAR(ccline.cmdbuff); + s->xpc.xp_context = EXPAND_NOTHING; + if (s->hiscnt == get_hislen()) { + p = s->lookfor; // back to the old one + } else { + p = get_histentry(s->histype)[s->hiscnt].hisstr; + } + + if (s->histype == HIST_SEARCH + && p != s->lookfor + && (old_firstc = (uint8_t)p[strlen(p) + 1]) != s->firstc) { + // Correct for the separator character used when + // adding the history entry vs the one used now. + // First loop: count length. + // Second loop: copy the characters. + for (int i = 0; i <= 1; i++) { + len = 0; + for (int j = 0; p[j] != NUL; j++) { + // Replace old sep with new sep, unless it is + // escaped. + if (p[j] == old_firstc + && (j == 0 || p[j - 1] != '\\')) { + if (i > 0) { + ccline.cmdbuff[len] = (char)s->firstc; + } + } else { + // Escape new sep, unless it is already + // escaped. + if (p[j] == s->firstc + && (j == 0 || p[j - 1] != '\\')) { + if (i > 0) { + ccline.cmdbuff[len] = '\\'; + } + len++; + } - int i = mb_get_class(p); - while (p > ccline.cmdbuff && mb_get_class(p) == i) { - p = mb_prevptr(ccline.cmdbuff, p); + if (i > 0) { + ccline.cmdbuff[len] = p[j]; + } + } + len++; } - if (mb_get_class(p) != i) { - p += utfc_ptr2len((char *)p); + if (i == 0) { + alloc_cmdbuff(len); } } + ccline.cmdbuff[len] = NUL; + } else { + alloc_cmdbuff((int)strlen(p)); + STRCPY(ccline.cmdbuff, p); + } - ccline.cmdpos = (int)(p - ccline.cmdbuff); - ccline.cmdlen -= j - ccline.cmdpos; - int i = ccline.cmdpos; - - while (i < ccline.cmdlen) { - ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; - } - - // Truncate at the end, required for multi-byte chars. - ccline.cmdbuff[ccline.cmdlen] = NUL; - if (ccline.cmdlen == 0) { - s->is_state.search_start = s->is_state.save_cursor; - // save view settings, so that the screen won't be restored at the - // wrong position - s->is_state.old_viewstate = s->is_state.init_viewstate; - } - redrawcmd(); - } else if (ccline.cmdlen == 0 && s->c != Ctrl_W - && ccline.cmdprompt == NULL && s->indent == 0) { - // In ex and debug mode it doesn't make sense to return. - if (exmode_active || ccline.cmdfirstc == '>') { - return command_line_not_changed(s); - } + ccline.cmdpos = ccline.cmdlen = (int)strlen(ccline.cmdbuff); + redrawcmd(); + return CMDLINE_CHANGED; + } + beep_flush(); + return CMDLINE_NOT_CHANGED; +} - XFREE_CLEAR(ccline.cmdbuff); // no commandline to return - if (!cmd_silent && !ui_has(kUICmdline)) { - if (cmdmsg_rl) { - msg_col = Columns; - } else { - msg_col = 0; - } - msg_putchar(' '); // delete ':' - } - s->is_state.search_start = s->is_state.save_cursor; - redraw_cmdline = true; - return 0; // back to cmd mode +static int command_line_handle_key(CommandLineState *s) +{ + // Big switch for a typed command line character. + switch (s->c) { + case K_BS: + case Ctrl_H: + case K_DEL: + case K_KDEL: + case Ctrl_W: + switch (command_line_erase_chars(s)) { + case CMDLINE_NOT_CHANGED: + return command_line_not_changed(s); + case GOTO_NORMAL_MODE: + return 0; // back to cmd mode + default: + return command_line_changed(s); } - return command_line_changed(s); case K_INS: case K_KINS: @@ -1550,28 +1819,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode("", MODE_LANGMAP, false)) { - // ":lmap" mappings exists, toggle use of mappings. - State ^= MODE_LANGMAP; - if (s->b_im_ptr != NULL) { - if (State & MODE_LANGMAP) { - *s->b_im_ptr = B_IMODE_LMAP; - } else { - *s->b_im_ptr = B_IMODE_NONE; - } - } - } - - if (s->b_im_ptr != NULL) { - if (s->b_im_ptr == &curbuf->b_p_iminsert) { - set_iminsert_global(); - } else { - set_imsearch_global(); - } - } - ui_cursor_shape(); // may show different cursor shape - // Show/unshow value of 'keymap' in status lines later. - status_redraw_curbuf(); + command_line_toggle_langmap(s); return command_line_not_changed(s); case Ctrl_U: { @@ -1607,58 +1855,15 @@ static int command_line_handle_key(CommandLineState *s) // putting it in history return 0; // back to cmd mode - case Ctrl_R: { // insert register - putcmdline('"', true); - no_mapping++; - allow_keys++; - int i = s->c = plain_vgetc(); // CTRL-R <char> - if (i == Ctrl_O) { - i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R - } - - if (i == Ctrl_R) { - s->c = plain_vgetc(); // CTRL-R CTRL-R <char> - } - no_mapping--; - allow_keys--; - // Insert the result of an expression. - // Need to save the current command line, to be able to enter - // a new one... - new_cmdpos = -1; - if (s->c == '=') { - if (ccline.cmdfirstc == '=' // can't do this recursively - || cmdline_star > 0) { // or when typing a password - beep_flush(); - s->c = ESC; - } else { - s->c = get_expr_register(); - } - } - - if (s->c != ESC) { // use ESC to cancel inserting register - cmdline_paste(s->c, i == Ctrl_R, false); - - // When there was a serious error abort getting the - // command line. - if (aborting()) { - s->gotesc = true; // will free ccline.cmdbuff after - // putting it in history - return 0; // back to cmd mode - } - KeyTyped = false; // Don't do p_wc completion. - if (new_cmdpos >= 0) { - // set_cmdline_pos() was used - if (new_cmdpos > ccline.cmdlen) { - ccline.cmdpos = ccline.cmdlen; - } else { - ccline.cmdpos = new_cmdpos; - } - } + case Ctrl_R: // insert register + switch (command_line_insert_reg(s)) { + case CMDLINE_NOT_CHANGED: + return command_line_not_changed(s); + case GOTO_NORMAL_MODE: + return 0; // back to cmd mode + default: + return command_line_changed(s); } - ccline.special_char = NUL; - redrawcmd(); - return command_line_changed(s); - } case Ctrl_D: if (showmatches(&s->xpc, false) == EXPAND_NOTHING) { @@ -1683,7 +1888,7 @@ static int command_line_handle_key(CommandLineState *s) } ccline.cmdspos += cells; - ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos); + ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos); } while ((s->c == K_S_RIGHT || s->c == K_C_RIGHT || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) && ccline.cmdbuff[ccline.cmdpos] != ' '); @@ -1739,26 +1944,7 @@ static int command_line_handle_key(CommandLineState *s) FALLTHROUGH; case K_LEFTMOUSE: case K_RIGHTMOUSE: - if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) { - s->ignore_drag_release = true; - } else { - s->ignore_drag_release = false; - } - - ccline.cmdspos = cmd_startcol(); - for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; - ccline.cmdpos++) { - int cells = cmdline_charsize(ccline.cmdpos); - if (mouse_row <= cmdline_row + ccline.cmdspos / Columns - && mouse_col < ccline.cmdspos % Columns + cells) { - break; - } - - // Count ">" for double-wide char that doesn't fit. - correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos); - ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1; - ccline.cmdspos += cells; - } + command_line_left_right_mouse(s); return command_line_not_changed(s); // Mouse scroll wheel: ignored here @@ -1798,13 +1984,17 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_A: // all matches - if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) { - break; - } if (cmdline_pum_active()) { + // As Ctrl-A completes all the matches, close the popup + // menu (if present) cmdline_pum_cleanup(&ccline); - s->xpc.xp_context = EXPAND_NOTHING; } + + if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) { + break; + } + s->xpc.xp_context = EXPAND_NOTHING; + s->did_wild_list = false; return command_line_changed(s); case Ctrl_L: @@ -1821,8 +2011,8 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_N: // next match case Ctrl_P: // previous match if (s->xpc.xp_numfiles > 0) { - if (nextwild(&s->xpc, (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT, - 0, s->firstc != '@') == FAIL) { + const int wild_type = (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT; + if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) { break; } return command_line_not_changed(s); @@ -1837,88 +2027,27 @@ static int command_line_handle_key(CommandLineState *s) case K_KPAGEUP: case K_PAGEDOWN: case K_KPAGEDOWN: - if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) { - // no history - return command_line_not_changed(s); - } - - s->save_hiscnt = s->hiscnt; - - // save current command string so it can be restored later - if (s->lookfor == NULL) { - s->lookfor = vim_strsave(ccline.cmdbuff); - s->lookfor[ccline.cmdpos] = NUL; - } - - bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N - || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN); - command_line_next_histidx(s, next_match); - - if (s->hiscnt != s->save_hiscnt) { - // jumped to other entry - char_u *p; - int len = 0; - int old_firstc; - - XFREE_CLEAR(ccline.cmdbuff); - s->xpc.xp_context = EXPAND_NOTHING; - if (s->hiscnt == get_hislen()) { - p = s->lookfor; // back to the old one - } else { - p = get_histentry(s->histype)[s->hiscnt].hisstr; + if (cmdline_pum_active() + && (s->c == K_PAGEUP || s->c == K_PAGEDOWN + || s->c == K_KPAGEUP || s->c == K_KPAGEDOWN)) { + // If the popup menu is displayed, then PageUp and PageDown + // are used to scroll the menu. + const int wild_type = + (s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN) ? WILD_PAGEDOWN : WILD_PAGEUP; + if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) { + break; } - - if (s->histype == HIST_SEARCH - && p != s->lookfor - && (old_firstc = p[STRLEN(p) + 1]) != s->firstc) { - // Correct for the separator character used when - // adding the history entry vs the one used now. - // First loop: count length. - // Second loop: copy the characters. - for (int i = 0; i <= 1; i++) { - len = 0; - for (int j = 0; p[j] != NUL; j++) { - // Replace old sep with new sep, unless it is - // escaped. - if (p[j] == old_firstc - && (j == 0 || p[j - 1] != '\\')) { - if (i > 0) { - ccline.cmdbuff[len] = (char_u)s->firstc; - } - } else { - // Escape new sep, unless it is already - // escaped. - if (p[j] == s->firstc - && (j == 0 || p[j - 1] != '\\')) { - if (i > 0) { - ccline.cmdbuff[len] = '\\'; - } - len++; - } - - if (i > 0) { - ccline.cmdbuff[len] = p[j]; - } - } - len++; - } - - if (i == 0) { - alloc_cmdbuff(len); - } - } - ccline.cmdbuff[len] = NUL; - } else { - alloc_cmdbuff((int)STRLEN(p)); - STRCPY(ccline.cmdbuff, p); + return command_line_not_changed(s); + } else { + switch (command_line_browse_history(s)) { + case CMDLINE_CHANGED: + return command_line_changed(s); + case GOTO_NORMAL_MODE: + return 0; + default: + return command_line_not_changed(s); } - - ccline.cmdpos = ccline.cmdlen = (int)STRLEN(ccline.cmdbuff); - redrawcmd(); - return command_line_changed(s); } - beep_flush(); - return command_line_not_changed(s); case Ctrl_G: // next match case Ctrl_T: // previous match @@ -1994,9 +2123,9 @@ static int command_line_handle_key(CommandLineState *s) // put the character in the command line if (IS_SPECIAL(s->c) || mod_mask != 0) { - put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true); + put_on_cmdline((char *)get_special_key_name(s->c, mod_mask), -1, true); } else { - int j = utf_char2bytes(s->c, (char *)IObuff); + int j = utf_char2bytes(s->c, IObuff); IObuff[j] = NUL; // exclude composing chars put_on_cmdline(IObuff, j, true); } @@ -2018,16 +2147,35 @@ static int command_line_not_changed(CommandLineState *s) /// Guess that the pattern matches everything. Only finds specific cases, such /// as a trailing \|, which can happen while typing a pattern. -static int empty_pattern(char_u *p) +static bool empty_pattern(char *p, int delim) { - size_t n = STRLEN(p); + size_t n = strlen(p); + magic_T magic_val = MAGIC_ON; - // remove trailing \v and the like - while (n >= 2 && p[n - 2] == '\\' - && vim_strchr("mMvVcCZ", p[n - 1]) != NULL) { - n -= 2; + if (n > 0) { + (void)skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val); + } else { + return true; } - return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); + + return empty_pattern_magic(p, n, magic_val); +} + +static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val) +{ + // remove trailing \v and the like + while (len >= 2 && p[len - 2] == '\\' + && vim_strchr("mMvVcCZ", (uint8_t)p[len - 1]) != NULL) { + len -= 2; + } + + // true, if the pattern is empty, or the pattern ends with \| and magic is + // set (or it ends with '|' and very magic is set) + return len == 0 || (len > 1 + && ((p[len - 2] == '\\' + && p[len - 1] == '|' && magic_val == MAGIC_ON) + || (p[len - 2] != '\\' + && p[len - 1] == '|' && magic_val == MAGIC_ALL))); } handle_T cmdpreview_get_bufnr(void) @@ -2147,6 +2295,7 @@ static void cmdpreview_prepare(CpInfo *cpinfo) CpBufInfo cp_bufinfo; cp_bufinfo.buf = buf; + cp_bufinfo.save_b_u_synced = buf->b_u_synced; cp_bufinfo.save_b_u_time_cur = buf->b_u_time_cur; cp_bufinfo.save_b_u_seq_cur = buf->b_u_seq_cur; cp_bufinfo.save_b_u_newhead = buf->b_u_newhead; @@ -2184,6 +2333,8 @@ static void cmdpreview_prepare(CpInfo *cpinfo) cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers cmdmod.cmod_tab = 0; // Disable :tab modifier cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer + + u_sync(true); } // Restore the state of buffers and windows before command preview. @@ -2206,7 +2357,7 @@ static void cmdpreview_restore_state(CpInfo *cpinfo) aco_save_T aco; aucmd_prepbuf(&aco, buf); // Undo invisibly. This also moves the cursor! - if (!u_undo_and_forget(count)) { + if (!u_undo_and_forget(count, false)) { abort(); } aucmd_restbuf(&aco); @@ -2216,6 +2367,11 @@ static void cmdpreview_restore_state(CpInfo *cpinfo) buf->b_u_newhead = cp_bufinfo.save_b_u_newhead; buf->b_u_time_cur = cp_bufinfo.save_b_u_time_cur; } + + if (buf->b_u_curhead == NULL) { + buf->b_u_synced = cp_bufinfo.save_b_u_synced; + } + if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) { buf_set_changedtick(buf, cp_bufinfo.save_changedtick); } @@ -2272,7 +2428,7 @@ static bool cmdpreview_may_show(CommandLineState *s) CmdParseInfo cmdinfo; // Copy the command line so we can modify it. int cmdpreview_type = 0; - char *cmdline = xstrdup((char *)ccline.cmdbuff); + char *cmdline = xstrdup(ccline.cmdbuff); char *errormsg = NULL; emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { @@ -2343,7 +2499,7 @@ static bool cmdpreview_may_show(CommandLineState *s) if (cmdpreview_type != 0) { int save_rd = RedrawingDisabled; RedrawingDisabled = 0; - update_screen(SOME_VALID); + update_screen(); RedrawingDisabled = save_rd; } @@ -2364,9 +2520,9 @@ end: return cmdpreview_type != 0; } -static int command_line_changed(CommandLineState *s) +/// Trigger CmdlineChanged autocommands. +static void do_autocmd_cmdlinechanged(int firstc) { - // Trigger CmdlineChanged autocommands. if (has_event(EVENT_CMDLINECHANGED)) { TryState tstate; Error err = ERROR_INIT; @@ -2374,7 +2530,7 @@ static int command_line_changed(CommandLineState *s) dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; - firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-'); + firstcbuf[0] = (char)firstc; firstcbuf[1] = 0; // set v:event to a dictionary with information about the commandline @@ -2389,12 +2545,20 @@ static int command_line_changed(CommandLineState *s) bool 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(); } } +} + +static int command_line_changed(CommandLineState *s) +{ + // Trigger CmdlineChanged autocommands. + do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-'); + const bool prev_cmdpreview = cmdpreview; if (s->firstc == ':' && current_sctx.sc_sid == 0 // only if interactive && *p_icm != NUL // 'inccommand' is set @@ -2403,10 +2567,13 @@ static int command_line_changed(CommandLineState *s) && !vpeekc_any() && cmdpreview_may_show(s)) { // 'inccommand' preview has been shown. - } else if (cmdpreview) { - cmdpreview = false; - update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { + cmdpreview = false; + if (prev_cmdpreview) { + // TODO(bfredl): add an immediate redraw flag for cmdline mode which will trigger + // at next wait-for-input + update_screen(); // Clear 'inccommand' preview. + } if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) { may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); } @@ -2458,9 +2625,9 @@ static void abandon_cmdline(void) /// /// @param count only used for incremental search /// @param indent indent for inside conditionals -char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED) +char *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED) { - return command_line_enter(firstc, count, indent, true); + return (char *)command_line_enter(firstc, count, indent, true); } /// Get a command line with a prompt @@ -2493,10 +2660,10 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at CLEAR_FIELD(ccline); } ccline.prompt_id = last_prompt_id++; - ccline.cmdprompt = (char_u *)prompt; + ccline.cmdprompt = (char *)prompt; ccline.cmdattr = attr; ccline.xp_context = xp_context; - ccline.xp_arg = (char_u *)xp_arg; + ccline.xp_arg = (char *)xp_arg; ccline.input_fn = (firstc == '@'); ccline.highlight_callback = highlight_callback; @@ -2520,10 +2687,56 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at return ret; } -// Return current cmdline prompt -char_u *get_cmdprompt(void) +/// Read the 'wildmode' option, fill wim_flags[]. +int check_opt_wim(void) { - return ccline.cmdprompt; + char_u new_wim_flags[4]; + int i; + int idx = 0; + + for (i = 0; i < 4; i++) { + new_wim_flags[i] = 0; + } + + for (char *p = p_wim; *p; p++) { + for (i = 0; ASCII_ISALPHA(p[i]); i++) {} + if (p[i] != NUL && p[i] != ',' && p[i] != ':') { + return FAIL; + } + if (i == 7 && strncmp(p, "longest", 7) == 0) { + new_wim_flags[idx] |= WIM_LONGEST; + } else if (i == 4 && strncmp(p, "full", 4) == 0) { + new_wim_flags[idx] |= WIM_FULL; + } else if (i == 4 && strncmp(p, "list", 4) == 0) { + new_wim_flags[idx] |= WIM_LIST; + } else if (i == 8 && strncmp(p, "lastused", 8) == 0) { + new_wim_flags[idx] |= WIM_BUFLASTUSED; + } else { + return FAIL; + } + p += i; + if (*p == NUL) { + break; + } + if (*p == ',') { + if (idx == 3) { + return FAIL; + } + idx++; + } + } + + // fill remaining entries with last flag + while (idx < 3) { + new_wim_flags[idx + 1] = new_wim_flags[idx]; + idx++; + } + + // only when there are no errors, wim_flags[] is changed + for (i = 0; i < 4; i++) { + wim_flags[i] = new_wim_flags[i]; + } + return OK; } /// Return true when the text must not be changed and we can't switch to @@ -2536,10 +2749,8 @@ bool text_locked(void) return textlock != 0; } -/* - * Give an error message for a command that isn't allowed while the cmdline - * window is open or editing the cmdline in another way. - */ +// Give an error message for a command that isn't allowed while the cmdline +// window is open or editing the cmdline in another way. void text_locked_msg(void) { emsg(_(get_text_locked_msg())); @@ -2592,7 +2803,7 @@ static int cmdline_charsize(int idx) if (cmdline_star > 0) { // showing '*', always 1 position return 1; } - return ptr2cells((char *)ccline.cmdbuff + idx); + return ptr2cells(ccline.cmdbuff + idx); } /// Compute the offset of the cursor on the command line for the prompt and @@ -2618,7 +2829,7 @@ int cmd_screencol(int bytepos) } for (int i = 0; i < ccline.cmdlen && i < bytepos; - i += utfc_ptr2len((char *)ccline.cmdbuff + i)) { + i += utfc_ptr2len(ccline.cmdbuff + i)) { int c = cmdline_charsize(i); // Count ">" for double-wide multi-byte char that doesn't fit. correct_screencol(i, c, &col); @@ -2637,8 +2848,8 @@ int cmd_screencol(int bytepos) /// character that doesn't fit, so that a ">" must be displayed. static void correct_screencol(int idx, int cells, int *col) { - if (utfc_ptr2len((char *)ccline.cmdbuff + idx) > 1 - && utf_ptr2cells((char *)ccline.cmdbuff + idx) > 1 + if (utfc_ptr2len(ccline.cmdbuff + idx) > 1 + && utf_ptr2cells(ccline.cmdbuff + idx) > 1 && (*col) % Columns + cells > Columns) { (*col)++; } @@ -2655,7 +2866,7 @@ char *getexline(int c, void *cookie, int indent, bool do_concat) (void)vgetc(); } - return (char *)getcmdline(c, 1L, indent, do_concat); + return getcmdline(c, 1L, indent, do_concat); } bool cmdline_overstrike(void) @@ -2671,15 +2882,11 @@ bool cmdline_at_end(void) return (ccline.cmdpos >= ccline.cmdlen); } -/* - * Allocate a new command line buffer. - * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. - */ +// Allocate a new command line buffer. +// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. static void alloc_cmdbuff(int len) { - /* - * give some extra space to avoid having to allocate all the time - */ + // give some extra space to avoid having to allocate all the time if (len < 80) { len = 100; } else { @@ -2697,7 +2904,7 @@ void realloc_cmdbuff(int len) return; // no need to resize } - char_u *p = ccline.cmdbuff; + char *p = ccline.cmdbuff; alloc_cmdbuff(len); // will get some more // There isn't always a NUL after the command, but it may need to be // there, thus copy up to the NUL and add a NUL. @@ -2709,12 +2916,12 @@ void realloc_cmdbuff(int len) && ccline.xpc->xp_pattern != NULL && ccline.xpc->xp_context != EXPAND_NOTHING && ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) { - int i = (int)((char_u *)ccline.xpc->xp_pattern - p); + int i = (int)(ccline.xpc->xp_pattern - p); // If xp_pattern points inside the old cmdbuff it needs to be adjusted // to point into the newly allocated memory. if (i >= 0 && i <= ccline.cmdlen) { - ccline.xpc->xp_pattern = (char *)ccline.cmdbuff + i; + ccline.xpc->xp_pattern = ccline.cmdbuff + i; } } } @@ -2744,7 +2951,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, ParserLine parser_lines[] = { { .data = (const char *)colored_ccline->cmdbuff, - .size = STRLEN(colored_ccline->cmdbuff), + .size = strlen(colored_ccline->cmdbuff), .allocated = false, }, { NULL, 0, false }, @@ -2822,7 +3029,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) // Check whether result of the previous call is still valid. if (ccline_colors->prompt_id == colored_ccline->prompt_id && ccline_colors->cmdbuff != NULL - && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { + && strcmp(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { return ret; } @@ -2837,7 +3044,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) bool arg_allocated = false; typval_T arg = { .v_type = VAR_STRING, - .vval.v_string = (char *)colored_ccline->cmdbuff, + .vval.v_string = colored_ccline->cmdbuff, }; typval_T tv = { .v_type = VAR_UNKNOWN }; @@ -3021,10 +3228,8 @@ color_cmdline_error: #undef PRINT_ERRMSG } -/* - * Draw part of the cmdline at the current cursor position. But draw stars - * when cmdline_star is TRUE. - */ +// Draw part of the cmdline at the current cursor position. But draw stars +// when cmdline_star is true. static void draw_cmdline(int start, int len) { if (!color_cmdline(&ccline)) { @@ -3040,13 +3245,13 @@ static void draw_cmdline(int start, int len) if (cmdline_star > 0) { for (int i = 0; i < len; i++) { msg_putchar('*'); - i += utfc_ptr2len((char *)ccline.cmdbuff + start + i) - 1; + i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1; } } else if (p_arshape && !p_tbidi && len > 0) { bool do_arabicshape = false; int mb_l; for (int i = start; i < start + len; i += mb_l) { - char_u *p = ccline.cmdbuff + i; + char *p = ccline.cmdbuff + i; int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); @@ -3073,7 +3278,7 @@ static void draw_cmdline(int start, int len) } int newlen = 0; - if (utf_iscomposing(utf_ptr2char((char *)ccline.cmdbuff + start))) { + if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) { // Prepend a space to draw the leading composing char on. arshape_buf[0] = ' '; newlen = 1; @@ -3082,7 +3287,7 @@ static void draw_cmdline(int start, int len) int prev_c = 0; int prev_c1 = 0; for (int i = start; i < start + len; i += mb_l) { - char_u *p = ccline.cmdbuff + i; + char *p = ccline.cmdbuff + i; int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); @@ -3099,7 +3304,7 @@ static void draw_cmdline(int start, int len) if (i + mb_l >= start + len) { nc = NUL; } else { - nc = utf_ptr2char((char *)p + mb_l); + nc = utf_ptr2char(p + mb_l); } } else { // Displaying from left to right. @@ -3131,7 +3336,7 @@ static void draw_cmdline(int start, int len) } } - msg_outtrans_len((char_u *)arshape_buf, newlen); + msg_outtrans_len(arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: if (kv_size(ccline.last_colors.colors)) { @@ -3154,12 +3359,11 @@ draw_cmdline_no_arabicshape: static void ui_ext_cmdline_show(CmdlineInfo *line) { Arena arena = ARENA_EMPTY; - arena_start(&arena, &ui_ext_fixblk); Array content; if (cmdline_star) { content = arena_array(&arena, 1); size_t len = 0; - for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { + for (char *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } char *buf = arena_alloc(&arena, len, false); @@ -3190,7 +3394,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) char charbuf[2] = { (char)line->cmdfirstc, 0 }; ui_call_cmdline_show(content, line->cmdpos, cstr_as_string(charbuf), - cstr_as_string((char *)(line->cmdprompt)), + cstr_as_string((line->cmdprompt)), line->cmdindent, line->level); if (line->special_char) { @@ -3199,7 +3403,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) line->special_shift, line->level); } - arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); + arena_mem_free(arena_finish(&arena)); } void ui_ext_cmdline_block_append(size_t indent, const char *line) @@ -3276,11 +3480,9 @@ void cmdline_ui_flush(void) } } -/* - * Put a character on the command line. Shifts the following text to the - * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc. - * "c" must be printable (fit in one display cell)! - */ +// Put a character on the command line. Shifts the following text to the +// right when "shift" is true. Used for CTRL-V, CTRL-K, etc. +// "c" must be printable (fit in one display cell)! void putcmdline(char c, int shift) { if (cmd_silent) { @@ -3304,7 +3506,7 @@ void putcmdline(char c, int shift) ui_cursor_shape(); } -/// Undo a putcmdline(c, FALSE). +/// Undo a putcmdline(c, false). void unputcmdline(void) { if (cmd_silent) { @@ -3314,7 +3516,7 @@ void unputcmdline(void) if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) { msg_putchar(' '); } else { - draw_cmdline(ccline.cmdpos, utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos)); + draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos)); } msg_no_more = false; cursorcmd(); @@ -3322,22 +3524,20 @@ void unputcmdline(void) ui_cursor_shape(); } -/* - * Put the given string, of the given length, onto the command line. - * If len is -1, then STRLEN() is used to calculate the length. - * If 'redraw' is TRUE then the new part of the command line, and the remaining - * part will be redrawn, otherwise it will not. If this function is called - * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be - * called afterwards. - */ -void put_on_cmdline(char_u *str, int len, int redraw) +// Put the given string, of the given length, onto the command line. +// If len is -1, then strlen() is used to calculate the length. +// If 'redraw' is true then the new part of the command line, and the remaining +// part will be redrawn, otherwise it will not. If this function is called +// twice in a row, then 'redraw' should be false and redrawcmd() should be +// called afterwards. +void put_on_cmdline(char *str, int len, int redraw) { int i; int m; int c; if (len < 0) { - len = (int)STRLEN(str); + len = (int)strlen(str); } realloc_cmdbuff(ccline.cmdlen + len + 1); @@ -3350,13 +3550,13 @@ void put_on_cmdline(char_u *str, int len, int redraw) } else { // Count nr of characters in the new string. m = 0; - for (i = 0; i < len; i += utfc_ptr2len((char *)str + i)) { + for (i = 0; i < len; i += utfc_ptr2len(str + i)) { m++; } // Count nr of bytes in cmdline that are overwritten by these // characters. for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0; - i += utfc_ptr2len((char *)ccline.cmdbuff + i)) { + i += utfc_ptr2len(ccline.cmdbuff + i)) { m--; } if (i < ccline.cmdlen) { @@ -3374,17 +3574,17 @@ void put_on_cmdline(char_u *str, int len, int redraw) // When the inserted text starts with a composing character, // backup to the character before it. There could be two of them. i = 0; - c = utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos); + c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos); while (ccline.cmdpos > 0 && utf_iscomposing(c)) { i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1; ccline.cmdpos -= i; len += i; - c = utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos); + c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos); } if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) { // Check the previous character for Arabic combining pair. i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1; - if (arabic_combine(utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos - i), c)) { + if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) { ccline.cmdpos -= i; len += i; } else { @@ -3393,7 +3593,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) } if (i != 0) { // Also backup the cursor position. - i = ptr2cells((char *)ccline.cmdbuff + ccline.cmdpos); + i = ptr2cells(ccline.cmdbuff + ccline.cmdpos); ccline.cmdspos -= i; msg_col -= i; if (msg_col < 0) { @@ -3404,7 +3604,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) } if (redraw && !cmd_silent) { - msg_no_more = TRUE; + msg_no_more = true; i = cmdline_row; cursorcmd(); draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); @@ -3412,7 +3612,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) if (cmdline_row != i || ccline.overstrike) { msg_clr_eos(); } - msg_no_more = FALSE; + msg_no_more = false; } if (KeyTyped) { m = Columns * Rows; @@ -3432,7 +3632,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) if (ccline.cmdspos + c < m) { ccline.cmdspos += c; } - c = utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1; + c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; if (c > len - i - 1) { c = len - i - 1; } @@ -3476,7 +3676,7 @@ static void restore_cmdline(CmdlineInfo *ccp) static bool cmdline_paste(int regname, bool literally, bool remcr) { char *arg; - char_u *p; + char *p; bool allocated; // check for valid regname; also accept special characters for CTRL-R in @@ -3508,21 +3708,21 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) // When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate // part of the word. - p = (char_u *)arg; + p = arg; if (p_is && regname == Ctrl_W) { - char_u *w; + char *w; int len; // Locate start of last word in the cmd buffer. for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff;) { len = utf_head_off(ccline.cmdbuff, w - 1) + 1; - if (!vim_iswordc(utf_ptr2char((char *)w - len))) { + if (!vim_iswordc(utf_ptr2char(w - len))) { break; } w -= len; } len = (int)((ccline.cmdbuff + ccline.cmdpos) - w); - if (p_ic ? STRNICMP(w, arg, len) == 0 : STRNCMP(w, arg, len) == 0) { + if (p_ic ? STRNICMP(w, arg, len) == 0 : strncmp(w, arg, (size_t)len) == 0) { p += len; } } @@ -3537,25 +3737,23 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) return cmdline_paste_reg(regname, literally, remcr); } -/* - * Put a string on the command line. - * When "literally" is TRUE, insert literally. - * When "literally" is FALSE, insert as typed, but don't leave the command - * line. - */ -void cmdline_paste_str(char_u *s, int literally) +// Put a string on the command line. +// When "literally" is true, insert literally. +// When "literally" is false, insert as typed, but don't leave the command +// line. +void cmdline_paste_str(char *s, int literally) { int c, cv; if (literally) { - put_on_cmdline(s, -1, TRUE); + put_on_cmdline(s, -1, true); } else { while (*s != NUL) { - cv = *s; + cv = (uint8_t)(*s); if (cv == Ctrl_V && s[1]) { s++; } - c = mb_cptr2char_adv((const char_u **)&s); + c = mb_cptr2char_adv((const char **)&s); if (cv == Ctrl_V || c == ESC || c == Ctrl_C || c == CAR || c == NL || c == Ctrl_L || (c == Ctrl_BSL && *s == Ctrl_N)) { @@ -3596,7 +3794,7 @@ static void redrawcmdprompt(void) msg_putchar(ccline.cmdfirstc); } if (ccline.cmdprompt != NULL) { - msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr); + msg_puts_attr(ccline.cmdprompt, ccline.cmdattr); ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns; // do the reverse of cmd_startcol() if (ccline.cmdfirstc != NUL) { @@ -3609,9 +3807,7 @@ static void redrawcmdprompt(void) } } -/* - * Redraw what is currently on the command line. - */ +// Redraw what is currently on the command line. void redrawcmd(void) { if (cmd_silent) { @@ -3637,7 +3833,7 @@ void redrawcmd(void) redrawcmdprompt(); // Don't use more prompt, truncate the cmdline if it doesn't fit. - msg_no_more = TRUE; + msg_no_more = true; draw_cmdline(0, ccline.cmdlen); msg_clr_eos(); msg_no_more = false; @@ -3648,11 +3844,9 @@ void redrawcmd(void) putcmdline(ccline.special_char, ccline.special_shift); } - /* - * An emsg() before may have set msg_scroll. This is used in normal mode, - * in cmdline mode we can reset them now. - */ - msg_scroll = FALSE; // next message overwrites cmdline + // An emsg() before may have set msg_scroll. This is used in normal mode, + // in cmdline mode we can reset them now. + msg_scroll = false; // next message overwrites cmdline // Typing ':' at the more prompt may set skip_redraw. We don't want this // in cmdline mode. @@ -3670,6 +3864,9 @@ void compute_cmdrow(void) cmdline_row = wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height + global_stl_height(); } + if (cmdline_row == Rows && p_ch > 0) { + cmdline_row--; + } lines_left = cmdline_row; } @@ -3728,12 +3925,10 @@ void gotocmdline(bool clr) cmd_cursor_goto(cmdline_row, 0); } -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text with - * backspaces and the replacement string is inserted, followed by "c". - */ +// Check the word in front of the cursor for an abbreviation. +// Called when the non-id character "c" has been entered. +// When an abbreviation is recognized it is removed from the text with +// backspaces and the replacement string is inserted, followed by "c". static int ccheck_abbr(int c) { int spos = 0; @@ -3774,34 +3969,31 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) { #ifdef BACKSLASH_IN_FILENAME # define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" -# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`%#'\"|!<") - char_u buf[sizeof(PATH_ESC_CHARS)]; +# define BUFFER_ESC_CHARS (" \t\n*?[`%#'\"|!<") + char buf[sizeof(PATH_ESC_CHARS)]; int j = 0; // Don't escape '[', '{' and '!' if they are in 'isfname' and for the // ":buffer" command. for (const char *p = what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS; *p != NUL; p++) { - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) { + if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec((uint8_t)(*p))) { buf[j++] = *p; } } buf[j] = NUL; - char *p = (char *)vim_strsave_escaped((const char_u *)fname, - (const char_u *)buf); + char *p = vim_strsave_escaped(fname, buf); #else -# define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") -# define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") -# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`$\\%#'\"|!<") - char *p = - (char *)vim_strsave_escaped((const char_u *)fname, - what == VSE_SHELL ? SHELL_ESC_CHARS - : what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS); +# define PATH_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<" +# define SHELL_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<>();&" +# define BUFFER_ESC_CHARS " \t\n*?[`$\\%#'\"|!<" + char *p = vim_strsave_escaped(fname, + what == VSE_SHELL ? SHELL_ESC_CHARS : what == + VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS); if (what == VSE_SHELL && csh_like_shell()) { // For csh and similar shells need to put two backslashes before '!'. // One is taken by Vim, one by the shell. - char *s = (char *)vim_strsave_escaped((const char_u *)p, - (const char_u *)"!"); + char *s = vim_strsave_escaped(p, "!"); xfree(p); p = s; } @@ -3819,16 +4011,16 @@ char *vim_strsave_fnameescape(const char *const fname, const int what) /// Put a backslash before the file name in "pp", which is in allocated memory. void escape_fname(char **pp) { - char_u *p = xmalloc(STRLEN(*pp) + 2); + char *p = xmalloc(strlen(*pp) + 2); p[0] = '\\'; STRCPY(p + 1, *pp); xfree(*pp); - *pp = (char *)p; + *pp = p; } /// For each file name in files[num_files]: /// If 'orig_pat' starts with "~/", replace the home directory with "~". -void tilde_replace(char_u *orig_pat, int num_files, char **files) +void tilde_replace(char *orig_pat, int num_files, char **files) { char *p; @@ -3867,76 +4059,127 @@ static CmdlineInfo *get_ccline_ptr(void) } } -/// Get the current command-line completion type. -char_u *get_cmdline_completion(void) +/// Get the current command-line type. +/// Returns ':' or '/' or '?' or '@' or '>' or '-' +/// Only works when the command line is being edited. +/// Returns NUL when something is wrong. +static int get_cmdline_type(void) +{ + CmdlineInfo *p = get_ccline_ptr(); + + if (p == NULL) { + return NUL; + } + if (p->cmdfirstc == NUL) { + return (p->input_fn) ? '@' : '-'; + } + return p->cmdfirstc; +} + +/// Get the current command line in allocated memory. +/// Only works when the command line is being edited. +/// +/// @return NULL when something is wrong. +static char *get_cmdline_str(void) { if (cmdline_star > 0) { return NULL; } CmdlineInfo *p = get_ccline_ptr(); - if (p != NULL && p->xpc != NULL) { - set_expand_context(p->xpc); - char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context); - if (cmd_compl != NULL) { - return vim_strsave((char_u *)cmd_compl); - } + if (p == NULL) { + return NULL; } - - return NULL; + return xstrnsave(p->cmdbuff, (size_t)p->cmdlen); } -/* - * Get the current command line in allocated memory. - * Only works when the command line is being edited. - * Returns NULL when something is wrong. - */ -char_u *get_cmdline_str(void) +/// Get the current command-line completion type. +static char *get_cmdline_completion(void) { if (cmdline_star > 0) { return NULL; } CmdlineInfo *p = get_ccline_ptr(); - if (p == NULL) { + if (p == NULL || p->xpc == NULL) { return NULL; } - return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen); + + set_expand_context(p->xpc); + char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context); + if (cmd_compl != NULL) { + return xstrdup(cmd_compl); + } + + return NULL; +} + +/// "getcmdcompltype()" function +void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_cmdline_completion(); +} + +/// "getcmdline()" function +void f_getcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = get_cmdline_str(); } -/* - * Get the current command line position, counted in bytes. - * Zero is the first position. - * Only works when the command line is being edited. - * Returns -1 when something is wrong. - */ -int get_cmdline_pos(void) +/// "getcmdpos()" function +void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { CmdlineInfo *p = get_ccline_ptr(); + rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0; +} - if (p == NULL) { - return -1; - } - return p->cmdpos; +/// "getcmdscreenpos()" function +void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + CmdlineInfo *p = get_ccline_ptr(); + rettv->vval.v_number = p != NULL ? p->cmdspos + 1 : 0; } -/// Get the command line cursor screen position. -int get_cmdline_screen_pos(void) +/// "getcmdtype()" function +void f_getcmdtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xmallocz(1); + rettv->vval.v_string[0] = (char)get_cmdline_type(); +} + +/// Set the command line str to "str". +/// @return 1 when failed, 0 when OK. +static int set_cmdline_str(const char *str, int pos) { CmdlineInfo *p = get_ccline_ptr(); if (p == NULL) { - return -1; + return 1; } - return p->cmdspos; + + int len = (int)strlen(str); + realloc_cmdbuff(len + 1); + p->cmdlen = len; + STRCPY(p->cmdbuff, str); + + p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos; + new_cmdpos = p->cmdpos; + + redrawcmd(); + + // Trigger CmdlineChanged autocommands. + do_autocmd_cmdlinechanged(get_cmdline_type()); + + return 0; } -/* - * Set the command line byte position to "pos". Zero is the first position. - * Only works when the command line is being edited. - * Returns 1 when failed, 0 when OK. - */ -int set_cmdline_pos(int pos) +/// Set the command line byte position to "pos". Zero is the first position. +/// Only works when the command line is being edited. +/// @return 1 when failed, 0 when OK. +static int set_cmdline_pos(int pos) { CmdlineInfo *p = get_ccline_ptr(); @@ -3954,23 +4197,39 @@ int set_cmdline_pos(int pos) return 0; } -/* - * Get the current command-line type. - * Returns ':' or '/' or '?' or '@' or '>' or '-' - * Only works when the command line is being edited. - * Returns NUL when something is wrong. - */ -int get_cmdline_type(void) +/// "setcmdline()" function +void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - CmdlineInfo *p = get_ccline_ptr(); + if (tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_opt_number_arg(argvars, 1) == FAIL) { + return; + } - if (p == NULL) { - return NUL; + int pos = -1; + if (argvars[1].v_type != VAR_UNKNOWN) { + bool error = false; + + pos = (int)tv_get_number_chk(&argvars[1], &error) - 1; + if (error) { + return; + } + if (pos < 0) { + emsg(_(e_positive)); + return; + } } - if (p->cmdfirstc == NUL) { - return (p->input_fn) ? '@' : '-'; + + rettv->vval.v_number = set_cmdline_str(argvars[0].vval.v_string, pos); +} + +/// "setcmdpos()" function +void f_setcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int pos = (int)tv_get_number(&argvars[0]) - 1; + + if (pos >= 0) { + rettv->vval.v_number = set_cmdline_pos(pos); } - return p->cmdfirstc; } /// Return the first character of the current command line. @@ -3995,7 +4254,7 @@ int get_list_range(char **str, int *num1, int *num2) *str = skipwhite((*str)); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range - vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); *str += len; *num1 = (int)num; first = true; @@ -4003,7 +4262,7 @@ int get_list_range(char **str, int *num1, int *num2) *str = skipwhite((*str)); if (**str == ',') { // parse "to" part of range *str = skipwhite((*str) + 1); - vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); if (len > 0) { *num2 = (int)num; *str = skipwhite((*str) + len); @@ -4021,6 +4280,24 @@ void cmdline_init(void) CLEAR_FIELD(ccline); } +/// Check value of 'cedit' and set cedit_key. +/// Returns NULL if value is OK, error message otherwise. +char *check_cedit(void) +{ + int n; + + if (*p_cedit == NUL) { + cedit_key = -1; + } else { + n = string_to_key(p_cedit); + if (vim_isprintc(n)) { + return e_invarg; + } + cedit_key = n; + } + return NULL; +} + /// Open a window on the current command line and history. Allow editing in /// the window. Returns when the window is closed. /// Returns: @@ -4068,17 +4345,27 @@ static int open_cmdwin(void) ga_clear(&winsizes); return K_IGNORE; } + // Don't let quitting the More prompt make this fail. + got_int = false; + + // Set "cmdwin_type" before any autocommands may mess things up. cmdwin_type = get_cmdline_type(); cmdwin_level = ccline.level; // Create empty command-line buffer. - buf_open_scratch(0, _("[Command Line]")); + if (buf_open_scratch(0, _("[Command Line]")) == FAIL) { + // Some autocommand messed it up? + win_close(curwin, true, false); + ga_clear(&winsizes); + cmdwin_type = 0; + return Ctrl_C; + } // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value("bh", 0L, "wipe", OPT_LOCAL); - curwin->w_p_rl = cmdmsg_rl; - cmdmsg_rl = false; + set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL); curbuf->b_p_ma = true; curwin->w_p_fen = false; + curwin->w_p_rl = cmdmsg_rl; + cmdmsg_rl = false; // Don't allow switching to another buffer. curbuf->b_ro_locked++; @@ -4092,7 +4379,7 @@ static int open_cmdwin(void) add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true); add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true); } - set_option_value("ft", 0L, "vim", OPT_LOCAL); + set_option_value_give_err("ft", 0L, "vim", OPT_LOCAL); } curbuf->b_ro_locked--; @@ -4111,7 +4398,7 @@ static int open_cmdwin(void) i = 0; } if (get_histentry(histtype)[i].hisstr != NULL) { - ml_append(lnum++, (char *)get_histentry(histtype)[i].hisstr, (colnr_T)0, false); + ml_append(lnum++, get_histentry(histtype)[i].hisstr, (colnr_T)0, false); } } while (i != *get_hisidx(histtype)); } @@ -4119,7 +4406,7 @@ static int open_cmdwin(void) // Replace the empty last line with the current command-line and put the // cursor there. - ml_replace(curbuf->b_ml.ml_line_count, (char *)ccline.cmdbuff, true); + ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true); curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_cursor.col = ccline.cmdpos; changed_line_abv_curs(); @@ -4128,7 +4415,7 @@ static int open_cmdwin(void) ccline.redraw_state = kCmdRedrawNone; ui_call_cmdline_hide(ccline.level); } - redraw_later(curwin, SOME_VALID); + redraw_later(curwin, UPD_SOME_VALID); // No Ex mode here! exmode_active = false; @@ -4151,9 +4438,7 @@ static int open_cmdwin(void) RedrawingDisabled = 0; int save_count = save_batch_count(); - /* - * Call the main loop until <CR> or CTRL-C is typed. - */ + // Call the main loop until <CR> or CTRL-C is typed. normal_enter(true, false); RedrawingDisabled = i; @@ -4189,7 +4474,7 @@ static int open_cmdwin(void) if (histtype == HIST_CMD) { // Execute the command directly. - ccline.cmdbuff = (char_u *)xstrdup(p); + ccline.cmdbuff = xstrdup(p); cmdwin_result = CAR; } else { // First need to cancel what we were doing. @@ -4203,16 +4488,16 @@ static int open_cmdwin(void) // and don't modify the cmd window. ccline.cmdbuff = NULL; } else { - ccline.cmdbuff = vim_strsave(get_cursor_line_ptr()); + ccline.cmdbuff = xstrdup(get_cursor_line_ptr()); } if (ccline.cmdbuff == NULL) { - ccline.cmdbuff = vim_strsave((char_u *)""); + ccline.cmdbuff = xstrdup(""); ccline.cmdlen = 0; ccline.cmdbufflen = 1; ccline.cmdpos = 0; cmdwin_result = Ctrl_C; } else { - ccline.cmdlen = (int)STRLEN(ccline.cmdbuff); + ccline.cmdlen = (int)strlen(ccline.cmdbuff); ccline.cmdbufflen = ccline.cmdlen + 1; ccline.cmdpos = curwin->w_cursor.col; if (ccline.cmdpos > ccline.cmdlen) { @@ -4229,6 +4514,7 @@ static int open_cmdwin(void) // First go back to the original window. wp = curwin; set_bufref(&bufref, curbuf); + skip_win_fix_cursor = true; win_goto(old_curwin); // win_goto() may trigger an autocommand that already closes the @@ -4245,6 +4531,7 @@ static int open_cmdwin(void) // Restore window sizes. win_size_restore(&winsizes); + skip_win_fix_cursor = false; } ga_clear(&winsizes); @@ -4285,7 +4572,7 @@ char *script_get(exarg_T *const eap, size_t *const lenp) const char *const cmd = (const char *)eap->arg; if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) { - *lenp = STRLEN(eap->arg); + *lenp = strlen(eap->arg); return eap->skip ? NULL : xmemdupz(eap->arg, *lenp); } |