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.c204
1 files changed, 116 insertions, 88 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7d87e609ca..ace62ea729 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -125,6 +125,7 @@ typedef struct {
bool gotesc; // true when <ESC> just typed
int do_abbr; // when true check for abbr.
char *lookfor; // string to match
+ int lookforlen;
int hiscnt; // current history line in use
int save_hiscnt; // history line before attempting
// to jump to next match
@@ -385,7 +386,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
if (!use_last_pat) {
char c = *end;
*end = NUL;
- bool empty = empty_pattern_magic(p, strlen(p), magic);
+ bool empty = empty_pattern_magic(p, (size_t)(end - p), magic);
*end = c;
if (empty) {
goto theend;
@@ -538,7 +539,8 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state
if (!use_last_pat) {
next_char = ccline.cmdbuff[skiplen + patlen];
ccline.cmdbuff[skiplen + patlen] = NUL;
- if (empty_pattern(ccline.cmdbuff + skiplen, search_delim) && !no_hlsearch) {
+ if (empty_pattern(ccline.cmdbuff + skiplen, (size_t)patlen, search_delim)
+ && !no_hlsearch) {
redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(true);
}
@@ -809,7 +811,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
- msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
+ msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
@@ -900,12 +902,11 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
&& ccline.cmdlen
&& s->firstc != NUL
&& (s->some_key_typed || s->histype == HIST_SEARCH)) {
- size_t cmdbufflen = strlen(ccline.cmdbuff);
- add_to_history(s->histype, ccline.cmdbuff, cmdbufflen, true,
+ add_to_history(s->histype, ccline.cmdbuff, (size_t)ccline.cmdlen, true,
s->histype == HIST_SEARCH ? s->firstc : NUL);
if (s->firstc == ':') {
xfree(new_last_cmdline);
- new_last_cmdline = xstrnsave(ccline.cmdbuff, cmdbufflen);
+ new_last_cmdline = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
}
}
@@ -1262,6 +1263,7 @@ static int command_line_execute(VimState *state, int key)
&& s->c != K_LEFT && s->c != K_RIGHT
&& (s->xpc.xp_numfiles > 0 || (s->c != Ctrl_P && s->c != Ctrl_N))) {
XFREE_CLEAR(s->lookfor);
+ s->lookforlen = 0;
}
// When there are matching completions to select <S-Tab> works like
@@ -1440,7 +1442,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
return FAIL;
}
skiplen = 0;
- patlen = (int)strlen(pat);
+ patlen = (int)last_search_pattern_len();
} else {
pat = ccline.cmdbuff + skiplen;
}
@@ -1574,7 +1576,8 @@ static int command_line_erase_chars(CommandLineState *s)
return CMDLINE_NOT_CHANGED;
}
- XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
+ dealloc_cmdbuff(); // no commandline to return
+
if (!cmd_silent && !ui_has(kUICmdline)) {
msg_col = 0;
msg_putchar(' '); // delete ':'
@@ -1702,7 +1705,6 @@ static void command_line_left_right_mouse(CommandLineState *s)
static void command_line_next_histidx(CommandLineState *s, bool next_match)
{
- int j = (int)strlen(s->lookfor);
while (true) {
// one step backwards
if (!next_match) {
@@ -1746,7 +1748,7 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->save_hiscnt
|| strncmp(get_histentry(s->histype)[s->hiscnt].hisstr,
- s->lookfor, (size_t)j) == 0) {
+ s->lookfor, (size_t)s->lookforlen) == 0) {
break;
}
}
@@ -1765,30 +1767,34 @@ static int command_line_browse_history(CommandLineState *s)
// save current command string so it can be restored later
if (s->lookfor == NULL) {
- s->lookfor = xstrdup(ccline.cmdbuff);
+ s->lookfor = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
s->lookfor[ccline.cmdpos] = NUL;
+ s->lookforlen = ccline.cmdpos;
}
bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
|| s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
command_line_next_histidx(s, next_match);
- if (s->hiscnt != s->save_hiscnt) {
- // jumped to other entry
+ if (s->hiscnt != s->save_hiscnt) { // jumped to other entry
char *p;
+ int plen;
int old_firstc;
- XFREE_CLEAR(ccline.cmdbuff);
+ dealloc_cmdbuff();
+
s->xpc.xp_context = EXPAND_NOTHING;
if (s->hiscnt == get_hislen()) {
p = s->lookfor; // back to the old one
+ plen = s->lookforlen;
} else {
p = get_histentry(s->histype)[s->hiscnt].hisstr;
+ plen = (int)get_histentry(s->histype)[s->hiscnt].hisstrlen;
}
if (s->histype == HIST_SEARCH
&& p != s->lookfor
- && (old_firstc = (uint8_t)p[strlen(p) + 1]) != s->firstc) {
+ && (old_firstc = (uint8_t)p[plen + 1]) != s->firstc) {
int len = 0;
// Correct for the separator character used when
// adding the history entry vs the one used now.
@@ -1827,12 +1833,13 @@ static int command_line_browse_history(CommandLineState *s)
}
}
ccline.cmdbuff[len] = NUL;
+ ccline.cmdpos = ccline.cmdlen = len;
} else {
- alloc_cmdbuff((int)strlen(p));
+ alloc_cmdbuff(plen);
STRCPY(ccline.cmdbuff, p);
+ ccline.cmdpos = ccline.cmdlen = plen;
}
- ccline.cmdpos = ccline.cmdlen = (int)strlen(ccline.cmdbuff);
redrawcmd();
return CMDLINE_CHANGED;
}
@@ -2201,18 +2208,17 @@ static int command_line_not_changed(CommandLineState *s)
/// Guess that the pattern matches everything. Only finds specific cases, such
/// as a trailing \|, which can happen while typing a pattern.
-static bool empty_pattern(char *p, int delim)
+static bool empty_pattern(char *p, size_t len, int delim)
{
- size_t n = strlen(p);
magic_T magic_val = MAGIC_ON;
- if (n > 0) {
+ if (len > 0) {
skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val);
} else {
return true;
}
- return empty_pattern_magic(p, n, magic_val);
+ return empty_pattern_magic(p, len, magic_val);
}
static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
@@ -2225,11 +2231,9 @@ static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
// true, if the pattern is empty, or the pattern ends with \| and magic is
// set (or it ends with '|' and very magic is set)
- return len == 0 || (len > 1
- && ((p[len - 2] == '\\'
- && p[len - 1] == '|' && magic_val == MAGIC_ON)
- || (p[len - 2] != '\\'
- && p[len - 1] == '|' && magic_val == MAGIC_ALL)));
+ return len == 0 || (len > 1 && p[len - 1] == '|'
+ && ((p[len - 2] == '\\' && magic_val == MAGIC_ON)
+ || (p[len - 2] != '\\' && magic_val == MAGIC_ALL)));
}
handle_T cmdpreview_get_bufnr(void)
@@ -2546,6 +2550,9 @@ static bool cmdpreview_may_show(CommandLineState *s)
goto end;
}
+ // Cursor may be at the end of the message grid rather than at cmdspos.
+ // Place it there in case preview callback flushes it. #30696
+ cursorcmd();
// 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();
@@ -2653,7 +2660,7 @@ static void do_autocmd_cmdlinechanged(int firstc)
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
- msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
+ msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
@@ -2710,7 +2717,7 @@ static int command_line_changed(CommandLineState *s)
/// Abandon the command line.
static void abandon_cmdline(void)
{
- XFREE_CLEAR(ccline.cmdbuff);
+ dealloc_cmdbuff();
ccline.redraw_state = kCmdRedrawNone;
if (msg_scrolled == 0) {
compute_cmdrow();
@@ -2751,13 +2758,13 @@ char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNU
///
/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
/// @param[in] prompt Prompt string: what is displayed before the user text.
-/// @param[in] attr Prompt highlighting.
+/// @param[in] hl_id Prompt highlight id.
/// @param[in] xp_context Type of expansion.
/// @param[in] xp_arg User-defined expansion argument.
/// @param[in] highlight_callback Callback used for highlighting user input.
///
/// @return [allocated] Command line or NULL.
-char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr,
+char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
const int xp_context, const char *const xp_arg,
const Callback highlight_callback)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
@@ -2775,7 +2782,7 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at
}
ccline.prompt_id = last_prompt_id++;
ccline.cmdprompt = (char *)prompt;
- ccline.cmdattr = attr;
+ ccline.hl_id = hl_id;
ccline.xp_context = xp_context;
ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
@@ -3000,8 +3007,15 @@ bool cmdline_at_end(void)
return (ccline.cmdpos >= ccline.cmdlen);
}
-// Allocate a new command line buffer.
-// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
+/// Deallocate a command line buffer, updating the buffer size and length.
+static void dealloc_cmdbuff(void)
+{
+ XFREE_CLEAR(ccline.cmdbuff);
+ ccline.cmdlen = ccline.cmdbufflen = 0;
+}
+
+/// Allocate a new command line buffer.
+/// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
static void alloc_cmdbuff(int len)
{
// give some extra space to avoid having to allocate all the time
@@ -3023,6 +3037,7 @@ void realloc_cmdbuff(int len)
}
char *p = ccline.cmdbuff;
+
alloc_cmdbuff(len); // will get some more
// There isn't always a NUL after the command, but it may need to be
// there, thus copy up to the NUL and add a NUL.
@@ -3083,15 +3098,13 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = (int)chunk.start.col,
- .attr = 0,
+ .hl_id = 0,
}));
}
- const int id = syn_name2id(chunk.group);
- const int attr = (id == 0 ? 0 : syn_id2attr(id));
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)chunk.start.col,
.end = (int)chunk.end_col,
- .attr = attr,
+ .hl_id = syn_name2id(chunk.group),
}));
prev_end = chunk.end_col;
}
@@ -3099,7 +3112,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = colored_ccline->cmdlen,
- .attr = 0,
+ .hl_id = 0,
}));
}
kvi_destroy(colors);
@@ -3128,7 +3141,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
#define PRINT_ERRMSG(...) \
do { \
msg_putchar('\n'); \
- msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, __VA_ARGS__); \
+ msg_printf_hl(HLF_E, __VA_ARGS__); \
printed_errmsg = true; \
} while (0)
bool ret = true;
@@ -3263,7 +3276,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = (int)start,
- .attr = 0,
+ .hl_id = 0,
}));
}
const varnumber_T end =
@@ -3287,12 +3300,10 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
if (group == NULL) {
goto color_cmdline_error;
}
- const int id = syn_name2id(group);
- const int attr = (id == 0 ? 0 : syn_id2attr(id));
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)start,
.end = (int)end,
- .attr = attr,
+ .hl_id = syn_name2id(group),
}));
i++;
});
@@ -3300,7 +3311,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = colored_ccline->cmdlen,
- .attr = 0,
+ .hl_id = 0,
}));
}
prev_prompt_errors = 0;
@@ -3362,10 +3373,10 @@ static void draw_cmdline(int start, int len)
continue;
}
const int chunk_start = MAX(chunk.start, start);
- msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.attr);
+ msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.hl_id, false);
}
} else {
- msg_outtrans_len(ccline.cmdbuff + start, len, 0);
+ msg_outtrans_len(ccline.cmdbuff + start, len, 0, false);
}
}
}
@@ -3391,7 +3402,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
Array item = arena_array(&arena, 2);
- ADD_C(item, INTEGER_OBJ(chunk.attr));
+ ADD_C(item, INTEGER_OBJ(chunk.hl_id == 0 ? 0 : syn_id2attr(chunk.hl_id)));
assert(chunk.end >= chunk.start);
ADD_C(item, STRING_OBJ(cbuf_as_string(line->cmdbuff + chunk.start,
@@ -3581,38 +3592,14 @@ void put_on_cmdline(const char *str, int len, bool redraw)
memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len);
ccline.cmdbuff[ccline.cmdlen] = NUL;
- {
- // When the inserted text starts with a composing character,
- // backup to the character before it. There could be two of them.
- 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;
+ // When the inserted text starts with a composing character,
+ // backup to the character before it.
+ if (ccline.cmdpos > 0 && (uint8_t)ccline.cmdbuff[ccline.cmdpos] >= 0x80) {
+ int i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos);
+ if (i != 0) {
ccline.cmdpos -= i;
len += i;
- c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
- }
- if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) {
- // Check the previous character for Arabic combining pair.
- i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
- if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) {
- ccline.cmdpos -= i;
- len += i;
- } else {
- i = 0;
- }
- }
- if (i != 0) {
- // Also backup the cursor position.
- i = ptr2cells(ccline.cmdbuff + ccline.cmdpos);
- ccline.cmdspos -= i;
- msg_col -= i;
- if (msg_col < 0) {
- msg_col += Columns;
- msg_row--;
- }
+ ccline.cmdspos = cmd_screencol(ccline.cmdpos);
}
}
@@ -3801,7 +3788,7 @@ static void redrawcmdprompt(void)
msg_putchar(ccline.cmdfirstc);
}
if (ccline.cmdprompt != NULL) {
- msg_puts_attr(ccline.cmdprompt, ccline.cmdattr);
+ msg_puts_hl(ccline.cmdprompt, ccline.hl_id, false);
ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
// do the reverse of cmd_startcol()
if (ccline.cmdfirstc != NUL) {
@@ -4074,14 +4061,44 @@ static char *get_cmdline_str(void)
return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
}
+/// Get the current command-line completion pattern.
+static char *get_cmdline_completion_pattern(void)
+{
+ if (cmdline_star > 0) {
+ return NULL;
+ }
+
+ CmdlineInfo *p = get_ccline_ptr();
+ if (p == NULL || p->xpc == NULL) {
+ return NULL;
+ }
+
+ 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 *compl_pat = p->xpc->xp_pattern;
+ if (compl_pat == NULL) {
+ return NULL;
+ }
+
+ return xstrdup(compl_pat);
+}
+
/// Get the current command-line completion type.
static char *get_cmdline_completion(void)
{
if (cmdline_star > 0) {
return NULL;
}
- CmdlineInfo *p = get_ccline_ptr();
+ CmdlineInfo *p = get_ccline_ptr();
if (p == NULL || p->xpc == NULL) {
return NULL;
}
@@ -4111,6 +4128,13 @@ static char *get_cmdline_completion(void)
return xstrdup(cmd_compl);
}
+/// "getcmdcomplpat()" function
+void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_cmdline_completion_pattern();
+}
+
/// "getcmdcompltype()" function
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -4509,17 +4533,20 @@ static int open_cmdwin(void)
cmdwin_result = Ctrl_C;
}
// Set the new command line from the cmdline buffer.
- xfree(ccline.cmdbuff);
+ dealloc_cmdbuff();
+
if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed
const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!";
+ size_t plen = (cmdwin_result == K_XF2) ? 2 : 3;
if (histtype == HIST_CMD) {
// Execute the command directly.
- ccline.cmdbuff = xstrdup(p);
+ ccline.cmdbuff = xmemdupz(p, plen);
+ ccline.cmdlen = (int)plen;
+ ccline.cmdbufflen = (int)plen + 1;
cmdwin_result = CAR;
} else {
// First need to cancel what we were doing.
- ccline.cmdbuff = NULL;
stuffcharReadbuff(':');
stuffReadbuff(p);
stuffcharReadbuff(CAR);
@@ -4529,17 +4556,18 @@ static int open_cmdwin(void)
// and don't modify the cmd window.
ccline.cmdbuff = NULL;
} else {
- ccline.cmdbuff = xstrdup(get_cursor_line_ptr());
+ ccline.cmdlen = get_cursor_line_len();
+ ccline.cmdbufflen = ccline.cmdlen + 1;
+ ccline.cmdbuff = xstrnsave(get_cursor_line_ptr(), (size_t)ccline.cmdlen);
}
+
if (ccline.cmdbuff == NULL) {
- ccline.cmdbuff = xstrdup("");
+ ccline.cmdbuff = xmemdupz("", 0);
ccline.cmdlen = 0;
ccline.cmdbufflen = 1;
ccline.cmdpos = 0;
cmdwin_result = Ctrl_C;
} else {
- ccline.cmdlen = (int)strlen(ccline.cmdbuff);
- ccline.cmdbufflen = ccline.cmdlen + 1;
ccline.cmdpos = curwin->w_cursor.col;
// If the cursor is on the last character, it probably should be after it.
if (ccline.cmdpos == ccline.cmdlen - 1 || ccline.cmdpos > ccline.cmdlen) {
@@ -4748,7 +4776,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
p = lastnl + 1;
msg_start();
msg_clr_eos();
- msg_puts_len(prompt, p - prompt, get_echo_attr());
+ msg_puts_len(prompt, p - prompt, get_echo_hl_id(), false);
msg_didout = false;
msg_starthere();
}
@@ -4759,7 +4787,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
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(),
+ rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
xp_type, xp_arg, input_callback);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);