diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
commit | 9be89f131f87608f224f0ee06d199fcd09d32176 (patch) | |
tree | 11022dcfa9e08cb4ac5581b16734196128688d48 /src/nvim/ex_getln.c | |
parent | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (diff) | |
parent | 88085c2e80a7e3ac29aabb6b5420377eed99b8b6 (diff) | |
download | rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.gz rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.bz2 rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.zip |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/ex_getln.c')
-rw-r--r-- | src/nvim/ex_getln.c | 277 |
1 files changed, 209 insertions, 68 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f18dc0f747..7d87e609ca 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -28,6 +28,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" @@ -132,7 +133,8 @@ typedef struct { int did_wild_list; // did wild_list() recently int wim_index; // index in wim_flags[] int save_msg_scroll; - int save_State; // remember State when called + int save_State; // remember State when called + int prev_cmdpos; char *save_p_icm; int some_key_typed; // one of the keys was typed // mouse drag and release events are ignored, unless they are @@ -222,6 +224,12 @@ static int cmdpreview_ns = 0; static const char e_active_window_or_buffer_changed_or_deleted[] = N_("E199: Active window or buffer changed or deleted"); +static void trigger_cmd_autocmd(int typechar, event_T evt) +{ + char typestr[2] = { (char)typechar, NUL }; + apply_autocmds(evt, typestr, typestr, false, curbuf); +} + static void save_viewstate(win_T *wp, viewstate_T *vs) FUNC_ATTR_NONNULL_ALL { @@ -259,6 +267,18 @@ static void init_incsearch_state(incsearch_state_T *s) save_viewstate(curwin, &s->old_viewstate); } +static void set_search_match(pos_T *t) +{ + // First move cursor to end of match, then to the start. This + // moves the whole match onto the screen when 'nowrap' is set. + t->lnum += search_match_lines; + t->col = search_match_endcol; + if (t->lnum > curbuf->b_ml.ml_line_count) { + t->lnum = curbuf->b_ml.ml_line_count; + coladvance(curwin, MAXCOL); + } +} + // Return true when 'incsearch' highlighting is to be done. // Sets search_first_line and search_last_line to the address range. static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *s, @@ -382,13 +402,8 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s parse_cmd_address(&ea, &dummy, true); if (ea.addr_count > 0) { // Allow for reverse match. - if (ea.line2 < ea.line1) { - search_first_line = ea.line2; - search_last_line = ea.line1; - } else { - search_first_line = ea.line1; - search_last_line = ea.line2; - } + search_first_line = MIN(ea.line1, ea.line1); + search_last_line = MAX(ea.line2, ea.line1); } else if (cmd[0] == 's' && cmd[1] != 'o') { // :s defaults to the current line search_first_line = curwin->w_cursor.lnum; @@ -683,6 +698,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear .indent = indent, .save_msg_scroll = msg_scroll, .save_State = State, + .prev_cmdpos = -1, .ignore_drag_release = true, }; CommandLineState *s = &state; @@ -1025,11 +1041,7 @@ static int command_line_handle_ctrl_bsl(CommandLineState *s) // 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; - } + ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos); KeyTyped = false; // Don't do p_wc completion. redrawcmd(); @@ -1649,11 +1661,7 @@ static int command_line_insert_reg(CommandLineState *s) 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; - } + ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos); } } new_cmdpos = save_new_cmdpos; @@ -2110,7 +2118,7 @@ static int command_line_handle_key(CommandLineState *s) s->do_abbr = false; // don't do abbreviation now ccline.special_char = NUL; // may need to remove ^ when composing char was typed - if (utf_iscomposing(s->c) && !cmd_silent) { + if (utf_iscomposing_first(s->c) && !cmd_silent) { if (ui_has(kUICmdline)) { // TODO(bfredl): why not make unputcmdline also work with true? unputcmdline(); @@ -2174,6 +2182,12 @@ static int command_line_handle_key(CommandLineState *s) static int command_line_not_changed(CommandLineState *s) { + // Trigger CursorMovedC autocommands. + if (ccline.cmdpos != s->prev_cmdpos) { + trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC); + s->prev_cmdpos = ccline.cmdpos; + } + // Incremental searches for "/" and "?": // Enter command_line_not_changed() when a character has been read but the // command line did not change. Then we only search and redraw if something @@ -2532,6 +2546,10 @@ static bool cmdpreview_may_show(CommandLineState *s) goto end; } + // Flush now: external cmdline may itself wish to update the screen which is + // currently disallowed during cmdpreview(no longer needed in case that changes). + cmdline_ui_flush(); + // Swap invalid command range if needed if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) { linenr_T lnum = ea.line1; @@ -2647,6 +2665,12 @@ static int command_line_changed(CommandLineState *s) // Trigger CmdlineChanged autocommands. do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-'); + // Trigger CursorMovedC autocommands. + if (ccline.cmdpos != s->prev_cmdpos) { + trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC); + s->prev_cmdpos = ccline.cmdpos; + } + const bool prev_cmdpreview = cmdpreview; if (s->firstc == ':' && current_sctx.sc_sid == 0 // only if interactive @@ -3004,7 +3028,6 @@ void realloc_cmdbuff(int len) // there, thus copy up to the NUL and add a NUL. memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen); ccline.cmdbuff[ccline.cmdlen] = NUL; - xfree(p); if (ccline.xpc != NULL && ccline.xpc->xp_pattern != NULL @@ -3018,6 +3041,8 @@ void realloc_cmdbuff(int len) ccline.xpc->xp_pattern = ccline.cmdbuff + i; } } + + xfree(p); } enum { MAX_CB_ERRORS = 1, }; @@ -3450,11 +3475,9 @@ void cmdline_screen_cleared(void) /// called by ui_flush, do what redraws necessary to keep cmdline updated. void cmdline_ui_flush(void) { - static bool flushing = false; - if (!ui_has(kUICmdline) || flushing) { + if (!ui_has(kUICmdline)) { return; } - flushing = true; int level = ccline.level; CmdlineInfo *line = &ccline; while (level > 0 && line) { @@ -3469,7 +3492,6 @@ void cmdline_ui_flush(void) } line = line->prev_ccline; } - flushing = false; } // Put a character on the command line. Shifts the following text to the @@ -3524,10 +3546,6 @@ void unputcmdline(void) // called afterwards. void put_on_cmdline(const char *str, int len, bool redraw) { - int i; - int m; - int c; - if (len < 0) { len = (int)strlen(str); } @@ -3541,7 +3559,8 @@ void put_on_cmdline(const char *str, int len, bool redraw) ccline.cmdlen += len; } else { // Count nr of characters in the new string. - m = 0; + int m = 0; + int i; for (i = 0; i < len; i += utfc_ptr2len(str + i)) { m++; } @@ -3565,9 +3584,11 @@ void put_on_cmdline(const char *str, int len, bool 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); - while (ccline.cmdpos > 0 && utf_iscomposing(c)) { + int i = 0; + int c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos); + // TODO(bfredl): this can be corrected/simplified as utf_head_off implements the + // correct grapheme cluster breaks + while (ccline.cmdpos > 0 && utf_iscomposing_legacy(c)) { i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1; ccline.cmdpos -= i; len += i; @@ -3597,7 +3618,7 @@ void put_on_cmdline(const char *str, int len, bool redraw) if (redraw && !cmd_silent) { msg_no_more = true; - i = cmdline_row; + int i = cmdline_row; cursorcmd(); draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); // Avoid clearing the rest of the line too often. @@ -3606,6 +3627,7 @@ void put_on_cmdline(const char *str, int len, bool redraw) } msg_no_more = false; } + int m; if (KeyTyped) { m = Columns * Rows; if (m < 0) { // overflow, Columns or Rows at weird value @@ -3614,8 +3636,8 @@ void put_on_cmdline(const char *str, int len, bool redraw) } else { m = MAXCOL; } - for (i = 0; i < len; i++) { - c = cmdline_charsize(ccline.cmdpos); + for (int i = 0; i < len; i++) { + int c = cmdline_charsize(ccline.cmdpos); // count ">" for a double-wide char that doesn't fit. correct_screencol(ccline.cmdpos, c, &ccline.cmdspos); // Stop cursor at the end of the screen, but do increment the @@ -3625,9 +3647,7 @@ void put_on_cmdline(const char *str, int len, bool redraw) ccline.cmdspos += c; } c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; - if (c > len - i - 1) { - c = len - i - 1; - } + c = MIN(c, len - i - 1); ccline.cmdpos += c; i += c; ccline.cmdpos++; @@ -3864,17 +3884,13 @@ void cursorcmd(void) } if (ui_has(kUICmdline)) { - if (ccline.redraw_state < kCmdRedrawPos) { - ccline.redraw_state = kCmdRedrawPos; - } + ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos); return; } msg_row = cmdline_row + (ccline.cmdspos / Columns); msg_col = ccline.cmdspos % Columns; - if (msg_row >= Rows) { - msg_row = Rows - 1; - } + msg_row = MIN(msg_row, Rows - 1); msg_cursor_goto(msg_row, msg_col); } @@ -4070,18 +4086,22 @@ static char *get_cmdline_completion(void) return NULL; } - set_expand_context(p->xpc); - if (p->xpc->xp_context == EXPAND_UNSUCCESSFUL) { + int xp_context = p->xpc->xp_context; + if (xp_context == EXPAND_NOTHING) { + set_expand_context(p->xpc); + xp_context = p->xpc->xp_context; + p->xpc->xp_context = EXPAND_NOTHING; + } + if (xp_context == EXPAND_UNSUCCESSFUL) { return NULL; } - char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context); + char *cmd_compl = get_user_cmd_complete(NULL, xp_context); if (cmd_compl == NULL) { return NULL; } - if (p->xpc->xp_context == EXPAND_USER_LIST - || p->xpc->xp_context == EXPAND_USER_DEFINED) { + if (xp_context == EXPAND_USER_LIST || xp_context == EXPAND_USER_DEFINED) { size_t buflen = strlen(cmd_compl) + strlen(p->xpc->xp_arg) + 2; char *buffer = xmalloc(buflen); snprintf(buffer, buflen, "%s,%s", cmd_compl, p->xpc->xp_arg); @@ -4112,6 +4132,15 @@ void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0; } +/// "getcmdprompt()" function +void f_getcmdprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + CmdlineInfo *p = get_ccline_ptr(); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = p != NULL && p->cmdprompt != NULL + ? xstrdup(p->cmdprompt) : NULL; +} + /// "getcmdscreenpos()" function void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -4166,11 +4195,8 @@ static int set_cmdline_pos(int pos) // The position is not set directly but after CTRL-\ e or CTRL-R = has // changed the command line. - if (pos < 0) { - new_cmdpos = 0; - } else { - new_cmdpos = pos; - } + new_cmdpos = MAX(0, pos); + return 0; } @@ -4276,7 +4302,7 @@ const char *did_set_cedit(optset_T *args) cedit_key = -1; } else { int n = string_to_key(p_cedit); - if (vim_isprintc(n)) { + if (n == 0 || vim_isprintc(n)) { return e_invarg; } cedit_key = n; @@ -4297,7 +4323,6 @@ static int open_cmdwin(void) win_T *old_curwin = curwin; int i; garray_T winsizes; - char typestr[2]; int save_restart_edit = restart_edit; int save_State = State; bool save_exmode = exmode_active; @@ -4440,9 +4465,7 @@ static int open_cmdwin(void) cmdwin_result = 0; // Trigger CmdwinEnter autocommands. - typestr[0] = (char)cmdwin_type; - typestr[1] = NUL; - apply_autocmds(EVENT_CMDWINENTER, typestr, typestr, false, curbuf); + trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINENTER); if (restart_edit != 0) { // autocmd with ":startinsert" stuffcharReadbuff(K_NOP); } @@ -4460,7 +4483,7 @@ static int open_cmdwin(void) const bool save_KeyTyped = KeyTyped; // Trigger CmdwinLeave autocommands. - apply_autocmds(EVENT_CMDWINLEAVE, typestr, typestr, false, curbuf); + trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINLEAVE); // Restore KeyTyped in case it is modified by autocommands KeyTyped = save_KeyTyped; @@ -4623,14 +4646,132 @@ char *script_get(exarg_T *const eap, size_t *const lenp) return (char *)ga.ga_data; } -static void set_search_match(pos_T *t) +/// This function is used by f_input() and f_inputdialog() functions. The third +/// argument to f_input() specifies the type of completion to use at the +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. +void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, + const bool secret) + FUNC_ATTR_NONNULL_ALL { - // First move cursor to end of match, then to the start. This - // moves the whole match onto the screen when 'nowrap' is set. - t->lnum += search_match_lines; - t->col = search_match_endcol; - if (t->lnum > curbuf->b_ml.ml_line_count) { - t->lnum = curbuf->b_ml.ml_line_count; - coladvance(curwin, MAXCOL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *prompt; + const char *defstr = ""; + typval_T *cancelreturn = NULL; + typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; + const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; + char prompt_buf[NUMBUFLEN]; + char defstr_buf[NUMBUFLEN]; + char cancelreturn_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; + if (argvars[0].v_type == VAR_DICT) { + if (argvars[1].v_type != VAR_UNKNOWN) { + emsg(_("E5050: {opts} must be the only argument")); + return; + } + dict_T *const dict = argvars[0].vval.v_dict; + prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); + if (prompt == NULL) { + return; + } + defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); + if (defstr == NULL) { + return; + } + dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); + if (cancelreturn_di != NULL) { + cancelreturn = &cancelreturn_di->di_tv; + } + xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), + xp_name_buf, def); + if (xp_name == NULL) { // error + return; + } + if (xp_name == def) { // default to NULL + xp_name = NULL; + } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } + } else { + prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); + if (prompt == NULL) { + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) { + defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); + if (defstr == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); + if (strarg2 == NULL) { + return; + } + if (inputdialog) { + cancelreturn_strarg2.v_type = VAR_STRING; + cancelreturn_strarg2.vval.v_string = (char *)strarg2; + cancelreturn = &cancelreturn_strarg2; + } else { + xp_name = strarg2; + } + } + } + } + + int xp_type = EXPAND_NOTHING; + char *xp_arg = NULL; + if (xp_name != NULL) { + // input() with a third argument: completion + const int xp_namelen = (int)strlen(xp_name); + + uint32_t argt = 0; + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, + &argt, &xp_arg) == FAIL) { + return; + } + } + + const bool cmd_silent_save = cmd_silent; + + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_has(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl + 1; + msg_start(); + msg_clr_eos(); + msg_puts_len(prompt, p - prompt, get_echo_attr()); + msg_didout = false; + msg_starthere(); + } + } + cmdline_row = msg_row; + + stuffReadbuffSpec(defstr); + + const int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_attr(), + xp_type, xp_arg, input_callback); + ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); + + if (rettv->vval.v_string == NULL && cancelreturn != NULL) { + tv_copy(cancelreturn, rettv); } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; + cmd_silent = cmd_silent_save; } |