diff options
Diffstat (limited to 'src/nvim/ex_getln.c')
-rw-r--r-- | src/nvim/ex_getln.c | 1129 |
1 files changed, 688 insertions, 441 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 78b8e43e65..4c26cfe500 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -11,7 +11,9 @@ #include <stdlib.h> #include <string.h> +#include "nvim/api/extmark.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/arabic.h" #include "nvim/ascii.h" #include "nvim/assert.h" @@ -33,15 +35,18 @@ #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" -#include "nvim/keymap.h" +#include "nvim/keycodes.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -67,6 +72,7 @@ #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/ui.h" +#include "nvim/undo.h" #include "nvim/vim.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" @@ -103,11 +109,9 @@ typedef enum { kCmdRedrawAll, } CmdRedraw; -/* - * Variables shared between getcmdline(), redrawcmdline() and others. - * These need to be saved when using CTRL-R |, that's why they are in a - * structure. - */ +// Variables shared between getcmdline(), redrawcmdline() and others. +// These need to be saved when using CTRL-R |, that's why they are in a +// structure. struct cmdline_info { char_u *cmdbuff; // pointer to command line buffer int cmdbufflen; // length of cmdbuff @@ -134,6 +138,7 @@ struct cmdline_info { bool special_shift; ///< shift of last putcmdline char CmdRedraw redraw_state; ///< needed redraw for external cmdline }; + /// Last value of prompt_id, incremented when doing new prompt static unsigned last_prompt_id = 0; @@ -191,13 +196,12 @@ typedef struct command_line_state { typedef struct cmdline_info CmdlineInfo; -/* 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(). - * TODO: make it local to getcmdline() and pass it around. */ +/// 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(). static struct cmdline_info ccline; -static int cmd_showtail; // Only show path tail in lists ? +static int cmd_showtail; // Only show path tail in lists ? static int new_cmdpos; // position set by set_cmdline_pos() @@ -229,27 +233,13 @@ static int compl_match_arraysize; static int compl_startcol; static int compl_selected; -/// |:checkhealth| completion items -/// -/// Regenerates on every new command line prompt, to accomodate changes on the -/// runtime files. -typedef struct { - garray_T names; // healthcheck names - unsigned last_gen; // last_prompt_id where names were generated -} CheckhealthComp; - -/// Cookie used when converting filepath to name -struct healthchecks_cookie { - garray_T *names; // global healthchecks - bool is_lua; // true if the current entry is a Lua healthcheck -}; - -static CheckhealthComp healthchecks = { GA_INIT(sizeof(char_u *), 10), 0 }; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" #endif +static handle_T cmdpreview_bufnr = 0; +static long cmdpreview_ns = 0; + static int cmd_hkmap = 0; // Hebrew mapping during command line static void save_viewstate(viewstate_T *vs) @@ -292,61 +282,25 @@ static void init_incsearch_state(incsearch_state_T *s) /// Given to ExpandGeneric() to obtain all available heathcheck names. /// @param[in] idx Index of the healthcheck item. /// @param[in] xp Not used. -static char_u *get_healthcheck_names(expand_T *xp, int idx) -{ - // Generate the first time or on new prompt. - if (healthchecks.last_gen == 0 || healthchecks.last_gen != last_prompt_id) { - ga_clear_strings(&healthchecks.names); - char *patterns[3] = { "autoload/health/**.vim", "lua/**/**/health/init.lua", // NOLINT - "lua/**/**/health.lua" }; // NOLINT - for (int i = 0; i < 3; i++) { - struct healthchecks_cookie hcookie = { .names = &healthchecks.names, .is_lua = i != 0 }; - do_in_runtimepath((char_u *)patterns[i], DIP_ALL, get_healthcheck_cb, &hcookie); - - if (healthchecks.names.ga_len > 0) { - ga_remove_duplicate_strings(&healthchecks.names); - } - } - // Tracked to regenerate items on next prompt. - healthchecks.last_gen = last_prompt_id; - } - return idx < - (int)healthchecks.names.ga_len ? ((char_u **)(healthchecks.names.ga_data))[idx] : NULL; -} - -/// Transform healthcheck file path into it's name. -/// -/// Used as a callback for do_in_runtimepath -/// @param[in] path Expanded path to a possible healthcheck. -/// @param[out] cookie Array where names will be inserted. -static void get_healthcheck_cb(char_u *path, void *cookie) +static char *get_healthcheck_names(expand_T *xp, int idx) { - if (path != NULL) { - struct healthchecks_cookie *hcookie = (struct healthchecks_cookie *)cookie; - char *pattern; - char *sub = "\\1"; - char_u *res; - - if (hcookie->is_lua) { - // Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp" - pattern = ".*lua[\\/]\\(.\\{-}\\)[\\/]health\\([\\/]init\\)\\?\\.lua$"; - } else { - // Vim: transform "../autoload/health/provider.vim" into "provider" - pattern = ".*[\\/]\\([^\\/]*\\)\\.vim$"; - } + static Object names = OBJECT_INIT; + static unsigned last_gen = 0; - res = do_string_sub(path, (char_u *)pattern, (char_u *)sub, NULL, (char_u *)"g"); - if (hcookie->is_lua && res != NULL) { - // Replace slashes with dots as represented by the healthcheck plugin. - char_u *ares = do_string_sub(res, (char_u *)"[\\/]", (char_u *)".", NULL, (char_u *)"g"); - xfree(res); - res = ares; - } + if (last_gen != last_prompt_id || last_gen == 0) { + Array a = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err); + api_clear_error(&err); + api_free_object(names); + names = res; + last_gen = last_prompt_id; + } - if (res != NULL) { - GA_APPEND(char_u *, hcookie->names, res); - } + if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) { + return names.data.array.items[idx].data.string.data; } + return NULL; } // Return true when 'incsearch' highlighting is to be done. @@ -355,12 +309,11 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s int *skiplen, int *patlen) FUNC_ATTR_NONNULL_ALL { - char_u *cmd; - cmdmod_T save_cmdmod = cmdmod; - char_u *p; + char *cmd; + char *p; bool delim_optional = false; int delim; - char_u *end; + char *end; char *dummy; exarg_T ea; pos_T save_cursor; @@ -390,14 +343,14 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s memset(&ea, 0, sizeof(ea)); ea.line1 = 1; ea.line2 = 1; - ea.cmd = ccline.cmdbuff; + ea.cmd = (char *)ccline.cmdbuff; ea.addr_type = ADDR_LINES; - parse_command_modifiers(&ea, &dummy, true); - cmdmod = save_cmdmod; + cmdmod_T dummy_cmdmod; + parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true); cmd = skip_range(ea.cmd, NULL); - if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) { + if (vim_strchr("sgvl", *cmd) == NULL) { goto theend; } @@ -421,7 +374,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s if (*p == '!') { p = skipwhite(p + 1); } - while (ASCII_ISALPHA(*(p = skipwhite(p)))) { + while (ASCII_ISALPHA(*(p = skipwhite((char *)p)))) { p++; } if (*p == NUL) { @@ -449,7 +402,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s p = skipwhite(p); delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++; *search_delim = delim; - end = skip_regexp(p, delim, p_magic, NULL); + end = (char *)skip_regexp((char_u *)p, delim, p_magic, NULL); use_last_pat = end == p && *end == delim; if (end == p && !use_last_pat) { @@ -458,11 +411,11 @@ 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_u c = *end; + char c = *end; int empty; *end = NUL; - empty = empty_pattern(p); + empty = empty_pattern((char_u *)p); *end = c; if (empty) { goto theend; @@ -470,7 +423,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s } // found a non-empty pattern or // - *skiplen = (int)(p - ccline.cmdbuff); + *skiplen = (int)((char_u *)p - ccline.cmdbuff); *patlen = (int)(end - p); // parse the address range @@ -632,7 +585,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat validate_cursor(); // May redraw the status line to show the cursor position. - if (p_ru && curwin->w_status_height > 0) { + if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { curwin->w_redr_status = true; } @@ -686,8 +639,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((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c) - != NULL) { + || vim_strchr((p_magic ? "\\~^$.*[" : "\\^$"), *c) != NULL) { // put a backslash before special characters stuffcharReadbuff(*c); *c = '\\'; @@ -731,14 +683,25 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// Internal entry point for cmdline mode. /// -/// caller must use save_cmdline and restore_cmdline. Best is to use -/// getcmdline or getcmdline_prompt, instead of calling this directly. -static uint8_t *command_line_enter(int firstc, long count, int indent) +/// @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) { + bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages); + + if (cmdheight0) { + // If cmdheight is 0, cmdheight must be set to 1 when we enter command line. + set_option_value("ch", 1L, NULL, 0); + update_screen(VALID); // redraw the screen NOW + } + // can be invoked recursively, identify each level static int cmdline_level = 0; cmdline_level++; + bool save_cmdpreview = cmdpreview; + cmdpreview = false; CommandLineState state = { .firstc = firstc, .count = count, @@ -750,6 +713,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) CommandLineState *s = &state; s->save_p_icm = vim_strsave(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. + // Some changes will be needed if this is no longer the case. + assert(init_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) { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } if (s->firstc == -1) { s->firstc = NUL; @@ -772,7 +749,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.cmdindent = (s->firstc > 0 ? s->indent : 0); // alloc initial ccline.cmdbuff - alloc_cmdbuff(exmode_active ? 250 : s->indent + 1); + alloc_cmdbuff(indent + 50); ccline.cmdlen = ccline.cmdpos = 0; ccline.cmdbuff[0] = NUL; @@ -789,6 +766,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.cmdlen = s->indent; } + if (cmdline_level == 50) { + // Somehow got into a loop recursively calling getcmdline(), bail out. + emsg(_(e_command_too_recursive)); + goto theend; + } + ExpandInit(&s->xpc); ccline.xpc = &s->xpc; @@ -818,15 +801,15 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (ccline.input_fn) { s->xpc.xp_context = ccline.xp_context; - s->xpc.xp_pattern = ccline.cmdbuff; - s->xpc.xp_arg = ccline.xp_arg; + s->xpc.xp_pattern = (char *)ccline.cmdbuff; + s->xpc.xp_arg = (char *)ccline.xp_arg; } // Avoid scrolling when called by a recursive do_cmdline(), e.g. when // doing ":@0" when register 0 doesn't contain a CR. msg_scroll = false; - State = CMDLINE; + State = MODE_CMDLINE; if (s->firstc == '/' || s->firstc == '?' || s->firstc == '@') { // Use ":lmap" mappings for search pattern and input(). @@ -837,7 +820,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } if (*s->b_im_ptr == B_IMODE_LMAP) { - State |= LANGMAP; + State |= MODE_LANGMAP; } } @@ -892,11 +875,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) tv_dict_set_keys_readonly(dict); try_enter(&tstate); - apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf, - false, curbuf); + apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); restore_v_event(dict, &save_v_event); - tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { msg_putchar('\n'); @@ -906,7 +887,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } tl_ret = true; } - trigger_modechanged(); + may_trigger_modechanged(); state_enter(&s->state); @@ -918,8 +899,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) tv_dict_add_bool(dict, S_LEN("abort"), s->gotesc ? kBoolVarTrue : kBoolVarFalse); try_enter(&tstate); - apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf, - false, curbuf); + apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf); // error printed below, to avoid redraw issues tl_ret = try_leave(&tstate, &err); if (tv_dict_get_number(dict, "abort") != 0) { @@ -933,11 +913,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ExpandCleanup(&s->xpc); ccline.xpc = NULL; - if (s->gotesc) { - // There might be a preview window open for inccommand. Close it. - close_preview_windows(); - } - finish_incsearch_highlighting(s->gotesc, &s->is_state, false); if (ccline.cmdbuff != NULL) { @@ -977,17 +952,21 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) need_wait_return = false; } - set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE, - SID_NONE); + set_string_option_direct("icm", -1, (char *)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); + } setmouse(); ui_cursor_shape(); // may show different cursor shape + sb_text_end_cmdline(); + +theend: xfree(s->save_p_icm); xfree(ccline.last_colors.cmdbuff); kv_destroy(ccline.last_colors.colors); - sb_text_end_cmdline(); - char_u *p = ccline.cmdbuff; if (ui_has(kUICmdline)) { @@ -996,6 +975,21 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } cmdline_level--; + + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } else { + ccline.cmdbuff = NULL; + } + + if (cmdheight0) { + // Restore cmdheight + set_option_value("ch", 0L, NULL, 0); + + // Redraw is needed for command line completion + redraw_all_later(CLEAR); + } + return p; } @@ -1188,7 +1182,7 @@ static int command_line_execute(VimState *state, int key) // cursor int found = false; - int j = (int)(s->xpc.xp_pattern - ccline.cmdbuff); + int j = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff); int i = 0; while (--j > 0) { // check for start of menu name @@ -1243,7 +1237,7 @@ static int command_line_execute(VimState *state, int key) int found = false; int j = ccline.cmdpos; - int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff); + int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff); while (--j > i) { j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j); if (vim_ispathsep(ccline.cmdbuff[j])) { @@ -1264,7 +1258,7 @@ static int command_line_execute(VimState *state, int key) int found = false; int j = ccline.cmdpos - 1; - int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff); + int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff); while (--j > i) { j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j); if (vim_ispathsep(ccline.cmdbuff[j]) @@ -1309,12 +1303,13 @@ static int command_line_execute(VimState *state, int key) } } - // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert - // mode when 'insertmode' is set, CTRL-\ e prompts for an expression. + // CTRL-\ CTRL-N 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 @@ -1339,15 +1334,9 @@ static int command_line_execute(VimState *state, int key) s->c = get_expr_register(); if (s->c == '=') { - // Need to save and restore ccline. And set "textlock" - // to avoid nasty things like going to another buffer when - // evaluating an expression. - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); textlock++; p = get_expr_line(); textlock--; - restore_cmdline(&save_ccline); if (p != NULL) { len = (int)STRLEN(p); @@ -1376,9 +1365,6 @@ static int command_line_execute(VimState *state, int key) redrawcmd(); return command_line_not_changed(s); } else { - if (s->c == Ctrl_G && p_im && restart_edit == 0) { - restart_edit = 'a'; - } s->gotesc = true; // will free ccline.cmdbuff after putting it // in history return 0; // back to Normal mode @@ -1587,9 +1573,12 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ int search_flags = SEARCH_NOOF; char_u save; - if (search_delim == ccline.cmdbuff[skiplen]) { pat = last_search_pattern(); + if (pat == NULL) { + restore_last_search_pattern(); + return FAIL; + } skiplen = 0; patlen = (int)STRLEN(pat); } else { @@ -1758,7 +1747,7 @@ static int command_line_handle_key(CommandLineState *s) } if (mb_get_class(p) != i) { - p += utfc_ptr2len(p); + p += utfc_ptr2len((char *)p); } } @@ -1809,11 +1798,11 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode("", LANGMAP, false)) { + if (map_to_exists_mode("", MODE_LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. - State ^= LANGMAP; + State ^= MODE_LANGMAP; if (s->b_im_ptr != NULL) { - if (State & LANGMAP) { + if (State & MODE_LANGMAP) { *s->b_im_ptr = B_IMODE_LMAP; } else { *s->b_im_ptr = B_IMODE_NONE; @@ -1869,6 +1858,7 @@ static int command_line_handle_key(CommandLineState *s) 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 @@ -1877,7 +1867,8 @@ static int command_line_handle_key(CommandLineState *s) if (i == Ctrl_R) { s->c = plain_vgetc(); // CTRL-R CTRL-R <char> } - --no_mapping; + 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... @@ -1888,10 +1879,7 @@ static int command_line_handle_key(CommandLineState *s) beep_flush(); s->c = ESC; } else { - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); s->c = get_expr_register(); - restore_cmdline(&save_ccline); } } @@ -1943,7 +1931,7 @@ static int command_line_handle_key(CommandLineState *s) } ccline.cmdspos += cells; - ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos); + ccline.cmdpos += utfc_ptr2len((char *)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] != ' '); @@ -1978,7 +1966,6 @@ static int command_line_handle_key(CommandLineState *s) // Ignore mouse event or open_cmdwin() result. return command_line_not_changed(s); - case K_MIDDLEDRAG: case K_MIDDLERELEASE: return command_line_not_changed(s); // Ignore mouse @@ -1988,7 +1975,6 @@ static int command_line_handle_key(CommandLineState *s) redrawcmd(); return command_line_changed(s); - case K_LEFTDRAG: case K_LEFTRELEASE: case K_RIGHTDRAG: @@ -2018,7 +2004,7 @@ static int command_line_handle_key(CommandLineState *s) // 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.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1; ccline.cmdspos += cells; } return command_line_not_changed(s); @@ -2038,7 +2024,6 @@ static int command_line_handle_key(CommandLineState *s) case K_MOUSEMOVE: return command_line_not_changed(s); - case K_SELECT: // end of Select mode mapping - ignore return command_line_not_changed(s); @@ -2119,7 +2104,7 @@ static int command_line_handle_key(CommandLineState *s) int len = 0; int old_firstc; - xfree(ccline.cmdbuff); + XFREE_CLEAR(ccline.cmdbuff); s->xpc.xp_context = EXPAND_NOTHING; if (s->hiscnt == hislen) { p = s->lookfor; // back to the old one @@ -2191,7 +2176,11 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_Q: s->ignore_drag_release = true; putcmdline('^', true); - s->c = get_literal(); // get next (two) character(s) + + // Get next (two) characters. + // Do not include modifiers into the key for CTRL-SHIFT-V. + s->c = get_literal(mod_mask & MOD_MASK_SHIFT); + s->do_abbr = false; // don't do abbreviation now ccline.special_char = NUL; // may need to remove ^ when composing char was typed @@ -2251,14 +2240,13 @@ static int command_line_handle_key(CommandLineState *s) if (IS_SPECIAL(s->c) || mod_mask != 0) { put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true); } else { - int j = utf_char2bytes(s->c, IObuff); + int j = utf_char2bytes(s->c, (char *)IObuff); IObuff[j] = NUL; // exclude composing chars put_on_cmdline(IObuff, j, true); } return command_line_changed(s); } - static int command_line_not_changed(CommandLineState *s) { // Incremental searches for "/" and "?": @@ -2280,12 +2268,272 @@ static int empty_pattern(char_u *p) // remove trailing \v and the like while (n >= 2 && p[n - 2] == '\\' - && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) { + && vim_strchr("mMvVcCZ", p[n - 1]) != NULL) { n -= 2; } return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); } +handle_T cmdpreview_get_bufnr(void) +{ + return cmdpreview_bufnr; +} + +long cmdpreview_get_ns(void) +{ + return cmdpreview_ns; +} + +/// Sets up command preview buffer. +/// +/// @return Pointer to command preview buffer if succeeded, NULL if failed. +static buf_T *cmdpreview_open_buf(void) +{ + buf_T *cmdpreview_buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; + + // If preview buffer doesn't exist, open one. + if (cmdpreview_buf == NULL) { + Error err = ERROR_INIT; + handle_T bufnr = nvim_create_buf(false, true, &err); + + if (ERROR_SET(&err)) { + return NULL; + } + + cmdpreview_buf = buflist_findnr(bufnr); + } + + // Preview buffer cannot preview itself! + if (cmdpreview_buf == curbuf) { + return NULL; + } + + // Rename preview buffer. + aco_save_T aco; + aucmd_prepbuf(&aco, cmdpreview_buf); + int retv = rename_buffer("[Preview]"); + aucmd_restbuf(&aco); + + if (retv == FAIL) { + return NULL; + } + + // Temporarily switch to preview buffer to set it up for previewing. + aucmd_prepbuf(&aco, cmdpreview_buf); + buf_clear(); + curbuf->b_p_ma = true; + curbuf->b_p_ul = -1; + curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) + aucmd_restbuf(&aco); + cmdpreview_bufnr = cmdpreview_buf->handle; + + return cmdpreview_buf; +} + +/// Open command preview window if it's not already open. +/// Returns to original window after opening command preview window. +/// +/// @param cmdpreview_buf Pointer to command preview buffer +/// +/// @return Pointer to command preview window if succeeded, NULL if failed. +static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf) +{ + win_T *save_curwin = curwin; + + // Open preview window. + if (win_split((int)p_cwh, WSP_BOT) == FAIL) { + return NULL; + } + + win_T *preview_win = curwin; + Error err = ERROR_INIT; + + // Switch to preview buffer + try_start(); + int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0); + if (try_end(&err) || result == FAIL) { + api_clear_error(&err); + return NULL; + } + + curwin->w_p_cul = false; + curwin->w_p_cuc = false; + curwin->w_p_spell = false; + curwin->w_p_fen = false; + + win_enter(save_curwin, false); + return preview_win; +} + +/// Closes any open command preview windows. +static void cmdpreview_close_win(void) +{ + buf_T *buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; + if (buf != NULL) { + close_windows(buf, false); + } +} + +/// Show 'inccommand' preview if command is previewable. It works like this: +/// 1. Store current undo information so we can revert to current state later. +/// 2. Execute the preview callback with the parsed command, preview buffer number and preview +/// namespace number as arguments. The preview callback sets the highlight and does the +/// changes required for the preview if needed. +/// 3. Preview callback returns 0, 1 or 2. 0 means no preview is shown. 1 means preview is shown +/// but preview window doesn't need to be opened. 2 means preview is shown and preview window +/// needs to be opened if inccommand=split. +/// 4. Use the return value of the preview callback to determine whether to +/// open the preview window or not and open preview window if needed. +/// 5. If the return value of the preview callback is not 0, update the screen while the effects +/// of the preview are still in place. +/// 6. Revert all changes made by the preview callback. +/// +/// @return whether preview is shown or not. +static bool cmdpreview_may_show(CommandLineState *s) +{ + // Parse the command line and return if it fails. + exarg_T ea; + CmdParseInfo cmdinfo; + // Copy the command line so we can modify it. + int cmdpreview_type = 0; + char *cmdline = xstrdup((char *)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)) { + emsg_off--; + goto end; + } + emsg_off--; + + // Check if command is previewable, if not, don't attempt to show preview + if (!(ea.argt & EX_PREVIEW)) { + undo_cmdmod(&cmdinfo.cmdmod); + goto end; + } + + // Swap invalid command range if needed + if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) { + linenr_T lnum = ea.line1; + ea.line1 = ea.line2; + ea.line2 = lnum; + } + + time_t save_b_u_time_cur = curbuf->b_u_time_cur; + long save_b_u_seq_cur = curbuf->b_u_seq_cur; + u_header_T *save_b_u_newhead = curbuf->b_u_newhead; + long save_b_p_ul = curbuf->b_p_ul; + int save_b_changed = curbuf->b_changed; + int save_w_p_cul = curwin->w_p_cul; + int save_w_p_cuc = curwin->w_p_cuc; + bool save_hls = p_hls; + varnumber_T save_changedtick = buf_get_changedtick(curbuf); + bool icm_split = *p_icm == 's'; // inccommand=split + buf_T *cmdpreview_buf; + win_T *cmdpreview_win; + cmdmod_T save_cmdmod = cmdmod; + + cmdpreview = true; + emsg_silent++; // Block error reporting as the command may be incomplete, + // but still update v:errmsg + msg_silent++; // Block messages, namely ones that prompt + block_autocmds(); // Block events + garray_T save_view; + win_size_save(&save_view); // Save current window sizes + save_search_patterns(); // Save search patterns + curbuf->b_p_ul = LONG_MAX; // Make sure we can undo all changes + curwin->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights + curwin->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights + p_hls = false; // Don't show search highlighting during live substitution + 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 + + // Open preview buffer if inccommand=split. + if (!icm_split) { + cmdpreview_bufnr = 0; + } else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) { + abort(); + } + + // Setup preview namespace if it's not already set. + if (!cmdpreview_ns) { + cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT); + } + + // Execute the preview callback and use its return value to determine whether to show preview or + // open the preview window. The preview callback also handles doing the changes and highlights for + // the preview. + Error err = ERROR_INIT; + try_start(); + cmdpreview_type = execute_cmd(&ea, &cmdinfo, true); + if (try_end(&err)) { + api_clear_error(&err); + cmdpreview_type = 0; + } + + // If inccommand=split and preview callback returns 2, open preview window. + if (icm_split && cmdpreview_type == 2 + && (cmdpreview_win = cmdpreview_open_win(cmdpreview_buf)) == NULL) { + // If there's not enough room to open the preview window, just preview without the window. + cmdpreview_type = 1; + } + + // If preview callback is nonzero, update screen now. + if (cmdpreview_type != 0) { + int save_rd = RedrawingDisabled; + RedrawingDisabled = 0; + update_screen(SOME_VALID); + RedrawingDisabled = save_rd; + } + + // Close preview window if it's open. + if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) { + cmdpreview_close_win(); + } + // Clear preview highlights. + extmark_clear(curbuf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL); + + curbuf->b_changed = save_b_changed; // Preserve 'modified' during preview + + if (curbuf->b_u_seq_cur != save_b_u_seq_cur) { + // Undo invisibly. This also moves the cursor! + while (curbuf->b_u_seq_cur != save_b_u_seq_cur) { + if (!u_undo_and_forget(1)) { + abort(); + } + } + // Restore newhead. It is meaningless when curhead is valid, but we must + // restore it so that undotree() is identical before/after the preview. + curbuf->b_u_newhead = save_b_u_newhead; + curbuf->b_u_time_cur = save_b_u_time_cur; + } + if (save_changedtick != buf_get_changedtick(curbuf)) { + buf_set_changedtick(curbuf, save_changedtick); + } + + cmdmod = save_cmdmod; // Restore cmdmod + p_hls = save_hls; // Restore 'hlsearch' + curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline' + curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn' + curbuf->b_p_ul = save_b_p_ul; // Restore 'undolevels' + restore_search_patterns(); // Restore search patterns + win_size_restore(&save_view); // Restore window sizes + ga_clear(&save_view); + unblock_autocmds(); // Unblock events + msg_silent--; // Unblock messages + emsg_silent--; // Unblock error reporting + + // Restore the window "view". + curwin->w_cursor = s->is_state.save_cursor; + restore_viewstate(&s->is_state.old_viewstate); + update_topline(curwin); + + redrawcmdline(); +end: + xfree(cmdline); + return cmdpreview_type != 0; +} + static int command_line_changed(CommandLineState *s) { // Trigger CmdlineChanged autocommands. @@ -2305,8 +2553,7 @@ static int command_line_changed(CommandLineState *s) tv_dict_set_keys_readonly(dict); try_enter(&tstate); - apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf, - (char_u *)firstcbuf, false, curbuf); + apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf); restore_v_event(dict, &save_v_event); bool tl_ret = try_leave(&tstate, &err); @@ -2318,35 +2565,16 @@ static int command_line_changed(CommandLineState *s) } } - // 'incsearch' highlighting. if (s->firstc == ':' && current_sctx.sc_sid == 0 // only if interactive && *p_icm != NUL // 'inccommand' is set && curbuf->b_p_ma // buffer is modifiable && cmdline_star == 0 // not typing a password - && cmd_can_preview(ccline.cmdbuff) - && !vpeekc_any()) { - // Show 'inccommand' preview. It works like this: - // 1. Do the command. - // 2. Command implementation detects CMDPREVIEW state, then: - // - Update the screen while the effects are in place. - // - Immediately undo the effects. - State |= CMDPREVIEW; - emsg_silent++; // Block error reporting as the command may be incomplete - msg_silent++; // Block messages, namely ones that prompt - do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT|DOCMD_PREVIEW); - msg_silent--; // Unblock messages - emsg_silent--; // Unblock error reporting - - // Restore the window "view". - curwin->w_cursor = s->is_state.save_cursor; - restore_viewstate(&s->is_state.old_viewstate); - update_topline(curwin); - - redrawcmdline(); - } else if (State & CMDPREVIEW) { - State = (State & ~CMDPREVIEW); - close_preview_windows(); + && !vpeekc_any() + && cmdpreview_may_show(s)) { + // 'inccommand' preview has been shown. + } else if (cmdpreview) { + cmdpreview = false; update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) { @@ -2402,14 +2630,7 @@ static void abandon_cmdline(void) /// @param indent indent for inside conditionals char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED) { - // Be prepared for situations where cmdline can be invoked recursively. - // That includes cmd mappings, event handlers, as well as update_screen() - // (custom status line eval), which all may invoke ":normal :". - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - char_u *retval = command_line_enter(firstc, count, indent); - restore_cmdline(&save_ccline); - return retval; + return command_line_enter(firstc, count, indent, true); } /// Get a command line with a prompt @@ -2425,7 +2646,7 @@ char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_ /// @param[in] highlight_callback Callback used for highlighting user input. /// /// @return [allocated] Command line or NULL. -char *getcmdline_prompt(const char firstc, const char *const prompt, const int attr, +char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr, const int xp_context, const char *const xp_arg, const Callback highlight_callback) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC @@ -2433,8 +2654,14 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a const int msg_col_save = msg_col; CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - + bool did_save_ccline = false; + if (ccline.cmdbuff != NULL) { + // Save the values of the current cmdline and restore them below. + save_cmdline(&save_ccline); + did_save_ccline = true; + } else { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } ccline.prompt_id = last_prompt_id++; ccline.cmdprompt = (char_u *)prompt; ccline.cmdattr = attr; @@ -2446,9 +2673,11 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a int msg_silent_saved = msg_silent; msg_silent = 0; - char *const ret = (char *)command_line_enter(firstc, 1L, 0); + char *const ret = (char *)command_line_enter(firstc, 1L, 0, false); - restore_cmdline(&save_ccline); + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } msg_silent = msg_silent_saved; // Restore msg_col, the prompt from input() may have changed it. // But only if called recursively and the commandline is therefore being @@ -2461,14 +2690,18 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a return ret; } -/* - * Return TRUE when the text must not be changed and we can't switch to - * another window or buffer. Used when editing the command line etc. - */ -int text_locked(void) +// Return current cmdline prompt +char_u *get_cmdprompt(void) +{ + return ccline.cmdprompt; +} + +/// Return true when the text must not be changed and we can't switch to +/// another window or buffer. True when editing the command line etc. +bool text_locked(void) { if (cmdwin_type != 0) { - return TRUE; + return true; } return textlock != 0; } @@ -2487,8 +2720,19 @@ char *get_text_locked_msg(void) if (cmdwin_type != 0) { return e_cmdwin; } else { - return e_secure; + return e_textlock; + } +} + +/// Check for text, window or buffer locked. +/// Give an error message and return true if something is locked. +bool text_or_buf_locked(void) +{ + if (text_locked()) { + text_locked_msg(); + return true; } + return curbuf_locked(); } /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and @@ -2496,7 +2740,7 @@ char *get_text_locked_msg(void) bool curbuf_locked(void) { if (curbuf->b_ro_locked > 0) { - emsg(_("E788: Not allowed to edit another buffer now")); + emsg(_(e_cannot_edit_other_buf)); return true; } return allbuf_locked(); @@ -2518,7 +2762,7 @@ static int cmdline_charsize(int idx) if (cmdline_star > 0) { // showing '*', always 1 position return 1; } - return ptr2cells(ccline.cmdbuff + idx); + return ptr2cells((char *)ccline.cmdbuff + idx); } /// Compute the offset of the cursor on the command line for the prompt and @@ -2528,7 +2772,6 @@ static int cmd_startcol(void) return ccline.cmdindent + ((ccline.cmdfirstc != NUL) ? 1 : 0); } - /// Compute the column position for a byte position on the command line. static int cmd_screencol(int bytepos) { @@ -2545,7 +2788,7 @@ static int cmd_screencol(int bytepos) } for (int i = 0; i < ccline.cmdlen && i < bytepos; - i += utfc_ptr2len(ccline.cmdbuff + i)) { + i += utfc_ptr2len((char *)ccline.cmdbuff + i)) { int c = cmdline_charsize(i); // Count ">" for double-wide multi-byte char that doesn't fit. correct_screencol(i, c, &col); @@ -2564,8 +2807,8 @@ static 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(ccline.cmdbuff + idx) > 1 - && utf_ptr2cells(ccline.cmdbuff + idx) > 1 + if (utfc_ptr2len((char *)ccline.cmdbuff + idx) > 1 + && utf_ptr2cells((char *)ccline.cmdbuff + idx) > 1 && (*col) % Columns + cells > Columns) { (*col)++; } @@ -2575,24 +2818,25 @@ static void correct_screencol(int idx, int cells, int *col) /// /// @param c normally ':', NUL for ":append" /// @param indent indent for inside conditionals -char_u *getexline(int c, void *cookie, int indent, bool do_concat) +char *getexline(int c, void *cookie, int indent, bool do_concat) { // When executing a register, remove ':' that's in front of each line. if (exec_from_reg && vpeekc() == ':') { (void)vgetc(); } - return getcmdline(c, 1L, indent, do_concat); + return (char *)getcmdline(c, 1L, indent, do_concat); } bool cmdline_overstrike(void) + FUNC_ATTR_PURE { return ccline.overstrike; } - /// Return true if the cursor is at the end of the cmdline. bool cmdline_at_end(void) + FUNC_ATTR_PURE { return (ccline.cmdpos >= ccline.cmdlen); } @@ -2600,7 +2844,6 @@ bool cmdline_at_end(void) /* * Allocate a new command line buffer. * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. - * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen. */ static void alloc_cmdbuff(int len) { @@ -2638,17 +2881,17 @@ static 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)(ccline.xpc->xp_pattern - p); + int i = (int)((char_u *)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 = ccline.cmdbuff + i; + ccline.xpc->xp_pattern = (char *)ccline.cmdbuff + i; } } } -static char_u *arshape_buf = NULL; +static char *arshape_buf = NULL; #if defined(EXITFREE) void free_arshape_buf(void) @@ -2766,7 +3009,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) bool arg_allocated = false; typval_T arg = { .v_type = VAR_STRING, - .vval.v_string = colored_ccline->cmdbuff, + .vval.v_string = (char *)colored_ccline->cmdbuff, }; typval_T tv = { .v_type = VAR_UNKNOWN }; @@ -2927,7 +3170,7 @@ color_cmdline_end: // Note: errors “output” is cached just as well as regular results. ccline_colors->prompt_id = colored_ccline->prompt_id; if (arg_allocated) { - ccline_colors->cmdbuff = (char *)arg.vval.v_string; + ccline_colors->cmdbuff = arg.vval.v_string; } else { ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff, (size_t)colored_ccline->cmdlen); @@ -2969,7 +3212,7 @@ static void draw_cmdline(int start, int len) if (cmdline_star > 0) { for (int i = 0; i < len; i++) { msg_putchar('*'); - i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1; + i += utfc_ptr2len((char *)ccline.cmdbuff + start + i) - 1; } } else if (p_arshape && !p_tbidi && len > 0) { bool do_arabicshape = false; @@ -2979,7 +3222,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { do_arabicshape = true; break; } @@ -3002,7 +3245,7 @@ static void draw_cmdline(int start, int len) } int newlen = 0; - if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) { + if (utf_iscomposing(utf_ptr2char((char *)ccline.cmdbuff + start))) { // Prepend a space to draw the leading composing char on. arshape_buf[0] = ' '; newlen = 1; @@ -3015,7 +3258,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { int pc; int pc1 = 0; int nc = 0; @@ -3028,7 +3271,7 @@ static void draw_cmdline(int start, int len) if (i + mb_l >= start + len) { nc = NUL; } else { - nc = utf_ptr2char(p + mb_l); + nc = utf_ptr2char((char *)p + mb_l); } } else { // Displaying from left to right. @@ -3060,7 +3303,7 @@ static void draw_cmdline(int start, int len) } } - msg_outtrans_len(arshape_buf, newlen); + msg_outtrans_len((char_u *)arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: if (kv_size(ccline.last_colors.colors)) { @@ -3082,45 +3325,53 @@ draw_cmdline_no_arabicshape: static void ui_ext_cmdline_show(CmdlineInfo *line) { + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); Array content = ARRAY_DICT_INIT; if (cmdline_star) { + content = arena_array(&arena, 1); size_t len = 0; for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } - char *buf = xmallocz(len); + char *buf = arena_alloc(&arena, len, false); memset(buf, '*', len); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len))); + ADD_C(content, ARRAY_OBJ(item)); } else if (kv_size(line->last_colors.colors)) { + content = arena_array(&arena, kv_size(line->last_colors.colors)); for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(chunk.attr)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(chunk.attr)); assert(chunk.end >= chunk.start); - ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, - (size_t)(chunk.end-chunk.start)))); - ADD(content, ARRAY_OBJ(item)); + ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start, + (size_t)(chunk.end - chunk.start)))); + ADD_C(content, ARRAY_OBJ(item)); } } else { - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff)))); + content = arena_array(&arena, 1); + ADD_C(content, ARRAY_OBJ(item)); } + char charbuf[2] = { (char)line->cmdfirstc, 0 }; ui_call_cmdline_show(content, line->cmdpos, - cchar_to_string((char)line->cmdfirstc), - cstr_to_string((char *)(line->cmdprompt)), + cstr_as_string(charbuf), + cstr_as_string((char *)(line->cmdprompt)), line->cmdindent, line->level); if (line->special_char) { - ui_call_cmdline_special_char(cchar_to_string(line->special_char), + charbuf[0] = line->special_char; + ui_call_cmdline_special_char(cstr_as_string(charbuf), line->special_shift, line->level); } + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } void ui_ext_cmdline_block_append(size_t indent, const char *line) @@ -3136,9 +3387,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); if (cmdline_block.size > 1) { - ui_call_cmdline_block_append(copy_array(content)); + ui_call_cmdline_block_append(content); } else { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } } @@ -3158,10 +3409,10 @@ void cmdline_screen_cleared(void) } if (cmdline_block.size) { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } - int prev_level = ccline.level-1; + int prev_level = ccline.level - 1; CmdlineInfo *line = ccline.prev_ccline; while (prev_level > 0 && line) { if (line->level == prev_level) { @@ -3215,7 +3466,8 @@ void putcmdline(char c, int shift) } msg_no_more = false; } else if (ccline.redraw_state != kCmdRedrawAll) { - ui_call_cmdline_special_char(cchar_to_string(c), shift, + char charbuf[2] = { c, 0 }; + ui_call_cmdline_special_char(cstr_as_string(charbuf), shift, ccline.level); } cursorcmd(); @@ -3234,7 +3486,7 @@ void unputcmdline(void) if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) { msg_putchar(' '); } else { - draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos)); + draw_cmdline(ccline.cmdpos, utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos)); } msg_no_more = false; cursorcmd(); @@ -3270,13 +3522,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(str + i)) { + for (i = 0; i < len; i += utfc_ptr2len((char *)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(ccline.cmdbuff + i)) { + i += utfc_ptr2len((char *)ccline.cmdbuff + i)) { m--; } if (i < ccline.cmdlen) { @@ -3294,17 +3546,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(ccline.cmdbuff + ccline.cmdpos); + c = utf_ptr2char((char *)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(ccline.cmdbuff + ccline.cmdpos); + c = utf_ptr2char((char *)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(ccline.cmdbuff + ccline.cmdpos - i), c)) { + if (arabic_combine(utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos - i), c)) { ccline.cmdpos -= i; len += i; } else { @@ -3313,7 +3565,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) } if (i != 0) { // Also backup the cursor position. - i = ptr2cells(ccline.cmdbuff + ccline.cmdpos); + i = ptr2cells((char *)ccline.cmdbuff + ccline.cmdpos); ccline.cmdspos -= i; msg_col -= i; if (msg_col < 0) { @@ -3352,7 +3604,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) if (ccline.cmdspos + c < m) { ccline.cmdspos += c; } - c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; + c = utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1; if (c > len - i - 1) { c = len - i - 1; } @@ -3366,53 +3618,23 @@ void put_on_cmdline(char_u *str, int len, int redraw) } } -/* - * Save ccline, because obtaining the "=" register may execute "normal :cmd" - * and overwrite it. But get_cmdline_str() may need it, thus make it - * available globally in prev_ccline. - */ +/// Save ccline, because obtaining the "=" register may execute "normal :cmd" +/// and overwrite it. static void save_cmdline(struct cmdline_info *ccp) { *ccp = ccline; + memset(&ccline, 0, sizeof(struct cmdline_info)); ccline.prev_ccline = ccp; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - ccline.xpc = NULL; - ccline.special_char = NUL; - ccline.level = 0; + ccline.cmdbuff = NULL; // signal that ccline is not in use } -/* - * Restore ccline after it has been saved with save_cmdline(). - */ +/// Restore ccline after it has been saved with save_cmdline(). static void restore_cmdline(struct cmdline_info *ccp) FUNC_ATTR_NONNULL_ALL { ccline = *ccp; } -/* - * Save the command line into allocated memory. Returns a pointer to be - * passed to restore_cmdline_alloc() later. - */ -char_u *save_cmdline_alloc(void) - FUNC_ATTR_NONNULL_RET -{ - struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info)); - save_cmdline(p); - return (char_u *)p; -} - -/* - * Restore the command line from the return value of save_cmdline_alloc(). - */ -void restore_cmdline_alloc(char_u *p) - FUNC_ATTR_NONNULL_ALL -{ - restore_cmdline((struct cmdline_info *)p); - xfree(p); -} - /// Paste a yank register into the command line. /// Used by CTRL-R command in command-line mode. /// insert_reg() can't be used here, because special characters from the @@ -3428,7 +3650,6 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) char_u *arg; char_u *p; bool allocated; - struct cmdline_info save_ccline; // check for valid regname; also accept special characters for CTRL-R in // the command line @@ -3445,14 +3666,11 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) return FAIL; } - - // Need to save and restore ccline. And set "textlock" to avoid nasty - // things like going to another buffer when evaluating an expression. - save_cmdline(&save_ccline); + // Need to set "textlock" to avoid nasty things like going to another + // buffer when evaluating an expression. textlock++; const bool i = get_spec_reg(regname, &arg, &allocated, true); textlock--; - restore_cmdline(&save_ccline); if (i) { // Got the value of a special register in "arg". @@ -3470,7 +3688,7 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) // 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(w - len))) { + if (!vim_iswordc(utf_ptr2char((char *)w - len))) { break; } w -= len; @@ -3603,7 +3821,7 @@ void redrawcmd(void) msg_no_more = TRUE; draw_cmdline(0, ccline.cmdlen); msg_clr_eos(); - msg_no_more = FALSE; + msg_no_more = false; ccline.cmdspos = cmd_screencol(ccline.cmdpos); @@ -3631,7 +3849,7 @@ void compute_cmdrow(void) } else { win_T *wp = lastwin_nofloating(); cmdline_row = wp->w_winrow + wp->w_height - + wp->w_status_height; + + wp->w_hsep_height + wp->w_status_height + global_stl_height(); } lines_left = cmdline_row; } @@ -3651,7 +3869,7 @@ static void cursorcmd(void) } if (cmdmsg_rl) { - msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1)); + msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1)); msg_col = Columns - (ccline.cmdspos % (Columns - 1)) - 1; if (msg_row <= 0) { msg_row = Rows - 1; @@ -3670,7 +3888,7 @@ static void cursorcmd(void) static void cmd_cursor_goto(int row, int col) { ScreenGrid *grid = &msg_grid_adj; - screen_adjust_grid(&grid, &row, &col); + grid_adjust(&grid, &row, &col); ui_grid_cursor_goto(grid->handle, row, col); } @@ -3770,7 +3988,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape) ui_flush(); } - i = (int)(xp->xp_pattern - ccline.cmdbuff); + i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff); assert(ccline.cmdpos >= i); xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i; @@ -3779,7 +3997,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape) p2 = ExpandOne(xp, NULL, NULL, 0, type); } else { // Translate string into pattern and expand it. - p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); + p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); const int use_options = ( options | WILD_HOME_REPLACE @@ -3793,7 +4011,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape) // xp->xp_pattern might have been modified by ExpandOne (for example, // in lua completion), so recompute the pattern index and length - i = (int)(xp->xp_pattern - ccline.cmdbuff); + i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff); xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i; // Longest match: make sure it is not shorter, happens with :help. @@ -3814,7 +4032,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape) difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len); if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) { realloc_cmdbuff(ccline.cmdlen + difflen + 4); - xp->xp_pattern = ccline.cmdbuff + i; + xp->xp_pattern = (char *)ccline.cmdbuff + i; } assert(ccline.cmdpos <= ccline.cmdlen); memmove(&ccline.cmdbuff[ccline.cmdpos + difflen], @@ -3924,13 +4142,13 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode compl_selected = findex; cmdline_pum_display(false); } else if (p_wmnu) { - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, + win_redr_status_matches(xp, xp->xp_numfiles, (char_u **)xp->xp_files, findex, cmd_showtail); } if (findex == -1) { return vim_strsave(orig_save); } - return vim_strsave(xp->xp_files[findex]); + return vim_strsave((char_u *)xp->xp_files[findex]); } else { return NULL; } @@ -3940,12 +4158,12 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode ss = vim_strsave(orig_save ? orig_save : (char_u *)""); } else if (mode == WILD_APPLY) { ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"") : - xp->xp_files[findex]); + (char_u *)xp->xp_files[findex]); } // free old names if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { - FreeWild(xp->xp_numfiles, xp->xp_files); + FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); xp->xp_numfiles = -1; XFREE_CLEAR(orig_save); } @@ -3963,7 +4181,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode /* * Do the expansion. */ - if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, + if (ExpandFromContext(xp, str, &xp->xp_numfiles, (char_u ***)&xp->xp_files, options) == FAIL) { #ifdef FNAME_ILLEGAL /* Illegal file name has been silently skipped. But when there @@ -3980,7 +4198,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode } } else { // Escape the matches for use on the command line. - ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); + ExpandEscape(xp, str, xp->xp_numfiles, (char_u **)xp->xp_files, options); /* * Check for matching suffixes in file names. @@ -4001,9 +4219,9 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode * expand_wildcards, only need to check the first two. */ non_suf_match = 0; - for (i = 0; i < 2; ++i) { - if (match_suffix(xp->xp_files[i])) { - ++non_suf_match; + for (i = 0; i < 2; i++) { + if (match_suffix((char_u *)xp->xp_files[i])) { + non_suf_match++; } } } @@ -4020,7 +4238,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode } } if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) { - ss = vim_strsave(xp->xp_files[0]); + ss = vim_strsave((char_u *)xp->xp_files[0]); } } } @@ -4055,7 +4273,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode } } - ss = (char_u *)xstrndup((char *)xp->xp_files[0], len); + ss = (char_u *)xstrndup(xp->xp_files[0], len); findex = -1; // next p_wc gets first one } @@ -4105,7 +4323,7 @@ void ExpandInit(expand_T *xp) void ExpandCleanup(expand_T *xp) { if (xp->xp_numfiles >= 0) { - FreeWild(xp->xp_numfiles, xp->xp_files); + FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files); xp->xp_numfiles = -1; } } @@ -4114,6 +4332,7 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o { int i; char_u *p; + const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE; /* * May change home directory back to "~" @@ -4145,10 +4364,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o #endif } #ifdef BACKSLASH_IN_FILENAME - p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what); #else p = (char_u *)vim_strsave_fnameescape((const char *)files[i], - xp->xp_shell); + xp->xp_shell ? VSE_SHELL : vse_what); #endif xfree(files[i]); files[i] = p; @@ -4180,25 +4399,30 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o } } -/// Escape special characters in a file name for use as a command argument +/// Escape special characters in "fname", depending on "what": /// /// @param[in] fname File name to escape. -/// @param[in] shell What to escape for: if false, escapes for VimL command, -/// if true then it escapes for a shell command. +/// @param[in] what What to escape for: +/// - VSE_NONE: for when used as a file name argument after a Vim command. +/// - VSE_SHELL: for a shell command. +/// - VSE_BUFFER: for the ":buffer" command. /// /// @return [allocated] escaped file name. -char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATTR_UNUSED) +char *vim_strsave_fnameescape(const char *const fname, const int what) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { #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)]; int j = 0; - // Don't escape '[', '{' and '!' if they are in 'isfname'. - for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) { - if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) { - buf[j++] = *s; + // 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)) { + buf[j++] = *p; } } buf[j] = NUL; @@ -4207,9 +4431,12 @@ char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATT #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, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); - if (shell && csh_like_shell()) { + (char *)vim_strsave_escaped((const char_u *)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, @@ -4246,14 +4473,13 @@ static void escape_fname(char_u **pp) */ void tilde_replace(char_u *orig_pat, int num_files, char_u **files) { - int i; - char_u *p; + char *p; if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) { - for (i = 0; i < num_files; ++i) { - p = home_replace_save(NULL, files[i]); + for (int i = 0; i < num_files; i++) { + p = home_replace_save(NULL, (char *)files[i]); xfree(files[i]); - files[i] = p; + files[i] = (char_u *)p; } } } @@ -4294,7 +4520,7 @@ static int showmatches(expand_T *xp, int wildmenu) } } else { num_files = xp->xp_numfiles; - files_found = xp->xp_files; + files_found = (char_u **)xp->xp_files; showtail = cmd_showtail; } @@ -4312,7 +4538,7 @@ static int showmatches(expand_T *xp, int wildmenu) compl_match_array[i].pum_text = L_SHOWFILE(i); } char_u *endpos = (showtail - ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern); + ? sm_gettail((char_u *)xp->xp_pattern, true) : (char_u *)xp->xp_pattern); if (ui_has(kUICmdline)) { compl_startcol = (int)(endpos - ccline.cmdbuff); } else { @@ -4344,10 +4570,10 @@ static int showmatches(expand_T *xp, int wildmenu) if (!showtail && (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE); - j = vim_strsize(NameBuff); + home_replace(NULL, (char *)files_found[i], (char *)NameBuff, MAXPATHL, true); + j = vim_strsize((char *)NameBuff); } else { - j = vim_strsize(L_SHOWFILE(i)); + j = vim_strsize((char *)L_SHOWFILE(i)); } if (j > maxlen) { maxlen = j; @@ -4414,8 +4640,7 @@ static int showmatches(expand_T *xp, int wildmenu) if (showtail) { p = L_SHOWFILE(k); } else { - home_replace(NULL, files_found[k], NameBuff, MAXPATHL, - TRUE); + home_replace(NULL, (char *)files_found[k], (char *)NameBuff, MAXPATHL, true); p = NameBuff; } } else { @@ -4466,7 +4691,7 @@ char_u *sm_gettail(char_u *s, bool eager) #endif ) { if (eager) { - t = p+1; + t = p + 1; } else { had_sep = true; } @@ -4496,18 +4721,18 @@ static int expand_showtail(expand_T *xp) return FALSE; } - end = path_tail(xp->xp_pattern); - if (end == xp->xp_pattern) { // there is no path separator - return FALSE; + end = (char_u *)path_tail(xp->xp_pattern); + if (end == (char_u *)xp->xp_pattern) { // there is no path separator + return false; } - for (s = xp->xp_pattern; s < end; s++) { - /* Skip escaped wildcards. Only when the backslash is not a path - * separator, on DOS the '*' "path\*\file" must not be skipped. */ + for (s = (char_u *)xp->xp_pattern; s < end; s++) { + // Skip escaped wildcards. Only when the backslash is not a path + // separator, on DOS the '*' "path\*\file" must not be skipped. if (rem_backslash(s)) { - ++s; - } else if (vim_strchr((char_u *)"*?[", *s) != NULL) { - return FALSE; + s++; + } else if (vim_strchr("*?[", *s) != NULL) { + return false; } } return TRUE; @@ -4623,7 +4848,7 @@ char_u *addstar(char_u *fname, size_t len, int context) * ` could be anywhere in the file name. * When the name ends in '$' don't add a star, remove the '$'. */ - tail = path_tail(retval); + tail = (char_u *)path_tail((char *)retval); ends_in_star = (len > 0 && retval[len - 1] == '*'); #ifndef BACKSLASH_IN_FILENAME for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) { @@ -4635,8 +4860,8 @@ char_u *addstar(char_u *fname, size_t len, int context) #endif if ((*retval != '~' || tail != retval) && !ends_in_star - && vim_strchr(tail, '$') == NULL - && vim_strchr(retval, '`') == NULL) { + && vim_strchr((char *)tail, '$') == NULL + && vim_strchr((char *)retval, '`') == NULL) { retval[len++] = '*'; } else if (len > 0 && retval[len - 1] == '$') { --len; @@ -4689,7 +4914,7 @@ char_u *addstar(char_u *fname, size_t len, int context) * EXPAND_ENV_VARS Complete environment variable names * EXPAND_USER Complete user names */ -static void set_expand_context(expand_T *xp) +void set_expand_context(expand_T *xp) { // only expansion for ':', '>' and '=' command-lines if (ccline.cmdfirstc != ':' @@ -4721,11 +4946,11 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline if (use_ccline && ccline.cmdfirstc == '=') { // pass CMD_SIZE because there is no real command - set_context_for_expression(xp, str, CMD_SIZE); + set_context_for_expression(xp, (char *)str, CMD_SIZE); } else if (use_ccline && ccline.input_fn) { xp->xp_context = ccline.xp_context; - xp->xp_pattern = ccline.cmdbuff; - xp->xp_arg = ccline.xp_arg; + xp->xp_pattern = (char *)ccline.cmdbuff; + xp->xp_arg = (char *)ccline.xp_arg; } else { while (nextcomm != NULL) { nextcomm = set_one_cmd_context(xp, nextcomm); @@ -4734,7 +4959,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline /* Store the string here so that call_user_expand_func() can get to them * easily. */ - xp->xp_line = str; + xp->xp_line = (char *)str; xp->xp_col = col; str[col] = old_char; @@ -4769,9 +4994,9 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u * } // add star to file name, or convert to regexp if not exp. files. - assert((str + col) - xp->xp_pattern >= 0); - xp->xp_pattern_len = (size_t)((str + col) - xp->xp_pattern); - file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); + assert((str + col) - (char_u *)xp->xp_pattern >= 0); + xp->xp_pattern_len = (size_t)((str + col) - (char_u *)xp->xp_pattern); + file_str = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); if (p_wic) { options += WILD_ICASE; @@ -4839,7 +5064,7 @@ static void cleanup_help_tags(int num_file, char_u **file) } } -typedef char_u *(*ExpandFunc)(expand_T *, int); +typedef char *(*ExpandFunc)(expand_T *, int); /// Do the expansion based on xp->xp_context and "pat". /// @@ -4938,8 +5163,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** if (xp->xp_context == EXPAND_HELP) { /* With an empty argument we would get all the help tags, which is * very slow. Get matches for "help" instead. */ - if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat, - num_file, file, false) == OK) { + if (find_help_tags(*pat == NUL ? "help" : (char *)pat, + num_file, (char ***)file, false) == OK) { cleanup_help_tags(*num_file, *file); return OK; } @@ -4956,10 +5181,10 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** return OK; } if (xp->xp_context == EXPAND_BUFFERS) { - return ExpandBufnames(pat, num_file, file, options); + return ExpandBufnames((char *)pat, num_file, (char ***)file, options); } if (xp->xp_context == EXPAND_DIFF_BUFFERS) { - return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER); + return ExpandBufnames((char *)pat, num_file, (char ***)file, options | BUF_DIFF_FILTER); } if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) { @@ -5008,7 +5233,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** return nlua_expand_pat(xp, pat, num_file, file); } - regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { return FAIL; } @@ -5049,8 +5274,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** { EXPAND_SYNTAX, get_syntax_name, true, true }, { EXPAND_SYNTIME, get_syntime_arg, true, true }, { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true }, - { EXPAND_EVENTS, get_event_name, true, true }, - { EXPAND_AUGROUP, get_augroup_name, true, true }, + { EXPAND_EVENTS, expand_get_event_name, true, true }, + { EXPAND_AUGROUP, expand_get_augroup_name, true, true }, { EXPAND_CSCOPE, get_cscope_name, true, true }, { EXPAND_SIGN, get_sign_name, true, true }, { EXPAND_PROFILE, get_profile_name, true, true }, @@ -5104,16 +5329,16 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha char_u *str; // count the number of matching names - for (i = 0;; ++i) { - str = (*func)(xp, i); + for (i = 0;; i++) { + str = (char_u *)(*func)(xp, i); if (str == NULL) { // end of list break; } if (*str == NUL) { // skip empty strings continue; } - if (vim_regexec(regmatch, str, (colnr_T)0)) { - ++count; + if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) { + count++; } } if (count == 0) { @@ -5126,14 +5351,14 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha // copy the matching names into allocated memory count = 0; for (i = 0;; i++) { - str = (*func)(xp, i); + str = (char_u *)(*func)(xp, i); if (str == NULL) { // End of list. break; } if (*str == NUL) { // Skip empty strings. continue; } - if (vim_regexec(regmatch, str, (colnr_T)0)) { + if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) { if (escaped) { str = vim_strsave_escaped(str, (char_u *)" \t\\."); } else { @@ -5225,7 +5450,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int hashtab_T found_ht; hash_init(&found_ht); for (s = path;; s = e) { - e = vim_strchr(s, ENV_SEPCHAR); + e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR); if (e == NULL) { e = s + STRLEN(s); } @@ -5306,7 +5531,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T typval_T args[4]; char_u *pat = NULL; const sctx_T save_current_sctx = current_sctx; - struct cmdline_info save_ccline; if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) { return NULL; @@ -5319,24 +5543,19 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T ccline.cmdbuff[ccline.cmdlen] = 0; } - pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len); + pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len); args[0].v_type = VAR_STRING; args[1].v_type = VAR_STRING; args[2].v_type = VAR_NUMBER; args[3].v_type = VAR_UNKNOWN; - args[0].vval.v_string = pat; + args[0].vval.v_string = (char *)pat; args[1].vval.v_string = xp->xp_line; args[2].vval.v_number = xp->xp_col; - // Save the cmdline, we don't know what the function may do. - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; current_sctx = xp->xp_script_ctx; - void *const ret = user_expand_func(xp->xp_arg, 3, args); + void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args); - ccline = save_ccline; current_sctx = save_current_sctx; if (ccline.cmdbuff != NULL) { ccline.cmdbuff[ccline.cmdlen] = keep; @@ -5363,7 +5582,7 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, ga_init(&ga, (int)sizeof(char *), 3); for (char_u *s = retstr; *s != NUL; s = e) { - e = vim_strchr(s, '\n'); + e = (char_u *)vim_strchr((char *)s, '\n'); if (e == NULL) { e = s + STRLEN(s); } @@ -5371,7 +5590,7 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, *e = NUL; const bool skip = xp->xp_pattern[0] - && vim_regexec(regmatch, s, (colnr_T)0) == 0; + && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0; *e = keep; if (!skip) { GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s))); @@ -5582,8 +5801,8 @@ static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file) for (int i = 0; i < ga.ga_len; i++) { char_u *match = ((char_u **)ga.ga_data)[i]; - s = path_tail(match); - memmove(match, s, STRLEN(s)+1); + s = (char_u *)path_tail((char *)match); + memmove(match, s, STRLEN(s) + 1); } if (GA_EMPTY(&ga)) { @@ -5599,7 +5818,6 @@ static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file) return OK; } - /// Expand `file` for all comma-separated directories in `path`. /// Adds matches to `ga`. void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) @@ -5613,7 +5831,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) // Loop over all entries in {path}. while (*path != NUL) { // Copy one item of the path to buf[] and concatenate the file name. - copy_option_part(&path, buf, MAXPATHL, ","); + copy_option_part((char **)&path, (char *)buf, MAXPATHL, ","); if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) { add_pathsep((char *)buf); STRCAT(buf, file); // NOLINT @@ -5640,7 +5858,6 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) xfree(buf); } - /********************************* * Command line history stuff * *********************************/ @@ -5689,7 +5906,7 @@ static char *(history_names[]) = * Function given to ExpandGeneric() to obtain the possible first * arguments of the ":history command. */ -static char_u *get_history_arg(expand_T *xp, int idx) +static char *get_history_arg(expand_T *xp, int idx) { static char_u compl[2] = { NUL, NUL }; char *short_names = ":=@>?/"; @@ -5698,13 +5915,13 @@ static char_u *get_history_arg(expand_T *xp, int idx) if (idx < short_names_count) { compl[0] = (char_u)short_names[idx]; - return compl; + return (char *)compl; } if (idx < short_names_count + history_name_count) { - return (char_u *)history_names[idx - short_names_count]; + return history_names[idx - short_names_count]; } if (idx == short_names_count + history_name_count) { - return (char_u *)"all"; + return "all"; } return NULL; } @@ -5864,7 +6081,7 @@ HistoryType get_histtype(const char *const name, const size_t len, const bool re } } - if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) { + if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) { return hist_char2type(name[0]); } @@ -5874,7 +6091,7 @@ HistoryType get_histtype(const char *const name, const size_t len, const bool re static int last_maptick = -1; // last seen maptick /// Add the given string to the given history. If the string is already in the -/// history then it is moved to the front. "histype" may be one of he HIST_ +/// history then it is moved to the front. "histype" may be one of the HIST_ /// values. /// /// @parma in_map consider maptick when inside a mapping @@ -5888,7 +6105,7 @@ void add_to_history(int histype, char_u *new_entry, int in_map, int sep) } assert(histype != HIST_DEFAULT); - if (cmdmod.keeppatterns && histype == HIST_SEARCH) { + if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) { return; } @@ -5930,7 +6147,6 @@ void add_to_history(int histype, char_u *new_entry, int in_map, int sep) } } - /* * Get identifier of newest history entry. * "histype" may be one of the HIST_ values. @@ -5945,14 +6161,11 @@ int get_history_idx(int histype) return history[histype][hisidx[histype]].hisnum; } - -/* - * Get pointer to the command line info to use. cmdline_paste() may clear - * ccline and put the previous value in prev_ccline. - */ +/// Get pointer to the command line info to use. save_cmdline() may clear +/// ccline and put the previous value in ccline.prev_ccline. static struct cmdline_info *get_ccline_ptr(void) { - if ((State & CMDLINE) == 0) { + if ((State & MODE_CMDLINE) == 0) { return NULL; } else if (ccline.cmdbuff != NULL) { return &ccline; @@ -5963,6 +6176,25 @@ static struct cmdline_info *get_ccline_ptr(void) } } +/// Get the current command-line completion type. +char_u *get_cmdline_completion(void) +{ + if (cmdline_star > 0) { + return NULL; + } + struct cmdline_info *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); + } + } + + return NULL; +} + /* * Get the current command line in allocated memory. * Only works when the command line is being edited. @@ -5997,6 +6229,17 @@ int get_cmdline_pos(void) return p->cmdpos; } +/// Get the command line cursor screen position. +int get_cmdline_screen_pos(void) +{ + struct cmdline_info *p = get_ccline_ptr(); + + if (p == NULL) { + return -1; + } + return p->cmdspos; +} + /* * Set the command line byte position to "pos". Zero is the first position. * Only works when the command line is being edited. @@ -6067,7 +6310,7 @@ static int calc_hist_idx(int histype, int num) wrapped = TRUE; } } - if (hist[i].hisnum == num && hist[i].hisstr != NULL) { + if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) { return i; } } else if (-num <= hislen) { @@ -6136,7 +6379,7 @@ int del_history_entry(int histype, char_u *str) && histype < HIST_COUNT && *str != NUL && (idx = hisidx[histype]) >= 0 - && (regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING)) + && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING)) != NULL) { i = last = idx; do { @@ -6144,7 +6387,7 @@ int del_history_entry(int histype, char_u *str) if (hisptr->hisstr == NULL) { break; } - if (vim_regexec(®match, hisptr->hisstr, (colnr_T)0)) { + if (vim_regexec(®match, (char *)hisptr->hisstr, (colnr_T)0)) { found = true; hist_free_entry(hisptr); } else { @@ -6217,20 +6460,20 @@ int get_list_range(char_u **str, int *num1, int *num2) int first = false; varnumber_T num; - *str = skipwhite(*str); + *str = (char_u *)skipwhite((char *)(*str)); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); *str += len; *num1 = (int)num; first = true; } - *str = skipwhite(*str); + *str = (char_u *)skipwhite((char *)(*str)); if (**str == ',') { // parse "to" part of range - *str = skipwhite(*str + 1); + *str = (char_u *)skipwhite((char *)(*str) + 1); vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); if (len > 0) { *num2 = (int)num; - *str = skipwhite(*str + len); + *str = (char_u *)skipwhite((char *)(*str) + len); } else if (!first) { // no number given at all return FAIL; } @@ -6253,7 +6496,7 @@ void ex_history(exarg_T *eap) int idx; int i, j, k; char_u *end; - char_u *arg = eap->arg; + char_u *arg = (char_u *)eap->arg; if (hislen == 0) { msg(_("'history' option is zero")); @@ -6263,14 +6506,14 @@ void ex_history(exarg_T *eap) if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) { end = arg; while (ASCII_ISALPHA(*end) - || vim_strchr((char_u *)":=@>/?", *end) != NULL) { + || vim_strchr(":=@>/?", *end) != NULL) { end++; } histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false); if (histype1 == HIST_INVALID) { if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; - histype2 = HIST_COUNT-1; + histype2 = HIST_COUNT - 1; } else { emsg(_(e_trailing)); return; @@ -6296,10 +6539,10 @@ void ex_history(exarg_T *eap) j = hisidx1; k = hisidx2; if (j < 0) { - j = (-j > hislen) ? 0 : hist[(hislen+j+idx+1) % hislen].hisnum; + j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum; } if (k < 0) { - k = (-k > hislen) ? 0 : hist[(hislen+k+idx+1) % hislen].hisnum; + k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum; } if (idx >= 0 && j <= k) { for (i = idx + 1; !got_int; ++i) { @@ -6311,13 +6554,13 @@ void ex_history(exarg_T *eap) msg_putchar('\n'); snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ', hist[i].hisnum); - if (vim_strsize(hist[i].hisstr) > Columns - 10) { - trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff), + if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) { + trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff), Columns - 10, IOSIZE - (int)STRLEN(IObuff)); } else { STRCAT(IObuff, hist[i].hisstr); } - msg_outtrans(IObuff); + msg_outtrans((char *)IObuff); ui_flush(); } if (i == idx) { @@ -6349,6 +6592,11 @@ int hist_type2char(int type) return NUL; } +void cmdline_init(void) +{ + memset(&ccline, 0, sizeof(struct cmdline_info)); +} + /// Open a window on the current command line and history. Allow editing in /// the window. Returns when the window is closed. /// Returns: @@ -6357,7 +6605,6 @@ int hist_type2char(int type) /// K_IGNORE if editing continues static int open_cmdwin(void) { - struct cmdline_info save_ccline; bufref_T old_curbuf; bufref_T bufref; win_T *old_curwin = curwin; @@ -6365,15 +6612,15 @@ static int open_cmdwin(void) int i; linenr_T lnum; garray_T winsizes; - char_u typestr[2]; + char typestr[2]; int save_restart_edit = restart_edit; int save_State = State; bool save_exmode = exmode_active; int save_cmdmsg_rl = cmdmsg_rl; + // Can't do this when text or buffer is locked. // Can't do this recursively. Can't do it when typing a password. - if (cmdwin_type != 0 - || cmdline_star > 0) { + if (text_or_buf_locked() || cmdwin_type != 0 || cmdline_star > 0) { beep_flush(); return K_IGNORE; } @@ -6388,12 +6635,13 @@ static int open_cmdwin(void) pum_undisplay(true); // don't use a new tab page - cmdmod.tab = 0; - cmdmod.noswapfile = 1; + cmdmod.cmod_tab = 0; + cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Create a window for the command-line buffer. if (win_split((int)p_cwh, WSP_BOT) == FAIL) { beep_flush(); + ga_clear(&winsizes); return K_IGNORE; } cmdwin_type = get_cmdline_type(); @@ -6417,8 +6665,8 @@ static int open_cmdwin(void) const int histtype = hist_char2type(cmdwin_type); if (histtype == HIST_CMD || histtype == HIST_DEBUG) { if (p_wc == TAB) { - add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT, false); - add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL, false); + 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); } @@ -6439,7 +6687,7 @@ static int open_cmdwin(void) i = 0; } if (history[histtype][i].hisstr != NULL) { - ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false); + ml_append(lnum++, (char *)history[histtype][i].hisstr, (colnr_T)0, false); } } while (i != hisidx[histtype]); } @@ -6447,7 +6695,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, ccline.cmdbuff, true); + ml_replace(curbuf->b_ml.ml_line_count, (char *)ccline.cmdbuff, true); curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_cursor.col = ccline.cmdpos; changed_line_abv_curs(); @@ -6458,20 +6706,17 @@ static int open_cmdwin(void) } redraw_later(curwin, SOME_VALID); - // Save the command line info, can be used recursively. - save_cmdline(&save_ccline); - // No Ex mode here! exmode_active = false; - State = NORMAL; + State = MODE_NORMAL; setmouse(); // Reset here so it can be set by a CmdWinEnter autocommand. cmdwin_result = 0; // Trigger CmdwinEnter autocommands. - typestr[0] = (char_u)cmdwin_type; + typestr[0] = (char)cmdwin_type; typestr[1] = NUL; apply_autocmds(EVENT_CMDWINENTER, typestr, typestr, false, curbuf); if (restart_edit != 0) { // autocmd with ":startinsert" @@ -6498,8 +6743,6 @@ static int open_cmdwin(void) // Restore KeyTyped in case it is modified by autocommands KeyTyped = save_KeyTyped; - // Restore the command line info. - restore_cmdline(&save_ccline); cmdwin_type = 0; cmdwin_level = 0; @@ -6563,13 +6806,13 @@ static int open_cmdwin(void) set_bufref(&bufref, curbuf); win_goto(old_curwin); if (win_valid(wp) && wp != curwin) { - win_close(wp, true); + win_close(wp, true, false); } // win_close() may have already wiped the buffer when 'bh' is // set to 'wipe', autocommands may have closed other windows if (bufref_valid(&bufref) && bufref.br_buf != curbuf) { - close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false); + close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false); } // Restore window sizes. @@ -6581,12 +6824,19 @@ static int open_cmdwin(void) cmdmsg_rl = save_cmdmsg_rl; State = save_State; - trigger_modechanged(); + may_trigger_modechanged(); setmouse(); return cmdwin_result; } +/// @return true if in the cmdwin, not editing the command line. +bool is_in_cmdwin(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return cmdwin_type != 0 && get_cmdline_type() == NUL; +} + /// Get script string /// /// Used for commands which accept either `:command script` or @@ -6616,13 +6866,10 @@ char *script_get(exarg_T *const eap, size_t *const lenp) ga_init(&ga, 1, 0x400); } - const char *const end_pattern = ( - cmd[2] != NUL - ? (const char *)skipwhite((const char_u *)cmd + 2) - : "."); + const char *const end_pattern = (cmd[2] != NUL ? (const char *)skipwhite(cmd + 2) : "."); for (;;) { - char *const theline = (char *)eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : - NUL, eap->cookie, 0, true); + char *const theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL, eap->cookie, 0, + true); if (theline == NULL || strcmp(end_pattern, theline) == 0) { xfree(theline); |