1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/// change.c: functions related to changing text
5
6#include "nvim/assert.h"
7#include "nvim/buffer.h"
8#include "nvim/buffer_updates.h"
9#include "nvim/change.h"
10#include "nvim/charset.h"
11#include "nvim/cursor.h"
12#include "nvim/diff.h"
13#include "nvim/edit.h"
14#include "nvim/eval.h"
15#include "nvim/fileio.h"
16#include "nvim/fold.h"
17#include "nvim/indent.h"
18#include "nvim/indent_c.h"
19#include "nvim/mark.h"
20#include "nvim/memline.h"
21#include "nvim/misc1.h"
22#include "nvim/move.h"
23#include "nvim/option.h"
24#include "nvim/screen.h"
25#include "nvim/search.h"
26#include "nvim/state.h"
27#include "nvim/ui.h"
28#include "nvim/undo.h"
29
30#ifdef INCLUDE_GENERATED_DECLARATIONS
31# include "change.c.generated.h"
32#endif
33
34/// If the file is readonly, give a warning message with the first change.
35/// Don't do this for autocommands.
36/// Doesn't use emsg(), because it flushes the macro buffer.
37/// If we have undone all changes b_changed will be false, but "b_did_warn"
38/// will be true.
39/// "col" is the column for the message; non-zero when in insert mode and
40/// 'showmode' is on.
41/// Careful: may trigger autocommands that reload the buffer.
42void change_warning(int col)
43{
44 static char *w_readonly = N_("W10: Warning: Changing a readonly file");
45
46 if (curbuf->b_did_warn == false
47 && curbufIsChanged() == 0
48 && !autocmd_busy
49 && curbuf->b_p_ro) {
50 curbuf_lock++;
51 apply_autocmds(EVENT_FILECHANGEDRO, NULL, NULL, false, curbuf);
52 curbuf_lock--;
53 if (!curbuf->b_p_ro) {
54 return;
55 }
56 // Do what msg() does, but with a column offset if the warning should
57 // be after the mode message.
58 msg_start();
59 if (msg_row == Rows - 1) {
60 msg_col = col;
61 }
62 msg_source(HL_ATTR(HLF_W));
63 msg_ext_set_kind("wmsg");
64 MSG_PUTS_ATTR(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST);
65 set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
66 msg_clr_eos();
67 (void)msg_end();
68 if (msg_silent == 0 && !silent_mode && ui_active()) {
69 ui_flush();
70 os_delay(1000L, true); // give the user time to think about it
71 }
72 curbuf->b_did_warn = true;
73 redraw_cmdline = false; // don't redraw and erase the message
74 if (msg_row < Rows - 1) {
75 showmode();
76 }
77 }
78}
79
80/// Call this function when something in the current buffer is changed.
81///
82/// Most often called through changed_bytes() and changed_lines(), which also
83/// mark the area of the display to be redrawn.
84///
85/// Careful: may trigger autocommands that reload the buffer.
86void changed(void)
87{
88 if (!curbuf->b_changed) {
89 int save_msg_scroll = msg_scroll;
90
91 // Give a warning about changing a read-only file. This may also
92 // check-out the file, thus change "curbuf"!
93 change_warning(0);
94
95 // Create a swap file if that is wanted.
96 // Don't do this for "nofile" and "nowrite" buffer types.
97 if (curbuf->b_may_swap
98 && !bt_dontwrite(curbuf)
99 ) {
100 int save_need_wait_return = need_wait_return;
101
102 need_wait_return = false;
103 ml_open_file(curbuf);
104
105 // The ml_open_file() can cause an ATTENTION message.
106 // Wait two seconds, to make sure the user reads this unexpected
107 // message. Since we could be anywhere, call wait_return() now,
108 // and don't let the emsg() set msg_scroll.
109 if (need_wait_return && emsg_silent == 0) {
110 ui_flush();
111 os_delay(2000L, true);
112 wait_return(true);
113 msg_scroll = save_msg_scroll;
114 } else {
115 need_wait_return = save_need_wait_return;
116 }
117 }
118 changed_internal();
119 }
120 buf_inc_changedtick(curbuf);
121
122 // If a pattern is highlighted, the position may now be invalid.
123 highlight_match = false;
124}
125
126/// Internal part of changed(), no user interaction.
127/// Also used for recovery.
128void changed_internal(void)
129{
130 curbuf->b_changed = true;
131 ml_setflags(curbuf);
132 check_status(curbuf);
133 redraw_tabline = true;
134 need_maketitle = true; // set window title later
135}
136
137/// Common code for when a change was made.
138/// See changed_lines() for the arguments.
139/// Careful: may trigger autocommands that reload the buffer.
140static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume,
141 long xtra)
142{
143 int i;
144 int cols;
145 pos_T *p;
146 int add;
147
148 // mark the buffer as modified
149 changed();
150
151 if (curwin->w_p_diff && diff_internal()) {
152 curtab->tp_diff_update = true;
153 }
154
155 // set the '. mark
156 if (!cmdmod.keepjumps) {
157 RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), 0);
158
159 // Create a new entry if a new undo-able change was started or we
160 // don't have an entry yet.
161 if (curbuf->b_new_change || curbuf->b_changelistlen == 0) {
162 if (curbuf->b_changelistlen == 0) {
163 add = true;
164 } else {
165 // Don't create a new entry when the line number is the same
166 // as the last one and the column is not too far away. Avoids
167 // creating many entries for typing "xxxxx".
168 p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark;
169 if (p->lnum != lnum) {
170 add = true;
171 } else {
172 cols = comp_textwidth(false);
173 if (cols == 0) {
174 cols = 79;
175 }
176 add = (p->col + cols < col || col + cols < p->col);
177 }
178 }
179 if (add) {
180 // This is the first of a new sequence of undo-able changes
181 // and it's at some distance of the last change. Use a new
182 // position in the changelist.
183 curbuf->b_new_change = false;
184
185 if (curbuf->b_changelistlen == JUMPLISTSIZE) {
186 // changelist is full: remove oldest entry
187 curbuf->b_changelistlen = JUMPLISTSIZE - 1;
188 memmove(curbuf->b_changelist, curbuf->b_changelist + 1,
189 sizeof(curbuf->b_changelist[0]) * (JUMPLISTSIZE - 1));
190 FOR_ALL_TAB_WINDOWS(tp, wp) {
191 // Correct position in changelist for other windows on
192 // this buffer.
193 if (wp->w_buffer == curbuf && wp->w_changelistidx > 0) {
194 wp->w_changelistidx--;
195 }
196 }
197 }
198 FOR_ALL_TAB_WINDOWS(tp, wp) {
199 // For other windows, if the position in the changelist is
200 // at the end it stays at the end.
201 if (wp->w_buffer == curbuf
202 && wp->w_changelistidx == curbuf->b_changelistlen) {
203 wp->w_changelistidx++;
204 }
205 }
206 curbuf->b_changelistlen++;
207 }
208 }
209 curbuf->b_changelist[curbuf->b_changelistlen - 1] =
210 curbuf->b_last_change;
211 // The current window is always after the last change, so that "g,"
212 // takes you back to it.
213 curwin->w_changelistidx = curbuf->b_changelistlen;
214 }
215
216 FOR_ALL_TAB_WINDOWS(tp, wp) {
217 if (wp->w_buffer == curbuf) {
218 // Mark this window to be redrawn later.
219 if (wp->w_redr_type < VALID) {
220 wp->w_redr_type = VALID;
221 }
222
223 // Check if a change in the buffer has invalidated the cached
224 // values for the cursor.
225 // Update the folds for this window. Can't postpone this, because
226 // a following operator might work on the whole fold: ">>dd".
227 foldUpdate(wp, lnum, lnume + xtra - 1);
228
229 // The change may cause lines above or below the change to become
230 // included in a fold. Set lnum/lnume to the first/last line that
231 // might be displayed differently.
232 // Set w_cline_folded here as an efficient way to update it when
233 // inserting lines just above a closed fold. */
234 bool folded = hasFoldingWin(wp, lnum, &lnum, NULL, false, NULL);
235 if (wp->w_cursor.lnum == lnum) {
236 wp->w_cline_folded = folded;
237 }
238 folded = hasFoldingWin(wp, lnume, NULL, &lnume, false, NULL);
239 if (wp->w_cursor.lnum == lnume) {
240 wp->w_cline_folded = folded;
241 }
242
243 // If the changed line is in a range of previously folded lines,
244 // compare with the first line in that range.
245 if (wp->w_cursor.lnum <= lnum) {
246 i = find_wl_entry(wp, lnum);
247 if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) {
248 changed_line_abv_curs_win(wp);
249 }
250 }
251
252 if (wp->w_cursor.lnum > lnum) {
253 changed_line_abv_curs_win(wp);
254 } else if (wp->w_cursor.lnum == lnum && wp->w_cursor.col >= col) {
255 changed_cline_bef_curs_win(wp);
256 }
257 if (wp->w_botline >= lnum) {
258 // Assume that botline doesn't change (inserted lines make
259 // other lines scroll down below botline).
260 approximate_botline_win(wp);
261 }
262
263 // Check if any w_lines[] entries have become invalid.
264 // For entries below the change: Correct the lnums for
265 // inserted/deleted lines. Makes it possible to stop displaying
266 // after the change.
267 for (i = 0; i < wp->w_lines_valid; i++) {
268 if (wp->w_lines[i].wl_valid) {
269 if (wp->w_lines[i].wl_lnum >= lnum) {
270 if (wp->w_lines[i].wl_lnum < lnume) {
271 // line included in change
272 wp->w_lines[i].wl_valid = false;
273 } else if (xtra != 0) {
274 // line below change
275 wp->w_lines[i].wl_lnum += xtra;
276 wp->w_lines[i].wl_lastlnum += xtra;
277 }
278 } else if (wp->w_lines[i].wl_lastlnum >= lnum) {
279 // change somewhere inside this range of folded lines,
280 // may need to be redrawn
281 wp->w_lines[i].wl_valid = false;
282 }
283 }
284 }
285
286 // Take care of side effects for setting w_topline when folds have
287 // changed. Esp. when the buffer was changed in another window.
288 if (hasAnyFolding(wp)) {
289 set_topline(wp, wp->w_topline);
290 }
291
292 // Relative numbering may require updating more. Cursor line
293 // highlighting probably needs to be updated if it's below the
294 // change.
295 if (wp->w_p_rnu
296 || (wp->w_p_cul && lnum <= wp->w_last_cursorline)) {
297 redraw_win_later(wp, SOME_VALID);
298 }
299 }
300 }
301
302 // Call update_screen() later, which checks out what needs to be redrawn,
303 // since it notices b_mod_set and then uses b_mod_*.
304 if (must_redraw < VALID) {
305 must_redraw = VALID;
306 }
307
308 // when the cursor line is changed always trigger CursorMoved
309 if (lnum <= curwin->w_cursor.lnum
310 && lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum) {
311 curwin->w_last_cursormoved.lnum = 0;
312 }
313}
314
315static void changedOneline(buf_T *buf, linenr_T lnum)
316{
317 if (buf->b_mod_set) {
318 // find the maximum area that must be redisplayed
319 if (lnum < buf->b_mod_top) {
320 buf->b_mod_top = lnum;
321 } else if (lnum >= buf->b_mod_bot) {
322 buf->b_mod_bot = lnum + 1;
323 }
324 } else {
325 // set the area that must be redisplayed to one line
326 buf->b_mod_set = true;
327 buf->b_mod_top = lnum;
328 buf->b_mod_bot = lnum + 1;
329 buf->b_mod_xlines = 0;
330 }
331}
332
333/// Changed bytes within a single line for the current buffer.
334/// - marks the windows on this buffer to be redisplayed
335/// - marks the buffer changed by calling changed()
336/// - invalidates cached values
337/// Careful: may trigger autocommands that reload the buffer.
338void changed_bytes(linenr_T lnum, colnr_T col)
339{
340 changedOneline(curbuf, lnum);
341 changed_common(lnum, col, lnum + 1, 0L);
342 // notify any channels that are watching
343 buf_updates_send_changes(curbuf, lnum, 1, 1, true);
344
345 // Diff highlighting in other diff windows may need to be updated too.
346 if (curwin->w_p_diff) {
347 linenr_T wlnum;
348
349 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
350 if (wp->w_p_diff && wp != curwin) {
351 redraw_win_later(wp, VALID);
352 wlnum = diff_lnum_win(lnum, wp);
353 if (wlnum > 0) {
354 changedOneline(wp->w_buffer, wlnum);
355 }
356 }
357 }
358 }
359}
360
361/// Appended "count" lines below line "lnum" in the current buffer.
362/// Must be called AFTER the change and after mark_adjust().
363/// Takes care of marking the buffer to be redrawn and sets the changed flag.
364void appended_lines(linenr_T lnum, long count)
365{
366 changed_lines(lnum + 1, 0, lnum + 1, count, true);
367}
368
369/// Like appended_lines(), but adjust marks first.
370void appended_lines_mark(linenr_T lnum, long count)
371{
372 // Skip mark_adjust when adding a line after the last one, there can't
373 // be marks there. But it's still needed in diff mode.
374 if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
375 mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false);
376 }
377 changed_lines(lnum + 1, 0, lnum + 1, count, true);
378}
379
380/// Deleted "count" lines at line "lnum" in the current buffer.
381/// Must be called AFTER the change and after mark_adjust().
382/// Takes care of marking the buffer to be redrawn and sets the changed flag.
383void deleted_lines(linenr_T lnum, long count)
384{
385 changed_lines(lnum, 0, lnum + count, -count, true);
386}
387
388/// Like deleted_lines(), but adjust marks first.
389/// Make sure the cursor is on a valid line before calling, a GUI callback may
390/// be triggered to display the cursor.
391void deleted_lines_mark(linenr_T lnum, long count)
392{
393 mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, -count, false);
394 changed_lines(lnum, 0, lnum + count, -count, true);
395}
396
397/// Marks the area to be redrawn after a change.
398///
399/// @param buf the buffer where lines were changed
400/// @param lnum first line with change
401/// @param lnume line below last changed line
402/// @param xtra number of extra lines (negative when deleting)
403void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra)
404{
405 if (buf->b_mod_set) {
406 // find the maximum area that must be redisplayed
407 if (lnum < buf->b_mod_top) {
408 buf->b_mod_top = lnum;
409 }
410 if (lnum < buf->b_mod_bot) {
411 // adjust old bot position for xtra lines
412 buf->b_mod_bot += xtra;
413 if (buf->b_mod_bot < lnum) {
414 buf->b_mod_bot = lnum;
415 }
416 }
417 if (lnume + xtra > buf->b_mod_bot) {
418 buf->b_mod_bot = lnume + xtra;
419 }
420 buf->b_mod_xlines += xtra;
421 } else {
422 // set the area that must be redisplayed
423 buf->b_mod_set = true;
424 buf->b_mod_top = lnum;
425 buf->b_mod_bot = lnume + xtra;
426 buf->b_mod_xlines = xtra;
427 }
428}
429
430/// Changed lines for the current buffer.
431/// Must be called AFTER the change and after mark_adjust().
432/// - mark the buffer changed by calling changed()
433/// - mark the windows on this buffer to be redisplayed
434/// - invalidate cached values
435/// "lnum" is the first line that needs displaying, "lnume" the first line
436/// below the changed lines (BEFORE the change).
437/// When only inserting lines, "lnum" and "lnume" are equal.
438/// Takes care of calling changed() and updating b_mod_*.
439/// Careful: may trigger autocommands that reload the buffer.
440void
441changed_lines(
442 linenr_T lnum, // first line with change
443 colnr_T col, // column in first line with change
444 linenr_T lnume, // line below last changed line
445 long xtra, // number of extra lines (negative when deleting)
446 bool do_buf_event // some callers like undo/redo call changed_lines()
447 // and then increment changedtick *again*. This flag
448 // allows these callers to send the nvim_buf_lines_event
449 // events after they're done modifying changedtick.
450)
451{
452 changed_lines_buf(curbuf, lnum, lnume, xtra);
453
454 if (xtra == 0 && curwin->w_p_diff && !diff_internal()) {
455 // When the number of lines doesn't change then mark_adjust() isn't
456 // called and other diff buffers still need to be marked for
457 // displaying.
458 linenr_T wlnum;
459
460 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
461 if (wp->w_p_diff && wp != curwin) {
462 redraw_win_later(wp, VALID);
463 wlnum = diff_lnum_win(lnum, wp);
464 if (wlnum > 0) {
465 changed_lines_buf(wp->w_buffer, wlnum,
466 lnume - lnum + wlnum, 0L);
467 }
468 }
469 }
470 }
471
472 changed_common(lnum, col, lnume, xtra);
473
474 if (do_buf_event) {
475 int64_t num_added = (int64_t)(lnume + xtra - lnum);
476 int64_t num_removed = lnume - lnum;
477 buf_updates_send_changes(curbuf, lnum, num_added, num_removed, true);
478 }
479}
480
481/// Called when the changed flag must be reset for buffer `buf`.
482/// When `ff` is true also reset 'fileformat'.
483/// When `always_inc_changedtick` is true b:changedtick is incremented even
484/// when the changed flag was off.
485void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
486{
487 if (buf->b_changed || (ff && file_ff_differs(buf, false))) {
488 buf->b_changed = false;
489 ml_setflags(buf);
490 if (ff) {
491 save_file_ff(buf);
492 }
493 check_status(buf);
494 redraw_tabline = true;
495 need_maketitle = true; // set window title later
496 buf_inc_changedtick(buf);
497 } else if (always_inc_changedtick) {
498 buf_inc_changedtick(buf);
499 }
500}
501
502/// Insert string "p" at the cursor position. Stops at a NUL byte.
503/// Handles Replace mode and multi-byte characters.
504void ins_bytes(char_u *p)
505{
506 ins_bytes_len(p, STRLEN(p));
507}
508
509/// Insert string "p" with length "len" at the cursor position.
510/// Handles Replace mode and multi-byte characters.
511void ins_bytes_len(char_u *p, size_t len)
512{
513 size_t n;
514 for (size_t i = 0; i < len; i += n) {
515 if (enc_utf8) {
516 // avoid reading past p[len]
517 n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i));
518 } else {
519 n = (size_t)(*mb_ptr2len)(p + i);
520 }
521 ins_char_bytes(p + i, n);
522 }
523}
524
525/// Insert or replace a single character at the cursor position.
526/// When in REPLACE or VREPLACE mode, replace any existing character.
527/// Caller must have prepared for undo.
528/// For multi-byte characters we get the whole character, the caller must
529/// convert bytes to a character.
530void ins_char(int c)
531{
532 char_u buf[MB_MAXBYTES + 1];
533 size_t n = (size_t)utf_char2bytes(c, buf);
534
535 // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
536 // Happens for CTRL-Vu9900.
537 if (buf[0] == 0) {
538 buf[0] = '\n';
539 }
540 ins_char_bytes(buf, n);
541}
542
543void ins_char_bytes(char_u *buf, size_t charlen)
544{
545 // Break tabs if needed.
546 if (virtual_active() && curwin->w_cursor.coladd > 0) {
547 coladvance_force(getviscol());
548 }
549
550 size_t col = (size_t)curwin->w_cursor.col;
551 linenr_T lnum = curwin->w_cursor.lnum;
552 char_u *oldp = ml_get(lnum);
553 size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL
554
555 // The lengths default to the values for when not replacing.
556 size_t oldlen = 0; // nr of bytes inserted
557 size_t newlen = charlen; // nr of bytes deleted (0 when not replacing)
558
559 if (State & REPLACE_FLAG) {
560 if (State & VREPLACE_FLAG) {
561 // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag.
562 // Returns the old value of list, so when finished,
563 // curwin->w_p_list should be set back to this.
564 int old_list = curwin->w_p_list;
565 if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
566 curwin->w_p_list = false;
567 }
568 // In virtual replace mode each character may replace one or more
569 // characters (zero if it's a TAB). Count the number of bytes to
570 // be deleted to make room for the new character, counting screen
571 // cells. May result in adding spaces to fill a gap.
572 colnr_T vcol;
573 getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
574 colnr_T new_vcol = vcol + chartabsize(buf, vcol);
575 while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
576 vcol += chartabsize(oldp + col + oldlen, vcol);
577 // Don't need to remove a TAB that takes us to the right
578 // position.
579 if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
580 break;
581 }
582 oldlen += (size_t)(*mb_ptr2len)(oldp + col + oldlen);
583 // Deleted a bit too much, insert spaces.
584 if (vcol > new_vcol) {
585 newlen += (size_t)(vcol - new_vcol);
586 }
587 }
588 curwin->w_p_list = old_list;
589 } else if (oldp[col] != NUL) {
590 // normal replace
591 oldlen = (size_t)(*mb_ptr2len)(oldp + col);
592 }
593
594
595 // Push the replaced bytes onto the replace stack, so that they can be
596 // put back when BS is used. The bytes of a multi-byte character are
597 // done the other way around, so that the first byte is popped off
598 // first (it tells the byte length of the character).
599 replace_push(NUL);
600 for (size_t i = 0; i < oldlen; i++) {
601 i += (size_t)replace_push_mb(oldp + col + i) - 1;
602 }
603 }
604
605 char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen));
606
607 // Copy bytes before the cursor.
608 if (col > 0) {
609 memmove(newp, oldp, (size_t)col);
610 }
611
612 // Copy bytes after the changed character(s).
613 char_u *p = newp + col;
614 if (linelen > col + oldlen) {
615 memmove(p + newlen, oldp + col + oldlen,
616 (size_t)(linelen - col - oldlen));
617 }
618
619 // Insert or overwrite the new character.
620 memmove(p, buf, charlen);
621
622 // Fill with spaces when necessary.
623 for (size_t i = charlen; i < newlen; i++) {
624 p[i] = ' ';
625 }
626
627 // Replace the line in the buffer.
628 ml_replace(lnum, newp, false);
629
630 // mark the buffer as changed and prepare for displaying
631 changed_bytes(lnum, (colnr_T)col);
632
633 // If we're in Insert or Replace mode and 'showmatch' is set, then briefly
634 // show the match for right parens and braces.
635 if (p_sm && (State & INSERT)
636 && msg_silent == 0
637 && !ins_compl_active()
638 ) {
639 showmatch(utf_ptr2char(buf));
640 }
641
642 if (!p_ri || (State & REPLACE_FLAG)) {
643 // Normal insert: move cursor right
644 curwin->w_cursor.col += (int)charlen;
645 }
646 // TODO(Bram): should try to update w_row here, to avoid recomputing it later.
647}
648
649/// Insert a string at the cursor position.
650/// Note: Does NOT handle Replace mode.
651/// Caller must have prepared for undo.
652void ins_str(char_u *s)
653{
654 char_u *oldp, *newp;
655 int newlen = (int)STRLEN(s);
656 int oldlen;
657 colnr_T col;
658 linenr_T lnum = curwin->w_cursor.lnum;
659
660 if (virtual_active() && curwin->w_cursor.coladd > 0) {
661 coladvance_force(getviscol());
662 }
663
664 col = curwin->w_cursor.col;
665 oldp = ml_get(lnum);
666 oldlen = (int)STRLEN(oldp);
667
668 newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1);
669 if (col > 0) {
670 memmove(newp, oldp, (size_t)col);
671 }
672 memmove(newp + col, s, (size_t)newlen);
673 int bytes = oldlen - col + 1;
674 assert(bytes >= 0);
675 memmove(newp + col + newlen, oldp + col, (size_t)bytes);
676 ml_replace(lnum, newp, false);
677 changed_bytes(lnum, col);
678 curwin->w_cursor.col += newlen;
679}
680
681// Delete one character under the cursor.
682// If "fixpos" is true, don't leave the cursor on the NUL after the line.
683// Caller must have prepared for undo.
684//
685// return FAIL for failure, OK otherwise
686int del_char(bool fixpos)
687{
688 // Make sure the cursor is at the start of a character.
689 mb_adjust_cursor();
690 if (*get_cursor_pos_ptr() == NUL) {
691 return FAIL;
692 }
693 return del_chars(1L, fixpos);
694}
695
696/// Like del_bytes(), but delete characters instead of bytes.
697int del_chars(long count, int fixpos)
698{
699 int bytes = 0;
700 long i;
701 char_u *p;
702 int l;
703
704 p = get_cursor_pos_ptr();
705 for (i = 0; i < count && *p != NUL; i++) {
706 l = (*mb_ptr2len)(p);
707 bytes += l;
708 p += l;
709 }
710 return del_bytes(bytes, fixpos, true);
711}
712
713/// Delete "count" bytes under the cursor.
714/// If "fixpos" is true, don't leave the cursor on the NUL after the line.
715/// Caller must have prepared for undo.
716///
717/// @param count number of bytes to be deleted
718/// @param fixpos_arg leave the cursor on the NUL after the line
719/// @param use_delcombine 'delcombine' option applies
720///
721/// @return FAIL for failure, OK otherwise
722int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
723{
724 linenr_T lnum = curwin->w_cursor.lnum;
725 colnr_T col = curwin->w_cursor.col;
726 bool fixpos = fixpos_arg;
727 char_u *oldp = ml_get(lnum);
728 colnr_T oldlen = (colnr_T)STRLEN(oldp);
729
730 // Can't do anything when the cursor is on the NUL after the line.
731 if (col >= oldlen) {
732 return FAIL;
733 }
734 // If "count" is zero there is nothing to do.
735 if (count == 0) {
736 return OK;
737 }
738 // If "count" is negative the caller must be doing something wrong.
739 if (count < 1) {
740 IEMSGN("E950: Invalid count for del_bytes(): %ld", count);
741 return FAIL;
742 }
743
744 // If 'delcombine' is set and deleting (less than) one character, only
745 // delete the last combining character.
746 if (p_deco && use_delcombine && enc_utf8
747 && utfc_ptr2len(oldp + col) >= count) {
748 int cc[MAX_MCO];
749 int n;
750
751 (void)utfc_ptr2char(oldp + col, cc);
752 if (cc[0] != NUL) {
753 // Find the last composing char, there can be several.
754 n = col;
755 do {
756 col = n;
757 count = utf_ptr2len(oldp + n);
758 n += count;
759 } while (UTF_COMPOSINGLIKE(oldp + col, oldp + n));
760 fixpos = false;
761 }
762 }
763
764 // When count is too big, reduce it.
765 int movelen = oldlen - col - count + 1; // includes trailing NUL
766 if (movelen <= 1) {
767 // If we just took off the last character of a non-blank line, and
768 // fixpos is TRUE, we don't want to end up positioned at the NUL,
769 // unless "restart_edit" is set or 'virtualedit' contains "onemore".
770 if (col > 0 && fixpos && restart_edit == 0
771 && (ve_flags & VE_ONEMORE) == 0
772 ) {
773 curwin->w_cursor.col--;
774 curwin->w_cursor.coladd = 0;
775 curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
776 }
777 count = oldlen - col;
778 movelen = 1;
779 }
780
781 // If the old line has been allocated the deletion can be done in the
782 // existing line. Otherwise a new line has to be allocated.
783 bool was_alloced = ml_line_alloced(); // check if oldp was allocated
784 char_u *newp;
785 if (was_alloced) {
786 ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen);
787 newp = oldp; // use same allocated memory
788 } else { // need to allocate a new line
789 newp = xmalloc((size_t)(oldlen + 1 - count));
790 memmove(newp, oldp, (size_t)col);
791 }
792 memmove(newp + col, oldp + col + count, (size_t)movelen);
793 if (!was_alloced) {
794 ml_replace(lnum, newp, false);
795 }
796
797 // mark the buffer as changed and prepare for displaying
798 changed_bytes(lnum, curwin->w_cursor.col);
799
800 return OK;
801}
802
803/// Copy the indent from ptr to the current line (and fill to size).
804/// Leaves the cursor on the first non-blank in the line.
805/// @return true if the line was changed.
806int copy_indent(int size, char_u *src)
807{
808 char_u *p = NULL;
809 char_u *line = NULL;
810 char_u *s;
811 int todo;
812 int ind_len;
813 int line_len = 0;
814 int tab_pad;
815 int ind_done;
816 int round;
817
818 // Round 1: compute the number of characters needed for the indent
819 // Round 2: copy the characters.
820 for (round = 1; round <= 2; round++) {
821 todo = size;
822 ind_len = 0;
823 ind_done = 0;
824 s = src;
825
826 // Count/copy the usable portion of the source line.
827 while (todo > 0 && ascii_iswhite(*s)) {
828 if (*s == TAB) {
829 tab_pad = (int)curbuf->b_p_ts
830 - (ind_done % (int)curbuf->b_p_ts);
831
832 // Stop if this tab will overshoot the target.
833 if (todo < tab_pad) {
834 break;
835 }
836 todo -= tab_pad;
837 ind_done += tab_pad;
838 } else {
839 todo--;
840 ind_done++;
841 }
842 ind_len++;
843
844 if (p != NULL) {
845 *p++ = *s;
846 }
847 s++;
848 }
849
850 // Fill to next tabstop with a tab, if possible.
851 tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
852
853 if ((todo >= tab_pad) && !curbuf->b_p_et) {
854 todo -= tab_pad;
855 ind_len++;
856
857 if (p != NULL) {
858 *p++ = TAB;
859 }
860 }
861
862 // Add tabs required for indent.
863 while (todo >= (int)curbuf->b_p_ts && !curbuf->b_p_et) {
864 todo -= (int)curbuf->b_p_ts;
865 ind_len++;
866
867 if (p != NULL) {
868 *p++ = TAB;
869 }
870 }
871
872 // Count/add spaces required for indent.
873 while (todo > 0) {
874 todo--;
875 ind_len++;
876
877 if (p != NULL) {
878 *p++ = ' ';
879 }
880 }
881
882 if (p == NULL) {
883 // Allocate memory for the result: the copied indent, new indent
884 // and the rest of the line.
885 line_len = (int)STRLEN(get_cursor_line_ptr()) + 1;
886 assert(ind_len + line_len >= 0);
887 size_t line_size;
888 STRICT_ADD(ind_len, line_len, &line_size, size_t);
889 line = xmalloc(line_size);
890 p = line;
891 }
892 }
893
894 // Append the original line
895 memmove(p, get_cursor_line_ptr(), (size_t)line_len);
896
897 // Replace the line
898 ml_replace(curwin->w_cursor.lnum, line, false);
899
900 // Put the cursor after the indent.
901 curwin->w_cursor.col = ind_len;
902 return true;
903}
904
905/// open_line: Add a new line below or above the current line.
906///
907/// For VREPLACE mode, we only add a new line when we get to the end of the
908/// file, otherwise we just start replacing the next line.
909///
910/// Caller must take care of undo. Since VREPLACE may affect any number of
911/// lines however, it may call u_save_cursor() again when starting to change a
912/// new line.
913/// "flags": OPENLINE_DELSPACES delete spaces after cursor
914/// OPENLINE_DO_COM format comments
915/// OPENLINE_KEEPTRAIL keep trailing spaces
916/// OPENLINE_MARKFIX adjust mark positions after the line break
917/// OPENLINE_COM_LIST format comments with list or 2nd line indent
918///
919/// "second_line_indent": indent for after ^^D in Insert mode or if flag
920/// OPENLINE_COM_LIST
921///
922/// @return true on success, false on failure
923int open_line(
924 int dir, // FORWARD or BACKWARD
925 int flags,
926 int second_line_indent
927)
928{
929 char_u *next_line = NULL; // copy of the next line
930 char_u *p_extra = NULL; // what goes to next line
931 colnr_T less_cols = 0; // less columns for mark in new line
932 colnr_T less_cols_off = 0; // columns to skip for mark adjust
933 pos_T old_cursor; // old cursor position
934 colnr_T newcol = 0; // new cursor column
935 int newindent = 0; // auto-indent of the new line
936 bool trunc_line = false; // truncate current line afterwards
937 bool retval = false; // return value
938 int extra_len = 0; // length of p_extra string
939 int lead_len; // length of comment leader
940 char_u *lead_flags; // position in 'comments' for comment leader
941 char_u *leader = NULL; // copy of comment leader
942 char_u *allocated = NULL; // allocated memory
943 char_u *p;
944 char_u saved_char = NUL; // init for GCC
945 pos_T *pos;
946 bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin
947 && *curbuf->b_p_inde == NUL);
948 bool no_si = false; // reset did_si afterwards
949 int first_char = NUL; // init for GCC
950 int vreplace_mode;
951 bool did_append; // appended a new line
952 int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting
953
954 // make a copy of the current line so we can mess with it
955 char_u *saved_line = vim_strsave(get_cursor_line_ptr());
956
957 if (State & VREPLACE_FLAG) {
958 // With VREPLACE we make a copy of the next line, which we will be
959 // starting to replace. First make the new line empty and let vim play
960 // with the indenting and comment leader to its heart's content. Then
961 // we grab what it ended up putting on the new line, put back the
962 // original line, and call ins_char() to put each new character onto
963 // the line, replacing what was there before and pushing the right
964 // stuff onto the replace stack. -- webb.
965 if (curwin->w_cursor.lnum < orig_line_count) {
966 next_line = vim_strsave(ml_get(curwin->w_cursor.lnum + 1));
967 } else {
968 next_line = vim_strsave((char_u *)"");
969 }
970
971 // In VREPLACE mode, a NL replaces the rest of the line, and starts
972 // replacing the next line, so push all of the characters left on the
973 // line onto the replace stack. We'll push any other characters that
974 // might be replaced at the start of the next line (due to autoindent
975 // etc) a bit later.
976 replace_push(NUL); // Call twice because BS over NL expects it
977 replace_push(NUL);
978 p = saved_line + curwin->w_cursor.col;
979 while (*p != NUL) {
980 p += replace_push_mb(p);
981 }
982 saved_line[curwin->w_cursor.col] = NUL;
983 }
984
985 if ((State & INSERT)
986 && !(State & VREPLACE_FLAG)
987 ) {
988 p_extra = saved_line + curwin->w_cursor.col;
989 if (do_si) { // need first char after new line break
990 p = skipwhite(p_extra);
991 first_char = *p;
992 }
993 extra_len = (int)STRLEN(p_extra);
994 saved_char = *p_extra;
995 *p_extra = NUL;
996 }
997
998 u_clearline(); // cannot do "U" command when adding lines
999 did_si = false;
1000 ai_col = 0;
1001
1002 // If we just did an auto-indent, then we didn't type anything on
1003 // the prior line, and it should be truncated. Do this even if 'ai' is not
1004 // set because automatically inserting a comment leader also sets did_ai.
1005 if (dir == FORWARD && did_ai) {
1006 trunc_line = true;
1007 }
1008
1009 // If 'autoindent' and/or 'smartindent' is set, try to figure out what
1010 // indent to use for the new line.
1011 if (curbuf->b_p_ai
1012 || do_si
1013 ) {
1014 // count white space on current line
1015 newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, false);
1016 if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
1017 newindent = second_line_indent; // for ^^D command in insert mode
1018 }
1019
1020 // Do smart indenting.
1021 // In insert/replace mode (only when dir == FORWARD)
1022 // we may move some text to the next line. If it starts with '{'
1023 // don't add an indent. Fixes inserting a NL before '{' in line
1024 // "if (condition) {"
1025 if (!trunc_line && do_si && *saved_line != NUL
1026 && (p_extra == NULL || first_char != '{')) {
1027 char_u *ptr;
1028 char_u last_char;
1029
1030 old_cursor = curwin->w_cursor;
1031 ptr = saved_line;
1032 if (flags & OPENLINE_DO_COM) {
1033 lead_len = get_leader_len(ptr, NULL, false, true);
1034 } else {
1035 lead_len = 0;
1036 }
1037 if (dir == FORWARD) {
1038 // Skip preprocessor directives, unless they are
1039 // recognised as comments.
1040 if (lead_len == 0 && ptr[0] == '#') {
1041 while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) {
1042 ptr = ml_get(--curwin->w_cursor.lnum);
1043 }
1044 newindent = get_indent();
1045 }
1046 if (flags & OPENLINE_DO_COM) {
1047 lead_len = get_leader_len(ptr, NULL, false, true);
1048 } else {
1049 lead_len = 0;
1050 }
1051 if (lead_len > 0) {
1052 // This case gets the following right:
1053 // \*
1054 // * A comment (read '\' as '/').
1055 // */
1056 // #define IN_THE_WAY
1057 // This should line up here;
1058 p = skipwhite(ptr);
1059 if (p[0] == '/' && p[1] == '*') {
1060 p++;
1061 }
1062 if (p[0] == '*') {
1063 for (p++; *p; p++) {
1064 if (p[0] == '/' && p[-1] == '*') {
1065 // End of C comment, indent should line up
1066 // with the line containing the start of
1067 // the comment
1068 curwin->w_cursor.col = (colnr_T)(p - ptr);
1069 if ((pos = findmatch(NULL, NUL)) != NULL) {
1070 curwin->w_cursor.lnum = pos->lnum;
1071 newindent = get_indent();
1072 }
1073 }
1074 }
1075 }
1076 } else { // Not a comment line
1077 // Find last non-blank in line
1078 p = ptr + STRLEN(ptr) - 1;
1079 while (p > ptr && ascii_iswhite(*p)) {
1080 p--;
1081 }
1082 last_char = *p;
1083
1084 // find the character just before the '{' or ';'
1085 if (last_char == '{' || last_char == ';') {
1086 if (p > ptr) {
1087 p--;
1088 }
1089 while (p > ptr && ascii_iswhite(*p)) {
1090 p--;
1091 }
1092 }
1093 // Try to catch lines that are split over multiple
1094 // lines. eg:
1095 // if (condition &&
1096 // condition) {
1097 // Should line up here!
1098 // }
1099 if (*p == ')') {
1100 curwin->w_cursor.col = (colnr_T)(p - ptr);
1101 if ((pos = findmatch(NULL, '(')) != NULL) {
1102 curwin->w_cursor.lnum = pos->lnum;
1103 newindent = get_indent();
1104 ptr = get_cursor_line_ptr();
1105 }
1106 }
1107 // If last character is '{' do indent, without
1108 // checking for "if" and the like.
1109 if (last_char == '{') {
1110 did_si = true; // do indent
1111 no_si = true; // don't delete it when '{' typed
1112 // Look for "if" and the like, use 'cinwords'.
1113 // Don't do this if the previous line ended in ';' or
1114 // '}'.
1115 } else if (last_char != ';' && last_char != '}'
1116 && cin_is_cinword(ptr)) {
1117 did_si = true;
1118 }
1119 }
1120 } else { // dir == BACKWARD
1121 // Skip preprocessor directives, unless they are
1122 // recognised as comments.
1123 if (lead_len == 0 && ptr[0] == '#') {
1124 bool was_backslashed = false;
1125
1126 while ((ptr[0] == '#' || was_backslashed)
1127 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
1128 if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') {
1129 was_backslashed = true;
1130 } else {
1131 was_backslashed = false;
1132 }
1133 ptr = ml_get(++curwin->w_cursor.lnum);
1134 }
1135 if (was_backslashed) {
1136 newindent = 0; // Got to end of file
1137 } else {
1138 newindent = get_indent();
1139 }
1140 }
1141 p = skipwhite(ptr);
1142 if (*p == '}') { // if line starts with '}': do indent
1143 did_si = true;
1144 } else { // can delete indent when '{' typed
1145 can_si_back = true;
1146 }
1147 }
1148 curwin->w_cursor = old_cursor;
1149 }
1150 if (do_si) {
1151 can_si = true;
1152 }
1153
1154 did_ai = true;
1155 }
1156
1157 // Find out if the current line starts with a comment leader.
1158 // This may then be inserted in front of the new line.
1159 end_comment_pending = NUL;
1160 if (flags & OPENLINE_DO_COM) {
1161 lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true);
1162 } else {
1163 lead_len = 0;
1164 }
1165 if (lead_len > 0) {
1166 char_u *lead_repl = NULL; // replaces comment leader
1167 int lead_repl_len = 0; // length of *lead_repl
1168 char_u lead_middle[COM_MAX_LEN]; // middle-comment string
1169 char_u lead_end[COM_MAX_LEN]; // end-comment string
1170 char_u *comment_end = NULL; // where lead_end has been found
1171 int extra_space = false; // append extra space
1172 int current_flag;
1173 int require_blank = false; // requires blank after middle
1174 char_u *p2;
1175
1176 // If the comment leader has the start, middle or end flag, it may not
1177 // be used or may be replaced with the middle leader.
1178 for (p = lead_flags; *p && *p != ':'; p++) {
1179 if (*p == COM_BLANK) {
1180 require_blank = true;
1181 continue;
1182 }
1183 if (*p == COM_START || *p == COM_MIDDLE) {
1184 current_flag = *p;
1185 if (*p == COM_START) {
1186 // Doing "O" on a start of comment does not insert leader.
1187 if (dir == BACKWARD) {
1188 lead_len = 0;
1189 break;
1190 }
1191
1192 // find start of middle part
1193 (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
1194 require_blank = false;
1195 }
1196
1197 // Isolate the strings of the middle and end leader.
1198 while (*p && p[-1] != ':') { // find end of middle flags
1199 if (*p == COM_BLANK) {
1200 require_blank = true;
1201 }
1202 p++;
1203 }
1204 (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
1205
1206 while (*p && p[-1] != ':') { // find end of end flags
1207 // Check whether we allow automatic ending of comments
1208 if (*p == COM_AUTO_END) {
1209 end_comment_pending = -1; // means we want to set it
1210 }
1211 p++;
1212 }
1213 size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
1214
1215 if (end_comment_pending == -1) { // we can set it now
1216 end_comment_pending = lead_end[n - 1];
1217 }
1218
1219 // If the end of the comment is in the same line, don't use
1220 // the comment leader.
1221 if (dir == FORWARD) {
1222 for (p = saved_line + lead_len; *p; p++) {
1223 if (STRNCMP(p, lead_end, n) == 0) {
1224 comment_end = p;
1225 lead_len = 0;
1226 break;
1227 }
1228 }
1229 }
1230
1231 // Doing "o" on a start of comment inserts the middle leader.
1232 if (lead_len > 0) {
1233 if (current_flag == COM_START) {
1234 lead_repl = lead_middle;
1235 lead_repl_len = (int)STRLEN(lead_middle);
1236 }
1237
1238 // If we have hit RETURN immediately after the start
1239 // comment leader, then put a space after the middle
1240 // comment leader on the next line.
1241 if (!ascii_iswhite(saved_line[lead_len - 1])
1242 && ((p_extra != NULL
1243 && (int)curwin->w_cursor.col == lead_len)
1244 || (p_extra == NULL
1245 && saved_line[lead_len] == NUL)
1246 || require_blank)) {
1247 extra_space = true;
1248 }
1249 }
1250 break;
1251 }
1252 if (*p == COM_END) {
1253 // Doing "o" on the end of a comment does not insert leader.
1254 // Remember where the end is, might want to use it to find the
1255 // start (for C-comments).
1256 if (dir == FORWARD) {
1257 comment_end = skipwhite(saved_line);
1258 lead_len = 0;
1259 break;
1260 }
1261
1262 // Doing "O" on the end of a comment inserts the middle leader.
1263 // Find the string for the middle leader, searching backwards.
1264 while (p > curbuf->b_p_com && *p != ',') {
1265 p--;
1266 }
1267 for (lead_repl = p; lead_repl > curbuf->b_p_com
1268 && lead_repl[-1] != ':'; lead_repl--) {
1269 }
1270 lead_repl_len = (int)(p - lead_repl);
1271
1272 // We can probably always add an extra space when doing "O" on
1273 // the comment-end
1274 extra_space = true;
1275
1276 // Check whether we allow automatic ending of comments
1277 for (p2 = p; *p2 && *p2 != ':'; p2++) {
1278 if (*p2 == COM_AUTO_END) {
1279 end_comment_pending = -1; // means we want to set it
1280 }
1281 }
1282 if (end_comment_pending == -1) {
1283 // Find last character in end-comment string
1284 while (*p2 && *p2 != ',') {
1285 p2++;
1286 }
1287 end_comment_pending = p2[-1];
1288 }
1289 break;
1290 }
1291 if (*p == COM_FIRST) {
1292 // Comment leader for first line only: Don't repeat leader
1293 // when using "O", blank out leader when using "o".
1294 if (dir == BACKWARD) {
1295 lead_len = 0;
1296 } else {
1297 lead_repl = (char_u *)"";
1298 lead_repl_len = 0;
1299 }
1300 break;
1301 }
1302 }
1303 if (lead_len > 0) {
1304 // allocate buffer (may concatenate p_extra later)
1305 int bytes = lead_len
1306 + lead_repl_len
1307 + extra_space
1308 + extra_len
1309 + (second_line_indent > 0 ? second_line_indent : 0)
1310 + 1;
1311 assert(bytes >= 0);
1312 leader = xmalloc((size_t)bytes);
1313 allocated = leader; // remember to free it later
1314
1315 STRLCPY(leader, saved_line, lead_len + 1);
1316
1317 // Replace leader with lead_repl, right or left adjusted
1318 if (lead_repl != NULL) {
1319 int c = 0;
1320 int off = 0;
1321
1322 for (p = lead_flags; *p != NUL && *p != ':'; ) {
1323 if (*p == COM_RIGHT || *p == COM_LEFT) {
1324 c = *p++;
1325 } else if (ascii_isdigit(*p) || *p == '-') {
1326 off = getdigits_int(&p, true, 0);
1327 } else {
1328 p++;
1329 }
1330 }
1331 if (c == COM_RIGHT) { // right adjusted leader
1332 // find last non-white in the leader to line up with
1333 for (p = leader + lead_len - 1; p > leader
1334 && ascii_iswhite(*p); p--) {
1335 }
1336 p++;
1337
1338 // Compute the length of the replaced characters in
1339 // screen characters, not bytes.
1340 {
1341 int repl_size = vim_strnsize(lead_repl,
1342 lead_repl_len);
1343 int old_size = 0;
1344 char_u *endp = p;
1345 int l;
1346
1347 while (old_size < repl_size && p > leader) {
1348 MB_PTR_BACK(leader, p);
1349 old_size += ptr2cells(p);
1350 }
1351 l = lead_repl_len - (int)(endp - p);
1352 if (l != 0) {
1353 memmove(endp + l, endp,
1354 (size_t)((leader + lead_len) - endp));
1355 }
1356 lead_len += l;
1357 }
1358 memmove(p, lead_repl, (size_t)lead_repl_len);
1359 if (p + lead_repl_len > leader + lead_len) {
1360 p[lead_repl_len] = NUL;
1361 }
1362
1363 // blank-out any other chars from the old leader.
1364 while (--p >= leader) {
1365 int l = utf_head_off(leader, p);
1366
1367 if (l > 1) {
1368 p -= l;
1369 if (ptr2cells(p) > 1) {
1370 p[1] = ' ';
1371 l--;
1372 }
1373 memmove(p + 1, p + l + 1,
1374 (size_t)((leader + lead_len) - (p + l + 1)));
1375 lead_len -= l;
1376 *p = ' ';
1377 } else if (!ascii_iswhite(*p)) {
1378 *p = ' ';
1379 }
1380 }
1381 } else { // left adjusted leader
1382 p = skipwhite(leader);
1383 // Compute the length of the replaced characters in
1384 // screen characters, not bytes. Move the part that is
1385 // not to be overwritten.
1386 {
1387 int repl_size = vim_strnsize(lead_repl,
1388 lead_repl_len);
1389 int i;
1390 int l;
1391
1392 for (i = 0; i < lead_len && p[i] != NUL; i += l) {
1393 l = (*mb_ptr2len)(p + i);
1394 if (vim_strnsize(p, i + l) > repl_size) {
1395 break;
1396 }
1397 }
1398 if (i != lead_repl_len) {
1399 memmove(p + lead_repl_len, p + i,
1400 (size_t)(lead_len - i - (p - leader)));
1401 lead_len += lead_repl_len - i;
1402 }
1403 }
1404 memmove(p, lead_repl, (size_t)lead_repl_len);
1405
1406 // Replace any remaining non-white chars in the old
1407 // leader by spaces. Keep Tabs, the indent must
1408 // remain the same.
1409 for (p += lead_repl_len; p < leader + lead_len; p++) {
1410 if (!ascii_iswhite(*p)) {
1411 // Don't put a space before a TAB.
1412 if (p + 1 < leader + lead_len && p[1] == TAB) {
1413 lead_len--;
1414 memmove(p, p + 1, (size_t)(leader + lead_len - p));
1415 } else {
1416 int l = (*mb_ptr2len)(p);
1417
1418 if (l > 1) {
1419 if (ptr2cells(p) > 1) {
1420 // Replace a double-wide char with
1421 // two spaces
1422 l--;
1423 *p++ = ' ';
1424 }
1425 memmove(p + 1, p + l, (size_t)(leader + lead_len - p));
1426 lead_len -= l - 1;
1427 }
1428 *p = ' ';
1429 }
1430 }
1431 }
1432 *p = NUL;
1433 }
1434
1435 // Recompute the indent, it may have changed.
1436 if (curbuf->b_p_ai
1437 || do_si
1438 ) {
1439 newindent = get_indent_str(leader, (int)curbuf->b_p_ts, false);
1440 }
1441
1442 // Add the indent offset
1443 if (newindent + off < 0) {
1444 off = -newindent;
1445 newindent = 0;
1446 } else {
1447 newindent += off;
1448 }
1449
1450 // Correct trailing spaces for the shift, so that
1451 // alignment remains equal.
1452 while (off > 0 && lead_len > 0
1453 && leader[lead_len - 1] == ' ') {
1454 // Don't do it when there is a tab before the space
1455 if (vim_strchr(skipwhite(leader), '\t') != NULL) {
1456 break;
1457 }
1458 lead_len--;
1459 off--;
1460 }
1461
1462 // If the leader ends in white space, don't add an
1463 // extra space
1464 if (lead_len > 0 && ascii_iswhite(leader[lead_len - 1])) {
1465 extra_space = false;
1466 }
1467 leader[lead_len] = NUL;
1468 }
1469
1470 if (extra_space) {
1471 leader[lead_len++] = ' ';
1472 leader[lead_len] = NUL;
1473 }
1474
1475 newcol = lead_len;
1476
1477 // if a new indent will be set below, remove the indent that
1478 // is in the comment leader
1479 if (newindent
1480 || did_si
1481 ) {
1482 while (lead_len && ascii_iswhite(*leader)) {
1483 lead_len--;
1484 newcol--;
1485 leader++;
1486 }
1487 }
1488
1489 did_si = can_si = false;
1490 } else if (comment_end != NULL) {
1491 // We have finished a comment, so we don't use the leader.
1492 // If this was a C-comment and 'ai' or 'si' is set do a normal
1493 // indent to align with the line containing the start of the
1494 // comment.
1495 if (comment_end[0] == '*' && comment_end[1] == '/'
1496 && (curbuf->b_p_ai || do_si)) {
1497 old_cursor = curwin->w_cursor;
1498 curwin->w_cursor.col = (colnr_T)(comment_end - saved_line);
1499 if ((pos = findmatch(NULL, NUL)) != NULL) {
1500 curwin->w_cursor.lnum = pos->lnum;
1501 newindent = get_indent();
1502 }
1503 curwin->w_cursor = old_cursor;
1504 }
1505 }
1506 }
1507
1508 // (State == INSERT || State == REPLACE), only when dir == FORWARD
1509 if (p_extra != NULL) {
1510 *p_extra = saved_char; // restore char that NUL replaced
1511
1512 // When 'ai' set or "flags" has OPENLINE_DELSPACES, skip to the first
1513 // non-blank.
1514 //
1515 // When in REPLACE mode, put the deleted blanks on the replace stack,
1516 // preceded by a NUL, so they can be put back when a BS is entered.
1517 if (REPLACE_NORMAL(State)) {
1518 replace_push(NUL); // end of extra blanks
1519 }
1520 if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) {
1521 while ((*p_extra == ' ' || *p_extra == '\t')
1522 && !utf_iscomposing(utf_ptr2char(p_extra + 1))) {
1523 if (REPLACE_NORMAL(State)) {
1524 replace_push(*p_extra);
1525 }
1526 p_extra++;
1527 less_cols_off++;
1528 }
1529 }
1530
1531 // columns for marks adjusted for removed columns
1532 less_cols = (int)(p_extra - saved_line);
1533 }
1534
1535 if (p_extra == NULL) {
1536 p_extra = (char_u *)""; // append empty line
1537 }
1538
1539 // concatenate leader and p_extra, if there is a leader
1540 if (lead_len > 0) {
1541 if (flags & OPENLINE_COM_LIST && second_line_indent > 0) {
1542 int i;
1543 int padding = second_line_indent
1544 - (newindent + (int)STRLEN(leader));
1545
1546 // Here whitespace is inserted after the comment char.
1547 // Below, set_indent(newindent, SIN_INSERT) will insert the
1548 // whitespace needed before the comment char.
1549 for (i = 0; i < padding; i++) {
1550 STRCAT(leader, " ");
1551 less_cols--;
1552 newcol++;
1553 }
1554 }
1555 STRCAT(leader, p_extra);
1556 p_extra = leader;
1557 did_ai = true; // So truncating blanks works with comments
1558 less_cols -= lead_len;
1559 } else {
1560 end_comment_pending = NUL; // turns out there was no leader
1561 }
1562
1563 old_cursor = curwin->w_cursor;
1564 if (dir == BACKWARD) {
1565 curwin->w_cursor.lnum--;
1566 }
1567 if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) {
1568 if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
1569 goto theend;
1570 }
1571 // Postpone calling changed_lines(), because it would mess up folding
1572 // with markers.
1573 // Skip mark_adjust when adding a line after the last one, there can't
1574 // be marks there. But still needed in diff mode.
1575 if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count
1576 || curwin->w_p_diff) {
1577 mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false);
1578 }
1579 did_append = true;
1580 } else {
1581 // In VREPLACE mode we are starting to replace the next line.
1582 curwin->w_cursor.lnum++;
1583 if (curwin->w_cursor.lnum >= Insstart.lnum + vr_lines_changed) {
1584 // In case we NL to a new line, BS to the previous one, and NL
1585 // again, we don't want to save the new line for undo twice.
1586 (void)u_save_cursor(); // errors are ignored!
1587 vr_lines_changed++;
1588 }
1589 ml_replace(curwin->w_cursor.lnum, p_extra, true);
1590 changed_bytes(curwin->w_cursor.lnum, 0);
1591 curwin->w_cursor.lnum--;
1592 did_append = false;
1593 }
1594
1595 inhibit_delete_count++;
1596 if (newindent
1597 || did_si
1598 ) {
1599 curwin->w_cursor.lnum++;
1600 if (did_si) {
1601 int sw = get_sw_value(curbuf);
1602
1603 if (p_sr) {
1604 newindent -= newindent % sw;
1605 }
1606 newindent += sw;
1607 }
1608 // Copy the indent
1609 if (curbuf->b_p_ci) {
1610 (void)copy_indent(newindent, saved_line);
1611
1612 // Set the 'preserveindent' option so that any further screwing
1613 // with the line doesn't entirely destroy our efforts to preserve
1614 // it. It gets restored at the function end.
1615 curbuf->b_p_pi = true;
1616 } else {
1617 (void)set_indent(newindent, SIN_INSERT);
1618 }
1619 less_cols -= curwin->w_cursor.col;
1620
1621 ai_col = curwin->w_cursor.col;
1622
1623 // In REPLACE mode, for each character in the new indent, there must
1624 // be a NUL on the replace stack, for when it is deleted with BS
1625 if (REPLACE_NORMAL(State)) {
1626 for (colnr_T n = 0; n < curwin->w_cursor.col; n++) {
1627 replace_push(NUL);
1628 }
1629 }
1630 newcol += curwin->w_cursor.col;
1631 if (no_si) {
1632 did_si = false;
1633 }
1634 }
1635 inhibit_delete_count--;
1636
1637 // In REPLACE mode, for each character in the extra leader, there must be
1638 // a NUL on the replace stack, for when it is deleted with BS.
1639 if (REPLACE_NORMAL(State)) {
1640 while (lead_len-- > 0) {
1641 replace_push(NUL);
1642 }
1643 }
1644
1645 curwin->w_cursor = old_cursor;
1646
1647 if (dir == FORWARD) {
1648 if (trunc_line || (State & INSERT)) {
1649 // truncate current line at cursor
1650 saved_line[curwin->w_cursor.col] = NUL;
1651 // Remove trailing white space, unless OPENLINE_KEEPTRAIL used.
1652 if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) {
1653 truncate_spaces(saved_line);
1654 }
1655 ml_replace(curwin->w_cursor.lnum, saved_line, false);
1656 saved_line = NULL;
1657 if (did_append) {
1658 changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
1659 curwin->w_cursor.lnum + 1, 1L, true);
1660 did_append = false;
1661
1662 // Move marks after the line break to the new line.
1663 if (flags & OPENLINE_MARKFIX) {
1664 mark_col_adjust(curwin->w_cursor.lnum,
1665 curwin->w_cursor.col + less_cols_off,
1666 1L, (long)-less_cols, 0);
1667 }
1668 } else {
1669 changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
1670 }
1671 }
1672
1673 // Put the cursor on the new line. Careful: the scrollup() above may
1674 // have moved w_cursor, we must use old_cursor.
1675 curwin->w_cursor.lnum = old_cursor.lnum + 1;
1676 }
1677 if (did_append) {
1678 changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true);
1679 }
1680
1681 curwin->w_cursor.col = newcol;
1682 curwin->w_cursor.coladd = 0;
1683
1684 // In VREPLACE mode, we are handling the replace stack ourselves, so stop
1685 // fixthisline() from doing it (via change_indent()) by telling it we're in
1686 // normal INSERT mode.
1687 if (State & VREPLACE_FLAG) {
1688 vreplace_mode = State; // So we know to put things right later
1689 State = INSERT;
1690 } else {
1691 vreplace_mode = 0;
1692 }
1693 // May do lisp indenting.
1694 if (!p_paste
1695 && leader == NULL
1696 && curbuf->b_p_lisp
1697 && curbuf->b_p_ai) {
1698 fixthisline(get_lisp_indent);
1699 ai_col = (colnr_T)getwhitecols_curline();
1700 }
1701 // May do indenting after opening a new line.
1702 if (!p_paste
1703 && (curbuf->b_p_cin
1704 || *curbuf->b_p_inde != NUL
1705 )
1706 && in_cinkeys(dir == FORWARD
1707 ? KEY_OPEN_FORW
1708 : KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum))) {
1709 do_c_expr_indent();
1710 ai_col = (colnr_T)getwhitecols_curline();
1711 }
1712 if (vreplace_mode != 0) {
1713 State = vreplace_mode;
1714 }
1715
1716 // Finally, VREPLACE gets the stuff on the new line, then puts back the
1717 // original line, and inserts the new stuff char by char, pushing old stuff
1718 // onto the replace stack (via ins_char()).
1719 if (State & VREPLACE_FLAG) {
1720 // Put new line in p_extra
1721 p_extra = vim_strsave(get_cursor_line_ptr());
1722
1723 // Put back original line
1724 ml_replace(curwin->w_cursor.lnum, next_line, false);
1725
1726 // Insert new stuff into line again
1727 curwin->w_cursor.col = 0;
1728 curwin->w_cursor.coladd = 0;
1729 ins_bytes(p_extra); // will call changed_bytes()
1730 xfree(p_extra);
1731 next_line = NULL;
1732 }
1733
1734 retval = true; // success!
1735theend:
1736 curbuf->b_p_pi = saved_pi;
1737 xfree(saved_line);
1738 xfree(next_line);
1739 xfree(allocated);
1740 return retval;
1741} // NOLINT(readability/fn_size)
1742
1743/// Delete from cursor to end of line.
1744/// Caller must have prepared for undo.
1745/// If "fixpos" is true fix the cursor position when done.
1746void truncate_line(int fixpos)
1747{
1748 char_u *newp;
1749 linenr_T lnum = curwin->w_cursor.lnum;
1750 colnr_T col = curwin->w_cursor.col;
1751
1752 if (col == 0) {
1753 newp = vim_strsave((char_u *)"");
1754 } else {
1755 newp = vim_strnsave(ml_get(lnum), (size_t)col);
1756 }
1757 ml_replace(lnum, newp, false);
1758
1759 // mark the buffer as changed and prepare for displaying
1760 changed_bytes(lnum, curwin->w_cursor.col);
1761
1762 // If "fixpos" is true we don't want to end up positioned at the NUL.
1763 if (fixpos && curwin->w_cursor.col > 0) {
1764 curwin->w_cursor.col--;
1765 }
1766}
1767
1768/// Delete "nlines" lines at the cursor.
1769/// Saves the lines for undo first if "undo" is true.
1770void del_lines(long nlines, int undo)
1771{
1772 long n;
1773 linenr_T first = curwin->w_cursor.lnum;
1774
1775 if (nlines <= 0) {
1776 return;
1777 }
1778
1779 // save the deleted lines for undo
1780 if (undo && u_savedel(first, nlines) == FAIL) {
1781 return;
1782 }
1783
1784 for (n = 0; n < nlines; ) {
1785 if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete
1786 break;
1787 }
1788
1789 ml_delete(first, true);
1790 n++;
1791
1792 // If we delete the last line in the file, stop
1793 if (first > curbuf->b_ml.ml_line_count) {
1794 break;
1795 }
1796 }
1797
1798 // Correct the cursor position before calling deleted_lines_mark(), it may
1799 // trigger a callback to display the cursor.
1800 curwin->w_cursor.col = 0;
1801 check_cursor_lnum();
1802
1803 // adjust marks, mark the buffer as changed and prepare for displaying
1804 deleted_lines_mark(first, n);
1805}
1806