change.c (75715B)
1 /// change.c: functions related to changing text 2 3 #include <assert.h> 4 #include <stdbool.h> 5 #include <stdint.h> 6 #include <string.h> 7 8 #include "nvim/ascii_defs.h" 9 #include "nvim/assert_defs.h" 10 #include "nvim/autocmd.h" 11 #include "nvim/autocmd_defs.h" 12 #include "nvim/buffer.h" 13 #include "nvim/buffer_defs.h" 14 #include "nvim/buffer_updates.h" 15 #include "nvim/change.h" 16 #include "nvim/charset.h" 17 #include "nvim/cursor.h" 18 #include "nvim/diff.h" 19 #include "nvim/drawscreen.h" 20 #include "nvim/edit.h" 21 #include "nvim/eval/vars.h" 22 #include "nvim/ex_cmds_defs.h" 23 #include "nvim/extmark.h" 24 #include "nvim/extmark_defs.h" 25 #include "nvim/fold.h" 26 #include "nvim/gettext_defs.h" 27 #include "nvim/globals.h" 28 #include "nvim/highlight_defs.h" 29 #include "nvim/indent.h" 30 #include "nvim/indent_c.h" 31 #include "nvim/insexpand.h" 32 #include "nvim/macros_defs.h" 33 #include "nvim/mark.h" 34 #include "nvim/mark_defs.h" 35 #include "nvim/marktree_defs.h" 36 #include "nvim/mbyte.h" 37 #include "nvim/mbyte_defs.h" 38 #include "nvim/memline.h" 39 #include "nvim/memline_defs.h" 40 #include "nvim/memory.h" 41 #include "nvim/message.h" 42 #include "nvim/move.h" 43 #include "nvim/option.h" 44 #include "nvim/option_vars.h" 45 #include "nvim/os/time.h" 46 #include "nvim/plines.h" 47 #include "nvim/pos_defs.h" 48 #include "nvim/search.h" 49 #include "nvim/spell.h" 50 #include "nvim/state.h" 51 #include "nvim/state_defs.h" 52 #include "nvim/strings.h" 53 #include "nvim/textformat.h" 54 #include "nvim/types_defs.h" 55 #include "nvim/ui.h" 56 #include "nvim/undo.h" 57 #include "nvim/vim_defs.h" 58 59 #include "change.c.generated.h" 60 61 /// If the file is readonly, give a warning message with the first change. 62 /// Don't do this for autocommands. 63 /// Doesn't use emsg(), because it flushes the macro buffer. 64 /// If we have undone all changes b_changed will be false, but "b_did_warn" 65 /// will be true. 66 /// "col" is the column for the message; non-zero when in insert mode and 67 /// 'showmode' is on. 68 /// Careful: may trigger autocommands that reload the buffer. 69 void change_warning(buf_T *buf, int col) 70 { 71 static const char *w_readonly = N_("W10: Warning: Changing a readonly file"); 72 73 if (buf->b_did_warn == false 74 && curbufIsChanged() == 0 75 && !autocmd_busy 76 && buf->b_p_ro) { 77 buf->b_ro_locked++; 78 apply_autocmds(EVENT_FILECHANGEDRO, NULL, NULL, false, buf); 79 buf->b_ro_locked--; 80 if (!buf->b_p_ro) { 81 return; 82 } 83 // Do what msg() does, but with a column offset if the warning should 84 // be after the mode message. 85 msg_start(); 86 if (msg_row == Rows - 1) { 87 msg_col = col; 88 } 89 msg_source(HLF_W); 90 msg_ext_set_kind("wmsg"); 91 msg_puts_hl(_(w_readonly), HLF_W, true); 92 set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); 93 msg_clr_eos(); 94 msg_end(); 95 if (msg_silent == 0 && !silent_mode && ui_active()) { 96 msg_delay(1002, true); // give the user time to think about it 97 } 98 buf->b_did_warn = true; 99 redraw_cmdline = false; // don't redraw and erase the message 100 if (msg_row < Rows - 1) { 101 showmode(); 102 } 103 } 104 } 105 106 /// Call this function when something in a buffer is changed. 107 /// 108 /// Most often called through changed_bytes() and changed_lines(), which also 109 /// mark the area of the display to be redrawn. 110 /// 111 /// Careful: may trigger autocommands that reload the buffer. 112 void changed(buf_T *buf) 113 { 114 if (!buf->b_changed) { 115 int save_msg_scroll = msg_scroll; 116 117 // Give a warning about changing a read-only file. This may also 118 // check-out the file, thus change "curbuf"! 119 change_warning(buf, 0); 120 121 // Create a swap file if that is wanted. 122 // Don't do this for "nofile" and "nowrite" buffer types. 123 if (buf->b_may_swap && !bt_dontwrite(buf)) { 124 bool save_need_wait_return = need_wait_return; 125 126 need_wait_return = false; 127 ml_open_file(buf); 128 129 // The ml_open_file() can cause an ATTENTION message. 130 // Wait two seconds, to make sure the user reads this unexpected 131 // message. Since we could be anywhere, call wait_return() now, 132 // and don't let the emsg() set msg_scroll. 133 if (need_wait_return && emsg_silent == 0 && !in_assert_fails && !ui_has(kUIMessages)) { 134 msg_delay(2002, true); 135 wait_return(true); 136 msg_scroll = save_msg_scroll; 137 } else { 138 need_wait_return = save_need_wait_return; 139 } 140 } 141 changed_internal(buf); 142 } 143 buf_inc_changedtick(buf); 144 145 // If a pattern is highlighted, the position may now be invalid. 146 highlight_match = false; 147 } 148 149 /// Internal part of changed(), no user interaction. 150 /// Also used for recovery. 151 void changed_internal(buf_T *buf) 152 { 153 buf->b_changed = true; 154 buf->b_changed_invalid = true; 155 ml_setflags(buf); 156 redraw_buf_status_later(buf); 157 redraw_tabline = true; 158 need_maketitle = true; // set window title later 159 } 160 161 /// Invalidate a window's w_valid flags and w_lines[] entries after changing lines. 162 static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col, linenr_T lnume, 163 linenr_T xtra) 164 { 165 // If the changed line is in a range of previously folded lines, 166 // compare with the first line in that range. 167 if (wp->w_cursor.lnum <= lnum) { 168 int i = find_wl_entry(wp, lnum); 169 if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) { 170 changed_line_abv_curs_win(wp); 171 } 172 } 173 174 if (wp->w_cursor.lnum > lnum) { 175 changed_line_abv_curs_win(wp); 176 } else if (wp->w_cursor.lnum == lnum && wp->w_cursor.col >= col) { 177 changed_cline_bef_curs(wp); 178 } 179 if (wp->w_botline >= lnum) { 180 if (xtra < 0) { 181 invalidate_botline_win(wp); 182 } else { 183 // Assume that botline doesn't change (inserted lines make 184 // other lines scroll down below botline). 185 approximate_botline_win(wp); 186 } 187 } 188 189 // If lines have been inserted/deleted and the buffer has virt_lines, or 190 // inline virt_text with 'wrap' enabled, invalidate the line after the changed 191 // lines. virt_lines may now be drawn above that line, and inline virt_text 192 // may cause that line to wrap. 193 if ((xtra < 0 && wp->w_p_wrap && buf_meta_total(wp->w_buffer, kMTMetaInline)) 194 || (xtra != 0 && buf_meta_total(wp->w_buffer, kMTMetaLines))) { 195 lnume++; 196 } 197 // Check if any w_lines[] entries have become invalid. 198 // For entries below the change: Correct the lnums for inserted/deleted lines. 199 // Makes it possible to stop displaying after the change. 200 for (int i = 0; i < wp->w_lines_valid; i++) { 201 if (wp->w_lines[i].wl_valid) { 202 if (wp->w_lines[i].wl_lnum >= lnum) { 203 // Do not change wl_lnum at index zero, it is used to compare with w_topline. 204 // Invalidate it instead. 205 if (i == 0 || wp->w_lines[i].wl_lnum < lnume) { 206 // line included in change 207 wp->w_lines[i].wl_valid = false; 208 } else if (xtra != 0) { 209 // line below change 210 wp->w_lines[i].wl_lnum += xtra; 211 wp->w_lines[i].wl_foldend += xtra; 212 wp->w_lines[i].wl_lastlnum += xtra; 213 } 214 } else if (wp->w_lines[i].wl_lastlnum >= lnum) { 215 // change somewhere inside this range of folded or concealed lines, 216 // may need to be redrawn 217 wp->w_lines[i].wl_valid = false; 218 } 219 } 220 } 221 } 222 223 /// Line changed_lines_invalidate_win(), but for all windows displaying a buffer. 224 void changed_lines_invalidate_buf(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, 225 linenr_T xtra) 226 { 227 FOR_ALL_TAB_WINDOWS(tp, wp) { 228 if (wp->w_buffer == buf) { 229 changed_lines_invalidate_win(wp, lnum, col, lnume, xtra); 230 } 231 } 232 } 233 234 /// Common code for when a change was made. 235 /// See changed_lines() for the arguments. 236 /// Careful: may trigger autocommands that reload the buffer. 237 static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra) 238 { 239 // mark the buffer as modified 240 changed(buf); 241 242 FOR_ALL_WINDOWS_IN_TAB(win, curtab) { 243 if (win->w_buffer == buf && win->w_p_diff && diff_internal()) { 244 curtab->tp_diff_update = true; 245 diff_update_line(lnum); 246 } 247 } 248 249 // set the '. mark 250 if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) { 251 fmarkv_T view = INIT_FMARKV; 252 // Set the markview only if lnum is visible, as changes might be done 253 // outside of the current window view. 254 255 if (curwin->w_buffer == buf) { 256 if (lnum >= curwin->w_topline && lnum <= curwin->w_botline) { 257 view = mark_view_make(curwin->w_topline, curwin->w_cursor); 258 } 259 } 260 RESET_FMARK(&buf->b_last_change, ((pos_T) { lnum, col, 0 }), buf->handle, view); 261 262 // Create a new entry if a new undo-able change was started or we 263 // don't have an entry yet. 264 if (buf->b_new_change || buf->b_changelistlen == 0) { 265 bool add; 266 if (buf->b_changelistlen == 0) { 267 add = true; 268 } else { 269 // Don't create a new entry when the line number is the same 270 // as the last one and the column is not too far away. Avoids 271 // creating many entries for typing "xxxxx". 272 pos_T *p = &buf->b_changelist[buf->b_changelistlen - 1].mark; 273 if (p->lnum != lnum) { 274 add = true; 275 } else { 276 int cols = comp_textwidth(false); 277 if (cols == 0) { 278 cols = 79; 279 } 280 add = (p->col + cols < col || col + cols < p->col); 281 } 282 } 283 if (add) { 284 // This is the first of a new sequence of undo-able changes 285 // and it's at some distance of the last change. Use a new 286 // position in the changelist. 287 buf->b_new_change = false; 288 289 if (buf->b_changelistlen == JUMPLISTSIZE) { 290 // changelist is full: remove oldest entry 291 buf->b_changelistlen = JUMPLISTSIZE - 1; 292 memmove(buf->b_changelist, buf->b_changelist + 1, 293 sizeof(buf->b_changelist[0]) * (JUMPLISTSIZE - 1)); 294 FOR_ALL_TAB_WINDOWS(tp, wp) { 295 // Correct position in changelist for other windows on 296 // this buffer. 297 if (wp->w_buffer == buf && wp->w_changelistidx > 0) { 298 wp->w_changelistidx--; 299 } 300 } 301 } 302 FOR_ALL_TAB_WINDOWS(tp, wp) { 303 // For other windows, if the position in the changelist is 304 // at the end it stays at the end. 305 if (wp->w_buffer == buf 306 && wp->w_changelistidx == buf->b_changelistlen) { 307 wp->w_changelistidx++; 308 } 309 } 310 buf->b_changelistlen++; 311 } 312 } 313 buf->b_changelist[buf->b_changelistlen - 1] = 314 buf->b_last_change; 315 // The current window is always after the last change, so that "g," 316 // takes you back to it. 317 if (curwin->w_buffer == buf) { 318 curwin->w_changelistidx = buf->b_changelistlen; 319 } 320 } 321 322 if (curwin->w_buffer == buf && VIsual_active) { 323 check_visual_pos(); 324 } 325 326 FOR_ALL_TAB_WINDOWS(tp, wp) { 327 if (wp->w_buffer == buf) { 328 // Mark this window to be redrawn later. 329 if (!redraw_not_allowed && wp->w_redr_type < UPD_VALID) { 330 wp->w_redr_type = UPD_VALID; 331 } 332 333 // When inserting/deleting lines and the window has specific lines 334 // to be redrawn, w_redraw_top and w_redraw_bot may now be invalid, 335 // so just redraw everything. 336 if (xtra != 0 && wp->w_redraw_top != 0) { 337 redraw_later(wp, UPD_NOT_VALID); 338 } 339 340 linenr_T last = lnume + xtra - 1; // last line after the change 341 342 // Reset "w_skipcol" if the topline length has become smaller to 343 // such a degree that nothing will be visible anymore, accounting 344 // for 'smoothscroll' <<< or 'listchars' "precedes" marker. 345 if (wp->w_skipcol > 0 346 && (last < wp->w_topline 347 || (wp->w_topline >= lnum 348 && wp->w_topline < lnume 349 && (linetabsize_eol(wp, wp->w_topline) 350 <= wp->w_skipcol + sms_marker_overlap(wp, -1))))) { 351 wp->w_skipcol = 0; 352 } 353 354 // Check if a change in the buffer has invalidated the cached 355 // values for the cursor. 356 // Update the folds for this window. Can't postpone this, because 357 // a following operator might work on the whole fold: ">>dd". 358 foldUpdate(wp, lnum, last); 359 360 // The change may cause lines above or below the change to become 361 // included in a fold. Set lnum/lnume to the first/last line that 362 // might be displayed differently. 363 // Set w_cline_folded here as an efficient way to update it when 364 // inserting lines just above a closed fold. 365 bool folded = hasFoldingWin(wp, lnum, &lnum, NULL, false, NULL); 366 if (wp->w_cursor.lnum == lnum) { 367 wp->w_cline_folded = folded; 368 } 369 folded = hasFoldingWin(wp, last, NULL, &last, false, NULL); 370 if (wp->w_cursor.lnum == last) { 371 wp->w_cline_folded = folded; 372 } 373 374 changed_lines_invalidate_win(wp, lnum, col, lnume, xtra); 375 376 // Take care of side effects for setting w_topline when folds have 377 // changed. Esp. when the buffer was changed in another window. 378 if (hasAnyFolding(wp)) { 379 set_topline(wp, wp->w_topline); 380 } 381 382 // If lines have been added or removed, relative numbering always 383 // requires an update even if cursor didn't move. 384 if (wp->w_p_rnu && xtra != 0) { 385 wp->w_last_cursor_lnum_rnu = 0; 386 } 387 388 if (wp->w_p_cul && wp->w_last_cursorline >= lnum) { 389 if (wp->w_last_cursorline < lnume) { 390 // If 'cursorline' was inside the change, it has already 391 // been invalidated in w_lines[] by the loop above. 392 wp->w_last_cursorline = 0; 393 } else { 394 // If 'cursorline' was below the change, adjust its lnum. 395 wp->w_last_cursorline += xtra; 396 } 397 } 398 } 399 400 if (wp == curwin && xtra != 0 && search_hl_has_cursor_lnum >= lnum) { 401 search_hl_has_cursor_lnum += xtra; 402 } 403 } 404 405 // Call update_screen() later, which checks out what needs to be redrawn, 406 // since it notices b_mod_set and then uses b_mod_*. 407 set_must_redraw(UPD_VALID); 408 409 // when the cursor line is changed always trigger CursorMoved 410 if (last_cursormoved_win == curwin && curwin->w_buffer == buf 411 && lnum <= curwin->w_cursor.lnum 412 && lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum) { 413 last_cursormoved.lnum = 0; 414 } 415 } 416 417 /// Changed bytes within a single line for the current buffer. 418 /// - marks the windows on this buffer to be redisplayed 419 /// - marks the buffer changed by calling changed() 420 /// - invalidates cached values 421 /// Careful: may trigger autocommands that reload the buffer. 422 void changed_bytes(linenr_T lnum, colnr_T col) 423 { 424 changed_lines_redraw_buf(curbuf, lnum, lnum + 1, 0); 425 changed_common(curbuf, lnum, col, lnum + 1, 0); 426 // When text has been changed at the end of the line, possibly the start of 427 // the next line may have SpellCap that should be removed or it needs to be 428 // displayed. Schedule the next line for redrawing just in case. 429 // Don't do this when displaying '$' at the end of changed text. 430 if (spell_check_window(curwin) 431 && lnum < curbuf->b_ml.ml_line_count 432 && vim_strchr(p_cpo, CPO_DOLLAR) == NULL) { 433 redrawWinline(curwin, lnum + 1); 434 } 435 // notify any channels that are watching 436 buf_updates_send_changes(curbuf, lnum, 1, 1); 437 438 // Diff highlighting in other diff windows may need to be updated too. 439 if (curwin->w_p_diff) { 440 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 441 if (wp->w_p_diff && wp != curwin) { 442 redraw_later(wp, UPD_VALID); 443 linenr_T wlnum = diff_lnum_win(lnum, wp); 444 if (wlnum > 0) { 445 changed_lines_redraw_buf(wp->w_buffer, wlnum, wlnum + 1, 0); 446 } 447 } 448 } 449 } 450 } 451 452 /// insert/delete bytes at column 453 /// 454 /// Like changed_bytes() but also adjust extmark for "new" bytes. 455 void inserted_bytes(linenr_T lnum, colnr_T start_col, int old_col, int new_col) 456 { 457 if (curbuf_splice_pending == 0) { 458 extmark_splice_cols(curbuf, (int)lnum - 1, start_col, old_col, new_col, kExtmarkUndo); 459 } 460 461 changed_bytes(lnum, start_col); 462 } 463 464 /// Appended "count" lines below line "lnum" in the given buffer. 465 /// Must be called AFTER the change and after mark_adjust(). 466 /// Takes care of marking the buffer to be redrawn and sets the changed flag. 467 void appended_lines_buf(buf_T *buf, linenr_T lnum, linenr_T count) 468 { 469 changed_lines(buf, lnum + 1, 0, lnum + 1, count, true); 470 } 471 472 /// Appended "count" lines below line "lnum" in the current buffer. 473 /// Must be called AFTER the change and after mark_adjust(). 474 /// Takes care of marking the buffer to be redrawn and sets the changed flag. 475 void appended_lines(linenr_T lnum, linenr_T count) 476 { 477 appended_lines_buf(curbuf, lnum, count); 478 } 479 480 /// Like appended_lines(), but adjust marks first. 481 void appended_lines_mark(linenr_T lnum, int count) 482 { 483 mark_adjust(lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0, kExtmarkUndo); 484 changed_lines(curbuf, lnum + 1, 0, lnum + 1, (linenr_T)count, true); 485 } 486 487 /// Deleted "count" lines at line "lnum" in the given buffer. 488 /// Must be called AFTER the change and after mark_adjust(). 489 /// Takes care of marking the buffer to be redrawn and sets the changed flag. 490 void deleted_lines_buf(buf_T *buf, linenr_T lnum, linenr_T count) 491 { 492 changed_lines(buf, lnum, 0, lnum + count, -count, true); 493 } 494 495 /// Deleted "count" lines at line "lnum" in the current buffer. 496 /// Must be called AFTER the change and after mark_adjust(). 497 /// Takes care of marking the buffer to be redrawn and sets the changed flag. 498 void deleted_lines(linenr_T lnum, linenr_T count) 499 { 500 deleted_lines_buf(curbuf, lnum, count); 501 } 502 503 /// Like deleted_lines(), but adjust marks first. 504 /// Make sure the cursor is on a valid line before calling, a GUI callback may 505 /// be triggered to display the cursor. 506 void deleted_lines_mark(linenr_T lnum, int count) 507 { 508 bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY; 509 510 mark_adjust(lnum, (linenr_T)(lnum + count - 1), MAXLNUM, -(linenr_T)count, kExtmarkNOOP); 511 // if we deleted the entire buffer, we need to implicitly add a new empty line 512 extmark_adjust(curbuf, lnum, (linenr_T)(lnum + count - 1), MAXLNUM, 513 -(linenr_T)count + (made_empty ? 1 : 0), kExtmarkUndo); 514 changed_lines(curbuf, lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true); 515 } 516 517 /// Marks the area to be redrawn after a change. 518 /// Consider also calling changed_lines_invalidate_buf(). 519 /// 520 /// @param buf the buffer where lines were changed 521 /// @param lnum first line with change 522 /// @param lnume line below last changed line 523 /// @param xtra number of extra lines (negative when deleting) 524 void changed_lines_redraw_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra) 525 { 526 // If lines have been deleted and there may be decorations in the buffer, ensure 527 // win_update() calculates the height of, and redraws the line to which or whence 528 // from its mark may have moved. When lines are deleted, a virt_line mark may 529 // have moved be drawn two lines below so increase by one more. 530 if (xtra != 0 && buf->b_marktree->n_keys > 0) { 531 lnume += 1 + (xtra < 0 && buf_meta_total(buf, kMTMetaLines)); 532 } 533 534 if (buf->b_mod_set) { 535 // find the maximum area that must be redisplayed 536 buf->b_mod_top = MIN(buf->b_mod_top, lnum); 537 if (lnum < buf->b_mod_bot) { 538 // adjust old bot position for xtra lines 539 buf->b_mod_bot += xtra; 540 buf->b_mod_bot = MAX(buf->b_mod_bot, lnum); 541 } 542 buf->b_mod_bot = MAX(buf->b_mod_bot, lnume + xtra); 543 buf->b_mod_xlines += xtra; 544 } else { 545 // set the area that must be redisplayed 546 buf->b_mod_set = true; 547 buf->b_mod_top = lnum; 548 buf->b_mod_bot = lnume + xtra; 549 buf->b_mod_xlines = xtra; 550 } 551 } 552 553 /// Changed lines for a buffer. 554 /// Must be called AFTER the change and after mark_adjust(). 555 /// - mark the buffer changed by calling changed() 556 /// - mark the windows on this buffer to be redisplayed 557 /// - invalidate cached values 558 /// "lnum" is the first line that needs displaying, "lnume" the first line 559 /// below the changed lines (BEFORE the change). 560 /// When only inserting lines, "lnum" and "lnume" are equal. 561 /// Takes care of calling changed() and updating b_mod_*. 562 /// Careful: may trigger autocommands that reload the buffer. 563 /// 564 /// @param lnum first line with change 565 /// @param col column in first line with change 566 /// @param lnume line below last changed line 567 /// @param xtra number of extra lines (negative when deleting) 568 /// @param do_buf_event some callers like undo/redo call changed_lines() and 569 /// then increment changedtick *again*. This flag allows these callers to send 570 /// the nvim_buf_lines_event events after they're done modifying changedtick. 571 void changed_lines(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, 572 bool do_buf_event) 573 { 574 changed_lines_redraw_buf(buf, lnum, lnume, xtra); 575 576 if (xtra == 0 && curwin->w_p_diff && curwin->w_buffer == buf && !diff_internal()) { 577 // When the number of lines doesn't change then mark_adjust() isn't 578 // called and other diff buffers still need to be marked for 579 // displaying. 580 linenr_T wlnum; 581 582 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 583 if (wp->w_p_diff && wp != curwin) { 584 redraw_later(wp, UPD_VALID); 585 wlnum = diff_lnum_win(lnum, wp); 586 if (wlnum > 0) { 587 changed_lines_redraw_buf(wp->w_buffer, wlnum, lnume - lnum + wlnum, 0); 588 } 589 } 590 } 591 } 592 593 changed_common(buf, lnum, col, lnume, xtra); 594 595 if (do_buf_event) { 596 int64_t num_added = (int64_t)(lnume + xtra - lnum); 597 int64_t num_removed = lnume - lnum; 598 buf_updates_send_changes(buf, lnum, num_added, num_removed); 599 } 600 } 601 602 /// Called when the changed flag must be reset for buffer `buf`. 603 /// When `ff` is true also reset 'fileformat'. 604 /// When `always_inc_changedtick` is true b:changedtick is incremented even 605 /// when the changed flag was off. 606 void unchanged(buf_T *buf, bool ff, bool always_inc_changedtick) 607 { 608 if (buf->b_changed || (ff && file_ff_differs(buf, false))) { 609 buf->b_changed = false; 610 buf->b_changed_invalid = true; 611 ml_setflags(buf); 612 if (ff) { 613 save_file_ff(buf); 614 } 615 redraw_buf_status_later(buf); 616 redraw_tabline = true; 617 need_maketitle = true; // set window title later 618 buf_inc_changedtick(buf); 619 } else if (always_inc_changedtick) { 620 buf_inc_changedtick(buf); 621 } 622 } 623 624 /// Save the current values of 'fileformat' and 'fileencoding', so that we know 625 /// the file must be considered changed when the value is different. 626 void save_file_ff(buf_T *buf) 627 { 628 buf->b_start_ffc = (unsigned char)(*buf->b_p_ff); 629 buf->b_start_eof = buf->b_p_eof; 630 buf->b_start_eol = buf->b_p_eol; 631 buf->b_start_bomb = buf->b_p_bomb; 632 633 // Only use free/alloc when necessary, they take time. 634 if (buf->b_start_fenc == NULL 635 || strcmp(buf->b_start_fenc, buf->b_p_fenc) != 0) { 636 xfree(buf->b_start_fenc); 637 buf->b_start_fenc = xstrdup(buf->b_p_fenc); 638 } 639 } 640 641 /// Return true if 'fileformat' and/or 'fileencoding' has a different value 642 /// from when editing started (save_file_ff() called). 643 /// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was 644 /// changed and 'binary' is not set. 645 /// Also when 'endofline' was changed and 'fixeol' is not set. 646 /// When "ignore_empty" is true don't consider a new, empty buffer to be 647 /// changed. 648 bool file_ff_differs(buf_T *buf, bool ignore_empty) 649 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 650 { 651 // In a buffer that was never loaded the options are not valid. 652 if (buf->b_flags & BF_NEVERLOADED) { 653 return false; 654 } 655 if (ignore_empty 656 && (buf->b_flags & BF_NEW) 657 && buf->b_ml.ml_line_count == 1 658 && *ml_get_buf(buf, 1) == NUL) { 659 return false; 660 } 661 if (buf->b_start_ffc != *buf->b_p_ff) { 662 return true; 663 } 664 if ((buf->b_p_bin || !buf->b_p_fixeol) 665 && (buf->b_start_eof != buf->b_p_eof || buf->b_start_eol != buf->b_p_eol)) { 666 return true; 667 } 668 if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) { 669 return true; 670 } 671 if (buf->b_start_fenc == NULL) { 672 return *buf->b_p_fenc != NUL; 673 } 674 return strcmp(buf->b_start_fenc, buf->b_p_fenc) != 0; 675 } 676 677 /// Insert string "p" at the cursor position. Stops at a NUL byte. 678 /// Handles Replace mode and multi-byte characters. 679 void ins_bytes(char *p) 680 { 681 ins_bytes_len(p, strlen(p)); 682 } 683 684 /// Insert string "p" with length "len" at the cursor position. 685 /// Handles Replace mode and multi-byte characters. 686 void ins_bytes_len(char *p, size_t len) 687 { 688 size_t n; 689 for (size_t i = 0; i < len; i += n) { 690 // avoid reading past p[len] 691 n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i)); 692 ins_char_bytes(p + i, n); 693 } 694 } 695 696 /// Insert or replace a single character at the cursor position. 697 /// When in MODE_REPLACE or MODE_VREPLACE state, replace any existing character. 698 /// Caller must have prepared for undo. 699 /// For multi-byte characters we get the whole character, the caller must 700 /// convert bytes to a character. 701 void ins_char(int c) 702 { 703 char buf[MB_MAXCHAR + 1]; 704 size_t n = (size_t)utf_char2bytes(c, buf); 705 706 // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte. 707 // Happens for CTRL-Vu9900. 708 if (buf[0] == 0) { 709 buf[0] = '\n'; 710 } 711 ins_char_bytes(buf, n); 712 } 713 714 void ins_char_bytes(char *buf, size_t charlen) 715 { 716 // Break tabs if needed. 717 if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { 718 coladvance_force(getviscol()); 719 } 720 721 size_t col = (size_t)curwin->w_cursor.col; 722 linenr_T lnum = curwin->w_cursor.lnum; 723 char *oldp = ml_get(lnum); 724 size_t linelen = (size_t)ml_get_len(lnum) + 1; // length of old line including NUL 725 726 // The lengths default to the values for when not replacing. 727 size_t oldlen = 0; // nr of bytes inserted 728 size_t newlen = charlen; // nr of bytes deleted (0 when not replacing) 729 730 if (State & REPLACE_FLAG) { 731 if (State & VREPLACE_FLAG) { 732 // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag. 733 // Returns the old value of list, so when finished, 734 // curwin->w_p_list should be set back to this. 735 int old_list = curwin->w_p_list; 736 if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { 737 curwin->w_p_list = false; 738 } 739 // In virtual replace mode each character may replace one or more 740 // characters (zero if it's a TAB). Count the number of bytes to 741 // be deleted to make room for the new character, counting screen 742 // cells. May result in adding spaces to fill a gap. 743 colnr_T vcol; 744 getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL); 745 colnr_T new_vcol = vcol + win_chartabsize(curwin, buf, vcol); 746 while (oldp[col + oldlen] != NUL && vcol < new_vcol) { 747 vcol += win_chartabsize(curwin, oldp + col + oldlen, vcol); 748 // Don't need to remove a TAB that takes us to the right 749 // position. 750 if (vcol > new_vcol && oldp[col + oldlen] == TAB) { 751 break; 752 } 753 oldlen += (size_t)utfc_ptr2len(oldp + col + oldlen); 754 // Deleted a bit too much, insert spaces. 755 if (vcol > new_vcol) { 756 newlen += (size_t)(vcol - new_vcol); 757 } 758 } 759 curwin->w_p_list = old_list; 760 } else if (oldp[col] != NUL) { 761 // normal replace 762 oldlen = (size_t)utfc_ptr2len(oldp + col); 763 } 764 765 // Push the replaced bytes onto the replace stack, so that they can be 766 // put back when BS is used. The bytes of a multi-byte character are 767 // done the other way around, so that the first byte is popped off 768 // first (it tells the byte length of the character). 769 replace_push_nul(); 770 replace_push(oldp + col, oldlen); 771 } 772 773 char *newp = xmalloc(linelen + newlen - oldlen); 774 775 // Copy bytes before the cursor. 776 if (col > 0) { 777 memmove(newp, oldp, col); 778 } 779 780 // Copy bytes after the changed character(s). 781 char *p = newp + col; 782 if (linelen > col + oldlen) { 783 memmove(p + newlen, oldp + col + oldlen, linelen - col - oldlen); 784 } 785 786 // Insert or overwrite the new character. 787 memmove(p, buf, charlen); 788 789 // Fill with spaces when necessary. 790 for (size_t i = charlen; i < newlen; i++) { 791 p[i] = ' '; 792 } 793 794 // Replace the line in the buffer. 795 ml_replace(lnum, newp, false); 796 797 // mark the buffer as changed and prepare for displaying 798 inserted_bytes(lnum, (colnr_T)col, (int)oldlen, (int)newlen); 799 800 // If we're in Insert or Replace mode and 'showmatch' is set, then briefly 801 // show the match for right parens and braces. 802 if (p_sm && (State & MODE_INSERT) 803 && msg_silent == 0 804 && !ins_compl_active()) { 805 showmatch(utf_ptr2char(buf)); 806 } 807 808 if (!p_ri || (State & REPLACE_FLAG)) { 809 // Normal insert: move cursor right 810 curwin->w_cursor.col += (colnr_T)charlen; 811 } 812 // TODO(Bram): should try to update w_row here, to avoid recomputing it later. 813 } 814 815 /// Insert a string at the cursor position. 816 /// Note: Does NOT handle Replace mode. 817 /// Caller must have prepared for undo. 818 void ins_str(char *s, size_t slen) 819 { 820 linenr_T lnum = curwin->w_cursor.lnum; 821 822 if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { 823 coladvance_force(getviscol()); 824 } 825 826 colnr_T col = curwin->w_cursor.col; 827 char *oldp = ml_get(lnum); 828 int oldlen = ml_get_len(lnum); 829 830 char *newp = xmalloc((size_t)oldlen + slen + 1); 831 if (col > 0) { 832 memmove(newp, oldp, (size_t)col); 833 } 834 memmove(newp + col, s, slen); 835 int bytes = oldlen - col + 1; 836 assert(bytes >= 0); 837 memmove(newp + col + slen, oldp + col, (size_t)bytes); 838 ml_replace(lnum, newp, false); 839 inserted_bytes(lnum, col, 0, (int)slen); 840 curwin->w_cursor.col += (colnr_T)slen; 841 } 842 843 // Delete one character under the cursor. 844 // If "fixpos" is true, don't leave the cursor on the NUL after the line. 845 // Caller must have prepared for undo. 846 // 847 // return FAIL for failure, OK otherwise 848 int del_char(bool fixpos) 849 { 850 // Make sure the cursor is at the start of a character. 851 mb_adjust_cursor(); 852 if (*get_cursor_pos_ptr() == NUL) { 853 return FAIL; 854 } 855 return del_chars(1, fixpos); 856 } 857 858 /// Like del_bytes(), but delete characters instead of bytes. 859 int del_chars(int count, int fixpos) 860 { 861 int bytes = 0; 862 char *p = get_cursor_pos_ptr(); 863 for (int i = 0; i < count && *p != NUL; i++) { 864 int l = utfc_ptr2len(p); 865 bytes += l; 866 p += l; 867 } 868 return del_bytes(bytes, fixpos, true); 869 } 870 871 /// Delete "count" bytes under the cursor. 872 /// If "fixpos" is true, don't leave the cursor on the NUL after the line. 873 /// Caller must have prepared for undo. 874 /// 875 /// @param count number of bytes to be deleted 876 /// @param fixpos_arg leave the cursor on the NUL after the line 877 /// @param use_delcombine 'delcombine' option applies 878 /// 879 /// @return FAIL for failure, OK otherwise 880 int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) 881 { 882 linenr_T lnum = curwin->w_cursor.lnum; 883 colnr_T col = curwin->w_cursor.col; 884 bool fixpos = fixpos_arg; 885 char *oldp = ml_get(lnum); 886 colnr_T oldlen = ml_get_len(lnum); 887 888 // Can't do anything when the cursor is on the NUL after the line. 889 if (col >= oldlen) { 890 return FAIL; 891 } 892 // If "count" is zero there is nothing to do. 893 if (count == 0) { 894 return OK; 895 } 896 // If "count" is negative the caller must be doing something wrong. 897 if (count < 1) { 898 siemsg("E292: Invalid count for del_bytes(): %ld", (int64_t)count); 899 return FAIL; 900 } 901 902 // If 'delcombine' is set and deleting (less than) one character, only 903 // delete the last combining character. 904 if (p_deco && use_delcombine && utfc_ptr2len(oldp + col) >= count) { 905 char *p0 = oldp + col; 906 GraphemeState state = GRAPHEME_STATE_INIT; 907 if (utf_composinglike(p0, p0 + utf_ptr2len(p0), &state)) { 908 // Find the last composing char, there can be several. 909 int n = col; 910 do { 911 col = n; 912 count = utf_ptr2len(oldp + n); 913 n += count; 914 } while (utf_composinglike(oldp + col, oldp + n, &state)); 915 fixpos = false; 916 } 917 } 918 919 // When count is too big, reduce it. 920 int movelen = oldlen - col - count + 1; // includes trailing NUL 921 if (movelen <= 1) { 922 // If we just took off the last character of a non-blank line, and 923 // fixpos is true, we don't want to end up positioned at the NUL, 924 // unless "restart_edit" is set or 'virtualedit' contains "onemore". 925 if (col > 0 && fixpos && restart_edit == 0 926 && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) { 927 curwin->w_cursor.col--; 928 curwin->w_cursor.coladd = 0; 929 curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col); 930 } 931 count = oldlen - col; 932 movelen = 1; 933 } 934 colnr_T newlen = oldlen - count; 935 936 // If the old line has been allocated the deletion can be done in the 937 // existing line. Otherwise a new line has to be allocated. 938 bool alloc_newp = !ml_line_alloced(); // check if oldp was allocated 939 char *newp; 940 if (!alloc_newp) { 941 ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen); 942 newp = oldp; // use same allocated memory 943 } else { // need to allocate a new line 944 newp = xmallocz((size_t)newlen); 945 memmove(newp, oldp, (size_t)col); 946 } 947 memmove(newp + col, oldp + col + count, (size_t)movelen); 948 if (alloc_newp) { 949 ml_replace(lnum, newp, false); 950 } else { 951 curbuf->b_ml.ml_line_textlen = newlen + 1; 952 } 953 954 // mark the buffer as changed and prepare for displaying 955 inserted_bytes(lnum, col, count, 0); 956 957 return OK; 958 } 959 960 /// open_line: Add a new line below or above the current line. 961 /// 962 /// For MODE_VREPLACE state, we only add a new line when we get to the end of 963 /// the file, otherwise we just start replacing the next line. 964 /// 965 /// Caller must take care of undo. Since MODE_VREPLACE may affect any number of 966 /// lines however, it may call u_save_cursor() again when starting to change a 967 /// new line. 968 /// "flags": OPENLINE_DELSPACES delete spaces after cursor 969 /// OPENLINE_DO_COM format comments 970 /// OPENLINE_KEEPTRAIL keep trailing spaces 971 /// OPENLINE_MARKFIX adjust mark positions after the line break 972 /// OPENLINE_COM_LIST format comments with list or 2nd line indent 973 /// OPENLINE_FORCE_INDENT set indent from second_line_indent, ignore 'autoindent' 974 /// 975 /// "second_line_indent": indent for after ^^D in Insert mode or if flag 976 /// OPENLINE_COM_LIST 977 /// "did_do_comment" is set to true when intentionally putting the comment 978 /// leader in front of the new line. 979 /// 980 /// @param dir FORWARD or BACKWARD 981 /// 982 /// @return true on success, false on failure 983 bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) 984 { 985 char *next_line = NULL; // copy of the next line 986 char *p_extra = NULL; // what goes to next line 987 colnr_T less_cols = 0; // less columns for mark in new line 988 colnr_T less_cols_off = 0; // columns to skip for mark adjust 989 pos_T old_cursor; // old cursor position 990 colnr_T newcol = 0; // new cursor column 991 int newindent = 0; // auto-indent of the new line 992 bool trunc_line = false; // truncate current line afterwards 993 bool retval = false; // return value 994 int extra_len = 0; // length of p_extra string 995 int lead_len; // length of comment leader 996 int comment_start = 0; // start index of the comment leader 997 char *lead_flags; // position in 'comments' for comment leader 998 char *leader = NULL; // copy of comment leader 999 char *allocated = NULL; // allocated memory 1000 char *p; 1001 char saved_char = NUL; // init for GCC 1002 pos_T *pos; 1003 bool do_si = may_do_si(); 1004 bool no_si = false; // reset did_si afterwards 1005 int first_char = NUL; // init for GCC 1006 int vreplace_mode; 1007 bool did_append; // appended a new line 1008 int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting 1009 1010 linenr_T lnum = curwin->w_cursor.lnum; 1011 colnr_T mincol = curwin->w_cursor.col + 1; 1012 1013 // make a copy of the current line so we can mess with it 1014 char *saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); 1015 1016 if (State & VREPLACE_FLAG) { 1017 // With MODE_VREPLACE we make a copy of the next line, which we will be 1018 // starting to replace. First make the new line empty and let vim play 1019 // with the indenting and comment leader to its heart's content. Then 1020 // we grab what it ended up putting on the new line, put back the 1021 // original line, and call ins_char() to put each new character onto 1022 // the line, replacing what was there before and pushing the right 1023 // stuff onto the replace stack. -- webb. 1024 if (curwin->w_cursor.lnum < orig_line_count) { 1025 next_line = xstrnsave(ml_get(curwin->w_cursor.lnum + 1), 1026 (size_t)ml_get_len(curwin->w_cursor.lnum + 1)); 1027 } else { 1028 next_line = xstrdup(""); 1029 } 1030 1031 // In MODE_VREPLACE state, a NL replaces the rest of the line, and 1032 // starts replacing the next line, so push all of the characters left 1033 // on the line onto the replace stack. We'll push any other characters 1034 // that might be replaced at the start of the next line (due to 1035 // autoindent etc) a bit later. 1036 replace_push_nul(); // Call twice because BS over NL expects it 1037 replace_push_nul(); 1038 p = saved_line + curwin->w_cursor.col; 1039 replace_push(p, strlen(p)); 1040 saved_line[curwin->w_cursor.col] = NUL; 1041 } 1042 1043 if ((State & MODE_INSERT) && (State & VREPLACE_FLAG) == 0) { 1044 p_extra = saved_line + curwin->w_cursor.col; 1045 if (do_si) { // need first char after new line break 1046 p = skipwhite(p_extra); 1047 first_char = (unsigned char)(*p); 1048 } 1049 extra_len = (int)strlen(p_extra); 1050 saved_char = *p_extra; 1051 *p_extra = NUL; 1052 } 1053 1054 u_clearline(curbuf); // cannot do "U" command when adding lines 1055 did_si = false; 1056 ai_col = 0; 1057 1058 // If we just did an auto-indent, then we didn't type anything on 1059 // the prior line, and it should be truncated. Do this even if 'ai' is not 1060 // set because automatically inserting a comment leader also sets did_ai. 1061 if (dir == FORWARD && did_ai) { 1062 trunc_line = true; 1063 } 1064 1065 if ((flags & OPENLINE_FORCE_INDENT)) { 1066 newindent = second_line_indent; 1067 } else if (curbuf->b_p_ai || do_si) { 1068 // If 'autoindent' and/or 'smartindent' is set, try to figure out what 1069 // indent to use for the new line. 1070 // count white space on current line 1071 newindent = indent_size_ts(saved_line, curbuf->b_p_ts, curbuf->b_p_vts_array); 1072 if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) { 1073 newindent = second_line_indent; // for ^^D command in insert mode 1074 } 1075 1076 // Do smart indenting. 1077 // In insert/replace mode (only when dir == FORWARD) 1078 // we may move some text to the next line. If it starts with '{' 1079 // don't add an indent. Fixes inserting a NL before '{' in line 1080 // "if (condition) {" 1081 if (!trunc_line && do_si && *saved_line != NUL 1082 && (p_extra == NULL || first_char != '{')) { 1083 old_cursor = curwin->w_cursor; 1084 char *ptr = saved_line; 1085 if (flags & OPENLINE_DO_COM) { 1086 lead_len = get_leader_len(ptr, NULL, false, true); 1087 } else { 1088 lead_len = 0; 1089 } 1090 if (dir == FORWARD) { 1091 // Skip preprocessor directives, unless they are recognised as comments. 1092 if (lead_len == 0 && ptr[0] == '#') { 1093 while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) { 1094 ptr = ml_get(--curwin->w_cursor.lnum); 1095 } 1096 newindent = get_indent(); 1097 } 1098 if (flags & OPENLINE_DO_COM) { 1099 lead_len = get_leader_len(ptr, NULL, false, true); 1100 } else { 1101 lead_len = 0; 1102 } 1103 if (lead_len > 0) { 1104 // This case gets the following right: 1105 // \* 1106 // * A comment (read '\' as '/'). 1107 // */ 1108 // #define IN_THE_WAY 1109 // This should line up here; 1110 p = skipwhite(ptr); 1111 if (p[0] == '/' && p[1] == '*') { 1112 p++; 1113 } 1114 if (p[0] == '*') { 1115 for (p++; *p; p++) { 1116 if (p[0] == '/' && p[-1] == '*') { 1117 // End of C comment, indent should line up 1118 // with the line containing the start of 1119 // the comment. 1120 curwin->w_cursor.col = (colnr_T)(p - ptr); 1121 if ((pos = findmatch(NULL, NUL)) != NULL) { 1122 curwin->w_cursor.lnum = pos->lnum; 1123 newindent = get_indent(); 1124 break; 1125 } 1126 // this may make "ptr" invalid, get it again 1127 ptr = ml_get(curwin->w_cursor.lnum); 1128 p = ptr + curwin->w_cursor.col; 1129 } 1130 } 1131 } 1132 } else { // Not a comment line 1133 // Find last non-blank in line 1134 p = ptr + strlen(ptr) - 1; 1135 while (p > ptr && ascii_iswhite(*p)) { 1136 p--; 1137 } 1138 char last_char = *p; 1139 1140 // find the character just before the '{' or ';' 1141 if (last_char == '{' || last_char == ';') { 1142 if (p > ptr) { 1143 p--; 1144 } 1145 while (p > ptr && ascii_iswhite(*p)) { 1146 p--; 1147 } 1148 } 1149 // Try to catch lines that are split over multiple 1150 // lines. eg: 1151 // if (condition && 1152 // condition) { 1153 // Should line up here! 1154 // } 1155 if (*p == ')') { 1156 curwin->w_cursor.col = (colnr_T)(p - ptr); 1157 if ((pos = findmatch(NULL, '(')) != NULL) { 1158 curwin->w_cursor.lnum = pos->lnum; 1159 newindent = get_indent(); 1160 ptr = get_cursor_line_ptr(); 1161 } 1162 } 1163 // If last character is '{' do indent, without 1164 // checking for "if" and the like. 1165 if (last_char == '{') { 1166 did_si = true; // do indent 1167 no_si = true; // don't delete it when '{' typed 1168 // Look for "if" and the like, use 'cinwords'. 1169 // Don't do this if the previous line ended in ';' or 1170 // '}'. 1171 } else if (last_char != ';' && last_char != '}' 1172 && cin_is_cinword(ptr)) { 1173 did_si = true; 1174 } 1175 } 1176 } else { // dir == BACKWARD 1177 // Skip preprocessor directives, unless they are 1178 // recognised as comments. 1179 if (lead_len == 0 && ptr[0] == '#') { 1180 bool was_backslashed = false; 1181 1182 while ((ptr[0] == '#' || was_backslashed) 1183 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { 1184 if (*ptr && ptr[strlen(ptr) - 1] == '\\') { 1185 was_backslashed = true; 1186 } else { 1187 was_backslashed = false; 1188 } 1189 ptr = ml_get(++curwin->w_cursor.lnum); 1190 } 1191 if (was_backslashed) { 1192 newindent = 0; // Got to end of file 1193 } else { 1194 newindent = get_indent(); 1195 } 1196 } 1197 p = skipwhite(ptr); 1198 if (*p == '}') { // if line starts with '}': do indent 1199 did_si = true; 1200 } else { // can delete indent when '{' typed 1201 can_si_back = true; 1202 } 1203 } 1204 curwin->w_cursor = old_cursor; 1205 } 1206 if (do_si) { 1207 can_si = true; 1208 } 1209 1210 did_ai = true; 1211 } 1212 1213 // May do indenting after opening a new line. 1214 bool do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL) 1215 && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK, 1216 ' ', linewhite(curwin->w_cursor.lnum)) 1217 && !(flags & OPENLINE_FORCE_INDENT); 1218 1219 // Find out if the current line starts with a comment leader. 1220 // This may then be inserted in front of the new line. 1221 end_comment_pending = NUL; 1222 if (flags & OPENLINE_DO_COM) { 1223 lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); 1224 if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD 1225 && (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) { 1226 // Check for a line comment after code. 1227 comment_start = check_linecomment(saved_line); 1228 if (comment_start != MAXCOL) { 1229 lead_len = get_leader_len(saved_line + comment_start, &lead_flags, false, true); 1230 if (lead_len != 0) { 1231 lead_len += comment_start; 1232 if (did_do_comment != NULL) { 1233 *did_do_comment = true; 1234 } 1235 } 1236 } 1237 } 1238 } else { 1239 lead_len = 0; 1240 } 1241 if (lead_len > 0) { 1242 char *lead_repl = NULL; // replaces comment leader 1243 int lead_repl_len = 0; // length of *lead_repl 1244 char lead_middle[COM_MAX_LEN]; // middle-comment string 1245 char lead_end[COM_MAX_LEN]; // end-comment string 1246 char *comment_end = NULL; // where lead_end has been found 1247 int extra_space = false; // append extra space 1248 bool require_blank = false; // requires blank after middle 1249 char *p2; 1250 1251 // If the comment leader has the start, middle or end flag, it may not 1252 // be used or may be replaced with the middle leader. 1253 for (p = lead_flags; *p && *p != ':'; p++) { 1254 if (*p == COM_BLANK) { 1255 require_blank = true; 1256 continue; 1257 } 1258 if (*p == COM_START || *p == COM_MIDDLE) { 1259 int current_flag = (unsigned char)(*p); 1260 if (*p == COM_START) { 1261 // Doing "O" on a start of comment does not insert leader. 1262 if (dir == BACKWARD) { 1263 lead_len = 0; 1264 break; 1265 } 1266 1267 // find start of middle part 1268 copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); 1269 require_blank = false; 1270 } 1271 1272 // Isolate the strings of the middle and end leader. 1273 while (*p && p[-1] != ':') { // find end of middle flags 1274 if (*p == COM_BLANK) { 1275 require_blank = true; 1276 } 1277 p++; 1278 } 1279 copy_option_part(&p, lead_middle, COM_MAX_LEN, ","); 1280 1281 while (*p && p[-1] != ':') { // find end of end flags 1282 // Check whether we allow automatic ending of comments 1283 if (*p == COM_AUTO_END) { 1284 end_comment_pending = -1; // means we want to set it 1285 } 1286 p++; 1287 } 1288 size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); 1289 1290 if (end_comment_pending == -1) { // we can set it now 1291 end_comment_pending = (unsigned char)lead_end[n - 1]; 1292 } 1293 1294 // If the end of the comment is in the same line, don't use 1295 // the comment leader. 1296 if (dir == FORWARD) { 1297 for (p = saved_line + lead_len; *p; p++) { 1298 if (strncmp(p, lead_end, n) == 0) { 1299 comment_end = p; 1300 lead_len = 0; 1301 break; 1302 } 1303 } 1304 } 1305 1306 // Doing "o" on a start of comment inserts the middle leader. 1307 if (lead_len > 0) { 1308 if (current_flag == COM_START) { 1309 lead_repl = lead_middle; 1310 lead_repl_len = (int)strlen(lead_middle); 1311 } 1312 1313 // If we have hit RETURN immediately after the start 1314 // comment leader, then put a space after the middle 1315 // comment leader on the next line. 1316 if (!ascii_iswhite(saved_line[lead_len - 1]) 1317 && ((p_extra != NULL 1318 && (int)curwin->w_cursor.col == lead_len) 1319 || (p_extra == NULL 1320 && saved_line[lead_len] == NUL) 1321 || require_blank)) { 1322 extra_space = true; 1323 } 1324 } 1325 break; 1326 } 1327 if (*p == COM_END) { 1328 // Doing "o" on the end of a comment does not insert leader. 1329 // Remember where the end is, might want to use it to find the 1330 // start (for C-comments). 1331 if (dir == FORWARD) { 1332 comment_end = skipwhite(saved_line); 1333 lead_len = 0; 1334 break; 1335 } 1336 1337 // Doing "O" on the end of a comment inserts the middle leader. 1338 // Find the string for the middle leader, searching backwards. 1339 while (p > curbuf->b_p_com && *p != ',') { 1340 p--; 1341 } 1342 for (lead_repl = p; lead_repl > curbuf->b_p_com 1343 && lead_repl[-1] != ':'; lead_repl--) {} 1344 lead_repl_len = (int)(p - lead_repl); 1345 1346 // We can probably always add an extra space when doing "O" on 1347 // the comment-end 1348 extra_space = true; 1349 1350 // Check whether we allow automatic ending of comments 1351 for (p2 = p; *p2 && *p2 != ':'; p2++) { 1352 if (*p2 == COM_AUTO_END) { 1353 end_comment_pending = -1; // means we want to set it 1354 } 1355 } 1356 if (end_comment_pending == -1) { 1357 // Find last character in end-comment string 1358 while (*p2 && *p2 != ',') { 1359 p2++; 1360 } 1361 end_comment_pending = (unsigned char)p2[-1]; 1362 } 1363 break; 1364 } 1365 if (*p == COM_FIRST) { 1366 // Comment leader for first line only: Don't repeat leader 1367 // when using "O", blank out leader when using "o". 1368 if (dir == BACKWARD) { 1369 lead_len = 0; 1370 } else { 1371 lead_repl = ""; 1372 lead_repl_len = 0; 1373 } 1374 break; 1375 } 1376 } 1377 if (lead_len > 0) { 1378 // allocate buffer (may concatenate p_extra later) 1379 int bytes = lead_len 1380 + lead_repl_len 1381 + extra_space 1382 + extra_len 1383 + (second_line_indent > 0 ? second_line_indent : 0) 1384 + 1; 1385 assert(bytes >= 0); 1386 leader = xmalloc((size_t)bytes); 1387 allocated = leader; // remember to free it later 1388 1389 xmemcpyz(leader, saved_line, (size_t)lead_len); 1390 1391 // TODO(vim): handle multi-byte and double width chars 1392 for (int li = 0; li < comment_start; li++) { 1393 if (!ascii_iswhite(leader[li])) { 1394 leader[li] = ' '; 1395 } 1396 } 1397 1398 // Replace leader with lead_repl, right or left adjusted 1399 if (lead_repl != NULL) { 1400 int c = 0; 1401 int off = 0; 1402 1403 for (p = lead_flags; *p != NUL && *p != ':';) { 1404 if (*p == COM_RIGHT || *p == COM_LEFT) { 1405 c = (unsigned char)(*p++); 1406 } else if (ascii_isdigit(*p) || *p == '-') { 1407 off = getdigits_int(&p, true, 0); 1408 } else { 1409 p++; 1410 } 1411 } 1412 if (c == COM_RIGHT) { // right adjusted leader 1413 // find last non-white in the leader to line up with 1414 for (p = leader + lead_len - 1; p > leader 1415 && ascii_iswhite(*p); p--) {} 1416 p++; 1417 1418 // Compute the length of the replaced characters in 1419 // screen characters, not bytes. 1420 { 1421 int repl_size = vim_strnsize(lead_repl, lead_repl_len); 1422 int old_size = 0; 1423 char *endp = p; 1424 1425 while (old_size < repl_size && p > leader) { 1426 MB_PTR_BACK(leader, p); 1427 old_size += ptr2cells(p); 1428 } 1429 int l = lead_repl_len - (int)(endp - p); 1430 if (l != 0) { 1431 memmove(endp + l, endp, 1432 (size_t)((leader + lead_len) - endp)); 1433 } 1434 lead_len += l; 1435 } 1436 memmove(p, lead_repl, (size_t)lead_repl_len); 1437 if (p + lead_repl_len > leader + lead_len) { 1438 p[lead_repl_len] = NUL; 1439 } 1440 1441 // blank-out any other chars from the old leader. 1442 while (--p >= leader) { 1443 int l = utf_head_off(leader, p); 1444 1445 if (l > 1) { 1446 p -= l; 1447 if (ptr2cells(p) > 1) { 1448 p[1] = ' '; 1449 l--; 1450 } 1451 memmove(p + 1, p + l + 1, 1452 (size_t)((leader + lead_len) - (p + l + 1))); 1453 lead_len -= l; 1454 *p = ' '; 1455 } else if (!ascii_iswhite(*p)) { 1456 *p = ' '; 1457 } 1458 } 1459 } else { // left adjusted leader 1460 p = skipwhite(leader); 1461 // Compute the length of the replaced characters in 1462 // screen characters, not bytes. Move the part that is 1463 // not to be overwritten. 1464 { 1465 int repl_size = vim_strnsize(lead_repl, lead_repl_len); 1466 int i; 1467 int l; 1468 1469 for (i = 0; i < lead_len && p[i] != NUL; i += l) { 1470 l = utfc_ptr2len(p + i); 1471 if (vim_strnsize(p, i + l) > repl_size) { 1472 break; 1473 } 1474 } 1475 if (i != lead_repl_len) { 1476 memmove(p + lead_repl_len, p + i, 1477 (size_t)(lead_len - i - (p - leader))); 1478 lead_len += lead_repl_len - i; 1479 } 1480 } 1481 memmove(p, lead_repl, (size_t)lead_repl_len); 1482 1483 // Replace any remaining non-white chars in the old 1484 // leader by spaces. Keep Tabs, the indent must 1485 // remain the same. 1486 for (p += lead_repl_len; p < leader + lead_len; p++) { 1487 if (!ascii_iswhite(*p)) { 1488 // Don't put a space before a TAB. 1489 if (p + 1 < leader + lead_len && p[1] == TAB) { 1490 lead_len--; 1491 memmove(p, p + 1, (size_t)(leader + lead_len - p)); 1492 } else { 1493 int l = utfc_ptr2len(p); 1494 1495 if (l > 1) { 1496 if (ptr2cells(p) > 1) { 1497 // Replace a double-wide char with 1498 // two spaces 1499 l--; 1500 *p++ = ' '; 1501 } 1502 memmove(p + 1, p + l, (size_t)(leader + lead_len - p)); 1503 lead_len -= l - 1; 1504 } 1505 *p = ' '; 1506 } 1507 } 1508 } 1509 *p = NUL; 1510 } 1511 1512 // Recompute the indent, it may have changed. 1513 if (curbuf->b_p_ai || do_si) { 1514 newindent = indent_size_ts(leader, curbuf->b_p_ts, curbuf->b_p_vts_array); 1515 } 1516 1517 // Add the indent offset 1518 if (newindent + off < 0) { 1519 off = -newindent; 1520 newindent = 0; 1521 } else { 1522 newindent += off; 1523 } 1524 1525 // Correct trailing spaces for the shift, so that 1526 // alignment remains equal. 1527 while (off > 0 && lead_len > 0 1528 && leader[lead_len - 1] == ' ') { 1529 // Don't do it when there is a tab before the space 1530 if (vim_strchr(skipwhite(leader), '\t') != NULL) { 1531 break; 1532 } 1533 lead_len--; 1534 off--; 1535 } 1536 1537 // If the leader ends in white space, don't add an 1538 // extra space 1539 if (lead_len > 0 && ascii_iswhite(leader[lead_len - 1])) { 1540 extra_space = false; 1541 } 1542 leader[lead_len] = NUL; 1543 } 1544 1545 if (extra_space) { 1546 leader[lead_len++] = ' '; 1547 leader[lead_len] = NUL; 1548 } 1549 1550 newcol = lead_len; 1551 1552 // if a new indent will be set below, remove the indent that 1553 // is in the comment leader 1554 if (newindent || did_si) { 1555 while (lead_len && ascii_iswhite(*leader)) { 1556 lead_len--; 1557 newcol--; 1558 leader++; 1559 } 1560 } 1561 did_si = can_si = false; 1562 } else if (comment_end != NULL) { 1563 // We have finished a comment, so we don't use the leader. 1564 // If this was a C-comment and 'ai' or 'si' is set do a normal 1565 // indent to align with the line containing the start of the 1566 // comment. 1567 if (comment_end[0] == '*' && comment_end[1] == '/' 1568 && (curbuf->b_p_ai || do_si)) { 1569 old_cursor = curwin->w_cursor; 1570 curwin->w_cursor.col = (colnr_T)(comment_end - saved_line); 1571 if ((pos = findmatch(NULL, NUL)) != NULL) { 1572 curwin->w_cursor.lnum = pos->lnum; 1573 newindent = get_indent(); 1574 } 1575 curwin->w_cursor = old_cursor; 1576 } 1577 } 1578 } 1579 1580 // (State == MODE_INSERT || State == MODE_REPLACE), only when dir == FORWARD 1581 if (p_extra != NULL) { 1582 *p_extra = saved_char; // restore char that NUL replaced 1583 1584 // When 'ai' set or "flags" has OPENLINE_DELSPACES, skip to the first 1585 // non-blank. 1586 // 1587 // When in MODE_REPLACE state, put the deleted blanks on the replace 1588 // stack, preceded by a NUL, so they can be put back when a BS is 1589 // entered. 1590 if (REPLACE_NORMAL(State)) { 1591 replace_push_nul(); // end of extra blanks 1592 } 1593 if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) { 1594 while ((*p_extra == ' ' || *p_extra == '\t') 1595 && !utf_iscomposing_first(utf_ptr2char(p_extra + 1))) { 1596 if (REPLACE_NORMAL(State)) { 1597 replace_push(p_extra, 1); // always ascii, len = 1 1598 } 1599 p_extra++; 1600 less_cols_off++; 1601 } 1602 } 1603 1604 // columns for marks adjusted for removed columns 1605 less_cols = (int)(p_extra - saved_line); 1606 } 1607 1608 if (p_extra == NULL) { 1609 p_extra = ""; // append empty line 1610 } 1611 1612 // concatenate leader and p_extra, if there is a leader 1613 if (lead_len > 0) { 1614 if (flags & OPENLINE_COM_LIST && second_line_indent > 0) { 1615 int padding = second_line_indent 1616 - (newindent + (int)strlen(leader)); 1617 1618 // Here whitespace is inserted after the comment char. 1619 // Below, set_indent(newindent, SIN_INSERT) will insert the 1620 // whitespace needed before the comment char. 1621 for (int i = 0; i < padding; i++) { 1622 strcat(leader, " "); 1623 less_cols--; 1624 newcol++; 1625 } 1626 } 1627 strcat(leader, p_extra); 1628 p_extra = leader; 1629 did_ai = true; // So truncating blanks works with comments 1630 less_cols -= lead_len; 1631 } else { 1632 end_comment_pending = NUL; // turns out there was no leader 1633 } 1634 1635 curbuf_splice_pending++; 1636 old_cursor = curwin->w_cursor; 1637 int old_cmod_flags = cmdmod.cmod_flags; 1638 char *prompt_moved = NULL; 1639 if (dir == BACKWARD) { 1640 // In case of prompt buffer, when we are applying 'normal O' operation on line of prompt, 1641 // we can't add a new line before the prompt. In this case, we move the prompt text one 1642 // line below and create a new prompt line as current line. 1643 if (bt_prompt(curbuf) && curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum) { 1644 char *prompt_line = ml_get(curwin->w_cursor.lnum); 1645 char *prompt = prompt_text(); 1646 size_t prompt_len = strlen(prompt); 1647 1648 if (strncmp(prompt_line, prompt, prompt_len) == 0) { 1649 STRMOVE(prompt_line, prompt_line + prompt_len); 1650 // We are moving the lines but the b_prompt_start mark needs to stay in 1651 // place so freezing marks before making the move. 1652 cmdmod.cmod_flags = cmdmod.cmod_flags | CMOD_LOCKMARKS; 1653 ml_replace(curwin->w_cursor.lnum, prompt_line, true); 1654 prompt_moved = concat_str(prompt, p_extra); 1655 p_extra = prompt_moved; 1656 } 1657 } 1658 curwin->w_cursor.lnum--; 1659 } 1660 if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) { 1661 if (ml_append(curwin->w_cursor.lnum, p_extra, 0, false) == FAIL) { 1662 goto theend; 1663 } 1664 // Postpone calling changed_lines(), because it would mess up folding 1665 // with markers. 1666 mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1, 0, kExtmarkNOOP); 1667 did_append = true; 1668 } else { 1669 // In MODE_VREPLACE state we are starting to replace the next line. 1670 curwin->w_cursor.lnum++; 1671 if (curwin->w_cursor.lnum >= Insstart.lnum + vr_lines_changed) { 1672 // In case we NL to a new line, BS to the previous one, and NL 1673 // again, we don't want to save the new line for undo twice. 1674 u_save_cursor(); // errors are ignored! 1675 vr_lines_changed++; 1676 } 1677 ml_replace(curwin->w_cursor.lnum, p_extra, true); 1678 changed_bytes(curwin->w_cursor.lnum, 0); 1679 // TODO(vigoux): extmark_splice_cols here?? 1680 curwin->w_cursor.lnum--; 1681 did_append = false; 1682 } 1683 1684 inhibit_delete_count++; 1685 if (newindent || did_si) { 1686 curwin->w_cursor.lnum++; 1687 if (did_si) { 1688 int sw = get_sw_value(curbuf); 1689 1690 if (p_sr) { 1691 newindent -= newindent % sw; 1692 } 1693 newindent += sw; 1694 } 1695 // Copy the indent 1696 if (curbuf->b_p_ci) { 1697 copy_indent(newindent, saved_line); 1698 1699 // Set the 'preserveindent' option so that any further screwing 1700 // with the line doesn't entirely destroy our efforts to preserve 1701 // it. It gets restored at the function end. 1702 curbuf->b_p_pi = true; 1703 } else { 1704 set_indent(newindent, SIN_INSERT|SIN_NOMARK); 1705 } 1706 less_cols -= curwin->w_cursor.col; 1707 1708 ai_col = curwin->w_cursor.col; 1709 1710 // In MODE_REPLACE state, for each character in the new indent, there 1711 // must be a NUL on the replace stack, for when it is deleted with BS 1712 if (REPLACE_NORMAL(State)) { 1713 for (colnr_T n = 0; n < curwin->w_cursor.col; n++) { 1714 replace_push_nul(); 1715 } 1716 } 1717 newcol += curwin->w_cursor.col; 1718 if (no_si) { 1719 did_si = false; 1720 } 1721 } 1722 inhibit_delete_count--; 1723 1724 // In MODE_REPLACE state, for each character in the extra leader, there 1725 // must be a NUL on the replace stack, for when it is deleted with BS. 1726 if (REPLACE_NORMAL(State)) { 1727 while (lead_len-- > 0) { 1728 replace_push_nul(); 1729 } 1730 } 1731 1732 curwin->w_cursor = old_cursor; 1733 1734 if (dir == FORWARD) { 1735 if (trunc_line || (State & MODE_INSERT)) { 1736 // truncate current line at cursor 1737 saved_line[curwin->w_cursor.col] = NUL; 1738 // Remove trailing white space, unless OPENLINE_KEEPTRAIL used. 1739 if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) { 1740 truncate_spaces(saved_line, (size_t)curwin->w_cursor.col); 1741 } 1742 ml_replace(curwin->w_cursor.lnum, saved_line, false); 1743 1744 int new_len = (int)strlen(saved_line); 1745 1746 // TODO(vigoux): maybe there is issues there with expandtabs ? 1747 int cols_spliced = 0; 1748 if (new_len < curwin->w_cursor.col) { 1749 extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, 1750 new_len, curwin->w_cursor.col - new_len, 0, kExtmarkUndo); 1751 cols_spliced = curwin->w_cursor.col - new_len; 1752 } 1753 1754 saved_line = NULL; 1755 if (did_append) { 1756 // Always move extmarks - Here we move only the line where the cursor is, 1757 // the previous mark_adjust() took care of the lines after. 1758 int cols_added = mincol - 1 + less_cols_off - less_cols; 1759 extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced, 1760 0, less_cols_off, less_cols_off, 1761 1, cols_added, 1 + cols_added, kExtmarkUndo); 1762 1763 changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, 1764 curwin->w_cursor.lnum + 1, 1, true); 1765 did_append = false; 1766 1767 // Move marks after the line break to the new line. 1768 if (flags & OPENLINE_MARKFIX) { 1769 mark_col_adjust(curwin->w_cursor.lnum, 1770 curwin->w_cursor.col + less_cols_off, 1771 1, -less_cols, 0); 1772 } 1773 } else { 1774 changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); 1775 } 1776 } 1777 1778 // Put the cursor on the new line. Careful: the scrollup() above may 1779 // have moved w_cursor, we must use old_cursor. 1780 curwin->w_cursor.lnum = old_cursor.lnum + 1; 1781 } 1782 if (did_append) { 1783 // bail out and just get the final length of the line we just manipulated 1784 bcount_t extra = ml_get_len(curwin->w_cursor.lnum); 1785 extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0, 1786 0, 0, 0, 1, 0, 1 + extra, kExtmarkUndo); 1787 changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1, true); 1788 } 1789 curbuf_splice_pending--; 1790 1791 curwin->w_cursor.col = newcol; 1792 curwin->w_cursor.coladd = 0; 1793 1794 // In MODE_VREPLACE state, we are handling the replace stack ourselves, so 1795 // stop fixthisline() from doing it (via change_indent()) by telling it 1796 // we're in normal MODE_INSERT state. 1797 if (State & VREPLACE_FLAG) { 1798 vreplace_mode = State; // So we know to put things right later 1799 State = MODE_INSERT; 1800 } else { 1801 vreplace_mode = 0; 1802 } 1803 1804 if (!p_paste) { 1805 if (leader == NULL 1806 && !use_indentexpr_for_lisp() 1807 && curbuf->b_p_lisp 1808 && curbuf->b_p_ai) { 1809 // do lisp indenting 1810 fixthisline(get_lisp_indent); 1811 ai_col = (colnr_T)getwhitecols_curline(); 1812 } else if (do_cindent || (curbuf->b_p_ai && use_indentexpr_for_lisp())) { 1813 // do 'cindent' or 'indentexpr' indenting 1814 do_c_expr_indent(); 1815 ai_col = (colnr_T)getwhitecols_curline(); 1816 } 1817 } 1818 1819 if (vreplace_mode != 0) { 1820 State = vreplace_mode; 1821 } 1822 1823 // Finally, MODE_VREPLACE gets the stuff on the new line, then puts back 1824 // the original line, and inserts the new stuff char by char, pushing old 1825 // stuff onto the replace stack (via ins_char()). 1826 if (State & VREPLACE_FLAG) { 1827 // Put new line in p_extra 1828 p_extra = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); 1829 1830 // Put back original line 1831 ml_replace(curwin->w_cursor.lnum, next_line, false); 1832 1833 // Insert new stuff into line again 1834 curwin->w_cursor.col = 0; 1835 curwin->w_cursor.coladd = 0; 1836 ins_bytes(p_extra); // will call changed_bytes() 1837 xfree(p_extra); 1838 next_line = NULL; 1839 } 1840 1841 retval = true; // success! 1842 theend: 1843 curbuf->b_p_pi = saved_pi; 1844 xfree(saved_line); 1845 xfree(next_line); 1846 xfree(allocated); 1847 xfree(prompt_moved); 1848 cmdmod.cmod_flags = old_cmod_flags; 1849 return retval; 1850 } 1851 1852 /// Delete from cursor to end of line. 1853 /// Caller must have prepared for undo. 1854 /// If "fixpos" is true fix the cursor position when done. 1855 void truncate_line(int fixpos) 1856 { 1857 linenr_T lnum = curwin->w_cursor.lnum; 1858 colnr_T col = curwin->w_cursor.col; 1859 char *old_line = ml_get(lnum); 1860 char *newp = col == 0 ? xstrdup("") : xstrnsave(old_line, (size_t)col); 1861 int deleted = ml_get_len(lnum) - col; 1862 1863 ml_replace(lnum, newp, false); 1864 1865 // mark the buffer as changed and prepare for displaying 1866 inserted_bytes(lnum, curwin->w_cursor.col, deleted, 0); 1867 1868 // If "fixpos" is true we don't want to end up positioned at the NUL. 1869 if (fixpos && curwin->w_cursor.col > 0) { 1870 curwin->w_cursor.col--; 1871 } 1872 } 1873 1874 /// Delete "nlines" lines at the cursor. 1875 /// Saves the lines for undo first if "undo" is true. 1876 void del_lines(linenr_T nlines, bool undo) 1877 { 1878 int n; 1879 linenr_T first = curwin->w_cursor.lnum; 1880 1881 if (nlines <= 0) { 1882 return; 1883 } 1884 1885 // save the deleted lines for undo 1886 if (undo && u_savedel(first, nlines) == FAIL) { 1887 return; 1888 } 1889 1890 for (n = 0; n < nlines;) { 1891 if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete 1892 break; 1893 } 1894 1895 ml_delete_flags(first, ML_DEL_MESSAGE); 1896 n++; 1897 1898 // If we delete the last line in the file, stop 1899 if (first > curbuf->b_ml.ml_line_count) { 1900 break; 1901 } 1902 } 1903 1904 // Correct the cursor position before calling deleted_lines_mark(), it may 1905 // trigger a callback to display the cursor. 1906 curwin->w_cursor.col = 0; 1907 check_cursor_lnum(curwin); 1908 1909 // adjust marks, mark the buffer as changed and prepare for displaying 1910 deleted_lines_mark(first, n); 1911 } 1912 1913 /// Returns the length in bytes of the prefix of the given string which introduces a comment. 1914 /// 1915 /// If this string is not a comment then 0 is returned. 1916 /// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader. 1917 /// "backward" must be true for the "O" command. 1918 /// If "include_space" is set, include trailing whitespace while calculating the length. 1919 int get_leader_len(char *line, char **flags, bool backward, bool include_space) 1920 { 1921 int j; 1922 bool got_com = false; 1923 char part_buf[COM_MAX_LEN]; // buffer for one option part 1924 char *string; // pointer to comment string 1925 int middle_match_len = 0; 1926 char *saved_flags = NULL; 1927 1928 int result = 0; 1929 int i = 0; 1930 while (ascii_iswhite(line[i])) { // leading white space is ignored 1931 i++; 1932 } 1933 1934 // Repeat to match several nested comment strings. 1935 while (line[i] != NUL) { 1936 // scan through the 'comments' option for a match 1937 bool found_one = false; 1938 for (char *list = curbuf->b_p_com; *list;) { 1939 // Get one option part into part_buf[]. Advance "list" to next 1940 // one. Put "string" at start of string. 1941 if (!got_com && flags != NULL) { 1942 *flags = list; // remember where flags started 1943 } 1944 char *prev_list = list; 1945 copy_option_part(&list, part_buf, COM_MAX_LEN, ","); 1946 string = vim_strchr(part_buf, ':'); 1947 if (string == NULL) { // missing ':', ignore this part 1948 continue; 1949 } 1950 *string++ = NUL; // isolate flags from string 1951 1952 // If we found a middle match previously, use that match when this 1953 // is not a middle or end. 1954 if (middle_match_len != 0 1955 && vim_strchr(part_buf, COM_MIDDLE) == NULL 1956 && vim_strchr(part_buf, COM_END) == NULL) { 1957 break; 1958 } 1959 1960 // When we already found a nested comment, only accept further 1961 // nested comments. 1962 if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) { 1963 continue; 1964 } 1965 1966 // When 'O' flag present and using "O" command skip this one. 1967 if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) { 1968 continue; 1969 } 1970 1971 // Line contents and string must match. 1972 // When string starts with white space, must have some white space 1973 // (but the amount does not need to match, there might be a mix of 1974 // TABs and spaces). 1975 if (ascii_iswhite(string[0])) { 1976 if (i == 0 || !ascii_iswhite(line[i - 1])) { 1977 continue; // missing white space 1978 } 1979 while (ascii_iswhite(string[0])) { 1980 string++; 1981 } 1982 } 1983 for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) {} 1984 if (string[j] != NUL) { 1985 continue; // string doesn't match 1986 } 1987 // When 'b' flag used, there must be white space or an 1988 // end-of-line after the string in the line. 1989 if (vim_strchr(part_buf, COM_BLANK) != NULL 1990 && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { 1991 continue; 1992 } 1993 1994 // We have found a match, stop searching unless this is a middle 1995 // comment. The middle comment can be a substring of the end 1996 // comment in which case it's better to return the length of the 1997 // end comment and its flags. Thus we keep searching with middle 1998 // and end matches and use an end match if it matches better. 1999 if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { 2000 if (middle_match_len == 0) { 2001 middle_match_len = j; 2002 saved_flags = prev_list; 2003 } 2004 continue; 2005 } 2006 if (middle_match_len != 0 && j > middle_match_len) { 2007 // Use this match instead of the middle match, since it's a 2008 // longer thus better match. 2009 middle_match_len = 0; 2010 } 2011 2012 if (middle_match_len == 0) { 2013 i += j; 2014 } 2015 found_one = true; 2016 break; 2017 } 2018 2019 if (middle_match_len != 0) { 2020 // Use the previously found middle match after failing to find a 2021 // match with an end. 2022 if (!got_com && flags != NULL) { 2023 *flags = saved_flags; 2024 } 2025 i += middle_match_len; 2026 found_one = true; 2027 } 2028 2029 // No match found, stop scanning. 2030 if (!found_one) { 2031 break; 2032 } 2033 2034 result = i; 2035 2036 // Include any trailing white space. 2037 while (ascii_iswhite(line[i])) { 2038 i++; 2039 } 2040 2041 if (include_space) { 2042 result = i; 2043 } 2044 2045 // If this comment doesn't nest, stop here. 2046 got_com = true; 2047 if (vim_strchr(part_buf, COM_NEST) == NULL) { 2048 break; 2049 } 2050 } 2051 return result; 2052 } 2053 2054 /// Return the offset at which the last comment in line starts. If there is no 2055 /// comment in the whole line, -1 is returned. 2056 /// 2057 /// When "flags" is not null, it is set to point to the flags describing the 2058 /// recognized comment leader. 2059 int get_last_leader_offset(char *line, char **flags) 2060 { 2061 int result = -1; 2062 int j; 2063 int lower_check_bound = 0; 2064 char *com_leader; 2065 char *com_flags; 2066 char part_buf[COM_MAX_LEN]; // buffer for one option part 2067 2068 // Repeat to match several nested comment strings. 2069 int i = (int)strlen(line); 2070 while (--i >= lower_check_bound) { 2071 // scan through the 'comments' option for a match 2072 bool found_one = false; 2073 for (char *list = curbuf->b_p_com; *list;) { 2074 char *flags_save = list; 2075 2076 // Get one option part into part_buf[]. Advance list to next one. 2077 // put string at start of string. 2078 copy_option_part(&list, part_buf, COM_MAX_LEN, ","); 2079 char *string = vim_strchr(part_buf, ':'); 2080 if (string == NULL) { // If everything is fine, this cannot actually 2081 // happen. 2082 continue; 2083 } 2084 *string++ = NUL; // Isolate flags from string. 2085 com_leader = string; 2086 2087 // Line contents and string must match. 2088 // When string starts with white space, must have some white space 2089 // (but the amount does not need to match, there might be a mix of 2090 // TABs and spaces). 2091 if (ascii_iswhite(string[0])) { 2092 if (i == 0 || !ascii_iswhite(line[i - 1])) { 2093 continue; 2094 } 2095 while (ascii_iswhite(*string)) { 2096 string++; 2097 } 2098 } 2099 for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) { 2100 // do nothing 2101 } 2102 if (string[j] != NUL) { 2103 continue; 2104 } 2105 2106 // When 'b' flag used, there must be white space or an 2107 // end-of-line after the string in the line. 2108 if (vim_strchr(part_buf, COM_BLANK) != NULL 2109 && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) { 2110 continue; 2111 } 2112 2113 if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { 2114 // For a middlepart comment, only consider it to match if 2115 // everything before the current position in the line is 2116 // whitespace. Otherwise we would think we are inside a 2117 // comment if the middle part appears somewhere in the middle 2118 // of the line. E.g. for C the "*" appears often. 2119 for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {} 2120 if (j < i) { 2121 continue; 2122 } 2123 } 2124 2125 // We have found a match, stop searching. 2126 found_one = true; 2127 2128 if (flags) { 2129 *flags = flags_save; 2130 } 2131 com_flags = flags_save; 2132 2133 break; 2134 } 2135 2136 if (found_one) { 2137 char part_buf2[COM_MAX_LEN]; // buffer for one option part 2138 2139 result = i; 2140 // If this comment nests, continue searching. 2141 if (vim_strchr(part_buf, COM_NEST) != NULL) { 2142 continue; 2143 } 2144 2145 lower_check_bound = i; 2146 2147 // Let's verify whether the comment leader found is a substring 2148 // of other comment leaders. If it is, let's adjust the 2149 // lower_check_bound so that we make sure that we have determined 2150 // the comment leader correctly. 2151 2152 while (ascii_iswhite(*com_leader)) { 2153 com_leader++; 2154 } 2155 int len1 = (int)strlen(com_leader); 2156 2157 for (char *list = curbuf->b_p_com; *list;) { 2158 char *flags_save = list; 2159 2160 copy_option_part(&list, part_buf2, COM_MAX_LEN, ","); 2161 if (flags_save == com_flags) { 2162 continue; 2163 } 2164 char *string = vim_strchr(part_buf2, ':'); 2165 string++; 2166 while (ascii_iswhite(*string)) { 2167 string++; 2168 } 2169 int len2 = (int)strlen(string); 2170 if (len2 == 0) { 2171 continue; 2172 } 2173 2174 // Now we have to verify whether string ends with a substring 2175 // beginning the com_leader. 2176 for (int off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) { 2177 off--; 2178 if (!strncmp(string + off, com_leader, (size_t)(len2 - off))) { 2179 lower_check_bound = MIN(lower_check_bound, i - off); 2180 } 2181 } 2182 } 2183 } 2184 } 2185 return result; 2186 }