aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_getln.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_getln.c')
-rw-r--r--src/nvim/ex_getln.c277
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;
}