edit.c (129514B)
1 // edit.c: functions for Insert mode 2 3 #include <assert.h> 4 #include <ctype.h> 5 #include <inttypes.h> 6 #include <stdbool.h> 7 #include <stddef.h> 8 #include <string.h> 9 #include <uv.h> 10 11 #include "klib/kvec.h" 12 #include "nvim/api/private/defs.h" 13 #include "nvim/ascii_defs.h" 14 #include "nvim/autocmd.h" 15 #include "nvim/autocmd_defs.h" 16 #include "nvim/buffer.h" 17 #include "nvim/buffer_defs.h" 18 #include "nvim/change.h" 19 #include "nvim/charset.h" 20 #include "nvim/cursor.h" 21 #include "nvim/decoration.h" 22 #include "nvim/digraph.h" 23 #include "nvim/drawscreen.h" 24 #include "nvim/edit.h" 25 #include "nvim/errors.h" 26 #include "nvim/eval.h" 27 #include "nvim/eval/typval_defs.h" 28 #include "nvim/eval/vars.h" 29 #include "nvim/ex_cmds_defs.h" 30 #include "nvim/ex_docmd.h" 31 #include "nvim/extmark.h" 32 #include "nvim/extmark_defs.h" 33 #include "nvim/fileio.h" 34 #include "nvim/fold.h" 35 #include "nvim/getchar.h" 36 #include "nvim/gettext_defs.h" 37 #include "nvim/globals.h" 38 #include "nvim/grid.h" 39 #include "nvim/grid_defs.h" 40 #include "nvim/highlight.h" 41 #include "nvim/highlight_defs.h" 42 #include "nvim/highlight_group.h" 43 #include "nvim/indent.h" 44 #include "nvim/indent_c.h" 45 #include "nvim/insexpand.h" 46 #include "nvim/keycodes.h" 47 #include "nvim/macros_defs.h" 48 #include "nvim/mapping.h" 49 #include "nvim/mark.h" 50 #include "nvim/mark_defs.h" 51 #include "nvim/marktree_defs.h" 52 #include "nvim/mbyte.h" 53 #include "nvim/mbyte_defs.h" 54 #include "nvim/memline.h" 55 #include "nvim/memline_defs.h" 56 #include "nvim/memory.h" 57 #include "nvim/message.h" 58 #include "nvim/mouse.h" 59 #include "nvim/move.h" 60 #include "nvim/normal.h" 61 #include "nvim/normal_defs.h" 62 #include "nvim/ops.h" 63 #include "nvim/option.h" 64 #include "nvim/option_vars.h" 65 #include "nvim/os/input.h" 66 #include "nvim/plines.h" 67 #include "nvim/popupmenu.h" 68 #include "nvim/pos_defs.h" 69 #include "nvim/register.h" 70 #include "nvim/search.h" 71 #include "nvim/state.h" 72 #include "nvim/state_defs.h" 73 #include "nvim/strings.h" 74 #include "nvim/syntax.h" 75 #include "nvim/terminal.h" 76 #include "nvim/textformat.h" 77 #include "nvim/textobject.h" 78 #include "nvim/types_defs.h" 79 #include "nvim/ui.h" 80 #include "nvim/ui_defs.h" 81 #include "nvim/undo.h" 82 #include "nvim/vim_defs.h" 83 #include "nvim/window.h" 84 85 typedef struct { 86 VimState state; 87 cmdarg_T *ca; 88 int mincol; 89 int cmdchar; 90 int cmdchar_todo; // cmdchar to handle once in init_prompt 91 bool ins_just_started; 92 int startln; 93 int count; 94 int c; 95 int lastc; 96 int i; 97 bool did_backspace; // previous char was backspace 98 bool line_is_white; // line is empty before insert 99 linenr_T old_topline; // topline before insertion 100 int old_topfill; 101 int inserted_space; // just inserted a space 102 int replaceState; 103 int did_restart_edit; // remember if insert mode was restarted 104 // after a ctrl+o 105 bool nomove; 106 } InsertState; 107 108 #include "edit.c.generated.h" 109 enum { 110 BACKSPACE_CHAR = 1, 111 BACKSPACE_WORD = 2, 112 BACKSPACE_WORD_NOT_SPACE = 3, 113 BACKSPACE_LINE = 4, 114 }; 115 116 /// Set when doing something for completion that may call edit() recursively, 117 /// which is not allowed. 118 static bool compl_busy = false; 119 120 static colnr_T Insstart_textlen; // length of line when insert started 121 static colnr_T Insstart_blank_vcol; // vcol for first inserted blank 122 static bool update_Insstart_orig = true; // set Insstart_orig to Insstart 123 124 /// the text of the previous insert, K_SPECIAL is escaped 125 static String last_insert = STRING_INIT; 126 static int last_insert_skip; // nr of chars in front of previous insert 127 static int new_insert_skip; // nr of chars in front of current insert 128 static int did_restart_edit; // "restart_edit" when calling edit() 129 130 static bool can_cindent; // may do cindenting on this line 131 132 static bool revins_on; // reverse insert mode on 133 static int revins_chars; // how much to skip after edit 134 static int revins_legal; // was the last char 'legal'? 135 static int revins_scol; // start column of revins session 136 137 static bool ins_need_undo; // call u_save() before inserting a 138 // char. Set when edit() is called. 139 // after that arrow_used is used. 140 141 static TriState dont_sync_undo = kFalse; // CTRL-G U prevents syncing undo 142 // for the next left/right cursor key 143 144 static linenr_T o_lnum = 0; 145 146 static kvec_t(char) replace_stack = KV_INITIAL_VALUE; 147 148 #define TRIGGER_AUTOCOMPLETE() \ 149 do { \ 150 redraw_later(curwin, UPD_VALID); \ 151 update_screen(); /* Show char deletion immediately */ \ 152 ui_flush(); \ 153 ins_compl_enable_autocomplete(); \ 154 insert_do_complete(s); \ 155 break; \ 156 } while (0) 157 158 #define MAY_TRIGGER_AUTOCOMPLETE(c) \ 159 do { \ 160 if (ins_compl_has_autocomplete() && !char_avail() && curwin->w_cursor.col > 0) { \ 161 (c) = char_before_cursor(); \ 162 if (vim_isprintc(c)) { \ 163 TRIGGER_AUTOCOMPLETE(); \ 164 } \ 165 } \ 166 } while (0) 167 168 static void insert_enter(InsertState *s) 169 { 170 s->did_backspace = true; 171 s->old_topfill = -1; 172 s->replaceState = MODE_REPLACE; 173 s->cmdchar_todo = s->cmdchar; 174 s->ins_just_started = true; 175 // Remember whether editing was restarted after CTRL-O 176 did_restart_edit = restart_edit; 177 // sleep before redrawing, needed for "CTRL-O :" that results in an 178 // error message 179 msg_check_for_delay(true); 180 // set Insstart_orig to Insstart 181 update_Insstart_orig = true; 182 183 ins_compl_clear(); // clear stuff for CTRL-X mode 184 185 // Trigger InsertEnter autocommands. Do not do this for "r<CR>" or "grx". 186 if (s->cmdchar != 'r' && s->cmdchar != 'v') { 187 pos_T save_cursor = curwin->w_cursor; 188 189 const char *const ptr = s->cmdchar == 'R' ? "r" : s->cmdchar == 'V' ? "v" : "i"; 190 set_vim_var_string(VV_INSERTMODE, ptr, 1); 191 set_vim_var_string(VV_CHAR, NULL, -1); 192 ins_apply_autocmds(EVENT_INSERTENTER); 193 194 // Check for changed highlighting, e.g. for ModeMsg. 195 if (need_highlight_changed) { 196 highlight_changed(); 197 } 198 199 // Make sure the cursor didn't move. Do call check_cursor_col() in 200 // case the text was modified. Since Insert mode was not started yet 201 // a call to check_cursor_col() may move the cursor, especially with 202 // the "A" command, thus set State to avoid that. Also check that the 203 // line number is still valid (lines may have been deleted). 204 // Do not restore if v:char was set to a non-empty string. 205 if (!equalpos(curwin->w_cursor, save_cursor) 206 && *get_vim_var_str(VV_CHAR) == NUL 207 && save_cursor.lnum <= curbuf->b_ml.ml_line_count) { 208 int save_state = State; 209 210 curwin->w_cursor = save_cursor; 211 State = MODE_INSERT; 212 check_cursor_col(curwin); 213 State = save_state; 214 } 215 } 216 217 // When doing a paste with the middle mouse button, Insstart is set to 218 // where the paste started. 219 if (where_paste_started.lnum != 0) { 220 Insstart = where_paste_started; 221 } else { 222 Insstart = curwin->w_cursor; 223 if (s->startln) { 224 Insstart.col = 0; 225 } 226 } 227 228 Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); 229 Insstart_blank_vcol = MAXCOL; 230 231 if (!did_ai) { 232 ai_col = 0; 233 } 234 235 if (s->cmdchar != NUL && restart_edit == 0) { 236 ResetRedobuff(); 237 AppendNumberToRedobuff(s->count); 238 if (s->cmdchar == 'V' || s->cmdchar == 'v') { 239 // "gR" or "gr" command 240 AppendCharToRedobuff('g'); 241 AppendCharToRedobuff((s->cmdchar == 'v') ? 'r' : 'R'); 242 } else { 243 AppendCharToRedobuff(s->cmdchar); 244 if (s->cmdchar == 'g') { // "gI" command 245 AppendCharToRedobuff('I'); 246 } else if (s->cmdchar == 'r') { // "r<CR>" command 247 s->count = 1; // insert only one <CR> 248 } 249 } 250 } 251 252 if (s->cmdchar == 'R') { 253 State = MODE_REPLACE; 254 } else if (s->cmdchar == 'V' || s->cmdchar == 'v') { 255 State = MODE_VREPLACE; 256 s->replaceState = MODE_VREPLACE; 257 orig_line_count = curbuf->b_ml.ml_line_count; 258 vr_lines_changed = 1; 259 } else { 260 State = MODE_INSERT; 261 } 262 263 may_trigger_modechanged(); 264 stop_insert_mode = false; 265 266 // need to position cursor again when on a TAB and 267 // when on a char with inline virtual text 268 if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) { 269 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); 270 } 271 272 // Enable langmap or IME, indicated by 'iminsert'. 273 // Note that IME may enabled/disabled without us noticing here, thus the 274 // 'iminsert' value may not reflect what is actually used. It is updated 275 // when hitting <Esc>. 276 if (curbuf->b_p_iminsert == B_IMODE_LMAP) { 277 State |= MODE_LANGMAP; 278 } 279 280 setmouse(); 281 clear_showcmd(); 282 // there is no reverse replace mode 283 revins_on = (State == MODE_INSERT && p_ri); 284 if (revins_on) { 285 undisplay_dollar(); 286 } 287 revins_chars = 0; 288 revins_legal = 0; 289 revins_scol = -1; 290 291 // Handle restarting Insert mode. 292 // Don't do this for "CTRL-O ." (repeat an insert): we get here with 293 // restart_edit non-zero, and something in the stuff buffer. 294 if (restart_edit != 0 && stuff_empty()) { 295 // After a paste we consider text typed to be part of the insert for 296 // the pasted text. You can backspace over the pasted text too. 297 arrow_used = where_paste_started.lnum == 0; 298 restart_edit = 0; 299 300 // If the cursor was after the end-of-line before the CTRL-O and it is 301 // now at the end-of-line, put it after the end-of-line (this is not 302 // correct in very rare cases). 303 // Also do this if curswant is greater than the current virtual 304 // column. Eg after "^O$" or "^O80|". 305 validate_virtcol(curwin); 306 update_curswant(); 307 const char *ptr; 308 if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum) 309 || curwin->w_curswant > curwin->w_virtcol) 310 && *(ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) { 311 if (ptr[1] == NUL) { 312 curwin->w_cursor.col++; 313 } else { 314 s->i = utfc_ptr2len(ptr); 315 if (ptr[s->i] == NUL) { 316 curwin->w_cursor.col += s->i; 317 } 318 } 319 } 320 ins_at_eol = false; 321 } else { 322 arrow_used = false; 323 } 324 325 // we are in insert mode now, don't need to start it anymore 326 need_start_insertmode = false; 327 328 // Need to save the line for undo before inserting the first char. 329 ins_need_undo = true; 330 331 where_paste_started.lnum = 0; 332 can_cindent = true; 333 // The cursor line is not in a closed fold, unless restarting. 334 if (did_restart_edit == 0) { 335 foldOpenCursor(); 336 } 337 338 // If 'showmode' is set, show the current (insert/replace/..) mode. 339 // A warning message for changing a readonly file is given here, before 340 // actually changing anything. It's put after the mode, if any. 341 s->i = 0; 342 if (p_smd && msg_silent == 0) { 343 s->i = showmode(); 344 } 345 346 if (did_restart_edit == 0) { 347 change_warning(curbuf, s->i == 0 ? 0 : s->i + 1); 348 } 349 350 ui_cursor_shape(); // may show different cursor shape 351 do_digraph(-1); // clear digraphs 352 353 // Get the current length of the redo buffer, those characters have to be 354 // skipped if we want to get to the inserted characters. 355 String inserted = get_inserted(); 356 new_insert_skip = (int)inserted.size; 357 if (inserted.data != NULL) { 358 xfree(inserted.data); 359 } 360 361 old_indent = 0; 362 363 do { 364 state_enter(&s->state); 365 // If s->count != 0, `ins_esc` will prepare the redo buffer for reprocessing 366 // and return false, causing `state_enter` to be called again. 367 } while (!ins_esc(&s->count, s->cmdchar, s->nomove)); 368 369 // Always update o_lnum, so that a "CTRL-O ." that adds a line 370 // still puts the cursor back after the inserted text. 371 if (ins_at_eol) { 372 o_lnum = curwin->w_cursor.lnum; 373 } 374 375 pum_check_clear(); 376 377 foldUpdateAfterInsert(); 378 // When CTRL-C was typed got_int will be set, with the result 379 // that the autocommands won't be executed. When mapped got_int 380 // is not set, but let's keep the behavior the same. 381 if (s->cmdchar != 'r' && s->cmdchar != 'v' && s->c != Ctrl_C) { 382 ins_apply_autocmds(EVENT_INSERTLEAVE); 383 } 384 did_cursorhold = false; 385 386 // ins_redraw() triggers TextChangedI only when no characters 387 // are in the typeahead buffer, so reset curbuf->b_last_changedtick 388 // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!) 389 // and the TextChangedI autocommand has been triggered. 390 if (!char_avail() && curbuf->b_last_changedtick_i == buf_get_changedtick(curbuf)) { 391 curbuf->b_last_changedtick = buf_get_changedtick(curbuf); 392 } 393 } 394 395 static int insert_check(VimState *state) 396 { 397 InsertState *s = (InsertState *)state; 398 399 if (!revins_legal) { 400 revins_scol = -1; // reset on illegal motions 401 } else { 402 revins_legal = 0; 403 } 404 405 if (arrow_used) { // don't repeat insert when arrow key used 406 s->count = 0; 407 } 408 409 if (update_Insstart_orig) { 410 Insstart_orig = Insstart; 411 } 412 413 if (curbuf->terminal && !stop_insert_mode) { 414 // Exit Insert mode and go to Terminal mode. 415 stop_insert_mode = true; 416 restart_edit = 'I'; 417 stuffcharReadbuff(K_NOP); 418 } 419 420 if (stop_insert_mode && !ins_compl_active()) { 421 // ":stopinsert" used 422 s->count = 0; 423 return 0; // exit insert mode 424 } 425 426 // set curwin->w_curswant for next K_DOWN or K_UP 427 if (!arrow_used) { 428 curwin->w_set_curswant = true; 429 } 430 431 // If there is no typeahead may check for timestamps (e.g., for when a 432 // menu invoked a shell command). 433 if (stuff_empty()) { 434 did_check_timestamps = false; 435 if (need_check_timestamps) { 436 check_timestamps(false); 437 } 438 } 439 440 // When emsg() was called msg_scroll will have been set. 441 msg_scroll = false; 442 443 // Open fold at the cursor line, according to 'foldopen'. 444 if (fdo_flags & kOptFdoFlagInsert) { 445 foldOpenCursor(); 446 } 447 448 // Close folds where the cursor isn't, according to 'foldclose' 449 if (!char_avail()) { 450 foldCheckClose(); 451 } 452 453 if (bt_prompt(curbuf)) { 454 init_prompt(s->cmdchar_todo); 455 s->cmdchar_todo = NUL; 456 } 457 458 // If we inserted a character at the last position of the last line in the 459 // window, scroll the window one line up. This avoids an extra redraw. This 460 // is detected when the cursor column is smaller after inserting something. 461 // Don't do this when the topline changed already, it has already been 462 // adjusted (by insertchar() calling open_line())). 463 // Also don't do this when 'smoothscroll' is set, as the window should then 464 // be scrolled by screen lines. 465 if (curbuf->b_mod_set 466 && curwin->w_p_wrap 467 && !curwin->w_p_sms 468 && !s->did_backspace 469 && curwin->w_topline == s->old_topline 470 && curwin->w_topfill == s->old_topfill 471 && s->count <= 1) { 472 s->mincol = curwin->w_wcol; 473 validate_cursor_col(curwin); 474 475 if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), 476 curbuf->b_p_ts, 477 curbuf->b_p_vts_array, 478 false) 479 && curwin->w_wrow == curwin->w_view_height - 1 - get_scrolloff_value(curwin) 480 && (curwin->w_cursor.lnum != curwin->w_topline 481 || curwin->w_topfill > 0)) { 482 if (curwin->w_topfill > 0) { 483 curwin->w_topfill--; 484 } else if (hasFolding(curwin, curwin->w_topline, NULL, &s->old_topline)) { 485 set_topline(curwin, s->old_topline + 1); 486 } else { 487 set_topline(curwin, curwin->w_topline + 1); 488 } 489 } 490 } 491 492 // May need to adjust w_topline to show the cursor. 493 if (s->count <= 1) { 494 update_topline(curwin); 495 } 496 497 s->did_backspace = false; 498 499 if (s->count <= 1) { 500 validate_cursor(curwin); // may set must_redraw 501 } 502 503 // Redraw the display when no characters are waiting. 504 // Also shows mode, ruler and positions cursor. 505 ins_redraw(true); 506 507 if (curwin->w_p_scb) { 508 do_check_scrollbind(true); 509 } 510 511 if (curwin->w_p_crb) { 512 do_check_cursorbind(); 513 } 514 515 if (s->count <= 1) { 516 update_curswant(); 517 } 518 s->old_topline = curwin->w_topline; 519 s->old_topfill = curwin->w_topfill; 520 521 if (s->c != K_EVENT) { 522 s->lastc = s->c; // remember previous char for CTRL-D 523 } 524 525 // After using CTRL-G U the next cursor key will not break undo. 526 if (dont_sync_undo == kNone) { 527 dont_sync_undo = kTrue; 528 } else { 529 dont_sync_undo = kFalse; 530 } 531 532 // Trigger autocomplete when entering Insert mode, either directly 533 // or via change commands like 'ciw', 'cw', etc., before the first 534 // character is typed. 535 if (s->ins_just_started) { 536 s->ins_just_started = false; 537 if (ins_compl_has_autocomplete() && !char_avail() && curwin->w_cursor.col > 0) { 538 s->c = char_before_cursor(); 539 if (vim_isprintc(s->c)) { 540 ins_compl_enable_autocomplete(); 541 ins_compl_init_get_longest(); 542 insert_do_complete(s); 543 insert_handle_key_post(s); 544 return 1; 545 } 546 } 547 } 548 549 return 1; 550 } 551 552 static int insert_execute(VimState *state, int key) 553 { 554 InsertState *const s = (InsertState *)state; 555 if (stop_insert_mode) { 556 // Insert mode ended, possibly from a callback. 557 if (key != K_IGNORE && key != K_NOP) { 558 vungetc(key); 559 } 560 s->count = 0; 561 s->nomove = true; 562 ins_compl_prep(ESC); 563 return 0; 564 } 565 566 if (key == K_IGNORE || key == K_NOP) { 567 return -1; // get another key 568 } 569 s->c = key; 570 571 // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V. 572 if (key != K_EVENT) { 573 did_cursorhold = true; 574 } 575 576 // Special handling of keys while the popup menu is visible or wanted 577 // and the cursor is still in the completed word. Only when there is 578 // a match, skip this when no matches were found. 579 if (ins_compl_active() && curwin->w_cursor.col >= ins_compl_col() 580 && ins_compl_has_shown_match() && pum_wanted()) { 581 // BS: Delete one character from "compl_leader". 582 if ((s->c == K_BS || s->c == Ctrl_H) 583 && curwin->w_cursor.col > ins_compl_col() 584 && (s->c = ins_compl_bs()) == NUL) { 585 return 1; // continue 586 } 587 588 // When no match was selected or it was edited. 589 if (!ins_compl_used_match()) { 590 // CTRL-L: Add one character from the current match to 591 // "compl_leader". Except when at the original match and 592 // there is nothing to add, CTRL-L works like CTRL-P then. 593 if (s->c == Ctrl_L 594 && (!ctrl_x_mode_line_or_eval() 595 || ins_compl_long_shown_match())) { 596 ins_compl_addfrommatch(); 597 return 1; // continue 598 } 599 600 // A non-white character that fits in with the current 601 // completion: Add to "compl_leader". 602 if (ins_compl_accept_char(s->c)) { 603 // Trigger InsertCharPre. 604 char *str = do_insert_char_pre(s->c); 605 606 if (str != NULL) { 607 for (char *p = str; *p != NUL; MB_PTR_ADV(p)) { 608 ins_compl_addleader(utf_ptr2char(p)); 609 } 610 xfree(str); 611 } else { 612 ins_compl_addleader(s->c); 613 } 614 return 1; // continue 615 } 616 617 // Pressing CTRL-Y selects the current match. When 618 // compl_enter_selects is set the Enter key does the same. 619 if ((s->c == Ctrl_Y 620 || (ins_compl_enter_selects() 621 && (s->c == CAR || s->c == K_KENTER || s->c == NL))) 622 && stop_arrow() == OK) { 623 ins_compl_delete(false); 624 if (ins_compl_preinsert_longest() && !ins_compl_is_match_selected()) { 625 ins_compl_insert(false, true); 626 ins_compl_init_get_longest(); 627 return 1; // continue 628 } else { 629 ins_compl_insert(false, false); 630 } 631 } else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) { 632 // Delete preinserted text when typing special chars 633 ins_compl_delete(false); 634 } 635 } 636 } 637 638 // Prepare for or stop CTRL-X mode. This doesn't do completion, but it does 639 // fix up the text when finishing completion. 640 ins_compl_init_get_longest(); 641 if (ins_compl_prep(s->c)) { 642 return 1; // continue 643 } 644 645 // CTRL-\ CTRL-N goes to Normal mode, 646 // CTRL-\ CTRL-O is like CTRL-O but without moving the cursor 647 if (s->c == Ctrl_BSL) { 648 // may need to redraw when no more chars available now 649 ins_redraw(false); 650 no_mapping++; 651 allow_keys++; 652 s->c = plain_vgetc(); 653 no_mapping--; 654 allow_keys--; 655 if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) { 656 // it's something else 657 vungetc(s->c); 658 s->c = Ctrl_BSL; 659 } else { 660 if (s->c == Ctrl_O) { 661 ins_ctrl_o(); 662 ins_at_eol = false; // cursor keeps its column 663 s->nomove = true; 664 } 665 s->count = 0; 666 return 0; 667 } 668 } 669 670 if (s->c != K_EVENT) { 671 s->c = do_digraph(s->c); 672 } 673 674 if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode_cmdline()) { 675 insert_do_complete(s); 676 insert_handle_key_post(s); 677 return 1; 678 } 679 680 if (s->c == Ctrl_V || s->c == Ctrl_Q) { 681 ins_ctrl_v(); 682 s->c = Ctrl_V; // pretend CTRL-V is last typed character 683 return 1; // continue 684 } 685 686 if (cindent_on() && ctrl_x_mode_none()) { 687 s->line_is_white = inindent(0); 688 // A key name preceded by a bang means this key is not to be 689 // inserted. Skip ahead to the re-indenting below. 690 if (in_cinkeys(s->c, '!', s->line_is_white) 691 && stop_arrow() == OK) { 692 do_c_expr_indent(); 693 return 1; // continue 694 } 695 // A key name preceded by a star means that indenting has to be 696 // done before inserting the key. 697 if (can_cindent && in_cinkeys(s->c, '*', s->line_is_white) 698 && stop_arrow() == OK) { 699 do_c_expr_indent(); 700 } 701 } 702 703 if (curwin->w_p_rl) { 704 switch (s->c) { 705 case K_LEFT: 706 s->c = K_RIGHT; break; 707 case K_S_LEFT: 708 s->c = K_S_RIGHT; break; 709 case K_C_LEFT: 710 s->c = K_C_RIGHT; break; 711 case K_RIGHT: 712 s->c = K_LEFT; break; 713 case K_S_RIGHT: 714 s->c = K_S_LEFT; break; 715 case K_C_RIGHT: 716 s->c = K_C_LEFT; break; 717 } 718 } 719 720 // If 'keymodel' contains "startsel", may start selection. If it 721 // does, a CTRL-O and c will be stuffed, we need to get these 722 // characters. 723 if (ins_start_select(s->c)) { 724 return 1; // continue 725 } 726 727 return insert_handle_key(s); 728 } 729 730 static int insert_handle_key(InsertState *s) 731 { 732 // The big switch to handle a character in insert mode. 733 // TODO(tarruda): This could look better if a lookup table is used. 734 // (similar to normal mode `nv_cmds[]`) 735 switch (s->c) { 736 case ESC: // End input mode 737 if (echeck_abbr(ESC + ABBR_OFF)) { 738 break; 739 } 740 FALLTHROUGH; 741 742 case Ctrl_C: // End input mode 743 if (s->c == Ctrl_C && cmdwin_type != 0) { 744 // Close the cmdline window. 745 cmdwin_result = K_IGNORE; 746 got_int = false; // don't stop executing autocommands et al 747 s->nomove = true; 748 return 0; // exit insert mode 749 } 750 if (s->c == Ctrl_C && bt_prompt(curbuf)) { 751 if (invoke_prompt_interrupt()) { 752 if (!bt_prompt(curbuf)) { 753 // buffer changed to a non-prompt buffer, get out of 754 // Insert mode 755 return 0; 756 } 757 break; 758 } 759 } 760 761 return 0; // exit insert mode 762 763 case Ctrl_Z: 764 goto normalchar; // insert CTRL-Z as normal char 765 766 case Ctrl_O: // execute one command 767 if (ctrl_x_mode_omni()) { 768 insert_do_complete(s); 769 break; 770 } 771 772 if (echeck_abbr(Ctrl_O + ABBR_OFF)) { 773 break; 774 } 775 776 ins_ctrl_o(); 777 778 // don't move the cursor left when 'virtualedit' has "onemore". 779 if (get_ve_flags(curwin) & kOptVeFlagOnemore) { 780 ins_at_eol = false; 781 s->nomove = true; 782 } 783 784 s->count = 0; 785 return 0; // exit insert mode 786 787 case K_INS: // toggle insert/replace mode 788 case K_KINS: 789 ins_insert(s->replaceState); 790 break; 791 792 case K_SELECT: // end of Select mode mapping - ignore 793 break; 794 795 case K_HELP: // Help key works like <ESC> <Help> 796 case K_F1: 797 case K_XF1: 798 stuffcharReadbuff(K_HELP); 799 return 0; // exit insert mode 800 801 case ' ': 802 if (mod_mask != MOD_MASK_CTRL) { 803 goto normalchar; 804 } 805 FALLTHROUGH; 806 case K_ZERO: // Insert the previously inserted text. 807 case NUL: 808 case Ctrl_A: 809 // For ^@ the trailing ESC will end the insert, unless there is an 810 // error. 811 if (stuff_inserted(NUL, 1, (s->c == Ctrl_A)) == FAIL 812 && s->c != Ctrl_A) { 813 return 0; // exit insert mode 814 } 815 s->inserted_space = false; 816 break; 817 818 case Ctrl_R: // insert the contents of a register 819 if (ctrl_x_mode_register() && !ins_compl_active()) { 820 insert_do_complete(s); 821 break; 822 } 823 ins_reg(); 824 auto_format(false, true); 825 s->inserted_space = false; 826 break; 827 828 case Ctrl_G: // commands starting with CTRL-G 829 ins_ctrl_g(); 830 break; 831 832 case Ctrl_HAT: // switch input mode and/or langmap 833 ins_ctrl_hat(); 834 break; 835 836 case Ctrl__: // switch between languages 837 if (!p_ari) { 838 goto normalchar; 839 } 840 ins_ctrl_(); 841 break; 842 843 case Ctrl_D: // Make indent one shiftwidth smaller. 844 if (ctrl_x_mode_path_defines()) { 845 insert_do_complete(s); 846 break; 847 } 848 FALLTHROUGH; 849 850 case Ctrl_T: // Make indent one shiftwidth greater. 851 if (s->c == Ctrl_T && ctrl_x_mode_thesaurus()) { 852 if (check_compl_option(false)) { 853 insert_do_complete(s); 854 } 855 break; 856 } 857 ins_shift(s->c, s->lastc); 858 auto_format(false, true); 859 s->inserted_space = false; 860 break; 861 862 case K_DEL: // delete character under the cursor 863 case K_KDEL: 864 ins_del(); 865 auto_format(false, true); 866 break; 867 868 case K_BS: // delete character before the cursor 869 case Ctrl_H: 870 s->did_backspace = ins_bs(s->c, BACKSPACE_CHAR, &s->inserted_space); 871 auto_format(false, true); 872 if (s->did_backspace) { 873 MAY_TRIGGER_AUTOCOMPLETE(s->c); 874 } 875 break; 876 877 case Ctrl_W: // delete word before the cursor 878 if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) { 879 // In a prompt window CTRL-W is used for window commands. 880 // Use Shift-CTRL-W to delete a word. 881 stuffcharReadbuff(Ctrl_W); 882 restart_edit = 'A'; 883 s->nomove = true; 884 s->count = 0; 885 return 0; 886 } 887 s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space); 888 auto_format(false, true); 889 if (s->did_backspace) { 890 MAY_TRIGGER_AUTOCOMPLETE(s->c); 891 } 892 break; 893 894 case Ctrl_U: // delete all inserted text in current line 895 // CTRL-X CTRL-U completes with 'completefunc'. 896 if (ctrl_x_mode_function()) { 897 insert_do_complete(s); 898 } else { 899 s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space); 900 auto_format(false, true); 901 s->inserted_space = false; 902 if (s->did_backspace) { 903 MAY_TRIGGER_AUTOCOMPLETE(s->c); 904 } 905 } 906 break; 907 908 case K_LEFTMOUSE: // mouse keys 909 case K_LEFTMOUSE_NM: 910 case K_LEFTDRAG: 911 case K_LEFTRELEASE: 912 case K_LEFTRELEASE_NM: 913 case K_MOUSEMOVE: 914 case K_MIDDLEMOUSE: 915 case K_MIDDLEDRAG: 916 case K_MIDDLERELEASE: 917 case K_RIGHTMOUSE: 918 case K_RIGHTDRAG: 919 case K_RIGHTRELEASE: 920 case K_X1MOUSE: 921 case K_X1DRAG: 922 case K_X1RELEASE: 923 case K_X2MOUSE: 924 case K_X2DRAG: 925 case K_X2RELEASE: 926 ins_mouse(s->c); 927 break; 928 929 case K_MOUSEDOWN: // Default action for scroll wheel up: scroll up 930 ins_mousescroll(MSCR_DOWN); 931 break; 932 933 case K_MOUSEUP: // Default action for scroll wheel down: scroll down 934 ins_mousescroll(MSCR_UP); 935 break; 936 937 case K_MOUSELEFT: // Scroll wheel left 938 ins_mousescroll(MSCR_LEFT); 939 break; 940 941 case K_MOUSERIGHT: // Scroll wheel right 942 ins_mousescroll(MSCR_RIGHT); 943 break; 944 945 case K_IGNORE: // Something mapped to nothing 946 break; 947 948 case K_PASTE_START: 949 paste_repeat(1); 950 goto check_pum; 951 952 case K_EVENT: // some event 953 state_handle_k_event(); 954 // If CTRL-G U was used apply it to the next typed key. 955 if (dont_sync_undo == kTrue) { 956 dont_sync_undo = kNone; 957 } 958 goto check_pum; 959 960 case K_COMMAND: // <Cmd>command<CR> 961 do_cmdline(NULL, getcmdkeycmd, NULL, 0); 962 goto check_pum; 963 964 case K_LUA: 965 map_execute_lua(false, false); 966 967 check_pum: 968 // nvim_select_popupmenu_item() can be called from the handling of 969 // K_EVENT, K_COMMAND, or K_LUA. 970 // TODO(bfredl): Not entirely sure this indirection is necessary 971 // but doing like this ensures using nvim_select_popupmenu_item is 972 // equivalent to selecting the item with a typed key. 973 if (pum_want.active) { 974 if (pum_visible()) { 975 // Set this to NULL so that ins_complete() will update the message. 976 edit_submode_extra = NULL; 977 insert_do_complete(s); 978 if (pum_want.finish) { 979 // accept the item and stop completion 980 ins_compl_prep(Ctrl_Y); 981 } 982 } 983 pum_want.active = false; 984 } 985 986 if (curbuf->b_u_synced) { 987 // The K_EVENT, K_COMMAND, or K_LUA caused undo to be synced. 988 // Need to save the line for undo before inserting the next char. 989 ins_need_undo = true; 990 } 991 break; 992 993 case K_HOME: // <Home> 994 case K_KHOME: 995 case K_S_HOME: 996 case K_C_HOME: 997 ins_home(s->c); 998 break; 999 1000 case K_END: // <End> 1001 case K_KEND: 1002 case K_S_END: 1003 case K_C_END: 1004 ins_end(s->c); 1005 break; 1006 1007 case K_LEFT: // <Left> 1008 if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) { 1009 ins_s_left(); 1010 } else { 1011 ins_left(); 1012 } 1013 break; 1014 1015 case K_S_LEFT: // <S-Left> 1016 case K_C_LEFT: 1017 ins_s_left(); 1018 break; 1019 1020 case K_RIGHT: // <Right> 1021 if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) { 1022 ins_s_right(); 1023 } else { 1024 ins_right(); 1025 } 1026 break; 1027 1028 case K_S_RIGHT: // <S-Right> 1029 case K_C_RIGHT: 1030 ins_s_right(); 1031 break; 1032 1033 case K_UP: // <Up> 1034 if (pum_visible()) { 1035 insert_do_complete(s); 1036 } else if (mod_mask & MOD_MASK_SHIFT) { 1037 ins_pageup(); 1038 } else { 1039 ins_up(false); 1040 } 1041 break; 1042 1043 case K_S_UP: // <S-Up> 1044 case K_PAGEUP: 1045 case K_KPAGEUP: 1046 if (pum_visible()) { 1047 insert_do_complete(s); 1048 } else { 1049 ins_pageup(); 1050 } 1051 break; 1052 1053 case K_DOWN: // <Down> 1054 if (pum_visible()) { 1055 insert_do_complete(s); 1056 } else if (mod_mask & MOD_MASK_SHIFT) { 1057 ins_pagedown(); 1058 } else { 1059 ins_down(false); 1060 } 1061 break; 1062 1063 case K_S_DOWN: // <S-Down> 1064 case K_PAGEDOWN: 1065 case K_KPAGEDOWN: 1066 if (pum_visible()) { 1067 insert_do_complete(s); 1068 } else { 1069 ins_pagedown(); 1070 } 1071 break; 1072 1073 case K_S_TAB: // When not mapped, use like a normal TAB 1074 s->c = TAB; 1075 FALLTHROUGH; 1076 1077 case TAB: // TAB or Complete patterns along path 1078 if (ctrl_x_mode_path_patterns()) { 1079 insert_do_complete(s); 1080 break; 1081 } 1082 s->inserted_space = false; 1083 if (ins_tab()) { 1084 goto normalchar; // insert TAB as a normal char 1085 } 1086 auto_format(false, true); 1087 break; 1088 1089 case K_KENTER: // <Enter> 1090 s->c = CAR; 1091 FALLTHROUGH; 1092 case CAR: 1093 case NL: 1094 // In a quickfix window a <CR> jumps to the error under the 1095 // cursor. 1096 if (bt_quickfix(curbuf) && s->c == CAR) { 1097 if (curwin->w_llist_ref == NULL) { // quickfix window 1098 do_cmdline_cmd(".cc"); 1099 } else { // location list window 1100 do_cmdline_cmd(".ll"); 1101 } 1102 break; 1103 } 1104 if (cmdwin_type != 0) { 1105 // Execute the command in the cmdline window. 1106 cmdwin_result = CAR; 1107 return 0; 1108 } 1109 if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) { 1110 prompt_invoke_callback(); 1111 if (!bt_prompt(curbuf)) { 1112 // buffer changed to a non-prompt buffer, get out of 1113 // Insert mode 1114 return 0; 1115 } 1116 break; 1117 } 1118 if (!ins_eol(s->c)) { 1119 return 0; // out of memory 1120 } 1121 auto_format(false, false); 1122 s->inserted_space = false; 1123 break; 1124 1125 case Ctrl_K: // digraph or keyword completion 1126 if (ctrl_x_mode_dictionary()) { 1127 if (check_compl_option(true)) { 1128 insert_do_complete(s); 1129 } 1130 break; 1131 } 1132 1133 s->c = ins_digraph(); 1134 if (s->c == NUL) { 1135 break; 1136 } 1137 goto normalchar; 1138 1139 case Ctrl_X: // Enter CTRL-X mode 1140 ins_ctrl_x(); 1141 break; 1142 1143 case Ctrl_RSB: // Tag name completion after ^X 1144 if (!ctrl_x_mode_tags()) { 1145 goto normalchar; 1146 } else { 1147 insert_do_complete(s); 1148 } 1149 break; 1150 1151 case Ctrl_F: // File name completion after ^X 1152 if (!ctrl_x_mode_files()) { 1153 goto normalchar; 1154 } else { 1155 insert_do_complete(s); 1156 } 1157 break; 1158 1159 case 's': // Spelling completion after ^X 1160 case Ctrl_S: 1161 if (!ctrl_x_mode_spell()) { 1162 goto normalchar; 1163 } else { 1164 insert_do_complete(s); 1165 } 1166 break; 1167 1168 case Ctrl_L: // Whole line completion after ^X 1169 if (!ctrl_x_mode_whole_line()) { 1170 goto normalchar; 1171 } 1172 FALLTHROUGH; 1173 1174 case Ctrl_P: // Do previous/next pattern completion 1175 case Ctrl_N: 1176 // if 'complete' is empty then plain ^P is no longer special, 1177 // but it is under other ^X modes 1178 if (*curbuf->b_p_cpt == NUL 1179 && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line()) 1180 && !compl_status_local()) { 1181 goto normalchar; 1182 } 1183 1184 insert_do_complete(s); 1185 break; 1186 1187 case Ctrl_Y: // copy from previous line or scroll down 1188 case Ctrl_E: // copy from next line or scroll up 1189 s->c = ins_ctrl_ey(s->c); 1190 break; 1191 1192 default: 1193 1194 normalchar: 1195 // Insert a normal character. 1196 1197 if (!p_paste) { 1198 // Trigger InsertCharPre. 1199 char *str = do_insert_char_pre(s->c); 1200 1201 if (str != NULL) { 1202 if (*str != NUL && stop_arrow() != FAIL) { 1203 // Insert the new value of v:char literally. 1204 for (char *p = str; *p != NUL; MB_PTR_ADV(p)) { 1205 s->c = utf_ptr2char(p); 1206 if (s->c == CAR || s->c == K_KENTER || s->c == NL) { 1207 ins_eol(s->c); 1208 } else { 1209 ins_char(s->c); 1210 } 1211 } 1212 AppendToRedobuffLit(str, -1); 1213 } 1214 xfree(str); 1215 s->c = NUL; 1216 } 1217 1218 // If the new value is already inserted or an empty string 1219 // then don't insert any character. 1220 if (s->c == NUL) { 1221 break; 1222 } 1223 } 1224 // Try to perform smart-indenting. 1225 ins_try_si(s->c); 1226 1227 if (s->c == ' ') { 1228 s->inserted_space = true; 1229 if (inindent(0)) { 1230 can_cindent = false; 1231 } 1232 if (Insstart_blank_vcol == MAXCOL 1233 && curwin->w_cursor.lnum == Insstart.lnum) { 1234 Insstart_blank_vcol = get_nolist_virtcol(); 1235 } 1236 } 1237 1238 // Insert a normal character and check for abbreviations on a 1239 // special character. Let CTRL-] expand abbreviations without 1240 // inserting it. 1241 if (vim_iswordc(s->c) 1242 // Add ABBR_OFF for characters above 0x100, this is 1243 // what check_abbr() expects. 1244 || (!echeck_abbr((s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c) 1245 && s->c != Ctrl_RSB)) { 1246 insert_special(s->c, false, false); 1247 revins_legal++; 1248 revins_chars++; 1249 } 1250 1251 auto_format(false, true); 1252 1253 // When inserting a character the cursor line must never be in a 1254 // closed fold. 1255 foldOpenCursor(); 1256 // Trigger autocompletion 1257 if (ins_compl_has_autocomplete() && !char_avail() && vim_isprintc(s->c)) { 1258 TRIGGER_AUTOCOMPLETE(); 1259 } 1260 1261 break; 1262 } // end of switch (s->c) 1263 1264 insert_handle_key_post(s); 1265 return 1; // continue 1266 } 1267 1268 static void insert_do_complete(InsertState *s) 1269 { 1270 compl_busy = true; 1271 disable_fold_update++; // don't redraw folds here 1272 if (ins_complete(s->c, true) == FAIL) { 1273 compl_status_clear(); 1274 } 1275 disable_fold_update--; 1276 compl_busy = false; 1277 can_si = may_do_si(); // allow smartindenting 1278 } 1279 1280 static void insert_do_cindent(InsertState *s) 1281 { 1282 // Indent now if a key was typed that is in 'cinkeys'. 1283 if (in_cinkeys(s->c, ' ', s->line_is_white)) { 1284 if (stop_arrow() == OK) { 1285 // re-indent the current line 1286 do_c_expr_indent(); 1287 } 1288 } 1289 } 1290 1291 static void insert_handle_key_post(InsertState *s) 1292 { 1293 // If typed something may trigger CursorHoldI again. 1294 if (s->c != K_EVENT 1295 // but not in CTRL-X mode, a script can't restore the state 1296 && ctrl_x_mode_normal()) { 1297 did_cursorhold = false; 1298 } 1299 1300 // Check if we need to cancel completion mode because the window 1301 // or tab page was changed 1302 if (ins_compl_active() && !ins_compl_win_active(curwin)) { 1303 ins_compl_cancel(); 1304 } 1305 1306 // If the cursor was moved we didn't just insert a space 1307 if (arrow_used) { 1308 s->inserted_space = false; 1309 } 1310 1311 if (can_cindent && cindent_on() && ctrl_x_mode_normal()) { 1312 insert_do_cindent(s); 1313 } 1314 } 1315 1316 /// edit(): Start inserting text. 1317 /// 1318 /// "cmdchar" can be: 1319 /// 'i' normal insert command 1320 /// 'a' normal append command 1321 /// 'R' replace command 1322 /// 'r' "r<CR>" command: insert one <CR>. 1323 /// Note: count can be > 1, for redo, but still only one <CR> is inserted. 1324 /// <Esc> is not used for redo. 1325 /// 'g' "gI" command. 1326 /// 'V' "gR" command for Virtual Replace mode. 1327 /// 'v' "gr" command for single character Virtual Replace mode. 1328 /// 1329 /// This function is not called recursively. For CTRL-O commands, it returns 1330 /// and lets the caller handle the Normal-mode command. 1331 /// 1332 /// @param cmdchar command that started the insert 1333 /// @param startln if true, insert at start of line 1334 /// @param count repeat count for the command 1335 /// 1336 /// @return true if a CTRL-O command caused the return (insert mode pending). 1337 bool edit(int cmdchar, bool startln, int count) 1338 { 1339 if (curbuf->terminal) { 1340 if (ex_normal_busy) { 1341 // Do not enter terminal mode from ex_normal(), which would cause havoc 1342 // (such as terminal-mode recursiveness). Instead set a flag to force-set 1343 // the value of `restart_edit` before `ex_normal` returns. 1344 restart_edit = 'i'; 1345 force_restart_edit = true; 1346 return false; 1347 } 1348 return terminal_enter(); 1349 } 1350 1351 // Don't allow inserting in the sandbox. 1352 if (sandbox != 0) { 1353 emsg(_(e_sandbox)); 1354 return false; 1355 } 1356 1357 // Don't allow changes in the buffer while editing the cmdline. The 1358 // caller of getcmdline() may get confused. 1359 // Don't allow recursive insert mode when busy with completion. 1360 // Allow in dummy buffers since they are only used internally 1361 if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible() 1362 || expr_map_locked()) { 1363 emsg(_(e_textlock)); 1364 return false; 1365 } 1366 1367 InsertState s[1]; 1368 memset(s, 0, sizeof(InsertState)); 1369 s->state.execute = insert_execute; 1370 s->state.check = insert_check; 1371 s->cmdchar = cmdchar; 1372 s->startln = startln; 1373 s->count = count; 1374 insert_enter(s); 1375 return s->c == Ctrl_O; 1376 } 1377 1378 bool ins_need_undo_get(void) 1379 { 1380 return ins_need_undo; 1381 } 1382 1383 /// Redraw for Insert mode. 1384 /// This is postponed until getting the next character to make '$' in the 'cpo' 1385 /// option work correctly. 1386 /// Only redraw when there are no characters available. This speeds up 1387 /// inserting sequences of characters (e.g., for CTRL-R). 1388 /// 1389 /// @param ready not busy with something 1390 void ins_redraw(bool ready) 1391 { 1392 if (char_avail()) { 1393 return; 1394 } 1395 1396 // Trigger CursorMoved if the cursor moved. Not when the popup menu is 1397 // visible, the command might delete it. 1398 if (ready && has_event(EVENT_CURSORMOVEDI) 1399 && (last_cursormoved_win != curwin 1400 || !equalpos(last_cursormoved, curwin->w_cursor)) 1401 && !pum_visible()) { 1402 // Need to update the screen first, to make sure syntax 1403 // highlighting is correct after making a change (e.g., inserting 1404 // a "(". The autocommand may also require a redraw, so it's done 1405 // again below, unfortunately. 1406 if (syntax_present(curwin) && must_redraw) { 1407 update_screen(); 1408 } 1409 // Make sure curswant is correct, an autocommand may call 1410 // getcurpos() 1411 update_curswant(); 1412 ins_apply_autocmds(EVENT_CURSORMOVEDI); 1413 last_cursormoved_win = curwin; 1414 last_cursormoved = curwin->w_cursor; 1415 } 1416 1417 // Trigger TextChangedI if changedtick_i differs. 1418 if (ready && has_event(EVENT_TEXTCHANGEDI) 1419 && curbuf->b_last_changedtick_i != buf_get_changedtick(curbuf) 1420 && !pum_visible()) { 1421 aco_save_T aco; 1422 varnumber_T tick = buf_get_changedtick(curbuf); 1423 1424 // save and restore curwin and curbuf, in case the autocmd changes them 1425 aucmd_prepbuf(&aco, curbuf); 1426 apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf); 1427 aucmd_restbuf(&aco); 1428 curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); 1429 if (tick != buf_get_changedtick(curbuf)) { // see ins_apply_autocmds() 1430 u_save(curwin->w_cursor.lnum, 1431 (linenr_T)(curwin->w_cursor.lnum + 1)); 1432 } 1433 } 1434 1435 // Trigger TextChangedP if changedtick_pum differs. When the popupmenu 1436 // closes TextChangedI will need to trigger for backwards compatibility, 1437 // thus use different b_last_changedtick* variables. 1438 if (ready && has_event(EVENT_TEXTCHANGEDP) 1439 && curbuf->b_last_changedtick_pum != buf_get_changedtick(curbuf) 1440 && pum_visible()) { 1441 aco_save_T aco; 1442 varnumber_T tick = buf_get_changedtick(curbuf); 1443 1444 // save and restore curwin and curbuf, in case the autocmd changes them 1445 aucmd_prepbuf(&aco, curbuf); 1446 apply_autocmds(EVENT_TEXTCHANGEDP, NULL, NULL, false, curbuf); 1447 aucmd_restbuf(&aco); 1448 curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf); 1449 if (tick != buf_get_changedtick(curbuf)) { // see ins_apply_autocmds() 1450 u_save(curwin->w_cursor.lnum, 1451 (linenr_T)(curwin->w_cursor.lnum + 1)); 1452 } 1453 } 1454 1455 if (ready) { 1456 may_trigger_win_scrolled_resized(); 1457 } 1458 1459 // Trigger BufModified if b_changed_invalid is set. 1460 if (ready && has_event(EVENT_BUFMODIFIEDSET) 1461 && curbuf->b_changed_invalid == true 1462 && !pum_visible()) { 1463 apply_autocmds(EVENT_BUFMODIFIEDSET, NULL, NULL, false, curbuf); 1464 curbuf->b_changed_invalid = false; 1465 } 1466 1467 // Trigger SafeState if nothing is pending. 1468 may_trigger_safestate(ready 1469 && !ins_compl_active() 1470 && !pum_visible()); 1471 1472 pum_check_clear(); 1473 show_cursor_info_later(false); 1474 if (must_redraw) { 1475 update_screen(); 1476 } else { 1477 redraw_statuslines(); 1478 if (clear_cmdline || redraw_cmdline || redraw_mode) { 1479 showmode(); // clear cmdline and show mode 1480 } 1481 } 1482 setcursor(); 1483 emsg_on_display = false; // may remove error message now 1484 } 1485 1486 // Handle a CTRL-V or CTRL-Q typed in Insert mode. 1487 static void ins_ctrl_v(void) 1488 { 1489 bool did_putchar = false; 1490 1491 // may need to redraw when no more chars available now 1492 ins_redraw(false); 1493 1494 if (redrawing() && !char_avail()) { 1495 edit_putchar('^', true); 1496 did_putchar = true; 1497 } 1498 AppendToRedobuff(CTRL_V_STR); 1499 1500 add_to_showcmd_c(Ctrl_V); 1501 1502 // Do not include modifiers into the key for CTRL-SHIFT-V. 1503 int c = get_literal(mod_mask & MOD_MASK_SHIFT); 1504 if (did_putchar) { 1505 // when the line fits in 'columns' the '^' is at the start of the next 1506 // line and will not removed by the redraw 1507 edit_unputchar(); 1508 } 1509 clear_showcmd(); 1510 insert_special(c, true, true); 1511 revins_chars++; 1512 revins_legal++; 1513 } 1514 1515 // Put a character directly onto the screen. It's not stored in a buffer. 1516 // Used while handling CTRL-K, CTRL-V, etc. in Insert mode. 1517 static int pc_status; 1518 #define PC_STATUS_UNSET 0 // nothing was put on screen 1519 #define PC_STATUS_RIGHT 1 // right half of double-wide char 1520 #define PC_STATUS_LEFT 2 // left half of double-wide char 1521 #define PC_STATUS_SET 3 // pc_schar was filled 1522 static schar_T pc_schar; // saved char 1523 static int pc_attr; 1524 static int pc_row; 1525 static int pc_col; 1526 1527 void edit_putchar(int c, bool highlight) 1528 { 1529 if (curwin->w_grid_alloc.chars == NULL && default_grid.chars == NULL) { 1530 return; 1531 } 1532 1533 int attr; 1534 update_topline(curwin); // just in case w_topline isn't valid 1535 validate_cursor(curwin); 1536 if (highlight) { 1537 attr = HL_ATTR(HLF_8); 1538 } else { 1539 attr = 0; 1540 } 1541 pc_row = curwin->w_wrow; 1542 pc_status = PC_STATUS_UNSET; 1543 grid_line_start(&curwin->w_grid, pc_row); 1544 if (curwin->w_p_rl) { 1545 pc_col = curwin->w_view_width - 1 - curwin->w_wcol; 1546 1547 if (grid_line_getchar(pc_col, NULL) == NUL) { 1548 grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr); 1549 curwin->w_wcol--; 1550 pc_status = PC_STATUS_RIGHT; 1551 } 1552 } else { 1553 pc_col = curwin->w_wcol; 1554 1555 if (grid_line_getchar(pc_col + 1, NULL) == NUL) { 1556 // pc_col is the left half of a double-width char 1557 pc_status = PC_STATUS_LEFT; 1558 } 1559 } 1560 1561 // save the character to be able to put it back 1562 if (pc_status == PC_STATUS_UNSET) { 1563 pc_schar = grid_line_getchar(pc_col, &pc_attr); 1564 pc_status = PC_STATUS_SET; 1565 } 1566 1567 char buf[MB_MAXCHAR + 1]; 1568 grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr); 1569 grid_line_flush(); 1570 } 1571 1572 /// @return the effective prompt for the specified buffer. 1573 char *buf_prompt_text(const buf_T *const buf) 1574 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE 1575 { 1576 if (buf->b_prompt_text == NULL) { 1577 return "% "; 1578 } 1579 return buf->b_prompt_text; 1580 } 1581 1582 /// @return the effective prompt for the current buffer. 1583 char *prompt_text(void) 1584 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE 1585 { 1586 return buf_prompt_text(curbuf); 1587 } 1588 1589 // Prepare for prompt mode: Make sure the last line has the prompt text. 1590 // Move the cursor to this line. 1591 static void init_prompt(int cmdchar_todo) 1592 { 1593 char *prompt = prompt_text(); 1594 int prompt_len = (int)strlen(prompt); 1595 1596 // In case the mark is set to a nonexistent line. 1597 curbuf->b_prompt_start.mark.lnum = MAX(1, MIN(curbuf->b_prompt_start.mark.lnum, 1598 curbuf->b_ml.ml_line_count)); 1599 1600 curwin->w_cursor.lnum = MAX(curwin->w_cursor.lnum, curbuf->b_prompt_start.mark.lnum); 1601 char *text = ml_get(curbuf->b_prompt_start.mark.lnum); 1602 colnr_T text_len = ml_get_len(curbuf->b_prompt_start.mark.lnum); 1603 1604 if ((curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum 1605 && (curbuf->b_prompt_start.mark.col < prompt_len 1606 || curbuf->b_prompt_start.mark.col > text_len 1607 || !strnequal(text + curbuf->b_prompt_start.mark.col - prompt_len, prompt, 1608 (size_t)prompt_len)))) { 1609 // prompt is missing, insert it or append a line with it 1610 if (*text == NUL) { 1611 ml_replace(curbuf->b_prompt_start.mark.lnum, prompt, true); 1612 inserted_bytes(curbuf->b_prompt_start.mark.lnum, 0, 0, prompt_len); 1613 } else { 1614 const linenr_T lnum = curbuf->b_ml.ml_line_count; 1615 ml_append(lnum, prompt, 0, false); 1616 appended_lines_mark(lnum, 1); 1617 curbuf->b_prompt_start.mark.lnum = curbuf->b_ml.ml_line_count; 1618 // Like submitting, undo history was relevant to the old prompt. 1619 u_clearallandblockfree(curbuf); 1620 } 1621 curbuf->b_prompt_start.mark.col = prompt_len; 1622 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 1623 coladvance(curwin, MAXCOL); 1624 } 1625 1626 // Insert always starts after the prompt, allow editing text after it. 1627 if (Insstart_orig.lnum != curbuf->b_prompt_start.mark.lnum 1628 || Insstart_orig.col != curbuf->b_prompt_start.mark.col) { 1629 Insstart.lnum = curbuf->b_prompt_start.mark.lnum; 1630 Insstart.col = curbuf->b_prompt_start.mark.col; 1631 Insstart_orig = Insstart; 1632 Insstart_textlen = Insstart.col; 1633 Insstart_blank_vcol = MAXCOL; 1634 arrow_used = false; 1635 } 1636 1637 if (cmdchar_todo == 'A') { 1638 coladvance(curwin, MAXCOL); 1639 } 1640 if (curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum) { 1641 curwin->w_cursor.col = MAX(curwin->w_cursor.col, curbuf->b_prompt_start.mark.col); 1642 } 1643 // Make sure the cursor is in a valid position. 1644 check_cursor(curwin); 1645 } 1646 1647 /// @return true if the cursor is in the editable position of the prompt line. 1648 bool prompt_curpos_editable(void) 1649 FUNC_ATTR_PURE 1650 { 1651 return curwin->w_cursor.lnum > curbuf->b_prompt_start.mark.lnum 1652 || (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum 1653 && curwin->w_cursor.col >= curbuf->b_prompt_start.mark.col); 1654 } 1655 1656 // Undo the previous edit_putchar(). 1657 void edit_unputchar(void) 1658 { 1659 if (pc_status != PC_STATUS_UNSET) { 1660 if (pc_status == PC_STATUS_RIGHT) { 1661 curwin->w_wcol++; 1662 } 1663 if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) { 1664 redrawWinline(curwin, curwin->w_cursor.lnum); 1665 } else { 1666 // TODO(bfredl): this could be smarter and also handle the dubyawidth case 1667 grid_line_start(&curwin->w_grid, pc_row); 1668 grid_line_put_schar(pc_col, pc_schar, pc_attr); 1669 grid_line_flush(); 1670 } 1671 } 1672 } 1673 1674 /// Called when "$" is in 'cpoptions': display a '$' at the end of the changed 1675 /// text. Only works when cursor is in the line that changes. 1676 void display_dollar(colnr_T col_arg) 1677 { 1678 colnr_T col = MAX(col_arg, 0); 1679 1680 if (!redrawing()) { 1681 return; 1682 } 1683 1684 colnr_T save_col = curwin->w_cursor.col; 1685 curwin->w_cursor.col = col; 1686 1687 // If on the last byte of a multi-byte move to the first byte. 1688 char *p = get_cursor_line_ptr(); 1689 curwin->w_cursor.col -= utf_head_off(p, p + col); 1690 curs_columns(curwin, false); // Recompute w_wrow and w_wcol 1691 if (curwin->w_wcol < curwin->w_view_width) { 1692 edit_putchar('$', false); 1693 dollar_vcol = curwin->w_virtcol; 1694 } 1695 curwin->w_cursor.col = save_col; 1696 } 1697 1698 // Call this function before moving the cursor from the normal insert position 1699 // in insert mode. 1700 void undisplay_dollar(void) 1701 { 1702 if (dollar_vcol < 0) { 1703 return; 1704 } 1705 1706 dollar_vcol = -1; 1707 redrawWinline(curwin, curwin->w_cursor.lnum); 1708 } 1709 1710 /// Truncate the space at the end of a line. This is to be used only in an 1711 /// insert mode. It handles fixing the replace stack for MODE_REPLACE and 1712 /// MODE_VREPLACE modes. 1713 void truncate_spaces(char *line, size_t len) 1714 { 1715 int i; 1716 1717 // find start of trailing white space 1718 for (i = (int)len - 1; i >= 0 && ascii_iswhite(line[i]); i--) { 1719 if (State & REPLACE_FLAG) { 1720 replace_join(0); // remove a NUL from the replace stack 1721 } 1722 } 1723 line[i + 1] = NUL; 1724 } 1725 1726 /// Backspace the cursor until the given column. Handles MODE_REPLACE and 1727 /// MODE_VREPLACE modes correctly. May also be used when not in insert mode at 1728 /// all. Will attempt not to go before "col" even when there is a composing 1729 /// character. 1730 void backspace_until_column(int col) 1731 { 1732 while ((int)curwin->w_cursor.col > col) { 1733 curwin->w_cursor.col--; 1734 if (State & REPLACE_FLAG) { 1735 replace_do_bs(col); 1736 } else if (!del_char_after_col(col)) { 1737 break; 1738 } 1739 } 1740 } 1741 1742 /// Like del_char(), but make sure not to go before column "limit_col". 1743 /// Only matters when there are composing characters. 1744 /// 1745 /// @param limit_col only delete the character if it is after this column 1746 // 1747 /// @return true when something was deleted. 1748 static bool del_char_after_col(int limit_col) 1749 { 1750 if (limit_col >= 0) { 1751 colnr_T ecol = curwin->w_cursor.col + 1; 1752 1753 // Make sure the cursor is at the start of a character, but 1754 // skip forward again when going too far back because of a 1755 // composing character. 1756 mb_adjust_cursor(); 1757 while (curwin->w_cursor.col < (colnr_T)limit_col) { 1758 int l = utf_ptr2len(get_cursor_pos_ptr()); 1759 1760 if (l == 0) { // end of line 1761 break; 1762 } 1763 curwin->w_cursor.col += l; 1764 } 1765 if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) { 1766 return false; 1767 } 1768 del_bytes(ecol - curwin->w_cursor.col, false, true); 1769 } else { 1770 del_char(false); 1771 } 1772 return true; 1773 } 1774 1775 /// Next character is interpreted literally. 1776 /// A one, two or three digit decimal number is interpreted as its byte value. 1777 /// If one or two digits are entered, the next character is given to vungetc(). 1778 /// For Unicode a character > 255 may be returned. 1779 /// 1780 /// @param no_simplify do not include modifiers into the key 1781 int get_literal(bool no_simplify) 1782 { 1783 int nc; 1784 bool hex = false; 1785 bool octal = false; 1786 int unicode = 0; 1787 1788 if (got_int) { 1789 return Ctrl_C; 1790 } 1791 1792 no_mapping++; // don't map the next key hits 1793 int cc = 0; 1794 int i = 0; 1795 while (true) { 1796 nc = plain_vgetc(); 1797 if (!no_simplify) { 1798 nc = merge_modifiers(nc, &mod_mask); 1799 } 1800 if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { 1801 // A character with non-Shift modifiers should not be a valid 1802 // character for i_CTRL-V_digit. 1803 break; 1804 } 1805 if ((State & MODE_CMDLINE) == 0 && MB_BYTE2LEN_CHECK(nc) == 1) { 1806 add_to_showcmd(nc); 1807 } 1808 if (nc == 'x' || nc == 'X') { 1809 hex = true; 1810 } else if (nc == 'o' || nc == 'O') { 1811 octal = true; 1812 } else if (nc == 'u' || nc == 'U') { 1813 unicode = nc; 1814 } else { 1815 if (hex 1816 || unicode != 0) { 1817 if (!ascii_isxdigit(nc)) { 1818 break; 1819 } 1820 cc = cc * 16 + hex2nr(nc); 1821 } else if (octal) { 1822 if (nc < '0' || nc > '7') { 1823 break; 1824 } 1825 cc = cc * 8 + nc - '0'; 1826 } else { 1827 if (!ascii_isdigit(nc)) { 1828 break; 1829 } 1830 cc = cc * 10 + nc - '0'; 1831 } 1832 1833 i++; 1834 } 1835 1836 if (cc > 255 1837 && unicode == 0) { 1838 cc = 255; // limit range to 0-255 1839 } 1840 nc = 0; 1841 1842 if (hex) { // hex: up to two chars 1843 if (i >= 2) { 1844 break; 1845 } 1846 } else if (unicode) { // Unicode: up to four or eight chars 1847 if ((unicode == 'u' && i >= 4) || (unicode == 'U' && i >= 8)) { 1848 break; 1849 } 1850 } else if (i >= 3) { // decimal or octal: up to three chars 1851 break; 1852 } 1853 } 1854 if (i == 0) { // no number entered 1855 if (nc == K_ZERO) { // NUL is stored as NL 1856 cc = '\n'; 1857 nc = 0; 1858 } else { 1859 cc = nc; 1860 nc = 0; 1861 } 1862 } 1863 1864 if (cc == 0) { // NUL is stored as NL 1865 cc = '\n'; 1866 } 1867 1868 no_mapping--; 1869 if (nc) { 1870 vungetc(nc); 1871 // A character typed with i_CTRL-V_digit cannot have modifiers. 1872 mod_mask = 0; 1873 } 1874 got_int = false; // CTRL-C typed after CTRL-V is not an interrupt 1875 return cc; 1876 } 1877 1878 /// Insert character, taking care of special keys and mod_mask 1879 /// 1880 /// @param ctrlv `c` was typed after CTRL-V 1881 static void insert_special(int c, int allow_modmask, int ctrlv) 1882 { 1883 // Special function key, translate into "<Key>". Up to the last '>' is 1884 // inserted with ins_str(), so as not to replace characters in replace 1885 // mode. 1886 // Only use mod_mask for special keys, to avoid things like <S-Space>, 1887 // unless 'allow_modmask' is true. 1888 if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key. 1889 allow_modmask = true; 1890 } 1891 if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) { 1892 char *p = get_special_key_name(c, mod_mask); 1893 int len = (int)strlen(p); 1894 c = (uint8_t)p[len - 1]; 1895 if (len > 2) { 1896 if (stop_arrow() == FAIL) { 1897 return; 1898 } 1899 p[len - 1] = NUL; 1900 ins_str(p, (size_t)(len - 1)); 1901 AppendToRedobuffLit(p, -1); 1902 ctrlv = false; 1903 } 1904 } 1905 if (stop_arrow() == OK) { 1906 insertchar(c, ctrlv ? INSCHAR_CTRLV : 0, -1); 1907 } 1908 } 1909 1910 // Special characters in this context are those that need processing other 1911 // than the simple insertion that can be performed here. This includes ESC 1912 // which terminates the insert, and CR/NL which need special processing to 1913 // open up a new line. This routine tries to optimize insertions performed by 1914 // the "redo", "undo" or "put" commands, so it needs to know when it should 1915 // stop and defer processing to the "normal" mechanism. 1916 // '0' and '^' are special, because they can be followed by CTRL-D. 1917 #define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') 1918 1919 /// "flags": INSCHAR_FORMAT - force formatting 1920 /// INSCHAR_CTRLV - char typed just after CTRL-V 1921 /// INSCHAR_NO_FEX - don't use 'formatexpr' 1922 /// 1923 /// NOTE: passes the flags value straight through to internal_format() which, 1924 /// beside INSCHAR_FORMAT (above), is also looking for these: 1925 /// INSCHAR_DO_COM - format comments 1926 /// INSCHAR_COM_LIST - format comments with num list or 2nd line indent 1927 /// 1928 /// @param c character to insert or NUL 1929 /// @param flags INSCHAR_FORMAT, etc. 1930 /// @param second_indent indent for second line if >= 0 1931 void insertchar(int c, int flags, int second_indent) 1932 { 1933 char *p; 1934 int force_format = flags & INSCHAR_FORMAT; 1935 1936 const int textwidth = comp_textwidth(force_format); 1937 const bool fo_ins_blank = has_format_option(FO_INS_BLANK); 1938 1939 // Try to break the line in two or more pieces when: 1940 // - Always do this if we have been called to do formatting only. 1941 // - Always do this when 'formatoptions' has the 'a' flag and the line 1942 // ends in white space. 1943 // - Otherwise: 1944 // - Don't do this if inserting a blank 1945 // - Don't do this if an existing character is being replaced, unless 1946 // we're in MODE_VREPLACE state. 1947 // - Do this if the cursor is not on the line where insert started 1948 // or - 'formatoptions' doesn't have 'l' or the line was not too long 1949 // before the insert. 1950 // - 'formatoptions' doesn't have 'b' or a blank was inserted at or 1951 // before 'textwidth' 1952 if (textwidth > 0 1953 && (force_format 1954 || (!ascii_iswhite(c) 1955 && !((State & REPLACE_FLAG) 1956 && !(State & VREPLACE_FLAG) 1957 && *get_cursor_pos_ptr() != NUL) 1958 && (curwin->w_cursor.lnum != Insstart.lnum 1959 || ((!has_format_option(FO_INS_LONG) 1960 || Insstart_textlen <= (colnr_T)textwidth) 1961 && (!fo_ins_blank 1962 || Insstart_blank_vcol <= (colnr_T)textwidth)))))) { 1963 // Format with 'formatexpr' when it's set. Use internal formatting 1964 // when 'formatexpr' isn't set or it returns non-zero. 1965 bool do_internal = true; 1966 colnr_T virtcol = get_nolist_virtcol() 1967 + char2cells(c != NUL ? c : gchar_cursor()); 1968 1969 if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0 1970 && (force_format || virtcol > (colnr_T)textwidth)) { 1971 do_internal = (fex_format(curwin->w_cursor.lnum, 1, c) != 0); 1972 // It may be required to save for undo again, e.g. when setline() 1973 // was called. 1974 ins_need_undo = true; 1975 } 1976 if (do_internal) { 1977 internal_format(textwidth, second_indent, flags, c == NUL, c); 1978 } 1979 } 1980 1981 if (c == NUL) { // only formatting was wanted 1982 return; 1983 } 1984 1985 // Check whether this character should end a comment. 1986 if (did_ai && c == end_comment_pending) { 1987 char lead_end[COM_MAX_LEN]; // end-comment string 1988 1989 // Need to remove existing (middle) comment leader and insert end 1990 // comment leader. First, check what comment leader we can find. 1991 char *line = get_cursor_line_ptr(); 1992 int i = get_leader_len(line, &p, false, true); 1993 if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { // Just checking 1994 // Skip middle-comment string 1995 while (*p && p[-1] != ':') { // find end of middle flags 1996 p++; 1997 } 1998 int middle_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); 1999 // Don't count trailing white space for middle_len 2000 while (middle_len > 0 && ascii_iswhite(lead_end[middle_len - 1])) { 2001 middle_len--; 2002 } 2003 2004 // Find the end-comment string 2005 while (*p && p[-1] != ':') { // find end of end flags 2006 p++; 2007 } 2008 int end_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); 2009 2010 // Skip white space before the cursor 2011 i = curwin->w_cursor.col; 2012 while (--i >= 0 && ascii_iswhite(line[i])) {} 2013 i++; 2014 2015 // Skip to before the middle leader 2016 i -= middle_len; 2017 2018 // Check some expected things before we go on 2019 if (i >= 0 && end_len > 0 2020 && (uint8_t)lead_end[end_len - 1] == end_comment_pending) { 2021 // Backspace over all the stuff we want to replace 2022 backspace_until_column(i); 2023 2024 // Insert the end-comment string, except for the last 2025 // character, which will get inserted as normal later. 2026 ins_bytes_len(lead_end, (size_t)(end_len - 1)); 2027 } 2028 } 2029 } 2030 end_comment_pending = NUL; 2031 2032 did_ai = false; 2033 did_si = false; 2034 can_si = false; 2035 can_si_back = false; 2036 2037 // If there's any pending input, grab up to INPUT_BUFLEN at once. 2038 // This speeds up normal text input considerably. 2039 // Don't do this when 'cindent' or 'indentexpr' is set, because we might 2040 // need to re-indent at a ':', or any other character (but not what 2041 // 'paste' is set).. 2042 // Don't do this when there an InsertCharPre autocommand is defined, 2043 // because we need to fire the event for every character. 2044 // Do the check for InsertCharPre before the call to vpeekc() because the 2045 // InsertCharPre autocommand could change the input buffer. 2046 if (!ISSPECIAL(c) 2047 && (utf_char2len(c) == 1) 2048 && !has_event(EVENT_INSERTCHARPRE) 2049 && !test_disable_char_avail 2050 && vpeekc() != NUL 2051 && !(State & REPLACE_FLAG) 2052 && !cindent_on() 2053 && !p_ri) { 2054 #define INPUT_BUFLEN 100 2055 char buf[INPUT_BUFLEN + 1]; 2056 colnr_T virtcol = 0; 2057 2058 buf[0] = (char)c; 2059 int i = 1; 2060 if (textwidth > 0) { 2061 virtcol = get_nolist_virtcol(); 2062 } 2063 // Stop the string when: 2064 // - no more chars available 2065 // - finding a special character (command key) 2066 // - buffer is full 2067 // - running into the 'textwidth' boundary 2068 // - need to check for abbreviation: A non-word char after a word-char 2069 while ((c = vpeekc()) != NUL 2070 && !ISSPECIAL(c) 2071 && MB_BYTE2LEN(c) == 1 2072 && i < INPUT_BUFLEN 2073 && (textwidth == 0 2074 || (virtcol += byte2cells((uint8_t)buf[i - 1])) < (colnr_T)textwidth) 2075 && !(!no_abbr && !vim_iswordc(c) && vim_iswordc((uint8_t)buf[i - 1]))) { 2076 c = vgetc(); 2077 buf[i++] = (char)c; 2078 } 2079 2080 do_digraph(-1); // clear digraphs 2081 do_digraph((uint8_t)buf[i - 1]); // may be the start of a digraph 2082 buf[i] = NUL; 2083 ins_str(buf, (size_t)i); 2084 if (flags & INSCHAR_CTRLV) { 2085 redo_literal((uint8_t)(*buf)); 2086 i = 1; 2087 } else { 2088 i = 0; 2089 } 2090 if (buf[i] != NUL) { 2091 AppendToRedobuffLit(buf + i, -1); 2092 } 2093 } else { 2094 int cc; 2095 2096 if ((cc = utf_char2len(c)) > 1) { 2097 char buf[MB_MAXCHAR + 1]; 2098 2099 utf_char2bytes(c, buf); 2100 buf[cc] = NUL; 2101 ins_char_bytes(buf, (size_t)cc); 2102 AppendCharToRedobuff(c); 2103 } else { 2104 ins_char(c); 2105 if (flags & INSCHAR_CTRLV) { 2106 redo_literal(c); 2107 } else { 2108 AppendCharToRedobuff(c); 2109 } 2110 } 2111 } 2112 } 2113 2114 // Put a character in the redo buffer, for when just after a CTRL-V. 2115 static void redo_literal(int c) 2116 { 2117 char buf[10]; 2118 2119 // Only digits need special treatment. Translate them into a string of 2120 // three digits. 2121 if (ascii_isdigit(c)) { 2122 vim_snprintf(buf, sizeof(buf), "%03d", c); 2123 AppendToRedobuff(buf); 2124 } else { 2125 AppendCharToRedobuff(c); 2126 } 2127 } 2128 2129 /// start_arrow() is called when an arrow key is used in insert mode. 2130 /// For undo/redo it resembles hitting the <ESC> key. 2131 /// 2132 /// @param end_insert_pos can be NULL 2133 void start_arrow(pos_T *end_insert_pos) 2134 { 2135 start_arrow_common(end_insert_pos, true); 2136 } 2137 2138 /// Like start_arrow() but with end_change argument. 2139 /// Will prepare for redo of CTRL-G U if "end_change" is false. 2140 /// 2141 /// @param end_insert_pos can be NULL 2142 /// @param end_change end undoable change 2143 static void start_arrow_with_change(pos_T *end_insert_pos, bool end_change) 2144 { 2145 start_arrow_common(end_insert_pos, end_change); 2146 if (!end_change) { 2147 AppendCharToRedobuff(Ctrl_G); 2148 AppendCharToRedobuff('U'); 2149 } 2150 } 2151 2152 /// @param end_insert_pos can be NULL 2153 /// @param end_change end undoable change 2154 static void start_arrow_common(pos_T *end_insert_pos, bool end_change) 2155 { 2156 if (!arrow_used && end_change) { // something has been inserted 2157 AppendToRedobuff(ESC_STR); 2158 stop_insert(end_insert_pos, false, false); 2159 arrow_used = true; // This means we stopped the current insert. 2160 } 2161 check_spell_redraw(); 2162 } 2163 2164 // If we skipped highlighting word at cursor, do it now. 2165 // It may be skipped again, thus reset spell_redraw_lnum first. 2166 static void check_spell_redraw(void) 2167 { 2168 if (spell_redraw_lnum != 0) { 2169 linenr_T lnum = spell_redraw_lnum; 2170 2171 spell_redraw_lnum = 0; 2172 redrawWinline(curwin, lnum); 2173 } 2174 } 2175 2176 // stop_arrow() is called before a change is made in insert mode. 2177 // If an arrow key has been used, start a new insertion. 2178 // Returns FAIL if undo is impossible, shouldn't insert then. 2179 int stop_arrow(void) 2180 { 2181 if (arrow_used) { 2182 Insstart = curwin->w_cursor; // new insertion starts here 2183 if (Insstart.col > Insstart_orig.col && !ins_need_undo) { 2184 // Don't update the original insert position when moved to the 2185 // right, except when nothing was inserted yet. 2186 update_Insstart_orig = false; 2187 } 2188 Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); 2189 2190 if (u_save_cursor() == OK) { 2191 arrow_used = false; 2192 ins_need_undo = false; 2193 } 2194 ai_col = 0; 2195 if (State & VREPLACE_FLAG) { 2196 orig_line_count = curbuf->b_ml.ml_line_count; 2197 vr_lines_changed = 1; 2198 } 2199 ResetRedobuff(); 2200 AppendToRedobuff("1i"); // Pretend we start an insertion. 2201 new_insert_skip = 2; 2202 } else if (ins_need_undo) { 2203 if (u_save_cursor() == OK) { 2204 ins_need_undo = false; 2205 } 2206 } 2207 2208 // Always open fold at the cursor line when inserting something. 2209 foldOpenCursor(); 2210 2211 return arrow_used || ins_need_undo ? FAIL : OK; 2212 } 2213 2214 /// Do a few things to stop inserting. 2215 /// "end_insert_pos" is where insert ended. It is NULL when we already jumped 2216 /// to another window/buffer. 2217 /// 2218 /// @param esc called by ins_esc() 2219 /// @param nomove <c-\><c-o>, don't move cursor 2220 static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) 2221 { 2222 stop_redo_ins(); 2223 kv_destroy(replace_stack); // abandon replace stack (reinitializes) 2224 2225 // Save the inserted text for later redo with ^@ and CTRL-A. 2226 // Don't do it when "restart_edit" was set and nothing was inserted, 2227 // otherwise CTRL-O w and then <Left> will clear "last_insert". 2228 String inserted = get_inserted(); 2229 int added = inserted.data == NULL ? 0 : (int)inserted.size - new_insert_skip; 2230 if (did_restart_edit == 0 || added > 0) { 2231 xfree(last_insert.data); 2232 last_insert = inserted; // structure copy 2233 last_insert_skip = added < 0 ? 0 : new_insert_skip; 2234 } else { 2235 xfree(inserted.data); 2236 } 2237 2238 if (!arrow_used && end_insert_pos != NULL) { 2239 int cc; 2240 // Auto-format now. It may seem strange to do this when stopping an 2241 // insertion (or moving the cursor), but it's required when appending 2242 // a line and having it end in a space. But only do it when something 2243 // was actually inserted, otherwise undo won't work. 2244 if (!ins_need_undo && has_format_option(FO_AUTO)) { 2245 pos_T tpos = curwin->w_cursor; 2246 2247 // When the cursor is at the end of the line after a space the 2248 // formatting will move it to the following word. Avoid that by 2249 // moving the cursor onto the space. 2250 cc = 'x'; 2251 if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL) { 2252 dec_cursor(); 2253 cc = gchar_cursor(); 2254 if (!ascii_iswhite(cc)) { 2255 curwin->w_cursor = tpos; 2256 } 2257 } 2258 2259 auto_format(true, false); 2260 2261 if (ascii_iswhite(cc)) { 2262 if (gchar_cursor() != NUL) { 2263 inc_cursor(); 2264 } 2265 // If the cursor is still at the same character, also keep 2266 // the "coladd". 2267 if (gchar_cursor() == NUL 2268 && curwin->w_cursor.lnum == tpos.lnum 2269 && curwin->w_cursor.col == tpos.col) { 2270 curwin->w_cursor.coladd = tpos.coladd; 2271 } 2272 } 2273 } 2274 2275 // If a space was inserted for auto-formatting, remove it now. 2276 check_auto_format(true); 2277 2278 // If we just did an auto-indent, remove the white space from the end 2279 // of the line, and put the cursor back. 2280 // Do this when ESC was used or moving the cursor up/down. 2281 // Check for the old position still being valid, just in case the text 2282 // got changed unexpectedly. 2283 if (!nomove && did_ai && (esc || (vim_strchr(p_cpo, CPO_INDENT) == NULL 2284 && curwin->w_cursor.lnum != 2285 end_insert_pos->lnum)) 2286 && end_insert_pos->lnum <= curbuf->b_ml.ml_line_count) { 2287 pos_T tpos = curwin->w_cursor; 2288 colnr_T prev_col = end_insert_pos->col; 2289 2290 curwin->w_cursor = *end_insert_pos; 2291 check_cursor_col(curwin); // make sure it is not past the line 2292 while (true) { 2293 if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) { 2294 curwin->w_cursor.col--; 2295 } 2296 cc = gchar_cursor(); 2297 if (!ascii_iswhite(cc)) { 2298 break; 2299 } 2300 if (del_char(true) == FAIL) { 2301 break; // should not happen 2302 } 2303 } 2304 if (curwin->w_cursor.lnum != tpos.lnum) { 2305 curwin->w_cursor = tpos; 2306 } else if (curwin->w_cursor.col < prev_col) { 2307 // reset tpos, could have been invalidated in the loop above 2308 tpos = curwin->w_cursor; 2309 tpos.col++; 2310 if (cc != NUL && gchar_pos(&tpos) == NUL) { 2311 curwin->w_cursor.col++; // put cursor back on the NUL 2312 } 2313 } 2314 2315 // <C-S-Right> may have started Visual mode, adjust the position for 2316 // deleted characters. 2317 if (VIsual_active) { 2318 check_visual_pos(); 2319 } 2320 } 2321 } 2322 did_ai = false; 2323 did_si = false; 2324 can_si = false; 2325 can_si_back = false; 2326 2327 // Set '[ and '] to the inserted text. When end_insert_pos is NULL we are 2328 // now in a different buffer. 2329 if (end_insert_pos != NULL) { 2330 curbuf->b_op_start = Insstart; 2331 curbuf->b_op_start_orig = Insstart_orig; 2332 curbuf->b_op_end = *end_insert_pos; 2333 } 2334 } 2335 2336 // Set the last inserted text to a single character. 2337 // Used for the replace command. 2338 void set_last_insert(int c) 2339 { 2340 xfree(last_insert.data); 2341 last_insert.data = xmalloc(MB_MAXBYTES * 3 + 5); 2342 char *s = last_insert.data; 2343 // Use the CTRL-V only when entering a special char 2344 if (c < ' ' || c == DEL) { 2345 *s++ = Ctrl_V; 2346 } 2347 s = add_char2buf(c, s); 2348 *s++ = ESC; 2349 *s = NUL; 2350 last_insert.size = (size_t)(s - last_insert.data); 2351 last_insert_skip = 0; 2352 } 2353 2354 #if defined(EXITFREE) 2355 void free_last_insert(void) 2356 { 2357 API_CLEAR_STRING(last_insert); 2358 } 2359 #endif 2360 2361 // move cursor to start of line 2362 // if flags & BL_WHITE move to first non-white 2363 // if flags & BL_SOL move to first non-white if startofline is set, 2364 // otherwise keep "curswant" column 2365 // if flags & BL_FIX don't leave the cursor on a NUL. 2366 void beginline(int flags) 2367 { 2368 if ((flags & BL_SOL) && !p_sol) { 2369 coladvance(curwin, curwin->w_curswant); 2370 } else { 2371 curwin->w_cursor.col = 0; 2372 curwin->w_cursor.coladd = 0; 2373 2374 if (flags & (BL_WHITE | BL_SOL)) { 2375 for (char *ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr) 2376 && !((flags & BL_FIX) && ptr[1] == NUL); ptr++) { 2377 curwin->w_cursor.col++; 2378 } 2379 } 2380 curwin->w_set_curswant = true; 2381 } 2382 adjust_skipcol(); 2383 } 2384 2385 // oneright oneleft cursor_down cursor_up 2386 // 2387 // Move one char {right,left,down,up}. 2388 // Doesn't move onto the NUL past the end of the line, unless it is allowed. 2389 // Return OK when successful, FAIL when we hit a line of file boundary. 2390 2391 int oneright(void) 2392 { 2393 char *ptr; 2394 2395 if (virtual_active(curwin)) { 2396 pos_T prevpos = curwin->w_cursor; 2397 2398 // Adjust for multi-wide char (excluding TAB) 2399 ptr = get_cursor_pos_ptr(); 2400 coladvance(curwin, getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) 2401 ? ptr2cells(ptr) : 1)); 2402 curwin->w_set_curswant = true; 2403 // Return OK if the cursor moved, FAIL otherwise (at window edge). 2404 return (prevpos.col != curwin->w_cursor.col 2405 || prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL; 2406 } 2407 2408 ptr = get_cursor_pos_ptr(); 2409 if (*ptr == NUL) { 2410 return FAIL; // already at the very end 2411 } 2412 2413 int l = utfc_ptr2len(ptr); 2414 2415 // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit' 2416 // contains "onemore". 2417 if (ptr[l] == NUL && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) { 2418 return FAIL; 2419 } 2420 curwin->w_cursor.col += l; 2421 2422 curwin->w_set_curswant = true; 2423 adjust_skipcol(); 2424 return OK; 2425 } 2426 2427 int oneleft(void) 2428 { 2429 if (virtual_active(curwin)) { 2430 int v = getviscol(); 2431 2432 if (v == 0) { 2433 return FAIL; 2434 } 2435 2436 // We might get stuck on 'showbreak', skip over it. 2437 int width = 1; 2438 while (true) { 2439 coladvance(curwin, v - width); 2440 // getviscol() is slow, skip it when 'showbreak' is empty, 2441 // 'breakindent' is not set and there are no multi-byte 2442 // characters 2443 if (getviscol() < v) { 2444 break; 2445 } 2446 width++; 2447 } 2448 2449 if (curwin->w_cursor.coladd == 1) { 2450 // Adjust for multi-wide char (not a TAB) 2451 char *ptr = get_cursor_pos_ptr(); 2452 if (*ptr != TAB && vim_isprintc(utf_ptr2char(ptr)) && ptr2cells(ptr) > 1) { 2453 curwin->w_cursor.coladd = 0; 2454 } 2455 } 2456 2457 curwin->w_set_curswant = true; 2458 adjust_skipcol(); 2459 return OK; 2460 } 2461 2462 if (curwin->w_cursor.col == 0) { 2463 return FAIL; 2464 } 2465 2466 curwin->w_set_curswant = true; 2467 curwin->w_cursor.col--; 2468 2469 // if the character on the left of the current cursor is a multi-byte 2470 // character, move to its first byte 2471 mb_adjust_cursor(); 2472 adjust_skipcol(); 2473 return OK; 2474 } 2475 2476 /// Move the cursor up "n" lines in window "wp". Takes care of closed folds. 2477 /// Skips over concealed lines when "skip_conceal" is true. 2478 void cursor_up_inner(win_T *wp, linenr_T n, bool skip_conceal) 2479 { 2480 linenr_T lnum = wp->w_cursor.lnum; 2481 2482 if (n >= lnum) { 2483 lnum = 1; 2484 } else if (win_lines_concealed(wp)) { 2485 // Count each sequence of folded lines as one logical line. 2486 2487 // go to the start of the current fold 2488 hasFolding(wp, lnum, &lnum, NULL); 2489 2490 while (n--) { 2491 // move up one line 2492 lnum--; 2493 if (lnum <= 1) { 2494 break; 2495 } 2496 n += skip_conceal && decor_conceal_line(wp, lnum - 1, true); 2497 // If we entered a fold, move to the beginning, unless in 2498 // Insert mode or when 'foldopen' contains "all": it will open 2499 // in a moment. 2500 if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & kOptFdoFlagAll))) { 2501 hasFolding(wp, lnum, &lnum, NULL); 2502 } 2503 } 2504 lnum = MAX(lnum, 1); 2505 } else { 2506 lnum -= n; 2507 } 2508 2509 wp->w_cursor.lnum = lnum; 2510 } 2511 2512 /// @param upd_topline When true: update topline 2513 int cursor_up(linenr_T n, bool upd_topline) 2514 { 2515 // This fails if the cursor is already in the first line. 2516 if (n > 0 && curwin->w_cursor.lnum <= 1) { 2517 return FAIL; 2518 } 2519 cursor_up_inner(curwin, n, false); 2520 2521 // try to advance to the column we want to be at 2522 coladvance(curwin, curwin->w_curswant); 2523 2524 if (upd_topline) { 2525 update_topline(curwin); // make sure curwin->w_topline is valid 2526 } 2527 2528 return OK; 2529 } 2530 2531 /// Move the cursor down "n" lines in window "wp". Takes care of closed folds. 2532 /// Skips over concealed lines when "skip_conceal" is true. 2533 void cursor_down_inner(win_T *wp, int n, bool skip_conceal) 2534 { 2535 linenr_T lnum = wp->w_cursor.lnum; 2536 linenr_T line_count = wp->w_buffer->b_ml.ml_line_count; 2537 2538 if (lnum + n >= line_count) { 2539 lnum = line_count; 2540 } else if (win_lines_concealed(wp)) { 2541 linenr_T last; 2542 2543 // count each sequence of folded lines as one logical line 2544 while (n--) { 2545 if (hasFoldingWin(wp, lnum, NULL, &last, true, NULL)) { 2546 lnum = last + 1; 2547 } else { 2548 lnum++; 2549 } 2550 if (lnum >= line_count) { 2551 break; 2552 } 2553 n += skip_conceal && decor_conceal_line(wp, lnum - 1, true); 2554 } 2555 lnum = MIN(lnum, line_count); 2556 } else { 2557 lnum += (linenr_T)n; 2558 } 2559 2560 wp->w_cursor.lnum = lnum; 2561 } 2562 2563 /// @param upd_topline When true: update topline 2564 int cursor_down(int n, bool upd_topline) 2565 { 2566 linenr_T lnum = curwin->w_cursor.lnum; 2567 // This fails if the cursor is already in the last (folded) line. 2568 hasFoldingWin(curwin, lnum, NULL, &lnum, true, NULL); 2569 if (n > 0 && lnum >= curwin->w_buffer->b_ml.ml_line_count) { 2570 return FAIL; 2571 } 2572 cursor_down_inner(curwin, n, false); 2573 2574 // try to advance to the column we want to be at 2575 coladvance(curwin, curwin->w_curswant); 2576 2577 if (upd_topline) { 2578 update_topline(curwin); // make sure curwin->w_topline is valid 2579 } 2580 2581 return OK; 2582 } 2583 2584 /// Stuff the last inserted text in the read buffer. 2585 /// Last_insert actually is a copy of the redo buffer, so we 2586 /// first have to remove the command. 2587 /// 2588 /// @param c Command character to be inserted 2589 /// @param count Repeat this many times 2590 /// @param no_esc Don't add an ESC at the end 2591 int stuff_inserted(int c, int count, int no_esc) 2592 { 2593 char last = NUL; 2594 2595 String insert = get_last_insert(); // text to be inserted 2596 if (insert.data == NULL) { 2597 emsg(_(e_noinstext)); 2598 return FAIL; 2599 } 2600 2601 // may want to stuff the command character, to start Insert mode 2602 if (c != NUL) { 2603 stuffcharReadbuff(c); 2604 } 2605 2606 if (insert.size > 0) { 2607 // look for the last ESC in 'insert' 2608 for (char *p = insert.data + insert.size - 1; p >= insert.data; p--) { 2609 if (*p == ESC) { 2610 insert.size = (size_t)(p - insert.data); 2611 break; 2612 } 2613 } 2614 } 2615 2616 if (insert.size > 0) { 2617 char *p = insert.data + insert.size - 1; 2618 // when the last char is either "0" or "^" it will be quoted if no ESC 2619 // comes after it OR if it will inserted more than once and "ptr" 2620 // starts with ^D. -- Acevedo 2621 if ((*p == '0' || *p == '^') 2622 && (no_esc || (*insert.data == Ctrl_D && count > 1))) { 2623 last = *p; 2624 insert.size--; 2625 } 2626 } 2627 2628 do { 2629 stuffReadbuffLen(insert.data, (ptrdiff_t)insert.size); 2630 // A trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^". 2631 switch (last) { 2632 case '0': 2633 stuffReadbuffLen(S_LEN("\026\060\064\070")); 2634 break; 2635 case '^': 2636 stuffReadbuffLen(S_LEN("\026^")); 2637 break; 2638 default: 2639 break; 2640 } 2641 } while (--count > 0); 2642 2643 // may want to stuff a trailing ESC, to get out of Insert mode 2644 if (!no_esc) { 2645 stuffcharReadbuff(ESC); 2646 } 2647 2648 return OK; 2649 } 2650 2651 String get_last_insert(void) 2652 FUNC_ATTR_PURE 2653 { 2654 return last_insert.data == NULL ? NULL_STRING : (String){ 2655 .data = last_insert.data + last_insert_skip, 2656 .size = last_insert.size - (size_t)last_insert_skip, 2657 }; 2658 } 2659 2660 // Get last inserted string, and remove trailing <Esc>. 2661 // Returns pointer to allocated memory (must be freed) or NULL. 2662 char *get_last_insert_save(void) 2663 { 2664 String insert = get_last_insert(); 2665 2666 if (insert.data == NULL) { 2667 return NULL; 2668 } 2669 2670 char *s = xmemdupz(insert.data, insert.size); 2671 if (insert.size > 0 && s[insert.size - 1] == ESC) { // remain trailing ESC 2672 s[--insert.size] = NUL; 2673 } 2674 return s; 2675 } 2676 2677 /// Check the word in front of the cursor for an abbreviation. 2678 /// Called when the non-id character "c" has been entered. 2679 /// When an abbreviation is recognized it is removed from the text and 2680 /// the replacement string is inserted in typebuf.tb_buf[], followed by "c". 2681 /// 2682 /// @param c character 2683 /// 2684 /// @return true if the word is a known abbreviation. 2685 static bool echeck_abbr(int c) 2686 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 2687 { 2688 // Don't check for abbreviation in paste mode, when disabled and just 2689 // after moving around with cursor keys. 2690 if (p_paste || no_abbr || arrow_used) { 2691 return false; 2692 } 2693 2694 return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col, 2695 curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); 2696 } 2697 2698 // replace-stack functions 2699 // 2700 // When replacing characters, the replaced characters are remembered for each 2701 // new character. This is used to re-insert the old text when backspacing. 2702 // 2703 // There is a NUL headed list of characters for each character that is 2704 // currently in the file after the insertion point. When BS is used, one NUL 2705 // headed list is put back for the deleted character. 2706 // 2707 // For a newline, there are two NUL headed lists. One contains the characters 2708 // that the NL replaced. The extra one stores the characters after the cursor 2709 // that were deleted (always white space). 2710 2711 /// Push character that is replaced onto the replace stack. 2712 /// 2713 /// replace_offset is normally 0, in which case replace_push will add a new 2714 /// character at the end of the stack. If replace_offset is not 0, that many 2715 /// characters will be left on the stack above the newly inserted character. 2716 /// 2717 /// @param str character that is replaced (NUL is none) 2718 /// @param len length of character in bytes 2719 void replace_push(char *str, size_t len) 2720 { 2721 // TODO(bfredl): replace_offset is suss af, if we don't need it, this 2722 // function is just kv_concat() :p 2723 if (kv_size(replace_stack) < (size_t)replace_offset) { // nothing to do 2724 return; 2725 } 2726 2727 kv_ensure_space(replace_stack, len); 2728 2729 char *p = replace_stack.items + kv_size(replace_stack) - replace_offset; 2730 if (replace_offset) { 2731 memmove(p + len, p, (size_t)replace_offset); 2732 } 2733 memcpy(p, str, len); 2734 kv_size(replace_stack) += len; 2735 } 2736 2737 /// push NUL as separator between entries in the stack 2738 void replace_push_nul(void) 2739 { 2740 replace_push("", 1); 2741 } 2742 2743 /// Check top of replace stack, pop it if it was NUL 2744 /// 2745 /// when a non-NUL byte is found, use mb_replace_pop_ins() to 2746 /// pop one complete multibyte character. 2747 /// 2748 /// @return -1 if stack is empty, last byte of char or NUL otherwise 2749 static int replace_pop_if_nul(void) 2750 { 2751 int ch = (kv_size(replace_stack)) ? (uint8_t)kv_A(replace_stack, kv_size(replace_stack) - 1) : -1; 2752 if (ch == NUL) { 2753 kv_size(replace_stack)--; 2754 } 2755 return ch; 2756 } 2757 2758 /// Join the top two items on the replace stack. This removes to "off"'th NUL 2759 /// encountered. 2760 /// 2761 /// @param off offset for which NUL to remove 2762 void replace_join(int off) 2763 { 2764 for (ssize_t i = (ssize_t)kv_size(replace_stack); --i >= 0;) { 2765 if (kv_A(replace_stack, i) == NUL && off-- <= 0) { 2766 kv_size(replace_stack)--; 2767 memmove(&kv_A(replace_stack, i), &kv_A(replace_stack, i + 1), 2768 (kv_size(replace_stack) - (size_t)i)); 2769 return; 2770 } 2771 } 2772 } 2773 2774 /// Pop bytes from the replace stack until a NUL is found, and insert them 2775 /// before the cursor. Can only be used in MODE_REPLACE or MODE_VREPLACE state. 2776 static void replace_pop_ins(void) 2777 { 2778 int oldState = State; 2779 2780 State = MODE_NORMAL; // don't want MODE_REPLACE here 2781 while ((replace_pop_if_nul()) > 0) { 2782 mb_replace_pop_ins(); 2783 dec_cursor(); 2784 } 2785 State = oldState; 2786 } 2787 2788 /// Insert multibyte char popped from the replace stack. 2789 /// 2790 /// caller must already have checked the top of the stack is not NUL!! 2791 static void mb_replace_pop_ins(void) 2792 { 2793 int len = utf_head_off(&kv_A(replace_stack, 0), 2794 &kv_A(replace_stack, kv_size(replace_stack) - 1)) + 1; 2795 kv_size(replace_stack) -= (size_t)len; 2796 ins_bytes_len(&kv_A(replace_stack, kv_size(replace_stack)), (size_t)len); 2797 } 2798 2799 // Handle doing a BS for one character. 2800 // cc < 0: replace stack empty, just move cursor 2801 // cc == 0: character was inserted, delete it 2802 // cc > 0: character was replaced, put cc (first byte of original char) back 2803 // and check for more characters to be put back 2804 // When "limit_col" is >= 0, don't delete before this column. Matters when 2805 // using composing characters, use del_char_after_col() instead of del_char(). 2806 static void replace_do_bs(int limit_col) 2807 { 2808 colnr_T start_vcol; 2809 const int l_State = State; 2810 2811 int cc = replace_pop_if_nul(); 2812 if (cc > 0) { 2813 int orig_len = 0; 2814 int orig_vcols = 0; 2815 if (l_State & VREPLACE_FLAG) { 2816 // Get the number of screen cells used by the character we are 2817 // going to delete. 2818 getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL); 2819 orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol); 2820 } 2821 del_char_after_col(limit_col); 2822 if (l_State & VREPLACE_FLAG) { 2823 orig_len = get_cursor_pos_len(); 2824 } 2825 replace_pop_ins(); 2826 2827 if (l_State & VREPLACE_FLAG) { 2828 // Get the number of screen cells used by the inserted characters 2829 char *p = get_cursor_pos_ptr(); 2830 int ins_len = get_cursor_pos_len() - orig_len; 2831 int vcol = start_vcol; 2832 for (int i = 0; i < ins_len; i++) { 2833 vcol += win_chartabsize(curwin, p + i, vcol); 2834 i += utfc_ptr2len(p) - 1; 2835 } 2836 vcol -= start_vcol; 2837 2838 // Delete spaces that were inserted after the cursor to keep the 2839 // text aligned. 2840 curwin->w_cursor.col += ins_len; 2841 while (vcol > orig_vcols && gchar_cursor() == ' ') { 2842 del_char(false); 2843 orig_vcols++; 2844 } 2845 curwin->w_cursor.col -= ins_len; 2846 } 2847 2848 // mark the buffer as changed and prepare for displaying 2849 changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); 2850 } else if (cc == 0) { 2851 del_char_after_col(limit_col); 2852 } 2853 } 2854 2855 static void ins_reg(void) 2856 { 2857 bool need_redraw = false; 2858 int literally = 0; 2859 int vis_active = VIsual_active; 2860 2861 // If we are going to wait for a character, show a '"'. 2862 pc_status = PC_STATUS_UNSET; 2863 if (redrawing() && !char_avail()) { 2864 // may need to redraw when no more chars available now 2865 ins_redraw(false); 2866 2867 edit_putchar('"', true); 2868 add_to_showcmd_c(Ctrl_R); 2869 } 2870 2871 // Don't map the register name. This also prevents the mode message to be 2872 // deleted when ESC is hit. 2873 no_mapping++; 2874 allow_keys++; 2875 int regname = plain_vgetc(); 2876 LANGMAP_ADJUST(regname, true); 2877 if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) { 2878 // Get a third key for literal register insertion 2879 literally = regname; 2880 add_to_showcmd_c(literally); 2881 regname = plain_vgetc(); 2882 LANGMAP_ADJUST(regname, true); 2883 } 2884 no_mapping--; 2885 allow_keys--; 2886 2887 // Don't call u_sync() while typing the expression or giving an error 2888 // message for it. Only call it explicitly. 2889 no_u_sync++; 2890 if (regname == '=') { 2891 pos_T curpos = curwin->w_cursor; 2892 2893 // Sync undo when evaluating the expression calls setline() or 2894 // append(), so that it can be undone separately. 2895 u_sync_once = 2; 2896 2897 regname = get_expr_register(); 2898 2899 // Cursor may be moved back a column. 2900 curwin->w_cursor = curpos; 2901 check_cursor(curwin); 2902 } 2903 if (regname == NUL || !valid_yank_reg(regname, false)) { 2904 vim_beep(kOptBoFlagRegister); 2905 need_redraw = true; // remove the '"' 2906 } else { 2907 yankreg_T *reg = get_yank_register(regname, YREG_PASTE); 2908 2909 if (literally == Ctrl_O || literally == Ctrl_P) { 2910 // Append the command to the redo buffer. 2911 AppendCharToRedobuff(Ctrl_R); 2912 AppendCharToRedobuff(literally); 2913 AppendCharToRedobuff(regname); 2914 2915 do_put(regname, NULL, BACKWARD, 1, 2916 (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND); 2917 } else if (reg->y_size > 1 && is_literal_register(regname)) { 2918 AppendCharToRedobuff(Ctrl_R); 2919 AppendCharToRedobuff(regname); 2920 do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND); 2921 } else if (insert_reg(regname, NULL, !!literally) == FAIL) { 2922 vim_beep(kOptBoFlagRegister); 2923 need_redraw = true; // remove the '"' 2924 } else if (stop_insert_mode) { 2925 // When the '=' register was used and a function was invoked that 2926 // did ":stopinsert" then stuff_empty() returns false but we won't 2927 // insert anything, need to remove the '"' 2928 need_redraw = true; 2929 } 2930 } 2931 no_u_sync--; 2932 if (u_sync_once == 1) { 2933 ins_need_undo = true; 2934 } 2935 u_sync_once = 0; 2936 2937 // If the inserted register is empty, we need to remove the '"'. Do this before 2938 // clearing showcmd, which emits an event that can also update the screen. 2939 if (need_redraw || stuff_empty()) { 2940 edit_unputchar(); 2941 } 2942 clear_showcmd(); 2943 2944 // Disallow starting Visual mode here, would get a weird mode. 2945 if (!vis_active && VIsual_active) { 2946 end_visual_mode(); 2947 } 2948 } 2949 2950 // CTRL-G commands in Insert mode. 2951 static void ins_ctrl_g(void) 2952 { 2953 // Right after CTRL-X the cursor will be after the ruler. 2954 setcursor(); 2955 2956 // Don't map the second key. This also prevents the mode message to be 2957 // deleted when ESC is hit. 2958 no_mapping++; 2959 allow_keys++; 2960 int c = plain_vgetc(); 2961 no_mapping--; 2962 allow_keys--; 2963 switch (c) { 2964 // CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col 2965 case K_UP: 2966 case Ctrl_K: 2967 case 'k': 2968 ins_up(true); 2969 break; 2970 2971 // CTRL-G j and CTRL-G <Down>: cursor down to Insstart.col 2972 case K_DOWN: 2973 case Ctrl_J: 2974 case 'j': 2975 ins_down(true); 2976 break; 2977 2978 // CTRL-G u: start new undoable edit 2979 case 'u': 2980 u_sync(true); 2981 ins_need_undo = true; 2982 2983 // Need to reset Insstart, esp. because a BS that joins 2984 // a line to the previous one must save for undo. 2985 update_Insstart_orig = false; 2986 Insstart = curwin->w_cursor; 2987 break; 2988 2989 // CTRL-G U: do not break undo with the next char. 2990 case 'U': 2991 // Allow one left/right cursor movement with the next char, 2992 // without breaking undo. 2993 dont_sync_undo = kNone; 2994 break; 2995 2996 case ESC: 2997 // Esc after CTRL-G cancels it. 2998 break; 2999 3000 // Unknown CTRL-G command, reserved for future expansion. 3001 default: 3002 vim_beep(kOptBoFlagCtrlg); 3003 } 3004 } 3005 3006 // CTRL-^ in Insert mode. 3007 static void ins_ctrl_hat(void) 3008 { 3009 if (map_to_exists_mode("", MODE_LANGMAP, false)) { 3010 // ":lmap" mappings exists, Toggle use of ":lmap" mappings. 3011 if (State & MODE_LANGMAP) { 3012 curbuf->b_p_iminsert = B_IMODE_NONE; 3013 State &= ~MODE_LANGMAP; 3014 } else { 3015 curbuf->b_p_iminsert = B_IMODE_LMAP; 3016 State |= MODE_LANGMAP; 3017 } 3018 } 3019 set_iminsert_global(curbuf); 3020 showmode(); 3021 // Show/unshow value of 'keymap' in status lines. 3022 status_redraw_curbuf(); 3023 } 3024 3025 /// Handle ESC in insert mode. 3026 /// 3027 /// @param[in,out] count repeat count of the insert command 3028 /// @param cmdchar command that started the insert 3029 /// @param nomove when true, don't move the cursor 3030 /// 3031 /// @return true when leaving insert mode, false when repeating the insert. 3032 static bool ins_esc(int *count, int cmdchar, bool nomove) 3033 FUNC_ATTR_NONNULL_ARG(1) 3034 { 3035 static bool disabled_redraw = false; 3036 3037 check_spell_redraw(); 3038 3039 int temp = curwin->w_cursor.col; 3040 if (disabled_redraw) { 3041 RedrawingDisabled--; 3042 disabled_redraw = false; 3043 } 3044 if (!arrow_used) { 3045 // Don't append the ESC for "r<CR>" and "grx". 3046 if (cmdchar != 'r' && cmdchar != 'v') { 3047 AppendToRedobuff(ESC_STR); 3048 } 3049 3050 // Repeating insert may take a long time. Check for 3051 // interrupt now and then. 3052 if (*count > 0) { 3053 line_breakcheck(); 3054 if (got_int) { 3055 *count = 0; 3056 } 3057 } 3058 3059 if (--*count > 0) { // repeat what was typed 3060 // Vi repeats the insert without replacing characters. 3061 if (vim_strchr(p_cpo, CPO_REPLCNT) != NULL) { 3062 State &= ~REPLACE_FLAG; 3063 } 3064 3065 start_redo_ins(); 3066 if (cmdchar == 'r' || cmdchar == 'v') { 3067 stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer 3068 } 3069 RedrawingDisabled++; 3070 disabled_redraw = true; 3071 // Repeat the insert 3072 return false; 3073 } 3074 stop_insert(&curwin->w_cursor, true, nomove); 3075 undisplay_dollar(); 3076 } 3077 3078 if (cmdchar != 'r' && cmdchar != 'v') { 3079 ins_apply_autocmds(EVENT_INSERTLEAVEPRE); 3080 } 3081 3082 // When an autoindent was removed, curswant stays after the 3083 // indent 3084 if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) { 3085 curwin->w_set_curswant = true; 3086 } 3087 3088 // Remember the last Insert position in the '^ mark. 3089 if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) { 3090 fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor); 3091 RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum, view); 3092 } 3093 3094 // The cursor should end up on the last inserted character. 3095 // Don't do it for CTRL-O, unless past the end of the line. 3096 if (!nomove 3097 && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0) 3098 && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active)) 3099 && !revins_on) { 3100 if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == kOptVeFlagAll) { 3101 oneleft(); 3102 if (restart_edit != NUL) { 3103 curwin->w_cursor.coladd++; 3104 } 3105 } else { 3106 curwin->w_cursor.col--; 3107 curwin->w_valid &= ~(VALID_WCOL|VALID_VIRTCOL); 3108 // Correct cursor for multi-byte character. 3109 mb_adjust_cursor(); 3110 } 3111 } 3112 3113 State = MODE_NORMAL; 3114 may_trigger_modechanged(); 3115 // need to position cursor again when on a TAB and 3116 // when on a char with inline virtual text 3117 if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) { 3118 curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); 3119 } 3120 3121 setmouse(); 3122 ui_cursor_shape(); // may show different cursor shape 3123 3124 // When recording or for CTRL-O, need to display the new mode. 3125 // Otherwise remove the mode message. 3126 if (reg_recording != 0 || restart_edit != NUL) { 3127 showmode(); 3128 } else if (p_smd && (got_int || !skip_showmode()) 3129 && !(p_ch == 0 && !ui_has(kUIMessages))) { 3130 unshowmode(false); 3131 } 3132 // Exit Insert mode 3133 return true; 3134 } 3135 3136 // Toggle language: revins_on. 3137 // Move to end of reverse inserted text. 3138 static void ins_ctrl_(void) 3139 { 3140 if (revins_on && revins_chars && revins_scol >= 0) { 3141 while (gchar_cursor() != NUL && revins_chars--) { 3142 curwin->w_cursor.col++; 3143 } 3144 } 3145 p_ri = !p_ri; 3146 revins_on = (State == MODE_INSERT && p_ri); 3147 if (revins_on) { 3148 revins_scol = curwin->w_cursor.col; 3149 revins_legal++; 3150 revins_chars = 0; 3151 undisplay_dollar(); 3152 } else { 3153 revins_scol = -1; 3154 } 3155 showmode(); 3156 } 3157 3158 /// If 'keymodel' contains "startsel", may start selection. 3159 /// 3160 /// @param c character to check 3161 // 3162 /// @return true when a CTRL-O and other keys stuffed. 3163 static bool ins_start_select(int c) 3164 FUNC_ATTR_WARN_UNUSED_RESULT 3165 { 3166 if (!km_startsel) { 3167 return false; 3168 } 3169 switch (c) { 3170 case K_KHOME: 3171 case K_KEND: 3172 case K_PAGEUP: 3173 case K_KPAGEUP: 3174 case K_PAGEDOWN: 3175 case K_KPAGEDOWN: 3176 if (!(mod_mask & MOD_MASK_SHIFT)) { 3177 break; 3178 } 3179 FALLTHROUGH; 3180 case K_S_LEFT: 3181 case K_S_RIGHT: 3182 case K_S_UP: 3183 case K_S_DOWN: 3184 case K_S_END: 3185 case K_S_HOME: 3186 // Start selection right away, the cursor can move with CTRL-O when 3187 // beyond the end of the line. 3188 start_selection(); 3189 3190 // Execute the key in (insert) Select mode. 3191 stuffcharReadbuff(Ctrl_O); 3192 if (mod_mask) { 3193 const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER, 3194 (char)(uint8_t)mod_mask, NUL }; 3195 stuffReadbuffLen(buf, 3); 3196 } 3197 stuffcharReadbuff(c); 3198 return true; 3199 } 3200 return false; 3201 } 3202 3203 // <Insert> key in Insert mode: toggle insert/replace mode. 3204 static void ins_insert(int replaceState) 3205 { 3206 set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) 3207 ? "i" 3208 : replaceState == MODE_VREPLACE ? "v" : "r"), 1); 3209 ins_apply_autocmds(EVENT_INSERTCHANGE); 3210 if (State & REPLACE_FLAG) { 3211 State = MODE_INSERT | (State & MODE_LANGMAP); 3212 } else { 3213 State = replaceState | (State & MODE_LANGMAP); 3214 } 3215 may_trigger_modechanged(); 3216 AppendCharToRedobuff(K_INS); 3217 showmode(); 3218 ui_cursor_shape(); // may show different cursor shape 3219 } 3220 3221 // Pressed CTRL-O in Insert mode. 3222 static void ins_ctrl_o(void) 3223 { 3224 restart_VIsual_select = 0; 3225 if (State & VREPLACE_FLAG) { 3226 restart_edit = 'V'; 3227 } else if (State & REPLACE_FLAG) { 3228 restart_edit = 'R'; 3229 } else { 3230 restart_edit = 'I'; 3231 } 3232 if (virtual_active(curwin)) { 3233 ins_at_eol = false; // cursor always keeps its column 3234 } else { 3235 ins_at_eol = (gchar_cursor() == NUL); 3236 } 3237 } 3238 3239 // If the cursor is on an indent, ^T/^D insert/delete one 3240 // shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>". 3241 // Always round the indent to 'shiftwidth', this is compatible 3242 // with vi. But vi only supports ^T and ^D after an 3243 // autoindent, we support it everywhere. 3244 static void ins_shift(int c, int lastc) 3245 { 3246 if (stop_arrow() == FAIL) { 3247 return; 3248 } 3249 AppendCharToRedobuff(c); 3250 3251 // 0^D and ^^D: remove all indent. 3252 if (c == Ctrl_D && (lastc == '0' || lastc == '^') 3253 && curwin->w_cursor.col > 0) { 3254 curwin->w_cursor.col--; 3255 del_char(false); // delete the '^' or '0' 3256 // In Replace mode, restore the characters that '^' or '0' replaced. 3257 if (State & REPLACE_FLAG) { 3258 replace_pop_ins(); 3259 } 3260 if (lastc == '^') { 3261 old_indent = get_indent(); // remember curr. indent 3262 } 3263 change_indent(INDENT_SET, 0, true, true); 3264 } else { 3265 change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, true); 3266 } 3267 3268 if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) { 3269 did_ai = false; 3270 } 3271 did_si = false; 3272 can_si = false; 3273 can_si_back = false; 3274 can_cindent = false; // no cindenting after ^D or ^T 3275 } 3276 3277 static void ins_del(void) 3278 { 3279 if (stop_arrow() == FAIL) { 3280 return; 3281 } 3282 if (gchar_cursor() == NUL) { // delete newline 3283 const int temp = curwin->w_cursor.col; 3284 if (!can_bs(BS_EOL) // only if "eol" included 3285 || do_join(2, false, true, false, false) == FAIL) { 3286 vim_beep(kOptBoFlagBackspace); 3287 } else { 3288 curwin->w_cursor.col = temp; 3289 // Adjust orig_line_count in case more lines have been deleted than 3290 // have been added. That makes sure, that open_line() later 3291 // can access all buffer lines correctly 3292 if (State & VREPLACE_FLAG 3293 && orig_line_count > curbuf->b_ml.ml_line_count) { 3294 orig_line_count = curbuf->b_ml.ml_line_count; 3295 } 3296 } 3297 } else if (del_char(false) == FAIL) { // delete char under cursor 3298 vim_beep(kOptBoFlagBackspace); 3299 } 3300 did_ai = false; 3301 did_si = false; 3302 can_si = false; 3303 can_si_back = false; 3304 AppendCharToRedobuff(K_DEL); 3305 } 3306 3307 /// Handle Backspace, delete-word and delete-line in Insert mode. 3308 /// 3309 /// @param c character that was typed 3310 /// @param mode backspace mode to use 3311 /// @param[in,out] inserted_space_p whether a space was the last 3312 // character inserted 3313 /// 3314 /// @return true when backspace was actually used. 3315 static bool ins_bs(int c, int mode, int *inserted_space_p) 3316 FUNC_ATTR_NONNULL_ARG(3) 3317 { 3318 int cc; 3319 int temp = 0; // init for GCC 3320 bool did_backspace = false; 3321 bool call_fix_indent = false; 3322 3323 // can't delete anything in an empty file 3324 // can't backup past first character in buffer 3325 // can't backup past starting point unless 'backspace' > 1 3326 // can backup to a previous line if 'backspace' == 0 3327 if (buf_is_empty(curbuf) 3328 || (!revins_on 3329 && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) 3330 || (!can_bs(BS_START) 3331 && ((arrow_used && !bt_prompt(curbuf)) 3332 || (curwin->w_cursor.lnum == Insstart_orig.lnum 3333 && curwin->w_cursor.col <= Insstart_orig.col))) 3334 || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 3335 && curwin->w_cursor.col <= ai_col) 3336 || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { 3337 vim_beep(kOptBoFlagBackspace); 3338 return false; 3339 } 3340 3341 if (stop_arrow() == FAIL) { 3342 return false; 3343 } 3344 bool in_indent = inindent(0); 3345 if (in_indent) { 3346 can_cindent = false; 3347 } 3348 end_comment_pending = NUL; // After BS, don't auto-end comment 3349 if (revins_on) { // put cursor after last inserted char 3350 inc_cursor(); 3351 } 3352 // Virtualedit: 3353 // BACKSPACE_CHAR eats a virtual space 3354 // BACKSPACE_WORD eats all coladd 3355 // BACKSPACE_LINE eats all coladd and keeps going 3356 if (curwin->w_cursor.coladd > 0) { 3357 if (mode == BACKSPACE_CHAR) { 3358 curwin->w_cursor.coladd--; 3359 return true; 3360 } 3361 if (mode == BACKSPACE_WORD) { 3362 curwin->w_cursor.coladd = 0; 3363 return true; 3364 } 3365 curwin->w_cursor.coladd = 0; 3366 } 3367 3368 // Delete newline! 3369 if (curwin->w_cursor.col == 0) { 3370 linenr_T lnum = Insstart.lnum; 3371 if (curwin->w_cursor.lnum == lnum || revins_on) { 3372 if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), 3373 (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { 3374 return false; 3375 } 3376 Insstart.lnum--; 3377 Insstart.col = ml_get_len(Insstart.lnum); 3378 } 3379 // In replace mode: 3380 // cc < 0: NL was inserted, delete it 3381 // cc >= 0: NL was replaced, put original characters back 3382 cc = -1; 3383 if (State & REPLACE_FLAG) { 3384 cc = replace_pop_if_nul(); // returns -1 if NL was inserted 3385 } 3386 // In replace mode, in the line we started replacing, we only move the 3387 // cursor. 3388 if ((State & REPLACE_FLAG) && curwin->w_cursor.lnum <= lnum) { 3389 dec_cursor(); 3390 } else { 3391 if (!(State & VREPLACE_FLAG) 3392 || curwin->w_cursor.lnum > orig_line_count) { 3393 temp = gchar_cursor(); // remember current char 3394 curwin->w_cursor.lnum--; 3395 3396 // When "aw" is in 'formatoptions' we must delete the space at 3397 // the end of the line, otherwise the line will be broken 3398 // again when auto-formatting. 3399 if (has_format_option(FO_AUTO) 3400 && has_format_option(FO_WHITE_PAR)) { 3401 const char *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum); 3402 int len = get_cursor_line_len(); 3403 if (len > 0 && ptr[len - 1] == ' ') { 3404 char *newp = xmemdupz(ptr, (size_t)(len - 1)); 3405 3406 if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { 3407 xfree(curbuf->b_ml.ml_line_ptr); 3408 } 3409 curbuf->b_ml.ml_line_ptr = newp; 3410 curbuf->b_ml.ml_line_textlen--; 3411 curbuf->b_ml.ml_flags |= ML_LINE_DIRTY; 3412 } 3413 } 3414 3415 do_join(2, false, false, false, false); 3416 if (temp == NUL && gchar_cursor() != NUL) { 3417 inc_cursor(); 3418 } 3419 } else { 3420 dec_cursor(); 3421 } 3422 3423 // In MODE_REPLACE mode we have to put back the text that was 3424 // replaced by the NL. On the replace stack is first a 3425 // NUL-terminated sequence of characters that were deleted and then 3426 // the characters that NL replaced. 3427 if (State & REPLACE_FLAG) { 3428 // Do the next ins_char() in MODE_NORMAL state, to 3429 // prevent ins_char() from replacing characters and 3430 // avoiding showmatch(). 3431 int oldState = State; 3432 State = MODE_NORMAL; 3433 // restore characters (blanks) deleted after cursor 3434 while (cc > 0) { 3435 colnr_T save_col = curwin->w_cursor.col; 3436 mb_replace_pop_ins(); 3437 curwin->w_cursor.col = save_col; 3438 cc = replace_pop_if_nul(); 3439 } 3440 // restore the characters that NL replaced 3441 replace_pop_ins(); 3442 State = oldState; 3443 } 3444 } 3445 did_ai = false; 3446 } else { 3447 // Delete character(s) before the cursor. 3448 if (revins_on) { // put cursor on last inserted char 3449 dec_cursor(); 3450 } 3451 colnr_T mincol = 0; 3452 // keep indent 3453 if (mode == BACKSPACE_LINE 3454 && (curbuf->b_p_ai || cindent_on()) 3455 && !revins_on) { 3456 colnr_T save_col = curwin->w_cursor.col; 3457 beginline(BL_WHITE); 3458 if (curwin->w_cursor.col < save_col) { 3459 mincol = curwin->w_cursor.col; 3460 // should now fix the indent to match with the previous line 3461 call_fix_indent = true; 3462 } 3463 curwin->w_cursor.col = save_col; 3464 } 3465 3466 // Handle deleting one 'shiftwidth' or 'softtabstop'. 3467 if (mode == BACKSPACE_CHAR 3468 && ((p_sta && in_indent) 3469 || ((get_sts_value() != 0 || tabstop_count(curbuf->b_p_vsts_array)) 3470 && curwin->w_cursor.col > 0 3471 && (*(get_cursor_pos_ptr() - 1) == TAB 3472 || (*(get_cursor_pos_ptr() - 1) == ' ' 3473 && (!*inserted_space_p || arrow_used)))))) { 3474 *inserted_space_p = false; 3475 3476 bool const use_ts = !curwin->w_p_list || curwin->w_p_lcs_chars.tab1; 3477 char *const line = get_cursor_line_ptr(); 3478 char *const cursor_ptr = line + curwin->w_cursor.col; 3479 3480 colnr_T vcol = 0; 3481 colnr_T space_vcol = 0; 3482 StrCharInfo sci = utf_ptr2StrCharInfo(line); 3483 StrCharInfo space_sci = sci; 3484 bool prev_space = false; 3485 3486 // Compute virtual column of cursor position, and find the last 3487 // whitespace before cursor that is preceded by non-whitespace. 3488 // Use charsize_nowrap() so that virtual text and wrapping are ignored. 3489 while (sci.ptr < cursor_ptr) { 3490 bool cur_space = ascii_iswhite(sci.chr.value); 3491 if (!prev_space && cur_space) { 3492 space_sci = sci; 3493 space_vcol = vcol; 3494 } 3495 vcol += charsize_nowrap(curbuf, sci.ptr, use_ts, vcol, sci.chr.value); 3496 sci = utfc_next(sci); 3497 prev_space = cur_space; 3498 } 3499 3500 // Compute the virtual column where we want to be. 3501 colnr_T want_vcol = vcol > 0 ? vcol - 1 : 0; 3502 if (p_sta && in_indent) { 3503 want_vcol -= want_vcol % get_sw_value(curbuf); 3504 } else { 3505 want_vcol = tabstop_start(want_vcol, get_sts_value(), curbuf->b_p_vsts_array); 3506 } 3507 3508 // Find the position to stop backspacing. 3509 // Use charsize_nowrap() so that virtual text and wrapping are ignored. 3510 while (true) { 3511 int size = charsize_nowrap(curbuf, space_sci.ptr, use_ts, space_vcol, space_sci.chr.value); 3512 if (space_vcol + size > want_vcol) { 3513 break; 3514 } 3515 space_vcol += size; 3516 space_sci = utfc_next(space_sci); 3517 } 3518 colnr_T const want_col = (int)(space_sci.ptr - line); 3519 3520 // Delete characters until we are at or before want_col. 3521 while (curwin->w_cursor.col > want_col) { 3522 dec_cursor(); 3523 if (State & REPLACE_FLAG) { 3524 // Don't delete characters before the insert point when in Replace mode. 3525 if (curwin->w_cursor.lnum != Insstart.lnum 3526 || curwin->w_cursor.col >= Insstart.col) { 3527 replace_do_bs(-1); 3528 } 3529 } else { 3530 del_char(false); 3531 } 3532 } 3533 3534 // Insert extra spaces until we are at want_vcol. 3535 for (; space_vcol < want_vcol; space_vcol++) { 3536 // Remember the first char we inserted. 3537 if (curwin->w_cursor.lnum == Insstart_orig.lnum 3538 && curwin->w_cursor.col < Insstart_orig.col) { 3539 Insstart_orig.col = curwin->w_cursor.col; 3540 } 3541 3542 if (State & VREPLACE_FLAG) { 3543 ins_char(' '); 3544 } else { 3545 ins_str(S_LEN(" ")); 3546 if ((State & REPLACE_FLAG)) { 3547 replace_push_nul(); 3548 } 3549 } 3550 } 3551 } else { 3552 // Delete up to starting point, start of line or previous word. 3553 3554 int cclass = mb_get_class(get_cursor_pos_ptr()); 3555 do { 3556 if (!revins_on) { // put cursor on char to be deleted 3557 dec_cursor(); 3558 } 3559 cc = gchar_cursor(); 3560 // look multi-byte character class 3561 int prev_cclass = cclass; 3562 cclass = mb_get_class(get_cursor_pos_ptr()); 3563 if (mode == BACKSPACE_WORD && !ascii_isspace(cc)) { // start of word? 3564 mode = BACKSPACE_WORD_NOT_SPACE; 3565 temp = vim_iswordc(cc); 3566 } else if (mode == BACKSPACE_WORD_NOT_SPACE 3567 && ((ascii_isspace(cc) || vim_iswordc(cc) != temp) 3568 || prev_cclass != cclass)) { // end of word? 3569 if (!revins_on) { 3570 inc_cursor(); 3571 } else if (State & REPLACE_FLAG) { 3572 dec_cursor(); 3573 } 3574 break; 3575 } 3576 if (State & REPLACE_FLAG) { 3577 replace_do_bs(-1); 3578 } else { 3579 bool has_composing = false; 3580 if (p_deco) { 3581 char *p0 = get_cursor_pos_ptr(); 3582 has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0), NULL); 3583 } 3584 del_char(false); 3585 // If there are combining characters and 'delcombine' is set 3586 // move the cursor back. Don't back up before the base character. 3587 if (has_composing) { 3588 inc_cursor(); 3589 } 3590 if (revins_chars) { 3591 revins_chars--; 3592 revins_legal++; 3593 } 3594 if (revins_on && gchar_cursor() == NUL) { 3595 break; 3596 } 3597 } 3598 // Just a single backspace?: 3599 if (mode == BACKSPACE_CHAR) { 3600 break; 3601 } 3602 } while (revins_on 3603 || (curwin->w_cursor.col > mincol 3604 && (can_bs(BS_NOSTOP) 3605 || (curwin->w_cursor.lnum != Insstart_orig.lnum 3606 || curwin->w_cursor.col != Insstart_orig.col)))); 3607 } 3608 did_backspace = true; 3609 } 3610 did_si = false; 3611 can_si = false; 3612 can_si_back = false; 3613 if (curwin->w_cursor.col <= 1) { 3614 did_ai = false; 3615 } 3616 3617 if (call_fix_indent) { 3618 fix_indent(); 3619 } 3620 3621 // It's a little strange to put backspaces into the redo 3622 // buffer, but it makes auto-indent a lot easier to deal 3623 // with. 3624 AppendCharToRedobuff(c); 3625 3626 // If deleted before the insertion point, adjust it 3627 if (curwin->w_cursor.lnum == Insstart_orig.lnum 3628 && curwin->w_cursor.col < Insstart_orig.col) { 3629 Insstart_orig.col = curwin->w_cursor.col; 3630 } 3631 3632 // vi behaviour: the cursor moves backward but the character that 3633 // was there remains visible 3634 // Vim behaviour: the cursor moves backward and the character that 3635 // was there is erased from the screen. 3636 // We can emulate the vi behaviour by pretending there is a dollar 3637 // displayed even when there isn't. 3638 // --pkv Sun Jan 19 01:56:40 EST 2003 3639 if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) { 3640 dollar_vcol = curwin->w_virtcol; 3641 } 3642 3643 // When deleting a char the cursor line must never be in a closed fold. 3644 // E.g., when 'foldmethod' is indent and deleting the first non-white 3645 // char before a Tab. 3646 if (did_backspace) { 3647 foldOpenCursor(); 3648 } 3649 return did_backspace; 3650 } 3651 3652 static void ins_left(void) 3653 { 3654 const bool end_change = dont_sync_undo == kFalse; // end undoable change 3655 3656 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3657 foldOpenCursor(); 3658 } 3659 undisplay_dollar(); 3660 pos_T tpos = curwin->w_cursor; 3661 if (oneleft() == OK) { 3662 start_arrow_with_change(&tpos, end_change); 3663 if (!end_change) { 3664 AppendCharToRedobuff(K_LEFT); 3665 } 3666 // If exit reversed string, position is fixed 3667 if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) { 3668 revins_legal++; 3669 } 3670 revins_chars++; 3671 } else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) { 3672 // if 'whichwrap' set for cursor in insert mode may go to previous line. 3673 // always break undo when moving upwards/downwards, else undo may break 3674 start_arrow(&tpos); 3675 curwin->w_cursor.lnum--; 3676 coladvance(curwin, MAXCOL); 3677 curwin->w_set_curswant = true; // so we stay at the end 3678 } else { 3679 vim_beep(kOptBoFlagCursor); 3680 } 3681 dont_sync_undo = kFalse; 3682 } 3683 3684 static void ins_home(int c) 3685 { 3686 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3687 foldOpenCursor(); 3688 } 3689 undisplay_dollar(); 3690 pos_T tpos = curwin->w_cursor; 3691 if (c == K_C_HOME) { 3692 curwin->w_cursor.lnum = 1; 3693 } 3694 curwin->w_cursor.col = 0; 3695 curwin->w_cursor.coladd = 0; 3696 curwin->w_curswant = 0; 3697 start_arrow(&tpos); 3698 } 3699 3700 static void ins_end(int c) 3701 { 3702 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3703 foldOpenCursor(); 3704 } 3705 undisplay_dollar(); 3706 pos_T tpos = curwin->w_cursor; 3707 if (c == K_C_END) { 3708 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; 3709 } 3710 coladvance(curwin, MAXCOL); 3711 curwin->w_curswant = MAXCOL; 3712 3713 start_arrow(&tpos); 3714 } 3715 3716 static void ins_s_left(void) 3717 { 3718 const bool end_change = dont_sync_undo == kFalse; // end undoable change 3719 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3720 foldOpenCursor(); 3721 } 3722 undisplay_dollar(); 3723 if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0) { 3724 start_arrow_with_change(&curwin->w_cursor, end_change); 3725 if (!end_change) { 3726 AppendCharToRedobuff(K_S_LEFT); 3727 } 3728 bck_word(1, false, false); 3729 curwin->w_set_curswant = true; 3730 } else { 3731 vim_beep(kOptBoFlagCursor); 3732 } 3733 dont_sync_undo = kFalse; 3734 } 3735 3736 /// @param end_change end undoable change 3737 static void ins_right(void) 3738 { 3739 const bool end_change = dont_sync_undo == kFalse; // end undoable change 3740 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3741 foldOpenCursor(); 3742 } 3743 undisplay_dollar(); 3744 if (gchar_cursor() != NUL || virtual_active(curwin)) { 3745 start_arrow_with_change(&curwin->w_cursor, end_change); 3746 if (!end_change) { 3747 AppendCharToRedobuff(K_RIGHT); 3748 } 3749 curwin->w_set_curswant = true; 3750 if (virtual_active(curwin)) { 3751 oneright(); 3752 } else { 3753 curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); 3754 } 3755 3756 revins_legal++; 3757 if (revins_chars) { 3758 revins_chars--; 3759 } 3760 } else if (vim_strchr(p_ww, ']') != NULL 3761 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { 3762 // if 'whichwrap' set for cursor in insert mode, may move the 3763 // cursor to the next line 3764 start_arrow(&curwin->w_cursor); 3765 curwin->w_set_curswant = true; 3766 curwin->w_cursor.lnum++; 3767 curwin->w_cursor.col = 0; 3768 } else { 3769 vim_beep(kOptBoFlagCursor); 3770 } 3771 dont_sync_undo = kFalse; 3772 } 3773 3774 static void ins_s_right(void) 3775 { 3776 const bool end_change = dont_sync_undo == kFalse; // end undoable change 3777 if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) { 3778 foldOpenCursor(); 3779 } 3780 undisplay_dollar(); 3781 if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count 3782 || gchar_cursor() != NUL) { 3783 start_arrow_with_change(&curwin->w_cursor, end_change); 3784 if (!end_change) { 3785 AppendCharToRedobuff(K_S_RIGHT); 3786 } 3787 fwd_word(1, false, 0); 3788 curwin->w_set_curswant = true; 3789 } else { 3790 vim_beep(kOptBoFlagCursor); 3791 } 3792 dont_sync_undo = kFalse; 3793 } 3794 3795 /// @param startcol when true move to Insstart.col 3796 static void ins_up(bool startcol) 3797 { 3798 linenr_T old_topline = curwin->w_topline; 3799 int old_topfill = curwin->w_topfill; 3800 3801 undisplay_dollar(); 3802 pos_T tpos = curwin->w_cursor; 3803 if (cursor_up(1, true) == OK) { 3804 if (startcol) { 3805 coladvance(curwin, getvcol_nolist(&Insstart)); 3806 } 3807 if (old_topline != curwin->w_topline 3808 || old_topfill != curwin->w_topfill) { 3809 redraw_later(curwin, UPD_VALID); 3810 } 3811 start_arrow(&tpos); 3812 can_cindent = true; 3813 } else { 3814 vim_beep(kOptBoFlagCursor); 3815 } 3816 } 3817 3818 static void ins_pageup(void) 3819 { 3820 undisplay_dollar(); 3821 3822 if (mod_mask & MOD_MASK_CTRL) { 3823 // <C-PageUp>: tab page back 3824 if (first_tabpage->tp_next != NULL) { 3825 start_arrow(&curwin->w_cursor); 3826 goto_tabpage(-1); 3827 } 3828 return; 3829 } 3830 3831 pos_T tpos = curwin->w_cursor; 3832 if (pagescroll(BACKWARD, 1, false) == OK) { 3833 start_arrow(&tpos); 3834 can_cindent = true; 3835 } else { 3836 vim_beep(kOptBoFlagCursor); 3837 } 3838 } 3839 3840 /// @param startcol when true move to Insstart.col 3841 static void ins_down(bool startcol) 3842 { 3843 linenr_T old_topline = curwin->w_topline; 3844 int old_topfill = curwin->w_topfill; 3845 3846 undisplay_dollar(); 3847 pos_T tpos = curwin->w_cursor; 3848 if (cursor_down(1, true) == OK) { 3849 if (startcol) { 3850 coladvance(curwin, getvcol_nolist(&Insstart)); 3851 } 3852 if (old_topline != curwin->w_topline 3853 || old_topfill != curwin->w_topfill) { 3854 redraw_later(curwin, UPD_VALID); 3855 } 3856 start_arrow(&tpos); 3857 can_cindent = true; 3858 } else { 3859 vim_beep(kOptBoFlagCursor); 3860 } 3861 } 3862 3863 static void ins_pagedown(void) 3864 { 3865 undisplay_dollar(); 3866 3867 if (mod_mask & MOD_MASK_CTRL) { 3868 // <C-PageDown>: tab page forward 3869 if (first_tabpage->tp_next != NULL) { 3870 start_arrow(&curwin->w_cursor); 3871 goto_tabpage(0); 3872 } 3873 return; 3874 } 3875 3876 pos_T tpos = curwin->w_cursor; 3877 if (pagescroll(FORWARD, 1, false) == OK) { 3878 start_arrow(&tpos); 3879 can_cindent = true; 3880 } else { 3881 vim_beep(kOptBoFlagCursor); 3882 } 3883 } 3884 3885 /// Handle TAB in Insert or Replace mode. 3886 /// 3887 /// @return true when the TAB needs to be inserted like a normal character. 3888 static bool ins_tab(void) 3889 FUNC_ATTR_WARN_UNUSED_RESULT 3890 { 3891 int temp; 3892 3893 if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) { 3894 Insstart_blank_vcol = get_nolist_virtcol(); 3895 } 3896 if (echeck_abbr(TAB + ABBR_OFF)) { 3897 return false; 3898 } 3899 3900 bool ind = inindent(0); 3901 if (ind) { 3902 can_cindent = false; 3903 } 3904 3905 // When nothing special, insert TAB like a normal character. 3906 if (!curbuf->b_p_et 3907 && !( 3908 p_sta 3909 && ind 3910 // These five lines mean 'tabstop' != 'shiftwidth' 3911 && ((tabstop_count(curbuf->b_p_vts_array) > 1) 3912 || (tabstop_count(curbuf->b_p_vts_array) == 1 3913 && tabstop_first(curbuf->b_p_vts_array) 3914 != get_sw_value(curbuf)) 3915 || (tabstop_count(curbuf->b_p_vts_array) == 0 3916 && curbuf->b_p_ts != get_sw_value(curbuf)))) 3917 && tabstop_count(curbuf->b_p_vsts_array) == 0 && get_sts_value() == 0) { 3918 return true; 3919 } 3920 3921 if (stop_arrow() == FAIL) { 3922 return true; 3923 } 3924 3925 did_ai = false; 3926 did_si = false; 3927 can_si = false; 3928 can_si_back = false; 3929 AppendToRedobuff("\t"); 3930 3931 if (p_sta && ind) { // insert tab in indent, use 'shiftwidth' 3932 temp = get_sw_value(curbuf); 3933 temp -= get_nolist_virtcol() % temp; 3934 } else if (tabstop_count(curbuf->b_p_vsts_array) > 0 3935 || curbuf->b_p_sts != 0) { 3936 // use 'softtabstop' when set 3937 temp = tabstop_padding(get_nolist_virtcol(), 3938 get_sts_value(), 3939 curbuf->b_p_vsts_array); 3940 } else { 3941 // otherwise use 'tabstop' 3942 temp = tabstop_padding(get_nolist_virtcol(), 3943 curbuf->b_p_ts, 3944 curbuf->b_p_vts_array); 3945 } 3946 3947 // Insert the first space with ins_char(). It will delete one char in 3948 // replace mode. Insert the rest with ins_str(); it will not delete any 3949 // chars. For MODE_VREPLACE state, we use ins_char() for all characters. 3950 ins_char(' '); 3951 while (--temp > 0) { 3952 if (State & VREPLACE_FLAG) { 3953 ins_char(' '); 3954 } else { 3955 ins_str(S_LEN(" ")); 3956 if (State & REPLACE_FLAG) { // no char replaced 3957 replace_push_nul(); 3958 } 3959 } 3960 } 3961 3962 // When 'expandtab' not set: Replace spaces by TABs where possible. 3963 if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0 3964 || get_sts_value() > 0 3965 || (p_sta && ind))) { 3966 char *ptr; 3967 char *saved_line = NULL; // init for GCC 3968 pos_T pos; 3969 pos_T *cursor; 3970 colnr_T want_vcol, vcol; 3971 int change_col = -1; 3972 int save_list = curwin->w_p_list; 3973 3974 // Get the current line. For MODE_VREPLACE state, don't make real 3975 // changes yet, just work on a copy of the line. 3976 if (State & VREPLACE_FLAG) { 3977 pos = curwin->w_cursor; 3978 cursor = &pos; 3979 saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); 3980 ptr = saved_line + pos.col; 3981 } else { 3982 ptr = get_cursor_pos_ptr(); 3983 cursor = &curwin->w_cursor; 3984 } 3985 3986 // When 'L' is not in 'cpoptions' a tab always takes up 'ts' spaces. 3987 if (vim_strchr(p_cpo, CPO_LISTWM) == NULL) { 3988 curwin->w_p_list = false; 3989 } 3990 3991 // Find first white before the cursor 3992 pos_T fpos = curwin->w_cursor; 3993 while (fpos.col > 0 && ascii_iswhite(ptr[-1])) { 3994 fpos.col--; 3995 ptr--; 3996 } 3997 3998 // In Replace mode, don't change characters before the insert point. 3999 if ((State & REPLACE_FLAG) 4000 && fpos.lnum == Insstart.lnum 4001 && fpos.col < Insstart.col) { 4002 ptr += Insstart.col - fpos.col; 4003 fpos.col = Insstart.col; 4004 } 4005 4006 // compute virtual column numbers of first white and cursor 4007 getvcol(curwin, &fpos, &vcol, NULL, NULL); 4008 getvcol(curwin, cursor, &want_vcol, NULL, NULL); 4009 4010 char *tab = "\t"; 4011 int32_t tab_v = (uint8_t)(*tab); 4012 4013 CharsizeArg csarg; 4014 CSType cstype = init_charsize_arg(&csarg, curwin, 0, tab); 4015 4016 // Use as many TABs as possible. Beware of 'breakindent', 'showbreak' 4017 // and 'linebreak' adding extra virtual columns. 4018 while (ascii_iswhite(*ptr)) { 4019 int i = win_charsize(cstype, vcol, tab, tab_v, &csarg).width; 4020 if (vcol + i > want_vcol) { 4021 break; 4022 } 4023 if (*ptr != TAB) { 4024 *ptr = TAB; 4025 if (change_col < 0) { 4026 change_col = fpos.col; // Column of first change 4027 // May have to adjust Insstart 4028 if (fpos.lnum == Insstart.lnum && fpos.col < Insstart.col) { 4029 Insstart.col = fpos.col; 4030 } 4031 } 4032 } 4033 fpos.col++; 4034 ptr++; 4035 vcol += i; 4036 } 4037 4038 if (change_col >= 0) { 4039 int repl_off = 0; 4040 // Skip over the spaces we need. 4041 cstype = init_charsize_arg(&csarg, curwin, 0, ptr); 4042 while (vcol < want_vcol && *ptr == ' ') { 4043 vcol += win_charsize(cstype, vcol, ptr, (uint8_t)(' '), &csarg).width; 4044 ptr++; 4045 repl_off++; 4046 } 4047 4048 if (vcol > want_vcol) { 4049 // Must have a char with 'showbreak' just before it. 4050 ptr--; 4051 repl_off--; 4052 } 4053 fpos.col += repl_off; 4054 4055 // Delete following spaces. 4056 int i = cursor->col - fpos.col; 4057 if (i > 0) { 4058 if (!(State & VREPLACE_FLAG)) { 4059 const colnr_T newp_len = curbuf->b_ml.ml_line_textlen - i; 4060 char *newp = xmalloc((size_t)newp_len); 4061 ptrdiff_t col = ptr - curbuf->b_ml.ml_line_ptr; 4062 if (col > 0) { 4063 memmove(newp, ptr - col, (size_t)col); 4064 } 4065 memmove(newp + col, ptr + i, (size_t)(newp_len - col)); 4066 if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { 4067 xfree(curbuf->b_ml.ml_line_ptr); 4068 } 4069 curbuf->b_ml.ml_line_ptr = newp; 4070 curbuf->b_ml.ml_line_textlen = newp_len; 4071 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; 4072 inserted_bytes(fpos.lnum, change_col, 4073 cursor->col - change_col, fpos.col - change_col); 4074 } else { 4075 STRMOVE(ptr, ptr + i); 4076 } 4077 // correct replace stack. 4078 if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { 4079 for (temp = i; --temp >= 0;) { 4080 replace_join(repl_off); 4081 } 4082 } 4083 } 4084 cursor->col -= i; 4085 4086 // In MODE_VREPLACE state, we haven't changed anything yet. Do it 4087 // now by backspacing over the changed spacing and then inserting 4088 // the new spacing. 4089 if (State & VREPLACE_FLAG) { 4090 // Backspace from real cursor to change_col 4091 backspace_until_column(change_col); 4092 4093 // Insert each char in saved_line from changed_col to 4094 // ptr-cursor 4095 ins_bytes_len(saved_line + change_col, (size_t)(cursor->col - change_col)); 4096 } 4097 } 4098 4099 if (State & VREPLACE_FLAG) { 4100 xfree(saved_line); 4101 } 4102 curwin->w_p_list = save_list; 4103 } 4104 4105 return false; 4106 } 4107 4108 /// Handle CR or NL in insert mode. 4109 /// 4110 /// @return false when it can't undo. 4111 bool ins_eol(int c) 4112 { 4113 if (echeck_abbr(c + ABBR_OFF)) { 4114 return true; 4115 } 4116 if (stop_arrow() == FAIL) { 4117 return false; 4118 } 4119 undisplay_dollar(); 4120 4121 // Strange Vi behaviour: In Replace mode, typing a NL will not delete the 4122 // character under the cursor. Only push a NUL on the replace stack, 4123 // nothing to put back when the NL is deleted. 4124 if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { 4125 replace_push_nul(); 4126 } 4127 4128 // In MODE_VREPLACE state, a NL replaces the rest of the line, and starts 4129 // replacing the next line, so we push all of the characters left on the 4130 // line onto the replace stack. This is not done here though, it is done 4131 // in open_line(). 4132 4133 // Put cursor on NUL if on the last char and coladd is 1 (happens after 4134 // CTRL-O). 4135 if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { 4136 coladvance(curwin, getviscol()); 4137 } 4138 4139 // NL in reverse insert will always start in the end of current line. 4140 if (revins_on) { 4141 curwin->w_cursor.col += get_cursor_pos_len(); 4142 } 4143 4144 AppendToRedobuff(NL_STR); 4145 bool i = open_line(FORWARD, 4146 has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, 4147 old_indent, NULL); 4148 old_indent = 0; 4149 can_cindent = true; 4150 // When inserting a line the cursor line must never be in a closed fold. 4151 foldOpenCursor(); 4152 4153 return i; 4154 } 4155 4156 // Handle digraph in insert mode. 4157 // Returns character still to be inserted, or NUL when nothing remaining to be 4158 // done. 4159 static int ins_digraph(void) 4160 { 4161 bool did_putchar = false; 4162 4163 pc_status = PC_STATUS_UNSET; 4164 if (redrawing() && !char_avail()) { 4165 // may need to redraw when no more chars available now 4166 ins_redraw(false); 4167 4168 edit_putchar('?', true); 4169 did_putchar = true; 4170 add_to_showcmd_c(Ctrl_K); 4171 } 4172 4173 // don't map the digraph chars. This also prevents the 4174 // mode message to be deleted when ESC is hit 4175 no_mapping++; 4176 allow_keys++; 4177 int c = plain_vgetc(); 4178 no_mapping--; 4179 allow_keys--; 4180 if (did_putchar) { 4181 // when the line fits in 'columns' the '?' is at the start of the next 4182 // line and will not be removed by the redraw 4183 edit_unputchar(); 4184 } 4185 4186 if (IS_SPECIAL(c) || mod_mask) { // special key 4187 clear_showcmd(); 4188 insert_special(c, true, false); 4189 return NUL; 4190 } 4191 if (c != ESC) { 4192 did_putchar = false; 4193 if (redrawing() && !char_avail()) { 4194 // may need to redraw when no more chars available now 4195 ins_redraw(false); 4196 4197 if (char2cells(c) == 1) { 4198 ins_redraw(false); 4199 edit_putchar(c, true); 4200 did_putchar = true; 4201 } 4202 add_to_showcmd_c(c); 4203 } 4204 no_mapping++; 4205 allow_keys++; 4206 int cc = plain_vgetc(); 4207 no_mapping--; 4208 allow_keys--; 4209 if (did_putchar) { 4210 // when the line fits in 'columns' the '?' is at the start of the 4211 // next line and will not be removed by a redraw 4212 edit_unputchar(); 4213 } 4214 if (cc != ESC) { 4215 AppendToRedobuff(CTRL_V_STR); 4216 c = digraph_get(c, cc, true); 4217 clear_showcmd(); 4218 return c; 4219 } 4220 } 4221 clear_showcmd(); 4222 return NUL; 4223 } 4224 4225 // Handle CTRL-E and CTRL-Y in Insert mode: copy char from other line. 4226 // Returns the char to be inserted, or NUL if none found. 4227 int ins_copychar(linenr_T lnum) 4228 { 4229 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { 4230 vim_beep(kOptBoFlagCopy); 4231 return NUL; 4232 } 4233 4234 // try to advance to the cursor column 4235 validate_virtcol(curwin); 4236 int const end_vcol = curwin->w_virtcol; 4237 char *line = ml_get(lnum); 4238 4239 CharsizeArg csarg; 4240 CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line); 4241 StrCharInfo ci = utf_ptr2StrCharInfo(line); 4242 int vcol = 0; 4243 while (vcol < end_vcol && *ci.ptr != NUL) { 4244 vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width; 4245 if (vcol > end_vcol) { 4246 break; 4247 } 4248 ci = utfc_next(ci); 4249 } 4250 4251 int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value; 4252 if (c == NUL) { 4253 vim_beep(kOptBoFlagCopy); 4254 } 4255 return c; 4256 } 4257 4258 // CTRL-Y or CTRL-E typed in Insert mode. 4259 static int ins_ctrl_ey(int tc) 4260 { 4261 int c = tc; 4262 4263 if (ctrl_x_mode_scroll()) { 4264 if (c == Ctrl_Y) { 4265 scrolldown_clamp(); 4266 } else { 4267 scrollup_clamp(); 4268 } 4269 redraw_later(curwin, UPD_VALID); 4270 } else { 4271 c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1)); 4272 if (c != NUL) { 4273 // The character must be taken literally, insert like it 4274 // was typed after a CTRL-V, and pretend 'textwidth' 4275 // wasn't set. Digits, 'o' and 'x' are special after a 4276 // CTRL-V, don't use it for these. 4277 if (c < 256 && !isalnum(c)) { 4278 AppendToRedobuff(CTRL_V_STR); 4279 } 4280 OptInt tw_save = curbuf->b_p_tw; 4281 curbuf->b_p_tw = -1; 4282 insert_special(c, true, false); 4283 curbuf->b_p_tw = tw_save; 4284 revins_chars++; 4285 revins_legal++; 4286 c = Ctrl_V; // pretend CTRL-V is last character 4287 auto_format(false, true); 4288 } 4289 } 4290 return c; 4291 } 4292 4293 // Get the value that w_virtcol would have when 'list' is off. 4294 // Unless 'cpo' contains the 'L' flag. 4295 colnr_T get_nolist_virtcol(void) 4296 { 4297 // check validity of cursor in current buffer 4298 if (curwin->w_buffer == NULL || curwin->w_buffer->b_ml.ml_mfp == NULL 4299 || curwin->w_cursor.lnum > curwin->w_buffer->b_ml.ml_line_count) { 4300 return 0; 4301 } 4302 if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { 4303 return getvcol_nolist(&curwin->w_cursor); 4304 } 4305 validate_virtcol(curwin); 4306 return curwin->w_virtcol; 4307 } 4308 4309 // Handle the InsertCharPre autocommand. 4310 // "c" is the character that was typed. 4311 // Return a pointer to allocated memory with the replacement string. 4312 // Return NULL to continue inserting "c". 4313 static char *do_insert_char_pre(int c) 4314 { 4315 char buf[MB_MAXBYTES + 1]; 4316 const int save_State = State; 4317 4318 if (c == Ctrl_RSB) { 4319 return NULL; 4320 } 4321 4322 // Return quickly when there is nothing to do. 4323 if (!has_event(EVENT_INSERTCHARPRE)) { 4324 return NULL; 4325 } 4326 size_t buflen = (size_t)utf_char2bytes(c, buf); 4327 buf[buflen] = NUL; 4328 4329 // Lock the text to avoid weird things from happening. 4330 textlock++; 4331 set_vim_var_string(VV_CHAR, buf, (ptrdiff_t)buflen); // set v:char 4332 4333 char *res = NULL; 4334 if (ins_apply_autocmds(EVENT_INSERTCHARPRE)) { 4335 // Get the value of v:char. It may be empty or more than one 4336 // character. Only use it when changed, otherwise continue with the 4337 // original character to avoid breaking autoindent. 4338 if (strcmp(buf, get_vim_var_str(VV_CHAR)) != 0) { 4339 res = xstrdup(get_vim_var_str(VV_CHAR)); 4340 } 4341 } 4342 4343 set_vim_var_string(VV_CHAR, NULL, -1); 4344 textlock--; 4345 4346 // Restore the State, it may have been changed. 4347 State = save_State; 4348 4349 return res; 4350 } 4351 4352 bool get_can_cindent(void) 4353 { 4354 return can_cindent; 4355 } 4356 4357 void set_can_cindent(bool val) 4358 { 4359 can_cindent = val; 4360 } 4361 4362 /// Trigger "event" and take care of fixing undo. 4363 int ins_apply_autocmds(event_T event) 4364 { 4365 varnumber_T tick = buf_get_changedtick(curbuf); 4366 4367 int r = apply_autocmds(event, NULL, NULL, false, curbuf); 4368 4369 // If u_savesub() was called then we are not prepared to start 4370 // a new line. Call u_save() with no contents to fix that. 4371 // Except when leaving Insert mode. 4372 if (event != EVENT_INSERTLEAVE && tick != buf_get_changedtick(curbuf)) { 4373 u_save(curwin->w_cursor.lnum, (linenr_T)(curwin->w_cursor.lnum + 1)); 4374 } 4375 4376 return r; 4377 }