blob: 149022a119a076b5085f468fc30a49c587caa9dc [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar5cc6a6e2009-01-22 19:48:55 +000011 * diff.c: code for diff'ing two, three or four buffers.
Bram Moolenaare828b762018-09-10 17:51:58 +020012 *
13 * There are three ways to diff:
14 * - Shell out to an external diff program, using files.
15 * - Use the compiled-in xdiff library.
16 * - Let 'diffexpr' do the work, using files.
Bram Moolenaar071d4272004-06-13 20:20:40 +000017 */
18
19#include "vim.h"
Bram Moolenaare828b762018-09-10 17:51:58 +020020#include "xdiff/xdiff.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +000021
22#if defined(FEAT_DIFF) || defined(PROTO)
23
Bram Moolenaar071d4272004-06-13 20:20:40 +000024static int diff_busy = FALSE; /* ex_diffgetput() is busy */
25
26/* flags obtained from the 'diffopt' option */
Bram Moolenaar785fc652018-09-15 19:17:38 +020027#define DIFF_FILLER 0x001 // display filler lines
28#define DIFF_IBLANK 0x002 // ignore empty lines
29#define DIFF_ICASE 0x004 // ignore case
30#define DIFF_IWHITE 0x008 // ignore change in white space
31#define DIFF_IWHITEALL 0x010 // ignore all white space changes
32#define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL
33#define DIFF_HORIZONTAL 0x040 // horizontal splits
34#define DIFF_VERTICAL 0x080 // vertical splits
35#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden
36#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
37#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
Bram Moolenaar274cea32018-09-12 18:00:12 +020038static int diff_flags = DIFF_INTERNAL | DIFF_FILLER;
Bram Moolenaar071d4272004-06-13 20:20:40 +000039
Bram Moolenaare828b762018-09-10 17:51:58 +020040static long diff_algorithm = 0;
41
Bram Moolenaar071d4272004-06-13 20:20:40 +000042#define LBUFLEN 50 /* length of line in diff file */
43
44static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it
45 doesn't work, MAYBE when not checked yet */
Bram Moolenaar48e330a2016-02-23 14:53:34 +010046#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +000047static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE
48 when it doesn't work, MAYBE when not
49 checked yet */
50#endif
51
Bram Moolenaare828b762018-09-10 17:51:58 +020052// used for diff input
53typedef struct {
54 char_u *din_fname; // used for external diff
55 mmfile_t din_mmfile; // used for internal diff
56} diffin_T;
57
58// used for diff result
59typedef struct {
60 char_u *dout_fname; // used for external diff
61 garray_T dout_ga; // used for internal diff
62} diffout_T;
63
64// two diff inputs and one result
65typedef struct {
66 diffin_T dio_orig; // original file input
67 diffin_T dio_new; // new file input
68 diffout_T dio_diff; // diff result
69 int dio_internal; // using internal diff
70} diffio_T;
71
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010072static int diff_buf_idx(buf_T *buf);
73static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp);
74static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after);
75static void diff_check_unchanged(tabpage_T *tp, diff_T *dp);
76static int diff_check_sanity(tabpage_T *tp, diff_T *dp);
77static void diff_redraw(int dofold);
Bram Moolenaare828b762018-09-10 17:51:58 +020078static int check_external_diff(diffio_T *diffio);
79static int diff_file(diffio_T *diffio);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010080static int diff_equal_entry(diff_T *dp, int idx1, int idx2);
81static int diff_cmp(char_u *s1, char_u *s2);
Bram Moolenaar071d4272004-06-13 20:20:40 +000082#ifdef FEAT_FOLDING
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010083static void diff_fold_update(diff_T *dp, int skip_idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +000084#endif
Bram Moolenaare828b762018-09-10 17:51:58 +020085static void diff_read(int idx_orig, int idx_new, diffout_T *fname);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010086static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new);
87static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp);
Bram Moolenaare828b762018-09-10 17:51:58 +020088static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new);
89static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new);
90static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
92#ifndef USE_CR
93# define tag_fgets vim_fgets
94#endif
95
96/*
Bram Moolenaar071d4272004-06-13 20:20:40 +000097 * Called when deleting or unloading a buffer: No longer make a diff with it.
Bram Moolenaar071d4272004-06-13 20:20:40 +000098 */
99 void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100100diff_buf_delete(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101{
102 int i;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000103 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104
Bram Moolenaar29323592016-07-24 22:04:11 +0200105 FOR_ALL_TABPAGES(tp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000106 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000107 i = diff_buf_idx_tp(buf, tp);
108 if (i != DB_COUNT)
109 {
110 tp->tp_diffbuf[i] = NULL;
111 tp->tp_diff_invalid = TRUE;
Bram Moolenaar5d55c0f2008-11-30 14:16:57 +0000112 if (tp == curtab)
113 diff_redraw(TRUE);
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000114 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115 }
116}
117
118/*
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000119 * Check if the current buffer should be added to or removed from the list of
120 * diff buffers.
121 */
122 void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100123diff_buf_adjust(win_T *win)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000124{
125 win_T *wp;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000126 int i;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000127
128 if (!win->w_p_diff)
129 {
130 /* When there is no window showing a diff for this buffer, remove
131 * it from the diffs. */
Bram Moolenaar29323592016-07-24 22:04:11 +0200132 FOR_ALL_WINDOWS(wp)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000133 if (wp->w_buffer == win->w_buffer && wp->w_p_diff)
134 break;
135 if (wp == NULL)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000136 {
137 i = diff_buf_idx(win->w_buffer);
138 if (i != DB_COUNT)
139 {
140 curtab->tp_diffbuf[i] = NULL;
141 curtab->tp_diff_invalid = TRUE;
Bram Moolenaar5d55c0f2008-11-30 14:16:57 +0000142 diff_redraw(TRUE);
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000143 }
144 }
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000145 }
146 else
147 diff_buf_add(win->w_buffer);
148}
149
150/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000151 * Add a buffer to make diffs for.
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000152 * Call this when a new buffer is being edited in the current window where
153 * 'diff' is set.
Bram Moolenaar5cc6a6e2009-01-22 19:48:55 +0000154 * Marks the current buffer as being part of the diff and requiring updating.
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000155 * This must be done before any autocmd, because a command may use info
156 * about the screen contents.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 */
158 void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100159diff_buf_add(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160{
161 int i;
162
163 if (diff_buf_idx(buf) != DB_COUNT)
164 return; /* It's already there. */
165
166 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000167 if (curtab->tp_diffbuf[i] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000169 curtab->tp_diffbuf[i] = buf;
170 curtab->tp_diff_invalid = TRUE;
Bram Moolenaar5d55c0f2008-11-30 14:16:57 +0000171 diff_redraw(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172 return;
173 }
174
Bram Moolenaar89eaa412016-07-31 14:17:27 +0200175 EMSGN(_("E96: Cannot diff more than %ld buffers"), DB_COUNT);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176}
177
178/*
Bram Moolenaar25ea0542017-02-03 23:16:28 +0100179 * Remove all buffers to make diffs for.
180 */
181 static void
182diff_buf_clear(void)
183{
184 int i;
185
186 for (i = 0; i < DB_COUNT; ++i)
187 if (curtab->tp_diffbuf[i] != NULL)
188 {
189 curtab->tp_diffbuf[i] = NULL;
190 curtab->tp_diff_invalid = TRUE;
191 diff_redraw(TRUE);
192 }
193}
194
195/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000196 * Find buffer "buf" in the list of diff buffers for the current tab page.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197 * Return its index or DB_COUNT if not found.
198 */
199 static int
Bram Moolenaar7454a062016-01-30 15:14:10 +0100200diff_buf_idx(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201{
202 int idx;
203
204 for (idx = 0; idx < DB_COUNT; ++idx)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000205 if (curtab->tp_diffbuf[idx] == buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206 break;
207 return idx;
208}
209
210/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000211 * Find buffer "buf" in the list of diff buffers for tab page "tp".
212 * Return its index or DB_COUNT if not found.
213 */
214 static int
Bram Moolenaar7454a062016-01-30 15:14:10 +0100215diff_buf_idx_tp(buf_T *buf, tabpage_T *tp)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000216{
217 int idx;
218
219 for (idx = 0; idx < DB_COUNT; ++idx)
220 if (tp->tp_diffbuf[idx] == buf)
221 break;
222 return idx;
223}
224
225/*
226 * Mark the diff info involving buffer "buf" as invalid, it will be updated
227 * when info is requested.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228 */
229 void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100230diff_invalidate(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000232 tabpage_T *tp;
233 int i;
234
Bram Moolenaar29323592016-07-24 22:04:11 +0200235 FOR_ALL_TABPAGES(tp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000237 i = diff_buf_idx_tp(buf, tp);
238 if (i != DB_COUNT)
239 {
240 tp->tp_diff_invalid = TRUE;
241 if (tp == curtab)
242 diff_redraw(TRUE);
243 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244 }
245}
246
247/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000248 * Called by mark_adjust(): update line numbers in "curbuf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 */
250 void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100251diff_mark_adjust(
252 linenr_T line1,
253 linenr_T line2,
254 long amount,
255 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000257 int idx;
258 tabpage_T *tp;
259
260 /* Handle all tab pages that use the current buffer in a diff. */
Bram Moolenaar29323592016-07-24 22:04:11 +0200261 FOR_ALL_TABPAGES(tp)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000262 {
263 idx = diff_buf_idx_tp(curbuf, tp);
264 if (idx != DB_COUNT)
265 diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after);
266 }
267}
268
269/*
270 * Update line numbers in tab page "tp" for "curbuf" with index "idx".
271 * This attempts to update the changes as much as possible:
272 * When inserting/deleting lines outside of existing change blocks, create a
273 * new change block and update the line numbers in following blocks.
274 * When inserting/deleting lines in existing change blocks, update them.
275 */
276 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100277diff_mark_adjust_tp(
278 tabpage_T *tp,
279 int idx,
280 linenr_T line1,
281 linenr_T line2,
282 long amount,
283 long amount_after)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000284{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 diff_T *dp;
286 diff_T *dprev;
287 diff_T *dnext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288 int i;
289 int inserted, deleted;
290 int n, off;
291 linenr_T last;
292 linenr_T lnum_deleted = line1; /* lnum of remaining deletion */
293 int check_unchanged;
294
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295 if (line2 == MAXLNUM)
296 {
297 /* mark_adjust(99, MAXLNUM, 9, 0): insert lines */
298 inserted = amount;
299 deleted = 0;
300 }
301 else if (amount_after > 0)
302 {
303 /* mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines*/
304 inserted = amount_after;
305 deleted = 0;
306 }
307 else
308 {
309 /* mark_adjust(98, 99, MAXLNUM, -2): delete lines */
310 inserted = 0;
311 deleted = -amount_after;
312 }
313
314 dprev = NULL;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000315 dp = tp->tp_first_diff;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316 for (;;)
317 {
318 /* If the change is after the previous diff block and before the next
319 * diff block, thus not touching an existing change, create a new diff
320 * block. Don't do this when ex_diffgetput() is busy. */
321 if ((dp == NULL || dp->df_lnum[idx] - 1 > line2
322 || (line2 == MAXLNUM && dp->df_lnum[idx] > line1))
323 && (dprev == NULL
324 || dprev->df_lnum[idx] + dprev->df_count[idx] < line1)
325 && !diff_busy)
326 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000327 dnext = diff_alloc_new(tp, dprev, dp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 if (dnext == NULL)
329 return;
330
331 dnext->df_lnum[idx] = line1;
332 dnext->df_count[idx] = inserted;
333 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000334 if (tp->tp_diffbuf[i] != NULL && i != idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 {
336 if (dprev == NULL)
337 dnext->df_lnum[i] = line1;
338 else
339 dnext->df_lnum[i] = line1
340 + (dprev->df_lnum[i] + dprev->df_count[i])
341 - (dprev->df_lnum[idx] + dprev->df_count[idx]);
342 dnext->df_count[i] = deleted;
343 }
344 }
345
346 /* if at end of the list, quit */
347 if (dp == NULL)
348 break;
349
350 /*
351 * Check for these situations:
352 * 1 2 3
353 * 1 2 3
354 * line1 2 3 4 5
355 * 2 3 4 5
356 * 2 3 4 5
357 * line2 2 3 4 5
358 * 3 5 6
359 * 3 5 6
360 */
361 /* compute last line of this change */
362 last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
363
364 /* 1. change completely above line1: nothing to do */
365 if (last >= line1 - 1)
366 {
367 /* 6. change below line2: only adjust for amount_after; also when
368 * "deleted" became zero when deleted all lines between two diffs */
369 if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2)
370 {
371 if (amount_after == 0)
372 break; /* nothing left to change */
373 dp->df_lnum[idx] += amount_after;
374 }
375 else
376 {
377 check_unchanged = FALSE;
378
379 /* 2. 3. 4. 5.: inserted/deleted lines touching this diff. */
380 if (deleted > 0)
381 {
382 if (dp->df_lnum[idx] >= line1)
383 {
384 off = dp->df_lnum[idx] - lnum_deleted;
385 if (last <= line2)
386 {
387 /* 4. delete all lines of diff */
388 if (dp->df_next != NULL
389 && dp->df_next->df_lnum[idx] - 1 <= line2)
390 {
391 /* delete continues in next diff, only do
392 * lines until that one */
393 n = dp->df_next->df_lnum[idx] - lnum_deleted;
394 deleted -= n;
395 n -= dp->df_count[idx];
396 lnum_deleted = dp->df_next->df_lnum[idx];
397 }
398 else
399 n = deleted - dp->df_count[idx];
400 dp->df_count[idx] = 0;
401 }
402 else
403 {
404 /* 5. delete lines at or just before top of diff */
405 n = off;
406 dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1;
407 check_unchanged = TRUE;
408 }
409 dp->df_lnum[idx] = line1;
410 }
411 else
412 {
413 off = 0;
414 if (last < line2)
415 {
416 /* 2. delete at end of of diff */
417 dp->df_count[idx] -= last - lnum_deleted + 1;
418 if (dp->df_next != NULL
419 && dp->df_next->df_lnum[idx] - 1 <= line2)
420 {
421 /* delete continues in next diff, only do
422 * lines until that one */
423 n = dp->df_next->df_lnum[idx] - 1 - last;
424 deleted -= dp->df_next->df_lnum[idx]
425 - lnum_deleted;
426 lnum_deleted = dp->df_next->df_lnum[idx];
427 }
428 else
429 n = line2 - last;
430 check_unchanged = TRUE;
431 }
432 else
433 {
434 /* 3. delete lines inside the diff */
435 n = 0;
436 dp->df_count[idx] -= deleted;
437 }
438 }
439
440 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000441 if (tp->tp_diffbuf[i] != NULL && i != idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442 {
443 dp->df_lnum[i] -= off;
444 dp->df_count[i] += n;
445 }
446 }
447 else
448 {
449 if (dp->df_lnum[idx] <= line1)
450 {
451 /* inserted lines somewhere in this diff */
452 dp->df_count[idx] += inserted;
453 check_unchanged = TRUE;
454 }
455 else
456 /* inserted lines somewhere above this diff */
457 dp->df_lnum[idx] += inserted;
458 }
459
460 if (check_unchanged)
461 /* Check if inserted lines are equal, may reduce the
462 * size of the diff. TODO: also check for equal lines
463 * in the middle and perhaps split the block. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000464 diff_check_unchanged(tp, dp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465 }
466 }
467
468 /* check if this block touches the previous one, may merge them. */
469 if (dprev != NULL && dprev->df_lnum[idx] + dprev->df_count[idx]
470 == dp->df_lnum[idx])
471 {
472 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000473 if (tp->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 dprev->df_count[i] += dp->df_count[i];
475 dprev->df_next = dp->df_next;
476 vim_free(dp);
477 dp = dprev->df_next;
478 }
479 else
480 {
481 /* Advance to next entry. */
482 dprev = dp;
483 dp = dp->df_next;
484 }
485 }
486
487 dprev = NULL;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000488 dp = tp->tp_first_diff;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 while (dp != NULL)
490 {
491 /* All counts are zero, remove this entry. */
492 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000493 if (tp->tp_diffbuf[i] != NULL && dp->df_count[i] != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 break;
495 if (i == DB_COUNT)
496 {
497 dnext = dp->df_next;
498 vim_free(dp);
499 dp = dnext;
500 if (dprev == NULL)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000501 tp->tp_first_diff = dnext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 else
503 dprev->df_next = dnext;
504 }
505 else
506 {
507 /* Advance to next entry. */
508 dprev = dp;
509 dp = dp->df_next;
510 }
511
512 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000514 if (tp == curtab)
515 {
516 diff_redraw(TRUE);
517
518 /* Need to recompute the scroll binding, may remove or add filler
519 * lines (e.g., when adding lines above w_topline). But it's slow when
520 * making many changes, postpone until redrawing. */
521 diff_need_scrollbind = TRUE;
522 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523}
524
525/*
526 * Allocate a new diff block and link it between "dprev" and "dp".
527 */
528 static diff_T *
Bram Moolenaar7454a062016-01-30 15:14:10 +0100529diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530{
531 diff_T *dnew;
532
533 dnew = (diff_T *)alloc((unsigned)sizeof(diff_T));
534 if (dnew != NULL)
535 {
536 dnew->df_next = dp;
537 if (dprev == NULL)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000538 tp->tp_first_diff = dnew;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 else
540 dprev->df_next = dnew;
541 }
542 return dnew;
543}
544
545/*
546 * Check if the diff block "dp" can be made smaller for lines at the start and
547 * end that are equal. Called after inserting lines.
548 * This may result in a change where all buffers have zero lines, the caller
549 * must take care of removing it.
550 */
551 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100552diff_check_unchanged(tabpage_T *tp, diff_T *dp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553{
554 int i_org;
555 int i_new;
556 int off_org, off_new;
557 char_u *line_org;
558 int dir = FORWARD;
559
560 /* Find the first buffers, use it as the original, compare the other
561 * buffer lines against this one. */
562 for (i_org = 0; i_org < DB_COUNT; ++i_org)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000563 if (tp->tp_diffbuf[i_org] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000564 break;
565 if (i_org == DB_COUNT) /* safety check */
566 return;
567
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000568 if (diff_check_sanity(tp, dp) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 return;
570
571 /* First check lines at the top, then at the bottom. */
572 off_org = 0;
573 off_new = 0;
574 for (;;)
575 {
576 /* Repeat until a line is found which is different or the number of
577 * lines has become zero. */
578 while (dp->df_count[i_org] > 0)
579 {
580 /* Copy the line, the next ml_get() will invalidate it. */
581 if (dir == BACKWARD)
582 off_org = dp->df_count[i_org] - 1;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000583 line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584 dp->df_lnum[i_org] + off_org, FALSE));
585 if (line_org == NULL)
586 return;
587 for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new)
588 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000589 if (tp->tp_diffbuf[i_new] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000590 continue;
591 if (dir == BACKWARD)
592 off_new = dp->df_count[i_new] - 1;
593 /* if other buffer doesn't have this line, it was inserted */
594 if (off_new < 0 || off_new >= dp->df_count[i_new])
595 break;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000596 if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597 dp->df_lnum[i_new] + off_new, FALSE)) != 0)
598 break;
599 }
600 vim_free(line_org);
601
602 /* Stop when a line isn't equal in all diff buffers. */
603 if (i_new != DB_COUNT)
604 break;
605
606 /* Line matched in all buffers, remove it from the diff. */
607 for (i_new = i_org; i_new < DB_COUNT; ++i_new)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000608 if (tp->tp_diffbuf[i_new] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609 {
610 if (dir == FORWARD)
611 ++dp->df_lnum[i_new];
612 --dp->df_count[i_new];
613 }
614 }
615 if (dir == BACKWARD)
616 break;
617 dir = BACKWARD;
618 }
619}
620
621/*
622 * Check if a diff block doesn't contain invalid line numbers.
623 * This can happen when the diff program returns invalid results.
624 */
625 static int
Bram Moolenaar7454a062016-01-30 15:14:10 +0100626diff_check_sanity(tabpage_T *tp, diff_T *dp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000627{
628 int i;
629
630 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000631 if (tp->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000632 if (dp->df_lnum[i] + dp->df_count[i] - 1
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000633 > tp->tp_diffbuf[i]->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634 return FAIL;
635 return OK;
636}
637
638/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000639 * Mark all diff buffers in the current tab page for redraw.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 */
641 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +0100642diff_redraw(
643 int dofold) /* also recompute the folds */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644{
645 win_T *wp;
646 int n;
647
Bram Moolenaar29323592016-07-24 22:04:11 +0200648 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 if (wp->w_p_diff)
650 {
Bram Moolenaar60f83772006-03-12 21:52:47 +0000651 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652#ifdef FEAT_FOLDING
653 if (dofold && foldmethodIsDiff(wp))
654 foldUpdateAll(wp);
655#endif
656 /* A change may have made filler lines invalid, need to take care
657 * of that for other windows. */
Bram Moolenaara80888d2012-10-21 22:18:21 +0200658 n = diff_check(wp, wp->w_topline);
659 if ((wp != curwin && wp->w_topfill > 0) || n > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661 if (wp->w_topfill > n)
662 wp->w_topfill = (n < 0 ? 0 : n);
Bram Moolenaara80888d2012-10-21 22:18:21 +0200663 else if (n > 0 && n > wp->w_topfill)
664 wp->w_topfill = n;
Bram Moolenaar846a2ff2014-05-28 11:35:37 +0200665 check_topfill(wp, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666 }
667 }
668}
669
Bram Moolenaare828b762018-09-10 17:51:58 +0200670 static void
671clear_diffin(diffin_T *din)
672{
673 if (din->din_fname == NULL)
674 {
675 vim_free(din->din_mmfile.ptr);
676 din->din_mmfile.ptr = NULL;
677 }
678 else
679 mch_remove(din->din_fname);
680}
681
682 static void
683clear_diffout(diffout_T *dout)
684{
685 if (dout->dout_fname == NULL)
686 ga_clear_strings(&dout->dout_ga);
687 else
688 mch_remove(dout->dout_fname);
689}
690
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691/*
Bram Moolenaare828b762018-09-10 17:51:58 +0200692 * Write buffer "buf" to a memory buffer.
693 * Return FAIL for failure.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694 */
695 static int
Bram Moolenaare828b762018-09-10 17:51:58 +0200696diff_write_buffer(buf_T *buf, diffin_T *din)
697{
698 linenr_T lnum;
699 char_u *s;
700 long len = 0;
701 char_u *ptr;
702
703 // xdiff requires one big block of memory with all the text.
704 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
705 len += STRLEN(ml_get_buf(buf, lnum, FALSE)) + 1;
706 ptr = lalloc(len, TRUE);
707 if (ptr == NULL)
708 {
709 // Allocating memory failed. This can happen, because we try to read
710 // the whole buffer text into memory. Set the failed flag, the diff
711 // will be retried with external diff. The flag is never reset.
712 buf->b_diff_failed = TRUE;
713 if (p_verbose > 0)
714 {
715 verbose_enter();
716 smsg((char_u *)
717 _("Not enough memory to use internal diff for buffer \"%s\""),
718 buf->b_fname);
719 verbose_leave();
720 }
721 return FAIL;
722 }
723 din->din_mmfile.ptr = (char *)ptr;
724 din->din_mmfile.size = len;
725
726 len = 0;
727 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
728 {
729 for (s = ml_get_buf(buf, lnum, FALSE); *s != NUL; )
730 {
731 if (diff_flags & DIFF_ICASE)
732 {
733 int c;
734
735 // xdiff doesn't support ignoring case, fold-case the text.
736#ifdef FEAT_MBYTE
737 int orig_len;
738 char_u cbuf[MB_MAXBYTES + 1];
739
740 c = PTR2CHAR(s);
741 c = enc_utf8 ? utf_fold(c) : MB_TOLOWER(c);
742 orig_len = MB_PTR2LEN(s);
743 if (mb_char2bytes(c, cbuf) != orig_len)
744 // TODO: handle byte length difference
745 mch_memmove(ptr + len, s, orig_len);
746 else
747 mch_memmove(ptr + len, cbuf, orig_len);
748
749 s += orig_len;
750 len += orig_len;
751#else
752 c = *s++;
753 ptr[len++] = TOLOWER_LOC(c);
754#endif
755 }
756 else
757 ptr[len++] = *s++;
758 }
759 ptr[len++] = NL;
760 }
761 return OK;
762}
763
764/*
765 * Write buffer "buf" to file or memory buffer.
766 * Return FAIL for failure.
767 */
768 static int
769diff_write(buf_T *buf, diffin_T *din)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770{
771 int r;
772 char_u *save_ff;
773
Bram Moolenaare828b762018-09-10 17:51:58 +0200774 if (din->din_fname == NULL)
775 return diff_write_buffer(buf, din);
776
777 // Always use 'fileformat' set to "unix".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 save_ff = buf->b_p_ff;
779 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
Bram Moolenaare828b762018-09-10 17:51:58 +0200780 r = buf_write(buf, din->din_fname, NULL,
781 (linenr_T)1, buf->b_ml.ml_line_count,
782 NULL, FALSE, FALSE, FALSE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783 free_string_option(buf->b_p_ff);
784 buf->b_p_ff = save_ff;
785 return r;
786}
787
788/*
Bram Moolenaare828b762018-09-10 17:51:58 +0200789 * Update the diffs for all buffers involved.
790 */
791 static void
792diff_try_update(
793 diffio_T *dio,
794 int idx_orig,
795 exarg_T *eap) // "eap" can be NULL
796{
797 buf_T *buf;
798 int idx_new;
799
800 if (dio->dio_internal)
801 {
802 ga_init2(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
803 }
804 else
805 {
806 // We need three temp file names.
807 dio->dio_orig.din_fname = vim_tempname('o', TRUE);
808 dio->dio_new.din_fname = vim_tempname('n', TRUE);
809 dio->dio_diff.dout_fname = vim_tempname('d', TRUE);
810 if (dio->dio_orig.din_fname == NULL
811 || dio->dio_new.din_fname == NULL
812 || dio->dio_diff.dout_fname == NULL)
813 goto theend;
814 }
815
816 // Check external diff is actually working.
817 if (!dio->dio_internal && check_external_diff(dio) == FAIL)
818 goto theend;
819
820 // :diffupdate!
821 if (eap != NULL && eap->forceit)
822 for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new)
823 {
824 buf = curtab->tp_diffbuf[idx_new];
825 if (buf_valid(buf))
826 buf_check_timestamp(buf, FALSE);
827 }
828
829 // Write the first buffer to a tempfile or mmfile_t.
830 buf = curtab->tp_diffbuf[idx_orig];
831 if (diff_write(buf, &dio->dio_orig) == FAIL)
832 goto theend;
833
834 // Make a difference between the first buffer and every other.
835 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
836 {
837 buf = curtab->tp_diffbuf[idx_new];
838 if (buf == NULL || buf->b_ml.ml_mfp == NULL)
839 continue; // skip buffer that isn't loaded
840
841 // Write the other buffer and diff with the first one.
842 if (diff_write(buf, &dio->dio_new) == FAIL)
843 continue;
844 if (diff_file(dio) == FAIL)
845 continue;
846
847 // Read the diff output and add each entry to the diff list.
848 diff_read(idx_orig, idx_new, &dio->dio_diff);
849
850 clear_diffin(&dio->dio_new);
851 clear_diffout(&dio->dio_diff);
852 }
853 clear_diffin(&dio->dio_orig);
854
855theend:
856 vim_free(dio->dio_orig.din_fname);
857 vim_free(dio->dio_new.din_fname);
858 vim_free(dio->dio_diff.dout_fname);
859}
860
861/*
862 * Return TRUE if the options are set to use the internal diff library.
863 * Note that if the internal diff failed for one of the buffers, the external
864 * diff will be used anyway.
865 */
866 static int
867diff_internal(void)
868{
869 return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL;
870}
871
872/*
873 * Return TRUE if the internal diff failed for one of the diff buffers.
874 */
875 static int
876diff_internal_failed(void)
877{
878 int idx;
879
880 // Only need to do something when there is another buffer.
881 for (idx = 0; idx < DB_COUNT; ++idx)
882 if (curtab->tp_diffbuf[idx] != NULL
883 && curtab->tp_diffbuf[idx]->b_diff_failed)
884 return TRUE;
885 return FALSE;
886}
887
888/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889 * Completely update the diffs for the buffers involved.
890 * This uses the ordinary "diff" command.
891 * The buffers are written to a file, also for unmodified buffers (the file
892 * could have been produced by autocommands, e.g. the netrw plugin).
893 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894 void
Bram Moolenaare828b762018-09-10 17:51:58 +0200895ex_diffupdate(exarg_T *eap) // "eap" can be NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 int idx_orig;
898 int idx_new;
Bram Moolenaare828b762018-09-10 17:51:58 +0200899 diffio_T diffio;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900
Bram Moolenaare828b762018-09-10 17:51:58 +0200901 // Delete all diffblocks.
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000902 diff_clear(curtab);
903 curtab->tp_diff_invalid = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904
Bram Moolenaare828b762018-09-10 17:51:58 +0200905 // Use the first buffer as the original text.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000907 if (curtab->tp_diffbuf[idx_orig] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908 break;
909 if (idx_orig == DB_COUNT)
910 return;
911
Bram Moolenaare828b762018-09-10 17:51:58 +0200912 // Only need to do something when there is another buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +0000914 if (curtab->tp_diffbuf[idx_new] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000915 break;
916 if (idx_new == DB_COUNT)
917 return;
918
Bram Moolenaare828b762018-09-10 17:51:58 +0200919 // Only use the internal method if it did not fail for one of the buffers.
920 vim_memset(&diffio, 0, sizeof(diffio));
921 diffio.dio_internal = diff_internal() && !diff_internal_failed();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922
Bram Moolenaare828b762018-09-10 17:51:58 +0200923 diff_try_update(&diffio, idx_orig, eap);
924 if (diffio.dio_internal && diff_internal_failed())
925 {
926 // Internal diff failed, use external diff instead.
927 vim_memset(&diffio, 0, sizeof(diffio));
928 diff_try_update(&diffio, idx_orig, eap);
929 }
930
931 // force updating cursor position on screen
932 curwin->w_valid_cursor.lnum = 0;
933
934 diff_redraw(TRUE);
935}
936
937/*
938 * Do a quick test if "diff" really works. Otherwise it looks like there
939 * are no differences. Can't use the return value, it's non-zero when
940 * there are differences.
941 */
942 static int
943check_external_diff(diffio_T *diffio)
944{
945 FILE *fd;
946 int ok;
947 int io_error = FALSE;
948
949 // May try twice, first with "-a" and then without.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950 for (;;)
951 {
952 ok = FALSE;
Bram Moolenaare828b762018-09-10 17:51:58 +0200953 fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w");
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000954 if (fd == NULL)
955 io_error = TRUE;
956 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000957 {
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000958 if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1)
959 io_error = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960 fclose(fd);
Bram Moolenaare828b762018-09-10 17:51:58 +0200961 fd = mch_fopen((char *)diffio->dio_new.din_fname, "w");
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000962 if (fd == NULL)
963 io_error = TRUE;
964 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965 {
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000966 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1)
967 io_error = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000968 fclose(fd);
Bram Moolenaare828b762018-09-10 17:51:58 +0200969 fd = NULL;
970 if (diff_file(diffio) == OK)
971 fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r");
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +0000972 if (fd == NULL)
973 io_error = TRUE;
974 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975 {
976 char_u linebuf[LBUFLEN];
977
978 for (;;)
979 {
980 /* There must be a line that contains "1c1". */
981 if (tag_fgets(linebuf, LBUFLEN, fd))
982 break;
983 if (STRNCMP(linebuf, "1c1", 3) == 0)
984 ok = TRUE;
985 }
986 fclose(fd);
987 }
Bram Moolenaare828b762018-09-10 17:51:58 +0200988 mch_remove(diffio->dio_diff.dout_fname);
989 mch_remove(diffio->dio_new.din_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000990 }
Bram Moolenaare828b762018-09-10 17:51:58 +0200991 mch_remove(diffio->dio_orig.din_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992 }
993
994#ifdef FEAT_EVAL
995 /* When using 'diffexpr' break here. */
996 if (*p_dex != NUL)
997 break;
998#endif
999
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001000#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 /* If the "-a" argument works, also check if "--binary" works. */
1002 if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE)
1003 {
1004 diff_a_works = TRUE;
1005 diff_bin_works = TRUE;
1006 continue;
1007 }
1008 if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE)
1009 {
1010 /* Tried --binary, but it failed. "-a" works though. */
1011 diff_bin_works = FALSE;
1012 ok = TRUE;
1013 }
1014#endif
1015
1016 /* If we checked if "-a" works already, break here. */
1017 if (diff_a_works != MAYBE)
1018 break;
1019 diff_a_works = ok;
1020
1021 /* If "-a" works break here, otherwise retry without "-a". */
1022 if (ok)
1023 break;
1024 }
1025 if (!ok)
1026 {
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00001027 if (io_error)
1028 EMSG(_("E810: Cannot read or write temp files"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 EMSG(_("E97: Cannot create diffs"));
1030 diff_a_works = MAYBE;
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001031#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032 diff_bin_works = MAYBE;
1033#endif
Bram Moolenaare828b762018-09-10 17:51:58 +02001034 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 }
Bram Moolenaare828b762018-09-10 17:51:58 +02001036 return OK;
1037}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038
Bram Moolenaare828b762018-09-10 17:51:58 +02001039/*
1040 * Invoke the xdiff function.
1041 */
1042 static int
1043diff_file_internal(diffio_T *diffio)
1044{
1045 xpparam_t param;
1046 xdemitconf_t emit_cfg;
1047 xdemitcb_t emit_cb;
Bram Moolenaarbd1d5602012-05-18 18:47:17 +02001048
Bram Moolenaare828b762018-09-10 17:51:58 +02001049 vim_memset(&param, 0, sizeof(param));
1050 vim_memset(&emit_cfg, 0, sizeof(emit_cfg));
1051 vim_memset(&emit_cb, 0, sizeof(emit_cb));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052
Bram Moolenaare828b762018-09-10 17:51:58 +02001053 param.flags = diff_algorithm;
1054
1055 if (diff_flags & DIFF_IWHITE)
1056 param.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
Bram Moolenaar785fc652018-09-15 19:17:38 +02001057 if (diff_flags & DIFF_IWHITEALL)
1058 param.flags |= XDF_IGNORE_WHITESPACE;
1059 if (diff_flags & DIFF_IWHITEEOL)
1060 param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
1061 if (diff_flags & DIFF_IBLANK)
1062 param.flags |= XDF_IGNORE_BLANK_LINES;
Bram Moolenaare828b762018-09-10 17:51:58 +02001063
1064 emit_cfg.ctxlen = 0; // don't need any diff_context here
1065 emit_cb.priv = &diffio->dio_diff;
1066 emit_cb.outf = xdiff_out;
1067 if (xdl_diff(&diffio->dio_orig.din_mmfile,
1068 &diffio->dio_new.din_mmfile,
1069 &param, &emit_cfg, &emit_cb) < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001071 EMSG(_("E960: Problem creating the internal diff"));
1072 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073 }
Bram Moolenaare828b762018-09-10 17:51:58 +02001074 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075}
1076
1077/*
1078 * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff".
Bram Moolenaare828b762018-09-10 17:51:58 +02001079 * return OK or FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 */
Bram Moolenaare828b762018-09-10 17:51:58 +02001081 static int
1082diff_file(diffio_T *dio)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001083{
1084 char_u *cmd;
Bram Moolenaar5fd0ca72009-05-13 16:56:33 +00001085 size_t len;
Bram Moolenaare828b762018-09-10 17:51:58 +02001086 char_u *tmp_orig = dio->dio_orig.din_fname;
1087 char_u *tmp_new = dio->dio_new.din_fname;
1088 char_u *tmp_diff = dio->dio_diff.dout_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001089
1090#ifdef FEAT_EVAL
1091 if (*p_dex != NUL)
Bram Moolenaare828b762018-09-10 17:51:58 +02001092 {
1093 // Use 'diffexpr' to generate the diff file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 eval_diff(tmp_orig, tmp_new, tmp_diff);
Bram Moolenaare828b762018-09-10 17:51:58 +02001095 return OK;
1096 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 else
1098#endif
Bram Moolenaare828b762018-09-10 17:51:58 +02001099 // Use xdiff for generating the diff.
1100 if (dio->dio_internal)
1101 {
1102 return diff_file_internal(dio);
1103 }
1104 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001105 {
Bram Moolenaar5fd0ca72009-05-13 16:56:33 +00001106 len = STRLEN(tmp_orig) + STRLEN(tmp_new)
1107 + STRLEN(tmp_diff) + STRLEN(p_srr) + 27;
1108 cmd = alloc((unsigned)len);
Bram Moolenaare828b762018-09-10 17:51:58 +02001109 if (cmd == NULL)
1110 return FAIL;
Bram Moolenaar95fb60a2005-03-08 22:29:20 +00001111
Bram Moolenaare828b762018-09-10 17:51:58 +02001112 // We don't want $DIFF_OPTIONS to get in the way.
1113 if (getenv("DIFF_OPTIONS"))
1114 vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)"");
1115
1116 // Build the diff command and execute it. Always use -a, binary
1117 // differences are of no use. Ignore errors, diff returns
1118 // non-zero when differences have been found.
Bram Moolenaar785fc652018-09-15 19:17:38 +02001119 vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s",
Bram Moolenaare828b762018-09-10 17:51:58 +02001120 diff_a_works == FALSE ? "" : "-a ",
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001121#if defined(MSWIN)
Bram Moolenaare828b762018-09-10 17:51:58 +02001122 diff_bin_works == TRUE ? "--binary " : "",
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123#else
Bram Moolenaare828b762018-09-10 17:51:58 +02001124 "",
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125#endif
Bram Moolenaare828b762018-09-10 17:51:58 +02001126 (diff_flags & DIFF_IWHITE) ? "-b " : "",
Bram Moolenaar785fc652018-09-15 19:17:38 +02001127 (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
1128 (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
1129 (diff_flags & DIFF_IBLANK) ? "-B " : "",
Bram Moolenaare828b762018-09-10 17:51:58 +02001130 (diff_flags & DIFF_ICASE) ? "-i " : "",
1131 tmp_orig, tmp_new);
1132 append_redir(cmd, (int)len, p_srr, tmp_diff);
1133 block_autocmds(); // avoid ShellCmdPost stuff
1134 (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT);
1135 unblock_autocmds();
1136 vim_free(cmd);
1137 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 }
1139}
1140
1141/*
1142 * Create a new version of a file from the current buffer and a diff file.
1143 * The buffer is written to a file, also for unmodified buffers (the file
1144 * could have been produced by autocommands, e.g. the netrw plugin).
1145 */
1146 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001147ex_diffpatch(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148{
1149 char_u *tmp_orig; /* name of original temp file */
1150 char_u *tmp_new; /* name of patched temp file */
1151 char_u *buf = NULL;
Bram Moolenaar5fd0ca72009-05-13 16:56:33 +00001152 size_t buflen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153 win_T *old_curwin = curwin;
1154 char_u *newname = NULL; /* name of patched file buffer */
1155#ifdef UNIX
1156 char_u dirbuf[MAXPATHL];
1157 char_u *fullname = NULL;
1158#endif
1159#ifdef FEAT_BROWSE
1160 char_u *browseFile = NULL;
1161 int browse_flag = cmdmod.browse;
1162#endif
Bram Moolenaar8767f522016-07-01 17:17:39 +02001163 stat_T st;
Bram Moolenaara95ab322017-03-11 19:21:53 +01001164 char_u *esc_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001165
1166#ifdef FEAT_BROWSE
1167 if (cmdmod.browse)
1168 {
Bram Moolenaar7171abe2004-10-11 10:06:20 +00001169 browseFile = do_browse(0, (char_u *)_("Patch file"),
Bram Moolenaarc36651b2018-04-29 12:22:56 +02001170 eap->arg, NULL, NULL,
1171 (char_u *)_(BROWSE_FILTER_ALL_FILES), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 if (browseFile == NULL)
1173 return; /* operation cancelled */
1174 eap->arg = browseFile;
1175 cmdmod.browse = FALSE; /* don't let do_ecmd() browse again */
1176 }
1177#endif
1178
1179 /* We need two temp file names. */
Bram Moolenaare5c421c2015-03-31 13:33:08 +02001180 tmp_orig = vim_tempname('o', FALSE);
1181 tmp_new = vim_tempname('n', FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001182 if (tmp_orig == NULL || tmp_new == NULL)
1183 goto theend;
1184
1185 /* Write the current buffer to "tmp_orig". */
1186 if (buf_write(curbuf, tmp_orig, NULL,
1187 (linenr_T)1, curbuf->b_ml.ml_line_count,
1188 NULL, FALSE, FALSE, FALSE, TRUE) == FAIL)
1189 goto theend;
1190
1191#ifdef UNIX
1192 /* Get the absolute path of the patchfile, changing directory below. */
1193 fullname = FullName_save(eap->arg, FALSE);
1194#endif
Bram Moolenaara95ab322017-03-11 19:21:53 +01001195 esc_name = vim_strsave_shellescape(
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196# ifdef UNIX
Bram Moolenaara95ab322017-03-11 19:21:53 +01001197 fullname != NULL ? fullname :
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198# endif
Bram Moolenaara95ab322017-03-11 19:21:53 +01001199 eap->arg, TRUE, TRUE);
1200 if (esc_name == NULL)
1201 goto theend;
1202 buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16;
Bram Moolenaar5fd0ca72009-05-13 16:56:33 +00001203 buf = alloc((unsigned)buflen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 if (buf == NULL)
1205 goto theend;
1206
1207#ifdef UNIX
Bram Moolenaar5cc6a6e2009-01-22 19:48:55 +00001208 /* Temporarily chdir to /tmp, to avoid patching files in the current
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 * directory when the patch file contains more than one patch. When we
1210 * have our own temp dir use that instead, it will be cleaned up when we
1211 * exit (any .rej files created). Don't change directory if we can't
1212 * return to the current. */
1213 if (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0)
1214 dirbuf[0] = NUL;
1215 else
1216 {
1217# ifdef TEMPDIRNAMES
1218 if (vim_tempdir != NULL)
Bram Moolenaar42335f52018-09-13 15:33:43 +02001219 vim_ignored = mch_chdir((char *)vim_tempdir);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 else
1221# endif
Bram Moolenaar42335f52018-09-13 15:33:43 +02001222 vim_ignored = mch_chdir("/tmp");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 shorten_fnames(TRUE);
1224 }
1225#endif
1226
1227#ifdef FEAT_EVAL
1228 if (*p_pex != NUL)
1229 /* Use 'patchexpr' to generate the new file. */
1230 eval_patch(tmp_orig,
1231# ifdef UNIX
1232 fullname != NULL ? fullname :
1233# endif
1234 eap->arg, tmp_new);
1235 else
1236#endif
1237 {
1238 /* Build the patch command and execute it. Ignore errors. Switch to
1239 * cooked mode to allow the user to respond to prompts. */
Bram Moolenaara95ab322017-03-11 19:21:53 +01001240 vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
1241 tmp_new, tmp_orig, esc_name);
Bram Moolenaar78ab3312007-09-29 12:16:41 +00001242 block_autocmds(); /* Avoid ShellCmdPost stuff */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED);
Bram Moolenaar78ab3312007-09-29 12:16:41 +00001244 unblock_autocmds();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 }
1246
1247#ifdef UNIX
1248 if (dirbuf[0] != NUL)
1249 {
1250 if (mch_chdir((char *)dirbuf) != 0)
1251 EMSG(_(e_prev_dir));
1252 shorten_fnames(TRUE);
1253 }
1254#endif
1255
1256 /* patch probably has written over the screen */
1257 redraw_later(CLEAR);
1258
1259 /* Delete any .orig or .rej file created. */
1260 STRCPY(buf, tmp_new);
1261 STRCAT(buf, ".orig");
1262 mch_remove(buf);
1263 STRCPY(buf, tmp_new);
1264 STRCAT(buf, ".rej");
1265 mch_remove(buf);
1266
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001267 /* Only continue if the output file was created. */
1268 if (mch_stat((char *)tmp_new, &st) < 0 || st.st_size == 0)
1269 EMSG(_("E816: Cannot read patch output"));
1270 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 {
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001272 if (curbuf->b_fname != NULL)
1273 {
1274 newname = vim_strnsave(curbuf->b_fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275 (int)(STRLEN(curbuf->b_fname) + 4));
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001276 if (newname != NULL)
1277 STRCAT(newname, ".new");
1278 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279
1280#ifdef FEAT_GUI
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001281 need_mouse_correct = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282#endif
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001283 /* don't use a new tab page, each tab page has its own diffs */
1284 cmdmod.tab = 0;
Bram Moolenaar80a94a52006-02-23 21:26:58 +00001285
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001286 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 {
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001288 /* Pretend it was a ":split fname" command */
1289 eap->cmdidx = CMD_split;
1290 eap->arg = tmp_new;
1291 do_exedit(eap, old_curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001293 /* check that split worked and editing tmp_new */
1294 if (curwin != old_curwin && win_valid(old_curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 {
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001296 /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1297 diff_win_options(curwin, TRUE);
1298 diff_win_options(old_curwin, TRUE);
1299
1300 if (newname != NULL)
1301 {
1302 /* do a ":file filename.new" on the patched buffer */
1303 eap->arg = newname;
1304 ex_file(eap);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001306 /* Do filetype detection with the new name. */
1307 if (au_has_group((char_u *)"filetypedetect"))
1308 do_cmdline_cmd((char_u *)":doau filetypedetect BufRead");
Bram Moolenaar6ec0a6c2009-07-22 14:23:13 +00001309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 }
1311 }
1312 }
1313
1314theend:
1315 if (tmp_orig != NULL)
1316 mch_remove(tmp_orig);
1317 vim_free(tmp_orig);
1318 if (tmp_new != NULL)
1319 mch_remove(tmp_new);
1320 vim_free(tmp_new);
1321 vim_free(newname);
1322 vim_free(buf);
1323#ifdef UNIX
1324 vim_free(fullname);
1325#endif
Bram Moolenaara95ab322017-03-11 19:21:53 +01001326 vim_free(esc_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327#ifdef FEAT_BROWSE
1328 vim_free(browseFile);
1329 cmdmod.browse = browse_flag;
1330#endif
1331}
1332
1333/*
1334 * Split the window and edit another file, setting options to show the diffs.
1335 */
1336 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001337ex_diffsplit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338{
1339 win_T *old_curwin = curwin;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001340 bufref_T old_curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001342 set_bufref(&old_curbuf, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343#ifdef FEAT_GUI
1344 need_mouse_correct = TRUE;
1345#endif
Bram Moolenaar46328f92016-08-28 15:39:57 +02001346 /* Need to compute w_fraction when no redraw happened yet. */
1347 validate_cursor();
1348 set_fraction(curwin);
1349
Bram Moolenaar80a94a52006-02-23 21:26:58 +00001350 /* don't use a new tab page, each tab page has its own diffs */
1351 cmdmod.tab = 0;
1352
Bram Moolenaarc4675a12006-03-15 22:50:30 +00001353 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001354 {
1355 /* Pretend it was a ":split fname" command */
1356 eap->cmdidx = CMD_split;
Bram Moolenaar4be06f92005-07-29 22:36:03 +00001357 curwin->w_p_diff = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001358 do_exedit(eap, old_curwin);
1359
1360 if (curwin != old_curwin) /* split must have worked */
1361 {
1362 /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1363 diff_win_options(curwin, TRUE);
Bram Moolenaarf29a82d2015-12-17 15:03:55 +01001364 if (win_valid(old_curwin))
1365 {
1366 diff_win_options(old_curwin, TRUE);
1367
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001368 if (bufref_valid(&old_curbuf))
Bram Moolenaarf29a82d2015-12-17 15:03:55 +01001369 /* Move the cursor position to that of the old window. */
1370 curwin->w_cursor.lnum = diff_get_corresponding_line(
Bram Moolenaar025e3e02016-10-18 14:50:18 +02001371 old_curbuf.br_buf, old_curwin->w_cursor.lnum);
Bram Moolenaarf29a82d2015-12-17 15:03:55 +01001372 }
Bram Moolenaar46328f92016-08-28 15:39:57 +02001373 /* Now that lines are folded scroll to show the cursor at the same
1374 * relative position. */
1375 scroll_to_fraction(curwin, curwin->w_height);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 }
1377 }
1378}
1379
1380/*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001381 * Set options to show diffs for the current window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001384ex_diffthis(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385{
1386 /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1387 diff_win_options(curwin, TRUE);
1388}
1389
Bram Moolenaar04f62f82017-07-19 18:18:39 +02001390 static void
1391set_diff_option(win_T *wp, int value)
1392{
1393 win_T *old_curwin = curwin;
1394
1395 curwin = wp;
1396 curbuf = curwin->w_buffer;
1397 ++curbuf_lock;
1398 set_option_value((char_u *)"diff", (long)value, NULL, OPT_LOCAL);
1399 --curbuf_lock;
1400 curwin = old_curwin;
1401 curbuf = curwin->w_buffer;
1402}
1403
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404/*
1405 * Set options in window "wp" for diff mode.
1406 */
1407 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001408diff_win_options(
1409 win_T *wp,
1410 int addbuf) /* Add buffer to diff. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411{
Bram Moolenaarf4d7f942010-02-24 14:34:19 +01001412# ifdef FEAT_FOLDING
1413 win_T *old_curwin = curwin;
1414
1415 /* close the manually opened folds */
1416 curwin = wp;
1417 newFoldLevel();
1418 curwin = old_curwin;
1419# endif
1420
Bram Moolenaar3368ea22010-09-21 16:56:35 +02001421 /* Use 'scrollbind' and 'cursorbind' when available */
Bram Moolenaar43929962015-07-03 15:06:56 +02001422 if (!wp->w_p_diff)
Bram Moolenaara87aa802013-07-03 15:47:03 +02001423 wp->w_p_scb_save = wp->w_p_scb;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02001424 wp->w_p_scb = TRUE;
Bram Moolenaar43929962015-07-03 15:06:56 +02001425 if (!wp->w_p_diff)
Bram Moolenaara87aa802013-07-03 15:47:03 +02001426 wp->w_p_crb_save = wp->w_p_crb;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 wp->w_p_crb = TRUE;
Bram Moolenaar43929962015-07-03 15:06:56 +02001428 if (!wp->w_p_diff)
Bram Moolenaara87aa802013-07-03 15:47:03 +02001429 wp->w_p_wrap_save = wp->w_p_wrap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430 wp->w_p_wrap = FALSE;
1431# ifdef FEAT_FOLDING
Bram Moolenaarf4d7f942010-02-24 14:34:19 +01001432 curwin = wp;
1433 curbuf = curwin->w_buffer;
Bram Moolenaar43929962015-07-03 15:06:56 +02001434 if (!wp->w_p_diff)
1435 {
1436 if (wp->w_p_diff_saved)
1437 free_string_option(wp->w_p_fdm_save);
Bram Moolenaara87aa802013-07-03 15:47:03 +02001438 wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
Bram Moolenaar43929962015-07-03 15:06:56 +02001439 }
Bram Moolenaarf4d7f942010-02-24 14:34:19 +01001440 set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff",
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00001441 OPT_LOCAL|OPT_FREE, 0);
Bram Moolenaarf4d7f942010-02-24 14:34:19 +01001442 curwin = old_curwin;
1443 curbuf = curwin->w_buffer;
Bram Moolenaar43929962015-07-03 15:06:56 +02001444 if (!wp->w_p_diff)
Bram Moolenaara87aa802013-07-03 15:47:03 +02001445 {
1446 wp->w_p_fdc_save = wp->w_p_fdc;
1447 wp->w_p_fen_save = wp->w_p_fen;
1448 wp->w_p_fdl_save = wp->w_p_fdl;
1449 }
Bram Moolenaarf4d7f942010-02-24 14:34:19 +01001450 wp->w_p_fdc = diff_foldcolumn;
1451 wp->w_p_fen = TRUE;
1452 wp->w_p_fdl = 0;
1453 foldUpdateAll(wp);
1454 /* make sure topline is not halfway a fold */
1455 changed_window_setting_win(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 if (vim_strchr(p_sbo, 'h') == NULL)
1458 do_cmdline_cmd((char_u *)"set sbo+=hor");
Bram Moolenaar04f62f82017-07-19 18:18:39 +02001459 /* Save the current values, to be restored in ex_diffoff(). */
Bram Moolenaara87aa802013-07-03 15:47:03 +02001460 wp->w_p_diff_saved = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461
Bram Moolenaar04f62f82017-07-19 18:18:39 +02001462 set_diff_option(wp, TRUE);
Bram Moolenaar43929962015-07-03 15:06:56 +02001463
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 if (addbuf)
1465 diff_buf_add(wp->w_buffer);
1466 redraw_win_later(wp, NOT_VALID);
1467}
1468
1469/*
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001470 * Set options not to show diffs. For the current window or all windows.
Bram Moolenaarf740b292006-02-16 22:11:02 +00001471 * Only in the current tab page.
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001472 */
1473 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001474ex_diffoff(exarg_T *eap)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001475{
1476 win_T *wp;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001477 int diffwin = FALSE;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001478
Bram Moolenaar29323592016-07-24 22:04:11 +02001479 FOR_ALL_WINDOWS(wp)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001480 {
Bram Moolenaar00462ff2013-09-20 20:13:53 +02001481 if (eap->forceit ? wp->w_p_diff : wp == curwin)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001482 {
Bram Moolenaar43929962015-07-03 15:06:56 +02001483 /* Set 'diff' off. If option values were saved in
1484 * diff_win_options(), restore the ones whose settings seem to have
1485 * been left over from diff mode. */
Bram Moolenaar04f62f82017-07-19 18:18:39 +02001486 set_diff_option(wp, FALSE);
Bram Moolenaara87aa802013-07-03 15:47:03 +02001487
Bram Moolenaara87aa802013-07-03 15:47:03 +02001488 if (wp->w_p_diff_saved)
1489 {
Bram Moolenaar33ca6bf2013-07-17 13:43:39 +02001490
Bram Moolenaar43929962015-07-03 15:06:56 +02001491 if (wp->w_p_scb)
1492 wp->w_p_scb = wp->w_p_scb_save;
Bram Moolenaar43929962015-07-03 15:06:56 +02001493 if (wp->w_p_crb)
1494 wp->w_p_crb = wp->w_p_crb_save;
Bram Moolenaar43929962015-07-03 15:06:56 +02001495 if (!wp->w_p_wrap)
1496 wp->w_p_wrap = wp->w_p_wrap_save;
1497#ifdef FEAT_FOLDING
1498 free_string_option(wp->w_p_fdm);
Bram Moolenaar79a213d2017-05-16 13:15:18 +02001499 wp->w_p_fdm = vim_strsave(
1500 *wp->w_p_fdm_save ? wp->w_p_fdm_save : (char_u*)"manual");
Bram Moolenaar43929962015-07-03 15:06:56 +02001501
1502 if (wp->w_p_fdc == diff_foldcolumn)
1503 wp->w_p_fdc = wp->w_p_fdc_save;
1504 if (wp->w_p_fdl == 0)
1505 wp->w_p_fdl = wp->w_p_fdl_save;
1506
Bram Moolenaar33ca6bf2013-07-17 13:43:39 +02001507 /* Only restore 'foldenable' when 'foldmethod' is not
1508 * "manual", otherwise we continue to show the diff folds. */
Bram Moolenaar43929962015-07-03 15:06:56 +02001509 if (wp->w_p_fen)
1510 wp->w_p_fen = foldmethodIsManual(wp) ? FALSE
1511 : wp->w_p_fen_save;
1512
1513 foldUpdateAll(wp);
Bram Moolenaar43929962015-07-03 15:06:56 +02001514#endif
Bram Moolenaar33ca6bf2013-07-17 13:43:39 +02001515 }
Bram Moolenaare67d5462016-08-27 22:40:42 +02001516 /* remove filler lines */
1517 wp->w_topfill = 0;
1518
1519 /* make sure topline is not halfway a fold and cursor is
1520 * invalidated */
1521 changed_window_setting_win(wp);
Bram Moolenaar33ca6bf2013-07-17 13:43:39 +02001522
Bram Moolenaara87aa802013-07-03 15:47:03 +02001523 /* Note: 'sbo' is not restored, it's a global option. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001524 diff_buf_adjust(wp);
1525 }
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001526 diffwin |= wp->w_p_diff;
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001527 }
1528
Bram Moolenaar25ea0542017-02-03 23:16:28 +01001529 /* Also remove hidden buffers from the list. */
1530 if (eap->forceit)
1531 diff_buf_clear();
1532
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001533 /* Remove "hor" from from 'scrollopt' if there are no diff windows left. */
1534 if (!diffwin && vim_strchr(p_sbo, 'h') != NULL)
1535 do_cmdline_cmd((char_u *)"set sbo-=hor");
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001536}
1537
1538/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 * Read the diff output and add each entry to the diff list.
1540 */
1541 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001542diff_read(
Bram Moolenaare828b762018-09-10 17:51:58 +02001543 int idx_orig, // idx of original file
1544 int idx_new, // idx of new file
1545 diffout_T *dout) // diff output
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546{
Bram Moolenaare828b762018-09-10 17:51:58 +02001547 FILE *fd = NULL;
1548 int line_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 diff_T *dprev = NULL;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001550 diff_T *dp = curtab->tp_first_diff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 diff_T *dn, *dpl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001552 char_u linebuf[LBUFLEN]; /* only need to hold the diff line */
Bram Moolenaare828b762018-09-10 17:51:58 +02001553 char_u *line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 long off;
1555 int i;
1556 linenr_T lnum_orig, lnum_new;
1557 long count_orig, count_new;
1558 int notset = TRUE; /* block "*dp" not set yet */
Bram Moolenaare828b762018-09-10 17:51:58 +02001559 enum {
1560 DIFF_ED,
1561 DIFF_UNIFIED,
1562 DIFF_NONE
1563 } diffstyle = DIFF_NONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564
Bram Moolenaare828b762018-09-10 17:51:58 +02001565 if (dout->dout_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001567 diffstyle = DIFF_UNIFIED;
1568 }
1569 else
1570 {
1571 fd = mch_fopen((char *)dout->dout_fname, "r");
1572 if (fd == NULL)
1573 {
1574 EMSG(_("E98: Cannot read diff output"));
1575 return;
1576 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001577 }
1578
1579 for (;;)
1580 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001581 if (fd == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001583 if (line_idx >= dout->dout_ga.ga_len)
1584 break; // did last line
1585 line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586 }
1587 else
1588 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001589 if (tag_fgets(linebuf, LBUFLEN, fd))
1590 break; // end of file
1591 line = linebuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 }
1593
Bram Moolenaare828b762018-09-10 17:51:58 +02001594 if (diffstyle == DIFF_NONE)
1595 {
1596 // Determine diff style.
1597 // ed like diff looks like this:
1598 // {first}[,{last}]c{first}[,{last}]
1599 // {first}a{first}[,{last}]
1600 // {first}[,{last}]d{first}
1601 //
1602 // unified diff looks like this:
1603 // --- file1 2018-03-20 13:23:35.783153140 +0100
1604 // +++ file2 2018-03-20 13:23:41.183156066 +0100
1605 // @@ -1,3 +1,5 @@
1606 if (isdigit(*line))
1607 diffstyle = DIFF_ED;
1608 else if ((STRNCMP(line, "@@ ", 3) == 0))
1609 diffstyle = DIFF_UNIFIED;
1610 else if ((STRNCMP(line, "--- ", 4) == 0)
1611 && (tag_fgets(linebuf, LBUFLEN, fd) == 0)
1612 && (STRNCMP(line, "+++ ", 4) == 0)
1613 && (tag_fgets(linebuf, LBUFLEN, fd) == 0)
1614 && (STRNCMP(line, "@@ ", 3) == 0))
1615 diffstyle = DIFF_UNIFIED;
Bram Moolenaar3b8defd2018-09-13 13:03:11 +02001616 else
1617 // Format not recognized yet, skip over this line. Cygwin diff
1618 // may put a warning at the start of the file.
1619 continue;
Bram Moolenaare828b762018-09-10 17:51:58 +02001620 }
1621
1622 if (diffstyle == DIFF_ED)
1623 {
1624 if (!isdigit(*line))
1625 continue; // not the start of a diff block
1626 if (parse_diff_ed(line, &lnum_orig, &count_orig,
1627 &lnum_new, &count_new) == FAIL)
1628 continue;
1629 }
1630 else if (diffstyle == DIFF_UNIFIED)
1631 {
1632 if (STRNCMP(line, "@@ ", 3) != 0)
1633 continue; // not the start of a diff block
1634 if (parse_diff_unified(line, &lnum_orig, &count_orig,
1635 &lnum_new, &count_new) == FAIL)
1636 continue;
1637 }
1638 else
1639 {
1640 EMSG(_("E959: Invalid diff format."));
1641 break;
1642 }
1643
1644 // Go over blocks before the change, for which orig and new are equal.
1645 // Copy blocks from orig to new.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646 while (dp != NULL
1647 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
1648 {
1649 if (notset)
1650 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1651 dprev = dp;
1652 dp = dp->df_next;
1653 notset = TRUE;
1654 }
1655
1656 if (dp != NULL
1657 && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]
1658 && lnum_orig + count_orig >= dp->df_lnum[idx_orig])
1659 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001660 // New block overlaps with existing block(s).
1661 // First find last block that overlaps.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next)
1663 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig])
1664 break;
1665
Bram Moolenaare828b762018-09-10 17:51:58 +02001666 // If the newly found block starts before the old one, set the
1667 // start back a number of lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668 off = dp->df_lnum[idx_orig] - lnum_orig;
1669 if (off > 0)
1670 {
1671 for (i = idx_orig; i < idx_new; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001672 if (curtab->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 dp->df_lnum[i] -= off;
1674 dp->df_lnum[idx_new] = lnum_new;
1675 dp->df_count[idx_new] = count_new;
1676 }
1677 else if (notset)
1678 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001679 // new block inside existing one, adjust new block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680 dp->df_lnum[idx_new] = lnum_new + off;
1681 dp->df_count[idx_new] = count_new - off;
1682 }
1683 else
Bram Moolenaare828b762018-09-10 17:51:58 +02001684 // second overlap of new block with existing block
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00001685 dp->df_count[idx_new] += count_new - count_orig
1686 + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]
1687 - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688
Bram Moolenaare828b762018-09-10 17:51:58 +02001689 // Adjust the size of the block to include all the lines to the
1690 // end of the existing block or the new diff, whatever ends last.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001691 off = (lnum_orig + count_orig)
1692 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
1693 if (off < 0)
1694 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001695 // new change ends in existing block, adjust the end if not
1696 // done already
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 if (notset)
1698 dp->df_count[idx_new] += -off;
1699 off = 0;
1700 }
Bram Moolenaard4b96bc2007-10-19 15:33:39 +00001701 for (i = idx_orig; i < idx_new; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001702 if (curtab->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
1704 - dp->df_lnum[i] + off;
1705
Bram Moolenaare828b762018-09-10 17:51:58 +02001706 // Delete the diff blocks that have been merged into one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001707 dn = dp->df_next;
1708 dp->df_next = dpl->df_next;
1709 while (dn != dp->df_next)
1710 {
1711 dpl = dn->df_next;
1712 vim_free(dn);
1713 dn = dpl;
1714 }
1715 }
1716 else
1717 {
Bram Moolenaare828b762018-09-10 17:51:58 +02001718 // Allocate a new diffblock.
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001719 dp = diff_alloc_new(curtab, dprev, dp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 if (dp == NULL)
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001721 goto done;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722
1723 dp->df_lnum[idx_orig] = lnum_orig;
1724 dp->df_count[idx_orig] = count_orig;
1725 dp->df_lnum[idx_new] = lnum_new;
1726 dp->df_count[idx_new] = count_new;
1727
Bram Moolenaare828b762018-09-10 17:51:58 +02001728 // Set values for other buffers, these must be equal to the
1729 // original buffer, otherwise there would have been a change
1730 // already.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731 for (i = idx_orig + 1; i < idx_new; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001732 if (curtab->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733 diff_copy_entry(dprev, dp, idx_orig, i);
1734 }
Bram Moolenaare828b762018-09-10 17:51:58 +02001735 notset = FALSE; // "*dp" has been set
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 }
1737
Bram Moolenaare828b762018-09-10 17:51:58 +02001738 // for remaining diff blocks orig and new are equal
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739 while (dp != NULL)
1740 {
1741 if (notset)
1742 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1743 dprev = dp;
1744 dp = dp->df_next;
1745 notset = TRUE;
1746 }
1747
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001748done:
Bram Moolenaare828b762018-09-10 17:51:58 +02001749 if (fd != NULL)
1750 fclose(fd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751}
1752
1753/*
1754 * Copy an entry at "dp" from "idx_orig" to "idx_new".
1755 */
1756 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001757diff_copy_entry(
1758 diff_T *dprev,
1759 diff_T *dp,
1760 int idx_orig,
1761 int idx_new)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762{
1763 long off;
1764
1765 if (dprev == NULL)
1766 off = 0;
1767 else
1768 off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig])
1769 - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]);
1770 dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off;
1771 dp->df_count[idx_new] = dp->df_count[idx_orig];
1772}
1773
1774/*
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001775 * Clear the list of diffblocks for tab page "tp".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776 */
Bram Moolenaarea408852005-06-25 22:49:46 +00001777 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01001778diff_clear(tabpage_T *tp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779{
1780 diff_T *p, *next_p;
1781
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001782 for (p = tp->tp_first_diff; p != NULL; p = next_p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 {
1784 next_p = p->df_next;
1785 vim_free(p);
1786 }
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001787 tp->tp_first_diff = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788}
1789
1790/*
1791 * Check diff status for line "lnum" in buffer "buf":
1792 * Returns 0 for nothing special
1793 * Returns -1 for a line that should be highlighted as changed.
1794 * Returns -2 for a line that should be highlighted as added/deleted.
1795 * Returns > 0 for inserting that many filler lines above it (never happens
1796 * when 'diffopt' doesn't contain "filler").
1797 * This should only be used for windows where 'diff' is set.
1798 */
1799 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01001800diff_check(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001802 int idx; /* index in tp_diffbuf[] for this buffer */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 diff_T *dp;
1804 int maxcount;
1805 int i;
1806 buf_T *buf = wp->w_buffer;
1807 int cmp;
1808
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001809 if (curtab->tp_diff_invalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 ex_diffupdate(NULL); /* update after a big change */
1811
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001812 if (curtab->tp_first_diff == NULL || !wp->w_p_diff) /* no diffs at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 return 0;
1814
1815 /* safety check: "lnum" must be a buffer line */
1816 if (lnum < 1 || lnum > buf->b_ml.ml_line_count + 1)
1817 return 0;
1818
1819 idx = diff_buf_idx(buf);
1820 if (idx == DB_COUNT)
1821 return 0; /* no diffs for buffer "buf" */
1822
1823#ifdef FEAT_FOLDING
1824 /* A closed fold never has filler lines. */
1825 if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL))
1826 return 0;
1827#endif
1828
1829 /* search for a change that includes "lnum" in the list of diffblocks. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001830 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
1832 break;
1833 if (dp == NULL || lnum < dp->df_lnum[idx])
1834 return 0;
1835
1836 if (lnum < dp->df_lnum[idx] + dp->df_count[idx])
1837 {
1838 int zero = FALSE;
1839
1840 /* Changed or inserted line. If the other buffers have a count of
1841 * zero, the lines were inserted. If the other buffers have the same
1842 * count, check if the lines are identical. */
1843 cmp = FALSE;
1844 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001845 if (i != idx && curtab->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001846 {
1847 if (dp->df_count[i] == 0)
1848 zero = TRUE;
1849 else
1850 {
1851 if (dp->df_count[i] != dp->df_count[idx])
1852 return -1; /* nr of lines changed. */
1853 cmp = TRUE;
1854 }
1855 }
1856 if (cmp)
1857 {
1858 /* Compare all lines. If they are equal the lines were inserted
1859 * in some buffers, deleted in others, but not changed. */
1860 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001861 if (i != idx && curtab->tp_diffbuf[i] != NULL
1862 && dp->df_count[i] != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 if (!diff_equal_entry(dp, idx, i))
1864 return -1;
1865 }
1866 /* If there is no buffer with zero lines then there is no difference
1867 * any longer. Happens when making a change (or undo) that removes
1868 * the difference. Can't remove the entry here, we might be halfway
1869 * updating the window. Just report the text as unchanged. Other
1870 * windows might still show the change though. */
1871 if (zero == FALSE)
1872 return 0;
1873 return -2;
1874 }
1875
1876 /* If 'diffopt' doesn't contain "filler", return 0. */
1877 if (!(diff_flags & DIFF_FILLER))
1878 return 0;
1879
1880 /* Insert filler lines above the line just below the change. Will return
1881 * 0 when this buf had the max count. */
1882 maxcount = 0;
1883 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001884 if (curtab->tp_diffbuf[i] != NULL && dp->df_count[i] > maxcount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885 maxcount = dp->df_count[i];
1886 return maxcount - dp->df_count[idx];
1887}
1888
1889/*
1890 * Compare two entries in diff "*dp" and return TRUE if they are equal.
1891 */
1892 static int
Bram Moolenaar7454a062016-01-30 15:14:10 +01001893diff_equal_entry(diff_T *dp, int idx1, int idx2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894{
1895 int i;
1896 char_u *line;
1897 int cmp;
1898
1899 if (dp->df_count[idx1] != dp->df_count[idx2])
1900 return FALSE;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001901 if (diff_check_sanity(curtab, dp) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902 return FALSE;
1903 for (i = 0; i < dp->df_count[idx1]; ++i)
1904 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001905 line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
Bram Moolenaar071d4272004-06-13 20:20:40 +00001906 dp->df_lnum[idx1] + i, FALSE));
1907 if (line == NULL)
1908 return FALSE;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00001909 cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 dp->df_lnum[idx2] + i, FALSE));
1911 vim_free(line);
1912 if (cmp != 0)
1913 return FALSE;
1914 }
1915 return TRUE;
1916}
1917
1918/*
Bram Moolenaarae96b8d2017-09-03 15:04:21 +02001919 * Compare the characters at "p1" and "p2". If they are equal (possibly
1920 * ignoring case) return TRUE and set "len" to the number of bytes.
1921 */
1922 static int
1923diff_equal_char(char_u *p1, char_u *p2, int *len)
1924{
1925#ifdef FEAT_MBYTE
1926 int l = (*mb_ptr2len)(p1);
1927
1928 if (l != (*mb_ptr2len)(p2))
1929 return FALSE;
1930 if (l > 1)
1931 {
1932 if (STRNCMP(p1, p2, l) != 0
1933 && (!enc_utf8
1934 || !(diff_flags & DIFF_ICASE)
1935 || utf_fold(utf_ptr2char(p1))
1936 != utf_fold(utf_ptr2char(p2))))
1937 return FALSE;
1938 *len = l;
1939 }
1940 else
1941#endif
1942 {
1943 if ((*p1 != *p2)
1944 && (!(diff_flags & DIFF_ICASE)
1945 || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2)))
1946 return FALSE;
1947 *len = 1;
1948 }
1949 return TRUE;
1950}
1951
1952/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953 * Compare strings "s1" and "s2" according to 'diffopt'.
1954 * Return non-zero when they are different.
1955 */
1956 static int
Bram Moolenaar7454a062016-01-30 15:14:10 +01001957diff_cmp(char_u *s1, char_u *s2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958{
1959 char_u *p1, *p2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001961
Bram Moolenaar785fc652018-09-15 19:17:38 +02001962 if ((diff_flags & DIFF_IBLANK)
1963 && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL))
1964 return 0;
1965
1966 if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967 return STRCMP(s1, s2);
Bram Moolenaar785fc652018-09-15 19:17:38 +02001968 if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 return MB_STRICMP(s1, s2);
1970
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 p1 = s1;
1972 p2 = s2;
Bram Moolenaar785fc652018-09-15 19:17:38 +02001973
1974 // Ignore white space changes and possibly ignore case.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975 while (*p1 != NUL && *p2 != NUL)
1976 {
Bram Moolenaar785fc652018-09-15 19:17:38 +02001977 if (((diff_flags & DIFF_IWHITE)
1978 && VIM_ISWHITE(*p1) && VIM_ISWHITE(*p2))
1979 || ((diff_flags & DIFF_IWHITEALL)
1980 && (VIM_ISWHITE(*p1) || VIM_ISWHITE(*p2))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981 {
1982 p1 = skipwhite(p1);
1983 p2 = skipwhite(p2);
1984 }
1985 else
1986 {
Bram Moolenaarae96b8d2017-09-03 15:04:21 +02001987 if (!diff_equal_char(p1, p2, &l))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 break;
Bram Moolenaarae96b8d2017-09-03 15:04:21 +02001989 p1 += l;
1990 p2 += l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 }
1992 }
1993
Bram Moolenaar785fc652018-09-15 19:17:38 +02001994 // Ignore trailing white space.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 p1 = skipwhite(p1);
1996 p2 = skipwhite(p2);
1997 if (*p1 != NUL || *p2 != NUL)
1998 return 1;
1999 return 0;
2000}
2001
2002/*
2003 * Return the number of filler lines above "lnum".
2004 */
2005 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002006diff_check_fill(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007{
2008 int n;
2009
2010 /* be quick when there are no filler lines */
2011 if (!(diff_flags & DIFF_FILLER))
2012 return 0;
2013 n = diff_check(wp, lnum);
2014 if (n <= 0)
2015 return 0;
2016 return n;
2017}
2018
2019/*
2020 * Set the topline of "towin" to match the position in "fromwin", so that they
2021 * show the same diff'ed lines.
2022 */
2023 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01002024diff_set_topline(win_T *fromwin, win_T *towin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025{
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002026 buf_T *frombuf = fromwin->w_buffer;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 linenr_T lnum = fromwin->w_topline;
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002028 int fromidx;
2029 int toidx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 diff_T *dp;
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002031 int max_count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032 int i;
2033
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002034 fromidx = diff_buf_idx(frombuf);
2035 if (fromidx == DB_COUNT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 return; /* safety check */
2037
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002038 if (curtab->tp_diff_invalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039 ex_diffupdate(NULL); /* update after a big change */
2040
2041 towin->w_topfill = 0;
2042
2043 /* search for a change that includes "lnum" in the list of diffblocks. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002044 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002045 if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx])
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 break;
2047 if (dp == NULL)
2048 {
2049 /* After last change, compute topline relative to end of file; no
2050 * filler lines. */
2051 towin->w_topline = towin->w_buffer->b_ml.ml_line_count
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002052 - (frombuf->b_ml.ml_line_count - lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053 }
2054 else
2055 {
2056 /* Find index for "towin". */
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002057 toidx = diff_buf_idx(towin->w_buffer);
2058 if (toidx == DB_COUNT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059 return; /* safety check */
2060
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002061 towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
2062 if (lnum >= dp->df_lnum[fromidx])
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 {
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002064 /* Inside a change: compute filler lines. With three or more
2065 * buffers we need to know the largest count. */
2066 max_count = 0;
2067 for (i = 0; i < DB_COUNT; ++i)
2068 if (curtab->tp_diffbuf[i] != NULL
2069 && max_count < dp->df_count[i])
2070 max_count = dp->df_count[i];
2071
2072 if (dp->df_count[toidx] == dp->df_count[fromidx])
2073 {
2074 /* same number of lines: use same filler count */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075 towin->w_topfill = fromwin->w_topfill;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 }
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002077 else if (dp->df_count[toidx] > dp->df_count[fromidx])
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 {
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002079 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx])
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 {
Bram Moolenaarbb8f88b2008-01-18 16:40:00 +00002081 /* more lines in towin and fromwin doesn't show diff
2082 * lines, only filler lines */
2083 if (max_count - fromwin->w_topfill >= dp->df_count[toidx])
2084 {
2085 /* towin also only shows filler lines */
2086 towin->w_topline = dp->df_lnum[toidx]
2087 + dp->df_count[toidx];
2088 towin->w_topfill = fromwin->w_topfill;
2089 }
2090 else
2091 /* towin still has some diff lines to show */
2092 towin->w_topline = dp->df_lnum[toidx]
2093 + max_count - fromwin->w_topfill;
2094 }
2095 }
2096 else if (towin->w_topline >= dp->df_lnum[toidx]
2097 + dp->df_count[toidx])
2098 {
2099 /* less lines in towin and no diff lines to show: compute
2100 * filler lines */
2101 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
2102 if (diff_flags & DIFF_FILLER)
2103 {
2104 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx])
2105 /* fromwin is also out of diff lines */
2106 towin->w_topfill = fromwin->w_topfill;
2107 else
2108 /* fromwin has some diff lines */
2109 towin->w_topfill = dp->df_lnum[fromidx]
2110 + max_count - lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 }
2112 }
2113 }
2114 }
2115
2116 /* safety check (if diff info gets outdated strange things may happen) */
2117 towin->w_botfill = FALSE;
2118 if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count)
2119 {
2120 towin->w_topline = towin->w_buffer->b_ml.ml_line_count;
2121 towin->w_botfill = TRUE;
2122 }
2123 if (towin->w_topline < 1)
2124 {
2125 towin->w_topline = 1;
2126 towin->w_topfill = 0;
2127 }
2128
2129 /* When w_topline changes need to recompute w_botline and cursor position */
2130 invalidate_botline_win(towin);
2131 changed_line_abv_curs_win(towin);
2132
2133 check_topfill(towin, FALSE);
2134#ifdef FEAT_FOLDING
2135 (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline,
2136 NULL, TRUE, NULL);
2137#endif
2138}
2139
2140/*
2141 * This is called when 'diffopt' is changed.
2142 */
2143 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002144diffopt_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145{
2146 char_u *p;
2147 int diff_context_new = 6;
2148 int diff_flags_new = 0;
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002149 int diff_foldcolumn_new = 2;
Bram Moolenaare828b762018-09-10 17:51:58 +02002150 long diff_algorithm_new = 0;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002151 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152
2153 p = p_dip;
2154 while (*p != NUL)
2155 {
2156 if (STRNCMP(p, "filler", 6) == 0)
2157 {
2158 p += 6;
2159 diff_flags_new |= DIFF_FILLER;
2160 }
2161 else if (STRNCMP(p, "context:", 8) == 0 && VIM_ISDIGIT(p[8]))
2162 {
2163 p += 8;
2164 diff_context_new = getdigits(&p);
2165 }
Bram Moolenaar785fc652018-09-15 19:17:38 +02002166 else if (STRNCMP(p, "iblank", 6) == 0)
2167 {
2168 p += 6;
2169 diff_flags_new |= DIFF_IBLANK;
2170 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002171 else if (STRNCMP(p, "icase", 5) == 0)
2172 {
2173 p += 5;
2174 diff_flags_new |= DIFF_ICASE;
2175 }
Bram Moolenaar785fc652018-09-15 19:17:38 +02002176 else if (STRNCMP(p, "iwhiteall", 9) == 0)
2177 {
2178 p += 9;
2179 diff_flags_new |= DIFF_IWHITEALL;
2180 }
2181 else if (STRNCMP(p, "iwhiteeol", 9) == 0)
2182 {
2183 p += 9;
2184 diff_flags_new |= DIFF_IWHITEEOL;
2185 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002186 else if (STRNCMP(p, "iwhite", 6) == 0)
2187 {
2188 p += 6;
2189 diff_flags_new |= DIFF_IWHITE;
2190 }
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002191 else if (STRNCMP(p, "horizontal", 10) == 0)
2192 {
2193 p += 10;
2194 diff_flags_new |= DIFF_HORIZONTAL;
2195 }
2196 else if (STRNCMP(p, "vertical", 8) == 0)
2197 {
2198 p += 8;
2199 diff_flags_new |= DIFF_VERTICAL;
2200 }
2201 else if (STRNCMP(p, "foldcolumn:", 11) == 0 && VIM_ISDIGIT(p[11]))
2202 {
2203 p += 11;
2204 diff_foldcolumn_new = getdigits(&p);
2205 }
Bram Moolenaar97ce4192017-12-01 20:35:58 +01002206 else if (STRNCMP(p, "hiddenoff", 9) == 0)
2207 {
2208 p += 9;
2209 diff_flags_new |= DIFF_HIDDEN_OFF;
2210 }
Bram Moolenaare828b762018-09-10 17:51:58 +02002211 else if (STRNCMP(p, "indent-heuristic", 16) == 0)
2212 {
2213 p += 16;
2214 diff_algorithm_new |= XDF_INDENT_HEURISTIC;
2215 }
2216 else if (STRNCMP(p, "internal", 8) == 0)
2217 {
2218 p += 8;
2219 diff_flags_new |= DIFF_INTERNAL;
2220 }
2221 else if (STRNCMP(p, "algorithm:", 10) == 0)
2222 {
2223 p += 10;
2224 if (STRNCMP(p, "myers", 5) == 0)
2225 {
2226 p += 5;
2227 diff_algorithm_new = 0;
2228 }
2229 else if (STRNCMP(p, "minimal", 7) == 0)
2230 {
2231 p += 7;
2232 diff_algorithm_new = XDF_NEED_MINIMAL;
2233 }
2234 else if (STRNCMP(p, "patience", 8) == 0)
2235 {
2236 p += 8;
2237 diff_algorithm_new = XDF_PATIENCE_DIFF;
2238 }
2239 else if (STRNCMP(p, "histogram", 9) == 0)
2240 {
2241 p += 9;
2242 diff_algorithm_new = XDF_HISTOGRAM_DIFF;
2243 }
2244 }
2245
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 if (*p != ',' && *p != NUL)
2247 return FAIL;
2248 if (*p == ',')
2249 ++p;
2250 }
2251
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002252 /* Can't have both "horizontal" and "vertical". */
2253 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL))
2254 return FAIL;
2255
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 /* If "icase" or "iwhite" was added or removed, need to update the diff. */
Bram Moolenaare828b762018-09-10 17:51:58 +02002257 if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new)
Bram Moolenaar29323592016-07-24 22:04:11 +02002258 FOR_ALL_TABPAGES(tp)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002259 tp->tp_diff_invalid = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260
2261 diff_flags = diff_flags_new;
2262 diff_context = diff_context_new;
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002263 diff_foldcolumn = diff_foldcolumn_new;
Bram Moolenaare828b762018-09-10 17:51:58 +02002264 diff_algorithm = diff_algorithm_new;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265
2266 diff_redraw(TRUE);
2267
2268 /* recompute the scroll binding with the new option value, may
2269 * remove or add filler lines */
2270 check_scrollbind((linenr_T)0, 0L);
2271
2272 return OK;
2273}
2274
2275/*
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002276 * Return TRUE if 'diffopt' contains "horizontal".
2277 */
2278 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002279diffopt_horizontal(void)
Bram Moolenaarc4675a12006-03-15 22:50:30 +00002280{
2281 return (diff_flags & DIFF_HORIZONTAL) != 0;
2282}
2283
2284/*
Bram Moolenaar97ce4192017-12-01 20:35:58 +01002285 * Return TRUE if 'diffopt' contains "hiddenoff".
2286 */
2287 int
2288diffopt_hiddenoff(void)
2289{
2290 return (diff_flags & DIFF_HIDDEN_OFF) != 0;
2291}
2292
2293/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 * Find the difference within a changed line.
2295 * Returns TRUE if the line was added, no other buffer has it.
2296 */
2297 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002298diff_find_change(
2299 win_T *wp,
2300 linenr_T lnum,
2301 int *startp, /* first char of the change */
2302 int *endp) /* last char of the change */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303{
2304 char_u *line_org;
2305 char_u *line_new;
2306 int i;
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002307 int si_org, si_new;
2308 int ei_org, ei_new;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309 diff_T *dp;
2310 int idx;
2311 int off;
2312 int added = TRUE;
Bram Moolenaarda22b8c2017-09-02 18:01:50 +02002313 char_u *p1, *p2;
2314 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315
2316 /* Make a copy of the line, the next ml_get() will invalidate it. */
2317 line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE));
2318 if (line_org == NULL)
2319 return FALSE;
2320
2321 idx = diff_buf_idx(wp->w_buffer);
2322 if (idx == DB_COUNT) /* cannot happen */
Bram Moolenaarfa3491a2007-02-20 02:49:19 +00002323 {
2324 vim_free(line_org);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325 return FALSE;
Bram Moolenaarfa3491a2007-02-20 02:49:19 +00002326 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327
2328 /* search for a change that includes "lnum" in the list of diffblocks. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002329 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
2331 break;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002332 if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL)
Bram Moolenaarfa3491a2007-02-20 02:49:19 +00002333 {
2334 vim_free(line_org);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 return FALSE;
Bram Moolenaarfa3491a2007-02-20 02:49:19 +00002336 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337
2338 off = lnum - dp->df_lnum[idx];
2339
2340 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002341 if (curtab->tp_diffbuf[i] != NULL && i != idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002342 {
2343 /* Skip lines that are not in the other change (filler lines). */
2344 if (off >= dp->df_count[i])
2345 continue;
2346 added = FALSE;
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002347 line_new = ml_get_buf(curtab->tp_diffbuf[i],
2348 dp->df_lnum[i] + off, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349
2350 /* Search for start of difference */
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002351 si_org = si_new = 0;
2352 while (line_org[si_org] != NUL)
2353 {
Bram Moolenaar785fc652018-09-15 19:17:38 +02002354 if (((diff_flags & DIFF_IWHITE)
2355 && VIM_ISWHITE(line_org[si_org])
2356 && VIM_ISWHITE(line_new[si_new]))
2357 || ((diff_flags & DIFF_IWHITEALL)
2358 && (VIM_ISWHITE(line_org[si_org])
2359 || VIM_ISWHITE(line_new[si_new]))))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002360 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002361 si_org = (int)(skipwhite(line_org + si_org) - line_org);
2362 si_new = (int)(skipwhite(line_new + si_new) - line_new);
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002363 }
2364 else
2365 {
Bram Moolenaarda22b8c2017-09-02 18:01:50 +02002366 if (!diff_equal_char(line_org + si_org, line_new + si_new,
2367 &l))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002368 break;
Bram Moolenaarda22b8c2017-09-02 18:01:50 +02002369 si_org += l;
2370 si_new += l;
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002371 }
2372 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373#ifdef FEAT_MBYTE
2374 if (has_mbyte)
2375 {
2376 /* Move back to first byte of character in both lines (may
2377 * have "nn^" in line_org and "n^ in line_new). */
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002378 si_org -= (*mb_head_off)(line_org, line_org + si_org);
2379 si_new -= (*mb_head_off)(line_new, line_new + si_new);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 }
2381#endif
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002382 if (*startp > si_org)
2383 *startp = si_org;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384
2385 /* Search for end of difference, if any. */
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002386 if (line_org[si_org] != NUL || line_new[si_new] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387 {
2388 ei_org = (int)STRLEN(line_org);
2389 ei_new = (int)STRLEN(line_new);
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002390 while (ei_org >= *startp && ei_new >= si_new
2391 && ei_org >= 0 && ei_new >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392 {
Bram Moolenaar785fc652018-09-15 19:17:38 +02002393 if (((diff_flags & DIFF_IWHITE)
2394 && VIM_ISWHITE(line_org[ei_org])
2395 && VIM_ISWHITE(line_new[ei_new]))
2396 || ((diff_flags & DIFF_IWHITEALL)
2397 && (VIM_ISWHITE(line_org[ei_org])
2398 || VIM_ISWHITE(line_new[ei_new]))))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002399 {
2400 while (ei_org >= *startp
Bram Moolenaar1c465442017-03-12 20:10:05 +01002401 && VIM_ISWHITE(line_org[ei_org]))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002402 --ei_org;
2403 while (ei_new >= si_new
Bram Moolenaar1c465442017-03-12 20:10:05 +01002404 && VIM_ISWHITE(line_new[ei_new]))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002405 --ei_new;
2406 }
2407 else
2408 {
Bram Moolenaarda22b8c2017-09-02 18:01:50 +02002409 p1 = line_org + ei_org;
2410 p2 = line_new + ei_new;
2411#ifdef FEAT_MBYTE
2412 p1 -= (*mb_head_off)(line_org, p1);
2413 p2 -= (*mb_head_off)(line_new, p2);
2414#endif
2415 if (!diff_equal_char(p1, p2, &l))
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002416 break;
Bram Moolenaarda22b8c2017-09-02 18:01:50 +02002417 ei_org -= l;
2418 ei_new -= l;
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002419 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420 }
2421 if (*endp < ei_org)
2422 *endp = ei_org;
2423 }
2424 }
2425
2426 vim_free(line_org);
2427 return added;
2428}
2429
2430#if defined(FEAT_FOLDING) || defined(PROTO)
2431/*
2432 * Return TRUE if line "lnum" is not close to a diff block, this line should
2433 * be in a fold.
2434 * Return FALSE if there are no diff blocks at all in this window.
2435 */
2436 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002437diff_infold(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002438{
2439 int i;
2440 int idx = -1;
2441 int other = FALSE;
2442 diff_T *dp;
2443
2444 /* Return if 'diff' isn't set. */
2445 if (!wp->w_p_diff)
2446 return FALSE;
2447
2448 for (i = 0; i < DB_COUNT; ++i)
2449 {
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002450 if (curtab->tp_diffbuf[i] == wp->w_buffer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 idx = i;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002452 else if (curtab->tp_diffbuf[i] != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 other = TRUE;
2454 }
2455
2456 /* return here if there are no diffs in the window */
2457 if (idx == -1 || !other)
2458 return FALSE;
2459
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002460 if (curtab->tp_diff_invalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461 ex_diffupdate(NULL); /* update after a big change */
2462
2463 /* Return if there are no diff blocks. All lines will be folded. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002464 if (curtab->tp_first_diff == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465 return TRUE;
2466
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002467 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468 {
2469 /* If this change is below the line there can't be any further match. */
2470 if (dp->df_lnum[idx] - diff_context > lnum)
2471 break;
2472 /* If this change ends before the line we have a match. */
2473 if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum)
2474 return FALSE;
2475 }
2476 return TRUE;
2477}
2478#endif
2479
2480/*
2481 * "dp" and "do" commands.
2482 */
2483 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01002484nv_diffgetput(int put, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002485{
2486 exarg_T ea;
Bram Moolenaar6a643652014-10-31 13:54:25 +01002487 char_u buf[30];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488
Bram Moolenaarf2732452018-06-03 14:47:35 +02002489#ifdef FEAT_JOB_CHANNEL
2490 if (bt_prompt(curbuf))
2491 {
2492 vim_beep(BO_OPER);
2493 return;
2494 }
2495#endif
Bram Moolenaar6a643652014-10-31 13:54:25 +01002496 if (count == 0)
2497 ea.arg = (char_u *)"";
2498 else
2499 {
2500 vim_snprintf((char *)buf, 30, "%ld", count);
2501 ea.arg = buf;
2502 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 if (put)
2504 ea.cmdidx = CMD_diffput;
2505 else
2506 ea.cmdidx = CMD_diffget;
2507 ea.addr_count = 0;
2508 ea.line1 = curwin->w_cursor.lnum;
2509 ea.line2 = curwin->w_cursor.lnum;
2510 ex_diffgetput(&ea);
2511}
2512
2513/*
2514 * ":diffget"
2515 * ":diffput"
2516 */
2517 void
Bram Moolenaar7454a062016-01-30 15:14:10 +01002518ex_diffgetput(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002519{
2520 linenr_T lnum;
2521 int count;
2522 linenr_T off = 0;
2523 diff_T *dp;
2524 diff_T *dprev;
2525 diff_T *dfree;
2526 int idx_cur;
2527 int idx_other;
2528 int idx_from;
2529 int idx_to;
2530 int i;
2531 int added;
2532 char_u *p;
2533 aco_save_T aco;
2534 buf_T *buf;
2535 int start_skip, end_skip;
2536 int new_count;
Bram Moolenaar280f1262006-01-30 00:14:18 +00002537 int buf_empty;
Bram Moolenaar602eb742007-02-20 03:43:38 +00002538 int found_not_ma = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539
2540 /* Find the current buffer in the list of diff buffers. */
2541 idx_cur = diff_buf_idx(curbuf);
2542 if (idx_cur == DB_COUNT)
2543 {
2544 EMSG(_("E99: Current buffer is not in diff mode"));
2545 return;
2546 }
2547
2548 if (*eap->arg == NUL)
2549 {
2550 /* No argument: Find the other buffer in the list of diff buffers. */
2551 for (idx_other = 0; idx_other < DB_COUNT; ++idx_other)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002552 if (curtab->tp_diffbuf[idx_other] != curbuf
Bram Moolenaar602eb742007-02-20 03:43:38 +00002553 && curtab->tp_diffbuf[idx_other] != NULL)
2554 {
2555 if (eap->cmdidx != CMD_diffput
2556 || curtab->tp_diffbuf[idx_other]->b_p_ma)
2557 break;
2558 found_not_ma = TRUE;
2559 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002560 if (idx_other == DB_COUNT)
2561 {
Bram Moolenaar602eb742007-02-20 03:43:38 +00002562 if (found_not_ma)
2563 EMSG(_("E793: No other buffer in diff mode is modifiable"));
2564 else
2565 EMSG(_("E100: No other buffer in diff mode"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 return;
2567 }
2568
2569 /* Check that there isn't a third buffer in the list */
2570 for (i = idx_other + 1; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002571 if (curtab->tp_diffbuf[i] != curbuf
2572 && curtab->tp_diffbuf[i] != NULL
2573 && (eap->cmdidx != CMD_diffput || curtab->tp_diffbuf[i]->b_p_ma))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 {
2575 EMSG(_("E101: More than two buffers in diff mode, don't know which one to use"));
2576 return;
2577 }
2578 }
2579 else
2580 {
2581 /* Buffer number or pattern given. Ignore trailing white space. */
2582 p = eap->arg + STRLEN(eap->arg);
Bram Moolenaar1c465442017-03-12 20:10:05 +01002583 while (p > eap->arg && VIM_ISWHITE(p[-1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 --p;
2585 for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i)
2586 ;
2587 if (eap->arg + i == p) /* digits only */
2588 i = atol((char *)eap->arg);
2589 else
2590 {
Bram Moolenaar0c279bb2013-03-19 14:25:54 +01002591 i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592 if (i < 0)
2593 return; /* error message already given */
2594 }
2595 buf = buflist_findnr(i);
2596 if (buf == NULL)
2597 {
2598 EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg);
2599 return;
2600 }
Bram Moolenaar5cc6a6e2009-01-22 19:48:55 +00002601 if (buf == curbuf)
2602 return; /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603 idx_other = diff_buf_idx(buf);
2604 if (idx_other == DB_COUNT)
2605 {
2606 EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg);
2607 return;
2608 }
2609 }
2610
2611 diff_busy = TRUE;
2612
2613 /* When no range given include the line above or below the cursor. */
2614 if (eap->addr_count == 0)
2615 {
2616 /* Make it possible that ":diffget" on the last line gets line below
2617 * the cursor line when there is no difference above the cursor. */
2618 if (eap->cmdidx == CMD_diffget
2619 && eap->line1 == curbuf->b_ml.ml_line_count
2620 && diff_check(curwin, eap->line1) == 0
2621 && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0))
2622 ++eap->line2;
2623 else if (eap->line1 > 0)
2624 --eap->line1;
2625 }
2626
2627 if (eap->cmdidx == CMD_diffget)
2628 {
2629 idx_from = idx_other;
2630 idx_to = idx_cur;
2631 }
2632 else
2633 {
2634 idx_from = idx_cur;
2635 idx_to = idx_other;
2636 /* Need to make the other buffer the current buffer to be able to make
2637 * changes in it. */
2638 /* set curwin/curbuf to buf and save a few things */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002639 aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640 }
2641
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002642 /* May give the warning for a changed buffer here, which can trigger the
2643 * FileChangedRO autocommand, which may do nasty things and mess
2644 * everything up. */
2645 if (!curbuf->b_changed)
2646 {
2647 change_warning(0);
2648 if (diff_buf_idx(curbuf) != idx_to)
2649 {
2650 EMSG(_("E787: Buffer changed unexpectedly"));
2651 return;
2652 }
2653 }
2654
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 dprev = NULL;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002656 for (dp = curtab->tp_first_diff; dp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657 {
2658 if (dp->df_lnum[idx_cur] > eap->line2 + off)
2659 break; /* past the range that was specified */
2660
2661 dfree = NULL;
2662 lnum = dp->df_lnum[idx_to];
2663 count = dp->df_count[idx_to];
2664 if (dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off
2665 && u_save(lnum - 1, lnum + count) != FAIL)
2666 {
2667 /* Inside the specified range and saving for undo worked. */
2668 start_skip = 0;
2669 end_skip = 0;
2670 if (eap->addr_count > 0)
2671 {
2672 /* A range was specified: check if lines need to be skipped. */
2673 start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
2674 if (start_skip > 0)
2675 {
2676 /* range starts below start of current diff block */
2677 if (start_skip > count)
2678 {
2679 lnum += count;
2680 count = 0;
2681 }
2682 else
2683 {
2684 count -= start_skip;
2685 lnum += start_skip;
2686 }
2687 }
2688 else
2689 start_skip = 0;
2690
2691 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
2692 - (eap->line2 + off);
2693 if (end_skip > 0)
2694 {
2695 /* range ends above end of current/from diff block */
2696 if (idx_cur == idx_from) /* :diffput */
2697 {
2698 i = dp->df_count[idx_cur] - start_skip - end_skip;
2699 if (count > i)
2700 count = i;
2701 }
2702 else /* :diffget */
2703 {
2704 count -= end_skip;
2705 end_skip = dp->df_count[idx_from] - start_skip - count;
2706 if (end_skip < 0)
2707 end_skip = 0;
2708 }
2709 }
2710 else
2711 end_skip = 0;
2712 }
2713
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002714 buf_empty = BUFEMPTY();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715 added = 0;
2716 for (i = 0; i < count; ++i)
2717 {
Bram Moolenaar280f1262006-01-30 00:14:18 +00002718 /* remember deleting the last line of the buffer */
2719 buf_empty = curbuf->b_ml.ml_line_count == 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 ml_delete(lnum, FALSE);
2721 --added;
2722 }
2723 for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i)
2724 {
2725 linenr_T nr;
2726
2727 nr = dp->df_lnum[idx_from] + start_skip + i;
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002728 if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 break;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002730 p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from],
2731 nr, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 if (p != NULL)
2733 {
2734 ml_append(lnum + i - 1, p, 0, FALSE);
2735 vim_free(p);
2736 ++added;
Bram Moolenaar280f1262006-01-30 00:14:18 +00002737 if (buf_empty && curbuf->b_ml.ml_line_count == 2)
2738 {
2739 /* Added the first line into an empty buffer, need to
2740 * delete the dummy empty line. */
2741 buf_empty = FALSE;
2742 ml_delete((linenr_T)2, FALSE);
2743 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744 }
2745 }
2746 new_count = dp->df_count[idx_to] + added;
2747 dp->df_count[idx_to] = new_count;
2748
2749 if (start_skip == 0 && end_skip == 0)
2750 {
2751 /* Check if there are any other buffers and if the diff is
2752 * equal in them. */
2753 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002754 if (curtab->tp_diffbuf[i] != NULL && i != idx_from
2755 && i != idx_to
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 && !diff_equal_entry(dp, idx_from, i))
2757 break;
2758 if (i == DB_COUNT)
2759 {
2760 /* delete the diff entry, the buffers are now equal here */
2761 dfree = dp;
2762 dp = dp->df_next;
2763 if (dprev == NULL)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002764 curtab->tp_first_diff = dp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 else
2766 dprev->df_next = dp;
2767 }
2768 }
2769
2770 /* Adjust marks. This will change the following entries! */
2771 if (added != 0)
2772 {
2773 mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added);
2774 if (curwin->w_cursor.lnum >= lnum)
2775 {
2776 /* Adjust the cursor position if it's in/after the changed
2777 * lines. */
2778 if (curwin->w_cursor.lnum >= lnum + count)
2779 curwin->w_cursor.lnum += added;
2780 else if (added < 0)
2781 curwin->w_cursor.lnum = lnum;
2782 }
2783 }
2784 changed_lines(lnum, 0, lnum + count, (long)added);
2785
2786 if (dfree != NULL)
2787 {
2788 /* Diff is deleted, update folds in other windows. */
2789#ifdef FEAT_FOLDING
2790 diff_fold_update(dfree, idx_to);
2791#endif
2792 vim_free(dfree);
2793 }
2794 else
2795 /* mark_adjust() may have changed the count in a wrong way */
2796 dp->df_count[idx_to] = new_count;
2797
2798 /* When changing the current buffer, keep track of line numbers */
2799 if (idx_cur == idx_to)
2800 off += added;
2801 }
2802
2803 /* If before the range or not deleted, go to next diff. */
2804 if (dfree == NULL)
2805 {
2806 dprev = dp;
2807 dp = dp->df_next;
2808 }
2809 }
2810
2811 /* restore curwin/curbuf and a few other things */
Bram Moolenaara9d52e32010-07-31 16:44:19 +02002812 if (eap->cmdidx != CMD_diffget)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 {
2814 /* Syncing undo only works for the current buffer, but we change
2815 * another buffer. Sync undo if the command was typed. This isn't
2816 * 100% right when ":diffput" is used in a function or mapping. */
2817 if (KeyTyped)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002818 u_sync(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819 aucmd_restbuf(&aco);
2820 }
2821
2822 diff_busy = FALSE;
2823
2824 /* Check that the cursor is on a valid character and update it's position.
2825 * When there were filler lines the topline has become invalid. */
2826 check_cursor();
2827 changed_line_abv_curs();
2828
2829 /* Also need to redraw the other buffers. */
2830 diff_redraw(FALSE);
2831}
2832
2833#ifdef FEAT_FOLDING
2834/*
2835 * Update folds for all diff buffers for entry "dp".
2836 * Skip buffer with index "skip_idx".
2837 * When there are no diffs, all folds are removed.
2838 */
2839 static void
Bram Moolenaar7454a062016-01-30 15:14:10 +01002840diff_fold_update(diff_T *dp, int skip_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002841{
2842 int i;
2843 win_T *wp;
2844
Bram Moolenaar29323592016-07-24 22:04:11 +02002845 FOR_ALL_WINDOWS(wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846 for (i = 0; i < DB_COUNT; ++i)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002847 if (curtab->tp_diffbuf[i] == wp->w_buffer && i != skip_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848 foldUpdate(wp, dp->df_lnum[i],
2849 dp->df_lnum[i] + dp->df_count[i]);
2850}
2851#endif
2852
2853/*
2854 * Return TRUE if buffer "buf" is in diff-mode.
2855 */
2856 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002857diff_mode_buf(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002858{
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002859 tabpage_T *tp;
2860
Bram Moolenaar29323592016-07-24 22:04:11 +02002861 FOR_ALL_TABPAGES(tp)
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002862 if (diff_buf_idx_tp(buf, tp) != DB_COUNT)
2863 return TRUE;
2864 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865}
2866
2867/*
2868 * Move "count" times in direction "dir" to the next diff block.
2869 * Return FAIL if there isn't such a diff block.
2870 */
2871 int
Bram Moolenaar7454a062016-01-30 15:14:10 +01002872diff_move_to(int dir, long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873{
2874 int idx;
2875 linenr_T lnum = curwin->w_cursor.lnum;
2876 diff_T *dp;
2877
2878 idx = diff_buf_idx(curbuf);
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002879 if (idx == DB_COUNT || curtab->tp_first_diff == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880 return FAIL;
2881
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002882 if (curtab->tp_diff_invalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 ex_diffupdate(NULL); /* update after a big change */
2884
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002885 if (curtab->tp_first_diff == NULL) /* no diffs today */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 return FAIL;
2887
2888 while (--count >= 0)
2889 {
2890 /* Check if already before first diff. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002891 if (dir == BACKWARD && lnum <= curtab->tp_first_diff->df_lnum[idx])
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 break;
2893
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00002894 for (dp = curtab->tp_first_diff; ; dp = dp->df_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 {
2896 if (dp == NULL)
2897 break;
2898 if ((dir == FORWARD && lnum < dp->df_lnum[idx])
2899 || (dir == BACKWARD
2900 && (dp->df_next == NULL
2901 || lnum <= dp->df_next->df_lnum[idx])))
2902 {
2903 lnum = dp->df_lnum[idx];
2904 break;
2905 }
2906 }
2907 }
2908
2909 /* don't end up past the end of the file */
2910 if (lnum > curbuf->b_ml.ml_line_count)
2911 lnum = curbuf->b_ml.ml_line_count;
2912
2913 /* When the cursor didn't move at all we fail. */
2914 if (lnum == curwin->w_cursor.lnum)
2915 return FAIL;
2916
2917 setpcmark();
2918 curwin->w_cursor.lnum = lnum;
2919 curwin->w_cursor.col = 0;
2920
2921 return OK;
2922}
2923
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002924/*
2925 * Return the line number in the current window that is closest to "lnum1" in
2926 * "buf1" in diff mode.
2927 */
2928 static linenr_T
2929diff_get_corresponding_line_int(
Bram Moolenaar7454a062016-01-30 15:14:10 +01002930 buf_T *buf1,
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002931 linenr_T lnum1)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002932{
2933 int idx1;
2934 int idx2;
2935 diff_T *dp;
2936 int baseline = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002937
2938 idx1 = diff_buf_idx(buf1);
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002939 idx2 = diff_buf_idx(curbuf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02002940 if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL)
2941 return lnum1;
2942
2943 if (curtab->tp_diff_invalid)
2944 ex_diffupdate(NULL); /* update after a big change */
2945
2946 if (curtab->tp_first_diff == NULL) /* no diffs today */
2947 return lnum1;
2948
2949 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
2950 {
2951 if (dp->df_lnum[idx1] > lnum1)
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002952 return lnum1 - baseline;
2953 if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002954 {
2955 /* Inside the diffblock */
2956 baseline = lnum1 - dp->df_lnum[idx1];
2957 if (baseline > dp->df_count[idx2])
2958 baseline = dp->df_count[idx2];
2959
2960 return dp->df_lnum[idx2] + baseline;
2961 }
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002962 if ( (dp->df_lnum[idx1] == lnum1)
2963 && (dp->df_count[idx1] == 0)
2964 && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum)
2965 && ((dp->df_lnum[idx2] + dp->df_count[idx2])
2966 > curwin->w_cursor.lnum))
Bram Moolenaar860cae12010-06-05 23:22:07 +02002967 /*
2968 * Special case: if the cursor is just after a zero-count
2969 * block (i.e. all filler) and the target cursor is already
2970 * inside the corresponding block, leave the target cursor
2971 * unmoved. This makes repeated CTRL-W W operations work
2972 * as expected.
2973 */
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002974 return curwin->w_cursor.lnum;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002975 baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
2976 - (dp->df_lnum[idx2] + dp->df_count[idx2]);
2977 }
2978
2979 /* If we get here then the cursor is after the last diff */
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002980 return lnum1 - baseline;
2981}
Bram Moolenaar860cae12010-06-05 23:22:07 +02002982
Bram Moolenaar025e3e02016-10-18 14:50:18 +02002983/*
2984 * Return the line number in the current window that is closest to "lnum1" in
2985 * "buf1" in diff mode. Checks the line number to be valid.
2986 */
2987 linenr_T
2988diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1)
2989{
2990 linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1);
2991
2992 /* don't end up past the end of the file */
2993 if (lnum > curbuf->b_ml.ml_line_count)
2994 return curbuf->b_ml.ml_line_count;
2995 return lnum;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002996}
Bram Moolenaar860cae12010-06-05 23:22:07 +02002997
Bram Moolenaar071d4272004-06-13 20:20:40 +00002998/*
2999 * For line "lnum" in the current window find the equivalent lnum in window
3000 * "wp", compensating for inserted/deleted lines.
3001 */
3002 linenr_T
Bram Moolenaar7454a062016-01-30 15:14:10 +01003003diff_lnum_win(linenr_T lnum, win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004{
3005 diff_T *dp;
3006 int idx;
3007 int i;
3008 linenr_T n;
3009
3010 idx = diff_buf_idx(curbuf);
3011 if (idx == DB_COUNT) /* safety check */
3012 return (linenr_T)0;
3013
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00003014 if (curtab->tp_diff_invalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 ex_diffupdate(NULL); /* update after a big change */
3016
3017 /* search for a change that includes "lnum" in the list of diffblocks. */
Bram Moolenaar49d7bf12006-02-17 21:45:41 +00003018 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003019 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
3020 break;
3021
3022 /* When after the last change, compute relative to the last line number. */
3023 if (dp == NULL)
3024 return wp->w_buffer->b_ml.ml_line_count
3025 - (curbuf->b_ml.ml_line_count - lnum);
3026
3027 /* Find index for "wp". */
3028 i = diff_buf_idx(wp->w_buffer);
3029 if (i == DB_COUNT) /* safety check */
3030 return (linenr_T)0;
3031
3032 n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
3033 if (n > dp->df_lnum[i] + dp->df_count[i])
3034 n = dp->df_lnum[i] + dp->df_count[i];
3035 return n;
3036}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037
Bram Moolenaare828b762018-09-10 17:51:58 +02003038/*
3039 * Handle an ED style diff line.
3040 * Return FAIL if the line does not contain diff info.
3041 */
3042 static int
3043parse_diff_ed(
3044 char_u *line,
3045 linenr_T *lnum_orig,
3046 long *count_orig,
3047 linenr_T *lnum_new,
3048 long *count_new)
3049{
3050 char_u *p;
3051 long f1, l1, f2, l2;
3052 int difftype;
3053
3054 // The line must be one of three formats:
3055 // change: {first}[,{last}]c{first}[,{last}]
3056 // append: {first}a{first}[,{last}]
3057 // delete: {first}[,{last}]d{first}
3058 p = line;
3059 f1 = getdigits(&p);
3060 if (*p == ',')
3061 {
3062 ++p;
3063 l1 = getdigits(&p);
3064 }
3065 else
3066 l1 = f1;
3067 if (*p != 'a' && *p != 'c' && *p != 'd')
3068 return FAIL; // invalid diff format
3069 difftype = *p++;
3070 f2 = getdigits(&p);
3071 if (*p == ',')
3072 {
3073 ++p;
3074 l2 = getdigits(&p);
3075 }
3076 else
3077 l2 = f2;
3078 if (l1 < f1 || l2 < f2)
3079 return FAIL;
3080
3081 if (difftype == 'a')
3082 {
3083 *lnum_orig = f1 + 1;
3084 *count_orig = 0;
3085 }
3086 else
3087 {
3088 *lnum_orig = f1;
3089 *count_orig = l1 - f1 + 1;
3090 }
3091 if (difftype == 'd')
3092 {
3093 *lnum_new = f2 + 1;
3094 *count_new = 0;
3095 }
3096 else
3097 {
3098 *lnum_new = f2;
3099 *count_new = l2 - f2 + 1;
3100 }
3101 return OK;
3102}
3103
3104/*
3105 * Parses unified diff with zero(!) context lines.
3106 * Return FAIL if there is no diff information in "line".
3107 */
3108 static int
3109parse_diff_unified(
3110 char_u *line,
3111 linenr_T *lnum_orig,
3112 long *count_orig,
3113 linenr_T *lnum_new,
3114 long *count_new)
3115{
3116 char_u *p;
3117 long oldline, oldcount, newline, newcount;
3118
3119 // Parse unified diff hunk header:
3120 // @@ -oldline,oldcount +newline,newcount @@
3121 p = line;
3122 if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-')
3123 {
3124 oldline = getdigits(&p);
3125 if (*p == ',')
3126 {
3127 ++p;
3128 oldcount = getdigits(&p);
3129 }
3130 else
3131 oldcount = 1;
3132 if (*p++ == ' ' && *p++ == '+')
3133 {
3134 newline = getdigits(&p);
3135 if (*p == ',')
3136 {
3137 ++p;
3138 newcount = getdigits(&p);
3139 }
3140 else
3141 newcount = 1;
3142 }
3143 else
3144 return FAIL; // invalid diff format
3145
3146 if (oldcount == 0)
3147 oldline += 1;
3148 if (newcount == 0)
3149 newline += 1;
3150 if (newline == 0)
3151 newline = 1;
3152
3153 *lnum_orig = oldline;
3154 *count_orig = oldcount;
3155 *lnum_new = newline;
3156 *count_new = newcount;
3157
3158 return OK;
3159 }
3160
3161 return FAIL;
3162}
3163
3164/*
3165 * Callback function for the xdl_diff() function.
3166 * Stores the diff output in a grow array.
3167 */
3168 static int
3169xdiff_out(void *priv, mmbuffer_t *mb, int nbuf)
3170{
3171 diffout_T *dout = (diffout_T *)priv;
3172 int i;
3173 char_u *p;
3174
3175 for (i = 0; i < nbuf; i++)
3176 {
3177 // We are only interested in the header lines, skip text lines.
3178 if (STRNCMP(mb[i].ptr, "@@ ", 3) != 0)
3179 continue;
3180 if (ga_grow(&dout->dout_ga, 1) == FAIL)
3181 return -1;
3182 p = vim_strnsave((char_u *)mb[i].ptr, mb[i].size);
3183 if (p == NULL)
3184 return -1;
3185 ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
3186 }
3187 return 0;
3188}
3189
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190#endif /* FEAT_DIFF */