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 /// @file diff.c
5 ///
6 /// Code for diff'ing two, three or four buffers.
7 ///
8 /// There are three ways to diff:
9 /// - Shell out to an external diff program, using files.
10 /// - Use the compiled-in xdiff library.
11 /// - Let 'diffexpr' do the work, using files.
12
13 #include <inttypes.h>
14 #include <stdbool.h>
15
16 #include "nvim/ascii.h"
17 #include "nvim/buffer.h"
18 #include "nvim/change.h"
19 #include "nvim/charset.h"
20 #include "nvim/cursor.h"
21 #include "nvim/diff.h"
22 #include "nvim/eval.h"
23 #include "nvim/ex_cmds.h"
24 #include "nvim/ex_docmd.h"
25 #include "nvim/fileio.h"
26 #include "nvim/fold.h"
27 #include "nvim/mark.h"
28 #include "nvim/mbyte.h"
29 #include "nvim/memline.h"
30 #include "nvim/memory.h"
31 #include "nvim/message.h"
32 #include "nvim/misc1.h"
33 #include "nvim/move.h"
34 #include "nvim/normal.h"
35 #include "nvim/option.h"
36 #include "nvim/os/os.h"
37 #include "nvim/os/shell.h"
38 #include "nvim/path.h"
39 #include "nvim/screen.h"
40 #include "nvim/strings.h"
41 #include "nvim/undo.h"
42 #include "nvim/vim.h"
43 #include "nvim/window.h"
44 #include "xdiff/xdiff.h"
45
46 static int diff_busy = false; // using diff structs, don't change them
47 static bool diff_need_update = false; // ex_diffupdate needs to be called
48
49 // Flags obtained from the 'diffopt' option
50 #define DIFF_FILLER 0x001 // display filler lines
51 #define DIFF_IBLANK 0x002 // ignore empty lines
52 #define DIFF_ICASE 0x004 // ignore case
53 #define DIFF_IWHITE 0x008 // ignore change in white space
54 #define DIFF_IWHITEALL 0x010 // ignore all white space changes
55 #define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL
56 #define DIFF_HORIZONTAL 0x040 // horizontal splits
57 #define DIFF_VERTICAL 0x080 // vertical splits
58 #define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden
59 #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
60 #define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
61 #define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
62 #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
63 static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
64
65 static long diff_algorithm = 0;
66
67 #define LBUFLEN 50 // length of line in diff file
68
69 // kTrue when "diff -a" works, kFalse when it doesn't work,
70 // kNone when not checked yet
71 static TriState diff_a_works = kNone;
72
73 // used for diff input
74 typedef struct {
75 char_u *din_fname; // used for external diff
76 mmfile_t din_mmfile; // used for internal diff
77 } diffin_T;
78
79 // used for diff result
80 typedef struct {
81 char_u *dout_fname; // used for external diff
82 garray_T dout_ga; // used for internal diff
83 } diffout_T;
84
85 // two diff inputs and one result
86 typedef struct {
87 diffin_T dio_orig; // original file input
88 diffin_T dio_new; // new file input
89 diffout_T dio_diff; // diff result
90 int dio_internal; // using internal diff
91 } diffio_T;
92
93 #ifdef INCLUDE_GENERATED_DECLARATIONS
94 # include "diff.c.generated.h"
95 #endif
96
97 /// Called when deleting or unloading a buffer: No longer make a diff with it.
98 ///
99 /// @param buf
diff_buf_delete(buf_T * buf)100 void diff_buf_delete(buf_T *buf)
101 {
102 FOR_ALL_TABS(tp) {
103 int i = diff_buf_idx_tp(buf, tp);
104
105 if (i != DB_COUNT) {
106 tp->tp_diffbuf[i] = NULL;
107 tp->tp_diff_invalid = true;
108
109 if (tp == curtab) {
110 diff_redraw(true);
111 }
112 }
113 }
114 }
115
116 /// Check if the current buffer should be added to or removed from the list of
117 /// diff buffers.
118 ///
119 /// @param win
diff_buf_adjust(win_T * win)120 void diff_buf_adjust(win_T *win)
121 {
122 if (!win->w_p_diff) {
123 // When there is no window showing a diff for this buffer, remove
124 // it from the diffs.
125 bool found_win = false;
126 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
127 if ((wp->w_buffer == win->w_buffer) && wp->w_p_diff) {
128 found_win = true;
129 }
130 }
131
132 if (!found_win) {
133 int i = diff_buf_idx(win->w_buffer);
134 if (i != DB_COUNT) {
135 curtab->tp_diffbuf[i] = NULL;
136 curtab->tp_diff_invalid = true;
137 diff_redraw(true);
138 }
139 }
140 } else {
141 diff_buf_add(win->w_buffer);
142 }
143 }
144
145 /// Add a buffer to make diffs for.
146 ///
147 /// Call this when a new buffer is being edited in the current window where
148 /// 'diff' is set.
149 /// Marks the current buffer as being part of the diff and requiring updating.
150 /// This must be done before any autocmd, because a command may use info
151 /// about the screen contents.
152 ///
153 /// @param buf The buffer to add.
diff_buf_add(buf_T * buf)154 void diff_buf_add(buf_T *buf)
155 {
156 if (diff_buf_idx(buf) != DB_COUNT) {
157 // It's already there.
158 return;
159 }
160
161 int i;
162 for (i = 0; i < DB_COUNT; ++i) {
163 if (curtab->tp_diffbuf[i] == NULL) {
164 curtab->tp_diffbuf[i] = buf;
165 curtab->tp_diff_invalid = true;
166 diff_redraw(true);
167 return;
168 }
169 }
170
171 semsg(_("E96: Cannot diff more than %" PRId64 " buffers"), (int64_t)DB_COUNT);
172 }
173
174 ///
175 /// Remove all buffers to make diffs for.
176 ///
diff_buf_clear(void)177 static void diff_buf_clear(void)
178 {
179 for (int i = 0; i < DB_COUNT; i++) {
180 if (curtab->tp_diffbuf[i] != NULL) {
181 curtab->tp_diffbuf[i] = NULL;
182 curtab->tp_diff_invalid = true;
183 diff_redraw(true);
184 }
185 }
186 }
187
188 /// Find buffer "buf" in the list of diff buffers for the current tab page.
189 ///
190 /// @param buf The buffer to find.
191 ///
192 /// @return Its index or DB_COUNT if not found.
diff_buf_idx(buf_T * buf)193 static int diff_buf_idx(buf_T *buf)
194 {
195 int idx;
196 for (idx = 0; idx < DB_COUNT; ++idx) {
197 if (curtab->tp_diffbuf[idx] == buf) {
198 break;
199 }
200 }
201 return idx;
202 }
203
204 /// Find buffer "buf" in the list of diff buffers for tab page "tp".
205 ///
206 /// @param buf
207 /// @param tp
208 ///
209 /// @return its index or DB_COUNT if not found.
diff_buf_idx_tp(buf_T * buf,tabpage_T * tp)210 static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp)
211 {
212 int idx;
213 for (idx = 0; idx < DB_COUNT; ++idx) {
214 if (tp->tp_diffbuf[idx] == buf) {
215 break;
216 }
217 }
218 return idx;
219 }
220
221 /// Mark the diff info involving buffer "buf" as invalid, it will be updated
222 /// when info is requested.
223 ///
224 /// @param buf
diff_invalidate(buf_T * buf)225 void diff_invalidate(buf_T *buf)
226 {
227 FOR_ALL_TABS(tp) {
228 int i = diff_buf_idx_tp(buf, tp);
229 if (i != DB_COUNT) {
230 tp->tp_diff_invalid = true;
231 if (tp == curtab) {
232 diff_redraw(true);
233 }
234 }
235 }
236 }
237
238 /// Called by mark_adjust(): update line numbers in "curbuf".
239 ///
240 /// @param line1
241 /// @param line2
242 /// @param amount
243 /// @param amount_after
diff_mark_adjust(linenr_T line1,linenr_T line2,long amount,long amount_after)244 void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
245 {
246 // Handle all tab pages that use the current buffer in a diff.
247 FOR_ALL_TABS(tp) {
248 int idx = diff_buf_idx_tp(curbuf, tp);
249 if (idx != DB_COUNT) {
250 diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after);
251 }
252 }
253 }
254
255 /// Update line numbers in tab page "tp" for "curbuf" with index "idx".
256 ///
257 /// This attempts to update the changes as much as possible:
258 /// When inserting/deleting lines outside of existing change blocks, create a
259 /// new change block and update the line numbers in following blocks.
260 /// When inserting/deleting lines in existing change blocks, update them.
261 ///
262 /// @param tp
263 /// @param idx
264 /// @param line1
265 /// @param line2
266 /// @param amount
267 /// @amount_after
diff_mark_adjust_tp(tabpage_T * tp,int idx,linenr_T line1,linenr_T line2,long amount,long amount_after)268 static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount,
269 long amount_after)
270 {
271 if (diff_internal()) {
272 // Will update diffs before redrawing. Set _invalid to update the
273 // diffs themselves, set _update to also update folds properly just
274 // before redrawing.
275 // Do update marks here, it is needed for :%diffput.
276 tp->tp_diff_invalid = true;
277 tp->tp_diff_update = true;
278 }
279
280 int inserted;
281 int deleted;
282 if (line2 == MAXLNUM) {
283 // mark_adjust(99, MAXLNUM, 9, 0): insert lines
284 inserted = amount;
285 deleted = 0;
286 } else if (amount_after > 0) {
287 // mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines
288 inserted = amount_after;
289 deleted = 0;
290 } else {
291 // mark_adjust(98, 99, MAXLNUM, -2): delete lines
292 inserted = 0;
293 deleted = -amount_after;
294 }
295
296 diff_T *dprev = NULL;
297 diff_T *dp = tp->tp_first_diff;
298
299 linenr_T last;
300 linenr_T lnum_deleted = line1; // lnum of remaining deletion
301 int n;
302 int off;
303 for (;;) {
304 // If the change is after the previous diff block and before the next
305 // diff block, thus not touching an existing change, create a new diff
306 // block. Don't do this when ex_diffgetput() is busy.
307 if (((dp == NULL)
308 || (dp->df_lnum[idx] - 1 > line2)
309 || ((line2 == MAXLNUM) && (dp->df_lnum[idx] > line1)))
310 && ((dprev == NULL)
311 || (dprev->df_lnum[idx] + dprev->df_count[idx] < line1))
312 && !diff_busy) {
313 diff_T *dnext = diff_alloc_new(tp, dprev, dp);
314
315 dnext->df_lnum[idx] = line1;
316 dnext->df_count[idx] = inserted;
317 int i;
318 for (i = 0; i < DB_COUNT; ++i) {
319 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
320 if (dprev == NULL) {
321 dnext->df_lnum[i] = line1;
322 } else {
323 dnext->df_lnum[i] = line1
324 + (dprev->df_lnum[i] + dprev->df_count[i])
325 - (dprev->df_lnum[idx] + dprev->df_count[idx]);
326 }
327 dnext->df_count[i] = deleted;
328 }
329 }
330 }
331
332 // if at end of the list, quit
333 if (dp == NULL) {
334 break;
335 }
336
337 //
338 // Check for these situations:
339 // 1 2 3
340 // 1 2 3
341 // line1 2 3 4 5
342 // 2 3 4 5
343 // 2 3 4 5
344 // line2 2 3 4 5
345 // 3 5 6
346 // 3 5 6
347
348 // compute last line of this change
349 last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
350
351 // 1. change completely above line1: nothing to do
352 if (last >= line1 - 1) {
353 // 6. change below line2: only adjust for amount_after; also when
354 // "deleted" became zero when deleted all lines between two diffs.
355 if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
356 if (amount_after == 0) {
357 // nothing left to change
358 break;
359 }
360 dp->df_lnum[idx] += amount_after;
361 } else {
362 int check_unchanged = false;
363
364 // 2. 3. 4. 5.: inserted/deleted lines touching this diff.
365 if (deleted > 0) {
366 if (dp->df_lnum[idx] >= line1) {
367 off = dp->df_lnum[idx] - lnum_deleted;
368
369 if (last <= line2) {
370 // 4. delete all lines of diff
371 if ((dp->df_next != NULL)
372 && (dp->df_next->df_lnum[idx] - 1 <= line2)) {
373 // delete continues in next diff, only do
374 // lines until that one
375 n = dp->df_next->df_lnum[idx] - lnum_deleted;
376 deleted -= n;
377 n -= dp->df_count[idx];
378 lnum_deleted = dp->df_next->df_lnum[idx];
379 } else {
380 n = deleted - dp->df_count[idx];
381 }
382 dp->df_count[idx] = 0;
383 } else {
384 // 5. delete lines at or just before top of diff
385 n = off;
386 dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1;
387 check_unchanged = true;
388 }
389 dp->df_lnum[idx] = line1;
390 } else {
391 off = 0;
392
393 if (last < line2) {
394 // 2. delete at end of diff
395 dp->df_count[idx] -= last - lnum_deleted + 1;
396
397 if ((dp->df_next != NULL)
398 && (dp->df_next->df_lnum[idx] - 1 <= line2)) {
399 // delete continues in next diff, only do
400 // lines until that one
401 n = dp->df_next->df_lnum[idx] - 1 - last;
402 deleted -= dp->df_next->df_lnum[idx] - lnum_deleted;
403 lnum_deleted = dp->df_next->df_lnum[idx];
404 } else {
405 n = line2 - last;
406 }
407 check_unchanged = true;
408 } else {
409 // 3. delete lines inside the diff
410 n = 0;
411 dp->df_count[idx] -= deleted;
412 }
413 }
414
415 int i;
416 for (i = 0; i < DB_COUNT; ++i) {
417 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
418 dp->df_lnum[i] -= off;
419 dp->df_count[i] += n;
420 }
421 }
422 } else {
423 if (dp->df_lnum[idx] <= line1) {
424 // inserted lines somewhere in this diff
425 dp->df_count[idx] += inserted;
426 check_unchanged = true;
427 } else {
428 // inserted lines somewhere above this diff
429 dp->df_lnum[idx] += inserted;
430 }
431 }
432
433 if (check_unchanged) {
434 // Check if inserted lines are equal, may reduce the size of the
435 // diff.
436 //
437 // TODO: also check for equal lines in the middle and perhaps split
438 // the block.
439 diff_check_unchanged(tp, dp);
440 }
441 }
442 }
443
444 // check if this block touches the previous one, may merge them.
445 if ((dprev != NULL)
446 && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
447 int i;
448 for (i = 0; i < DB_COUNT; ++i) {
449 if (tp->tp_diffbuf[i] != NULL) {
450 dprev->df_count[i] += dp->df_count[i];
451 }
452 }
453 dprev->df_next = dp->df_next;
454 xfree(dp);
455 dp = dprev->df_next;
456 } else {
457 // Advance to next entry.
458 dprev = dp;
459 dp = dp->df_next;
460 }
461 }
462
463 dprev = NULL;
464 dp = tp->tp_first_diff;
465
466 while (dp != NULL) {
467 // All counts are zero, remove this entry.
468 int i;
469 for (i = 0; i < DB_COUNT; ++i) {
470 if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) {
471 break;
472 }
473 }
474
475 if (i == DB_COUNT) {
476 diff_T *dnext = dp->df_next;
477 xfree(dp);
478 dp = dnext;
479
480 if (dprev == NULL) {
481 tp->tp_first_diff = dnext;
482 } else {
483 dprev->df_next = dnext;
484 }
485 } else {
486 // Advance to next entry.
487 dprev = dp;
488 dp = dp->df_next;
489 }
490 }
491
492 if (tp == curtab) {
493 // Don't redraw right away, this updates the diffs, which can be slow.
494 need_diff_redraw = true;
495
496 // Need to recompute the scroll binding, may remove or add filler
497 // lines (e.g., when adding lines above w_topline). But it's slow when
498 // making many changes, postpone until redrawing.
499 diff_need_scrollbind = true;
500 }
501 }
502
503 /// Allocate a new diff block and link it between "dprev" and "dp".
504 ///
505 /// @param tp
506 /// @param dprev
507 /// @param dp
508 ///
509 /// @return The new diff block.
diff_alloc_new(tabpage_T * tp,diff_T * dprev,diff_T * dp)510 static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
511 {
512 diff_T *dnew = xmalloc(sizeof(*dnew));
513
514 dnew->df_next = dp;
515 if (dprev == NULL) {
516 tp->tp_first_diff = dnew;
517 } else {
518 dprev->df_next = dnew;
519 }
520
521 return dnew;
522 }
523
524 /// Check if the diff block "dp" can be made smaller for lines at the start and
525 /// end that are equal. Called after inserting lines.
526 ///
527 /// This may result in a change where all buffers have zero lines, the caller
528 /// must take care of removing it.
529 ///
530 /// @param tp
531 /// @param dp
diff_check_unchanged(tabpage_T * tp,diff_T * dp)532 static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
533 {
534 // Find the first buffers, use it as the original, compare the other
535 // buffer lines against this one.
536 int i_org;
537 for (i_org = 0; i_org < DB_COUNT; ++i_org) {
538 if (tp->tp_diffbuf[i_org] != NULL) {
539 break;
540 }
541 }
542
543 // safety check
544 if (i_org == DB_COUNT) {
545 return;
546 }
547
548 if (diff_check_sanity(tp, dp) == FAIL) {
549 return;
550 }
551
552 // First check lines at the top, then at the bottom.
553 int off_org = 0;
554 int off_new = 0;
555 int dir = FORWARD;
556 for (;;) {
557 // Repeat until a line is found which is different or the number of
558 // lines has become zero.
559 while (dp->df_count[i_org] > 0) {
560 // Copy the line, the next ml_get() will invalidate it.
561 if (dir == BACKWARD) {
562 off_org = dp->df_count[i_org] - 1;
563 }
564 char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
565 dp->df_lnum[i_org] + off_org,
566 false));
567
568 int i_new;
569 for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) {
570 if (tp->tp_diffbuf[i_new] == NULL) {
571 continue;
572 }
573
574 if (dir == BACKWARD) {
575 off_new = dp->df_count[i_new] - 1;
576 }
577
578 // if other buffer doesn't have this line, it was inserted
579 if ((off_new < 0) || (off_new >= dp->df_count[i_new])) {
580 break;
581 }
582
583 if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
584 dp->df_lnum[i_new] + off_new,
585 false)) != 0) {
586 break;
587 }
588 }
589 xfree(line_org);
590
591 // Stop when a line isn't equal in all diff buffers.
592 if (i_new != DB_COUNT) {
593 break;
594 }
595
596 // Line matched in all buffers, remove it from the diff.
597 for (i_new = i_org; i_new < DB_COUNT; ++i_new) {
598 if (tp->tp_diffbuf[i_new] != NULL) {
599 if (dir == FORWARD) {
600 dp->df_lnum[i_new]++;
601 }
602 dp->df_count[i_new]--;
603 }
604 }
605 }
606
607 if (dir == BACKWARD) {
608 break;
609 }
610 dir = BACKWARD;
611 }
612 }
613
614 /// Check if a diff block doesn't contain invalid line numbers.
615 /// This can happen when the diff program returns invalid results.
616 ///
617 /// @param tp
618 /// @param dp
619 ///
620 /// @return OK if the diff block doesn't contain invalid line numbers.
diff_check_sanity(tabpage_T * tp,diff_T * dp)621 static int diff_check_sanity(tabpage_T *tp, diff_T *dp)
622 {
623 int i;
624 for (i = 0; i < DB_COUNT; ++i) {
625 if (tp->tp_diffbuf[i] != NULL) {
626 if (dp->df_lnum[i] + dp->df_count[i] - 1
627 > tp->tp_diffbuf[i]->b_ml.ml_line_count) {
628 return FAIL;
629 }
630 }
631 }
632 return OK;
633 }
634
635 /// Mark all diff buffers in the current tab page for redraw.
636 ///
637 /// @param dofold Also recompute the folds
diff_redraw(bool dofold)638 void diff_redraw(bool dofold)
639 {
640 win_T *wp_other = NULL;
641 bool used_max_fill_other = false;
642 bool used_max_fill_curwin = false;
643
644 need_diff_redraw = false;
645 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
646 if (!wp->w_p_diff) {
647 continue;
648 }
649 redraw_later(wp, SOME_VALID);
650 if (wp != curwin) {
651 wp_other = wp;
652 }
653 if (dofold && foldmethodIsDiff(wp)) {
654 foldUpdateAll(wp);
655 }
656
657 // A change may have made filler lines invalid, need to take care
658 // of that for other windows.
659 int n = diff_check(wp, wp->w_topline);
660
661 if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) {
662 if (wp->w_topfill > n) {
663 wp->w_topfill = (n < 0 ? 0 : n);
664 } else if ((n > 0) && (n > wp->w_topfill)) {
665 wp->w_topfill = n;
666 if (wp == curwin) {
667 used_max_fill_curwin = true;
668 } else if (wp_other != NULL) {
669 used_max_fill_other = true;
670 }
671 }
672 check_topfill(wp, false);
673 }
674 }
675 if (wp_other != NULL && curwin->w_p_scb) {
676 if (used_max_fill_curwin) {
677 // The current window was set to used the maximum number of filler
678 // lines, may need to reduce them.
679 diff_set_topline(wp_other, curwin);
680 } else if (used_max_fill_other) {
681 // The other window was set to used the maximum number of filler
682 // lines, may need to reduce them.
683 diff_set_topline(curwin, wp_other);
684 }
685 }
686 }
687
clear_diffin(diffin_T * din)688 static void clear_diffin(diffin_T *din)
689 {
690 if (din->din_fname == NULL) {
691 XFREE_CLEAR(din->din_mmfile.ptr);
692 } else {
693 os_remove((char *)din->din_fname);
694 }
695 }
696
clear_diffout(diffout_T * dout)697 static void clear_diffout(diffout_T *dout)
698 {
699 if (dout->dout_fname == NULL) {
700 ga_clear_strings(&dout->dout_ga);
701 } else {
702 os_remove((char *)dout->dout_fname);
703 }
704 }
705
706 /// Write buffer "buf" to a memory buffer.
707 ///
708 /// @param buf
709 /// @param din
710 ///
711 /// @return FAIL for failure.
diff_write_buffer(buf_T * buf,diffin_T * din)712 static int diff_write_buffer(buf_T *buf, diffin_T *din)
713 {
714 linenr_T lnum;
715 char_u *s;
716 long len = 0;
717 char_u *ptr;
718
719 // xdiff requires one big block of memory with all the text.
720 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
721 len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1;
722 }
723 ptr = try_malloc(len);
724 if (ptr == NULL) {
725 // Allocating memory failed. This can happen, because we try to read
726 // the whole buffer text into memory. Set the failed flag, the diff
727 // will be retried with external diff. The flag is never reset.
728 buf->b_diff_failed = true;
729 if (p_verbose > 0) {
730 verbose_enter();
731 smsg(_("Not enough memory to use internal diff for buffer \"%s\""),
732 buf->b_fname);
733 verbose_leave();
734 }
735 return FAIL;
736 }
737 din->din_mmfile.ptr = (char *)ptr;
738 din->din_mmfile.size = len;
739
740 len = 0;
741 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
742 for (s = ml_get_buf(buf, lnum, false); *s != NUL;) {
743 if (diff_flags & DIFF_ICASE) {
744 char_u cbuf[MB_MAXBYTES + 1];
745
746 // xdiff doesn't support ignoring case, fold-case the text.
747 int c = utf_ptr2char(s);
748 c = utf_fold(c);
749 const int orig_len = utfc_ptr2len(s);
750 if (utf_char2bytes(c, cbuf) != orig_len) {
751 // TODO(Bram): handle byte length difference
752 memmove(ptr + len, s, orig_len);
753 } else {
754 memmove(ptr + len, cbuf, orig_len);
755 }
756
757 s += orig_len;
758 len += orig_len;
759 } else {
760 ptr[len++] = *s++;
761 }
762 }
763 ptr[len++] = NL;
764 }
765 return OK;
766 }
767
768 /// Write buffer "buf" to file or memory buffer.
769 ///
770 /// Always use 'fileformat' set to "unix".
771 ///
772 /// @param buf
773 /// @param din
774 ///
775 /// @return FAIL for failure
diff_write(buf_T * buf,diffin_T * din)776 static int diff_write(buf_T *buf, diffin_T *din)
777 {
778 if (din->din_fname == NULL) {
779 return diff_write_buffer(buf, din);
780 }
781
782 // Always use 'fileformat' set to "unix".
783 char_u *save_ff = buf->b_p_ff;
784 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
785 int r = buf_write(buf, din->din_fname, NULL,
786 (linenr_T)1, buf->b_ml.ml_line_count,
787 NULL, false, false, false, true);
788 free_string_option(buf->b_p_ff);
789 buf->b_p_ff = save_ff;
790 return r;
791 }
792
793 ///
794 /// Update the diffs for all buffers involved.
795 ///
796 /// @param dio
797 /// @param idx_orig
798 /// @param eap can be NULL
diff_try_update(diffio_T * dio,int idx_orig,exarg_T * eap)799 static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
800 {
801 buf_T *buf;
802 int idx_new;
803
804 if (dio->dio_internal) {
805 ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
806 } else {
807 // We need three temp file names.
808 dio->dio_orig.din_fname = vim_tempname();
809 dio->dio_new.din_fname = vim_tempname();
810 dio->dio_diff.dout_fname = vim_tempname();
811 if (dio->dio_orig.din_fname == NULL
812 || dio->dio_new.din_fname == NULL
813 || dio->dio_diff.dout_fname == NULL) {
814 goto theend;
815 }
816 }
817
818 // Check external diff is actually working.
819 if (!dio->dio_internal && check_external_diff(dio) == FAIL) {
820 goto theend;
821 }
822
823 // :diffupdate!
824 if (eap != NULL && eap->forceit) {
825 for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) {
826 buf = curtab->tp_diffbuf[idx_new];
827 if (buf_valid(buf)) {
828 buf_check_timestamp(buf);
829 }
830 }
831 }
832
833 // Write the first buffer to a tempfile or mmfile_t.
834 buf = curtab->tp_diffbuf[idx_orig];
835 if (diff_write(buf, &dio->dio_orig) == FAIL) {
836 goto theend;
837 }
838
839 // Make a difference between the first buffer and every other.
840 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
841 buf = curtab->tp_diffbuf[idx_new];
842 if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
843 continue; // skip buffer that isn't loaded
844 }
845
846 // Write the other buffer and diff with the first one.
847 if (diff_write(buf, &dio->dio_new) == FAIL) {
848 continue;
849 }
850 if (diff_file(dio) == FAIL) {
851 continue;
852 }
853
854 // Read the diff output and add each entry to the diff list.
855 diff_read(idx_orig, idx_new, &dio->dio_diff);
856
857 clear_diffin(&dio->dio_new);
858 clear_diffout(&dio->dio_diff);
859 }
860 clear_diffin(&dio->dio_orig);
861
862 theend:
863 xfree(dio->dio_orig.din_fname);
864 xfree(dio->dio_new.din_fname);
865 xfree(dio->dio_diff.dout_fname);
866 }
867
868 ///
869 /// Return true if the options are set to use the internal diff library.
870 /// Note that if the internal diff failed for one of the buffers, the external
871 /// diff will be used anyway.
872 ///
diff_internal(void)873 int diff_internal(void)
874 {
875 return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL;
876 }
877
878 ///
879 /// Return true if the internal diff failed for one of the diff buffers.
880 ///
diff_internal_failed(void)881 static int diff_internal_failed(void)
882 {
883 int idx;
884
885 // Only need to do something when there is another buffer.
886 for (idx = 0; idx < DB_COUNT; idx++) {
887 if (curtab->tp_diffbuf[idx] != NULL
888 && curtab->tp_diffbuf[idx]->b_diff_failed) {
889 return true;
890 }
891 }
892 return false;
893 }
894
895 /// Completely update the diffs for the buffers involved.
896 ///
897 /// When using the external "diff" command the buffers are written to a file,
898 /// also for unmodified buffers (the file could have been produced by
899 /// autocommands, e.g. the netrw plugin).
900 ///
901 /// @param eap can be NULL
ex_diffupdate(exarg_T * eap)902 void ex_diffupdate(exarg_T *eap)
903 {
904 if (diff_busy) {
905 diff_need_update = true;
906 return;
907 }
908
909 int had_diffs = curtab->tp_first_diff != NULL;
910
911 // Delete all diffblocks.
912 diff_clear(curtab);
913 curtab->tp_diff_invalid = false;
914
915 // Use the first buffer as the original text.
916 int idx_orig;
917 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) {
918 if (curtab->tp_diffbuf[idx_orig] != NULL) {
919 break;
920 }
921 }
922
923 if (idx_orig == DB_COUNT) {
924 goto theend;
925 }
926
927 // Only need to do something when there is another buffer.
928 int idx_new;
929 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) {
930 if (curtab->tp_diffbuf[idx_new] != NULL) {
931 break;
932 }
933 }
934
935 if (idx_new == DB_COUNT) {
936 goto theend;
937 }
938
939 // Only use the internal method if it did not fail for one of the buffers.
940 diffio_T diffio;
941 memset(&diffio, 0, sizeof(diffio));
942 diffio.dio_internal = diff_internal() && !diff_internal_failed();
943
944 diff_try_update(&diffio, idx_orig, eap);
945 if (diffio.dio_internal && diff_internal_failed()) {
946 // Internal diff failed, use external diff instead.
947 memset(&diffio, 0, sizeof(diffio));
948 diff_try_update(&diffio, idx_orig, eap);
949 }
950
951 // force updating cursor position on screen
952 curwin->w_valid_cursor.lnum = 0;
953
954 theend:
955 // A redraw is needed if there were diffs and they were cleared, or there
956 // are diffs now, which means they got updated.
957 if (had_diffs || curtab->tp_first_diff != NULL) {
958 diff_redraw(true);
959 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
960 }
961 }
962
963 ///
964 /// Do a quick test if "diff" really works. Otherwise it looks like there
965 /// are no differences. Can't use the return value, it's non-zero when
966 /// there are differences.
967 ///
check_external_diff(diffio_T * diffio)968 static int check_external_diff(diffio_T *diffio)
969 {
970 // May try twice, first with "-a" and then without.
971 int io_error = false;
972 TriState ok = kFalse;
973 for (;;) {
974 ok = kFalse;
975 FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w");
976
977 if (fd == NULL) {
978 io_error = true;
979 } else {
980 if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) {
981 io_error = true;
982 }
983 fclose(fd);
984 fd = os_fopen((char *)diffio->dio_new.din_fname, "w");
985
986 if (fd == NULL) {
987 io_error = true;
988 } else {
989 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) {
990 io_error = true;
991 }
992 fclose(fd);
993 fd = NULL;
994 if (diff_file(diffio) == OK) {
995 fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r");
996 }
997
998 if (fd == NULL) {
999 io_error = true;
1000 } else {
1001 char_u linebuf[LBUFLEN];
1002
1003 for (;;) {
1004 // For normal diff there must be a line that contains
1005 // "1c1". For unified diff "@@ -1 +1 @@".
1006 if (vim_fgets(linebuf, LBUFLEN, fd)) {
1007 break;
1008 }
1009
1010 if (STRNCMP(linebuf, "1c1", 3) == 0
1011 || STRNCMP(linebuf, "@@ -1 +1 @@", 11) == 0) {
1012 ok = kTrue;
1013 }
1014 }
1015 fclose(fd);
1016 }
1017 os_remove((char *)diffio->dio_diff.dout_fname);
1018 os_remove((char *)diffio->dio_new.din_fname);
1019 }
1020 os_remove((char *)diffio->dio_orig.din_fname);
1021 }
1022
1023 // When using 'diffexpr' break here.
1024 if (*p_dex != NUL) {
1025 break;
1026 }
1027
1028 // If we checked if "-a" works already, break here.
1029 if (diff_a_works != kNone) {
1030 break;
1031 }
1032 diff_a_works = ok;
1033
1034 // If "-a" works break here, otherwise retry without "-a".
1035 if (ok) {
1036 break;
1037 }
1038 }
1039
1040 if (!ok) {
1041 if (io_error) {
1042 emsg(_("E810: Cannot read or write temp files"));
1043 }
1044 emsg(_("E97: Cannot create diffs"));
1045 diff_a_works = kNone;
1046 return FAIL;
1047 }
1048 return OK;
1049 }
1050
1051 ///
1052 /// Invoke the xdiff function.
1053 ///
diff_file_internal(diffio_T * diffio)1054 static int diff_file_internal(diffio_T *diffio)
1055 {
1056 xpparam_t param;
1057 xdemitconf_t emit_cfg;
1058 xdemitcb_t emit_cb;
1059
1060 memset(¶m, 0, sizeof(param));
1061 memset(&emit_cfg, 0, sizeof(emit_cfg));
1062 memset(&emit_cb, 0, sizeof(emit_cb));
1063
1064 param.flags = diff_algorithm;
1065
1066 if (diff_flags & DIFF_IWHITE) {
1067 param.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
1068 }
1069 if (diff_flags & DIFF_IWHITEALL) {
1070 param.flags |= XDF_IGNORE_WHITESPACE;
1071 }
1072 if (diff_flags & DIFF_IWHITEEOL) {
1073 param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
1074 }
1075 if (diff_flags & DIFF_IBLANK) {
1076 param.flags |= XDF_IGNORE_BLANK_LINES;
1077 }
1078
1079 emit_cfg.ctxlen = 0; // don't need any diff_context here
1080 emit_cb.priv = &diffio->dio_diff;
1081 emit_cb.out_line = xdiff_out;
1082 if (xdl_diff(&diffio->dio_orig.din_mmfile,
1083 &diffio->dio_new.din_mmfile,
1084 ¶m, &emit_cfg, &emit_cb) < 0) {
1085 emsg(_("E960: Problem creating the internal diff"));
1086 return FAIL;
1087 }
1088 return OK;
1089 }
1090
1091 /// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff".
1092 ///
1093 /// @param dio
1094 ///
1095 /// @return OK or FAIL
diff_file(diffio_T * dio)1096 static int diff_file(diffio_T *dio)
1097 {
1098 char *tmp_orig = (char *)dio->dio_orig.din_fname;
1099 char *tmp_new = (char *)dio->dio_new.din_fname;
1100 char *tmp_diff = (char *)dio->dio_diff.dout_fname;
1101 if (*p_dex != NUL) {
1102 // Use 'diffexpr' to generate the diff file.
1103 eval_diff(tmp_orig, tmp_new, tmp_diff);
1104 return OK;
1105 }
1106 // Use xdiff for generating the diff.
1107 if (dio->dio_internal) {
1108 return diff_file_internal(dio);
1109 } else {
1110 const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
1111 + STRLEN(p_srr) + 27);
1112 char *const cmd = xmalloc(len);
1113
1114 // We don't want $DIFF_OPTIONS to get in the way.
1115 if (os_getenv("DIFF_OPTIONS")) {
1116 os_unsetenv("DIFF_OPTIONS");
1117 }
1118
1119 // Build the diff command and execute it. Always use -a, binary
1120 // differences are of no use. Ignore errors, diff returns
1121 // non-zero when differences have been found.
1122 vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s",
1123 diff_a_works == kFalse ? "" : "-a ",
1124 "",
1125 (diff_flags & DIFF_IWHITE) ? "-b " : "",
1126 (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
1127 (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
1128 (diff_flags & DIFF_IBLANK) ? "-B " : "",
1129 (diff_flags & DIFF_ICASE) ? "-i " : "",
1130 tmp_orig, tmp_new);
1131 append_redir(cmd, len, (char *)p_srr, tmp_diff);
1132 block_autocmds(); // Avoid ShellCmdPost stuff
1133 (void)call_shell((char_u *)cmd,
1134 kShellOptFilter | kShellOptSilent | kShellOptDoOut,
1135 NULL);
1136 unblock_autocmds();
1137 xfree(cmd);
1138 return OK;
1139 }
1140 }
1141
1142 /// Create a new version of a file from the current buffer and a diff file.
1143 ///
1144 /// The buffer is written to a file, also for unmodified buffers (the file
1145 /// could have been produced by autocommands, e.g. the netrw plugin).
1146 ///
1147 /// @param eap
ex_diffpatch(exarg_T * eap)1148 void ex_diffpatch(exarg_T *eap)
1149 {
1150 char_u *buf = NULL;
1151 win_T *old_curwin = curwin;
1152 char_u *newname = NULL; // name of patched file buffer
1153 char_u *esc_name = NULL;
1154
1155 #ifdef UNIX
1156 char *fullname = NULL;
1157 #endif
1158
1159 // We need two temp file names.
1160 // Name of original temp file.
1161 char_u *tmp_orig = vim_tempname();
1162 // Name of patched temp file.
1163 char_u *tmp_new = vim_tempname();
1164
1165 if ((tmp_orig == NULL) || (tmp_new == NULL)) {
1166 goto theend;
1167 }
1168
1169 // Write the current buffer to "tmp_orig".
1170 if (buf_write(curbuf, tmp_orig, NULL,
1171 (linenr_T)1, curbuf->b_ml.ml_line_count,
1172 NULL, false, false, false, true) == FAIL) {
1173 goto theend;
1174 }
1175
1176 #ifdef UNIX
1177 // Get the absolute path of the patchfile, changing directory below.
1178 fullname = FullName_save((char *)eap->arg, false);
1179 esc_name =
1180 vim_strsave_shellescape((fullname != NULL ? (char_u *)fullname : eap->arg), true, true);
1181 #else
1182 esc_name = vim_strsave_shellescape(eap->arg, true, true);
1183 #endif
1184 size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16;
1185 buf = xmalloc(buflen);
1186
1187 #ifdef UNIX
1188 char_u dirbuf[MAXPATHL];
1189 // Temporarily chdir to /tmp, to avoid patching files in the current
1190 // directory when the patch file contains more than one patch. When we
1191 // have our own temp dir use that instead, it will be cleaned up when we
1192 // exit (any .rej files created). Don't change directory if we can't
1193 // return to the current.
1194 if ((os_dirname(dirbuf, MAXPATHL) != OK)
1195 || (os_chdir((char *)dirbuf) != 0)) {
1196 dirbuf[0] = NUL;
1197 } else {
1198 char *tempdir = (char *)vim_gettempdir();
1199 if (tempdir == NULL) {
1200 tempdir = "/tmp";
1201 }
1202 os_chdir(tempdir);
1203 shorten_fnames(true);
1204 }
1205 #endif
1206
1207 if (*p_pex != NUL) {
1208 // Use 'patchexpr' to generate the new file.
1209 #ifdef UNIX
1210 eval_patch((char *)tmp_orig,
1211 (fullname != NULL ? fullname : (char *)eap->arg),
1212 (char *)tmp_new);
1213 #else
1214 eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
1215 #endif
1216 } else {
1217 // Build the patch command and execute it. Ignore errors.
1218 vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
1219 tmp_new, tmp_orig, esc_name);
1220 block_autocmds(); // Avoid ShellCmdPost stuff
1221 (void)call_shell(buf, kShellOptFilter, NULL);
1222 unblock_autocmds();
1223 }
1224
1225 #ifdef UNIX
1226 if (dirbuf[0] != NUL) {
1227 if (os_chdir((char *)dirbuf) != 0) {
1228 emsg(_(e_prev_dir));
1229 }
1230 shorten_fnames(true);
1231 }
1232 #endif
1233
1234 // Delete any .orig or .rej file created.
1235 STRCPY(buf, tmp_new);
1236 STRCAT(buf, ".orig");
1237 os_remove((char *)buf);
1238 STRCPY(buf, tmp_new);
1239 STRCAT(buf, ".rej");
1240 os_remove((char *)buf);
1241
1242 // Only continue if the output file was created.
1243 FileInfo file_info;
1244 bool info_ok = os_fileinfo((char *)tmp_new, &file_info);
1245 uint64_t filesize = os_fileinfo_size(&file_info);
1246 if (!info_ok || filesize == 0) {
1247 emsg(_("E816: Cannot read patch output"));
1248 } else {
1249 if (curbuf->b_fname != NULL) {
1250 newname = vim_strnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4);
1251 STRCAT(newname, ".new");
1252 }
1253
1254 // don't use a new tab page, each tab page has its own diffs
1255 cmdmod.tab = 0;
1256
1257 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
1258 // Pretend it was a ":split fname" command
1259 eap->cmdidx = CMD_split;
1260 eap->arg = tmp_new;
1261 do_exedit(eap, old_curwin);
1262
1263 // check that split worked and editing tmp_new
1264 if ((curwin != old_curwin) && win_valid(old_curwin)) {
1265 // Set 'diff', 'scrollbind' on and 'wrap' off.
1266 diff_win_options(curwin, true);
1267 diff_win_options(old_curwin, true);
1268
1269 if (newname != NULL) {
1270 // do a ":file filename.new" on the patched buffer
1271 eap->arg = newname;
1272 ex_file(eap);
1273
1274 // Do filetype detection with the new name.
1275 if (au_has_group((char_u *)"filetypedetect")) {
1276 do_cmdline_cmd(":doau filetypedetect BufRead");
1277 }
1278 }
1279 }
1280 }
1281 }
1282
1283 theend:
1284 if (tmp_orig != NULL) {
1285 os_remove((char *)tmp_orig);
1286 }
1287 xfree(tmp_orig);
1288
1289 if (tmp_new != NULL) {
1290 os_remove((char *)tmp_new);
1291 }
1292 xfree(tmp_new);
1293 xfree(newname);
1294 xfree(buf);
1295 #ifdef UNIX
1296 xfree(fullname);
1297 #endif
1298 xfree(esc_name);
1299 }
1300
1301 /// Split the window and edit another file, setting options to show the diffs.
1302 ///
1303 /// @param eap
ex_diffsplit(exarg_T * eap)1304 void ex_diffsplit(exarg_T *eap)
1305 {
1306 win_T *old_curwin = curwin;
1307 bufref_T old_curbuf;
1308 set_bufref(&old_curbuf, curbuf);
1309
1310 // Need to compute w_fraction when no redraw happened yet.
1311 validate_cursor();
1312 set_fraction(curwin);
1313
1314 // don't use a new tab page, each tab page has its own diffs
1315 cmdmod.tab = 0;
1316
1317 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
1318 // Pretend it was a ":split fname" command
1319 eap->cmdidx = CMD_split;
1320 curwin->w_p_diff = true;
1321 do_exedit(eap, old_curwin);
1322
1323 // split must have worked
1324 if (curwin != old_curwin) {
1325 // Set 'diff', 'scrollbind' on and 'wrap' off.
1326 diff_win_options(curwin, true);
1327 if (win_valid(old_curwin)) {
1328 diff_win_options(old_curwin, true);
1329
1330 if (bufref_valid(&old_curbuf)) {
1331 // Move the cursor position to that of the old window.
1332 curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf,
1333 old_curwin->w_cursor.lnum);
1334 }
1335 }
1336 // Now that lines are folded scroll to show the cursor at the same
1337 // relative position.
1338 scroll_to_fraction(curwin, curwin->w_height);
1339 }
1340 }
1341 }
1342
1343 // Set options to show diffs for the current window.
ex_diffthis(exarg_T * eap)1344 void ex_diffthis(exarg_T *eap)
1345 {
1346 // Set 'diff', 'scrollbind' on and 'wrap' off.
1347 diff_win_options(curwin, true);
1348 }
1349
set_diff_option(win_T * wp,int value)1350 static void set_diff_option(win_T *wp, int value)
1351 {
1352 win_T *old_curwin = curwin;
1353
1354 curwin = wp;
1355 curbuf = curwin->w_buffer;
1356 curbuf->b_ro_locked++;
1357 set_option_value("diff", (long)value, NULL, OPT_LOCAL);
1358 curbuf->b_ro_locked--;
1359 curwin = old_curwin;
1360 curbuf = curwin->w_buffer;
1361 }
1362
1363
1364 /// Set options in window "wp" for diff mode.
1365 ///
1366 /// @param addbuf Add buffer to diff.
diff_win_options(win_T * wp,int addbuf)1367 void diff_win_options(win_T *wp, int addbuf)
1368 {
1369 win_T *old_curwin = curwin;
1370
1371 // close the manually opened folds
1372 curwin = wp;
1373 newFoldLevel();
1374 curwin = old_curwin;
1375
1376 // Use 'scrollbind' and 'cursorbind' when available
1377 if (!wp->w_p_diff) {
1378 wp->w_p_scb_save = wp->w_p_scb;
1379 }
1380 wp->w_p_scb = true;
1381
1382 if (!wp->w_p_diff) {
1383 wp->w_p_crb_save = wp->w_p_crb;
1384 }
1385 wp->w_p_crb = true;
1386 if (!(diff_flags & DIFF_FOLLOWWRAP)) {
1387 if (!wp->w_p_diff) {
1388 wp->w_p_wrap_save = wp->w_p_wrap;
1389 }
1390 wp->w_p_wrap = false;
1391 }
1392 curwin = wp; // -V519
1393 curbuf = curwin->w_buffer;
1394
1395 if (!wp->w_p_diff) {
1396 if (wp->w_p_diff_saved) {
1397 free_string_option(wp->w_p_fdm_save);
1398 }
1399 wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
1400 }
1401 set_string_option_direct("fdm", -1, (char_u *)"diff",
1402 OPT_LOCAL | OPT_FREE, 0);
1403 curwin = old_curwin;
1404 curbuf = curwin->w_buffer;
1405
1406 if (!wp->w_p_diff) {
1407 wp->w_p_fen_save = wp->w_p_fen;
1408 wp->w_p_fdl_save = wp->w_p_fdl;
1409
1410 if (wp->w_p_diff_saved) {
1411 free_string_option(wp->w_p_fdc_save);
1412 }
1413 wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc);
1414 }
1415 xfree(wp->w_p_fdc);
1416 wp->w_p_fdc = (char_u *)xstrdup("2");
1417 assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9);
1418 snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn);
1419 wp->w_p_fen = true;
1420 wp->w_p_fdl = 0;
1421 foldUpdateAll(wp);
1422
1423 // make sure topline is not halfway through a fold
1424 changed_window_setting_win(wp);
1425 if (vim_strchr(p_sbo, 'h') == NULL) {
1426 do_cmdline_cmd("set sbo+=hor");
1427 }
1428
1429 // Save the current values, to be restored in ex_diffoff().
1430 wp->w_p_diff_saved = true;
1431
1432 set_diff_option(wp, true);
1433
1434 if (addbuf) {
1435 diff_buf_add(wp->w_buffer);
1436 }
1437 redraw_later(wp, NOT_VALID);
1438 }
1439
1440 /// Set options not to show diffs. For the current window or all windows.
1441 /// Only in the current tab page.
1442 ///
1443 /// @param eap
ex_diffoff(exarg_T * eap)1444 void ex_diffoff(exarg_T *eap)
1445 {
1446 int diffwin = false;
1447
1448 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
1449 if (eap->forceit ? wp->w_p_diff : (wp == curwin)) {
1450 // Set 'diff' off. If option values were saved in
1451 // diff_win_options(), restore the ones whose settings seem to have
1452 // been left over from diff mode.
1453 set_diff_option(wp, false);
1454
1455 if (wp->w_p_diff_saved) {
1456 if (wp->w_p_scb) {
1457 wp->w_p_scb = wp->w_p_scb_save;
1458 }
1459
1460 if (wp->w_p_crb) {
1461 wp->w_p_crb = wp->w_p_crb_save;
1462 }
1463 if (!(diff_flags & DIFF_FOLLOWWRAP)) {
1464 if (!wp->w_p_wrap) {
1465 wp->w_p_wrap = wp->w_p_wrap_save;
1466 }
1467 }
1468 free_string_option(wp->w_p_fdm);
1469 wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
1470 ? wp->w_p_fdm_save
1471 : (char_u *)"manual");
1472 free_string_option(wp->w_p_fdc);
1473 wp->w_p_fdc = vim_strsave(wp->w_p_fdc_save);
1474
1475 if (wp->w_p_fdl == 0) {
1476 wp->w_p_fdl = wp->w_p_fdl_save;
1477 }
1478 // Only restore 'foldenable' when 'foldmethod' is not
1479 // "manual", otherwise we continue to show the diff folds.
1480 if (wp->w_p_fen) {
1481 wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save;
1482 }
1483
1484 foldUpdateAll(wp);
1485 }
1486 // remove filler lines
1487 wp->w_topfill = 0;
1488
1489 // make sure topline is not halfway a fold and cursor is
1490 // invalidated
1491 changed_window_setting_win(wp);
1492
1493 // Note: 'sbo' is not restored, it's a global option.
1494 diff_buf_adjust(wp);
1495 }
1496 diffwin |= wp->w_p_diff;
1497 }
1498
1499 // Also remove hidden buffers from the list.
1500 if (eap->forceit) {
1501 diff_buf_clear();
1502 }
1503
1504 if (!diffwin) {
1505 diff_need_update = false;
1506 curtab->tp_diff_invalid = false;
1507 curtab->tp_diff_update = false;
1508 diff_clear(curtab);
1509 }
1510
1511 // Remove "hor" from from 'scrollopt' if there are no diff windows left.
1512 if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) {
1513 do_cmdline_cmd("set sbo-=hor");
1514 }
1515 }
1516
1517 /// Read the diff output and add each entry to the diff list.
1518 ///
1519 /// @param idx_orig idx of original file
1520 /// @param idx_new idx of new file
1521 /// @dout diff output
diff_read(int idx_orig,int idx_new,diffout_T * dout)1522 static void diff_read(int idx_orig, int idx_new, diffout_T *dout)
1523 {
1524 FILE *fd = NULL;
1525 int line_idx = 0;
1526 diff_T *dprev = NULL;
1527 diff_T *dp = curtab->tp_first_diff;
1528 diff_T *dn, *dpl;
1529 char_u linebuf[LBUFLEN]; // only need to hold the diff line
1530 char_u *line;
1531 long off;
1532 int i;
1533 linenr_T lnum_orig, lnum_new;
1534 long count_orig, count_new;
1535 int notset = true; // block "*dp" not set yet
1536 enum {
1537 DIFF_ED,
1538 DIFF_UNIFIED,
1539 DIFF_NONE,
1540 } diffstyle = DIFF_NONE;
1541
1542 if (dout->dout_fname == NULL) {
1543 diffstyle = DIFF_UNIFIED;
1544 } else {
1545 fd = os_fopen((char *)dout->dout_fname, "r");
1546 if (fd == NULL) {
1547 emsg(_("E98: Cannot read diff output"));
1548 return;
1549 }
1550 }
1551
1552 for (;;) {
1553 if (fd == NULL) {
1554 if (line_idx >= dout->dout_ga.ga_len) {
1555 break; // did last line
1556 }
1557 line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
1558 } else {
1559 if (vim_fgets(linebuf, LBUFLEN, fd)) {
1560 break; // end of file
1561 }
1562 line = linebuf;
1563 }
1564
1565 if (diffstyle == DIFF_NONE) {
1566 // Determine diff style.
1567 // ed like diff looks like this:
1568 // {first}[,{last}]c{first}[,{last}]
1569 // {first}a{first}[,{last}]
1570 // {first}[,{last}]d{first}
1571 //
1572 // unified diff looks like this:
1573 // --- file1 2018-03-20 13:23:35.783153140 +0100
1574 // +++ file2 2018-03-20 13:23:41.183156066 +0100
1575 // @@ -1,3 +1,5 @@
1576 if (isdigit(*line)) {
1577 diffstyle = DIFF_ED;
1578 } else if ((STRNCMP(line, "@@ ", 3) == 0)) {
1579 diffstyle = DIFF_UNIFIED;
1580 } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
1581 && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
1582 && (STRNCMP(line, "+++ ", 4) == 0)
1583 && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
1584 && (STRNCMP(line, "@@ ", 3) == 0)) {
1585 diffstyle = DIFF_UNIFIED;
1586 } else {
1587 // Format not recognized yet, skip over this line. Cygwin diff
1588 // may put a warning at the start of the file.
1589 continue;
1590 }
1591 }
1592
1593 if (diffstyle == DIFF_ED) {
1594 if (!isdigit(*line)) {
1595 continue; // not the start of a diff block
1596 }
1597 if (parse_diff_ed(line, &lnum_orig, &count_orig,
1598 &lnum_new, &count_new) == FAIL) {
1599 continue;
1600 }
1601 } else {
1602 assert(diffstyle == DIFF_UNIFIED);
1603 if (STRNCMP(line, "@@ ", 3) != 0) {
1604 continue; // not the start of a diff block
1605 }
1606 if (parse_diff_unified(line, &lnum_orig, &count_orig,
1607 &lnum_new, &count_new) == FAIL) {
1608 continue;
1609 }
1610 }
1611
1612 // Go over blocks before the change, for which orig and new are equal.
1613 // Copy blocks from orig to new.
1614 while (dp != NULL
1615 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
1616 if (notset) {
1617 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1618 }
1619 dprev = dp;
1620 dp = dp->df_next;
1621 notset = true;
1622 }
1623
1624 if ((dp != NULL)
1625 && (lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
1626 && (lnum_orig + count_orig >= dp->df_lnum[idx_orig])) {
1627 // New block overlaps with existing block(s).
1628 // First find last block that overlaps.
1629 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
1630 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) {
1631 break;
1632 }
1633 }
1634
1635 // If the newly found block starts before the old one, set the
1636 // start back a number of lines.
1637 off = dp->df_lnum[idx_orig] - lnum_orig;
1638
1639 if (off > 0) {
1640 for (i = idx_orig; i < idx_new; ++i) {
1641 if (curtab->tp_diffbuf[i] != NULL) {
1642 dp->df_lnum[i] -= off;
1643 }
1644 }
1645 dp->df_lnum[idx_new] = lnum_new;
1646 dp->df_count[idx_new] = count_new;
1647 } else if (notset) {
1648 // new block inside existing one, adjust new block
1649 dp->df_lnum[idx_new] = lnum_new + off;
1650 dp->df_count[idx_new] = count_new - off;
1651 } else {
1652 // second overlap of new block with existing block
1653 dp->df_count[idx_new] += count_new - count_orig
1654 + dpl->df_lnum[idx_orig] +
1655 dpl->df_count[idx_orig]
1656 - (dp->df_lnum[idx_orig] +
1657 dp->df_count[idx_orig]);
1658 }
1659
1660 // Adjust the size of the block to include all the lines to the
1661 // end of the existing block or the new diff, whatever ends last.
1662 off = (lnum_orig + count_orig)
1663 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
1664
1665 if (off < 0) {
1666 // new change ends in existing block, adjust the end if not
1667 // done already
1668 if (notset) {
1669 dp->df_count[idx_new] += -off;
1670 }
1671 off = 0;
1672 }
1673
1674 for (i = idx_orig; i < idx_new; ++i) {
1675 if (curtab->tp_diffbuf[i] != NULL) {
1676 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
1677 - dp->df_lnum[i] + off;
1678 }
1679 }
1680
1681 // Delete the diff blocks that have been merged into one.
1682 dn = dp->df_next;
1683 dp->df_next = dpl->df_next;
1684
1685 while (dn != dp->df_next) {
1686 dpl = dn->df_next;
1687 xfree(dn);
1688 dn = dpl;
1689 }
1690 } else {
1691 // Allocate a new diffblock.
1692 dp = diff_alloc_new(curtab, dprev, dp);
1693
1694 dp->df_lnum[idx_orig] = lnum_orig;
1695 dp->df_count[idx_orig] = count_orig;
1696 dp->df_lnum[idx_new] = lnum_new;
1697 dp->df_count[idx_new] = count_new;
1698
1699 // Set values for other buffers, these must be equal to the
1700 // original buffer, otherwise there would have been a change
1701 // already.
1702 for (i = idx_orig + 1; i < idx_new; ++i) {
1703 if (curtab->tp_diffbuf[i] != NULL) {
1704 diff_copy_entry(dprev, dp, idx_orig, i);
1705 }
1706 }
1707 }
1708 notset = false; // "*dp" has been set
1709 }
1710
1711 // for remaining diff blocks orig and new are equal
1712 while (dp != NULL) {
1713 if (notset) {
1714 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1715 }
1716 dprev = dp;
1717 dp = dp->df_next;
1718 notset = true;
1719 }
1720
1721 if (fd != NULL) {
1722 fclose(fd);
1723 }
1724 }
1725
1726 /// Copy an entry at "dp" from "idx_orig" to "idx_new".
1727 ///
1728 /// @param dprev
1729 /// @param dp
1730 /// @param idx_orig
1731 /// @param idx_new
diff_copy_entry(diff_T * dprev,diff_T * dp,int idx_orig,int idx_new)1732 static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new)
1733 {
1734 long off;
1735
1736 if (dprev == NULL) {
1737 off = 0;
1738 } else {
1739 off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig])
1740 - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]);
1741 }
1742 dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off;
1743 dp->df_count[idx_new] = dp->df_count[idx_orig];
1744 }
1745
1746 /// Clear the list of diffblocks for tab page "tp".
1747 ///
1748 /// @param tp
diff_clear(tabpage_T * tp)1749 void diff_clear(tabpage_T *tp)
1750 FUNC_ATTR_NONNULL_ALL
1751 {
1752 diff_T *p;
1753 diff_T *next_p;
1754 for (p = tp->tp_first_diff; p != NULL; p = next_p) {
1755 next_p = p->df_next;
1756 xfree(p);
1757 }
1758 tp->tp_first_diff = NULL;
1759 }
1760
1761 /// Check diff status for line "lnum" in buffer "buf":
1762 ///
1763 /// Returns 0 for nothing special
1764 /// Returns -1 for a line that should be highlighted as changed.
1765 /// Returns -2 for a line that should be highlighted as added/deleted.
1766 /// Returns > 0 for inserting that many filler lines above it (never happens
1767 /// when 'diffopt' doesn't contain "filler").
1768 /// This should only be used for windows where 'diff' is set.
1769 ///
1770 /// @param wp
1771 /// @param lnum
1772 ///
1773 /// @return diff status.
diff_check(win_T * wp,linenr_T lnum)1774 int diff_check(win_T *wp, linenr_T lnum)
1775 {
1776 int idx; // index in tp_diffbuf[] for this buffer
1777 diff_T *dp;
1778 int maxcount;
1779 int i;
1780 buf_T *buf = wp->w_buffer;
1781 int cmp;
1782
1783 if (curtab->tp_diff_invalid) {
1784 // update after a big change
1785 ex_diffupdate(NULL);
1786 }
1787
1788 // no diffs at all
1789 if ((curtab->tp_first_diff == NULL) || !wp->w_p_diff) {
1790 return 0;
1791 }
1792
1793 // safety check: "lnum" must be a buffer line
1794 if ((lnum < 1) || (lnum > buf->b_ml.ml_line_count + 1)) {
1795 return 0;
1796 }
1797
1798 idx = diff_buf_idx(buf);
1799
1800 if (idx == DB_COUNT) {
1801 // no diffs for buffer "buf"
1802 return 0;
1803 }
1804
1805 // A closed fold never has filler lines.
1806 if (hasFoldingWin(wp, lnum, NULL, NULL, true, NULL)) {
1807 return 0;
1808 }
1809
1810 // search for a change that includes "lnum" in the list of diffblocks.
1811 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
1812 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
1813 break;
1814 }
1815 }
1816
1817 if ((dp == NULL) || (lnum < dp->df_lnum[idx])) {
1818 return 0;
1819 }
1820
1821 if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
1822 int zero = false;
1823
1824 // Changed or inserted line. If the other buffers have a count of
1825 // zero, the lines were inserted. If the other buffers have the same
1826 // count, check if the lines are identical.
1827 cmp = false;
1828
1829 for (i = 0; i < DB_COUNT; ++i) {
1830 if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) {
1831 if (dp->df_count[i] == 0) {
1832 zero = true;
1833 } else {
1834 if (dp->df_count[i] != dp->df_count[idx]) {
1835 // nr of lines changed.
1836 return -1;
1837 }
1838 cmp = true;
1839 }
1840 }
1841 }
1842
1843 if (cmp) {
1844 // Compare all lines. If they are equal the lines were inserted
1845 // in some buffers, deleted in others, but not changed.
1846 for (i = 0; i < DB_COUNT; ++i) {
1847 if ((i != idx)
1848 && (curtab->tp_diffbuf[i] != NULL)
1849 && (dp->df_count[i] != 0)) {
1850 if (!diff_equal_entry(dp, idx, i)) {
1851 return -1;
1852 }
1853 }
1854 }
1855 }
1856
1857 // If there is no buffer with zero lines then there is no difference
1858 // any longer. Happens when making a change (or undo) that removes
1859 // the difference. Can't remove the entry here, we might be halfway
1860 // through updating the window. Just report the text as unchanged.
1861 // Other windows might still show the change though.
1862 if (zero == false) {
1863 return 0;
1864 }
1865 return -2;
1866 }
1867
1868 // If 'diffopt' doesn't contain "filler", return 0.
1869 if (!(diff_flags & DIFF_FILLER)) {
1870 return 0;
1871 }
1872
1873 // Insert filler lines above the line just below the change. Will return
1874 // 0 when this buf had the max count.
1875 maxcount = 0;
1876 for (i = 0; i < DB_COUNT; ++i) {
1877 if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
1878 maxcount = dp->df_count[i];
1879 }
1880 }
1881 return maxcount - dp->df_count[idx];
1882 }
1883
1884 /// Compare two entries in diff "dp" and return true if they are equal.
1885 ///
1886 /// @param dp diff
1887 /// @param idx1 first entry in diff "dp"
1888 /// @param idx2 second entry in diff "dp"
1889 ///
1890 /// @return true if two entries are equal.
diff_equal_entry(diff_T * dp,int idx1,int idx2)1891 static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
1892 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
1893 {
1894 if (dp->df_count[idx1] != dp->df_count[idx2]) {
1895 return false;
1896 }
1897
1898 if (diff_check_sanity(curtab, dp) == FAIL) {
1899 return false;
1900 }
1901
1902 for (int i = 0; i < dp->df_count[idx1]; i++) {
1903 char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
1904 dp->df_lnum[idx1] + i, false));
1905
1906 int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
1907 dp->df_lnum[idx2] + i, false));
1908 xfree(line);
1909
1910 if (cmp != 0) {
1911 return false;
1912 }
1913 }
1914 return true;
1915 }
1916
1917 // Compare the characters at "p1" and "p2". If they are equal (possibly
1918 // ignoring case) return true and set "len" to the number of bytes.
diff_equal_char(const char_u * const p1,const char_u * const p2,int * const len)1919 static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int *const len)
1920 {
1921 const int l = utfc_ptr2len(p1);
1922
1923 if (l != utfc_ptr2len(p2)) {
1924 return false;
1925 }
1926 if (l > 1) {
1927 if (STRNCMP(p1, p2, l) != 0
1928 && (!(diff_flags & DIFF_ICASE)
1929 || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
1930 return false;
1931 }
1932 *len = l;
1933 } else {
1934 if ((*p1 != *p2)
1935 && (!(diff_flags & DIFF_ICASE)
1936 || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) {
1937 return false;
1938 }
1939 *len = 1;
1940 }
1941 return true;
1942 }
1943
1944 /// Compare strings "s1" and "s2" according to 'diffopt'.
1945 /// Return non-zero when they are different.
1946 ///
1947 /// @param s1 The first string
1948 /// @param s2 The second string
1949 ///
1950 /// @return on-zero if the two strings are different.
diff_cmp(char_u * s1,char_u * s2)1951 static int diff_cmp(char_u *s1, char_u *s2)
1952 {
1953 if ((diff_flags & DIFF_IBLANK)
1954 && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) {
1955 return 0;
1956 }
1957
1958 if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) {
1959 return STRCMP(s1, s2);
1960 }
1961
1962 if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) {
1963 return mb_stricmp((const char *)s1, (const char *)s2);
1964 }
1965
1966 char_u *p1 = s1;
1967 char_u *p2 = s2;
1968
1969 // Ignore white space changes and possibly ignore case.
1970 while (*p1 != NUL && *p2 != NUL) {
1971 if (((diff_flags & DIFF_IWHITE)
1972 && ascii_iswhite(*p1) && ascii_iswhite(*p2))
1973 || ((diff_flags & DIFF_IWHITEALL)
1974 && (ascii_iswhite(*p1) || ascii_iswhite(*p2)))) {
1975 p1 = skipwhite(p1);
1976 p2 = skipwhite(p2);
1977 } else {
1978 int l;
1979 if (!diff_equal_char(p1, p2, &l)) {
1980 break;
1981 }
1982 p1 += l;
1983 p2 += l;
1984 }
1985 }
1986
1987 // Ignore trailing white space.
1988 p1 = skipwhite(p1);
1989 p2 = skipwhite(p2);
1990
1991 if ((*p1 != NUL) || (*p2 != NUL)) {
1992 return 1;
1993 }
1994 return 0;
1995 }
1996
1997 /// Set the topline of "towin" to match the position in "fromwin", so that they
1998 /// show the same diff'ed lines.
1999 ///
2000 /// @param fromwin
2001 /// @param towin
diff_set_topline(win_T * fromwin,win_T * towin)2002 void diff_set_topline(win_T *fromwin, win_T *towin)
2003 {
2004 buf_T *frombuf = fromwin->w_buffer;
2005 linenr_T lnum = fromwin->w_topline;
2006 diff_T *dp;
2007 int max_count;
2008 int i;
2009
2010 int fromidx = diff_buf_idx(frombuf);
2011 if (fromidx == DB_COUNT) {
2012 // safety check
2013 return;
2014 }
2015
2016 if (curtab->tp_diff_invalid) {
2017 // update after a big change
2018 ex_diffupdate(NULL);
2019 }
2020 towin->w_topfill = 0;
2021
2022
2023 // search for a change that includes "lnum" in the list of diffblocks.
2024 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2025 if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2026 break;
2027 }
2028 }
2029
2030 if (dp == NULL) {
2031 // After last change, compute topline relative to end of file; no
2032 // filler lines.
2033 towin->w_topline = towin->w_buffer->b_ml.ml_line_count
2034 - (frombuf->b_ml.ml_line_count - lnum);
2035 } else {
2036 // Find index for "towin".
2037 int toidx = diff_buf_idx(towin->w_buffer);
2038
2039 if (toidx == DB_COUNT) {
2040 // safety check
2041 return;
2042 }
2043 towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
2044
2045 if (lnum >= dp->df_lnum[fromidx]) {
2046 // Inside a change: compute filler lines. With three or more
2047 // buffers we need to know the largest count.
2048 max_count = 0;
2049
2050 for (i = 0; i < DB_COUNT; ++i) {
2051 if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
2052 max_count = dp->df_count[i];
2053 }
2054 }
2055
2056 if (dp->df_count[toidx] == dp->df_count[fromidx]) {
2057 // same number of lines: use same filler count
2058 towin->w_topfill = fromwin->w_topfill;
2059 } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
2060 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2061 // more lines in towin and fromwin doesn't show diff
2062 // lines, only filler lines
2063 if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
2064 // towin also only shows filler lines
2065 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
2066 towin->w_topfill = fromwin->w_topfill;
2067 } else {
2068 // towin still has some diff lines to show
2069 towin->w_topline = dp->df_lnum[toidx]
2070 + max_count - fromwin->w_topfill;
2071 }
2072 }
2073 } else if (towin->w_topline >= dp->df_lnum[toidx]
2074 + dp->df_count[toidx]) {
2075 // less lines in towin and no diff lines to show: compute
2076 // filler lines
2077 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
2078
2079 if (diff_flags & DIFF_FILLER) {
2080 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2081 // fromwin is also out of diff lines
2082 towin->w_topfill = fromwin->w_topfill;
2083 } else {
2084 // fromwin has some diff lines
2085 towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
2086 }
2087 }
2088 }
2089 }
2090 }
2091
2092 // safety check (if diff info gets outdated strange things may happen)
2093 towin->w_botfill = false;
2094
2095 if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) {
2096 towin->w_topline = towin->w_buffer->b_ml.ml_line_count;
2097 towin->w_botfill = true;
2098 }
2099
2100 if (towin->w_topline < 1) {
2101 towin->w_topline = 1;
2102 towin->w_topfill = 0;
2103 }
2104
2105 // When w_topline changes need to recompute w_botline and cursor position
2106 invalidate_botline_win(towin);
2107 changed_line_abv_curs_win(towin);
2108
2109 check_topfill(towin, false);
2110 (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline,
2111 NULL, true, NULL);
2112 }
2113
2114 /// This is called when 'diffopt' is changed.
2115 ///
2116 /// @return
diffopt_changed(void)2117 int diffopt_changed(void)
2118 {
2119 int diff_context_new = 6;
2120 int diff_flags_new = 0;
2121 int diff_foldcolumn_new = 2;
2122 long diff_algorithm_new = 0;
2123 long diff_indent_heuristic = 0;
2124
2125 char_u *p = p_dip;
2126 while (*p != NUL) {
2127 if (STRNCMP(p, "filler", 6) == 0) {
2128 p += 6;
2129 diff_flags_new |= DIFF_FILLER;
2130 } else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
2131 p += 8;
2132 diff_context_new = getdigits_int(&p, false, diff_context_new);
2133 } else if (STRNCMP(p, "iblank", 6) == 0) {
2134 p += 6;
2135 diff_flags_new |= DIFF_IBLANK;
2136 } else if (STRNCMP(p, "icase", 5) == 0) {
2137 p += 5;
2138 diff_flags_new |= DIFF_ICASE;
2139 } else if (STRNCMP(p, "iwhiteall", 9) == 0) {
2140 p += 9;
2141 diff_flags_new |= DIFF_IWHITEALL;
2142 } else if (STRNCMP(p, "iwhiteeol", 9) == 0) {
2143 p += 9;
2144 diff_flags_new |= DIFF_IWHITEEOL;
2145 } else if (STRNCMP(p, "iwhite", 6) == 0) {
2146 p += 6;
2147 diff_flags_new |= DIFF_IWHITE;
2148 } else if (STRNCMP(p, "horizontal", 10) == 0) {
2149 p += 10;
2150 diff_flags_new |= DIFF_HORIZONTAL;
2151 } else if (STRNCMP(p, "vertical", 8) == 0) {
2152 p += 8;
2153 diff_flags_new |= DIFF_VERTICAL;
2154 } else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
2155 p += 11;
2156 diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
2157 } else if (STRNCMP(p, "hiddenoff", 9) == 0) {
2158 p += 9;
2159 diff_flags_new |= DIFF_HIDDEN_OFF;
2160 } else if (STRNCMP(p, "closeoff", 8) == 0) {
2161 p += 8;
2162 diff_flags_new |= DIFF_CLOSE_OFF;
2163 } else if (STRNCMP(p, "followwrap", 10) == 0) {
2164 p += 10;
2165 diff_flags_new |= DIFF_FOLLOWWRAP;
2166 } else if (STRNCMP(p, "indent-heuristic", 16) == 0) {
2167 p += 16;
2168 diff_indent_heuristic = XDF_INDENT_HEURISTIC;
2169 } else if (STRNCMP(p, "internal", 8) == 0) {
2170 p += 8;
2171 diff_flags_new |= DIFF_INTERNAL;
2172 } else if (STRNCMP(p, "algorithm:", 10) == 0) {
2173 p += 10;
2174 if (STRNCMP(p, "myers", 5) == 0) {
2175 p += 5;
2176 diff_algorithm_new = 0;
2177 } else if (STRNCMP(p, "minimal", 7) == 0) {
2178 p += 7;
2179 diff_algorithm_new = XDF_NEED_MINIMAL;
2180 } else if (STRNCMP(p, "patience", 8) == 0) {
2181 p += 8;
2182 diff_algorithm_new = XDF_PATIENCE_DIFF;
2183 } else if (STRNCMP(p, "histogram", 9) == 0) {
2184 p += 9;
2185 diff_algorithm_new = XDF_HISTOGRAM_DIFF;
2186 } else {
2187 return FAIL;
2188 }
2189 }
2190
2191 if ((*p != ',') && (*p != NUL)) {
2192 return FAIL;
2193 }
2194
2195 if (*p == ',') {
2196 ++p;
2197 }
2198 }
2199
2200 diff_algorithm_new |= diff_indent_heuristic;
2201
2202 // Can't have both "horizontal" and "vertical".
2203 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) {
2204 return FAIL;
2205 }
2206
2207 // If flags were added or removed, or the algorithm was changed, need to
2208 // update the diff.
2209 if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) {
2210 FOR_ALL_TABS(tp) {
2211 tp->tp_diff_invalid = true;
2212 }
2213 }
2214
2215 diff_flags = diff_flags_new;
2216 diff_context = diff_context_new == 0 ? 1 : diff_context_new;
2217 diff_foldcolumn = diff_foldcolumn_new;
2218 diff_algorithm = diff_algorithm_new;
2219
2220 diff_redraw(true);
2221
2222 // recompute the scroll binding with the new option value, may
2223 // remove or add filler lines
2224 check_scrollbind((linenr_T)0, 0L);
2225 return OK;
2226 }
2227
2228 /// Check that "diffopt" contains "horizontal".
diffopt_horizontal(void)2229 bool diffopt_horizontal(void)
2230 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2231 {
2232 return (diff_flags & DIFF_HORIZONTAL) != 0;
2233 }
2234
2235 // Return true if 'diffopt' contains "hiddenoff".
diffopt_hiddenoff(void)2236 bool diffopt_hiddenoff(void)
2237 {
2238 return (diff_flags & DIFF_HIDDEN_OFF) != 0;
2239 }
2240
2241 // Return true if 'diffopt' contains "closeoff".
diffopt_closeoff(void)2242 bool diffopt_closeoff(void)
2243 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2244 {
2245 return (diff_flags & DIFF_CLOSE_OFF) != 0;
2246 }
2247
2248 // Return true if 'diffopt' contains "filler".
diffopt_filler(void)2249 bool diffopt_filler(void)
2250 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2251 {
2252 return (diff_flags & DIFF_FILLER) != 0;
2253 }
2254
2255 /// Find the difference within a changed line.
2256 ///
2257 /// @param wp window whose current buffer to check
2258 /// @param lnum line number to check within the buffer
2259 /// @param startp first char of the change
2260 /// @param endp last char of the change
2261 ///
2262 /// @return true if the line was added, no other buffer has it.
diff_find_change(win_T * wp,linenr_T lnum,int * startp,int * endp)2263 bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
2264 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
2265 {
2266 char_u *line_new;
2267 int si_org;
2268 int si_new;
2269 int ei_org;
2270 int ei_new;
2271 bool added = true;
2272 int l;
2273
2274 // Make a copy of the line, the next ml_get() will invalidate it.
2275 char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false));
2276
2277 int idx = diff_buf_idx(wp->w_buffer);
2278 if (idx == DB_COUNT) {
2279 // cannot happen
2280 xfree(line_org);
2281 return false;
2282 }
2283
2284 // search for a change that includes "lnum" in the list of diffblocks.
2285 diff_T *dp;
2286 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2287 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
2288 break;
2289 }
2290 }
2291
2292 if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
2293 xfree(line_org);
2294 return false;
2295 }
2296
2297 int off = lnum - dp->df_lnum[idx];
2298 int i;
2299 for (i = 0; i < DB_COUNT; ++i) {
2300 if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) {
2301 // Skip lines that are not in the other change (filler lines).
2302 if (off >= dp->df_count[i]) {
2303 continue;
2304 }
2305 added = false;
2306 line_new = ml_get_buf(curtab->tp_diffbuf[i],
2307 dp->df_lnum[i] + off, false);
2308
2309 // Search for start of difference
2310 si_org = si_new = 0;
2311
2312 while (line_org[si_org] != NUL) {
2313 if (((diff_flags & DIFF_IWHITE)
2314 && ascii_iswhite(line_org[si_org])
2315 && ascii_iswhite(line_new[si_new]))
2316 || ((diff_flags & DIFF_IWHITEALL)
2317 && (ascii_iswhite(line_org[si_org])
2318 || ascii_iswhite(line_new[si_new])))) {
2319 si_org = (int)(skipwhite(line_org + si_org) - line_org);
2320 si_new = (int)(skipwhite(line_new + si_new) - line_new);
2321 } else {
2322 if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
2323 break;
2324 }
2325 si_org += l;
2326 si_new += l;
2327 }
2328 }
2329
2330 // Move back to first byte of character in both lines (may
2331 // have "nn^" in line_org and "n^ in line_new).
2332 si_org -= utf_head_off(line_org, line_org + si_org);
2333 si_new -= utf_head_off(line_new, line_new + si_new);
2334
2335 if (*startp > si_org) {
2336 *startp = si_org;
2337 }
2338
2339 // Search for end of difference, if any.
2340 if ((line_org[si_org] != NUL) || (line_new[si_new] != NUL)) {
2341 ei_org = (int)STRLEN(line_org);
2342 ei_new = (int)STRLEN(line_new);
2343
2344 while (ei_org >= *startp
2345 && ei_new >= si_new
2346 && ei_org >= 0
2347 && ei_new >= 0) {
2348 if (((diff_flags & DIFF_IWHITE)
2349 && ascii_iswhite(line_org[ei_org])
2350 && ascii_iswhite(line_new[ei_new]))
2351 || ((diff_flags & DIFF_IWHITEALL)
2352 && (ascii_iswhite(line_org[ei_org])
2353 || ascii_iswhite(line_new[ei_new])))) {
2354 while (ei_org >= *startp && ascii_iswhite(line_org[ei_org])) {
2355 ei_org--;
2356 }
2357
2358 while (ei_new >= si_new && ascii_iswhite(line_new[ei_new])) {
2359 ei_new--;
2360 }
2361 } else {
2362 const char_u *p1 = line_org + ei_org;
2363 const char_u *p2 = line_new + ei_new;
2364
2365 p1 -= utf_head_off(line_org, p1);
2366 p2 -= utf_head_off(line_new, p2);
2367
2368 if (!diff_equal_char(p1, p2, &l)) {
2369 break;
2370 }
2371 ei_org -= l;
2372 ei_new -= l;
2373 }
2374 }
2375
2376 if (*endp < ei_org) {
2377 *endp = ei_org;
2378 }
2379 }
2380 }
2381 }
2382
2383 xfree(line_org);
2384 return added;
2385 }
2386
2387 /// Check that line "lnum" is not close to a diff block, this line should
2388 /// be in a fold.
2389 ///
2390 /// @param wp window containing the buffer to check
2391 /// @param lnum line number to check within the buffer
2392 ///
2393 /// @return false if there are no diff blocks at all in this window.
diff_infold(win_T * wp,linenr_T lnum)2394 bool diff_infold(win_T *wp, linenr_T lnum)
2395 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
2396 {
2397 bool other = false;
2398 diff_T *dp;
2399
2400 // Return if 'diff' isn't set.
2401 if (!wp->w_p_diff) {
2402 return false;
2403 }
2404
2405 int idx = -1;
2406 int i;
2407 for (i = 0; i < DB_COUNT; ++i) {
2408 if (curtab->tp_diffbuf[i] == wp->w_buffer) {
2409 idx = i;
2410 } else if (curtab->tp_diffbuf[i] != NULL) {
2411 other = true;
2412 }
2413 }
2414
2415 // return here if there are no diffs in the window
2416 if ((idx == -1) || !other) {
2417 return false;
2418 }
2419
2420 if (curtab->tp_diff_invalid) {
2421 // update after a big change
2422 ex_diffupdate(NULL);
2423 }
2424
2425 // Return if there are no diff blocks. All lines will be folded.
2426 if (curtab->tp_first_diff == NULL) {
2427 return true;
2428 }
2429
2430 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2431 // If this change is below the line there can't be any further match.
2432 if (dp->df_lnum[idx] - diff_context > lnum) {
2433 break;
2434 }
2435
2436 // If this change ends before the line we have a match.
2437 if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) {
2438 return false;
2439 }
2440 }
2441 return true;
2442 }
2443
2444 /// "dp" and "do" commands.
nv_diffgetput(bool put,size_t count)2445 void nv_diffgetput(bool put, size_t count)
2446 {
2447 exarg_T ea;
2448 char buf[30];
2449
2450 if (bt_prompt(curbuf)) {
2451 vim_beep(BO_OPER);
2452 return;
2453 }
2454 if (count == 0) {
2455 ea.arg = (char_u *)"";
2456 } else {
2457 vim_snprintf(buf, 30, "%zu", count);
2458 ea.arg = (char_u *)buf;
2459 }
2460
2461 if (put) {
2462 ea.cmdidx = CMD_diffput;
2463 } else {
2464 ea.cmdidx = CMD_diffget;
2465 }
2466
2467 ea.addr_count = 0;
2468 ea.line1 = curwin->w_cursor.lnum;
2469 ea.line2 = curwin->w_cursor.lnum;
2470 ex_diffgetput(&ea);
2471 }
2472
2473 /// ":diffget" and ":diffput"
2474 ///
2475 /// @param eap
ex_diffgetput(exarg_T * eap)2476 void ex_diffgetput(exarg_T *eap)
2477 {
2478 linenr_T lnum;
2479 int count;
2480 linenr_T off = 0;
2481 diff_T *dp;
2482 diff_T *dprev;
2483 diff_T *dfree;
2484 int i;
2485 int added;
2486 char_u *p;
2487 aco_save_T aco;
2488 buf_T *buf;
2489 int start_skip, end_skip;
2490 int new_count;
2491 int buf_empty;
2492 int found_not_ma = false;
2493 int idx_other;
2494 int idx_from;
2495 int idx_to;
2496
2497 // Find the current buffer in the list of diff buffers.
2498 int idx_cur = diff_buf_idx(curbuf);
2499 if (idx_cur == DB_COUNT) {
2500 emsg(_("E99: Current buffer is not in diff mode"));
2501 return;
2502 }
2503
2504 if (*eap->arg == NUL) {
2505 // No argument: Find the other buffer in the list of diff buffers.
2506 for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) {
2507 if ((curtab->tp_diffbuf[idx_other] != curbuf)
2508 && (curtab->tp_diffbuf[idx_other] != NULL)) {
2509 if ((eap->cmdidx != CMD_diffput)
2510 || MODIFIABLE(curtab->tp_diffbuf[idx_other])) {
2511 break;
2512 }
2513 found_not_ma = true;
2514 }
2515 }
2516
2517 if (idx_other == DB_COUNT) {
2518 if (found_not_ma) {
2519 emsg(_("E793: No other buffer in diff mode is modifiable"));
2520 } else {
2521 emsg(_("E100: No other buffer in diff mode"));
2522 }
2523 return;
2524 }
2525
2526 // Check that there isn't a third buffer in the list
2527 for (i = idx_other + 1; i < DB_COUNT; ++i) {
2528 if ((curtab->tp_diffbuf[i] != curbuf)
2529 && (curtab->tp_diffbuf[i] != NULL)
2530 && ((eap->cmdidx != CMD_diffput)
2531 || MODIFIABLE(curtab->tp_diffbuf[i]))) {
2532 emsg(_("E101: More than two buffers in diff mode, don't know "
2533 "which one to use"));
2534 return;
2535 }
2536 }
2537 } else {
2538 // Buffer number or pattern given. Ignore trailing white space.
2539 p = eap->arg + STRLEN(eap->arg);
2540 while (p > eap->arg && ascii_iswhite(p[-1])) {
2541 p--;
2542 }
2543
2544 for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) {
2545 }
2546
2547 if (eap->arg + i == p) {
2548 // digits only
2549 i = atol((char *)eap->arg);
2550 } else {
2551 i = buflist_findpat(eap->arg, p, false, true, false);
2552
2553 if (i < 0) {
2554 // error message already given
2555 return;
2556 }
2557 }
2558 buf = buflist_findnr(i);
2559
2560 if (buf == NULL) {
2561 semsg(_("E102: Can't find buffer \"%s\""), eap->arg);
2562 return;
2563 }
2564
2565 if (buf == curbuf) {
2566 // nothing to do
2567 return;
2568 }
2569 idx_other = diff_buf_idx(buf);
2570
2571 if (idx_other == DB_COUNT) {
2572 semsg(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg);
2573 return;
2574 }
2575 }
2576
2577 diff_busy = true;
2578
2579 // When no range given include the line above or below the cursor.
2580 if (eap->addr_count == 0) {
2581 // Make it possible that ":diffget" on the last line gets line below
2582 // the cursor line when there is no difference above the cursor.
2583 if ((eap->cmdidx == CMD_diffget)
2584 && (eap->line1 == curbuf->b_ml.ml_line_count)
2585 && (diff_check(curwin, eap->line1) == 0)
2586 && ((eap->line1 == 1) || (diff_check(curwin, eap->line1 - 1) == 0))) {
2587 ++eap->line2;
2588 } else if (eap->line1 > 0) {
2589 --eap->line1;
2590 }
2591 }
2592
2593 if (eap->cmdidx == CMD_diffget) {
2594 idx_from = idx_other;
2595 idx_to = idx_cur;
2596 } else {
2597 idx_from = idx_cur;
2598 idx_to = idx_other;
2599
2600 // Need to make the other buffer the current buffer to be able to make
2601 // changes in it.
2602
2603 // set curwin/curbuf to buf and save a few things
2604 aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
2605 }
2606
2607 // May give the warning for a changed buffer here, which can trigger the
2608 // FileChangedRO autocommand, which may do nasty things and mess
2609 // everything up.
2610 if (!curbuf->b_changed) {
2611 change_warning(curbuf, 0);
2612 if (diff_buf_idx(curbuf) != idx_to) {
2613 emsg(_("E787: Buffer changed unexpectedly"));
2614 goto theend;
2615 }
2616 }
2617
2618 dprev = NULL;
2619
2620 for (dp = curtab->tp_first_diff; dp != NULL;) {
2621 if (dp->df_lnum[idx_cur] > eap->line2 + off) {
2622 // past the range that was specified
2623 break;
2624 }
2625 dfree = NULL;
2626 lnum = dp->df_lnum[idx_to];
2627 count = dp->df_count[idx_to];
2628
2629 if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off)
2630 && (u_save(lnum - 1, lnum + count) != FAIL)) {
2631 // Inside the specified range and saving for undo worked.
2632 start_skip = 0;
2633 end_skip = 0;
2634
2635 if (eap->addr_count > 0) {
2636 // A range was specified: check if lines need to be skipped.
2637 start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
2638 if (start_skip > 0) {
2639 // range starts below start of current diff block
2640 if (start_skip > count) {
2641 lnum += count;
2642 count = 0;
2643 } else {
2644 count -= start_skip;
2645 lnum += start_skip;
2646 }
2647 } else {
2648 start_skip = 0;
2649 }
2650
2651 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
2652 - (eap->line2 + off);
2653
2654 if (end_skip > 0) {
2655 // range ends above end of current/from diff block
2656 if (idx_cur == idx_from) {
2657 // :diffput
2658 i = dp->df_count[idx_cur] - start_skip - end_skip;
2659
2660 if (count > i) {
2661 count = i;
2662 }
2663 } else {
2664 // :diffget
2665 count -= end_skip;
2666 end_skip = dp->df_count[idx_from] - start_skip - count;
2667
2668 if (end_skip < 0) {
2669 end_skip = 0;
2670 }
2671 }
2672 } else {
2673 end_skip = 0;
2674 }
2675 }
2676
2677 buf_empty = buf_is_empty(curbuf);
2678 added = 0;
2679
2680 for (i = 0; i < count; ++i) {
2681 // remember deleting the last line of the buffer
2682 buf_empty = curbuf->b_ml.ml_line_count == 1;
2683 ml_delete(lnum, false);
2684 added--;
2685 }
2686
2687 for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) {
2688 linenr_T nr = dp->df_lnum[idx_from] + start_skip + i;
2689 if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) {
2690 break;
2691 }
2692 p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
2693 ml_append(lnum + i - 1, p, 0, false);
2694 xfree(p);
2695 added++;
2696 if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) {
2697 // Added the first line into an empty buffer, need to
2698 // delete the dummy empty line.
2699 buf_empty = false;
2700 ml_delete((linenr_T)2, false);
2701 }
2702 }
2703 new_count = dp->df_count[idx_to] + added;
2704 dp->df_count[idx_to] = new_count;
2705
2706 if ((start_skip == 0) && (end_skip == 0)) {
2707 // Check if there are any other buffers and if the diff is
2708 // equal in them.
2709 for (i = 0; i < DB_COUNT; ++i) {
2710 if ((curtab->tp_diffbuf[i] != NULL)
2711 && (i != idx_from)
2712 && (i != idx_to)
2713 && !diff_equal_entry(dp, idx_from, i)) {
2714 break;
2715 }
2716 }
2717
2718 if (i == DB_COUNT) {
2719 // delete the diff entry, the buffers are now equal here
2720 dfree = dp;
2721 dp = dp->df_next;
2722
2723 if (dprev == NULL) {
2724 curtab->tp_first_diff = dp;
2725 } else {
2726 dprev->df_next = dp;
2727 }
2728 }
2729 }
2730
2731 // Adjust marks. This will change the following entries!
2732 if (added != 0) {
2733 mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added,
2734 kExtmarkUndo);
2735 if (curwin->w_cursor.lnum >= lnum) {
2736 // Adjust the cursor position if it's in/after the changed
2737 // lines.
2738 if (curwin->w_cursor.lnum >= lnum + count) {
2739 curwin->w_cursor.lnum += added;
2740 } else if (added < 0) {
2741 curwin->w_cursor.lnum = lnum;
2742 }
2743 }
2744 }
2745 changed_lines(lnum, 0, lnum + count, (long)added, true);
2746
2747 if (dfree != NULL) {
2748 // Diff is deleted, update folds in other windows.
2749 diff_fold_update(dfree, idx_to);
2750 xfree(dfree);
2751 } else {
2752 // mark_adjust() may have changed the count in a wrong way
2753 dp->df_count[idx_to] = new_count;
2754 }
2755
2756 // When changing the current buffer, keep track of line numbers
2757 if (idx_cur == idx_to) {
2758 off += added;
2759 }
2760 }
2761
2762 // If before the range or not deleted, go to next diff.
2763 if (dfree == NULL) {
2764 dprev = dp;
2765 dp = dp->df_next;
2766 }
2767 }
2768
2769 // restore curwin/curbuf and a few other things
2770 if (eap->cmdidx != CMD_diffget) {
2771 // Syncing undo only works for the current buffer, but we change
2772 // another buffer. Sync undo if the command was typed. This isn't
2773 // 100% right when ":diffput" is used in a function or mapping.
2774 if (KeyTyped) {
2775 u_sync(false);
2776 }
2777 aucmd_restbuf(&aco);
2778 }
2779
2780 theend:
2781 diff_busy = false;
2782 if (diff_need_update) {
2783 ex_diffupdate(NULL);
2784 }
2785
2786 // Check that the cursor is on a valid character and update its
2787 // position. When there were filler lines the topline has become
2788 // invalid.
2789 check_cursor();
2790 changed_line_abv_curs();
2791
2792 if (diff_need_update) {
2793 // redraw already done by ex_diffupdate()
2794 diff_need_update = false;
2795 } else {
2796 // Also need to redraw the other buffers.
2797 diff_redraw(false);
2798 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
2799 }
2800 }
2801
2802 /// Update folds for all diff buffers for entry "dp".
2803 ///
2804 /// Skip buffer with index "skip_idx".
2805 /// When there are no diffs, all folds are removed.
2806 ///
2807 /// @param dp
2808 /// @param skip_idx
diff_fold_update(diff_T * dp,int skip_idx)2809 static void diff_fold_update(diff_T *dp, int skip_idx)
2810 {
2811 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
2812 for (int i = 0; i < DB_COUNT; ++i) {
2813 if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) {
2814 foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]);
2815 }
2816 }
2817 }
2818 }
2819
2820 /// Checks that the buffer is in diff-mode.
2821 ///
2822 /// @param buf buffer to check.
diff_mode_buf(buf_T * buf)2823 bool diff_mode_buf(buf_T *buf)
2824 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
2825 {
2826 FOR_ALL_TABS(tp) {
2827 if (diff_buf_idx_tp(buf, tp) != DB_COUNT) {
2828 return true;
2829 }
2830 }
2831 return false;
2832 }
2833
2834 /// Move "count" times in direction "dir" to the next diff block.
2835 ///
2836 /// @param dir
2837 /// @param count
2838 ///
2839 /// @return FAIL if there isn't such a diff block.
diff_move_to(int dir,long count)2840 int diff_move_to(int dir, long count)
2841 {
2842 linenr_T lnum = curwin->w_cursor.lnum;
2843 int idx = diff_buf_idx(curbuf);
2844 if ((idx == DB_COUNT) || (curtab->tp_first_diff == NULL)) {
2845 return FAIL;
2846 }
2847
2848 if (curtab->tp_diff_invalid) {
2849 // update after a big change
2850 ex_diffupdate(NULL);
2851 }
2852
2853 if (curtab->tp_first_diff == NULL) {
2854 // no diffs today
2855 return FAIL;
2856 }
2857
2858 while (--count >= 0) {
2859 // Check if already before first diff.
2860 if ((dir == BACKWARD) && (lnum <= curtab->tp_first_diff->df_lnum[idx])) {
2861 break;
2862 }
2863
2864 diff_T *dp;
2865 for (dp = curtab->tp_first_diff;; dp = dp->df_next) {
2866 if (dp == NULL) {
2867 break;
2868 }
2869
2870 if (((dir == FORWARD) && (lnum < dp->df_lnum[idx]))
2871 || ((dir == BACKWARD)
2872 && ((dp->df_next == NULL)
2873 || (lnum <= dp->df_next->df_lnum[idx])))) {
2874 lnum = dp->df_lnum[idx];
2875 break;
2876 }
2877 }
2878 }
2879
2880 // don't end up past the end of the file
2881 if (lnum > curbuf->b_ml.ml_line_count) {
2882 lnum = curbuf->b_ml.ml_line_count;
2883 }
2884
2885 // When the cursor didn't move at all we fail.
2886 if (lnum == curwin->w_cursor.lnum) {
2887 return FAIL;
2888 }
2889
2890 setpcmark();
2891 curwin->w_cursor.lnum = lnum;
2892 curwin->w_cursor.col = 0;
2893
2894 return OK;
2895 }
2896
2897 /// Return the line number in the current window that is closest to "lnum1" in
2898 /// "buf1" in diff mode.
diff_get_corresponding_line_int(buf_T * buf1,linenr_T lnum1)2899 static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1)
2900 {
2901 int idx1;
2902 int idx2;
2903 diff_T *dp;
2904 int baseline = 0;
2905
2906 idx1 = diff_buf_idx(buf1);
2907 idx2 = diff_buf_idx(curbuf);
2908
2909 if ((idx1 == DB_COUNT)
2910 || (idx2 == DB_COUNT)
2911 || (curtab->tp_first_diff == NULL)) {
2912 return lnum1;
2913 }
2914
2915 if (curtab->tp_diff_invalid) {
2916 // update after a big change
2917 ex_diffupdate(NULL);
2918 }
2919
2920 if (curtab->tp_first_diff == NULL) {
2921 // no diffs today
2922 return lnum1;
2923 }
2924
2925 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2926 if (dp->df_lnum[idx1] > lnum1) {
2927 return lnum1 - baseline;
2928 }
2929 if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) {
2930 // Inside the diffblock
2931 baseline = lnum1 - dp->df_lnum[idx1];
2932
2933 if (baseline > dp->df_count[idx2]) {
2934 baseline = dp->df_count[idx2];
2935 }
2936
2937 return dp->df_lnum[idx2] + baseline;
2938 }
2939 if ((dp->df_lnum[idx1] == lnum1)
2940 && (dp->df_count[idx1] == 0)
2941 && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum)
2942 && ((dp->df_lnum[idx2] + dp->df_count[idx2])
2943 > curwin->w_cursor.lnum)) {
2944 // Special case: if the cursor is just after a zero-count
2945 // block (i.e. all filler) and the target cursor is already
2946 // inside the corresponding block, leave the target cursor
2947 // unmoved. This makes repeated CTRL-W W operations work
2948 // as expected.
2949 return curwin->w_cursor.lnum;
2950 }
2951 baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
2952 - (dp->df_lnum[idx2] + dp->df_count[idx2]);
2953 }
2954
2955 // If we get here then the cursor is after the last diff
2956 return lnum1 - baseline;
2957 }
2958
2959 /// Finds the corresponding line in a diff.
2960 ///
2961 /// @param buf1
2962 /// @param lnum1
2963 ///
2964 /// @return The corresponding line.
diff_get_corresponding_line(buf_T * buf1,linenr_T lnum1)2965 linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1)
2966 {
2967 linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1);
2968
2969 // don't end up past the end of the file
2970 if (lnum > curbuf->b_ml.ml_line_count) {
2971 return curbuf->b_ml.ml_line_count;
2972 }
2973 return lnum;
2974 }
2975
2976 /// For line "lnum" in the current window find the equivalent lnum in window
2977 /// "wp", compensating for inserted/deleted lines.
diff_lnum_win(linenr_T lnum,win_T * wp)2978 linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
2979 {
2980 diff_T *dp;
2981 int idx;
2982 int i;
2983 linenr_T n;
2984
2985 idx = diff_buf_idx(curbuf);
2986
2987 if (idx == DB_COUNT) {
2988 // safety check
2989 return (linenr_T)0;
2990 }
2991
2992 if (curtab->tp_diff_invalid) {
2993 // update after a big change
2994 ex_diffupdate(NULL);
2995 }
2996
2997 // search for a change that includes "lnum" in the list of diffblocks.
2998 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2999 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
3000 break;
3001 }
3002 }
3003
3004 // When after the last change, compute relative to the last line number.
3005 if (dp == NULL) {
3006 return wp->w_buffer->b_ml.ml_line_count
3007 - (curbuf->b_ml.ml_line_count - lnum);
3008 }
3009
3010 // Find index for "wp".
3011 i = diff_buf_idx(wp->w_buffer);
3012
3013 if (i == DB_COUNT) {
3014 // safety check
3015 return (linenr_T)0;
3016 }
3017
3018 n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
3019 if (n > dp->df_lnum[i] + dp->df_count[i]) {
3020 n = dp->df_lnum[i] + dp->df_count[i];
3021 }
3022 return n;
3023 }
3024
3025 ///
3026 /// Handle an ED style diff line.
3027 /// Return FAIL if the line does not contain diff info.
3028 ///
parse_diff_ed(char_u * line,linenr_T * lnum_orig,long * count_orig,linenr_T * lnum_new,long * count_new)3029 static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new,
3030 long *count_new)
3031 {
3032 char_u *p;
3033 long f1, l1, f2, l2;
3034 int difftype;
3035
3036 // The line must be one of three formats:
3037 // change: {first}[,{last}]c{first}[,{last}]
3038 // append: {first}a{first}[,{last}]
3039 // delete: {first}[,{last}]d{first}
3040 p = line;
3041 f1 = getdigits(&p, true, 0);
3042 if (*p == ',') {
3043 p++;
3044 l1 = getdigits(&p, true, 0);
3045 } else {
3046 l1 = f1;
3047 }
3048 if (*p != 'a' && *p != 'c' && *p != 'd') {
3049 return FAIL; // invalid diff format
3050 }
3051 difftype = *p++;
3052 f2 = getdigits(&p, true, 0);
3053 if (*p == ',') {
3054 p++;
3055 l2 = getdigits(&p, true, 0);
3056 } else {
3057 l2 = f2;
3058 }
3059 if (l1 < f1 || l2 < f2) {
3060 return FAIL;
3061 }
3062
3063 if (difftype == 'a') {
3064 *lnum_orig = f1 + 1;
3065 *count_orig = 0;
3066 } else {
3067 *lnum_orig = f1;
3068 *count_orig = l1 - f1 + 1;
3069 }
3070 if (difftype == 'd') {
3071 *lnum_new = f2 + 1;
3072 *count_new = 0;
3073 } else {
3074 *lnum_new = f2;
3075 *count_new = l2 - f2 + 1;
3076 }
3077 return OK;
3078 }
3079
3080 ///
3081 /// Parses unified diff with zero(!) context lines.
3082 /// Return FAIL if there is no diff information in "line".
3083 ///
parse_diff_unified(char_u * line,linenr_T * lnum_orig,long * count_orig,linenr_T * lnum_new,long * count_new)3084 static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig,
3085 linenr_T *lnum_new, long *count_new)
3086 {
3087 char_u *p;
3088 long oldline, oldcount, newline, newcount;
3089
3090 // Parse unified diff hunk header:
3091 // @@ -oldline,oldcount +newline,newcount @@
3092 p = line;
3093 if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') {
3094 oldline = getdigits(&p, true, 0);
3095 if (*p == ',') {
3096 p++;
3097 oldcount = getdigits(&p, true, 0);
3098 } else {
3099 oldcount = 1;
3100 }
3101 if (*p++ == ' ' && *p++ == '+') {
3102 newline = getdigits(&p, true, 0);
3103 if (*p == ',') {
3104 p++;
3105 newcount = getdigits(&p, true, 0);
3106 } else {
3107 newcount = 1;
3108 }
3109 } else {
3110 return FAIL; // invalid diff format
3111 }
3112
3113 if (oldcount == 0) {
3114 oldline += 1;
3115 }
3116 if (newcount == 0) {
3117 newline += 1;
3118 }
3119 if (newline == 0) {
3120 newline = 1;
3121 }
3122
3123 *lnum_orig = oldline;
3124 *count_orig = oldcount;
3125 *lnum_new = newline;
3126 *count_new = newcount;
3127
3128 return OK;
3129 }
3130
3131 return FAIL;
3132 }
3133
3134 ///
3135 /// Callback function for the xdl_diff() function.
3136 /// Stores the diff output in a grow array.
3137 ///
xdiff_out(void * priv,mmbuffer_t * mb,int nbuf)3138 static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf)
3139 {
3140 diffout_T *dout = (diffout_T *)priv;
3141 char_u *p;
3142
3143 // The header line always comes by itself, text lines in at least two
3144 // parts. We drop the text part.
3145 if (nbuf > 1) {
3146 return 0;
3147 }
3148
3149 // sanity check
3150 if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) {
3151 return 0;
3152 }
3153
3154 ga_grow(&dout->dout_ga, 1);
3155
3156 p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size);
3157 ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
3158 return 0;
3159 }
3160