diff options
Diffstat (limited to 'src/nvim/ex_getln.c')
-rw-r--r-- | src/nvim/ex_getln.c | 204 |
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); |