ex_getln.c (148992B)
1 // ex_getln.c: Functions for entering and editing an Ex command line. 2 3 #include <assert.h> 4 #include <inttypes.h> 5 #include <limits.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <time.h> 11 12 #include "klib/kvec.h" 13 #include "nvim/api/extmark.h" 14 #include "nvim/api/private/defs.h" 15 #include "nvim/api/private/helpers.h" 16 #include "nvim/api/vim.h" 17 #include "nvim/ascii_defs.h" 18 #include "nvim/autocmd.h" 19 #include "nvim/autocmd_defs.h" 20 #include "nvim/buffer.h" 21 #include "nvim/buffer_defs.h" 22 #include "nvim/charset.h" 23 #include "nvim/clipboard.h" 24 #include "nvim/cmdexpand.h" 25 #include "nvim/cmdexpand_defs.h" 26 #include "nvim/cmdhist.h" 27 #include "nvim/cursor.h" 28 #include "nvim/digraph.h" 29 #include "nvim/drawscreen.h" 30 #include "nvim/edit.h" 31 #include "nvim/errors.h" 32 #include "nvim/eval.h" 33 #include "nvim/eval/typval.h" 34 #include "nvim/eval/vars.h" 35 #include "nvim/ex_cmds.h" 36 #include "nvim/ex_cmds_defs.h" 37 #include "nvim/ex_docmd.h" 38 #include "nvim/ex_eval.h" 39 #include "nvim/ex_getln.h" 40 #include "nvim/extmark.h" 41 #include "nvim/garray.h" 42 #include "nvim/garray_defs.h" 43 #include "nvim/getchar.h" 44 #include "nvim/gettext_defs.h" 45 #include "nvim/globals.h" 46 #include "nvim/highlight_defs.h" 47 #include "nvim/highlight_group.h" 48 #include "nvim/keycodes.h" 49 #include "nvim/macros_defs.h" 50 #include "nvim/map_defs.h" 51 #include "nvim/mapping.h" 52 #include "nvim/mark.h" 53 #include "nvim/mark_defs.h" 54 #include "nvim/mbyte.h" 55 #include "nvim/memline.h" 56 #include "nvim/memory.h" 57 #include "nvim/memory_defs.h" 58 #include "nvim/message.h" 59 #include "nvim/mouse.h" 60 #include "nvim/move.h" 61 #include "nvim/normal.h" 62 #include "nvim/ops.h" 63 #include "nvim/option.h" 64 #include "nvim/option_defs.h" 65 #include "nvim/option_vars.h" 66 #include "nvim/os/input.h" 67 #include "nvim/os/os.h" 68 #include "nvim/path.h" 69 #include "nvim/popupmenu.h" 70 #include "nvim/pos_defs.h" 71 #include "nvim/profile.h" 72 #include "nvim/regexp.h" 73 #include "nvim/regexp_defs.h" 74 #include "nvim/register.h" 75 #include "nvim/search.h" 76 #include "nvim/state.h" 77 #include "nvim/state_defs.h" 78 #include "nvim/strings.h" 79 #include "nvim/types_defs.h" 80 #include "nvim/ui.h" 81 #include "nvim/ui_defs.h" 82 #include "nvim/undo.h" 83 #include "nvim/undo_defs.h" 84 #include "nvim/usercmd.h" 85 #include "nvim/vim_defs.h" 86 #include "nvim/viml/parser/expressions.h" 87 #include "nvim/viml/parser/parser.h" 88 #include "nvim/viml/parser/parser_defs.h" 89 #include "nvim/window.h" 90 91 /// Last value of prompt_id, incremented when doing new prompt 92 static unsigned last_prompt_id = 0; 93 94 // Struct to store the viewstate during 'incsearch' highlighting and 'inccommand' preview. 95 typedef struct { 96 colnr_T vs_curswant; 97 colnr_T vs_leftcol; 98 colnr_T vs_skipcol; 99 linenr_T vs_topline; 100 int vs_topfill; 101 linenr_T vs_botline; 102 int vs_empty_rows; 103 } viewstate_T; 104 105 // Struct to store the state of 'incsearch' highlighting. 106 typedef struct { 107 pos_T search_start; // where 'incsearch' starts searching 108 pos_T save_cursor; 109 handle_T winid; // window where this state is valid 110 viewstate_T init_viewstate; 111 viewstate_T old_viewstate; 112 pos_T match_start; 113 pos_T match_end; 114 bool did_incsearch; 115 bool incsearch_postponed; 116 optmagic_T magic_overruled_save; 117 } incsearch_state_T; 118 119 typedef struct { 120 VimState state; 121 int firstc; 122 int count; 123 int indent; 124 int c; 125 bool gotesc; // true when <ESC> just typed 126 bool do_abbr; // when true check for abbr. 127 char *lookfor; // string to match 128 int lookforlen; 129 int hiscnt; // current history line in use 130 int save_hiscnt; // history line before attempting 131 // to jump to next match 132 int histype; // history type to be used 133 incsearch_state_T is_state; 134 bool did_wild_list; // did wild_list() recently 135 int wim_index; // index in wim_flags[] 136 int save_msg_scroll; 137 int save_State; // remember State when called 138 int prev_cmdpos; 139 char *prev_cmdbuff; 140 char *save_p_icm; 141 bool skip_pum_redraw; 142 bool some_key_typed; // one of the keys was typed 143 // mouse drag and release events are ignored, unless they are 144 // preceded with a mouse down event 145 bool ignore_drag_release; 146 bool break_ctrl_c; 147 expand_T xpc; 148 OptInt *b_im_ptr; 149 buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid 150 int cmdline_type; 151 bool event_cmdlineleavepre_triggered; 152 bool did_hist_navigate; 153 } CommandLineState; 154 155 typedef struct { 156 u_header_T *save_b_u_oldhead; 157 u_header_T *save_b_u_newhead; 158 u_header_T *save_b_u_curhead; 159 int save_b_u_numhead; 160 bool save_b_u_synced; 161 int save_b_u_seq_last; 162 int save_b_u_save_nr_last; 163 int save_b_u_seq_cur; 164 time_t save_b_u_time_cur; 165 int save_b_u_save_nr_cur; 166 char *save_b_u_line_ptr; 167 linenr_T save_b_u_line_lnum; 168 colnr_T save_b_u_line_colnr; 169 } CpUndoInfo; 170 171 typedef struct { 172 buf_T *buf; 173 OptInt save_b_p_ul; 174 int save_b_p_ma; 175 int save_b_changed; 176 pos_T save_b_op_start; 177 pos_T save_b_op_end; 178 varnumber_T save_changedtick; 179 CpUndoInfo undo_info; 180 } CpBufInfo; 181 182 typedef struct { 183 win_T *win; 184 pos_T save_w_cursor; 185 viewstate_T save_viewstate; 186 int save_w_p_cul; 187 int save_w_p_cuc; 188 } CpWinInfo; 189 190 typedef struct { 191 kvec_t(CpWinInfo) win_info; 192 kvec_t(CpBufInfo) buf_info; 193 bool save_hls; 194 cmdmod_T save_cmdmod; 195 garray_T save_view; 196 } CpInfo; 197 198 /// Return value when handling keys in command-line mode. 199 enum { 200 CMDLINE_NOT_CHANGED = 1, 201 CMDLINE_CHANGED = 2, 202 GOTO_NORMAL_MODE = 3, 203 PROCESS_NEXT_KEY = 4, 204 }; 205 206 /// The current cmdline_info. It is initialized in getcmdline() and after that 207 /// used by other functions. When invoking getcmdline() recursively it needs 208 /// to be saved with save_cmdline() and restored with restore_cmdline(). 209 static CmdlineInfo ccline; 210 211 static int new_cmdpos; // position set by set_cmdline_pos() 212 213 /// currently displayed block of context 214 static Array cmdline_block = ARRAY_DICT_INIT; 215 216 /// Flag for command_line_handle_key to ignore <C-c> 217 /// 218 /// Used if it was received while processing highlight function in order for 219 /// user interrupting highlight function to not interrupt command-line. 220 static bool getln_interrupted_highlight = false; 221 222 static int cedit_key = -1; ///< key value of 'cedit' option 223 224 #include "ex_getln.c.generated.h" 225 226 static handle_T cmdpreview_bufnr = 0; 227 static int cmdpreview_ns = 0; 228 229 static const char e_active_window_or_buffer_changed_or_deleted[] 230 = N_("E199: Active window or buffer changed or deleted"); 231 232 static void trigger_cmd_autocmd(int typechar, event_T evt) 233 { 234 char typestr[2] = { (char)typechar, NUL }; 235 apply_autocmds(evt, typestr, typestr, false, curbuf); 236 } 237 238 static void save_viewstate(win_T *wp, viewstate_T *vs) 239 FUNC_ATTR_NONNULL_ALL 240 { 241 vs->vs_curswant = wp->w_curswant; 242 vs->vs_leftcol = wp->w_leftcol; 243 vs->vs_skipcol = wp->w_skipcol; 244 vs->vs_topline = wp->w_topline; 245 vs->vs_topfill = wp->w_topfill; 246 vs->vs_botline = wp->w_botline; 247 vs->vs_empty_rows = wp->w_empty_rows; 248 } 249 250 static void restore_viewstate(win_T *wp, viewstate_T *vs) 251 FUNC_ATTR_NONNULL_ALL 252 { 253 wp->w_curswant = vs->vs_curswant; 254 wp->w_leftcol = vs->vs_leftcol; 255 wp->w_skipcol = vs->vs_skipcol; 256 wp->w_topline = vs->vs_topline; 257 wp->w_topfill = vs->vs_topfill; 258 wp->w_botline = vs->vs_botline; 259 wp->w_empty_rows = vs->vs_empty_rows; 260 } 261 262 static void init_incsearch_state(incsearch_state_T *s) 263 { 264 s->winid = curwin->handle; 265 s->match_start = curwin->w_cursor; 266 s->did_incsearch = false; 267 s->incsearch_postponed = false; 268 s->magic_overruled_save = magic_overruled; 269 clearpos(&s->match_end); 270 s->save_cursor = curwin->w_cursor; // may be restored later 271 s->search_start = curwin->w_cursor; 272 save_viewstate(curwin, &s->init_viewstate); 273 save_viewstate(curwin, &s->old_viewstate); 274 } 275 276 static void set_search_match(pos_T *t) 277 { 278 // First move cursor to end of match, then to the start. This 279 // moves the whole match onto the screen when 'nowrap' is set. 280 t->lnum += search_match_lines; 281 t->col = search_match_endcol; 282 if (t->lnum > curbuf->b_ml.ml_line_count) { 283 t->lnum = curbuf->b_ml.ml_line_count; 284 coladvance(curwin, MAXCOL); 285 } 286 } 287 288 /// Parses the :[range]s/foo like commands and returns details needed for 289 /// incsearch and wildmenu completion. 290 /// Returns true if pattern is valid. 291 /// Sets skiplen, patlen, search_first_line, and search_last_line. 292 bool parse_pattern_and_range(pos_T *incsearch_start, int *search_delim, int *skiplen, int *patlen) 293 FUNC_ATTR_NONNULL_ALL 294 { 295 char *p; 296 bool delim_optional = false; 297 const char *dummy; 298 magic_T magic = 0; 299 300 *skiplen = 0; 301 *patlen = ccline.cmdlen; 302 303 // Default range 304 search_first_line = 0; 305 search_last_line = MAXLNUM; 306 307 exarg_T ea = { 308 .line1 = 1, 309 .line2 = 1, 310 .cmd = ccline.cmdbuff, 311 .addr_type = ADDR_LINES, 312 }; 313 314 cmdmod_T dummy_cmdmod; 315 // Skip over command modifiers 316 parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true); 317 318 // Skip over the range to find the command. 319 char *cmd = skip_range(ea.cmd, NULL); 320 if (vim_strchr("sgvl", (uint8_t)(*cmd)) == NULL) { 321 return false; 322 } 323 324 // Skip over command name to find pattern separator 325 for (p = cmd; ASCII_ISALPHA(*p); p++) {} 326 if (*skipwhite(p) == NUL) { 327 return false; 328 } 329 330 if (strncmp(cmd, "substitute", (size_t)(p - cmd)) == 0 331 || strncmp(cmd, "smagic", (size_t)(p - cmd)) == 0 332 || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0 333 || strncmp(cmd, "vglobal", (size_t)(p - cmd)) == 0) { 334 if (*cmd == 's' && cmd[1] == 'm') { 335 magic_overruled = OPTION_MAGIC_ON; 336 } else if (*cmd == 's' && cmd[1] == 'n') { 337 magic_overruled = OPTION_MAGIC_OFF; 338 } 339 } else if (strncmp(cmd, "sort", (size_t)MAX(p - cmd, 3)) == 0 340 || strncmp(cmd, "uniq", (size_t)MAX(p - cmd, 3)) == 0) { 341 // skip over ! and flags 342 if (*p == '!') { 343 p = skipwhite(p + 1); 344 } 345 while (ASCII_ISALPHA(*(p = skipwhite(p)))) { 346 p++; 347 } 348 if (*p == NUL) { 349 return false; 350 } 351 } else if (strncmp(cmd, "vimgrep", (size_t)MAX(p - cmd, 3)) == 0 352 || strncmp(cmd, "vimgrepadd", (size_t)MAX(p - cmd, 8)) == 0 353 || strncmp(cmd, "lvimgrep", (size_t)MAX(p - cmd, 2)) == 0 354 || strncmp(cmd, "lvimgrepadd", (size_t)MAX(p - cmd, 9)) == 0 355 || strncmp(cmd, "global", (size_t)(p - cmd)) == 0) { 356 // skip optional "!" 357 if (*p == '!') { 358 p++; 359 if (*skipwhite(p) == NUL) { 360 return false; 361 } 362 } 363 if (*cmd != 'g') { 364 delim_optional = true; 365 } 366 } else { 367 return false; 368 } 369 370 p = skipwhite(p); 371 int delim = (delim_optional && vim_isIDc((uint8_t)(*p))) ? ' ' : *p++; 372 *search_delim = delim; 373 374 char *end = skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic); 375 bool use_last_pat = end == p && *end == delim; 376 377 if (end == p && !use_last_pat) { 378 return false; 379 } 380 381 // Skip if the pattern matches everything (e.g., for 'hlsearch') 382 if (!use_last_pat) { 383 char c = *end; 384 *end = NUL; 385 bool empty = empty_pattern_magic(p, (size_t)(end - p), magic); 386 *end = c; 387 if (empty) { 388 return false; 389 } 390 } 391 392 // Found a non-empty pattern or // 393 *skiplen = (int)(p - ccline.cmdbuff); 394 *patlen = (int)(end - p); 395 396 // Parse the address range 397 pos_T save_cursor = curwin->w_cursor; 398 curwin->w_cursor = *incsearch_start; 399 400 parse_cmd_address(&ea, &dummy, true); 401 402 if (ea.addr_count > 0) { 403 // Allow for reverse match. 404 search_first_line = MIN(ea.line2, ea.line1); 405 search_last_line = MAX(ea.line2, ea.line1); 406 } else if (cmd[0] == 's' && cmd[1] != 'o') { 407 // :s defaults to the current line 408 search_first_line = search_last_line = curwin->w_cursor.lnum; 409 } 410 411 curwin->w_cursor = save_cursor; 412 return true; 413 } 414 415 /// Return true when 'incsearch' highlighting is to be done. 416 /// Sets search_first_line and search_last_line to the address range. 417 /// May change the last search pattern. 418 static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_state, 419 int *skiplen, int *patlen) 420 { 421 bool retval = false; 422 423 *skiplen = 0; 424 *patlen = ccline.cmdlen; 425 426 if (!p_is || cmd_silent) { 427 return false; 428 } 429 430 // By default search all lines 431 search_first_line = 0; 432 search_last_line = MAXLNUM; 433 434 if (firstc == '/' || firstc == '?') { 435 *search_delim = firstc; 436 return true; 437 } 438 439 if (firstc != ':') { 440 return false; 441 } 442 443 emsg_off++; 444 retval = parse_pattern_and_range(&is_state->search_start, search_delim, 445 skiplen, patlen); 446 emsg_off--; 447 448 return retval; 449 } 450 451 // May do 'incsearch' highlighting if desired. 452 static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state_T *s) 453 { 454 int skiplen, patlen; 455 int search_delim; 456 457 // Parsing range may already set the last search pattern. 458 // NOTE: must call restore_last_search_pattern() before returning! 459 save_last_search_pattern(); 460 461 if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen, &patlen)) { 462 restore_last_search_pattern(); 463 finish_incsearch_highlighting(false, s, true); 464 return; 465 } 466 467 // if there is a character waiting, search and redraw later 468 if (char_avail()) { 469 restore_last_search_pattern(); 470 s->incsearch_postponed = true; 471 return; 472 } 473 s->incsearch_postponed = false; 474 475 // Use the previous pattern for ":s//". 476 char next_char = ccline.cmdbuff[skiplen + patlen]; 477 bool use_last_pat = patlen == 0 && skiplen > 0 478 && ccline.cmdbuff[skiplen - 1] == next_char; 479 480 if (patlen != 0 || use_last_pat) { 481 ui_busy_start(); 482 ui_flush(); 483 } 484 485 if (search_first_line == 0) { 486 // start at the original cursor position 487 curwin->w_cursor = s->search_start; 488 } else if (search_first_line > curbuf->b_ml.ml_line_count) { 489 // start after the last line 490 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 491 curwin->w_cursor.col = MAXCOL; 492 } else { 493 // start at the first line in the range 494 curwin->w_cursor.lnum = search_first_line; 495 curwin->w_cursor.col = 0; 496 } 497 498 int found = 0; // do_search() result 499 500 if (patlen != 0 || use_last_pat) { 501 int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; 502 if (!p_hls) { 503 search_flags += SEARCH_KEEP; 504 } 505 if (search_first_line != 0) { 506 search_flags += SEARCH_START; 507 } 508 // Set the time limit to half a second. 509 proftime_T tm = profile_setlimit(500); 510 searchit_arg_T sia = { .sa_tm = &tm }; 511 ccline.cmdbuff[skiplen + patlen] = NUL; 512 emsg_off++; // So it doesn't beep if bad expr 513 found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim, 514 ccline.cmdbuff + skiplen, (size_t)patlen, count, 515 search_flags, &sia); 516 emsg_off--; 517 ccline.cmdbuff[skiplen + patlen] = next_char; 518 if (curwin->w_cursor.lnum < search_first_line 519 || curwin->w_cursor.lnum > search_last_line) { 520 // match outside of address range 521 found = 0; 522 curwin->w_cursor = s->search_start; 523 } 524 525 // if interrupted while searching, behave like it failed 526 if (got_int) { 527 vpeekc(); // remove <C-C> from input stream 528 got_int = false; // don't abandon the command line 529 found = 0; 530 } else if (char_avail()) { 531 // cancelled searching because a char was typed 532 s->incsearch_postponed = true; 533 } 534 ui_busy_stop(); 535 } else { 536 set_no_hlsearch(true); // turn off previous highlight 537 redraw_all_later(UPD_SOME_VALID); 538 } 539 540 highlight_match = found != 0; // add or remove search match position 541 542 // first restore the old curwin values, so the screen is 543 // positioned in the same way as the actual search command 544 restore_viewstate(curwin, &s->old_viewstate); 545 changed_cline_bef_curs(curwin); 546 update_topline(curwin); 547 548 pos_T end_pos = curwin->w_cursor; 549 if (found != 0) { 550 s->match_start = curwin->w_cursor; 551 set_search_match(&curwin->w_cursor); 552 validate_cursor(curwin); 553 s->match_end = curwin->w_cursor; 554 curwin->w_cursor = end_pos; 555 end_pos = s->match_end; 556 } 557 558 // Disable 'hlsearch' highlighting if the pattern matches 559 // everything. Avoids a flash when typing "foo\|". 560 if (!use_last_pat) { 561 next_char = ccline.cmdbuff[skiplen + patlen]; 562 ccline.cmdbuff[skiplen + patlen] = NUL; 563 if (empty_pattern(ccline.cmdbuff + skiplen, (size_t)patlen, search_delim) 564 && !no_hlsearch) { 565 redraw_all_later(UPD_SOME_VALID); 566 set_no_hlsearch(true); 567 } 568 ccline.cmdbuff[skiplen + patlen] = next_char; 569 } 570 571 validate_cursor(curwin); 572 573 // May redraw the status line to show the cursor position. 574 if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { 575 curwin->w_redr_status = true; 576 } 577 578 redraw_later(curwin, UPD_SOME_VALID); 579 update_screen(); 580 highlight_match = false; 581 restore_last_search_pattern(); 582 583 // Leave it at the end to make CTRL-R CTRL-W work. But not when beyond the 584 // end of the pattern, e.g. for ":s/pat/". 585 if (ccline.cmdbuff[skiplen + patlen] != NUL) { 586 curwin->w_cursor = s->search_start; 587 } else if (found != 0) { 588 curwin->w_cursor = end_pos; 589 curwin->w_valid_cursor = end_pos; // mark as valid for cmdline_show redraw 590 } 591 592 msg_starthere(); 593 redrawcmdline(); 594 s->did_incsearch = true; 595 } 596 597 // When CTRL-L typed: add character from the match to the pattern. 598 // May set "*c" to the added character. 599 // Return OK when calling command_line_not_changed. 600 static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) 601 FUNC_ATTR_NONNULL_ALL 602 { 603 int skiplen, patlen; 604 int search_delim; 605 606 // Parsing range may already set the last search pattern. 607 // NOTE: must call restore_last_search_pattern() before returning! 608 save_last_search_pattern(); 609 610 // Add a character from under the cursor for 'incsearch' 611 if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen, 612 &patlen)) { 613 restore_last_search_pattern(); 614 return FAIL; 615 } 616 restore_last_search_pattern(); 617 618 if (s->did_incsearch) { 619 curwin->w_cursor = s->match_end; 620 *c = gchar_cursor(); 621 if (*c != NUL) { 622 // If 'ignorecase' and 'smartcase' are set and the 623 // command line has no uppercase characters, convert 624 // the character to lowercase 625 if (p_ic && p_scs 626 && !pat_has_uppercase(ccline.cmdbuff + skiplen)) { 627 *c = mb_tolower(*c); 628 } 629 if (*c == search_delim 630 || vim_strchr((magic_isset() ? "\\~^$.*[" : "\\^$"), *c) != NULL) { 631 // put a backslash before special characters 632 stuffcharReadbuff(*c); 633 *c = '\\'; 634 } 635 // add any composing characters 636 if (utf_char2len(*c) != utfc_ptr2len(get_cursor_pos_ptr())) { 637 const int save_c = *c; 638 while (utf_char2len(*c) != utfc_ptr2len(get_cursor_pos_ptr())) { 639 curwin->w_cursor.col += utf_char2len(*c); 640 *c = gchar_cursor(); 641 stuffcharReadbuff(*c); 642 } 643 *c = save_c; 644 } 645 return FAIL; 646 } 647 } 648 return OK; 649 } 650 651 static void finish_incsearch_highlighting(bool gotesc, incsearch_state_T *s, 652 bool call_update_screen) 653 { 654 if (!s->did_incsearch) { 655 return; 656 } 657 658 s->did_incsearch = false; 659 if (gotesc) { 660 curwin->w_cursor = s->save_cursor; 661 } else { 662 if (!equalpos(s->save_cursor, s->search_start)) { 663 // put the '" mark at the original position 664 curwin->w_cursor = s->save_cursor; 665 setpcmark(); 666 } 667 curwin->w_cursor = s->search_start; 668 } 669 restore_viewstate(curwin, &s->old_viewstate); 670 highlight_match = false; 671 672 // by default search all lines 673 search_first_line = 0; 674 search_last_line = MAXLNUM; 675 676 magic_overruled = s->magic_overruled_save; 677 678 validate_cursor(curwin); // needed for TAB 679 status_redraw_all(); 680 redraw_all_later(UPD_SOME_VALID); 681 if (call_update_screen) { 682 update_screen(); 683 } 684 } 685 686 /// Initialize the current command-line info. 687 static void init_ccline(int firstc, int indent) 688 { 689 ccline.overstrike = false; // always start in insert mode 690 691 assert(indent >= 0); 692 693 // set some variables for redrawcmd() 694 ccline.cmdfirstc = (firstc == '@' ? 0 : firstc); 695 ccline.cmdindent = (firstc > 0 ? indent : 0); 696 697 // alloc initial ccline.cmdbuff 698 alloc_cmdbuff(indent + 50); 699 ccline.cmdlen = ccline.cmdpos = 0; 700 ccline.cmdbuff[0] = NUL; 701 702 ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL, 703 .colors = KV_INITIAL_VALUE }; 704 sb_text_start_cmdline(); 705 706 // autoindent for :insert and :append 707 if (firstc <= 0) { 708 memset(ccline.cmdbuff, ' ', (size_t)indent); 709 ccline.cmdbuff[indent] = NUL; 710 ccline.cmdpos = indent; 711 ccline.cmdspos = indent; 712 ccline.cmdlen = indent; 713 } 714 } 715 716 static void ui_ext_cmdline_hide(bool abort) 717 { 718 if (ui_has(kUICmdline)) { 719 cmdline_was_last_drawn = false; 720 ccline.redraw_state = kCmdRedrawNone; 721 ui_call_cmdline_hide(ccline.level, abort); 722 } 723 } 724 725 /// Internal entry point for cmdline mode. 726 /// 727 /// @param count only used for incremental search 728 /// @param indent indent for inside conditionals 729 /// @param clear_ccline clear ccline first 730 static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear_ccline) 731 { 732 // can be invoked recursively, identify each level 733 static int cmdline_level = 0; 734 cmdline_level++; 735 736 bool save_cmdpreview = cmdpreview; 737 cmdpreview = false; 738 CommandLineState state = { 739 .firstc = firstc, 740 .count = count, 741 .indent = indent, 742 .save_msg_scroll = msg_scroll, 743 .save_State = State, 744 .prev_cmdpos = -1, 745 .ignore_drag_release = true, 746 }; 747 CommandLineState *s = &state; 748 s->save_p_icm = xstrdup(p_icm); 749 init_incsearch_state(&s->is_state); 750 CmdlineInfo save_ccline; 751 bool did_save_ccline = false; 752 753 if (ccline.cmdbuff != NULL) { 754 // Currently ccline can never be in use if clear_ccline is false. 755 // Some changes will be needed if this is no longer the case. 756 assert(clear_ccline); 757 // Being called recursively. Since ccline is global, we need to save 758 // the current buffer and restore it when returning. 759 save_cmdline(&save_ccline); 760 did_save_ccline = true; 761 } else if (clear_ccline) { 762 CLEAR_FIELD(ccline); 763 } 764 765 if (s->firstc == -1) { 766 s->firstc = NUL; 767 s->break_ctrl_c = true; 768 } 769 770 init_ccline(s->firstc, s->indent); 771 assert(ccline.cmdbuff != NULL); 772 ccline.prompt_id = last_prompt_id++; 773 ccline.level = cmdline_level; 774 775 if (cmdline_level == 50) { 776 // Somehow got into a loop recursively calling getcmdline(), bail out. 777 emsg(_(e_command_too_recursive)); 778 goto theend; 779 } 780 781 ExpandInit(&s->xpc); 782 ccline.xpc = &s->xpc; 783 clear_cmdline_orig(); 784 785 cmdmsg_rl = (curwin->w_p_rl && *curwin->w_p_rlc == 's' 786 && (s->firstc == '/' || s->firstc == '?')); 787 788 msg_grid_validate(); 789 790 redir_off = true; // don't redirect the typed command 791 if (!cmd_silent) { 792 gotocmdline(true); 793 redrawcmdprompt(); // draw prompt or indent 794 ccline.cmdspos = cmd_startcol(); 795 } 796 s->xpc.xp_context = EXPAND_NOTHING; 797 s->xpc.xp_backslash = XP_BS_NONE; 798 #ifndef BACKSLASH_IN_FILENAME 799 s->xpc.xp_shell = false; 800 #endif 801 802 if (ccline.input_fn) { 803 s->xpc.xp_context = ccline.xp_context; 804 s->xpc.xp_pattern = ccline.cmdbuff; 805 s->xpc.xp_arg = ccline.xp_arg; 806 } 807 808 // Avoid scrolling when called by a recursive do_cmdline(), e.g. when 809 // doing ":@0" when register 0 doesn't contain a CR. 810 msg_scroll = false; 811 812 State = MODE_CMDLINE; 813 814 if (s->firstc == '/' || s->firstc == '?' || s->firstc == '@') { 815 // Use ":lmap" mappings for search pattern and input(). 816 if (curbuf->b_p_imsearch == B_IMODE_USE_INSERT) { 817 s->b_im_ptr = &curbuf->b_p_iminsert; 818 } else { 819 s->b_im_ptr = &curbuf->b_p_imsearch; 820 } 821 s->b_im_ptr_buf = curbuf; 822 if (*s->b_im_ptr == B_IMODE_LMAP) { 823 State |= MODE_LANGMAP; 824 } 825 } 826 827 setmouse(); 828 829 s->cmdline_type = firstc > 0 ? firstc : '-'; 830 Error err = ERROR_INIT; 831 char firstcbuf[2]; 832 firstcbuf[0] = (char)s->cmdline_type; 833 firstcbuf[1] = 0; 834 835 if (has_event(EVENT_CMDLINEENTER)) { 836 save_v_event_T save_v_event; 837 dict_T *dict = get_v_event(&save_v_event); 838 839 // set v:event to a dictionary with information about the commandline 840 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); 841 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); 842 tv_dict_set_keys_readonly(dict); 843 TRY_WRAP(&err, { 844 apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf); 845 restore_v_event(dict, &save_v_event); 846 }); 847 848 if (ERROR_SET(&err)) { 849 if (!ui_has(kUIMessages)) { 850 msg_putchar('\n'); 851 } 852 msg_scroll = true; 853 msg_puts_hl(err.msg, HLF_E, true); 854 api_clear_error(&err); 855 redrawcmd(); 856 } 857 err = ERROR_INIT; 858 } 859 may_trigger_modechanged(); 860 861 init_history(); 862 s->hiscnt = get_hislen(); // set hiscnt to impossible history value 863 s->histype = hist_char2type(s->firstc); 864 do_digraph(-1); // init digraph typeahead 865 866 // If something above caused an error, reset the flags, we do want to type 867 // and execute commands. Display may be messed up a bit. 868 if (did_emsg) { 869 redrawcmd(); 870 } 871 872 // Redraw the statusline in case it uses the current mode using the mode() 873 // function. 874 if (!cmd_silent && !exmode_active) { 875 bool found_one = false; 876 877 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 878 if (*p_stl != NUL || *wp->w_p_stl != NUL || *p_wbr != NUL || *wp->w_p_wbr != NUL) { 879 wp->w_redr_status = true; 880 found_one = true; 881 } 882 } 883 884 if (*p_tal != NUL) { 885 redraw_tabline = true; 886 found_one = true; 887 } 888 889 if (redraw_custom_title_later()) { 890 found_one = true; 891 } 892 893 if (found_one) { 894 redraw_statuslines(); 895 } 896 } 897 898 did_emsg = false; 899 got_int = false; 900 s->state.check = command_line_check; 901 s->state.execute = command_line_execute; 902 903 state_enter(&s->state); 904 905 // Trigger CmdlineLeavePre autocommands if not already triggered. 906 if (!s->event_cmdlineleavepre_triggered) { 907 set_vim_var_char(s->c); // Set v:char 908 trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE); 909 } 910 911 if (has_event(EVENT_CMDLINELEAVE)) { 912 save_v_event_T save_v_event; 913 dict_T *dict = get_v_event(&save_v_event); 914 915 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); 916 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); 917 tv_dict_set_keys_readonly(dict); 918 // not readonly: 919 tv_dict_add_bool(dict, S_LEN("abort"), 920 s->gotesc ? kBoolVarTrue : kBoolVarFalse); 921 set_vim_var_char(s->c); // Set v:char 922 TRY_WRAP(&err, { 923 apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf); 924 // error printed below, to avoid redraw issues 925 }); 926 if (tv_dict_get_number(dict, "abort") != 0) { 927 s->gotesc = true; 928 } 929 restore_v_event(dict, &save_v_event); 930 } 931 932 cmdmsg_rl = false; 933 934 // We could have reached here without having a chance to clean up wild menu 935 // if certain special keys like <Esc> or <C-\> were used as wildchar. Make 936 // sure to still clean up to avoid memory corruption. 937 if (cmdline_pum_active()) { 938 cmdline_pum_remove(false); 939 } else { 940 // A previous cmdline_pum_remove() may have deferred redraw. 941 pum_check_clear(); 942 } 943 wildmenu_cleanup(&ccline); 944 s->did_wild_list = false; 945 s->wim_index = 0; 946 947 ExpandCleanup(&s->xpc); 948 ccline.xpc = NULL; 949 clear_cmdline_orig(); 950 951 finish_incsearch_highlighting(s->gotesc, &s->is_state, false); 952 953 if (ccline.cmdbuff != NULL) { 954 // Put line in history buffer (":" and "=" only when it was typed). 955 if (s->histype != HIST_INVALID 956 && ccline.cmdlen 957 && s->firstc != NUL 958 && (s->some_key_typed || s->histype == HIST_SEARCH)) { 959 add_to_history(s->histype, ccline.cmdbuff, (size_t)ccline.cmdlen, true, 960 s->histype == HIST_SEARCH ? s->firstc : NUL); 961 if (s->firstc == ':') { 962 xfree(new_last_cmdline); 963 new_last_cmdline = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen); 964 } 965 } 966 967 if (s->gotesc) { 968 abandon_cmdline(); 969 } 970 } 971 972 // If the screen was shifted up, redraw the whole screen (later). 973 // If the line is too long, clear it, so ruler and shown command do 974 // not get printed in the middle of it. 975 msg_check(); 976 if (p_ch == 0 && !ui_has(kUIMessages)) { 977 set_must_redraw(UPD_VALID); 978 } 979 msg_scroll = s->save_msg_scroll; 980 redir_off = false; 981 982 if (ERROR_SET(&err)) { 983 if (!ui_has(kUIMessages)) { 984 msg_putchar('\n'); 985 } 986 emsg(err.msg); 987 did_emsg = false; 988 api_clear_error(&err); 989 } 990 991 // When the command line was typed, no need for a wait-return prompt. 992 if (s->some_key_typed && !ERROR_SET(&err)) { 993 need_wait_return = false; 994 } 995 996 set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE); 997 State = s->save_State; 998 if (cmdpreview != save_cmdpreview) { 999 cmdpreview = save_cmdpreview; // restore preview state 1000 redraw_all_later(UPD_SOME_VALID); 1001 } 1002 may_trigger_modechanged(); 1003 setmouse(); 1004 sb_text_end_cmdline(); 1005 1006 theend: 1007 xfree(s->save_p_icm); 1008 xfree(ccline.last_colors.cmdbuff); 1009 kv_destroy(ccline.last_colors.colors); 1010 1011 char *p = ccline.cmdbuff; 1012 1013 if (ui_has(kUICmdline)) { 1014 if (exmode_active) { 1015 ui_ext_cmdline_block_append(0, p); 1016 } 1017 ui_ext_cmdline_hide(s->gotesc); 1018 } 1019 if (!cmd_silent) { 1020 redraw_custom_title_later(); 1021 status_redraw_all(); // redraw to show mode change 1022 } 1023 1024 cmdline_level--; 1025 1026 if (did_save_ccline) { 1027 restore_cmdline(&save_ccline); 1028 } else { 1029 ccline.cmdbuff = NULL; 1030 } 1031 1032 xfree(s->prev_cmdbuff); 1033 return (uint8_t *)p; 1034 } 1035 1036 static int command_line_check(VimState *state) 1037 { 1038 CommandLineState *s = (CommandLineState *)state; 1039 1040 s->prev_cmdpos = ccline.cmdpos; 1041 XFREE_CLEAR(s->prev_cmdbuff); 1042 1043 redir_off = true; // Don't redirect the typed command. 1044 // Repeated, because a ":redir" inside 1045 // completion may switch it on. 1046 quit_more = false; // reset after CTRL-D which had a more-prompt 1047 1048 did_emsg = false; // There can't really be a reason why an error 1049 // that occurs while typing a command should 1050 // cause the command not to be executed. 1051 1052 if (ex_normal_busy == 0 && stuff_empty() && typebuf.tb_len == 0) { 1053 // There is no pending input from sources other than user input, so 1054 // Vim is going to wait for the user to type a key. Consider the 1055 // command line typed even if next key will trigger a mapping. 1056 s->some_key_typed = true; 1057 } 1058 1059 // Trigger SafeState if nothing is pending. 1060 may_trigger_safestate(s->xpc.xp_numfiles <= 0); 1061 1062 if (ccline.cmdbuff != NULL) { 1063 s->prev_cmdbuff = xstrdup(ccline.cmdbuff); 1064 } 1065 1066 // Defer screen update to avoid pum flicker during wildtrigger() 1067 if (s->c == K_WILD && s->firstc != '@') { 1068 s->skip_pum_redraw = true; 1069 } 1070 1071 cursorcmd(); // set the cursor on the right spot 1072 ui_cursor_shape(); 1073 return 1; 1074 } 1075 1076 /// Handle CTRL-\ pressed in Command-line mode: 1077 /// - CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode. 1078 /// - CTRL-\ e prompts for an expression. 1079 static int command_line_handle_ctrl_bsl(CommandLineState *s) 1080 { 1081 no_mapping++; 1082 allow_keys++; 1083 s->c = plain_vgetc(); 1084 no_mapping--; 1085 allow_keys--; 1086 1087 // CTRL-\ e doesn't work when obtaining an expression, unless it 1088 // is in a mapping. 1089 if (s->c != Ctrl_N 1090 && s->c != Ctrl_G 1091 && (s->c != 'e' 1092 || (ccline.cmdfirstc == '=' && KeyTyped) 1093 || cmdline_star > 0)) { 1094 vungetc(s->c); 1095 return PROCESS_NEXT_KEY; 1096 } 1097 1098 if (s->c == 'e') { 1099 // Replace the command line with the result of an expression. 1100 // This will call getcmdline() recursively in get_expr_register(). 1101 if (ccline.cmdpos == ccline.cmdlen) { 1102 new_cmdpos = 99999; // keep it at the end 1103 } else { 1104 new_cmdpos = ccline.cmdpos; 1105 } 1106 1107 s->c = get_expr_register(); 1108 if (s->c == '=') { 1109 // Evaluate the expression. Set "textlock" to avoid nasty things 1110 // like going to another buffer. 1111 textlock++; 1112 char *p = get_expr_line(); 1113 textlock--; 1114 1115 if (p != NULL) { 1116 int len = (int)strlen(p); 1117 realloc_cmdbuff(len + 1); 1118 ccline.cmdlen = len; 1119 STRCPY(ccline.cmdbuff, p); 1120 xfree(p); 1121 1122 // Restore the cursor or use the position set with 1123 // set_cmdline_pos(). 1124 ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos); 1125 1126 KeyTyped = false; // Don't do p_wc completion. 1127 redrawcmd(); 1128 return CMDLINE_CHANGED; 1129 } 1130 } 1131 beep_flush(); 1132 got_int = false; // don't abandon the command line 1133 did_emsg = false; 1134 emsg_on_display = false; 1135 redrawcmd(); 1136 return CMDLINE_NOT_CHANGED; 1137 } 1138 1139 s->gotesc = true; // will free ccline.cmdbuff after putting it in history 1140 return GOTO_NORMAL_MODE; 1141 } 1142 1143 /// Completion for 'wildchar' or 'wildcharm' key. 1144 /// - hitting <ESC> twice means: abandon command line. 1145 /// - wildcard expansion is only done when the 'wildchar' key is really 1146 /// typed, not when it comes from a macro 1147 /// @return CMDLINE_CHANGED if command line is changed or CMDLINE_NOT_CHANGED. 1148 static int command_line_wildchar_complete(CommandLineState *s) 1149 { 1150 int res; 1151 int options = WILD_NO_BEEP; 1152 bool escape = s->firstc != '@'; 1153 bool redraw_if_menu_empty = s->c == K_WILD; 1154 bool wim_noselect = p_wmnu && (wim_flags[0] & kOptWimFlagNoselect) != 0; 1155 1156 if (wim_flags[s->wim_index] & kOptWimFlagLastused) { 1157 options |= WILD_BUFLASTUSED; 1158 } 1159 if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice 1160 // If "list" is present, list matches unless already listed 1161 if (s->xpc.xp_numfiles > 1 1162 && !s->did_wild_list 1163 && (wim_flags[s->wim_index] & kOptWimFlagList)) { 1164 showmatches(&s->xpc, false, true, wim_noselect); 1165 redrawcmd(); 1166 s->did_wild_list = true; 1167 } 1168 if (wim_flags[s->wim_index] & kOptWimFlagLongest) { 1169 res = nextwild(&s->xpc, WILD_LONGEST, options, escape); 1170 } else if (wim_flags[s->wim_index] & kOptWimFlagFull) { 1171 res = nextwild(&s->xpc, WILD_NEXT, options, escape); 1172 } else { 1173 res = OK; // don't insert 'wildchar' now 1174 } 1175 } else { // typed p_wc first time 1176 bool wim_longest = (wim_flags[0] & kOptWimFlagLongest); 1177 bool wim_list = (wim_flags[0] & kOptWimFlagList); 1178 bool wim_full = (wim_flags[0] & kOptWimFlagFull); 1179 1180 s->wim_index = 0; 1181 if (s->c == p_wc || s->c == p_wcm || s->c == K_WILD || s->c == Ctrl_Z) { 1182 options |= WILD_MAY_EXPAND_PATTERN; 1183 if (s->c == K_WILD) { 1184 options |= WILD_FUNC_TRIGGER; 1185 } 1186 s->xpc.xp_pre_incsearch_pos = s->is_state.search_start; 1187 } 1188 int cmdpos_before = ccline.cmdpos; 1189 1190 // if 'wildmode' first contains "longest", get longest 1191 // common part 1192 if (wim_longest) { 1193 res = nextwild(&s->xpc, WILD_LONGEST, options, escape); 1194 } else { 1195 if (wim_noselect || wim_list) { 1196 options |= WILD_NOSELECT; 1197 } 1198 res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, escape); 1199 } 1200 1201 // Remove popup menu if no completion items are available 1202 if (redraw_if_menu_empty && s->xpc.xp_numfiles <= 0) { 1203 pum_check_clear(); 1204 } 1205 1206 // if interrupted while completing, behave like it failed 1207 if (got_int) { 1208 vpeekc(); // remove <C-C> from input stream 1209 got_int = false; // don't abandon the command line 1210 ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); 1211 s->xpc.xp_context = EXPAND_NOTHING; 1212 return CMDLINE_CHANGED; 1213 } 1214 1215 // Display matches 1216 if (res == OK && s->xpc.xp_numfiles > (wim_noselect ? 0 : 1)) { 1217 if (wim_longest) { 1218 bool found_longest_prefix = (ccline.cmdpos != cmdpos_before); 1219 if (wim_list || (p_wmnu && wim_full)) { 1220 showmatches(&s->xpc, p_wmnu, wim_list, true); 1221 } else if (!found_longest_prefix) { 1222 bool wim_list_next = (wim_flags[1] & kOptWimFlagList); 1223 bool wim_full_next = (wim_flags[1] & kOptWimFlagFull); 1224 bool wim_noselect_next = (wim_flags[1] & kOptWimFlagNoselect); 1225 if (wim_list_next || (p_wmnu && (wim_full_next || wim_noselect_next))) { 1226 if (wim_full_next && !wim_noselect_next) { 1227 nextwild(&s->xpc, WILD_NEXT, options, escape); 1228 } else { 1229 showmatches(&s->xpc, p_wmnu, wim_list_next, wim_noselect_next); 1230 } 1231 if (wim_list_next) { 1232 s->did_wild_list = true; 1233 } 1234 } 1235 } 1236 } else { 1237 if (wim_list || (p_wmnu && (wim_full || wim_noselect))) { 1238 showmatches(&s->xpc, p_wmnu, wim_list, wim_noselect); 1239 } else { 1240 vim_beep(kOptBoFlagWildmode); 1241 } 1242 } 1243 1244 redrawcmd(); 1245 if (wim_list) { 1246 s->did_wild_list = true; 1247 } 1248 } else if (s->xpc.xp_numfiles == -1) { 1249 s->xpc.xp_context = EXPAND_NOTHING; 1250 } 1251 } 1252 1253 if (s->wim_index < 3) { 1254 s->wim_index++; 1255 } 1256 1257 if (s->c == ESC) { 1258 s->gotesc = true; 1259 } 1260 1261 return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED; 1262 } 1263 1264 static void command_line_end_wildmenu(CommandLineState *s, bool key_is_wc) 1265 { 1266 if (cmdline_pum_active()) { 1267 s->skip_pum_redraw = (s->skip_pum_redraw && !key_is_wc 1268 && !ascii_iswhite(s->c) 1269 && (vim_isprintc(s->c) 1270 || s->c == K_BS || s->c == Ctrl_H || s->c == K_DEL 1271 || s->c == K_KDEL || s->c == Ctrl_W || s->c == Ctrl_U)); 1272 cmdline_pum_remove(s->skip_pum_redraw); 1273 } 1274 if (s->xpc.xp_numfiles != -1) { 1275 ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); 1276 } 1277 s->did_wild_list = false; 1278 if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { 1279 s->xpc.xp_context = EXPAND_NOTHING; 1280 } 1281 s->wim_index = 0; 1282 wildmenu_cleanup(&ccline); 1283 } 1284 1285 static int command_line_execute(VimState *state, int key) 1286 { 1287 if (key == K_IGNORE || key == K_NOP) { 1288 return -1; // get another key 1289 } 1290 1291 disptick_T display_tick_saved = display_tick; 1292 CommandLineState *s = (CommandLineState *)state; 1293 s->c = key; 1294 1295 // Skip wildmenu during history navigation via Up/Down keys 1296 if (s->c == K_WILD && s->did_hist_navigate) { 1297 s->did_hist_navigate = false; 1298 return 1; 1299 } 1300 1301 if (s->c == K_EVENT || s->c == K_COMMAND || s->c == K_LUA) { 1302 if (s->c == K_EVENT) { 1303 state_handle_k_event(); 1304 } else if (s->c == K_COMMAND) { 1305 do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT); 1306 } else { 1307 map_execute_lua(false, false); 1308 } 1309 // If the window changed incremental search state is not valid. 1310 if (s->is_state.winid != curwin->handle) { 1311 init_incsearch_state(&s->is_state); 1312 } 1313 // Re-apply 'incsearch' highlighting in case it was cleared. 1314 if (display_tick > display_tick_saved && s->is_state.did_incsearch) { 1315 may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); 1316 } 1317 1318 // nvim_select_popupmenu_item() can be called from the handling of 1319 // K_EVENT, K_COMMAND, or K_LUA. 1320 if (pum_want.active) { 1321 if (cmdline_pum_active()) { 1322 nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@'); 1323 if (pum_want.finish) { 1324 nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@'); 1325 command_line_end_wildmenu(s, false); 1326 } 1327 } 1328 pum_want.active = false; 1329 } 1330 1331 if (!cmdline_was_last_drawn) { 1332 redrawcmdline(); 1333 } 1334 return 1; 1335 } 1336 1337 if (KeyTyped) { 1338 s->some_key_typed = true; 1339 1340 if (cmdmsg_rl && !KeyStuffed) { 1341 // Invert horizontal movements and operations. Only when 1342 // typed by the user directly, not when the result of a 1343 // mapping. 1344 switch (s->c) { 1345 case K_RIGHT: 1346 s->c = K_LEFT; break; 1347 case K_S_RIGHT: 1348 s->c = K_S_LEFT; break; 1349 case K_C_RIGHT: 1350 s->c = K_C_LEFT; break; 1351 case K_LEFT: 1352 s->c = K_RIGHT; break; 1353 case K_S_LEFT: 1354 s->c = K_S_RIGHT; break; 1355 case K_C_LEFT: 1356 s->c = K_C_RIGHT; break; 1357 } 1358 } 1359 } 1360 1361 // Ignore got_int when CTRL-C was typed here. 1362 // Don't ignore it in :global, we really need to break then, e.g., for 1363 // ":g/pat/normal /pat" (without the <CR>). 1364 // Don't ignore it for the input() function. 1365 if ((s->c == Ctrl_C) 1366 && s->firstc != '@' 1367 // do clear got_int in Ex mode to avoid infinite Ctrl-C loop 1368 && (!s->break_ctrl_c || exmode_active) 1369 && !global_busy) { 1370 got_int = false; 1371 } 1372 1373 // free old command line when finished moving around in the history 1374 // list 1375 if (s->lookfor != NULL 1376 && s->c != K_S_DOWN && s->c != K_S_UP 1377 && s->c != K_DOWN && s->c != K_UP 1378 && s->c != K_PAGEDOWN && s->c != K_PAGEUP 1379 && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP 1380 && s->c != K_LEFT && s->c != K_RIGHT 1381 && (s->xpc.xp_numfiles > 0 || (s->c != Ctrl_P && s->c != Ctrl_N))) { 1382 XFREE_CLEAR(s->lookfor); 1383 s->lookforlen = 0; 1384 } 1385 1386 // When there are matching completions to select <S-Tab> works like 1387 // CTRL-P (unless 'wc' is <S-Tab>). 1388 if (s->c != p_wc && s->c == K_S_TAB && s->xpc.xp_numfiles > 0) { 1389 s->c = Ctrl_P; 1390 } 1391 1392 if (p_wmnu) { 1393 s->c = wildmenu_translate_key(&ccline, s->c, &s->xpc, s->did_wild_list); 1394 } 1395 1396 int wild_type = 0; 1397 const bool key_is_wc = (s->c == p_wc && KeyTyped) || s->c == p_wcm; 1398 if ((cmdline_pum_active() || wild_menu_showing || s->did_wild_list) 1399 && !key_is_wc && s->xpc.xp_numfiles > 0) { 1400 // Ctrl-Y: Accept the current selection and close the popup menu. 1401 // Ctrl-E: cancel the cmdline popup menu and return the original text. 1402 if (s->c == Ctrl_E || s->c == Ctrl_Y) { 1403 wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; 1404 nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@'); 1405 } 1406 } 1407 1408 // Trigger CmdlineLeavePre autocommand 1409 if ((KeyTyped && (s->c == '\n' || s->c == '\r' || s->c == K_KENTER || s->c == ESC)) 1410 || s->c == Ctrl_C) { 1411 set_vim_var_char(s->c); // Set v:char 1412 trigger_cmd_autocmd(s->cmdline_type, EVENT_CMDLINELEAVEPRE); 1413 s->event_cmdlineleavepre_triggered = true; 1414 if ((s->c == ESC || s->c == Ctrl_C) && (wim_flags[0] & kOptWimFlagList)) { 1415 set_no_hlsearch(true); 1416 } 1417 } 1418 1419 // The wildmenu is cleared if the pressed key is not used for 1420 // navigating the wild menu (i.e. the key is not 'wildchar' or 1421 // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L). 1422 // If the popup menu is displayed, then PageDown and PageUp keys are 1423 // also used to navigate the menu. 1424 bool end_wildmenu = (!key_is_wc && s->c != Ctrl_Z 1425 && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A 1426 && s->c != Ctrl_L); 1427 end_wildmenu = end_wildmenu && (!cmdline_pum_active() 1428 || (s->c != K_PAGEDOWN && s->c != K_PAGEUP 1429 && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP)); 1430 1431 // free expanded names when finished walking through matches 1432 if (end_wildmenu) { 1433 command_line_end_wildmenu(s, key_is_wc); 1434 } 1435 1436 if (p_wmnu) { 1437 s->c = wildmenu_process_key(&ccline, s->c, &s->xpc); 1438 } 1439 1440 // CTRL-\ CTRL-N or CTRL-\ CTRL-G goes to Normal mode, 1441 // CTRL-\ e prompts for an expression. 1442 if (s->c == Ctrl_BSL) { 1443 switch (command_line_handle_ctrl_bsl(s)) { 1444 case CMDLINE_CHANGED: 1445 return command_line_changed(s); 1446 case CMDLINE_NOT_CHANGED: 1447 return command_line_not_changed(s); 1448 case GOTO_NORMAL_MODE: 1449 return 0; // back to cmd mode 1450 default: 1451 s->c = Ctrl_BSL; // backslash key not processed by 1452 // command_line_handle_ctrl_bsl() 1453 } 1454 } 1455 1456 if (s->c == cedit_key || s->c == K_CMDWIN) { 1457 // TODO(vim): why is ex_normal_busy checked here? 1458 if ((s->c == K_CMDWIN || ex_normal_busy == 0) 1459 && got_int == false) { 1460 // Open a window to edit the command line (and history). 1461 s->c = open_cmdwin(); 1462 s->some_key_typed = true; 1463 } 1464 } else { 1465 s->c = do_digraph(s->c); 1466 } 1467 1468 if (s->c == '\n' 1469 || s->c == '\r' 1470 || s->c == K_KENTER 1471 || (s->c == ESC 1472 && (!KeyTyped || vim_strchr(p_cpo, CPO_ESC) != NULL))) { 1473 // In Ex mode a backslash escapes a newline. 1474 if (exmode_active 1475 && s->c != ESC 1476 && ccline.cmdpos == ccline.cmdlen 1477 && ccline.cmdpos > 0 1478 && ccline.cmdbuff[ccline.cmdpos - 1] == '\\') { 1479 if (s->c == K_KENTER) { 1480 s->c = '\n'; 1481 } 1482 } else { 1483 s->gotesc = false; // Might have typed ESC previously, don't 1484 // truncate the cmdline now. 1485 if (ccheck_abbr(s->c + ABBR_OFF)) { 1486 return command_line_changed(s); 1487 } 1488 1489 if (!cmd_silent) { 1490 if (!ui_has(kUICmdline)) { 1491 msg_cursor_goto(msg_row, 0); 1492 } 1493 ui_flush(); 1494 } 1495 return 0; 1496 } 1497 } 1498 1499 // Completion for 'wildchar', 'wildcharm', and wildtrigger() 1500 if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm || s->c == K_WILD 1501 || s->c == Ctrl_Z) { 1502 if (s->c == K_WILD) { 1503 emsg_silent++; // Silence the bell 1504 } 1505 int res = command_line_wildchar_complete(s); 1506 if (s->c == K_WILD) { 1507 emsg_silent--; 1508 } 1509 if (res == CMDLINE_CHANGED) { 1510 return command_line_changed(s); 1511 } 1512 if (s->c == K_WILD) { 1513 return command_line_not_changed(s); 1514 } 1515 } 1516 1517 s->gotesc = false; 1518 1519 // <S-Tab> goes to last match, in a clumsy way 1520 if (s->c == K_S_TAB && KeyTyped) { 1521 if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) { 1522 if (s->xpc.xp_numfiles > 1 1523 && ((!s->did_wild_list && (wim_flags[s->wim_index] & kOptWimFlagList)) || p_wmnu)) { 1524 // Trigger the popup menu when wildoptions=pum 1525 showmatches(&s->xpc, p_wmnu, wim_flags[s->wim_index] & kOptWimFlagList, 1526 wim_flags[0] & kOptWimFlagNoselect); 1527 } 1528 nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@'); 1529 nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@'); 1530 return command_line_changed(s); 1531 } 1532 } 1533 1534 if (s->c == NUL || s->c == K_ZERO) { 1535 // NUL is stored as NL 1536 s->c = NL; 1537 } 1538 1539 s->do_abbr = true; // default: check for abbreviation 1540 1541 // If already used to cancel/accept wildmenu, don't process the key further. 1542 if (wild_type == WILD_CANCEL || wild_type == WILD_APPLY) { 1543 // Apply search highlighting 1544 if (s->is_state.winid != curwin->handle) { 1545 init_incsearch_state(&s->is_state); 1546 } 1547 if (KeyTyped || vpeekc() == NUL) { 1548 may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); 1549 } 1550 return command_line_not_changed(s); 1551 } 1552 1553 return command_line_handle_key(s); 1554 } 1555 1556 // May adjust 'incsearch' highlighting for typing CTRL-G and CTRL-T, go to next 1557 // or previous match. 1558 // Returns FAIL when calling command_line_not_changed. 1559 static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_state_T *s, 1560 bool next_match) 1561 FUNC_ATTR_NONNULL_ALL 1562 { 1563 int skiplen, patlen, search_delim; 1564 1565 // Parsing range may already set the last search pattern. 1566 // NOTE: must call restore_last_search_pattern() before returning! 1567 save_last_search_pattern(); 1568 1569 if (!do_incsearch_highlighting(firstc, &search_delim, s, &skiplen, 1570 &patlen)) { 1571 restore_last_search_pattern(); 1572 return OK; 1573 } 1574 if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL) { 1575 restore_last_search_pattern(); 1576 return FAIL; 1577 } 1578 1579 ui_busy_start(); 1580 ui_flush(); 1581 1582 pos_T t; 1583 char *pat; 1584 int search_flags = SEARCH_NOOF; 1585 1586 if (search_delim == ccline.cmdbuff[skiplen]) { 1587 pat = last_search_pattern(); 1588 if (pat == NULL) { 1589 restore_last_search_pattern(); 1590 return FAIL; 1591 } 1592 skiplen = 0; 1593 patlen = (int)last_search_pattern_len(); 1594 } else { 1595 pat = ccline.cmdbuff + skiplen; 1596 } 1597 1598 bool bslsh = false; 1599 // do not search for the search end delimiter, 1600 // unless it is part of the pattern 1601 if (patlen > 2 && firstc == pat[patlen - 1]) { 1602 patlen--; 1603 if (pat[patlen - 1] == '\\') { 1604 pat[patlen - 1] = (char)(uint8_t)firstc; 1605 bslsh = true; 1606 } 1607 } 1608 1609 if (next_match) { 1610 t = s->match_end; 1611 if (lt(s->match_start, s->match_end)) { 1612 // start searching at the end of the match 1613 // not at the beginning of the next column 1614 decl(&t); 1615 } 1616 search_flags += SEARCH_COL; 1617 } else { 1618 t = s->match_start; 1619 } 1620 if (!p_hls) { 1621 search_flags += SEARCH_KEEP; 1622 } 1623 emsg_off++; 1624 char save = pat[patlen]; 1625 pat[patlen] = NUL; 1626 int found = searchit(curwin, curbuf, &t, NULL, 1627 next_match ? FORWARD : BACKWARD, 1628 pat, (size_t)patlen, count, search_flags, 1629 RE_SEARCH, NULL); 1630 emsg_off--; 1631 pat[patlen] = save; 1632 if (bslsh) { 1633 pat[patlen - 1] = '\\'; 1634 } 1635 ui_busy_stop(); 1636 if (found) { 1637 s->search_start = s->match_start; 1638 s->match_end = t; 1639 s->match_start = t; 1640 if (!next_match && firstc != '?') { 1641 // move just before the current match, so that 1642 // when nv_search finishes the cursor will be 1643 // put back on the match 1644 s->search_start = t; 1645 decl(&s->search_start); 1646 } else if (next_match && firstc == '?') { 1647 // move just after the current match, so that 1648 // when nv_search finishes the cursor will be 1649 // put back on the match 1650 s->search_start = t; 1651 incl(&s->search_start); 1652 } 1653 if (lt(t, s->search_start) && next_match) { 1654 // wrap around 1655 s->search_start = t; 1656 if (firstc == '?') { 1657 incl(&s->search_start); 1658 } else { 1659 decl(&s->search_start); 1660 } 1661 } 1662 1663 set_search_match(&s->match_end); 1664 curwin->w_cursor = s->match_start; 1665 changed_cline_bef_curs(curwin); 1666 update_topline(curwin); 1667 validate_cursor(curwin); 1668 highlight_match = true; 1669 save_viewstate(curwin, &s->old_viewstate); 1670 redraw_later(curwin, UPD_NOT_VALID); 1671 update_screen(); 1672 highlight_match = false; 1673 redrawcmdline(); 1674 curwin->w_cursor = s->match_end; 1675 } else { 1676 vim_beep(kOptBoFlagError); 1677 } 1678 restore_last_search_pattern(); 1679 return FAIL; 1680 } 1681 1682 /// Handle backspace, delete and CTRL-W keys in the command-line mode. 1683 static int command_line_erase_chars(CommandLineState *s) 1684 { 1685 if (s->c == K_KDEL) { 1686 s->c = K_DEL; 1687 } 1688 1689 // Delete current character is the same as backspace on next 1690 // character, except at end of line 1691 if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) { 1692 ccline.cmdpos++; 1693 } 1694 if (s->c == K_DEL) { 1695 ccline.cmdpos += mb_off_next(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos); 1696 } 1697 1698 if (ccline.cmdpos > 0) { 1699 int j = ccline.cmdpos; 1700 char *p = mb_prevptr(ccline.cmdbuff, ccline.cmdbuff + j); 1701 1702 if (s->c == Ctrl_W) { 1703 while (p > ccline.cmdbuff && ascii_isspace(*p)) { 1704 p = mb_prevptr(ccline.cmdbuff, p); 1705 } 1706 1707 int i = mb_get_class(p); 1708 while (p > ccline.cmdbuff && mb_get_class(p) == i) { 1709 p = mb_prevptr(ccline.cmdbuff, p); 1710 } 1711 1712 if (mb_get_class(p) != i) { 1713 p += utfc_ptr2len(p); 1714 } 1715 } 1716 1717 ccline.cmdpos = (int)(p - ccline.cmdbuff); 1718 ccline.cmdlen -= j - ccline.cmdpos; 1719 int i = ccline.cmdpos; 1720 1721 while (i < ccline.cmdlen) { 1722 ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; 1723 } 1724 1725 // Truncate at the end, required for multi-byte chars. 1726 ccline.cmdbuff[ccline.cmdlen] = NUL; 1727 if (ccline.cmdlen == 0) { 1728 s->is_state.search_start = s->is_state.save_cursor; 1729 // save view settings, so that the screen won't be restored at the 1730 // wrong position 1731 s->is_state.old_viewstate = s->is_state.init_viewstate; 1732 } 1733 redrawcmd(); 1734 } else if (ccline.cmdlen == 0 && s->c != Ctrl_W 1735 && ccline.cmdprompt == NULL && s->indent == 0) { 1736 // In ex and debug mode it doesn't make sense to return. 1737 if (exmode_active || ccline.cmdfirstc == '>') { 1738 return CMDLINE_NOT_CHANGED; 1739 } 1740 1741 dealloc_cmdbuff(); // no commandline to return 1742 1743 if (!cmd_silent && !ui_has(kUICmdline)) { 1744 msg_col = 0; 1745 msg_putchar(' '); // delete ':' 1746 } 1747 s->is_state.search_start = s->is_state.save_cursor; 1748 redraw_cmdline = true; 1749 return GOTO_NORMAL_MODE; 1750 } 1751 return CMDLINE_CHANGED; 1752 } 1753 1754 /// Handle the CTRL-^ key in the command-line mode and toggle the use of the 1755 /// language :lmap mappings and/or Input Method. 1756 static void command_line_toggle_langmap(CommandLineState *s) 1757 { 1758 OptInt *b_im_ptr = buf_valid(s->b_im_ptr_buf) ? s->b_im_ptr : NULL; 1759 if (map_to_exists_mode("", MODE_LANGMAP, false)) { 1760 // ":lmap" mappings exists, toggle use of mappings. 1761 State ^= MODE_LANGMAP; 1762 if (b_im_ptr != NULL) { 1763 if (State & MODE_LANGMAP) { 1764 *b_im_ptr = B_IMODE_LMAP; 1765 } else { 1766 *b_im_ptr = B_IMODE_NONE; 1767 } 1768 } 1769 } 1770 1771 if (b_im_ptr != NULL) { 1772 if (b_im_ptr == &curbuf->b_p_iminsert) { 1773 set_iminsert_global(curbuf); 1774 } else { 1775 set_imsearch_global(curbuf); 1776 } 1777 } 1778 ui_cursor_shape(); // may show different cursor shape 1779 // Show/unshow value of 'keymap' in status lines later. 1780 status_redraw_curbuf(); 1781 } 1782 1783 /// Handle the CTRL-R key in the command-line mode and insert the contents of a 1784 /// numbered or named register. 1785 static int command_line_insert_reg(CommandLineState *s) 1786 { 1787 const int save_new_cmdpos = new_cmdpos; 1788 1789 putcmdline('"', true); 1790 no_mapping++; 1791 allow_keys++; 1792 int i = s->c = plain_vgetc(); // CTRL-R <char> 1793 if (i == Ctrl_O) { 1794 i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R 1795 } 1796 1797 if (i == Ctrl_R) { 1798 s->c = plain_vgetc(); // CTRL-R CTRL-R <char> 1799 } 1800 no_mapping--; 1801 allow_keys--; 1802 // Insert the result of an expression. 1803 new_cmdpos = -1; 1804 if (s->c == '=') { 1805 if (ccline.cmdfirstc == '=' // can't do this recursively 1806 || cmdline_star > 0) { // or when typing a password 1807 beep_flush(); 1808 s->c = ESC; 1809 } else { 1810 s->c = get_expr_register(); 1811 } 1812 } 1813 1814 bool literally = false; 1815 if (s->c != ESC) { // use ESC to cancel inserting register 1816 literally = i == Ctrl_R || is_literal_register(s->c); 1817 cmdline_paste(s->c, literally, false); 1818 1819 // When there was a serious error abort getting the 1820 // command line. 1821 if (aborting()) { 1822 s->gotesc = true; // will free ccline.cmdbuff after 1823 // putting it in history 1824 return GOTO_NORMAL_MODE; 1825 } 1826 KeyTyped = false; // Don't do p_wc completion. 1827 if (new_cmdpos >= 0) { 1828 // set_cmdline_pos() was used 1829 ccline.cmdpos = MIN(ccline.cmdlen, new_cmdpos); 1830 } 1831 } 1832 new_cmdpos = save_new_cmdpos; 1833 1834 // remove the double quote 1835 ccline.special_char = NUL; 1836 redrawcmd(); 1837 1838 // With "literally": the command line has already changed. 1839 // Else: the text has been stuffed, but the command line didn't change yet. 1840 return literally ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED; 1841 } 1842 1843 /// Handle the Left and Right mouse clicks in the command-line mode. 1844 static void command_line_left_right_mouse(CommandLineState *s) 1845 { 1846 if (s->c == K_LEFTRELEASE || s->c == K_RIGHTRELEASE) { 1847 s->ignore_drag_release = true; 1848 } else { 1849 s->ignore_drag_release = false; 1850 } 1851 1852 ccline.cmdspos = cmd_startcol(); 1853 for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; 1854 ccline.cmdpos++) { 1855 int cells = cmdline_charsize(ccline.cmdpos); 1856 if (mouse_row <= cmdline_row + ccline.cmdspos / Columns 1857 && mouse_col < ccline.cmdspos % Columns + cells) { 1858 break; 1859 } 1860 1861 // Count ">" for double-wide char that doesn't fit. 1862 correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos); 1863 ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; 1864 ccline.cmdspos += cells; 1865 } 1866 } 1867 1868 static void command_line_next_histidx(CommandLineState *s, bool next_match) 1869 { 1870 while (true) { 1871 // one step backwards 1872 if (!next_match) { 1873 if (s->hiscnt == get_hislen()) { 1874 // first time 1875 s->hiscnt = *get_hisidx(s->histype); 1876 } else if (s->hiscnt == 0 && *get_hisidx(s->histype) != get_hislen() - 1) { 1877 s->hiscnt = get_hislen() - 1; 1878 } else if (s->hiscnt != *get_hisidx(s->histype) + 1) { 1879 s->hiscnt--; 1880 } else { 1881 // at top of list 1882 s->hiscnt = s->save_hiscnt; 1883 break; 1884 } 1885 } else { // one step forwards 1886 // on last entry, clear the line 1887 if (s->hiscnt == *get_hisidx(s->histype)) { 1888 s->hiscnt = get_hislen(); 1889 break; 1890 } 1891 1892 // not on a history line, nothing to do 1893 if (s->hiscnt == get_hislen()) { 1894 break; 1895 } 1896 1897 if (s->hiscnt == get_hislen() - 1) { 1898 // wrap around 1899 s->hiscnt = 0; 1900 } else { 1901 s->hiscnt++; 1902 } 1903 } 1904 1905 if (s->hiscnt < 0 || get_histentry(s->histype)[s->hiscnt].hisstr == NULL) { 1906 s->hiscnt = s->save_hiscnt; 1907 break; 1908 } 1909 1910 if ((s->c != K_UP && s->c != K_DOWN) 1911 || s->hiscnt == s->save_hiscnt 1912 || strncmp(get_histentry(s->histype)[s->hiscnt].hisstr, 1913 s->lookfor, (size_t)s->lookforlen) == 0) { 1914 break; 1915 } 1916 } 1917 } 1918 1919 /// Handle the Up, Down, Page Up, Page down, CTRL-N and CTRL-P key in the 1920 /// command-line mode. 1921 static int command_line_browse_history(CommandLineState *s) 1922 { 1923 if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) { 1924 // no history 1925 return CMDLINE_NOT_CHANGED; 1926 } 1927 1928 s->save_hiscnt = s->hiscnt; 1929 1930 // save current command string so it can be restored later 1931 if (s->lookfor == NULL) { 1932 s->lookfor = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen); 1933 s->lookfor[ccline.cmdpos] = NUL; 1934 s->lookforlen = ccline.cmdpos; 1935 } 1936 1937 bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N 1938 || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN); 1939 command_line_next_histidx(s, next_match); 1940 1941 if (s->hiscnt != s->save_hiscnt) { // jumped to other entry 1942 char *p; 1943 int plen; 1944 int old_firstc; 1945 1946 dealloc_cmdbuff(); 1947 1948 s->xpc.xp_context = EXPAND_NOTHING; 1949 if (s->hiscnt == get_hislen()) { 1950 p = s->lookfor; // back to the old one 1951 plen = s->lookforlen; 1952 } else { 1953 p = get_histentry(s->histype)[s->hiscnt].hisstr; 1954 plen = (int)get_histentry(s->histype)[s->hiscnt].hisstrlen; 1955 } 1956 1957 if (s->histype == HIST_SEARCH 1958 && p != s->lookfor 1959 && (old_firstc = (uint8_t)p[plen + 1]) != s->firstc) { 1960 int len = 0; 1961 // Correct for the separator character used when 1962 // adding the history entry vs the one used now. 1963 // First loop: count length. 1964 // Second loop: copy the characters. 1965 for (int i = 0; i <= 1; i++) { 1966 len = 0; 1967 for (int j = 0; p[j] != NUL; j++) { 1968 // Replace old sep with new sep, unless it is 1969 // escaped. 1970 if (p[j] == old_firstc 1971 && (j == 0 || p[j - 1] != '\\')) { 1972 if (i > 0) { 1973 ccline.cmdbuff[len] = (char)s->firstc; 1974 } 1975 } else { 1976 // Escape new sep, unless it is already 1977 // escaped. 1978 if (p[j] == s->firstc 1979 && (j == 0 || p[j - 1] != '\\')) { 1980 if (i > 0) { 1981 ccline.cmdbuff[len] = '\\'; 1982 } 1983 len++; 1984 } 1985 1986 if (i > 0) { 1987 ccline.cmdbuff[len] = p[j]; 1988 } 1989 } 1990 len++; 1991 } 1992 1993 if (i == 0) { 1994 alloc_cmdbuff(len); 1995 } 1996 } 1997 ccline.cmdbuff[len] = NUL; 1998 ccline.cmdpos = ccline.cmdlen = len; 1999 } else { 2000 alloc_cmdbuff(plen); 2001 STRCPY(ccline.cmdbuff, p); 2002 ccline.cmdpos = ccline.cmdlen = plen; 2003 } 2004 2005 redrawcmd(); 2006 return CMDLINE_CHANGED; 2007 } 2008 beep_flush(); 2009 return CMDLINE_NOT_CHANGED; 2010 } 2011 2012 static int command_line_handle_key(CommandLineState *s) 2013 { 2014 // For one key prompt, avoid putting ESC and Ctrl_C onto cmdline. 2015 // For all other keys, just put onto cmdline and exit. 2016 if (ccline.one_key && s->c != ESC && s->c != Ctrl_C) { 2017 goto end; 2018 } 2019 2020 // Big switch for a typed command line character. 2021 switch (s->c) { 2022 case K_BS: 2023 case Ctrl_H: 2024 case K_DEL: 2025 case K_KDEL: 2026 case Ctrl_W: 2027 switch (command_line_erase_chars(s)) { 2028 case CMDLINE_NOT_CHANGED: 2029 return command_line_not_changed(s); 2030 case GOTO_NORMAL_MODE: 2031 return 0; // back to cmd mode 2032 default: 2033 return command_line_changed(s); 2034 } 2035 2036 case K_INS: 2037 case K_KINS: 2038 ccline.overstrike = !ccline.overstrike; 2039 ui_cursor_shape(); // may show different cursor shape 2040 may_trigger_modechanged(); 2041 status_redraw_curbuf(); 2042 redraw_statuslines(); 2043 return command_line_not_changed(s); 2044 2045 case Ctrl_HAT: 2046 command_line_toggle_langmap(s); 2047 return command_line_not_changed(s); 2048 2049 case Ctrl_U: { 2050 // delete all characters left of the cursor 2051 int j = ccline.cmdpos; 2052 ccline.cmdlen -= j; 2053 int i = ccline.cmdpos = 0; 2054 while (i < ccline.cmdlen) { 2055 ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; 2056 } 2057 2058 // Truncate at the end, required for multi-byte chars. 2059 ccline.cmdbuff[ccline.cmdlen] = NUL; 2060 if (ccline.cmdlen == 0) { 2061 s->is_state.search_start = s->is_state.save_cursor; 2062 } 2063 redrawcmd(); 2064 return command_line_changed(s); 2065 } 2066 2067 case ESC: // get here if p_wc != ESC or when ESC typed twice 2068 case Ctrl_C: 2069 // In exmode it doesn't make sense to return. Except when 2070 // ":normal" runs out of characters. Also when highlight callback is active 2071 // <C-c> should interrupt only it. 2072 if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) 2073 || (getln_interrupted_highlight && s->c == Ctrl_C)) { 2074 getln_interrupted_highlight = false; 2075 return command_line_not_changed(s); 2076 } 2077 2078 s->gotesc = true; // will free ccline.cmdbuff after 2079 // putting it in history 2080 return 0; // back to cmd mode 2081 2082 case Ctrl_R: // insert register 2083 switch (command_line_insert_reg(s)) { 2084 case GOTO_NORMAL_MODE: 2085 return 0; // back to cmd mode 2086 case CMDLINE_CHANGED: 2087 return command_line_changed(s); 2088 default: 2089 return command_line_not_changed(s); 2090 } 2091 2092 case Ctrl_D: 2093 if (showmatches(&s->xpc, false, true, wim_flags[0] & kOptWimFlagNoselect) 2094 == EXPAND_NOTHING) { 2095 break; // Use ^D as normal char instead 2096 } 2097 2098 redrawcmd(); 2099 return 1; // don't do incremental search now 2100 2101 case K_RIGHT: 2102 case K_S_RIGHT: 2103 case K_C_RIGHT: 2104 do { 2105 if (ccline.cmdpos >= ccline.cmdlen) { 2106 break; 2107 } 2108 2109 int cells = cmdline_charsize(ccline.cmdpos); 2110 if (KeyTyped && ccline.cmdspos + cells >= Columns * Rows) { 2111 break; 2112 } 2113 2114 ccline.cmdspos += cells; 2115 ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos); 2116 } while ((s->c == K_S_RIGHT || s->c == K_C_RIGHT 2117 || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) 2118 && ccline.cmdbuff[ccline.cmdpos] != ' '); 2119 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 2120 return command_line_not_changed(s); 2121 2122 case K_LEFT: 2123 case K_S_LEFT: 2124 case K_C_LEFT: 2125 if (ccline.cmdpos == 0) { 2126 return command_line_not_changed(s); 2127 } 2128 do { 2129 ccline.cmdpos--; 2130 // Move to first byte of possibly multibyte char. 2131 ccline.cmdpos -= utf_head_off(ccline.cmdbuff, 2132 ccline.cmdbuff + ccline.cmdpos); 2133 ccline.cmdspos -= cmdline_charsize(ccline.cmdpos); 2134 } while (ccline.cmdpos > 0 2135 && (s->c == K_S_LEFT || s->c == K_C_LEFT 2136 || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) 2137 && ccline.cmdbuff[ccline.cmdpos - 1] != ' '); 2138 2139 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 2140 if (ccline.special_char != NUL) { 2141 putcmdline(ccline.special_char, ccline.special_shift); 2142 } 2143 2144 return command_line_not_changed(s); 2145 2146 case K_IGNORE: 2147 // Ignore mouse event or open_cmdwin() result. 2148 return command_line_not_changed(s); 2149 2150 case K_MIDDLEDRAG: 2151 case K_MIDDLERELEASE: 2152 return command_line_not_changed(s); // Ignore mouse 2153 2154 case K_MIDDLEMOUSE: 2155 cmdline_paste(eval_has_provider("clipboard", false) ? '*' : 0, true, true); 2156 redrawcmd(); 2157 return command_line_changed(s); 2158 2159 case K_LEFTDRAG: 2160 case K_LEFTRELEASE: 2161 case K_RIGHTDRAG: 2162 case K_RIGHTRELEASE: 2163 // Ignore drag and release events when the button-down wasn't 2164 // seen before. 2165 if (s->ignore_drag_release) { 2166 return command_line_not_changed(s); 2167 } 2168 FALLTHROUGH; 2169 case K_LEFTMOUSE: 2170 // Return on left click above number prompt 2171 if (ccline.mouse_used && mouse_row < cmdline_row) { 2172 *ccline.mouse_used = true; 2173 return 0; 2174 } 2175 FALLTHROUGH; 2176 case K_RIGHTMOUSE: 2177 command_line_left_right_mouse(s); 2178 return command_line_not_changed(s); 2179 2180 // Mouse scroll wheel: ignored here 2181 case K_MOUSEDOWN: 2182 case K_MOUSEUP: 2183 case K_MOUSELEFT: 2184 case K_MOUSERIGHT: 2185 // Alternate buttons ignored here 2186 case K_X1MOUSE: 2187 case K_X1DRAG: 2188 case K_X1RELEASE: 2189 case K_X2MOUSE: 2190 case K_X2DRAG: 2191 case K_X2RELEASE: 2192 case K_MOUSEMOVE: 2193 return command_line_not_changed(s); 2194 2195 case K_SELECT: // end of Select mode mapping - ignore 2196 return command_line_not_changed(s); 2197 2198 case Ctrl_B: // begin of command line 2199 case K_HOME: 2200 case K_KHOME: 2201 case K_S_HOME: 2202 case K_C_HOME: 2203 ccline.cmdpos = 0; 2204 ccline.cmdspos = cmd_startcol(); 2205 return command_line_not_changed(s); 2206 2207 case Ctrl_E: // end of command line 2208 case K_END: 2209 case K_KEND: 2210 case K_S_END: 2211 case K_C_END: 2212 ccline.cmdpos = ccline.cmdlen; 2213 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 2214 return command_line_not_changed(s); 2215 2216 case Ctrl_A: // all matches 2217 if (cmdline_pum_active()) { 2218 // As Ctrl-A completes all the matches, close the popup 2219 // menu (if present) 2220 cmdline_pum_cleanup(&ccline); 2221 } 2222 2223 if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) { 2224 break; 2225 } 2226 s->xpc.xp_context = EXPAND_NOTHING; 2227 s->did_wild_list = false; 2228 return command_line_changed(s); 2229 2230 case Ctrl_L: 2231 if (may_add_char_to_search(s->firstc, &s->c, &s->is_state) == OK) { 2232 return command_line_not_changed(s); 2233 } 2234 2235 // completion: longest common part 2236 if (nextwild(&s->xpc, WILD_LONGEST, 0, s->firstc != '@') == FAIL) { 2237 break; 2238 } 2239 return command_line_changed(s); 2240 2241 case Ctrl_N: // next match 2242 case Ctrl_P: // previous match 2243 if (s->xpc.xp_numfiles > 0) { 2244 const int wild_type = (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT; 2245 if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) { 2246 break; 2247 } 2248 return command_line_changed(s); 2249 } 2250 FALLTHROUGH; 2251 2252 case K_UP: 2253 case K_DOWN: 2254 case K_S_UP: 2255 case K_S_DOWN: 2256 case K_PAGEUP: 2257 case K_KPAGEUP: 2258 case K_PAGEDOWN: 2259 case K_KPAGEDOWN: 2260 if (cmdline_pum_active() 2261 && (s->c == K_PAGEUP || s->c == K_PAGEDOWN 2262 || s->c == K_KPAGEUP || s->c == K_KPAGEDOWN)) { 2263 // If the popup menu is displayed, then PageUp and PageDown 2264 // are used to scroll the menu. 2265 const int wild_type = 2266 (s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN) ? WILD_PAGEDOWN : WILD_PAGEUP; 2267 if (nextwild(&s->xpc, wild_type, 0, s->firstc != '@') == FAIL) { 2268 break; 2269 } 2270 return command_line_changed(s); 2271 } else { 2272 switch (command_line_browse_history(s)) { 2273 case CMDLINE_CHANGED: 2274 s->did_hist_navigate = true; 2275 return command_line_changed(s); 2276 case GOTO_NORMAL_MODE: 2277 return 0; 2278 default: 2279 return command_line_not_changed(s); 2280 } 2281 } 2282 2283 case Ctrl_G: // next match 2284 case Ctrl_T: // previous match 2285 if (may_do_command_line_next_incsearch(s->firstc, s->count, &s->is_state, 2286 s->c == Ctrl_G) == FAIL) { 2287 return command_line_not_changed(s); 2288 } 2289 break; 2290 2291 case Ctrl_V: 2292 case Ctrl_Q: 2293 s->ignore_drag_release = true; 2294 putcmdline('^', true); 2295 2296 // Get next (two) characters. 2297 // Do not include modifiers into the key for CTRL-SHIFT-V. 2298 s->c = get_literal(mod_mask & MOD_MASK_SHIFT); 2299 2300 s->do_abbr = false; // don't do abbreviation now 2301 ccline.special_char = NUL; 2302 // may need to remove ^ when composing char was typed 2303 if (utf_iscomposing_first(s->c) && !cmd_silent) { 2304 if (ui_has(kUICmdline)) { 2305 // TODO(bfredl): why not make unputcmdline also work with true? 2306 unputcmdline(); 2307 } else { 2308 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); 2309 msg_putchar(' '); 2310 cursorcmd(); 2311 } 2312 } 2313 break; 2314 2315 case Ctrl_K: 2316 s->ignore_drag_release = true; 2317 putcmdline('?', true); 2318 s->c = get_digraph(true); 2319 ccline.special_char = NUL; 2320 2321 if (s->c != NUL) { 2322 break; 2323 } 2324 2325 redrawcmd(); 2326 return command_line_not_changed(s); 2327 2328 case Ctrl__: // CTRL-_: switch language mode 2329 if (!p_ari) { 2330 break; 2331 } 2332 return command_line_not_changed(s); 2333 2334 case 'q': 2335 // Number prompts use the mouse and return on 'q' press 2336 if (ccline.mouse_used) { 2337 *ccline.cmdbuff = NUL; 2338 return 0; 2339 } 2340 FALLTHROUGH; 2341 2342 default: 2343 // Normal character with no special meaning. Just set mod_mask 2344 // to 0x0 so that typing Shift-Space in the GUI doesn't enter 2345 // the string <S-Space>. This should only happen after ^V. 2346 if (!IS_SPECIAL(s->c)) { 2347 mod_mask = 0x0; 2348 } 2349 break; 2350 } 2351 2352 // End of switch on command line character. 2353 // We come here if we have a normal character. 2354 if (s->do_abbr && (IS_SPECIAL(s->c) || !vim_iswordc(s->c)) 2355 // Add ABBR_OFF for characters above 0x100, this is 2356 // what check_abbr() expects. 2357 && (ccheck_abbr((s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c) 2358 || s->c == Ctrl_RSB)) { 2359 return command_line_changed(s); 2360 } 2361 2362 end: 2363 // put the character in the command line 2364 if (IS_SPECIAL(s->c) || mod_mask != 0) { 2365 put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true); 2366 } else { 2367 int j = utf_char2bytes(s->c, IObuff); 2368 IObuff[j] = NUL; // exclude composing chars 2369 put_on_cmdline(IObuff, j, true); 2370 } 2371 return ccline.one_key ? 0 : command_line_changed(s); 2372 } 2373 2374 /// Trigger CursorMovedC autocommands. 2375 static void may_trigger_cursormovedc(CommandLineState *s) 2376 { 2377 if (ccline.cmdpos != s->prev_cmdpos) { 2378 trigger_cmd_autocmd(s->cmdline_type, EVENT_CURSORMOVEDC); 2379 ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos); 2380 } 2381 } 2382 2383 static int command_line_not_changed(CommandLineState *s) 2384 { 2385 may_trigger_cursormovedc(s); 2386 s->prev_cmdpos = ccline.cmdpos; 2387 // Incremental searches for "/" and "?": 2388 // Enter command_line_not_changed() when a character has been read but the 2389 // command line did not change. Then we only search and redraw if something 2390 // changed in the past. 2391 // Enter command_line_changed() when the command line did change. 2392 if (!s->is_state.incsearch_postponed) { 2393 return 1; 2394 } 2395 return command_line_changed(s); 2396 } 2397 2398 /// Guess that the pattern matches everything. Only finds specific cases, such 2399 /// as a trailing \|, which can happen while typing a pattern. 2400 static bool empty_pattern(char *p, size_t len, int delim) 2401 { 2402 magic_T magic_val = MAGIC_ON; 2403 2404 if (len > 0) { 2405 skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val); 2406 } else { 2407 return true; 2408 } 2409 2410 return empty_pattern_magic(p, len, magic_val); 2411 } 2412 2413 static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val) 2414 { 2415 // remove trailing \v and the like 2416 while (len >= 2 && p[len - 2] == '\\' 2417 && vim_strchr("mMvVcCZ", (uint8_t)p[len - 1]) != NULL) { 2418 len -= 2; 2419 } 2420 2421 // true, if the pattern is empty, or the pattern ends with \| and magic is 2422 // set (or it ends with '|' and very magic is set) 2423 return len == 0 || (len > 1 && p[len - 1] == '|' 2424 && ((p[len - 2] == '\\' && magic_val == MAGIC_ON) 2425 || (p[len - 2] != '\\' && magic_val == MAGIC_ALL))); 2426 } 2427 2428 handle_T cmdpreview_get_bufnr(void) 2429 { 2430 return cmdpreview_bufnr; 2431 } 2432 2433 int cmdpreview_get_ns(void) 2434 { 2435 return cmdpreview_ns; 2436 } 2437 2438 /// Sets up command preview buffer. 2439 /// 2440 /// @return Pointer to command preview buffer if succeeded, NULL if failed. 2441 static buf_T *cmdpreview_open_buf(void) 2442 { 2443 buf_T *cmdpreview_buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; 2444 2445 // If preview buffer doesn't exist, open one. 2446 if (cmdpreview_buf == NULL) { 2447 Error err = ERROR_INIT; 2448 handle_T bufnr = nvim_create_buf(false, true, &err); 2449 2450 if (ERROR_SET(&err)) { 2451 return NULL; 2452 } 2453 2454 cmdpreview_buf = buflist_findnr(bufnr); 2455 } 2456 2457 // Preview buffer cannot preview itself! 2458 if (cmdpreview_buf == curbuf) { 2459 return NULL; 2460 } 2461 2462 // Rename preview buffer. 2463 aco_save_T aco; 2464 aucmd_prepbuf(&aco, cmdpreview_buf); 2465 int retv = rename_buffer("[Preview]"); 2466 aucmd_restbuf(&aco); 2467 2468 if (retv == FAIL) { 2469 return NULL; 2470 } 2471 2472 // Temporarily switch to preview buffer to set it up for previewing. 2473 aucmd_prepbuf(&aco, cmdpreview_buf); 2474 buf_clear(); 2475 curbuf->b_p_ma = true; 2476 curbuf->b_p_ul = -1; 2477 curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin) 2478 aucmd_restbuf(&aco); 2479 cmdpreview_bufnr = cmdpreview_buf->handle; 2480 2481 return cmdpreview_buf; 2482 } 2483 2484 /// Open command preview window if it's not already open. 2485 /// Returns to original window after opening command preview window. 2486 /// 2487 /// @param cmdpreview_buf Pointer to command preview buffer 2488 /// 2489 /// @return Pointer to command preview window if succeeded, NULL if failed. 2490 static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf) 2491 FUNC_ATTR_NONNULL_ALL 2492 { 2493 win_T *save_curwin = curwin; 2494 2495 // Open preview window. 2496 if (win_split((int)p_cwh, WSP_BOT) == FAIL) { 2497 return NULL; 2498 } 2499 2500 win_T *preview_win = curwin; 2501 Error err = ERROR_INIT; 2502 int result = OK; 2503 2504 // Switch to preview buffer 2505 TRY_WRAP(&err, { 2506 result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0); 2507 }); 2508 if (ERROR_SET(&err) || result == FAIL) { 2509 api_clear_error(&err); 2510 return NULL; 2511 } 2512 2513 curwin->w_p_cul = false; 2514 curwin->w_p_cuc = false; 2515 curwin->w_p_spell = false; 2516 curwin->w_p_fen = false; 2517 2518 win_enter(save_curwin, false); 2519 return preview_win; 2520 } 2521 2522 /// Closes any open command preview windows. 2523 static void cmdpreview_close_win(void) 2524 { 2525 buf_T *buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL; 2526 if (buf != NULL) { 2527 close_windows(buf, false); 2528 } 2529 } 2530 2531 /// Save the undo state of a buffer for command preview. 2532 static void cmdpreview_save_undo(CpUndoInfo *cp_undoinfo, buf_T *buf) 2533 FUNC_ATTR_NONNULL_ALL 2534 { 2535 cp_undoinfo->save_b_u_synced = buf->b_u_synced; 2536 cp_undoinfo->save_b_u_oldhead = buf->b_u_oldhead; 2537 cp_undoinfo->save_b_u_newhead = buf->b_u_newhead; 2538 cp_undoinfo->save_b_u_curhead = buf->b_u_curhead; 2539 cp_undoinfo->save_b_u_numhead = buf->b_u_numhead; 2540 cp_undoinfo->save_b_u_seq_last = buf->b_u_seq_last; 2541 cp_undoinfo->save_b_u_save_nr_last = buf->b_u_save_nr_last; 2542 cp_undoinfo->save_b_u_seq_cur = buf->b_u_seq_cur; 2543 cp_undoinfo->save_b_u_time_cur = buf->b_u_time_cur; 2544 cp_undoinfo->save_b_u_save_nr_cur = buf->b_u_save_nr_cur; 2545 cp_undoinfo->save_b_u_line_ptr = buf->b_u_line_ptr; 2546 cp_undoinfo->save_b_u_line_lnum = buf->b_u_line_lnum; 2547 cp_undoinfo->save_b_u_line_colnr = buf->b_u_line_colnr; 2548 } 2549 2550 /// Restore the undo state of a buffer for command preview. 2551 static void cmdpreview_restore_undo(const CpUndoInfo *cp_undoinfo, buf_T *buf) 2552 { 2553 buf->b_u_oldhead = cp_undoinfo->save_b_u_oldhead; 2554 buf->b_u_newhead = cp_undoinfo->save_b_u_newhead; 2555 buf->b_u_curhead = cp_undoinfo->save_b_u_curhead; 2556 buf->b_u_numhead = cp_undoinfo->save_b_u_numhead; 2557 buf->b_u_seq_last = cp_undoinfo->save_b_u_seq_last; 2558 buf->b_u_save_nr_last = cp_undoinfo->save_b_u_save_nr_last; 2559 buf->b_u_seq_cur = cp_undoinfo->save_b_u_seq_cur; 2560 buf->b_u_time_cur = cp_undoinfo->save_b_u_time_cur; 2561 buf->b_u_save_nr_cur = cp_undoinfo->save_b_u_save_nr_cur; 2562 buf->b_u_line_ptr = cp_undoinfo->save_b_u_line_ptr; 2563 buf->b_u_line_lnum = cp_undoinfo->save_b_u_line_lnum; 2564 buf->b_u_line_colnr = cp_undoinfo->save_b_u_line_colnr; 2565 if (buf->b_u_curhead == NULL) { 2566 buf->b_u_synced = cp_undoinfo->save_b_u_synced; 2567 } 2568 } 2569 2570 /// Save current state and prepare windows and buffers for command preview. 2571 static void cmdpreview_prepare(CpInfo *cpinfo) 2572 FUNC_ATTR_NONNULL_ALL 2573 { 2574 Set(ptr_t) saved_bufs = SET_INIT; 2575 2576 kv_init(cpinfo->buf_info); 2577 kv_init(cpinfo->win_info); 2578 2579 FOR_ALL_WINDOWS_IN_TAB(win, curtab) { 2580 buf_T *buf = win->w_buffer; 2581 2582 // Don't save state of command preview buffer or preview window. 2583 if (buf->handle == cmdpreview_bufnr) { 2584 continue; 2585 } 2586 2587 if (!set_has(ptr_t, &saved_bufs, buf)) { 2588 CpBufInfo cp_bufinfo; 2589 cp_bufinfo.buf = buf; 2590 cp_bufinfo.save_b_p_ma = buf->b_p_ma; 2591 cp_bufinfo.save_b_p_ul = buf->b_p_ul; 2592 cp_bufinfo.save_b_changed = buf->b_changed; 2593 cp_bufinfo.save_b_op_start = buf->b_op_start; 2594 cp_bufinfo.save_b_op_end = buf->b_op_end; 2595 cp_bufinfo.save_changedtick = buf_get_changedtick(buf); 2596 cmdpreview_save_undo(&cp_bufinfo.undo_info, buf); 2597 kv_push(cpinfo->buf_info, cp_bufinfo); 2598 set_put(ptr_t, &saved_bufs, buf); 2599 2600 u_clearall(buf); 2601 buf->b_p_ul = INT_MAX; // Make sure we can undo all changes 2602 } 2603 2604 CpWinInfo cp_wininfo; 2605 cp_wininfo.win = win; 2606 2607 // Save window cursor position and viewstate 2608 cp_wininfo.save_w_cursor = win->w_cursor; 2609 save_viewstate(win, &cp_wininfo.save_viewstate); 2610 2611 // Save 'cursorline' and 'cursorcolumn' 2612 cp_wininfo.save_w_p_cul = win->w_p_cul; 2613 cp_wininfo.save_w_p_cuc = win->w_p_cuc; 2614 2615 kv_push(cpinfo->win_info, cp_wininfo); 2616 2617 win->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights 2618 win->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights 2619 } 2620 2621 set_destroy(ptr_t, &saved_bufs); 2622 2623 cpinfo->save_hls = p_hls; 2624 cpinfo->save_cmdmod = cmdmod; 2625 win_size_save(&cpinfo->save_view); 2626 save_search_patterns(); 2627 2628 p_hls = false; // Don't show search highlighting during live substitution 2629 cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers 2630 cmdmod.cmod_tab = 0; // Disable :tab modifier 2631 cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer 2632 2633 u_sync(true); 2634 } 2635 2636 /// Restore the state of buffers and windows for command preview. 2637 static void cmdpreview_restore_state(CpInfo *cpinfo) 2638 FUNC_ATTR_NONNULL_ALL 2639 { 2640 for (size_t i = 0; i < cpinfo->buf_info.size; i++) { 2641 CpBufInfo cp_bufinfo = cpinfo->buf_info.items[i]; 2642 buf_T *buf = cp_bufinfo.buf; 2643 2644 buf->b_changed = cp_bufinfo.save_b_changed; 2645 2646 // Clear preview highlights. 2647 extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL); 2648 2649 if (buf->b_u_seq_cur != cp_bufinfo.undo_info.save_b_u_seq_cur) { 2650 int count = 0; 2651 2652 // Calculate how many undo steps are necessary to restore earlier state. 2653 for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead; 2654 uhp != NULL; 2655 uhp = uhp->uh_next.ptr, ++count) {} 2656 2657 aco_save_T aco; 2658 aucmd_prepbuf(&aco, buf); 2659 // Ensure all the entries will be undone 2660 if (curbuf->b_u_synced == false) { 2661 u_sync(true); 2662 } 2663 // Undo invisibly. This also moves the cursor! 2664 if (!u_undo_and_forget(count, false)) { 2665 abort(); 2666 } 2667 aucmd_restbuf(&aco); 2668 } 2669 2670 u_blockfree(buf); 2671 cmdpreview_restore_undo(&cp_bufinfo.undo_info, buf); 2672 2673 buf->b_op_start = cp_bufinfo.save_b_op_start; 2674 buf->b_op_end = cp_bufinfo.save_b_op_end; 2675 2676 if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) { 2677 buf_set_changedtick(buf, cp_bufinfo.save_changedtick); 2678 } 2679 2680 buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels' 2681 buf->b_p_ma = cp_bufinfo.save_b_p_ma; // Restore 'modifiable' 2682 } 2683 2684 for (size_t i = 0; i < cpinfo->win_info.size; i++) { 2685 CpWinInfo cp_wininfo = cpinfo->win_info.items[i]; 2686 win_T *win = cp_wininfo.win; 2687 2688 // Restore window cursor position and viewstate 2689 win->w_cursor = cp_wininfo.save_w_cursor; 2690 restore_viewstate(win, &cp_wininfo.save_viewstate); 2691 2692 // Restore 'cursorline' and 'cursorcolumn' 2693 win->w_p_cul = cp_wininfo.save_w_p_cul; 2694 win->w_p_cuc = cp_wininfo.save_w_p_cuc; 2695 2696 update_topline(win); 2697 } 2698 2699 cmdmod = cpinfo->save_cmdmod; // Restore cmdmod 2700 p_hls = cpinfo->save_hls; // Restore 'hlsearch' 2701 restore_search_patterns(); // Restore search patterns 2702 win_size_restore(&cpinfo->save_view); // Restore window sizes 2703 2704 ga_clear(&cpinfo->save_view); 2705 kv_destroy(cpinfo->win_info); 2706 kv_destroy(cpinfo->buf_info); 2707 } 2708 2709 /// Show 'inccommand' preview if command is previewable. It works like this: 2710 /// 1. Store current undo information so we can revert to current state later. 2711 /// 2. Execute the preview callback with the parsed command, preview buffer number and preview 2712 /// namespace number as arguments. The preview callback sets the highlight and does the 2713 /// changes required for the preview if needed. 2714 /// 3. Preview callback returns 0, 1 or 2. 0 means no preview is shown. 1 means preview is shown 2715 /// but preview window doesn't need to be opened. 2 means preview is shown and preview window 2716 /// needs to be opened if inccommand=split. 2717 /// 4. Use the return value of the preview callback to determine whether to 2718 /// open the preview window or not and open preview window if needed. 2719 /// 5. If the return value of the preview callback is not 0, update the screen while the effects 2720 /// of the preview are still in place. 2721 /// 6. Revert all changes made by the preview callback. 2722 /// 2723 /// @return whether preview is shown or not. 2724 static bool cmdpreview_may_show(CommandLineState *s) 2725 { 2726 // Parse the command line and return if it fails. 2727 exarg_T ea; 2728 CmdParseInfo cmdinfo; 2729 // Copy the command line so we can modify it. 2730 int cmdpreview_type = 0; 2731 char *cmdline = xstrdup(ccline.cmdbuff); 2732 const char *errormsg = NULL; 2733 emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg 2734 if (!parse_cmdline(&cmdline, &ea, &cmdinfo, &errormsg)) { 2735 emsg_off--; 2736 goto end; 2737 } 2738 emsg_off--; 2739 2740 // Check if command is previewable, if not, don't attempt to show preview 2741 if (!(ea.argt & EX_PREVIEW)) { 2742 undo_cmdmod(&cmdinfo.cmdmod); 2743 goto end; 2744 } 2745 2746 // Cursor may be at the end of the message grid rather than at cmdspos. 2747 // Place it there in case preview callback flushes it. #30696 2748 cursorcmd(); 2749 // Flush now: external cmdline may itself wish to update the screen which is 2750 // currently disallowed during cmdpreview (no longer needed in case that changes). 2751 cmdline_ui_flush(); 2752 2753 // Swap invalid command range if needed 2754 if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) { 2755 linenr_T lnum = ea.line1; 2756 ea.line1 = ea.line2; 2757 ea.line2 = lnum; 2758 } 2759 2760 CpInfo cpinfo; 2761 bool icm_split = *p_icm == 's'; // inccommand=split 2762 buf_T *cmdpreview_buf = NULL; 2763 win_T *cmdpreview_win = NULL; 2764 2765 emsg_silent++; // Block error reporting as the command may be incomplete, 2766 // but still update v:errmsg 2767 msg_silent++; // Block messages, namely ones that prompt 2768 block_autocmds(); // Block events 2769 2770 // Save current state and prepare for command preview. 2771 cmdpreview_prepare(&cpinfo); 2772 2773 // Open preview buffer if inccommand=split. 2774 if (icm_split && (cmdpreview_buf = cmdpreview_open_buf()) == NULL) { 2775 // Failed to create preview buffer, so disable preview. 2776 set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL("nosplit"), 0, SID_NONE); 2777 icm_split = false; 2778 } 2779 // Setup preview namespace if it's not already set. 2780 if (!cmdpreview_ns) { 2781 cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT); 2782 } 2783 2784 // Set cmdpreview state. 2785 cmdpreview = true; 2786 2787 // Execute the preview callback and use its return value to determine whether to show preview or 2788 // open the preview window. The preview callback also handles doing the changes and highlights for 2789 // the preview. 2790 Error err = ERROR_INIT; 2791 TRY_WRAP(&err, { 2792 cmdpreview_type = execute_cmd(&ea, &cmdinfo, true); 2793 }); 2794 if (ERROR_SET(&err)) { 2795 api_clear_error(&err); 2796 cmdpreview_type = 0; 2797 } 2798 2799 // If inccommand=split and preview callback returns 2, open preview window. 2800 if (icm_split && cmdpreview_type == 2 2801 && (cmdpreview_win = cmdpreview_open_win(cmdpreview_buf)) == NULL) { 2802 // If there's not enough room to open the preview window, just preview without the window. 2803 cmdpreview_type = 1; 2804 } 2805 2806 // If preview callback return value is nonzero, update screen now. 2807 if (cmdpreview_type != 0) { 2808 int save_rd = RedrawingDisabled; 2809 RedrawingDisabled = 0; 2810 update_screen(); 2811 RedrawingDisabled = save_rd; 2812 } 2813 2814 // Close preview window if it's open. 2815 if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) { 2816 cmdpreview_close_win(); 2817 } 2818 2819 // Restore state. 2820 cmdpreview_restore_state(&cpinfo); 2821 2822 unblock_autocmds(); // Unblock events 2823 msg_silent--; // Unblock messages 2824 emsg_silent--; // Unblock error reporting 2825 redrawcmdline(); 2826 end: 2827 xfree(cmdline); 2828 return cmdpreview_type != 0; 2829 } 2830 2831 /// Trigger CmdlineChanged autocommands. 2832 static void do_autocmd_cmdlinechanged(int firstc) 2833 { 2834 if (has_event(EVENT_CMDLINECHANGED)) { 2835 Error err = ERROR_INIT; 2836 save_v_event_T save_v_event; 2837 dict_T *dict = get_v_event(&save_v_event); 2838 2839 char firstcbuf[2]; 2840 firstcbuf[0] = (char)firstc; 2841 firstcbuf[1] = 0; 2842 2843 // set v:event to a dictionary with information about the commandline 2844 tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); 2845 tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); 2846 tv_dict_set_keys_readonly(dict); 2847 TRY_WRAP(&err, { 2848 apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf); 2849 restore_v_event(dict, &save_v_event); 2850 }); 2851 if (ERROR_SET(&err)) { 2852 if (!ui_has(kUIMessages)) { 2853 msg_putchar('\n'); 2854 } 2855 msg_scroll = true; 2856 msg_puts_hl(err.msg, HLF_E, true); 2857 api_clear_error(&err); 2858 redrawcmd(); 2859 } 2860 } 2861 } 2862 2863 static int command_line_changed(CommandLineState *s) 2864 { 2865 const bool prev_cmdpreview = cmdpreview; 2866 if (s->firstc == ':' 2867 && current_sctx.sc_sid == 0 // only if interactive 2868 && *p_icm != NUL // 'inccommand' is set 2869 && !exmode_active // not in ex mode 2870 && cmdline_star == 0 // not typing a password 2871 && !vpeekc_any() 2872 && cmdpreview_may_show(s)) { 2873 // 'inccommand' preview has been shown. 2874 } else { 2875 cmdpreview = false; 2876 if (prev_cmdpreview) { 2877 // TODO(bfredl): add an immediate redraw flag for cmdline mode which will trigger 2878 // at next wait-for-input 2879 update_screen(); // Clear 'inccommand' preview. 2880 } 2881 if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) { 2882 may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); 2883 } 2884 } 2885 2886 if (ccline.cmdpos != s->prev_cmdpos 2887 || (s->prev_cmdbuff != NULL && strcmp(s->prev_cmdbuff, ccline.cmdbuff) != 0)) { 2888 // Trigger CmdlineChanged autocommands. 2889 do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-'); 2890 } 2891 2892 may_trigger_cursormovedc(s); 2893 2894 if (p_arshape && !p_tbidi) { 2895 // Always redraw the whole command line to fix shaping and 2896 // right-left typing. Not efficient, but it works. 2897 // Do it only when there are no characters left to read 2898 // to avoid useless intermediate redraws. 2899 // if cmdline is external the ui handles shaping, no redraw needed. 2900 if (!ui_has(kUICmdline) && vpeekc() == NUL) { 2901 redrawcmd(); 2902 } 2903 } 2904 2905 return 1; 2906 } 2907 2908 /// Abandon the command line. 2909 static void abandon_cmdline(void) 2910 { 2911 dealloc_cmdbuff(); 2912 if (msg_scrolled == 0) { 2913 compute_cmdrow(); 2914 } 2915 // Avoid overwriting key prompt 2916 if (!ccline.one_key) { 2917 msg("", 0); 2918 redraw_cmdline = true; 2919 } 2920 } 2921 2922 /// getcmdline() - accept a command line starting with firstc. 2923 /// 2924 /// firstc == ':' get ":" command line. 2925 /// firstc == '/' or '?' get search pattern 2926 /// firstc == '=' get expression 2927 /// firstc == '@' get text for input() function 2928 /// firstc == '>' get text for debug mode 2929 /// firstc == NUL get text for :insert command 2930 /// firstc == -1 like NUL, and break on CTRL-C 2931 /// 2932 /// The line is collected in ccline.cmdbuff, which is reallocated to fit the 2933 /// command line. 2934 /// 2935 /// Careful: getcmdline() can be called recursively! 2936 /// 2937 /// Return pointer to allocated string if there is a commandline, NULL 2938 /// otherwise. 2939 /// 2940 /// @param count only used for incremental search 2941 /// @param indent indent for inside conditionals 2942 char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNUSED) 2943 { 2944 return (char *)command_line_enter(firstc, count, indent, true); 2945 } 2946 2947 /// Get a command line with a prompt 2948 /// 2949 /// This is prepared to be called recursively from getcmdline() (e.g. by 2950 /// f_input() when evaluating an expression from `<C-r>=`). 2951 /// 2952 /// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug. 2953 /// @param[in] prompt Prompt string: what is displayed before the user text. 2954 /// @param[in] hl_id Prompt highlight id. 2955 /// @param[in] xp_context Type of expansion. 2956 /// @param[in] xp_arg User-defined expansion argument. 2957 /// @param[in] highlight_callback Callback used for highlighting user input. 2958 /// @param[in] one_key Return after one key press for button prompt. 2959 /// @param[in] mouse_used Set to true when returning after right mouse click. 2960 /// 2961 /// @return [allocated] Command line or NULL. 2962 char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id, 2963 const int xp_context, const char *const xp_arg, 2964 const Callback highlight_callback, bool one_key, bool *mouse_used) 2965 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC 2966 { 2967 const int msg_col_save = msg_col; 2968 2969 CmdlineInfo save_ccline; 2970 bool did_save_ccline = false; 2971 if (ccline.cmdbuff != NULL) { 2972 // Save the values of the current cmdline and restore them below. 2973 save_cmdline(&save_ccline); 2974 did_save_ccline = true; 2975 } else { 2976 CLEAR_FIELD(ccline); 2977 } 2978 ccline.prompt_id = last_prompt_id++; 2979 ccline.cmdprompt = (char *)prompt; 2980 ccline.hl_id = hl_id; 2981 ccline.xp_context = xp_context; 2982 ccline.xp_arg = (char *)xp_arg; 2983 ccline.input_fn = (firstc == '@'); 2984 ccline.highlight_callback = highlight_callback; 2985 ccline.one_key = one_key; 2986 ccline.mouse_used = mouse_used; 2987 2988 const bool cmd_silent_saved = cmd_silent; 2989 int msg_silent_saved = msg_silent; 2990 msg_silent = 0; 2991 cmd_silent = false; // Want to see the prompt. 2992 2993 char *const ret = (char *)command_line_enter(firstc, 1, 0, false); 2994 ccline.redraw_state = kCmdRedrawNone; 2995 2996 if (did_save_ccline) { 2997 restore_cmdline(&save_ccline); 2998 } 2999 msg_silent = msg_silent_saved; 3000 cmd_silent = cmd_silent_saved; 3001 // Restore msg_col, the prompt from input() may have changed it. 3002 // But only if called recursively and the commandline is therefore being 3003 // restored to an old one; if not, the input() prompt stays on the screen, 3004 // so we need its modified msg_col left intact. 3005 if (ccline.cmdbuff != NULL) { 3006 msg_col = msg_col_save; 3007 } 3008 3009 return ret; 3010 } 3011 3012 /// Read the 'wildmode' option, fill wim_flags[]. 3013 int check_opt_wim(void) 3014 { 3015 uint8_t new_wim_flags[4]; 3016 int i; 3017 int idx = 0; 3018 3019 for (i = 0; i < 4; i++) { 3020 new_wim_flags[i] = 0; 3021 } 3022 3023 for (char *p = p_wim; *p; p++) { 3024 // Note: Keep this in sync with opt_wim_values. 3025 for (i = 0; ASCII_ISALPHA(p[i]); i++) {} 3026 if (p[i] != NUL && p[i] != ',' && p[i] != ':') { 3027 return FAIL; 3028 } 3029 if (i == 7 && strncmp(p, "longest", 7) == 0) { 3030 new_wim_flags[idx] |= kOptWimFlagLongest; 3031 } else if (i == 4 && strncmp(p, "full", 4) == 0) { 3032 new_wim_flags[idx] |= kOptWimFlagFull; 3033 } else if (i == 4 && strncmp(p, "list", 4) == 0) { 3034 new_wim_flags[idx] |= kOptWimFlagList; 3035 } else if (i == 8 && strncmp(p, "lastused", 8) == 0) { 3036 new_wim_flags[idx] |= kOptWimFlagLastused; 3037 } else if (i == 8 && strncmp(p, "noselect", 8) == 0) { 3038 new_wim_flags[idx] |= kOptWimFlagNoselect; 3039 } else { 3040 return FAIL; 3041 } 3042 p += i; 3043 if (*p == NUL) { 3044 break; 3045 } 3046 if (*p == ',') { 3047 if (idx == 3) { 3048 return FAIL; 3049 } 3050 idx++; 3051 } 3052 } 3053 3054 // fill remaining entries with last flag 3055 while (idx < 3) { 3056 new_wim_flags[idx + 1] = new_wim_flags[idx]; 3057 idx++; 3058 } 3059 3060 // only when there are no errors, wim_flags[] is changed 3061 for (i = 0; i < 4; i++) { 3062 wim_flags[i] = new_wim_flags[i]; 3063 } 3064 return OK; 3065 } 3066 3067 /// Return true when the text must not be changed and we can't switch to 3068 /// another window or buffer. True when editing the command line etc. 3069 bool text_locked(void) 3070 { 3071 if (cmdwin_type != 0) { 3072 return true; 3073 } 3074 if (expr_map_locked()) { 3075 return true; 3076 } 3077 return textlock != 0; 3078 } 3079 3080 // Give an error message for a command that isn't allowed while the cmdline 3081 // window is open or editing the cmdline in another way. 3082 void text_locked_msg(void) 3083 { 3084 emsg(_(get_text_locked_msg())); 3085 } 3086 3087 const char *get_text_locked_msg(void) 3088 { 3089 if (cmdwin_type != 0) { 3090 return e_cmdwin; 3091 } else { 3092 return e_textlock; 3093 } 3094 } 3095 3096 /// Check for text, window or buffer locked. 3097 /// Give an error message and return true if something is locked. 3098 bool text_or_buf_locked(void) 3099 { 3100 if (text_locked()) { 3101 text_locked_msg(); 3102 return true; 3103 } 3104 return curbuf_locked(); 3105 } 3106 3107 /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and 3108 /// return true when it is and give an error message. 3109 bool curbuf_locked(void) 3110 { 3111 if (curbuf->b_ro_locked > 0) { 3112 emsg(_(e_cannot_edit_other_buf)); 3113 return true; 3114 } 3115 return allbuf_locked(); 3116 } 3117 3118 // Check if "allbuf_lock" is set and return true when it is and give an error 3119 // message. 3120 bool allbuf_locked(void) 3121 { 3122 if (allbuf_lock > 0) { 3123 emsg(_("E811: Not allowed to change buffer information now")); 3124 return true; 3125 } 3126 return false; 3127 } 3128 3129 static int cmdline_charsize(int idx) 3130 { 3131 if (cmdline_star > 0) { // showing '*', always 1 position 3132 return 1; 3133 } 3134 return ptr2cells(ccline.cmdbuff + idx); 3135 } 3136 3137 /// Compute the offset of the cursor on the command line for the prompt and 3138 /// indent. 3139 static int cmd_startcol(void) 3140 { 3141 return ccline.cmdindent + ((ccline.cmdfirstc != NUL) ? 1 : 0); 3142 } 3143 3144 /// Compute the column position for a byte position on the command line. 3145 int cmd_screencol(int bytepos) 3146 { 3147 int m; // maximum column 3148 int col = cmd_startcol(); 3149 if (KeyTyped) { 3150 m = cmdline_win ? cmdline_win->w_view_width * cmdline_win->w_view_height : Columns * Rows; 3151 if (m < 0) { // overflow, Columns or Rows at weird value 3152 m = MAXCOL; 3153 } 3154 } else { 3155 m = MAXCOL; 3156 } 3157 3158 for (int i = 0; i < ccline.cmdlen && i < bytepos; 3159 i += utfc_ptr2len(ccline.cmdbuff + i)) { 3160 int c = cmdline_charsize(i); 3161 // Count ">" for double-wide multi-byte char that doesn't fit. 3162 correct_screencol(i, c, &col); 3163 3164 // If the cmdline doesn't fit, show cursor on last visible char. 3165 // Don't move the cursor itself, so we can still append. 3166 if ((col += c) >= m) { 3167 col -= c; 3168 break; 3169 } 3170 } 3171 return col; 3172 } 3173 3174 /// Check if the character at "idx", which is "cells" wide, is a multi-byte 3175 /// character that doesn't fit, so that a ">" must be displayed. 3176 static void correct_screencol(int idx, int cells, int *col) 3177 { 3178 if (utfc_ptr2len(ccline.cmdbuff + idx) > 1 3179 && utf_ptr2cells(ccline.cmdbuff + idx) > 1 3180 && (*col) % Columns + cells > Columns) { 3181 (*col)++; 3182 } 3183 } 3184 3185 /// Get an Ex command line for the ":" command. 3186 /// 3187 /// @param c normally ':', NUL for ":append" 3188 /// @param indent indent for inside conditionals 3189 char *getexline(int c, void *cookie, int indent, bool do_concat) 3190 { 3191 // When executing a register, remove ':' that's in front of each line. 3192 if (exec_from_reg && vpeekc() == ':') { 3193 vgetc(); 3194 } 3195 3196 return getcmdline(c, 1, indent, do_concat); 3197 } 3198 3199 bool cmdline_overstrike(void) 3200 FUNC_ATTR_PURE 3201 { 3202 return ccline.overstrike; 3203 } 3204 3205 /// Return true if the cursor is at the end of the cmdline. 3206 bool cmdline_at_end(void) 3207 FUNC_ATTR_PURE 3208 { 3209 return (ccline.cmdpos >= ccline.cmdlen); 3210 } 3211 3212 /// Deallocate a command line buffer, updating the buffer size and length. 3213 static void dealloc_cmdbuff(void) 3214 { 3215 XFREE_CLEAR(ccline.cmdbuff); 3216 ccline.cmdlen = ccline.cmdbufflen = 0; 3217 } 3218 3219 /// Allocate a new command line buffer. 3220 /// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. 3221 static void alloc_cmdbuff(int len) 3222 { 3223 // give some extra space to avoid having to allocate all the time 3224 if (len < 80) { 3225 len = 100; 3226 } else { 3227 len += 20; 3228 } 3229 3230 ccline.cmdbuff = xmalloc((size_t)len); 3231 ccline.cmdbufflen = len; 3232 } 3233 3234 /// Re-allocate the command line to length len + something extra. 3235 void realloc_cmdbuff(int len) 3236 { 3237 if (len < ccline.cmdbufflen) { 3238 return; // no need to resize 3239 } 3240 3241 char *p = ccline.cmdbuff; 3242 3243 alloc_cmdbuff(len); // will get some more 3244 // There isn't always a NUL after the command, but it may need to be 3245 // there, thus copy up to the NUL and add a NUL. 3246 memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen); 3247 ccline.cmdbuff[ccline.cmdlen] = NUL; 3248 3249 if (ccline.xpc != NULL 3250 && ccline.xpc->xp_pattern != NULL 3251 && ccline.xpc->xp_context != EXPAND_NOTHING 3252 && ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) { 3253 int i = (int)(ccline.xpc->xp_pattern - p); 3254 3255 // If xp_pattern points inside the old cmdbuff it needs to be adjusted 3256 // to point into the newly allocated memory. 3257 if (i >= 0 && i <= ccline.cmdlen) { 3258 ccline.xpc->xp_pattern = ccline.cmdbuff + i; 3259 } 3260 } 3261 3262 xfree(p); 3263 } 3264 3265 enum { MAX_CB_ERRORS = 1, }; 3266 3267 /// Color expression cmdline using built-in expressions parser 3268 /// 3269 /// @param[in] colored_ccline Command-line to color. 3270 /// @param[out] ret_ccline_colors What should be colored. 3271 /// 3272 /// Always colors the whole cmdline. 3273 static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, 3274 ColoredCmdline *const ret_ccline_colors) 3275 FUNC_ATTR_NONNULL_ALL 3276 { 3277 ParserLine parser_lines[] = { 3278 { 3279 .data = colored_ccline->cmdbuff, 3280 .size = strlen(colored_ccline->cmdbuff), 3281 .allocated = false, 3282 }, 3283 { NULL, 0, false }, 3284 }; 3285 ParserLine *plines_p = parser_lines; 3286 ParserHighlight colors; 3287 kvi_init(colors); 3288 ParserState pstate; 3289 viml_parser_init(&pstate, parser_simple_get_line, &plines_p, &colors); 3290 ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC); 3291 viml_pexpr_free_ast(east); 3292 viml_parser_destroy(&pstate); 3293 kv_resize(ret_ccline_colors->colors, kv_size(colors)); 3294 size_t prev_end = 0; 3295 for (size_t i = 0; i < kv_size(colors); i++) { 3296 const ParserHighlightChunk chunk = kv_A(colors, i); 3297 assert(chunk.start.col < INT_MAX); 3298 assert(chunk.end_col < INT_MAX); 3299 if (chunk.start.col != prev_end) { 3300 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { 3301 .start = (int)prev_end, 3302 .end = (int)chunk.start.col, 3303 .hl_id = 0, 3304 })); 3305 } 3306 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { 3307 .start = (int)chunk.start.col, 3308 .end = (int)chunk.end_col, 3309 .hl_id = syn_name2id(chunk.group), 3310 })); 3311 prev_end = chunk.end_col; 3312 } 3313 if (prev_end < (size_t)colored_ccline->cmdlen) { 3314 kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { 3315 .start = (int)prev_end, 3316 .end = colored_ccline->cmdlen, 3317 .hl_id = 0, 3318 })); 3319 } 3320 kvi_destroy(colors); 3321 } 3322 3323 /// Color command-line 3324 /// 3325 /// Should use built-in command parser or user-specified one. Currently only the 3326 /// latter is supported. 3327 /// 3328 /// @param[in,out] colored_ccline Command-line to color. Also holds a cache: 3329 /// if ->prompt_id and ->cmdbuff values happen 3330 /// to be equal to those from colored_cmdline it 3331 /// will just do nothing, assuming that ->colors 3332 /// already contains needed data. 3333 /// 3334 /// Always colors the whole cmdline. 3335 /// 3336 /// @return true if draw_cmdline may proceed, false if it does not need anything 3337 /// to do. 3338 static bool color_cmdline(CmdlineInfo *colored_ccline) 3339 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 3340 { 3341 bool printed_errmsg = false; 3342 3343 #define PRINT_ERRMSG(...) \ 3344 do { \ 3345 msg_scroll = true; \ 3346 msg_putchar('\n'); \ 3347 smsg(HLF_E, __VA_ARGS__); \ 3348 printed_errmsg = true; \ 3349 } while (0) 3350 bool ret = true; 3351 3352 ColoredCmdline *ccline_colors = &colored_ccline->last_colors; 3353 3354 // Check whether result of the previous call is still valid. 3355 if (ccline_colors->prompt_id == colored_ccline->prompt_id 3356 && ccline_colors->cmdbuff != NULL 3357 && strcmp(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { 3358 return ret; 3359 } 3360 3361 kv_size(ccline_colors->colors) = 0; 3362 3363 if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) { 3364 // Nothing to do, exiting. 3365 XFREE_CLEAR(ccline_colors->cmdbuff); 3366 return ret; 3367 } 3368 3369 bool arg_allocated = false; 3370 typval_T arg = { 3371 .v_type = VAR_STRING, 3372 .vval.v_string = colored_ccline->cmdbuff, 3373 }; 3374 typval_T tv = { .v_type = VAR_UNKNOWN }; 3375 3376 static unsigned prev_prompt_id = UINT_MAX; 3377 static int prev_prompt_errors = 0; 3378 Callback color_cb = CALLBACK_NONE; 3379 bool can_free_cb = false; 3380 Error err = ERROR_INIT; 3381 const char *err_errmsg = e_intern2; 3382 bool dgc_ret = true; 3383 3384 if (colored_ccline->prompt_id != prev_prompt_id) { 3385 prev_prompt_errors = 0; 3386 prev_prompt_id = colored_ccline->prompt_id; 3387 } else if (prev_prompt_errors >= MAX_CB_ERRORS) { 3388 goto color_cmdline_end; 3389 } 3390 if (colored_ccline->highlight_callback.type != kCallbackNone) { 3391 // Currently this should only happen while processing input() prompts. 3392 assert(colored_ccline->input_fn); 3393 color_cb = colored_ccline->highlight_callback; 3394 } else if (colored_ccline->cmdfirstc == ':') { 3395 TRY_WRAP(&err, { 3396 err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s"); 3397 dgc_ret = tv_dict_get_callback(get_globvar_dict(), S_LEN("Nvim_color_cmdline"), 3398 &color_cb); 3399 }); 3400 can_free_cb = true; 3401 } else if (colored_ccline->cmdfirstc == '=') { 3402 color_expr_cmdline(colored_ccline, ccline_colors); 3403 } 3404 if (ERROR_SET(&err) || !dgc_ret) { 3405 goto color_cmdline_error; 3406 } 3407 3408 if (color_cb.type == kCallbackNone) { 3409 goto color_cmdline_end; 3410 } 3411 if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) { 3412 arg_allocated = true; 3413 arg.vval.v_string = xmemdupz(colored_ccline->cmdbuff, (size_t)colored_ccline->cmdlen); 3414 } 3415 // msg_start() called by e.g. :echo may shift command-line to the first column 3416 // even though msg_silent is here. Two ways to workaround this problem without 3417 // altering message.c: use full_screen or save and restore msg_col. 3418 // 3419 // Saving and restoring full_screen does not work well with :redraw!. Saving 3420 // and restoring msg_col is neither ideal, but while with full_screen it 3421 // appears shifted one character to the right and cursor position is no longer 3422 // correct, with msg_col it just misses leading `:`. Since `redraw!` in 3423 // callback lags this is least of the user problems. 3424 // 3425 // Also using TRY_WRAP because error messages may overwrite typed 3426 // command-line which is not expected. 3427 getln_interrupted_highlight = false; 3428 bool cbcall_ret = true; 3429 TRY_WRAP(&err, { 3430 err_errmsg = N_("E5407: Callback has thrown an exception: %s"); 3431 const int saved_msg_col = msg_col; 3432 msg_silent++; 3433 cbcall_ret = callback_call(&color_cb, 1, &arg, &tv); 3434 msg_silent--; 3435 msg_col = saved_msg_col; 3436 if (got_int) { 3437 getln_interrupted_highlight = true; 3438 } 3439 }); 3440 if (ERROR_SET(&err) || !cbcall_ret) { 3441 goto color_cmdline_error; 3442 } 3443 if (tv.v_type != VAR_LIST) { 3444 PRINT_ERRMSG("%s", _("E5400: Callback should return list")); 3445 goto color_cmdline_error; 3446 } 3447 if (tv.vval.v_list == NULL) { 3448 goto color_cmdline_end; 3449 } 3450 varnumber_T prev_end = 0; 3451 int i = 0; 3452 TV_LIST_ITER_CONST(tv.vval.v_list, li, { 3453 if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) { 3454 PRINT_ERRMSG(_("E5401: List item %i is not a List"), i); 3455 goto color_cmdline_error; 3456 } 3457 const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list; 3458 if (tv_list_len(l) != 3) { 3459 PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %d /= 3"), 3460 i, tv_list_len(l)); 3461 goto color_cmdline_error; 3462 } 3463 bool error = false; 3464 const varnumber_T start = ( 3465 tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error)); 3466 if (error) { 3467 goto color_cmdline_error; 3468 } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) { 3469 PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " 3470 "[%" PRIdVARNUMBER ", %i)"), 3471 i, start, prev_end, colored_ccline->cmdlen); 3472 goto color_cmdline_error; 3473 } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) { 3474 PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits " 3475 "multibyte character"), i, start); 3476 goto color_cmdline_error; 3477 } 3478 if (start != prev_end) { 3479 kv_push(ccline_colors->colors, ((CmdlineColorChunk) { 3480 .start = (int)prev_end, 3481 .end = (int)start, 3482 .hl_id = 0, 3483 })); 3484 } 3485 const varnumber_T end = 3486 tv_get_number_chk(TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error); 3487 if (error) { 3488 goto color_cmdline_error; 3489 } else if (!(start < end && end <= colored_ccline->cmdlen)) { 3490 PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " 3491 "(%" PRIdVARNUMBER ", %i]"), 3492 i, end, start, colored_ccline->cmdlen); 3493 goto color_cmdline_error; 3494 } else if (end < colored_ccline->cmdlen 3495 && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]] 3496 == 0)) { 3497 PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " 3498 "character"), i, end); 3499 goto color_cmdline_error; 3500 } 3501 prev_end = end; 3502 const char *const group = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_last(l))); 3503 if (group == NULL) { 3504 goto color_cmdline_error; 3505 } 3506 kv_push(ccline_colors->colors, ((CmdlineColorChunk) { 3507 .start = (int)start, 3508 .end = (int)end, 3509 .hl_id = syn_name2id(group), 3510 })); 3511 i++; 3512 }); 3513 if (prev_end < colored_ccline->cmdlen) { 3514 kv_push(ccline_colors->colors, ((CmdlineColorChunk) { 3515 .start = (int)prev_end, 3516 .end = colored_ccline->cmdlen, 3517 .hl_id = 0, 3518 })); 3519 } 3520 prev_prompt_errors = 0; 3521 color_cmdline_end: 3522 assert(!ERROR_SET(&err)); 3523 if (can_free_cb) { 3524 callback_free(&color_cb); 3525 } 3526 xfree(ccline_colors->cmdbuff); 3527 // Note: errors “output” is cached just as well as regular results. 3528 ccline_colors->prompt_id = colored_ccline->prompt_id; 3529 if (arg_allocated) { 3530 ccline_colors->cmdbuff = arg.vval.v_string; 3531 } else { 3532 ccline_colors->cmdbuff = xmemdupz(colored_ccline->cmdbuff, (size_t)colored_ccline->cmdlen); 3533 } 3534 tv_clear(&tv); 3535 return ret; 3536 color_cmdline_error: 3537 if (ERROR_SET(&err)) { 3538 PRINT_ERRMSG(_(err_errmsg), err.msg); 3539 api_clear_error(&err); 3540 } 3541 assert(printed_errmsg); 3542 (void)printed_errmsg; 3543 3544 prev_prompt_errors++; 3545 kv_size(ccline_colors->colors) = 0; 3546 redrawcmdline(); 3547 ret = false; 3548 goto color_cmdline_end; 3549 #undef PRINT_ERRMSG 3550 } 3551 3552 // Draw part of the cmdline at the current cursor position. But draw stars 3553 // when cmdline_star is true. 3554 static void draw_cmdline(int start, int len) 3555 { 3556 if (ccline.cmdbuff == NULL || !color_cmdline(&ccline)) { 3557 return; 3558 } 3559 3560 if (ui_has(kUICmdline)) { 3561 ccline.special_char = NUL; 3562 ccline.redraw_state = kCmdRedrawAll; 3563 return; 3564 } 3565 3566 if (cmdline_star > 0) { 3567 for (int i = 0; i < len; i++) { 3568 msg_putchar('*'); 3569 i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1; 3570 } 3571 } else { 3572 if (kv_size(ccline.last_colors.colors)) { 3573 for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) { 3574 CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i); 3575 if (chunk.end <= start) { 3576 continue; 3577 } 3578 const int chunk_start = MAX(chunk.start, start); 3579 msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.hl_id, false); 3580 } 3581 } else { 3582 msg_outtrans_len(ccline.cmdbuff + start, len, 0, false); 3583 } 3584 } 3585 } 3586 3587 static void ui_ext_cmdline_show(CmdlineInfo *line) 3588 { 3589 Arena arena = ARENA_EMPTY; 3590 Array content; 3591 if (cmdline_star) { 3592 content = arena_array(&arena, 1); 3593 size_t len = 0; 3594 for (char *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { 3595 len++; 3596 } 3597 char *buf = arena_alloc(&arena, len, false); 3598 memset(buf, '*', len); 3599 Array item = arena_array(&arena, 3); 3600 ADD_C(item, INTEGER_OBJ(0)); 3601 ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len))); 3602 ADD_C(item, INTEGER_OBJ(0)); 3603 ADD_C(content, ARRAY_OBJ(item)); 3604 } else if (kv_size(line->last_colors.colors)) { 3605 content = arena_array(&arena, kv_size(line->last_colors.colors)); 3606 for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { 3607 CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); 3608 Array item = arena_array(&arena, 3); 3609 ADD_C(item, INTEGER_OBJ(chunk.hl_id == 0 ? 0 : syn_id2attr(chunk.hl_id))); 3610 3611 assert(chunk.end >= chunk.start); 3612 ADD_C(item, STRING_OBJ(cbuf_as_string(line->cmdbuff + chunk.start, 3613 (size_t)(chunk.end - chunk.start)))); 3614 ADD_C(item, INTEGER_OBJ(chunk.hl_id)); 3615 ADD_C(content, ARRAY_OBJ(item)); 3616 } 3617 } else { 3618 Array item = arena_array(&arena, 3); 3619 ADD_C(item, INTEGER_OBJ(0)); 3620 ADD_C(item, CSTR_AS_OBJ(line->cmdbuff)); 3621 ADD_C(item, INTEGER_OBJ(0)); 3622 content = arena_array(&arena, 1); 3623 ADD_C(content, ARRAY_OBJ(item)); 3624 } 3625 char charbuf[2] = { (char)line->cmdfirstc, 0 }; 3626 ui_call_cmdline_show(content, line->cmdpos, 3627 cstr_as_string(charbuf), 3628 cstr_as_string((line->cmdprompt)), 3629 line->cmdindent, line->level, line->hl_id); 3630 if (line->special_char) { 3631 charbuf[0] = line->special_char; 3632 ui_call_cmdline_special_char(cstr_as_string(charbuf), 3633 line->special_shift, 3634 line->level); 3635 } 3636 arena_mem_free(arena_finish(&arena)); 3637 } 3638 3639 void ui_ext_cmdline_block_append(size_t indent, const char *line) 3640 { 3641 char *buf = xmallocz(indent + strlen(line)); 3642 memset(buf, ' ', indent); 3643 memcpy(buf + indent, line, strlen(line)); 3644 3645 Array item = ARRAY_DICT_INIT; 3646 ADD(item, INTEGER_OBJ(0)); 3647 ADD(item, CSTR_AS_OBJ(buf)); 3648 ADD(item, INTEGER_OBJ(0)); 3649 Array content = ARRAY_DICT_INIT; 3650 ADD(content, ARRAY_OBJ(item)); 3651 ADD(cmdline_block, ARRAY_OBJ(content)); 3652 if (cmdline_block.size > 1) { 3653 ui_call_cmdline_block_append(content); 3654 } else { 3655 ui_call_cmdline_block_show(cmdline_block); 3656 } 3657 } 3658 3659 void ui_ext_cmdline_block_leave(void) 3660 { 3661 api_free_array(cmdline_block); 3662 cmdline_block = (Array)ARRAY_DICT_INIT; 3663 ui_call_cmdline_block_hide(); 3664 } 3665 3666 /// Extra redrawing needed for redraw! and on ui_attach. 3667 void cmdline_screen_cleared(void) 3668 { 3669 if (!ui_has(kUICmdline)) { 3670 return; 3671 } 3672 3673 if (cmdline_block.size) { 3674 ui_call_cmdline_block_show(cmdline_block); 3675 } 3676 3677 int prev_level = ccline.level - 1; 3678 CmdlineInfo *line = ccline.prev_ccline; 3679 while (prev_level > 0 && line) { 3680 if (line->level == prev_level) { 3681 // don't redraw a cmdline already shown in the cmdline window 3682 if (prev_level != cmdwin_level) { 3683 line->redraw_state = kCmdRedrawAll; 3684 } 3685 prev_level--; 3686 } 3687 line = line->prev_ccline; 3688 } 3689 redrawcmd(); 3690 } 3691 3692 /// called by ui_flush, do what redraws necessary to keep cmdline updated. 3693 void cmdline_ui_flush(void) 3694 { 3695 if (!ui_has(kUICmdline)) { 3696 return; 3697 } 3698 int level = ccline.level; 3699 CmdlineInfo *line = &ccline; 3700 while (level > 0 && line) { 3701 if (line->level == level) { 3702 CmdRedraw redraw_state = line->redraw_state; 3703 line->redraw_state = kCmdRedrawNone; 3704 if (redraw_state == kCmdRedrawAll) { 3705 cmdline_was_last_drawn = true; 3706 ui_ext_cmdline_show(line); 3707 } else if (redraw_state == kCmdRedrawPos && cmdline_was_last_drawn) { 3708 ui_call_cmdline_pos(line->cmdpos, line->level); 3709 } 3710 level--; 3711 } 3712 line = line->prev_ccline; 3713 } 3714 } 3715 3716 // Put a character on the command line. Shifts the following text to the 3717 // right when "shift" is true. Used for CTRL-V, CTRL-K, etc. 3718 // "c" must be printable (fit in one display cell)! 3719 void putcmdline(char c, bool shift) 3720 { 3721 if (cmd_silent) { 3722 return; 3723 } 3724 if (!ui_has(kUICmdline)) { 3725 msg_no_more = true; 3726 msg_putchar(c); 3727 if (shift) { 3728 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); 3729 } 3730 msg_no_more = false; 3731 } else if (ccline.redraw_state != kCmdRedrawAll) { 3732 char charbuf[2] = { c, 0 }; 3733 ui_call_cmdline_special_char(cstr_as_string(charbuf), shift, 3734 ccline.level); 3735 } 3736 cursorcmd(); 3737 ccline.special_char = c; 3738 ccline.special_shift = shift; 3739 ui_cursor_shape(); 3740 } 3741 3742 /// Undo a putcmdline(c, false). 3743 void unputcmdline(void) 3744 { 3745 if (cmd_silent) { 3746 return; 3747 } 3748 msg_no_more = true; 3749 if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) { 3750 msg_putchar(' '); 3751 } else { 3752 draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos)); 3753 } 3754 msg_no_more = false; 3755 cursorcmd(); 3756 ccline.special_char = NUL; 3757 ui_cursor_shape(); 3758 } 3759 3760 // Put the given string, of the given length, onto the command line. 3761 // If len is -1, then strlen() is used to calculate the length. 3762 // If 'redraw' is true then the new part of the command line, and the remaining 3763 // part will be redrawn, otherwise it will not. If this function is called 3764 // twice in a row, then 'redraw' should be false and redrawcmd() should be 3765 // called afterwards. 3766 void put_on_cmdline(const char *str, int len, bool redraw) 3767 { 3768 if (len < 0) { 3769 len = (int)strlen(str); 3770 } 3771 3772 realloc_cmdbuff(ccline.cmdlen + len + 1); 3773 3774 if (!ccline.overstrike) { 3775 memmove(ccline.cmdbuff + ccline.cmdpos + len, 3776 ccline.cmdbuff + ccline.cmdpos, 3777 (size_t)(ccline.cmdlen - ccline.cmdpos)); 3778 ccline.cmdlen += len; 3779 } else { 3780 // Count nr of characters in the new string. 3781 int m = 0; 3782 int i; 3783 for (i = 0; i < len; i += utfc_ptr2len(str + i)) { 3784 m++; 3785 } 3786 // Count nr of bytes in cmdline that are overwritten by these 3787 // characters. 3788 for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0; 3789 i += utfc_ptr2len(ccline.cmdbuff + i)) { 3790 m--; 3791 } 3792 if (i < ccline.cmdlen) { 3793 memmove(ccline.cmdbuff + ccline.cmdpos + len, 3794 ccline.cmdbuff + i, (size_t)(ccline.cmdlen - i)); 3795 ccline.cmdlen += ccline.cmdpos + len - i; 3796 } else { 3797 ccline.cmdlen = ccline.cmdpos + len; 3798 } 3799 } 3800 memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len); 3801 ccline.cmdbuff[ccline.cmdlen] = NUL; 3802 3803 // When the inserted text starts with a composing character, 3804 // backup to the character before it. 3805 if (ccline.cmdpos > 0 && (uint8_t)ccline.cmdbuff[ccline.cmdpos] >= 0x80) { 3806 int i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos); 3807 if (i != 0) { 3808 ccline.cmdpos -= i; 3809 len += i; 3810 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 3811 } 3812 } 3813 3814 if (redraw && !cmd_silent) { 3815 msg_no_more = true; 3816 int i = cmdline_row; 3817 cursorcmd(); 3818 draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); 3819 // Avoid clearing the rest of the line too often. 3820 if (cmdline_row != i || ccline.overstrike) { 3821 msg_clr_eos(); 3822 } 3823 msg_no_more = false; 3824 } 3825 int m; 3826 if (KeyTyped) { 3827 m = Columns * Rows; 3828 if (m < 0) { // overflow, Columns or Rows at weird value 3829 m = MAXCOL; 3830 } 3831 } else { 3832 m = MAXCOL; 3833 } 3834 for (int i = 0; i < len; i++) { 3835 int c = cmdline_charsize(ccline.cmdpos); 3836 // count ">" for a double-wide char that doesn't fit. 3837 correct_screencol(ccline.cmdpos, c, &ccline.cmdspos); 3838 // Stop cursor at the end of the screen, but do increment the 3839 // insert position, so that entering a very long command 3840 // works, even though you can't see it. 3841 if (ccline.cmdspos + c < m) { 3842 ccline.cmdspos += c; 3843 } 3844 c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1; 3845 c = MIN(c, len - i - 1); 3846 ccline.cmdpos += c; 3847 i += c; 3848 ccline.cmdpos++; 3849 } 3850 3851 if (redraw) { 3852 msg_check(); 3853 } 3854 } 3855 3856 /// Save ccline, because obtaining the "=" register may execute "normal :cmd" 3857 /// and overwrite it. 3858 static void save_cmdline(CmdlineInfo *ccp) 3859 { 3860 *ccp = ccline; 3861 CLEAR_FIELD(ccline); 3862 ccline.prev_ccline = ccp; 3863 ccline.cmdbuff = NULL; // signal that ccline is not in use 3864 } 3865 3866 /// Restore ccline after it has been saved with save_cmdline(). 3867 static void restore_cmdline(CmdlineInfo *ccp) 3868 FUNC_ATTR_NONNULL_ALL 3869 { 3870 ccline = *ccp; 3871 } 3872 3873 /// Paste a yank register into the command line. 3874 /// Used by CTRL-R command in command-line mode. 3875 /// insert_reg() can't be used here, because special characters from the 3876 /// register contents will be interpreted as commands. 3877 /// 3878 /// @param regname Register name. 3879 /// @param literally Insert text literally instead of "as typed". 3880 /// @param remcr When true, remove trailing CR. 3881 /// 3882 /// @returns FAIL for failure, OK otherwise 3883 static bool cmdline_paste(int regname, bool literally, bool remcr) 3884 { 3885 char *arg; 3886 bool allocated; 3887 3888 // check for valid regname; also accept special characters for CTRL-R in 3889 // the command line 3890 if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W 3891 && regname != Ctrl_A && regname != Ctrl_L 3892 && !valid_yank_reg(regname, false)) { 3893 return FAIL; 3894 } 3895 3896 // A register containing CTRL-R can cause an endless loop. Allow using 3897 // CTRL-C to break the loop. 3898 line_breakcheck(); 3899 if (got_int) { 3900 return FAIL; 3901 } 3902 3903 // Need to set "textlock" to avoid nasty things like going to another 3904 // buffer when evaluating an expression. 3905 textlock++; 3906 const bool i = get_spec_reg(regname, &arg, &allocated, true); 3907 textlock--; 3908 3909 if (i) { 3910 // Got the value of a special register in "arg". 3911 if (arg == NULL) { 3912 return FAIL; 3913 } 3914 3915 // When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate 3916 // part of the word. 3917 char *p = arg; 3918 if (p_is && regname == Ctrl_W) { 3919 char *w; 3920 int len; 3921 3922 // Locate start of last word in the cmd buffer. 3923 for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff;) { 3924 len = utf_head_off(ccline.cmdbuff, w - 1) + 1; 3925 if (!vim_iswordc(utf_ptr2char(w - len))) { 3926 break; 3927 } 3928 w -= len; 3929 } 3930 len = (int)((ccline.cmdbuff + ccline.cmdpos) - w); 3931 if (p_ic ? STRNICMP(w, arg, len) == 0 : strncmp(w, arg, (size_t)len) == 0) { 3932 p += len; 3933 } 3934 } 3935 3936 cmdline_paste_str(p, literally); 3937 if (allocated) { 3938 xfree(arg); 3939 } 3940 return OK; 3941 } 3942 3943 return cmdline_paste_reg(regname, literally, remcr); 3944 } 3945 3946 // Put a string on the command line. 3947 // When "literally" is true, insert literally. 3948 // When "literally" is false, insert as typed, but don't leave the command 3949 // line. 3950 void cmdline_paste_str(const char *s, bool literally) 3951 { 3952 if (literally) { 3953 put_on_cmdline(s, -1, true); 3954 } else { 3955 while (*s != NUL) { 3956 int cv = (uint8_t)(*s); 3957 if (cv == Ctrl_V && s[1]) { 3958 s++; 3959 } 3960 int c = mb_cptr2char_adv(&s); 3961 if (cv == Ctrl_V || c == ESC || c == Ctrl_C 3962 || c == CAR || c == NL || c == Ctrl_L 3963 || (c == Ctrl_BSL && *s == Ctrl_N)) { 3964 stuffcharReadbuff(Ctrl_V); 3965 } 3966 stuffcharReadbuff(c); 3967 } 3968 } 3969 } 3970 3971 // This function is called when the screen size changes and with incremental 3972 // search and in other situations where the command line may have been 3973 // overwritten. 3974 void redrawcmdline(void) 3975 { 3976 if (cmd_silent) { 3977 return; 3978 } 3979 need_wait_return = false; 3980 compute_cmdrow(); 3981 redrawcmd(); 3982 cursorcmd(); 3983 ui_cursor_shape(); 3984 } 3985 3986 static void redrawcmdprompt(void) 3987 { 3988 if (cmd_silent) { 3989 return; 3990 } 3991 if (ui_has(kUICmdline)) { 3992 ccline.redraw_state = kCmdRedrawAll; 3993 return; 3994 } 3995 if (ccline.cmdfirstc != NUL) { 3996 msg_putchar(ccline.cmdfirstc); 3997 } 3998 if (ccline.cmdprompt != NULL) { 3999 msg_puts_hl(ccline.cmdprompt, ccline.hl_id, false); 4000 ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns; 4001 // do the reverse of cmd_startcol() 4002 if (ccline.cmdfirstc != NUL) { 4003 ccline.cmdindent--; 4004 } 4005 } else { 4006 for (int i = ccline.cmdindent; i > 0; i--) { 4007 msg_putchar(' '); 4008 } 4009 } 4010 } 4011 4012 // Redraw what is currently on the command line. 4013 void redrawcmd(void) 4014 { 4015 if (cmd_silent) { 4016 return; 4017 } 4018 4019 if (ui_has(kUICmdline)) { 4020 draw_cmdline(0, ccline.cmdlen); 4021 return; 4022 } 4023 4024 // when 'incsearch' is set there may be no command line while redrawing 4025 if (ccline.cmdbuff == NULL) { 4026 msg_cursor_goto(cmdline_row, 0); 4027 msg_clr_eos(); 4028 return; 4029 } 4030 4031 redrawing_cmdline = true; 4032 4033 sb_text_restart_cmdline(); 4034 msg_start(); 4035 redrawcmdprompt(); 4036 4037 // Don't use more prompt, truncate the cmdline if it doesn't fit. 4038 msg_no_more = true; 4039 draw_cmdline(0, ccline.cmdlen); 4040 msg_clr_eos(); 4041 msg_no_more = false; 4042 4043 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 4044 4045 if (ccline.special_char != NUL) { 4046 putcmdline(ccline.special_char, ccline.special_shift); 4047 } 4048 4049 // An emsg() before may have set msg_scroll. This is used in normal mode, 4050 // in cmdline mode we can reset them now. 4051 msg_scroll = false; // next message overwrites cmdline 4052 4053 // Typing ':' at the more prompt may set skip_redraw. We don't want this 4054 // in cmdline mode. 4055 skip_redraw = false; 4056 4057 redrawing_cmdline = false; 4058 } 4059 4060 void compute_cmdrow(void) 4061 { 4062 if (exmode_active || msg_scrolled != 0) { 4063 cmdline_row = Rows - 1; 4064 } else { 4065 win_T *wp = lastwin_nofloating(); 4066 cmdline_row = wp->w_winrow + wp->w_height 4067 + wp->w_hsep_height + wp->w_status_height + global_stl_height(); 4068 } 4069 if (cmdline_row == Rows && p_ch > 0) { 4070 cmdline_row--; 4071 } 4072 lines_left = cmdline_row; 4073 } 4074 4075 void cursorcmd(void) 4076 { 4077 if (cmd_silent || ui_has(kUICmdline)) { 4078 return; 4079 } 4080 4081 msg_row = cmdline_row + (ccline.cmdspos / Columns); 4082 msg_col = ccline.cmdspos % Columns; 4083 msg_row = MIN(msg_row, Rows - 1); 4084 4085 msg_cursor_goto(msg_row, msg_col); 4086 } 4087 4088 void gotocmdline(bool clr) 4089 { 4090 if (ui_has(kUICmdline)) { 4091 return; 4092 } 4093 msg_start(); 4094 msg_col = 0; // always start in column 0 4095 if (clr) { // clear the bottom line(s) 4096 msg_clr_eos(); // will reset clear_cmdline 4097 } 4098 msg_cursor_goto(cmdline_row, 0); 4099 } 4100 4101 // Check the word in front of the cursor for an abbreviation. 4102 // Called when the non-id character "c" has been entered. 4103 // When an abbreviation is recognized it is removed from the text with 4104 // backspaces and the replacement string is inserted, followed by "c". 4105 static int ccheck_abbr(int c) 4106 { 4107 int spos = 0; 4108 4109 if (p_paste || no_abbr) { // no abbreviations or in paste mode 4110 return false; 4111 } 4112 4113 // Do not consider '<,'> be part of the mapping, skip leading whitespace. 4114 // Actually accepts any mark. 4115 while (spos < ccline.cmdlen && ascii_iswhite(ccline.cmdbuff[spos])) { 4116 spos++; 4117 } 4118 if (ccline.cmdlen - spos > 5 4119 && ccline.cmdbuff[spos] == '\'' 4120 && ccline.cmdbuff[spos + 2] == ',' 4121 && ccline.cmdbuff[spos + 3] == '\'') { 4122 spos += 5; 4123 } else { 4124 // check abbreviation from the beginning of the commandline 4125 spos = 0; 4126 } 4127 4128 return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, spos); 4129 } 4130 4131 /// Escape special characters in "fname", depending on "what": 4132 /// 4133 /// @param[in] fname File name to escape. 4134 /// @param[in] what What to escape for: 4135 /// - VSE_NONE: for when used as a file name argument after a Vim command. 4136 /// - VSE_SHELL: for a shell command. 4137 /// - VSE_BUFFER: for the ":buffer" command. 4138 /// 4139 /// @return [allocated] escaped file name. 4140 char *vim_strsave_fnameescape(const char *const fname, const int what) 4141 FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL 4142 { 4143 #ifdef BACKSLASH_IN_FILENAME 4144 # define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" 4145 # define BUFFER_ESC_CHARS (" \t\n*?[`%#'\"|!<") 4146 char buf[sizeof(PATH_ESC_CHARS)]; 4147 int j = 0; 4148 4149 // Don't escape '[', '{' and '!' if they are in 'isfname' and for the 4150 // ":buffer" command. 4151 for (const char *p = what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS; 4152 *p != NUL; p++) { 4153 if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec((uint8_t)(*p))) { 4154 buf[j++] = *p; 4155 } 4156 } 4157 buf[j] = NUL; 4158 char *p = vim_strsave_escaped(fname, buf); 4159 #else 4160 # define PATH_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<" 4161 # define SHELL_ESC_CHARS " \t\n*?[{`$\\%#'\"|!<>();&" 4162 # define BUFFER_ESC_CHARS " \t\n*?[`$\\%#'\"|!<" 4163 char *p = vim_strsave_escaped(fname, 4164 what == VSE_SHELL ? SHELL_ESC_CHARS : what == 4165 VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS); 4166 if (what == VSE_SHELL && csh_like_shell()) { 4167 // For csh and similar shells need to put two backslashes before '!'. 4168 // One is taken by Vim, one by the shell. 4169 char *s = vim_strsave_escaped(p, "!"); 4170 xfree(p); 4171 p = s; 4172 } 4173 #endif 4174 4175 // '>' and '+' are special at the start of some commands, e.g. ":edit" and 4176 // ":write". "cd -" has a special meaning. 4177 if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { 4178 escape_fname(&p); 4179 } 4180 4181 return p; 4182 } 4183 4184 /// Put a backslash before the file name in "pp", which is in allocated memory. 4185 void escape_fname(char **pp) 4186 { 4187 char *p = xmalloc(strlen(*pp) + 2); 4188 p[0] = '\\'; 4189 STRCPY(p + 1, *pp); 4190 xfree(*pp); 4191 *pp = p; 4192 } 4193 4194 /// For each file name in files[num_files]: 4195 /// If 'orig_pat' starts with "~/", replace the home directory with "~". 4196 void tilde_replace(char *orig_pat, int num_files, char **files) 4197 { 4198 if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) { 4199 for (int i = 0; i < num_files; i++) { 4200 char *p = home_replace_save(NULL, files[i]); 4201 xfree(files[i]); 4202 files[i] = p; 4203 } 4204 } 4205 } 4206 4207 /// Get a pointer to the current command line info. 4208 CmdlineInfo *get_cmdline_info(void) 4209 { 4210 return &ccline; 4211 } 4212 4213 unsigned get_cmdline_last_prompt_id(void) 4214 { 4215 return last_prompt_id; 4216 } 4217 4218 /// Get pointer to the command line info to use. save_cmdline() may clear 4219 /// ccline and put the previous value in ccline.prev_ccline. 4220 static CmdlineInfo *get_ccline_ptr(void) 4221 { 4222 if ((State & MODE_CMDLINE) == 0) { 4223 return NULL; 4224 } else if (ccline.cmdbuff != NULL) { 4225 return &ccline; 4226 } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) { 4227 return ccline.prev_ccline; 4228 } else { 4229 return NULL; 4230 } 4231 } 4232 4233 /// Get the current command-line type. 4234 /// Returns ':' or '/' or '?' or '@' or '>' or '-' 4235 /// Only works when the command line is being edited. 4236 /// Returns NUL when something is wrong. 4237 static int get_cmdline_type(void) 4238 { 4239 CmdlineInfo *p = get_ccline_ptr(); 4240 4241 if (p == NULL) { 4242 return NUL; 4243 } 4244 if (p->cmdfirstc == NUL) { 4245 return (p->input_fn) ? '@' : '-'; 4246 } 4247 return p->cmdfirstc; 4248 } 4249 4250 /// Get the current command line in allocated memory. 4251 /// Only works when the command line is being edited. 4252 /// 4253 /// @return NULL when something is wrong. 4254 static char *get_cmdline_str(void) 4255 { 4256 if (cmdline_star > 0) { 4257 return NULL; 4258 } 4259 CmdlineInfo *p = get_ccline_ptr(); 4260 4261 if (p == NULL) { 4262 return NULL; 4263 } 4264 return xstrnsave(p->cmdbuff, (size_t)p->cmdlen); 4265 } 4266 4267 /// Get the current command-line completion pattern. 4268 static char *get_cmdline_completion_pattern(void) 4269 { 4270 if (cmdline_star > 0) { 4271 return NULL; 4272 } 4273 4274 CmdlineInfo *p = get_ccline_ptr(); 4275 if (p == NULL || p->xpc == NULL) { 4276 return NULL; 4277 } 4278 4279 int xp_context = p->xpc->xp_context; 4280 if (xp_context == EXPAND_NOTHING) { 4281 set_expand_context(p->xpc); 4282 xp_context = p->xpc->xp_context; 4283 p->xpc->xp_context = EXPAND_NOTHING; 4284 } 4285 if (xp_context == EXPAND_UNSUCCESSFUL) { 4286 return NULL; 4287 } 4288 4289 char *compl_pat = p->xpc->xp_pattern; 4290 if (compl_pat == NULL) { 4291 return NULL; 4292 } 4293 4294 return xstrdup(compl_pat); 4295 } 4296 4297 /// Get the command-line completion type. 4298 static char *get_cmdline_completion(void) 4299 { 4300 if (cmdline_star > 0) { 4301 return NULL; 4302 } 4303 4304 CmdlineInfo *p = get_ccline_ptr(); 4305 if (p == NULL || p->xpc == NULL) { 4306 return NULL; 4307 } 4308 4309 int xp_context = p->xpc->xp_context; 4310 if (xp_context == EXPAND_NOTHING) { 4311 set_expand_context(p->xpc); 4312 xp_context = p->xpc->xp_context; 4313 p->xpc->xp_context = EXPAND_NOTHING; 4314 } 4315 if (xp_context == EXPAND_UNSUCCESSFUL) { 4316 return NULL; 4317 } 4318 4319 return cmdcomplete_type_to_str(xp_context, p->xpc->xp_arg); 4320 } 4321 4322 /// "getcmdcomplpat()" function 4323 void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4324 { 4325 rettv->v_type = VAR_STRING; 4326 rettv->vval.v_string = get_cmdline_completion_pattern(); 4327 } 4328 4329 /// "getcmdcompltype()" function 4330 void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4331 { 4332 rettv->v_type = VAR_STRING; 4333 rettv->vval.v_string = get_cmdline_completion(); 4334 } 4335 4336 /// "getcmdline()" function 4337 void f_getcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4338 { 4339 rettv->v_type = VAR_STRING; 4340 rettv->vval.v_string = get_cmdline_str(); 4341 } 4342 4343 /// "getcmdpos()" function 4344 void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4345 { 4346 CmdlineInfo *p = get_ccline_ptr(); 4347 rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0; 4348 } 4349 4350 /// "getcmdprompt()" function 4351 void f_getcmdprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4352 { 4353 CmdlineInfo *p = get_ccline_ptr(); 4354 rettv->v_type = VAR_STRING; 4355 rettv->vval.v_string = p != NULL && p->cmdprompt != NULL 4356 ? xstrdup(p->cmdprompt) : NULL; 4357 } 4358 4359 /// "getcmdscreenpos()" function 4360 void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4361 { 4362 CmdlineInfo *p = get_ccline_ptr(); 4363 rettv->vval.v_number = p != NULL ? p->cmdspos + 1 : 0; 4364 } 4365 4366 /// "getcmdtype()" function 4367 void f_getcmdtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4368 { 4369 rettv->v_type = VAR_STRING; 4370 rettv->vval.v_string = xmallocz(1); 4371 rettv->vval.v_string[0] = (char)get_cmdline_type(); 4372 } 4373 4374 /// Set the command line str to "str". 4375 /// @return 1 when failed, 0 when OK. 4376 static int set_cmdline_str(const char *str, int pos) 4377 { 4378 CmdlineInfo *p = get_ccline_ptr(); 4379 4380 if (p == NULL) { 4381 return 1; 4382 } 4383 4384 int len = (int)strlen(str); 4385 realloc_cmdbuff(len + 1); 4386 p->cmdlen = len; 4387 STRCPY(p->cmdbuff, str); 4388 4389 p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos; 4390 new_cmdpos = p->cmdpos; 4391 4392 redrawcmd(); 4393 4394 // Trigger CmdlineChanged autocommands. 4395 do_autocmd_cmdlinechanged(get_cmdline_type()); 4396 4397 return 0; 4398 } 4399 4400 /// Set the command line byte position to "pos". Zero is the first position. 4401 /// Only works when the command line is being edited. 4402 /// @return 1 when failed, 0 when OK. 4403 static int set_cmdline_pos(int pos) 4404 { 4405 CmdlineInfo *p = get_ccline_ptr(); 4406 4407 if (p == NULL) { 4408 return 1; 4409 } 4410 4411 // The position is not set directly but after CTRL-\ e or CTRL-R = has 4412 // changed the command line. 4413 new_cmdpos = MAX(0, pos); 4414 4415 return 0; 4416 } 4417 4418 /// "setcmdline()" function 4419 void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4420 { 4421 if (tv_check_for_string_arg(argvars, 0) == FAIL 4422 || tv_check_for_opt_number_arg(argvars, 1) == FAIL) { 4423 return; 4424 } 4425 4426 int pos = -1; 4427 if (argvars[1].v_type != VAR_UNKNOWN) { 4428 bool error = false; 4429 4430 pos = (int)tv_get_number_chk(&argvars[1], &error) - 1; 4431 if (error) { 4432 return; 4433 } 4434 if (pos < 0) { 4435 emsg(_(e_positive)); 4436 return; 4437 } 4438 } 4439 4440 // Use tv_get_string() to handle a NULL string like an empty string. 4441 rettv->vval.v_number = set_cmdline_str(tv_get_string(&argvars[0]), pos); 4442 } 4443 4444 /// "setcmdpos()" function 4445 void f_setcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4446 { 4447 const int pos = (int)tv_get_number(&argvars[0]) - 1; 4448 4449 if (pos >= 0) { 4450 rettv->vval.v_number = set_cmdline_pos(pos); 4451 } 4452 } 4453 4454 /// Return the first character of the current command line. 4455 int get_cmdline_firstc(void) 4456 { 4457 return ccline.cmdfirstc; 4458 } 4459 4460 /// Get indices that specify a range within a list (not a range of text lines 4461 /// in a buffer!) from a string. Used for ":history" and ":clist". 4462 /// 4463 /// @param str string to parse range from 4464 /// @param num1 from 4465 /// @param num2 to 4466 /// 4467 /// @return OK if parsed successfully, otherwise FAIL. 4468 int get_list_range(char **str, int *num1, int *num2) 4469 { 4470 int len; 4471 bool first = false; 4472 varnumber_T num; 4473 4474 *str = skipwhite((*str)); 4475 if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range 4476 vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL); 4477 *str += len; 4478 // overflow 4479 if (num > INT_MAX) { 4480 return FAIL; 4481 } 4482 4483 *num1 = (int)num; 4484 first = true; 4485 } 4486 *str = skipwhite((*str)); 4487 if (**str == ',') { // parse "to" part of range 4488 *str = skipwhite((*str) + 1); 4489 vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false, NULL); 4490 if (len > 0) { 4491 *str = skipwhite((*str) + len); 4492 // overflow 4493 if (num > INT_MAX) { 4494 return FAIL; 4495 } 4496 4497 *num2 = (int)num; 4498 } else if (!first) { // no number given at all 4499 return FAIL; 4500 } 4501 } else if (first) { // only one number given 4502 *num2 = *num1; 4503 } 4504 return OK; 4505 } 4506 4507 void cmdline_init(void) 4508 { 4509 CLEAR_FIELD(ccline); 4510 } 4511 4512 /// Check value of 'cedit' and set cedit_key. 4513 /// Returns NULL if value is OK, error message otherwise. 4514 const char *did_set_cedit(optset_T *args) 4515 { 4516 if (*p_cedit == NUL) { 4517 cedit_key = -1; 4518 } else { 4519 int n = string_to_key(p_cedit); 4520 if (n == 0 || vim_isprintc(n)) { 4521 return e_invarg; 4522 } 4523 cedit_key = n; 4524 } 4525 return NULL; 4526 } 4527 4528 /// Open a window on the current command line and history. Allow editing in 4529 /// the window. Returns when the window is closed. 4530 /// Returns: 4531 /// CR if the command is to be executed 4532 /// Ctrl_C if it is to be abandoned 4533 /// K_IGNORE if editing continues 4534 static int open_cmdwin(void) 4535 { 4536 bufref_T old_curbuf; 4537 bufref_T bufref; 4538 win_T *old_curwin = curwin; 4539 int i; 4540 garray_T winsizes; 4541 int save_restart_edit = restart_edit; 4542 int save_State = State; 4543 bool save_exmode = exmode_active; 4544 bool save_cmdmsg_rl = cmdmsg_rl; 4545 4546 // Can't do this when text or buffer is locked. 4547 // Can't do this recursively. Can't do it when typing a password. 4548 if (text_or_buf_locked() || cmdwin_type != 0 || cmdline_star > 0) { 4549 beep_flush(); 4550 return K_IGNORE; 4551 } 4552 4553 set_bufref(&old_curbuf, curbuf); 4554 4555 // Save current window sizes. 4556 win_size_save(&winsizes); 4557 4558 // When using completion in Insert mode with <C-R>=<C-F> one can open the 4559 // command line window, but we don't want the popup menu then. 4560 pum_undisplay(true); 4561 4562 // don't use a new tab page 4563 cmdmod.cmod_tab = 0; 4564 cmdmod.cmod_flags |= CMOD_NOSWAPFILE; 4565 4566 // Create a window for the command-line buffer. 4567 if (win_split((int)p_cwh, WSP_BOT) == FAIL) { 4568 beep_flush(); 4569 ga_clear(&winsizes); 4570 return K_IGNORE; 4571 } 4572 // win_split() autocommands may have messed with the old window or buffer. 4573 // Treat it as abandoning this command-line. 4574 if (!win_valid(old_curwin) || curwin == old_curwin || !bufref_valid(&old_curbuf) 4575 || old_curwin->w_buffer != old_curbuf.br_buf) { 4576 beep_flush(); 4577 ga_clear(&winsizes); 4578 return Ctrl_C; 4579 } 4580 // Don't let quitting the More prompt make this fail. 4581 got_int = false; 4582 4583 // Set "cmdwin_..." variables before any autocommands may mess things up. 4584 cmdwin_type = get_cmdline_type(); 4585 cmdwin_level = ccline.level; 4586 cmdwin_win = curwin; 4587 cmdwin_old_curwin = old_curwin; 4588 4589 // Create empty command-line buffer. Be especially cautious of BufLeave 4590 // autocommands from do_ecmd(), as cmdwin restrictions do not apply to them! 4591 const int newbuf_status = buf_open_scratch(0, NULL); 4592 const bool cmdwin_valid = win_valid(cmdwin_win); 4593 if (newbuf_status == FAIL || !cmdwin_valid || curwin != cmdwin_win || !win_valid(old_curwin) 4594 || !bufref_valid(&old_curbuf) || old_curwin->w_buffer != old_curbuf.br_buf) { 4595 if (newbuf_status == OK) { 4596 set_bufref(&bufref, curbuf); 4597 } 4598 if (cmdwin_valid && !last_window(cmdwin_win)) { 4599 win_close(cmdwin_win, true, false); 4600 } 4601 // win_close() autocommands may have already deleted the buffer. 4602 if (newbuf_status == OK && bufref_valid(&bufref) && bufref.br_buf != curbuf) { 4603 close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false); 4604 } 4605 4606 cmdwin_type = 0; 4607 cmdwin_level = 0; 4608 cmdwin_win = NULL; 4609 cmdwin_old_curwin = NULL; 4610 beep_flush(); 4611 ga_clear(&winsizes); 4612 return Ctrl_C; 4613 } 4614 cmdwin_buf = curbuf; 4615 4616 // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. 4617 set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); 4618 curbuf->b_p_ma = true; 4619 curwin->w_p_fen = false; 4620 curwin->w_p_rl = cmdmsg_rl; 4621 cmdmsg_rl = false; 4622 4623 // Don't allow switching to another buffer. 4624 curbuf->b_ro_locked++; 4625 4626 // Showing the prompt may have set need_wait_return, reset it. 4627 need_wait_return = false; 4628 4629 const int histtype = hist_char2type(cmdwin_type); 4630 if (histtype == HIST_CMD || histtype == HIST_DEBUG) { 4631 if (p_wc == TAB) { 4632 add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true); 4633 add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true); 4634 } 4635 set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("vim"), OPT_LOCAL); 4636 } 4637 curbuf->b_ro_locked--; 4638 4639 // Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin 4640 // sets 'textwidth' to 78). 4641 curbuf->b_p_tw = 0; 4642 4643 // Fill the buffer with the history. 4644 init_history(); 4645 if (get_hislen() > 0 && histtype != HIST_INVALID) { 4646 i = *get_hisidx(histtype); 4647 if (i >= 0) { 4648 linenr_T lnum = 0; 4649 do { 4650 if (++i == get_hislen()) { 4651 i = 0; 4652 } 4653 if (get_histentry(histtype)[i].hisstr != NULL) { 4654 ml_append(lnum++, get_histentry(histtype)[i].hisstr, 0, false); 4655 } 4656 } while (i != *get_hisidx(histtype)); 4657 } 4658 } 4659 4660 // Replace the empty last line with the current command-line and put the 4661 // cursor there. 4662 ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true); 4663 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 4664 curwin->w_cursor.col = ccline.cmdpos; 4665 changed_line_abv_curs(); 4666 invalidate_botline_win(curwin); 4667 ui_ext_cmdline_hide(false); 4668 redraw_later(curwin, UPD_SOME_VALID); 4669 4670 // No Ex mode here! 4671 exmode_active = false; 4672 4673 State = MODE_NORMAL; 4674 setmouse(); 4675 clear_showcmd(); 4676 4677 // Reset here so it can be set by a CmdwinEnter autocommand. 4678 cmdwin_result = 0; 4679 4680 // Trigger CmdwinEnter autocommands. 4681 trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINENTER); 4682 if (restart_edit != 0) { // autocmd with ":startinsert" 4683 stuffcharReadbuff(K_NOP); 4684 } 4685 4686 i = RedrawingDisabled; 4687 RedrawingDisabled = 0; 4688 int save_count = save_batch_count(); 4689 4690 // Call the main loop until <CR> or CTRL-C is typed. 4691 normal_enter(true, false); 4692 4693 RedrawingDisabled = i; 4694 restore_batch_count(save_count); 4695 4696 const bool save_KeyTyped = KeyTyped; 4697 4698 // Trigger CmdwinLeave autocommands. 4699 trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINLEAVE); 4700 4701 // Restore KeyTyped in case it is modified by autocommands 4702 KeyTyped = save_KeyTyped; 4703 4704 cmdwin_type = 0; 4705 cmdwin_level = 0; 4706 cmdwin_buf = NULL; 4707 cmdwin_win = NULL; 4708 cmdwin_old_curwin = NULL; 4709 4710 exmode_active = save_exmode; 4711 4712 // Safety check: The old window or buffer was changed or deleted: It's a bug 4713 // when this happens! 4714 if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf) 4715 || old_curwin->w_buffer != old_curbuf.br_buf) { 4716 cmdwin_result = Ctrl_C; 4717 emsg(_(e_active_window_or_buffer_changed_or_deleted)); 4718 } else { 4719 win_T *wp; 4720 // autocmds may abort script processing 4721 if (aborting() && cmdwin_result != K_IGNORE) { 4722 cmdwin_result = Ctrl_C; 4723 } 4724 // Set the new command line from the cmdline buffer. 4725 dealloc_cmdbuff(); 4726 4727 if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed 4728 const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; 4729 size_t plen = (cmdwin_result == K_XF2) ? 2 : 3; 4730 4731 if (histtype == HIST_CMD) { 4732 // Execute the command directly. 4733 ccline.cmdbuff = xmemdupz(p, plen); 4734 ccline.cmdlen = (int)plen; 4735 ccline.cmdbufflen = (int)plen + 1; 4736 cmdwin_result = CAR; 4737 } else { 4738 // First need to cancel what we were doing. 4739 stuffcharReadbuff(':'); 4740 stuffReadbuff(p); 4741 stuffcharReadbuff(CAR); 4742 } 4743 } else if (cmdwin_result == Ctrl_C) { 4744 // :q or :close, don't execute any command 4745 // and don't modify the cmd window. 4746 ccline.cmdbuff = NULL; 4747 } else { 4748 ccline.cmdlen = get_cursor_line_len(); 4749 ccline.cmdbufflen = ccline.cmdlen + 1; 4750 ccline.cmdbuff = xstrnsave(get_cursor_line_ptr(), (size_t)ccline.cmdlen); 4751 } 4752 4753 if (ccline.cmdbuff == NULL) { 4754 ccline.cmdbuff = xmemdupz("", 0); 4755 ccline.cmdlen = 0; 4756 ccline.cmdbufflen = 1; 4757 ccline.cmdpos = 0; 4758 cmdwin_result = Ctrl_C; 4759 } else { 4760 ccline.cmdpos = curwin->w_cursor.col; 4761 // If the cursor is on the last character, it probably should be after it. 4762 if (ccline.cmdpos == ccline.cmdlen - 1 || ccline.cmdpos > ccline.cmdlen) { 4763 ccline.cmdpos = ccline.cmdlen; 4764 } 4765 if (cmdwin_result == K_IGNORE) { 4766 ccline.cmdspos = cmd_screencol(ccline.cmdpos); 4767 redrawcmd(); 4768 } 4769 } 4770 4771 // Avoid command-line window first character being concealed. 4772 curwin->w_p_cole = 0; 4773 // First go back to the original window. 4774 wp = curwin; 4775 set_bufref(&bufref, curbuf); 4776 skip_win_fix_cursor = true; 4777 win_goto(old_curwin); 4778 4779 // win_goto() may trigger an autocommand that already closes the 4780 // cmdline window. 4781 if (win_valid(wp) && wp != curwin) { 4782 win_close(wp, true, false); 4783 } 4784 4785 // win_close() may have already wiped the buffer when 'bh' is 4786 // set to 'wipe', autocommands may have closed other windows 4787 if (bufref_valid(&bufref) && bufref.br_buf != curbuf) { 4788 close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false); 4789 } 4790 4791 // Restore window sizes. 4792 win_size_restore(&winsizes); 4793 skip_win_fix_cursor = false; 4794 } 4795 4796 ga_clear(&winsizes); 4797 restart_edit = save_restart_edit; 4798 cmdmsg_rl = save_cmdmsg_rl; 4799 4800 State = save_State; 4801 may_trigger_modechanged(); 4802 setmouse(); 4803 setcursor(); 4804 4805 return cmdwin_result; 4806 } 4807 4808 /// @return true if in the cmdwin, not editing the command line. 4809 bool is_in_cmdwin(void) 4810 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 4811 { 4812 return cmdwin_type != 0 && get_cmdline_type() == NUL; 4813 } 4814 4815 /// Get script string 4816 /// 4817 /// Used for commands which accept either `:command script` or 4818 /// 4819 /// :command << endmarker 4820 /// script 4821 /// endmarker 4822 /// 4823 /// @param eap Command being run. 4824 /// @param[out] lenp Location where length of resulting string is saved. Will 4825 /// be set to zero when skipping. 4826 /// 4827 /// @return [allocated] NULL or script. Does not show any error messages. 4828 /// NULL is returned when skipping and on error. 4829 char *script_get(exarg_T *const eap, size_t *const lenp) 4830 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC 4831 { 4832 char *cmd = eap->arg; 4833 4834 if (cmd[0] != '<' || cmd[1] != '<' || eap->ea_getline == NULL) { 4835 *lenp = strlen(eap->arg); 4836 return eap->skip ? NULL : xmemdupz(eap->arg, *lenp); 4837 } 4838 cmd += 2; 4839 4840 garray_T ga = { .ga_data = NULL, .ga_len = 0 }; 4841 4842 list_T *const l = heredoc_get(eap, cmd, true); 4843 if (l == NULL) { 4844 return NULL; 4845 } 4846 4847 if (!eap->skip) { 4848 ga_init(&ga, 1, 0x400); 4849 } 4850 4851 TV_LIST_ITER_CONST(l, li, { 4852 if (!eap->skip) { 4853 ga_concat(&ga, tv_get_string(TV_LIST_ITEM_TV(li))); 4854 ga_append(&ga, '\n'); 4855 } 4856 }); 4857 *lenp = (size_t)ga.ga_len; // Set length without trailing NUL. 4858 if (!eap->skip) { 4859 ga_append(&ga, NUL); 4860 } 4861 4862 tv_list_free(l); 4863 return (char *)ga.ga_data; 4864 } 4865 4866 /// This function is used by f_input() and f_inputdialog() functions. The third 4867 /// argument to f_input() specifies the type of completion to use at the 4868 /// prompt. The third argument to f_inputdialog() specifies the value to return 4869 /// when the user cancels the prompt. 4870 void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, 4871 const bool secret) 4872 FUNC_ATTR_NONNULL_ALL 4873 { 4874 rettv->v_type = VAR_STRING; 4875 rettv->vval.v_string = NULL; 4876 4877 if (cmdpreview) { 4878 return; 4879 } 4880 4881 const char *prompt; 4882 const char *defstr = ""; 4883 typval_T *cancelreturn = NULL; 4884 typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; 4885 const char *xp_name = NULL; 4886 Callback input_callback = CALLBACK_INIT; 4887 char prompt_buf[NUMBUFLEN]; 4888 char defstr_buf[NUMBUFLEN]; 4889 char cancelreturn_buf[NUMBUFLEN]; 4890 char xp_name_buf[NUMBUFLEN]; 4891 char def[1] = { 0 }; 4892 if (argvars[0].v_type == VAR_DICT) { 4893 if (argvars[1].v_type != VAR_UNKNOWN) { 4894 emsg(_("E5050: {opts} must be the only argument")); 4895 return; 4896 } 4897 dict_T *const dict = argvars[0].vval.v_dict; 4898 prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); 4899 if (prompt == NULL) { 4900 return; 4901 } 4902 defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); 4903 if (defstr == NULL) { 4904 return; 4905 } 4906 dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); 4907 if (cancelreturn_di != NULL) { 4908 cancelreturn = &cancelreturn_di->di_tv; 4909 } 4910 xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), 4911 xp_name_buf, def); 4912 if (xp_name == NULL) { // error 4913 return; 4914 } 4915 if (xp_name == def) { // default to NULL 4916 xp_name = NULL; 4917 } 4918 if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { 4919 return; 4920 } 4921 } else { 4922 prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); 4923 if (prompt == NULL) { 4924 return; 4925 } 4926 if (argvars[1].v_type != VAR_UNKNOWN) { 4927 defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); 4928 if (defstr == NULL) { 4929 return; 4930 } 4931 if (argvars[2].v_type != VAR_UNKNOWN) { 4932 const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); 4933 if (strarg2 == NULL) { 4934 return; 4935 } 4936 if (inputdialog) { 4937 cancelreturn_strarg2.v_type = VAR_STRING; 4938 cancelreturn_strarg2.vval.v_string = (char *)strarg2; 4939 cancelreturn = &cancelreturn_strarg2; 4940 } else { 4941 xp_name = strarg2; 4942 } 4943 } 4944 } 4945 } 4946 4947 int xp_type = EXPAND_NOTHING; 4948 char *xp_arg = NULL; 4949 if (xp_name != NULL) { 4950 // input() with a third argument: completion 4951 const int xp_namelen = (int)strlen(xp_name); 4952 4953 uint32_t argt = 0; 4954 if (parse_compl_arg(xp_name, xp_namelen, &xp_type, 4955 &argt, &xp_arg) == FAIL) { 4956 return; 4957 } 4958 } 4959 4960 // Only the part of the message after the last NL is considered as 4961 // prompt for the command line, unlsess cmdline is externalized 4962 const char *p = prompt; 4963 if (!ui_has(kUICmdline)) { 4964 const char *lastnl = strrchr(prompt, '\n'); 4965 if (lastnl != NULL) { 4966 p = lastnl + 1; 4967 msg_start(); 4968 msg_clr_eos(); 4969 msg_puts_len(prompt, p - prompt, get_echo_hl_id(), false); 4970 msg_didout = false; 4971 msg_starthere(); 4972 } 4973 } 4974 cmdline_row = msg_row; 4975 4976 stuffReadbuffSpec(defstr); 4977 4978 const int save_ex_normal_busy = ex_normal_busy; 4979 ex_normal_busy = 0; 4980 rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(), 4981 xp_type, xp_arg, input_callback, false, NULL); 4982 ex_normal_busy = save_ex_normal_busy; 4983 callback_free(&input_callback); 4984 4985 if (rettv->vval.v_string == NULL && cancelreturn != NULL) { 4986 tv_copy(cancelreturn, rettv); 4987 } 4988 4989 xfree(xp_arg); 4990 4991 // Since the user typed this, no need to wait for return. 4992 need_wait_return = false; 4993 msg_didout = false; 4994 } 4995 4996 /// "wildtrigger()" function 4997 void f_wildtrigger(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4998 { 4999 if (!(State & MODE_CMDLINE) || char_avail() 5000 || wild_menu_showing 5001 || cmdline_pum_active()) { 5002 return; 5003 } 5004 5005 int cmd_type = get_cmdline_type(); 5006 5007 if (cmd_type == ':' || cmd_type == '/' || cmd_type == '?') { 5008 // Add K_WILD as a single special key 5009 uint8_t key_string[4]; 5010 key_string[0] = K_SPECIAL; 5011 key_string[1] = KS_EXTRA; 5012 key_string[2] = KE_WILD; 5013 key_string[3] = NUL; 5014 5015 // Insert it into the typeahead buffer 5016 ins_typebuf((char *)key_string, REMAP_NONE, 0, true, false); 5017 } 5018 }