1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet: 2071d4279SBram Moolenaar * 3071d4279SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar 4071d4279SBram Moolenaar * 5071d4279SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions. 6071d4279SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed. 7071d4279SBram Moolenaar * See README.txt for an overview of the Vim source code. 8071d4279SBram Moolenaar */ 9071d4279SBram Moolenaar 10071d4279SBram Moolenaar /* 115cc6a6e7SBram Moolenaar * diff.c: code for diff'ing two, three or four buffers. 12e828b762SBram Moolenaar * 13e828b762SBram Moolenaar * There are three ways to diff: 14e828b762SBram Moolenaar * - Shell out to an external diff program, using files. 15e828b762SBram Moolenaar * - Use the compiled-in xdiff library. 16e828b762SBram Moolenaar * - Let 'diffexpr' do the work, using files. 17071d4279SBram Moolenaar */ 18071d4279SBram Moolenaar 19071d4279SBram Moolenaar #include "vim.h" 20e828b762SBram Moolenaar #include "xdiff/xdiff.h" 21071d4279SBram Moolenaar 22071d4279SBram Moolenaar #if defined(FEAT_DIFF) || defined(PROTO) 23071d4279SBram Moolenaar 24d2b58c0aSBram Moolenaar static int diff_busy = FALSE; // using diff structs, don't change them 25d2b58c0aSBram Moolenaar static int diff_need_update = FALSE; // ex_diffupdate needs to be called 26071d4279SBram Moolenaar 275d18efecSBram Moolenaar // flags obtained from the 'diffopt' option 28785fc656SBram Moolenaar #define DIFF_FILLER 0x001 // display filler lines 29785fc656SBram Moolenaar #define DIFF_IBLANK 0x002 // ignore empty lines 30785fc656SBram Moolenaar #define DIFF_ICASE 0x004 // ignore case 31785fc656SBram Moolenaar #define DIFF_IWHITE 0x008 // ignore change in white space 32785fc656SBram Moolenaar #define DIFF_IWHITEALL 0x010 // ignore all white space changes 33785fc656SBram Moolenaar #define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL 34785fc656SBram Moolenaar #define DIFF_HORIZONTAL 0x040 // horizontal splits 35785fc656SBram Moolenaar #define DIFF_VERTICAL 0x080 // vertical splits 36785fc656SBram Moolenaar #define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden 37785fc656SBram Moolenaar #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm 38c8234779SBram Moolenaar #define DIFF_CLOSE_OFF 0x400 // diffoff when closing window 394223d43cSBram Moolenaar #define DIFF_FOLLOWWRAP 0x800 // follow the wrap option 40785fc656SBram Moolenaar #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) 41c8234779SBram Moolenaar static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF; 42071d4279SBram Moolenaar 43e828b762SBram Moolenaar static long diff_algorithm = 0; 44e828b762SBram Moolenaar 455d18efecSBram Moolenaar #define LBUFLEN 50 // length of line in diff file 46071d4279SBram Moolenaar 475d18efecSBram Moolenaar static int diff_a_works = MAYBE; // TRUE when "diff -a" works, FALSE when it 485d18efecSBram Moolenaar // doesn't work, MAYBE when not checked yet 4948e330afSBram Moolenaar #if defined(MSWIN) 505d18efecSBram Moolenaar static int diff_bin_works = MAYBE; // TRUE when "diff --binary" works, FALSE 515d18efecSBram Moolenaar // when it doesn't work, MAYBE when not 525d18efecSBram Moolenaar // checked yet 53071d4279SBram Moolenaar #endif 54071d4279SBram Moolenaar 55e828b762SBram Moolenaar // used for diff input 56e828b762SBram Moolenaar typedef struct { 57e828b762SBram Moolenaar char_u *din_fname; // used for external diff 58e828b762SBram Moolenaar mmfile_t din_mmfile; // used for internal diff 59e828b762SBram Moolenaar } diffin_T; 60e828b762SBram Moolenaar 61e828b762SBram Moolenaar // used for diff result 62e828b762SBram Moolenaar typedef struct { 63e828b762SBram Moolenaar char_u *dout_fname; // used for external diff 64e828b762SBram Moolenaar garray_T dout_ga; // used for internal diff 65e828b762SBram Moolenaar } diffout_T; 66e828b762SBram Moolenaar 67e828b762SBram Moolenaar // two diff inputs and one result 68e828b762SBram Moolenaar typedef struct { 69e828b762SBram Moolenaar diffin_T dio_orig; // original file input 70e828b762SBram Moolenaar diffin_T dio_new; // new file input 71e828b762SBram Moolenaar diffout_T dio_diff; // diff result 72e828b762SBram Moolenaar int dio_internal; // using internal diff 73e828b762SBram Moolenaar } diffio_T; 74e828b762SBram Moolenaar 75f28dbceaSBram Moolenaar static int diff_buf_idx(buf_T *buf); 76f28dbceaSBram Moolenaar static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp); 77f28dbceaSBram Moolenaar static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after); 78f28dbceaSBram Moolenaar static void diff_check_unchanged(tabpage_T *tp, diff_T *dp); 79f28dbceaSBram Moolenaar static int diff_check_sanity(tabpage_T *tp, diff_T *dp); 80e828b762SBram Moolenaar static int check_external_diff(diffio_T *diffio); 81e828b762SBram Moolenaar static int diff_file(diffio_T *diffio); 82f28dbceaSBram Moolenaar static int diff_equal_entry(diff_T *dp, int idx1, int idx2); 83f28dbceaSBram Moolenaar static int diff_cmp(char_u *s1, char_u *s2); 84071d4279SBram Moolenaar #ifdef FEAT_FOLDING 85f28dbceaSBram Moolenaar static void diff_fold_update(diff_T *dp, int skip_idx); 86071d4279SBram Moolenaar #endif 87e828b762SBram Moolenaar static void diff_read(int idx_orig, int idx_new, diffout_T *fname); 88f28dbceaSBram Moolenaar static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new); 89f28dbceaSBram Moolenaar static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp); 90e828b762SBram Moolenaar static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); 91e828b762SBram Moolenaar static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); 92e828b762SBram Moolenaar static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf); 93071d4279SBram Moolenaar 94aeea7215SBram Moolenaar #define FOR_ALL_DIFFBLOCKS_IN_TAB(tp, dp) \ 95aeea7215SBram Moolenaar for ((dp) = (tp)->tp_first_diff; (dp) != NULL; (dp) = (dp)->df_next) 96aeea7215SBram Moolenaar 97071d4279SBram Moolenaar /* 98071d4279SBram Moolenaar * Called when deleting or unloading a buffer: No longer make a diff with it. 99071d4279SBram Moolenaar */ 100071d4279SBram Moolenaar void 1017454a06eSBram Moolenaar diff_buf_delete(buf_T *buf) 102071d4279SBram Moolenaar { 103071d4279SBram Moolenaar int i; 10449d7bf13SBram Moolenaar tabpage_T *tp; 105071d4279SBram Moolenaar 10629323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 10749d7bf13SBram Moolenaar { 10849d7bf13SBram Moolenaar i = diff_buf_idx_tp(buf, tp); 109071d4279SBram Moolenaar if (i != DB_COUNT) 110071d4279SBram Moolenaar { 11149d7bf13SBram Moolenaar tp->tp_diffbuf[i] = NULL; 11249d7bf13SBram Moolenaar tp->tp_diff_invalid = TRUE; 1135d55c0ffSBram Moolenaar if (tp == curtab) 1145d55c0ffSBram Moolenaar diff_redraw(TRUE); 11549d7bf13SBram Moolenaar } 116071d4279SBram Moolenaar } 117071d4279SBram Moolenaar } 118071d4279SBram Moolenaar 119071d4279SBram Moolenaar /* 1202df6dcc5SBram Moolenaar * Check if the current buffer should be added to or removed from the list of 1212df6dcc5SBram Moolenaar * diff buffers. 1222df6dcc5SBram Moolenaar */ 1232df6dcc5SBram Moolenaar void 1247454a06eSBram Moolenaar diff_buf_adjust(win_T *win) 1252df6dcc5SBram Moolenaar { 1262df6dcc5SBram Moolenaar win_T *wp; 12749d7bf13SBram Moolenaar int i; 1282df6dcc5SBram Moolenaar 1292df6dcc5SBram Moolenaar if (!win->w_p_diff) 1302df6dcc5SBram Moolenaar { 1315d18efecSBram Moolenaar // When there is no window showing a diff for this buffer, remove 1325d18efecSBram Moolenaar // it from the diffs. 13329323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 1342df6dcc5SBram Moolenaar if (wp->w_buffer == win->w_buffer && wp->w_p_diff) 1352df6dcc5SBram Moolenaar break; 1362df6dcc5SBram Moolenaar if (wp == NULL) 13749d7bf13SBram Moolenaar { 13849d7bf13SBram Moolenaar i = diff_buf_idx(win->w_buffer); 13949d7bf13SBram Moolenaar if (i != DB_COUNT) 14049d7bf13SBram Moolenaar { 14149d7bf13SBram Moolenaar curtab->tp_diffbuf[i] = NULL; 14249d7bf13SBram Moolenaar curtab->tp_diff_invalid = TRUE; 1435d55c0ffSBram Moolenaar diff_redraw(TRUE); 14449d7bf13SBram Moolenaar } 14549d7bf13SBram Moolenaar } 1462df6dcc5SBram Moolenaar } 1472df6dcc5SBram Moolenaar else 1482df6dcc5SBram Moolenaar diff_buf_add(win->w_buffer); 1492df6dcc5SBram Moolenaar } 1502df6dcc5SBram Moolenaar 1512df6dcc5SBram Moolenaar /* 152071d4279SBram Moolenaar * Add a buffer to make diffs for. 15349d7bf13SBram Moolenaar * Call this when a new buffer is being edited in the current window where 15449d7bf13SBram Moolenaar * 'diff' is set. 1555cc6a6e7SBram Moolenaar * Marks the current buffer as being part of the diff and requiring updating. 15649d7bf13SBram Moolenaar * This must be done before any autocmd, because a command may use info 15749d7bf13SBram Moolenaar * about the screen contents. 158071d4279SBram Moolenaar */ 159071d4279SBram Moolenaar void 1607454a06eSBram Moolenaar diff_buf_add(buf_T *buf) 161071d4279SBram Moolenaar { 162071d4279SBram Moolenaar int i; 163071d4279SBram Moolenaar 164071d4279SBram Moolenaar if (diff_buf_idx(buf) != DB_COUNT) 1655d18efecSBram Moolenaar return; // It's already there. 166071d4279SBram Moolenaar 167071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 16849d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] == NULL) 169071d4279SBram Moolenaar { 17049d7bf13SBram Moolenaar curtab->tp_diffbuf[i] = buf; 17149d7bf13SBram Moolenaar curtab->tp_diff_invalid = TRUE; 1725d55c0ffSBram Moolenaar diff_redraw(TRUE); 173071d4279SBram Moolenaar return; 174071d4279SBram Moolenaar } 175071d4279SBram Moolenaar 176b5443cc4SBram Moolenaar semsg(_("E96: Cannot diff more than %d buffers"), DB_COUNT); 177071d4279SBram Moolenaar } 178071d4279SBram Moolenaar 179071d4279SBram Moolenaar /* 18025ea0544SBram Moolenaar * Remove all buffers to make diffs for. 18125ea0544SBram Moolenaar */ 18225ea0544SBram Moolenaar static void 18325ea0544SBram Moolenaar diff_buf_clear(void) 18425ea0544SBram Moolenaar { 18525ea0544SBram Moolenaar int i; 18625ea0544SBram Moolenaar 18725ea0544SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 18825ea0544SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 18925ea0544SBram Moolenaar { 19025ea0544SBram Moolenaar curtab->tp_diffbuf[i] = NULL; 19125ea0544SBram Moolenaar curtab->tp_diff_invalid = TRUE; 19225ea0544SBram Moolenaar diff_redraw(TRUE); 19325ea0544SBram Moolenaar } 19425ea0544SBram Moolenaar } 19525ea0544SBram Moolenaar 19625ea0544SBram Moolenaar /* 19749d7bf13SBram Moolenaar * Find buffer "buf" in the list of diff buffers for the current tab page. 198071d4279SBram Moolenaar * Return its index or DB_COUNT if not found. 199071d4279SBram Moolenaar */ 200071d4279SBram Moolenaar static int 2017454a06eSBram Moolenaar diff_buf_idx(buf_T *buf) 202071d4279SBram Moolenaar { 203071d4279SBram Moolenaar int idx; 204071d4279SBram Moolenaar 205071d4279SBram Moolenaar for (idx = 0; idx < DB_COUNT; ++idx) 20649d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx] == buf) 207071d4279SBram Moolenaar break; 208071d4279SBram Moolenaar return idx; 209071d4279SBram Moolenaar } 210071d4279SBram Moolenaar 211071d4279SBram Moolenaar /* 21249d7bf13SBram Moolenaar * Find buffer "buf" in the list of diff buffers for tab page "tp". 21349d7bf13SBram Moolenaar * Return its index or DB_COUNT if not found. 21449d7bf13SBram Moolenaar */ 21549d7bf13SBram Moolenaar static int 2167454a06eSBram Moolenaar diff_buf_idx_tp(buf_T *buf, tabpage_T *tp) 21749d7bf13SBram Moolenaar { 21849d7bf13SBram Moolenaar int idx; 21949d7bf13SBram Moolenaar 22049d7bf13SBram Moolenaar for (idx = 0; idx < DB_COUNT; ++idx) 22149d7bf13SBram Moolenaar if (tp->tp_diffbuf[idx] == buf) 22249d7bf13SBram Moolenaar break; 22349d7bf13SBram Moolenaar return idx; 22449d7bf13SBram Moolenaar } 22549d7bf13SBram Moolenaar 22649d7bf13SBram Moolenaar /* 22749d7bf13SBram Moolenaar * Mark the diff info involving buffer "buf" as invalid, it will be updated 22849d7bf13SBram Moolenaar * when info is requested. 229071d4279SBram Moolenaar */ 230071d4279SBram Moolenaar void 2317454a06eSBram Moolenaar diff_invalidate(buf_T *buf) 232071d4279SBram Moolenaar { 23349d7bf13SBram Moolenaar tabpage_T *tp; 23449d7bf13SBram Moolenaar int i; 23549d7bf13SBram Moolenaar 23629323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 237071d4279SBram Moolenaar { 23849d7bf13SBram Moolenaar i = diff_buf_idx_tp(buf, tp); 23949d7bf13SBram Moolenaar if (i != DB_COUNT) 24049d7bf13SBram Moolenaar { 24149d7bf13SBram Moolenaar tp->tp_diff_invalid = TRUE; 24249d7bf13SBram Moolenaar if (tp == curtab) 243071d4279SBram Moolenaar diff_redraw(TRUE); 244071d4279SBram Moolenaar } 245071d4279SBram Moolenaar } 24649d7bf13SBram Moolenaar } 247071d4279SBram Moolenaar 248071d4279SBram Moolenaar /* 24949d7bf13SBram Moolenaar * Called by mark_adjust(): update line numbers in "curbuf". 250071d4279SBram Moolenaar */ 251071d4279SBram Moolenaar void 2527454a06eSBram Moolenaar diff_mark_adjust( 2537454a06eSBram Moolenaar linenr_T line1, 2547454a06eSBram Moolenaar linenr_T line2, 2557454a06eSBram Moolenaar long amount, 2567454a06eSBram Moolenaar long amount_after) 257071d4279SBram Moolenaar { 25849d7bf13SBram Moolenaar int idx; 25949d7bf13SBram Moolenaar tabpage_T *tp; 26049d7bf13SBram Moolenaar 2615d18efecSBram Moolenaar // Handle all tab pages that use the current buffer in a diff. 26229323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 26349d7bf13SBram Moolenaar { 26449d7bf13SBram Moolenaar idx = diff_buf_idx_tp(curbuf, tp); 26549d7bf13SBram Moolenaar if (idx != DB_COUNT) 26649d7bf13SBram Moolenaar diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after); 26749d7bf13SBram Moolenaar } 26849d7bf13SBram Moolenaar } 26949d7bf13SBram Moolenaar 27049d7bf13SBram Moolenaar /* 27149d7bf13SBram Moolenaar * Update line numbers in tab page "tp" for "curbuf" with index "idx". 27249d7bf13SBram Moolenaar * This attempts to update the changes as much as possible: 27349d7bf13SBram Moolenaar * When inserting/deleting lines outside of existing change blocks, create a 27449d7bf13SBram Moolenaar * new change block and update the line numbers in following blocks. 27549d7bf13SBram Moolenaar * When inserting/deleting lines in existing change blocks, update them. 27649d7bf13SBram Moolenaar */ 27749d7bf13SBram Moolenaar static void 2787454a06eSBram Moolenaar diff_mark_adjust_tp( 2797454a06eSBram Moolenaar tabpage_T *tp, 2807454a06eSBram Moolenaar int idx, 2817454a06eSBram Moolenaar linenr_T line1, 2827454a06eSBram Moolenaar linenr_T line2, 2837454a06eSBram Moolenaar long amount, 2847454a06eSBram Moolenaar long amount_after) 28549d7bf13SBram Moolenaar { 286071d4279SBram Moolenaar diff_T *dp; 287071d4279SBram Moolenaar diff_T *dprev; 288071d4279SBram Moolenaar diff_T *dnext; 289071d4279SBram Moolenaar int i; 290071d4279SBram Moolenaar int inserted, deleted; 291071d4279SBram Moolenaar int n, off; 292071d4279SBram Moolenaar linenr_T last; 2935d18efecSBram Moolenaar linenr_T lnum_deleted = line1; // lnum of remaining deletion 294071d4279SBram Moolenaar int check_unchanged; 295071d4279SBram Moolenaar 296e3521d9cSBram Moolenaar if (diff_internal()) 297e3521d9cSBram Moolenaar { 298198fa066SBram Moolenaar // Will update diffs before redrawing. Set _invalid to update the 299e3521d9cSBram Moolenaar // diffs themselves, set _update to also update folds properly just 300e3521d9cSBram Moolenaar // before redrawing. 3015f57bdcaSBram Moolenaar // Do update marks here, it is needed for :%diffput. 302e3521d9cSBram Moolenaar tp->tp_diff_invalid = TRUE; 303e3521d9cSBram Moolenaar tp->tp_diff_update = TRUE; 304e3521d9cSBram Moolenaar } 305e3521d9cSBram Moolenaar 306071d4279SBram Moolenaar if (line2 == MAXLNUM) 307071d4279SBram Moolenaar { 3085d18efecSBram Moolenaar // mark_adjust(99, MAXLNUM, 9, 0): insert lines 309071d4279SBram Moolenaar inserted = amount; 310071d4279SBram Moolenaar deleted = 0; 311071d4279SBram Moolenaar } 312071d4279SBram Moolenaar else if (amount_after > 0) 313071d4279SBram Moolenaar { 3145d18efecSBram Moolenaar // mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines 315071d4279SBram Moolenaar inserted = amount_after; 316071d4279SBram Moolenaar deleted = 0; 317071d4279SBram Moolenaar } 318071d4279SBram Moolenaar else 319071d4279SBram Moolenaar { 3205d18efecSBram Moolenaar // mark_adjust(98, 99, MAXLNUM, -2): delete lines 321071d4279SBram Moolenaar inserted = 0; 322071d4279SBram Moolenaar deleted = -amount_after; 323071d4279SBram Moolenaar } 324071d4279SBram Moolenaar 325071d4279SBram Moolenaar dprev = NULL; 32649d7bf13SBram Moolenaar dp = tp->tp_first_diff; 327071d4279SBram Moolenaar for (;;) 328071d4279SBram Moolenaar { 3295d18efecSBram Moolenaar // If the change is after the previous diff block and before the next 3305d18efecSBram Moolenaar // diff block, thus not touching an existing change, create a new diff 3315d18efecSBram Moolenaar // block. Don't do this when ex_diffgetput() is busy. 332071d4279SBram Moolenaar if ((dp == NULL || dp->df_lnum[idx] - 1 > line2 333071d4279SBram Moolenaar || (line2 == MAXLNUM && dp->df_lnum[idx] > line1)) 334071d4279SBram Moolenaar && (dprev == NULL 335071d4279SBram Moolenaar || dprev->df_lnum[idx] + dprev->df_count[idx] < line1) 336071d4279SBram Moolenaar && !diff_busy) 337071d4279SBram Moolenaar { 33849d7bf13SBram Moolenaar dnext = diff_alloc_new(tp, dprev, dp); 339071d4279SBram Moolenaar if (dnext == NULL) 340071d4279SBram Moolenaar return; 341071d4279SBram Moolenaar 342071d4279SBram Moolenaar dnext->df_lnum[idx] = line1; 343071d4279SBram Moolenaar dnext->df_count[idx] = inserted; 344071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 34549d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL && i != idx) 346071d4279SBram Moolenaar { 347071d4279SBram Moolenaar if (dprev == NULL) 348071d4279SBram Moolenaar dnext->df_lnum[i] = line1; 349071d4279SBram Moolenaar else 350071d4279SBram Moolenaar dnext->df_lnum[i] = line1 351071d4279SBram Moolenaar + (dprev->df_lnum[i] + dprev->df_count[i]) 352071d4279SBram Moolenaar - (dprev->df_lnum[idx] + dprev->df_count[idx]); 353071d4279SBram Moolenaar dnext->df_count[i] = deleted; 354071d4279SBram Moolenaar } 355071d4279SBram Moolenaar } 356071d4279SBram Moolenaar 3575d18efecSBram Moolenaar // if at end of the list, quit 358071d4279SBram Moolenaar if (dp == NULL) 359071d4279SBram Moolenaar break; 360071d4279SBram Moolenaar 361071d4279SBram Moolenaar /* 362071d4279SBram Moolenaar * Check for these situations: 363071d4279SBram Moolenaar * 1 2 3 364071d4279SBram Moolenaar * 1 2 3 365071d4279SBram Moolenaar * line1 2 3 4 5 366071d4279SBram Moolenaar * 2 3 4 5 367071d4279SBram Moolenaar * 2 3 4 5 368071d4279SBram Moolenaar * line2 2 3 4 5 369071d4279SBram Moolenaar * 3 5 6 370071d4279SBram Moolenaar * 3 5 6 371071d4279SBram Moolenaar */ 3725d18efecSBram Moolenaar // compute last line of this change 373071d4279SBram Moolenaar last = dp->df_lnum[idx] + dp->df_count[idx] - 1; 374071d4279SBram Moolenaar 3755d18efecSBram Moolenaar // 1. change completely above line1: nothing to do 376071d4279SBram Moolenaar if (last >= line1 - 1) 377071d4279SBram Moolenaar { 3785d18efecSBram Moolenaar // 6. change below line2: only adjust for amount_after; also when 3795d18efecSBram Moolenaar // "deleted" became zero when deleted all lines between two diffs 380071d4279SBram Moolenaar if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) 381071d4279SBram Moolenaar { 382071d4279SBram Moolenaar if (amount_after == 0) 3835d18efecSBram Moolenaar break; // nothing left to change 384071d4279SBram Moolenaar dp->df_lnum[idx] += amount_after; 385071d4279SBram Moolenaar } 386071d4279SBram Moolenaar else 387071d4279SBram Moolenaar { 388071d4279SBram Moolenaar check_unchanged = FALSE; 389071d4279SBram Moolenaar 3905d18efecSBram Moolenaar // 2. 3. 4. 5.: inserted/deleted lines touching this diff. 391071d4279SBram Moolenaar if (deleted > 0) 392071d4279SBram Moolenaar { 393071d4279SBram Moolenaar if (dp->df_lnum[idx] >= line1) 394071d4279SBram Moolenaar { 395071d4279SBram Moolenaar off = dp->df_lnum[idx] - lnum_deleted; 396071d4279SBram Moolenaar if (last <= line2) 397071d4279SBram Moolenaar { 3985d18efecSBram Moolenaar // 4. delete all lines of diff 399071d4279SBram Moolenaar if (dp->df_next != NULL 400071d4279SBram Moolenaar && dp->df_next->df_lnum[idx] - 1 <= line2) 401071d4279SBram Moolenaar { 4025d18efecSBram Moolenaar // delete continues in next diff, only do 4035d18efecSBram Moolenaar // lines until that one 404071d4279SBram Moolenaar n = dp->df_next->df_lnum[idx] - lnum_deleted; 405071d4279SBram Moolenaar deleted -= n; 406071d4279SBram Moolenaar n -= dp->df_count[idx]; 407071d4279SBram Moolenaar lnum_deleted = dp->df_next->df_lnum[idx]; 408071d4279SBram Moolenaar } 409071d4279SBram Moolenaar else 410071d4279SBram Moolenaar n = deleted - dp->df_count[idx]; 411071d4279SBram Moolenaar dp->df_count[idx] = 0; 412071d4279SBram Moolenaar } 413071d4279SBram Moolenaar else 414071d4279SBram Moolenaar { 4155d18efecSBram Moolenaar // 5. delete lines at or just before top of diff 416071d4279SBram Moolenaar n = off; 417071d4279SBram Moolenaar dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; 418071d4279SBram Moolenaar check_unchanged = TRUE; 419071d4279SBram Moolenaar } 420071d4279SBram Moolenaar dp->df_lnum[idx] = line1; 421071d4279SBram Moolenaar } 422071d4279SBram Moolenaar else 423071d4279SBram Moolenaar { 424071d4279SBram Moolenaar off = 0; 425071d4279SBram Moolenaar if (last < line2) 426071d4279SBram Moolenaar { 4275d18efecSBram Moolenaar // 2. delete at end of diff 428071d4279SBram Moolenaar dp->df_count[idx] -= last - lnum_deleted + 1; 429071d4279SBram Moolenaar if (dp->df_next != NULL 430071d4279SBram Moolenaar && dp->df_next->df_lnum[idx] - 1 <= line2) 431071d4279SBram Moolenaar { 4325d18efecSBram Moolenaar // delete continues in next diff, only do 4335d18efecSBram Moolenaar // lines until that one 434071d4279SBram Moolenaar n = dp->df_next->df_lnum[idx] - 1 - last; 435071d4279SBram Moolenaar deleted -= dp->df_next->df_lnum[idx] 436071d4279SBram Moolenaar - lnum_deleted; 437071d4279SBram Moolenaar lnum_deleted = dp->df_next->df_lnum[idx]; 438071d4279SBram Moolenaar } 439071d4279SBram Moolenaar else 440071d4279SBram Moolenaar n = line2 - last; 441071d4279SBram Moolenaar check_unchanged = TRUE; 442071d4279SBram Moolenaar } 443071d4279SBram Moolenaar else 444071d4279SBram Moolenaar { 4455d18efecSBram Moolenaar // 3. delete lines inside the diff 446071d4279SBram Moolenaar n = 0; 447071d4279SBram Moolenaar dp->df_count[idx] -= deleted; 448071d4279SBram Moolenaar } 449071d4279SBram Moolenaar } 450071d4279SBram Moolenaar 451071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 45249d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL && i != idx) 453071d4279SBram Moolenaar { 454071d4279SBram Moolenaar dp->df_lnum[i] -= off; 455071d4279SBram Moolenaar dp->df_count[i] += n; 456071d4279SBram Moolenaar } 457071d4279SBram Moolenaar } 458071d4279SBram Moolenaar else 459071d4279SBram Moolenaar { 460071d4279SBram Moolenaar if (dp->df_lnum[idx] <= line1) 461071d4279SBram Moolenaar { 4625d18efecSBram Moolenaar // inserted lines somewhere in this diff 463071d4279SBram Moolenaar dp->df_count[idx] += inserted; 464071d4279SBram Moolenaar check_unchanged = TRUE; 465071d4279SBram Moolenaar } 466071d4279SBram Moolenaar else 4675d18efecSBram Moolenaar // inserted lines somewhere above this diff 468071d4279SBram Moolenaar dp->df_lnum[idx] += inserted; 469071d4279SBram Moolenaar } 470071d4279SBram Moolenaar 471071d4279SBram Moolenaar if (check_unchanged) 4725d18efecSBram Moolenaar // Check if inserted lines are equal, may reduce the 4735d18efecSBram Moolenaar // size of the diff. TODO: also check for equal lines 4745d18efecSBram Moolenaar // in the middle and perhaps split the block. 47549d7bf13SBram Moolenaar diff_check_unchanged(tp, dp); 476071d4279SBram Moolenaar } 477071d4279SBram Moolenaar } 478071d4279SBram Moolenaar 4795d18efecSBram Moolenaar // check if this block touches the previous one, may merge them. 480071d4279SBram Moolenaar if (dprev != NULL && dprev->df_lnum[idx] + dprev->df_count[idx] 481071d4279SBram Moolenaar == dp->df_lnum[idx]) 482071d4279SBram Moolenaar { 483071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 48449d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL) 485071d4279SBram Moolenaar dprev->df_count[i] += dp->df_count[i]; 486071d4279SBram Moolenaar dprev->df_next = dp->df_next; 487071d4279SBram Moolenaar vim_free(dp); 488071d4279SBram Moolenaar dp = dprev->df_next; 489071d4279SBram Moolenaar } 490071d4279SBram Moolenaar else 491071d4279SBram Moolenaar { 4925d18efecSBram Moolenaar // Advance to next entry. 493071d4279SBram Moolenaar dprev = dp; 494071d4279SBram Moolenaar dp = dp->df_next; 495071d4279SBram Moolenaar } 496071d4279SBram Moolenaar } 497071d4279SBram Moolenaar 498071d4279SBram Moolenaar dprev = NULL; 49949d7bf13SBram Moolenaar dp = tp->tp_first_diff; 500071d4279SBram Moolenaar while (dp != NULL) 501071d4279SBram Moolenaar { 5025d18efecSBram Moolenaar // All counts are zero, remove this entry. 503071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 50449d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL && dp->df_count[i] != 0) 505071d4279SBram Moolenaar break; 506071d4279SBram Moolenaar if (i == DB_COUNT) 507071d4279SBram Moolenaar { 508071d4279SBram Moolenaar dnext = dp->df_next; 509071d4279SBram Moolenaar vim_free(dp); 510071d4279SBram Moolenaar dp = dnext; 511071d4279SBram Moolenaar if (dprev == NULL) 51249d7bf13SBram Moolenaar tp->tp_first_diff = dnext; 513071d4279SBram Moolenaar else 514071d4279SBram Moolenaar dprev->df_next = dnext; 515071d4279SBram Moolenaar } 516071d4279SBram Moolenaar else 517071d4279SBram Moolenaar { 5185d18efecSBram Moolenaar // Advance to next entry. 519071d4279SBram Moolenaar dprev = dp; 520071d4279SBram Moolenaar dp = dp->df_next; 521071d4279SBram Moolenaar } 522071d4279SBram Moolenaar 523071d4279SBram Moolenaar } 52449d7bf13SBram Moolenaar 52549d7bf13SBram Moolenaar if (tp == curtab) 52649d7bf13SBram Moolenaar { 5274f57eefeSBram Moolenaar // Don't redraw right away, this updates the diffs, which can be slow. 5284f57eefeSBram Moolenaar need_diff_redraw = TRUE; 529071d4279SBram Moolenaar 5305d18efecSBram Moolenaar // Need to recompute the scroll binding, may remove or add filler 5315d18efecSBram Moolenaar // lines (e.g., when adding lines above w_topline). But it's slow when 5325d18efecSBram Moolenaar // making many changes, postpone until redrawing. 53366fa271aSBram Moolenaar diff_need_scrollbind = TRUE; 534071d4279SBram Moolenaar } 53549d7bf13SBram Moolenaar } 536071d4279SBram Moolenaar 537071d4279SBram Moolenaar /* 538071d4279SBram Moolenaar * Allocate a new diff block and link it between "dprev" and "dp". 539071d4279SBram Moolenaar */ 540071d4279SBram Moolenaar static diff_T * 5417454a06eSBram Moolenaar diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) 542071d4279SBram Moolenaar { 543071d4279SBram Moolenaar diff_T *dnew; 544071d4279SBram Moolenaar 545c799fe20SBram Moolenaar dnew = ALLOC_ONE(diff_T); 546071d4279SBram Moolenaar if (dnew != NULL) 547071d4279SBram Moolenaar { 548071d4279SBram Moolenaar dnew->df_next = dp; 549071d4279SBram Moolenaar if (dprev == NULL) 55049d7bf13SBram Moolenaar tp->tp_first_diff = dnew; 551071d4279SBram Moolenaar else 552071d4279SBram Moolenaar dprev->df_next = dnew; 553071d4279SBram Moolenaar } 554071d4279SBram Moolenaar return dnew; 555071d4279SBram Moolenaar } 556071d4279SBram Moolenaar 557071d4279SBram Moolenaar /* 558071d4279SBram Moolenaar * Check if the diff block "dp" can be made smaller for lines at the start and 559071d4279SBram Moolenaar * end that are equal. Called after inserting lines. 560071d4279SBram Moolenaar * This may result in a change where all buffers have zero lines, the caller 561071d4279SBram Moolenaar * must take care of removing it. 562071d4279SBram Moolenaar */ 563071d4279SBram Moolenaar static void 5647454a06eSBram Moolenaar diff_check_unchanged(tabpage_T *tp, diff_T *dp) 565071d4279SBram Moolenaar { 566071d4279SBram Moolenaar int i_org; 567071d4279SBram Moolenaar int i_new; 568071d4279SBram Moolenaar int off_org, off_new; 569071d4279SBram Moolenaar char_u *line_org; 570071d4279SBram Moolenaar int dir = FORWARD; 571071d4279SBram Moolenaar 5725d18efecSBram Moolenaar // Find the first buffers, use it as the original, compare the other 5735d18efecSBram Moolenaar // buffer lines against this one. 574071d4279SBram Moolenaar for (i_org = 0; i_org < DB_COUNT; ++i_org) 57549d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_org] != NULL) 576071d4279SBram Moolenaar break; 5775d18efecSBram Moolenaar if (i_org == DB_COUNT) // safety check 578071d4279SBram Moolenaar return; 579071d4279SBram Moolenaar 58049d7bf13SBram Moolenaar if (diff_check_sanity(tp, dp) == FAIL) 581071d4279SBram Moolenaar return; 582071d4279SBram Moolenaar 5835d18efecSBram Moolenaar // First check lines at the top, then at the bottom. 584071d4279SBram Moolenaar off_org = 0; 585071d4279SBram Moolenaar off_new = 0; 586071d4279SBram Moolenaar for (;;) 587071d4279SBram Moolenaar { 5885d18efecSBram Moolenaar // Repeat until a line is found which is different or the number of 5895d18efecSBram Moolenaar // lines has become zero. 590071d4279SBram Moolenaar while (dp->df_count[i_org] > 0) 591071d4279SBram Moolenaar { 5925d18efecSBram Moolenaar // Copy the line, the next ml_get() will invalidate it. 593071d4279SBram Moolenaar if (dir == BACKWARD) 594071d4279SBram Moolenaar off_org = dp->df_count[i_org] - 1; 59549d7bf13SBram Moolenaar line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], 596071d4279SBram Moolenaar dp->df_lnum[i_org] + off_org, FALSE)); 597071d4279SBram Moolenaar if (line_org == NULL) 598071d4279SBram Moolenaar return; 599071d4279SBram Moolenaar for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) 600071d4279SBram Moolenaar { 60149d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_new] == NULL) 602071d4279SBram Moolenaar continue; 603071d4279SBram Moolenaar if (dir == BACKWARD) 604071d4279SBram Moolenaar off_new = dp->df_count[i_new] - 1; 6055d18efecSBram Moolenaar // if other buffer doesn't have this line, it was inserted 606071d4279SBram Moolenaar if (off_new < 0 || off_new >= dp->df_count[i_new]) 607071d4279SBram Moolenaar break; 60849d7bf13SBram Moolenaar if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], 609071d4279SBram Moolenaar dp->df_lnum[i_new] + off_new, FALSE)) != 0) 610071d4279SBram Moolenaar break; 611071d4279SBram Moolenaar } 612071d4279SBram Moolenaar vim_free(line_org); 613071d4279SBram Moolenaar 6145d18efecSBram Moolenaar // Stop when a line isn't equal in all diff buffers. 615071d4279SBram Moolenaar if (i_new != DB_COUNT) 616071d4279SBram Moolenaar break; 617071d4279SBram Moolenaar 6185d18efecSBram Moolenaar // Line matched in all buffers, remove it from the diff. 619071d4279SBram Moolenaar for (i_new = i_org; i_new < DB_COUNT; ++i_new) 62049d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_new] != NULL) 621071d4279SBram Moolenaar { 622071d4279SBram Moolenaar if (dir == FORWARD) 623071d4279SBram Moolenaar ++dp->df_lnum[i_new]; 624071d4279SBram Moolenaar --dp->df_count[i_new]; 625071d4279SBram Moolenaar } 626071d4279SBram Moolenaar } 627071d4279SBram Moolenaar if (dir == BACKWARD) 628071d4279SBram Moolenaar break; 629071d4279SBram Moolenaar dir = BACKWARD; 630071d4279SBram Moolenaar } 631071d4279SBram Moolenaar } 632071d4279SBram Moolenaar 633071d4279SBram Moolenaar /* 634071d4279SBram Moolenaar * Check if a diff block doesn't contain invalid line numbers. 635071d4279SBram Moolenaar * This can happen when the diff program returns invalid results. 636071d4279SBram Moolenaar */ 637071d4279SBram Moolenaar static int 6387454a06eSBram Moolenaar diff_check_sanity(tabpage_T *tp, diff_T *dp) 639071d4279SBram Moolenaar { 640071d4279SBram Moolenaar int i; 641071d4279SBram Moolenaar 642071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 64349d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL) 644071d4279SBram Moolenaar if (dp->df_lnum[i] + dp->df_count[i] - 1 64549d7bf13SBram Moolenaar > tp->tp_diffbuf[i]->b_ml.ml_line_count) 646071d4279SBram Moolenaar return FAIL; 647071d4279SBram Moolenaar return OK; 648071d4279SBram Moolenaar } 649071d4279SBram Moolenaar 650071d4279SBram Moolenaar /* 65149d7bf13SBram Moolenaar * Mark all diff buffers in the current tab page for redraw. 652071d4279SBram Moolenaar */ 6534f57eefeSBram Moolenaar void 6547454a06eSBram Moolenaar diff_redraw( 655e3521d9cSBram Moolenaar int dofold) // also recompute the folds 656071d4279SBram Moolenaar { 657071d4279SBram Moolenaar win_T *wp; 658071d4279SBram Moolenaar int n; 659071d4279SBram Moolenaar 6604f57eefeSBram Moolenaar need_diff_redraw = FALSE; 66129323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 662071d4279SBram Moolenaar if (wp->w_p_diff) 663071d4279SBram Moolenaar { 66460f8377eSBram Moolenaar redraw_win_later(wp, SOME_VALID); 665071d4279SBram Moolenaar #ifdef FEAT_FOLDING 666071d4279SBram Moolenaar if (dofold && foldmethodIsDiff(wp)) 667071d4279SBram Moolenaar foldUpdateAll(wp); 668071d4279SBram Moolenaar #endif 6695d18efecSBram Moolenaar // A change may have made filler lines invalid, need to take care 6705d18efecSBram Moolenaar // of that for other windows. 671071d4279SBram Moolenaar n = diff_check(wp, wp->w_topline); 672a80888d2SBram Moolenaar if ((wp != curwin && wp->w_topfill > 0) || n > 0) 673a80888d2SBram Moolenaar { 674071d4279SBram Moolenaar if (wp->w_topfill > n) 675071d4279SBram Moolenaar wp->w_topfill = (n < 0 ? 0 : n); 676a80888d2SBram Moolenaar else if (n > 0 && n > wp->w_topfill) 677a80888d2SBram Moolenaar wp->w_topfill = n; 678846a2ff5SBram Moolenaar check_topfill(wp, FALSE); 679071d4279SBram Moolenaar } 680071d4279SBram Moolenaar } 681071d4279SBram Moolenaar } 682071d4279SBram Moolenaar 683e828b762SBram Moolenaar static void 684e828b762SBram Moolenaar clear_diffin(diffin_T *din) 685e828b762SBram Moolenaar { 686e828b762SBram Moolenaar if (din->din_fname == NULL) 687e828b762SBram Moolenaar { 688e828b762SBram Moolenaar vim_free(din->din_mmfile.ptr); 689e828b762SBram Moolenaar din->din_mmfile.ptr = NULL; 690e828b762SBram Moolenaar } 691e828b762SBram Moolenaar else 692e828b762SBram Moolenaar mch_remove(din->din_fname); 693e828b762SBram Moolenaar } 694e828b762SBram Moolenaar 695e828b762SBram Moolenaar static void 696e828b762SBram Moolenaar clear_diffout(diffout_T *dout) 697e828b762SBram Moolenaar { 698e828b762SBram Moolenaar if (dout->dout_fname == NULL) 699e828b762SBram Moolenaar ga_clear_strings(&dout->dout_ga); 700e828b762SBram Moolenaar else 701e828b762SBram Moolenaar mch_remove(dout->dout_fname); 702e828b762SBram Moolenaar } 703e828b762SBram Moolenaar 704071d4279SBram Moolenaar /* 705e828b762SBram Moolenaar * Write buffer "buf" to a memory buffer. 706e828b762SBram Moolenaar * Return FAIL for failure. 707071d4279SBram Moolenaar */ 708071d4279SBram Moolenaar static int 709e828b762SBram Moolenaar diff_write_buffer(buf_T *buf, diffin_T *din) 710e828b762SBram Moolenaar { 711e828b762SBram Moolenaar linenr_T lnum; 712e828b762SBram Moolenaar char_u *s; 713e828b762SBram Moolenaar long len = 0; 714e828b762SBram Moolenaar char_u *ptr; 715e828b762SBram Moolenaar 716e828b762SBram Moolenaar // xdiff requires one big block of memory with all the text. 717e828b762SBram Moolenaar for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) 7186e272accSBram Moolenaar len += (long)STRLEN(ml_get_buf(buf, lnum, FALSE)) + 1; 71918a4ba29SBram Moolenaar ptr = alloc(len); 720e828b762SBram Moolenaar if (ptr == NULL) 721e828b762SBram Moolenaar { 722e828b762SBram Moolenaar // Allocating memory failed. This can happen, because we try to read 723e828b762SBram Moolenaar // the whole buffer text into memory. Set the failed flag, the diff 724e828b762SBram Moolenaar // will be retried with external diff. The flag is never reset. 725e828b762SBram Moolenaar buf->b_diff_failed = TRUE; 726e828b762SBram Moolenaar if (p_verbose > 0) 727e828b762SBram Moolenaar { 728e828b762SBram Moolenaar verbose_enter(); 729f9e3e09fSBram Moolenaar smsg(_("Not enough memory to use internal diff for buffer \"%s\""), 730e828b762SBram Moolenaar buf->b_fname); 731e828b762SBram Moolenaar verbose_leave(); 732e828b762SBram Moolenaar } 733e828b762SBram Moolenaar return FAIL; 734e828b762SBram Moolenaar } 735e828b762SBram Moolenaar din->din_mmfile.ptr = (char *)ptr; 736e828b762SBram Moolenaar din->din_mmfile.size = len; 737e828b762SBram Moolenaar 738e828b762SBram Moolenaar len = 0; 739e828b762SBram Moolenaar for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) 740e828b762SBram Moolenaar { 741e828b762SBram Moolenaar for (s = ml_get_buf(buf, lnum, FALSE); *s != NUL; ) 742e828b762SBram Moolenaar { 743e828b762SBram Moolenaar if (diff_flags & DIFF_ICASE) 744e828b762SBram Moolenaar { 745e828b762SBram Moolenaar int c; 746e828b762SBram Moolenaar int orig_len; 747e828b762SBram Moolenaar char_u cbuf[MB_MAXBYTES + 1]; 748e828b762SBram Moolenaar 74913505972SBram Moolenaar // xdiff doesn't support ignoring case, fold-case the text. 750e828b762SBram Moolenaar c = PTR2CHAR(s); 75159de417bSBram Moolenaar c = MB_CASEFOLD(c); 7521614a149SBram Moolenaar orig_len = mb_ptr2len(s); 753e828b762SBram Moolenaar if (mb_char2bytes(c, cbuf) != orig_len) 754e828b762SBram Moolenaar // TODO: handle byte length difference 755e828b762SBram Moolenaar mch_memmove(ptr + len, s, orig_len); 756e828b762SBram Moolenaar else 757e828b762SBram Moolenaar mch_memmove(ptr + len, cbuf, orig_len); 758e828b762SBram Moolenaar 759e828b762SBram Moolenaar s += orig_len; 760e828b762SBram Moolenaar len += orig_len; 761e828b762SBram Moolenaar } 762e828b762SBram Moolenaar else 763e828b762SBram Moolenaar ptr[len++] = *s++; 764e828b762SBram Moolenaar } 765e828b762SBram Moolenaar ptr[len++] = NL; 766e828b762SBram Moolenaar } 767e828b762SBram Moolenaar return OK; 768e828b762SBram Moolenaar } 769e828b762SBram Moolenaar 770e828b762SBram Moolenaar /* 771e828b762SBram Moolenaar * Write buffer "buf" to file or memory buffer. 772e828b762SBram Moolenaar * Return FAIL for failure. 773e828b762SBram Moolenaar */ 774e828b762SBram Moolenaar static int 775e828b762SBram Moolenaar diff_write(buf_T *buf, diffin_T *din) 776071d4279SBram Moolenaar { 777071d4279SBram Moolenaar int r; 778071d4279SBram Moolenaar char_u *save_ff; 779e1004401SBram Moolenaar int save_cmod_flags; 780071d4279SBram Moolenaar 781e828b762SBram Moolenaar if (din->din_fname == NULL) 782e828b762SBram Moolenaar return diff_write_buffer(buf, din); 783e828b762SBram Moolenaar 784e828b762SBram Moolenaar // Always use 'fileformat' set to "unix". 785071d4279SBram Moolenaar save_ff = buf->b_p_ff; 786071d4279SBram Moolenaar buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); 787e1004401SBram Moolenaar save_cmod_flags = cmdmod.cmod_flags; 788f4a1d1c0SBram Moolenaar // Writing the buffer is an implementation detail of performing the diff, 789f4a1d1c0SBram Moolenaar // so it shouldn't update the '[ and '] marks. 790e1004401SBram Moolenaar cmdmod.cmod_flags |= CMOD_LOCKMARKS; 791e828b762SBram Moolenaar r = buf_write(buf, din->din_fname, NULL, 792e828b762SBram Moolenaar (linenr_T)1, buf->b_ml.ml_line_count, 793071d4279SBram Moolenaar NULL, FALSE, FALSE, FALSE, TRUE); 794e1004401SBram Moolenaar cmdmod.cmod_flags = save_cmod_flags; 795071d4279SBram Moolenaar free_string_option(buf->b_p_ff); 796071d4279SBram Moolenaar buf->b_p_ff = save_ff; 797071d4279SBram Moolenaar return r; 798071d4279SBram Moolenaar } 799071d4279SBram Moolenaar 800071d4279SBram Moolenaar /* 801e828b762SBram Moolenaar * Update the diffs for all buffers involved. 802e828b762SBram Moolenaar */ 803e828b762SBram Moolenaar static void 804e828b762SBram Moolenaar diff_try_update( 805e828b762SBram Moolenaar diffio_T *dio, 806e828b762SBram Moolenaar int idx_orig, 807e828b762SBram Moolenaar exarg_T *eap) // "eap" can be NULL 808e828b762SBram Moolenaar { 809e828b762SBram Moolenaar buf_T *buf; 810e828b762SBram Moolenaar int idx_new; 811e828b762SBram Moolenaar 812e828b762SBram Moolenaar if (dio->dio_internal) 813e828b762SBram Moolenaar { 814e828b762SBram Moolenaar ga_init2(&dio->dio_diff.dout_ga, sizeof(char *), 1000); 815e828b762SBram Moolenaar } 816e828b762SBram Moolenaar else 817e828b762SBram Moolenaar { 818e828b762SBram Moolenaar // We need three temp file names. 819e828b762SBram Moolenaar dio->dio_orig.din_fname = vim_tempname('o', TRUE); 820e828b762SBram Moolenaar dio->dio_new.din_fname = vim_tempname('n', TRUE); 821e828b762SBram Moolenaar dio->dio_diff.dout_fname = vim_tempname('d', TRUE); 822e828b762SBram Moolenaar if (dio->dio_orig.din_fname == NULL 823e828b762SBram Moolenaar || dio->dio_new.din_fname == NULL 824e828b762SBram Moolenaar || dio->dio_diff.dout_fname == NULL) 825e828b762SBram Moolenaar goto theend; 826e828b762SBram Moolenaar } 827e828b762SBram Moolenaar 828e828b762SBram Moolenaar // Check external diff is actually working. 829e828b762SBram Moolenaar if (!dio->dio_internal && check_external_diff(dio) == FAIL) 830e828b762SBram Moolenaar goto theend; 831e828b762SBram Moolenaar 832e828b762SBram Moolenaar // :diffupdate! 833e828b762SBram Moolenaar if (eap != NULL && eap->forceit) 834e828b762SBram Moolenaar for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) 835e828b762SBram Moolenaar { 836e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_new]; 837e828b762SBram Moolenaar if (buf_valid(buf)) 838e828b762SBram Moolenaar buf_check_timestamp(buf, FALSE); 839e828b762SBram Moolenaar } 840e828b762SBram Moolenaar 841e828b762SBram Moolenaar // Write the first buffer to a tempfile or mmfile_t. 842e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_orig]; 843e828b762SBram Moolenaar if (diff_write(buf, &dio->dio_orig) == FAIL) 844e828b762SBram Moolenaar goto theend; 845e828b762SBram Moolenaar 846e828b762SBram Moolenaar // Make a difference between the first buffer and every other. 847e828b762SBram Moolenaar for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 848e828b762SBram Moolenaar { 849e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_new]; 850e828b762SBram Moolenaar if (buf == NULL || buf->b_ml.ml_mfp == NULL) 851e828b762SBram Moolenaar continue; // skip buffer that isn't loaded 852e828b762SBram Moolenaar 853e828b762SBram Moolenaar // Write the other buffer and diff with the first one. 854e828b762SBram Moolenaar if (diff_write(buf, &dio->dio_new) == FAIL) 855e828b762SBram Moolenaar continue; 856e828b762SBram Moolenaar if (diff_file(dio) == FAIL) 857e828b762SBram Moolenaar continue; 858e828b762SBram Moolenaar 859e828b762SBram Moolenaar // Read the diff output and add each entry to the diff list. 860e828b762SBram Moolenaar diff_read(idx_orig, idx_new, &dio->dio_diff); 861e828b762SBram Moolenaar 862e828b762SBram Moolenaar clear_diffin(&dio->dio_new); 863e828b762SBram Moolenaar clear_diffout(&dio->dio_diff); 864e828b762SBram Moolenaar } 865e828b762SBram Moolenaar clear_diffin(&dio->dio_orig); 866e828b762SBram Moolenaar 867e828b762SBram Moolenaar theend: 868e828b762SBram Moolenaar vim_free(dio->dio_orig.din_fname); 869e828b762SBram Moolenaar vim_free(dio->dio_new.din_fname); 870e828b762SBram Moolenaar vim_free(dio->dio_diff.dout_fname); 871e828b762SBram Moolenaar } 872e828b762SBram Moolenaar 873e828b762SBram Moolenaar /* 874e828b762SBram Moolenaar * Return TRUE if the options are set to use the internal diff library. 875e828b762SBram Moolenaar * Note that if the internal diff failed for one of the buffers, the external 876e828b762SBram Moolenaar * diff will be used anyway. 877e828b762SBram Moolenaar */ 878e3521d9cSBram Moolenaar int 879e828b762SBram Moolenaar diff_internal(void) 880e828b762SBram Moolenaar { 881975880b6SBram Moolenaar return (diff_flags & DIFF_INTERNAL) != 0 882975880b6SBram Moolenaar #ifdef FEAT_EVAL 883975880b6SBram Moolenaar && *p_dex == NUL 884975880b6SBram Moolenaar #endif 885975880b6SBram Moolenaar ; 886e828b762SBram Moolenaar } 887e828b762SBram Moolenaar 888e828b762SBram Moolenaar /* 889e828b762SBram Moolenaar * Return TRUE if the internal diff failed for one of the diff buffers. 890e828b762SBram Moolenaar */ 891e828b762SBram Moolenaar static int 892e828b762SBram Moolenaar diff_internal_failed(void) 893e828b762SBram Moolenaar { 894e828b762SBram Moolenaar int idx; 895e828b762SBram Moolenaar 896e828b762SBram Moolenaar // Only need to do something when there is another buffer. 897e828b762SBram Moolenaar for (idx = 0; idx < DB_COUNT; ++idx) 898e828b762SBram Moolenaar if (curtab->tp_diffbuf[idx] != NULL 899e828b762SBram Moolenaar && curtab->tp_diffbuf[idx]->b_diff_failed) 900e828b762SBram Moolenaar return TRUE; 901e828b762SBram Moolenaar return FALSE; 902e828b762SBram Moolenaar } 903e828b762SBram Moolenaar 904e828b762SBram Moolenaar /* 905071d4279SBram Moolenaar * Completely update the diffs for the buffers involved. 906e3521d9cSBram Moolenaar * When using the external "diff" command the buffers are written to a file, 907e3521d9cSBram Moolenaar * also for unmodified buffers (the file could have been produced by 908e3521d9cSBram Moolenaar * autocommands, e.g. the netrw plugin). 909071d4279SBram Moolenaar */ 910071d4279SBram Moolenaar void 911e828b762SBram Moolenaar ex_diffupdate(exarg_T *eap) // "eap" can be NULL 912071d4279SBram Moolenaar { 913071d4279SBram Moolenaar int idx_orig; 914071d4279SBram Moolenaar int idx_new; 915e828b762SBram Moolenaar diffio_T diffio; 916198fa066SBram Moolenaar int had_diffs = curtab->tp_first_diff != NULL; 917071d4279SBram Moolenaar 918d2b58c0aSBram Moolenaar if (diff_busy) 919d2b58c0aSBram Moolenaar { 920d2b58c0aSBram Moolenaar diff_need_update = TRUE; 921d2b58c0aSBram Moolenaar return; 922d2b58c0aSBram Moolenaar } 923d2b58c0aSBram Moolenaar 924e828b762SBram Moolenaar // Delete all diffblocks. 92549d7bf13SBram Moolenaar diff_clear(curtab); 92649d7bf13SBram Moolenaar curtab->tp_diff_invalid = FALSE; 927071d4279SBram Moolenaar 928e828b762SBram Moolenaar // Use the first buffer as the original text. 929071d4279SBram Moolenaar for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) 93049d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_orig] != NULL) 931071d4279SBram Moolenaar break; 932071d4279SBram Moolenaar if (idx_orig == DB_COUNT) 933198fa066SBram Moolenaar goto theend; 934071d4279SBram Moolenaar 935e828b762SBram Moolenaar // Only need to do something when there is another buffer. 936071d4279SBram Moolenaar for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 93749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_new] != NULL) 938071d4279SBram Moolenaar break; 939071d4279SBram Moolenaar if (idx_new == DB_COUNT) 940198fa066SBram Moolenaar goto theend; 941071d4279SBram Moolenaar 942e828b762SBram Moolenaar // Only use the internal method if it did not fail for one of the buffers. 943a80faa89SBram Moolenaar CLEAR_FIELD(diffio); 944e828b762SBram Moolenaar diffio.dio_internal = diff_internal() && !diff_internal_failed(); 945e828b762SBram Moolenaar 946e828b762SBram Moolenaar diff_try_update(&diffio, idx_orig, eap); 947e828b762SBram Moolenaar if (diffio.dio_internal && diff_internal_failed()) 948e828b762SBram Moolenaar { 949e828b762SBram Moolenaar // Internal diff failed, use external diff instead. 950a80faa89SBram Moolenaar CLEAR_FIELD(diffio); 951e828b762SBram Moolenaar diff_try_update(&diffio, idx_orig, eap); 952e828b762SBram Moolenaar } 953e828b762SBram Moolenaar 954e828b762SBram Moolenaar // force updating cursor position on screen 955e828b762SBram Moolenaar curwin->w_valid_cursor.lnum = 0; 956e828b762SBram Moolenaar 957198fa066SBram Moolenaar theend: 958198fa066SBram Moolenaar // A redraw is needed if there were diffs and they were cleared, or there 959198fa066SBram Moolenaar // are diffs now, which means they got updated. 960198fa066SBram Moolenaar if (had_diffs || curtab->tp_first_diff != NULL) 961198fa066SBram Moolenaar { 962e828b762SBram Moolenaar diff_redraw(TRUE); 963e8fa05b5SBram Moolenaar apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); 964e828b762SBram Moolenaar } 965198fa066SBram Moolenaar } 966071d4279SBram Moolenaar 967071d4279SBram Moolenaar /* 968071d4279SBram Moolenaar * Do a quick test if "diff" really works. Otherwise it looks like there 969071d4279SBram Moolenaar * are no differences. Can't use the return value, it's non-zero when 970071d4279SBram Moolenaar * there are differences. 971071d4279SBram Moolenaar */ 972e828b762SBram Moolenaar static int 973e828b762SBram Moolenaar check_external_diff(diffio_T *diffio) 974e828b762SBram Moolenaar { 975e828b762SBram Moolenaar FILE *fd; 976e828b762SBram Moolenaar int ok; 977e828b762SBram Moolenaar int io_error = FALSE; 978e828b762SBram Moolenaar 979e828b762SBram Moolenaar // May try twice, first with "-a" and then without. 980071d4279SBram Moolenaar for (;;) 981071d4279SBram Moolenaar { 982071d4279SBram Moolenaar ok = FALSE; 983e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w"); 984fe86f2d7SBram Moolenaar if (fd == NULL) 985fe86f2d7SBram Moolenaar io_error = TRUE; 986fe86f2d7SBram Moolenaar else 987071d4279SBram Moolenaar { 988fe86f2d7SBram Moolenaar if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) 989fe86f2d7SBram Moolenaar io_error = TRUE; 990071d4279SBram Moolenaar fclose(fd); 991e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_new.din_fname, "w"); 992fe86f2d7SBram Moolenaar if (fd == NULL) 993fe86f2d7SBram Moolenaar io_error = TRUE; 994fe86f2d7SBram Moolenaar else 995071d4279SBram Moolenaar { 996fe86f2d7SBram Moolenaar if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) 997fe86f2d7SBram Moolenaar io_error = TRUE; 998071d4279SBram Moolenaar fclose(fd); 999e828b762SBram Moolenaar fd = NULL; 1000e828b762SBram Moolenaar if (diff_file(diffio) == OK) 1001e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r"); 1002fe86f2d7SBram Moolenaar if (fd == NULL) 1003fe86f2d7SBram Moolenaar io_error = TRUE; 1004fe86f2d7SBram Moolenaar else 1005071d4279SBram Moolenaar { 1006071d4279SBram Moolenaar char_u linebuf[LBUFLEN]; 1007071d4279SBram Moolenaar 1008071d4279SBram Moolenaar for (;;) 1009071d4279SBram Moolenaar { 1010ad5c178aSglacambre // For normal diff there must be a line that contains 1011ad5c178aSglacambre // "1c1". For unified diff "@@ -1 +1 @@". 101200590740SBram Moolenaar if (vim_fgets(linebuf, LBUFLEN, fd)) 1013071d4279SBram Moolenaar break; 1014ad5c178aSglacambre if (STRNCMP(linebuf, "1c1", 3) == 0 1015ad5c178aSglacambre || STRNCMP(linebuf, "@@ -1 +1 @@", 11) == 0) 1016071d4279SBram Moolenaar ok = TRUE; 1017071d4279SBram Moolenaar } 1018071d4279SBram Moolenaar fclose(fd); 1019071d4279SBram Moolenaar } 1020e828b762SBram Moolenaar mch_remove(diffio->dio_diff.dout_fname); 1021e828b762SBram Moolenaar mch_remove(diffio->dio_new.din_fname); 1022071d4279SBram Moolenaar } 1023e828b762SBram Moolenaar mch_remove(diffio->dio_orig.din_fname); 1024071d4279SBram Moolenaar } 1025071d4279SBram Moolenaar 1026071d4279SBram Moolenaar #ifdef FEAT_EVAL 10275d18efecSBram Moolenaar // When using 'diffexpr' break here. 1028071d4279SBram Moolenaar if (*p_dex != NUL) 1029071d4279SBram Moolenaar break; 1030071d4279SBram Moolenaar #endif 1031071d4279SBram Moolenaar 103248e330afSBram Moolenaar #if defined(MSWIN) 10335d18efecSBram Moolenaar // If the "-a" argument works, also check if "--binary" works. 1034071d4279SBram Moolenaar if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE) 1035071d4279SBram Moolenaar { 1036071d4279SBram Moolenaar diff_a_works = TRUE; 1037071d4279SBram Moolenaar diff_bin_works = TRUE; 1038071d4279SBram Moolenaar continue; 1039071d4279SBram Moolenaar } 1040071d4279SBram Moolenaar if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE) 1041071d4279SBram Moolenaar { 10425d18efecSBram Moolenaar // Tried --binary, but it failed. "-a" works though. 1043071d4279SBram Moolenaar diff_bin_works = FALSE; 1044071d4279SBram Moolenaar ok = TRUE; 1045071d4279SBram Moolenaar } 1046071d4279SBram Moolenaar #endif 1047071d4279SBram Moolenaar 10485d18efecSBram Moolenaar // If we checked if "-a" works already, break here. 1049071d4279SBram Moolenaar if (diff_a_works != MAYBE) 1050071d4279SBram Moolenaar break; 1051071d4279SBram Moolenaar diff_a_works = ok; 1052071d4279SBram Moolenaar 10535d18efecSBram Moolenaar // If "-a" works break here, otherwise retry without "-a". 1054071d4279SBram Moolenaar if (ok) 1055071d4279SBram Moolenaar break; 1056071d4279SBram Moolenaar } 1057071d4279SBram Moolenaar if (!ok) 1058071d4279SBram Moolenaar { 1059fe86f2d7SBram Moolenaar if (io_error) 1060f9e3e09fSBram Moolenaar emsg(_("E810: Cannot read or write temp files")); 1061f9e3e09fSBram Moolenaar emsg(_("E97: Cannot create diffs")); 1062071d4279SBram Moolenaar diff_a_works = MAYBE; 106348e330afSBram Moolenaar #if defined(MSWIN) 1064071d4279SBram Moolenaar diff_bin_works = MAYBE; 1065071d4279SBram Moolenaar #endif 1066e828b762SBram Moolenaar return FAIL; 1067e828b762SBram Moolenaar } 1068e828b762SBram Moolenaar return OK; 1069071d4279SBram Moolenaar } 1070071d4279SBram Moolenaar 1071e828b762SBram Moolenaar /* 1072e828b762SBram Moolenaar * Invoke the xdiff function. 1073e828b762SBram Moolenaar */ 1074e828b762SBram Moolenaar static int 1075e828b762SBram Moolenaar diff_file_internal(diffio_T *diffio) 1076bd1d5608SBram Moolenaar { 1077e828b762SBram Moolenaar xpparam_t param; 1078e828b762SBram Moolenaar xdemitconf_t emit_cfg; 1079e828b762SBram Moolenaar xdemitcb_t emit_cb; 1080bd1d5608SBram Moolenaar 1081a80faa89SBram Moolenaar CLEAR_FIELD(param); 1082a80faa89SBram Moolenaar CLEAR_FIELD(emit_cfg); 1083a80faa89SBram Moolenaar CLEAR_FIELD(emit_cb); 1084071d4279SBram Moolenaar 1085e828b762SBram Moolenaar param.flags = diff_algorithm; 1086e828b762SBram Moolenaar 1087e828b762SBram Moolenaar if (diff_flags & DIFF_IWHITE) 1088e828b762SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; 1089785fc656SBram Moolenaar if (diff_flags & DIFF_IWHITEALL) 1090785fc656SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE; 1091785fc656SBram Moolenaar if (diff_flags & DIFF_IWHITEEOL) 1092785fc656SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; 1093785fc656SBram Moolenaar if (diff_flags & DIFF_IBLANK) 1094785fc656SBram Moolenaar param.flags |= XDF_IGNORE_BLANK_LINES; 1095e828b762SBram Moolenaar 1096e828b762SBram Moolenaar emit_cfg.ctxlen = 0; // don't need any diff_context here 1097e828b762SBram Moolenaar emit_cb.priv = &diffio->dio_diff; 1098e828b762SBram Moolenaar emit_cb.outf = xdiff_out; 1099e828b762SBram Moolenaar if (xdl_diff(&diffio->dio_orig.din_mmfile, 1100e828b762SBram Moolenaar &diffio->dio_new.din_mmfile, 1101e828b762SBram Moolenaar ¶m, &emit_cfg, &emit_cb) < 0) 1102071d4279SBram Moolenaar { 1103f9e3e09fSBram Moolenaar emsg(_("E960: Problem creating the internal diff")); 1104e828b762SBram Moolenaar return FAIL; 1105071d4279SBram Moolenaar } 1106e828b762SBram Moolenaar return OK; 1107071d4279SBram Moolenaar } 1108071d4279SBram Moolenaar 1109071d4279SBram Moolenaar /* 1110071d4279SBram Moolenaar * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". 1111e828b762SBram Moolenaar * return OK or FAIL; 1112071d4279SBram Moolenaar */ 1113e828b762SBram Moolenaar static int 1114e828b762SBram Moolenaar diff_file(diffio_T *dio) 1115071d4279SBram Moolenaar { 1116071d4279SBram Moolenaar char_u *cmd; 11175fd0ca70SBram Moolenaar size_t len; 1118e828b762SBram Moolenaar char_u *tmp_orig = dio->dio_orig.din_fname; 1119e828b762SBram Moolenaar char_u *tmp_new = dio->dio_new.din_fname; 1120e828b762SBram Moolenaar char_u *tmp_diff = dio->dio_diff.dout_fname; 1121071d4279SBram Moolenaar 1122071d4279SBram Moolenaar #ifdef FEAT_EVAL 1123071d4279SBram Moolenaar if (*p_dex != NUL) 1124e828b762SBram Moolenaar { 1125e828b762SBram Moolenaar // Use 'diffexpr' to generate the diff file. 1126071d4279SBram Moolenaar eval_diff(tmp_orig, tmp_new, tmp_diff); 1127e828b762SBram Moolenaar return OK; 1128e828b762SBram Moolenaar } 1129071d4279SBram Moolenaar else 1130071d4279SBram Moolenaar #endif 1131e828b762SBram Moolenaar // Use xdiff for generating the diff. 1132e828b762SBram Moolenaar if (dio->dio_internal) 1133e828b762SBram Moolenaar { 1134e828b762SBram Moolenaar return diff_file_internal(dio); 1135e828b762SBram Moolenaar } 1136e828b762SBram Moolenaar else 1137071d4279SBram Moolenaar { 11385fd0ca70SBram Moolenaar len = STRLEN(tmp_orig) + STRLEN(tmp_new) 11395fd0ca70SBram Moolenaar + STRLEN(tmp_diff) + STRLEN(p_srr) + 27; 1140964b3746SBram Moolenaar cmd = alloc(len); 1141e828b762SBram Moolenaar if (cmd == NULL) 1142e828b762SBram Moolenaar return FAIL; 1143e828b762SBram Moolenaar 1144e828b762SBram Moolenaar // We don't want $DIFF_OPTIONS to get in the way. 114595fb60acSBram Moolenaar if (getenv("DIFF_OPTIONS")) 114695fb60acSBram Moolenaar vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)""); 114795fb60acSBram Moolenaar 1148e828b762SBram Moolenaar // Build the diff command and execute it. Always use -a, binary 1149e828b762SBram Moolenaar // differences are of no use. Ignore errors, diff returns 1150e828b762SBram Moolenaar // non-zero when differences have been found. 1151785fc656SBram Moolenaar vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s", 1152071d4279SBram Moolenaar diff_a_works == FALSE ? "" : "-a ", 115348e330afSBram Moolenaar #if defined(MSWIN) 1154071d4279SBram Moolenaar diff_bin_works == TRUE ? "--binary " : "", 1155071d4279SBram Moolenaar #else 1156071d4279SBram Moolenaar "", 1157071d4279SBram Moolenaar #endif 1158071d4279SBram Moolenaar (diff_flags & DIFF_IWHITE) ? "-b " : "", 1159785fc656SBram Moolenaar (diff_flags & DIFF_IWHITEALL) ? "-w " : "", 1160785fc656SBram Moolenaar (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", 1161785fc656SBram Moolenaar (diff_flags & DIFF_IBLANK) ? "-B " : "", 1162071d4279SBram Moolenaar (diff_flags & DIFF_ICASE) ? "-i " : "", 1163071d4279SBram Moolenaar tmp_orig, tmp_new); 11645fd0ca70SBram Moolenaar append_redir(cmd, (int)len, p_srr, tmp_diff); 1165e828b762SBram Moolenaar block_autocmds(); // avoid ShellCmdPost stuff 1166071d4279SBram Moolenaar (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT); 116778ab331eSBram Moolenaar unblock_autocmds(); 1168071d4279SBram Moolenaar vim_free(cmd); 1169e828b762SBram Moolenaar return OK; 1170071d4279SBram Moolenaar } 1171071d4279SBram Moolenaar } 1172071d4279SBram Moolenaar 1173071d4279SBram Moolenaar /* 1174071d4279SBram Moolenaar * Create a new version of a file from the current buffer and a diff file. 1175071d4279SBram Moolenaar * The buffer is written to a file, also for unmodified buffers (the file 1176071d4279SBram Moolenaar * could have been produced by autocommands, e.g. the netrw plugin). 1177071d4279SBram Moolenaar */ 1178071d4279SBram Moolenaar void 11797454a06eSBram Moolenaar ex_diffpatch(exarg_T *eap) 1180071d4279SBram Moolenaar { 11815d18efecSBram Moolenaar char_u *tmp_orig; // name of original temp file 11825d18efecSBram Moolenaar char_u *tmp_new; // name of patched temp file 1183071d4279SBram Moolenaar char_u *buf = NULL; 11845fd0ca70SBram Moolenaar size_t buflen; 1185071d4279SBram Moolenaar win_T *old_curwin = curwin; 11865d18efecSBram Moolenaar char_u *newname = NULL; // name of patched file buffer 1187071d4279SBram Moolenaar #ifdef UNIX 1188071d4279SBram Moolenaar char_u dirbuf[MAXPATHL]; 1189071d4279SBram Moolenaar char_u *fullname = NULL; 1190071d4279SBram Moolenaar #endif 1191071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1192071d4279SBram Moolenaar char_u *browseFile = NULL; 1193e1004401SBram Moolenaar int save_cmod_flags = cmdmod.cmod_flags; 1194071d4279SBram Moolenaar #endif 11958767f52fSBram Moolenaar stat_T st; 1196a95ab321SBram Moolenaar char_u *esc_name = NULL; 1197071d4279SBram Moolenaar 1198071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1199e1004401SBram Moolenaar if (cmdmod.cmod_flags & CMOD_BROWSE) 1200071d4279SBram Moolenaar { 12017171abeaSBram Moolenaar browseFile = do_browse(0, (char_u *)_("Patch file"), 1202c36651b4SBram Moolenaar eap->arg, NULL, NULL, 1203c36651b4SBram Moolenaar (char_u *)_(BROWSE_FILTER_ALL_FILES), NULL); 1204071d4279SBram Moolenaar if (browseFile == NULL) 12055d18efecSBram Moolenaar return; // operation cancelled 1206071d4279SBram Moolenaar eap->arg = browseFile; 1207e1004401SBram Moolenaar cmdmod.cmod_flags &= ~CMOD_BROWSE; // don't let do_ecmd() browse again 1208071d4279SBram Moolenaar } 1209071d4279SBram Moolenaar #endif 1210071d4279SBram Moolenaar 12115d18efecSBram Moolenaar // We need two temp file names. 1212e5c421cfSBram Moolenaar tmp_orig = vim_tempname('o', FALSE); 1213e5c421cfSBram Moolenaar tmp_new = vim_tempname('n', FALSE); 1214071d4279SBram Moolenaar if (tmp_orig == NULL || tmp_new == NULL) 1215071d4279SBram Moolenaar goto theend; 1216071d4279SBram Moolenaar 12175d18efecSBram Moolenaar // Write the current buffer to "tmp_orig". 1218071d4279SBram Moolenaar if (buf_write(curbuf, tmp_orig, NULL, 1219071d4279SBram Moolenaar (linenr_T)1, curbuf->b_ml.ml_line_count, 1220071d4279SBram Moolenaar NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) 1221071d4279SBram Moolenaar goto theend; 1222071d4279SBram Moolenaar 1223071d4279SBram Moolenaar #ifdef UNIX 12245d18efecSBram Moolenaar // Get the absolute path of the patchfile, changing directory below. 1225071d4279SBram Moolenaar fullname = FullName_save(eap->arg, FALSE); 1226071d4279SBram Moolenaar #endif 1227a95ab321SBram Moolenaar esc_name = vim_strsave_shellescape( 1228071d4279SBram Moolenaar # ifdef UNIX 1229a95ab321SBram Moolenaar fullname != NULL ? fullname : 1230071d4279SBram Moolenaar # endif 1231a95ab321SBram Moolenaar eap->arg, TRUE, TRUE); 1232a95ab321SBram Moolenaar if (esc_name == NULL) 1233a95ab321SBram Moolenaar goto theend; 1234a95ab321SBram Moolenaar buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16; 1235964b3746SBram Moolenaar buf = alloc(buflen); 1236071d4279SBram Moolenaar if (buf == NULL) 1237071d4279SBram Moolenaar goto theend; 1238071d4279SBram Moolenaar 1239071d4279SBram Moolenaar #ifdef UNIX 12405d18efecSBram Moolenaar // Temporarily chdir to /tmp, to avoid patching files in the current 12415d18efecSBram Moolenaar // directory when the patch file contains more than one patch. When we 12425d18efecSBram Moolenaar // have our own temp dir use that instead, it will be cleaned up when we 12435d18efecSBram Moolenaar // exit (any .rej files created). Don't change directory if we can't 12445d18efecSBram Moolenaar // return to the current. 1245071d4279SBram Moolenaar if (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0) 1246071d4279SBram Moolenaar dirbuf[0] = NUL; 1247071d4279SBram Moolenaar else 1248071d4279SBram Moolenaar { 1249071d4279SBram Moolenaar # ifdef TEMPDIRNAMES 1250071d4279SBram Moolenaar if (vim_tempdir != NULL) 125142335f50SBram Moolenaar vim_ignored = mch_chdir((char *)vim_tempdir); 1252071d4279SBram Moolenaar else 1253071d4279SBram Moolenaar # endif 125442335f50SBram Moolenaar vim_ignored = mch_chdir("/tmp"); 1255071d4279SBram Moolenaar shorten_fnames(TRUE); 1256071d4279SBram Moolenaar } 1257071d4279SBram Moolenaar #endif 1258071d4279SBram Moolenaar 1259071d4279SBram Moolenaar #ifdef FEAT_EVAL 1260071d4279SBram Moolenaar if (*p_pex != NUL) 12615d18efecSBram Moolenaar // Use 'patchexpr' to generate the new file. 1262071d4279SBram Moolenaar eval_patch(tmp_orig, 1263071d4279SBram Moolenaar # ifdef UNIX 1264071d4279SBram Moolenaar fullname != NULL ? fullname : 1265071d4279SBram Moolenaar # endif 1266071d4279SBram Moolenaar eap->arg, tmp_new); 1267071d4279SBram Moolenaar else 1268071d4279SBram Moolenaar #endif 1269071d4279SBram Moolenaar { 12705d18efecSBram Moolenaar // Build the patch command and execute it. Ignore errors. Switch to 12715d18efecSBram Moolenaar // cooked mode to allow the user to respond to prompts. 1272a95ab321SBram Moolenaar vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", 1273a95ab321SBram Moolenaar tmp_new, tmp_orig, esc_name); 12745d18efecSBram Moolenaar block_autocmds(); // Avoid ShellCmdPost stuff 1275071d4279SBram Moolenaar (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED); 127678ab331eSBram Moolenaar unblock_autocmds(); 1277071d4279SBram Moolenaar } 1278071d4279SBram Moolenaar 1279071d4279SBram Moolenaar #ifdef UNIX 1280071d4279SBram Moolenaar if (dirbuf[0] != NUL) 1281071d4279SBram Moolenaar { 1282071d4279SBram Moolenaar if (mch_chdir((char *)dirbuf) != 0) 1283f9e3e09fSBram Moolenaar emsg(_(e_prev_dir)); 1284071d4279SBram Moolenaar shorten_fnames(TRUE); 1285071d4279SBram Moolenaar } 1286071d4279SBram Moolenaar #endif 1287071d4279SBram Moolenaar 12885d18efecSBram Moolenaar // patch probably has written over the screen 1289071d4279SBram Moolenaar redraw_later(CLEAR); 1290071d4279SBram Moolenaar 12915d18efecSBram Moolenaar // Delete any .orig or .rej file created. 1292071d4279SBram Moolenaar STRCPY(buf, tmp_new); 1293071d4279SBram Moolenaar STRCAT(buf, ".orig"); 1294071d4279SBram Moolenaar mch_remove(buf); 1295071d4279SBram Moolenaar STRCPY(buf, tmp_new); 1296071d4279SBram Moolenaar STRCAT(buf, ".rej"); 1297071d4279SBram Moolenaar mch_remove(buf); 1298071d4279SBram Moolenaar 12995d18efecSBram Moolenaar // Only continue if the output file was created. 13006ec0a6c4SBram Moolenaar if (mch_stat((char *)tmp_new, &st) < 0 || st.st_size == 0) 1301f9e3e09fSBram Moolenaar emsg(_("E816: Cannot read patch output")); 13026ec0a6c4SBram Moolenaar else 13036ec0a6c4SBram Moolenaar { 1304071d4279SBram Moolenaar if (curbuf->b_fname != NULL) 1305071d4279SBram Moolenaar { 1306071d4279SBram Moolenaar newname = vim_strnsave(curbuf->b_fname, 1307df44a27bSBram Moolenaar STRLEN(curbuf->b_fname) + 4); 1308071d4279SBram Moolenaar if (newname != NULL) 1309071d4279SBram Moolenaar STRCAT(newname, ".new"); 1310071d4279SBram Moolenaar } 1311071d4279SBram Moolenaar 1312071d4279SBram Moolenaar #ifdef FEAT_GUI 1313071d4279SBram Moolenaar need_mouse_correct = TRUE; 1314071d4279SBram Moolenaar #endif 13155d18efecSBram Moolenaar // don't use a new tab page, each tab page has its own diffs 1316e1004401SBram Moolenaar cmdmod.cmod_tab = 0; 131780a94a58SBram Moolenaar 1318c4675a19SBram Moolenaar if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) 1319071d4279SBram Moolenaar { 13205d18efecSBram Moolenaar // Pretend it was a ":split fname" command 1321071d4279SBram Moolenaar eap->cmdidx = CMD_split; 1322071d4279SBram Moolenaar eap->arg = tmp_new; 1323071d4279SBram Moolenaar do_exedit(eap, old_curwin); 1324071d4279SBram Moolenaar 13255d18efecSBram Moolenaar // check that split worked and editing tmp_new 13266ec0a6c4SBram Moolenaar if (curwin != old_curwin && win_valid(old_curwin)) 1327071d4279SBram Moolenaar { 13285d18efecSBram Moolenaar // Set 'diff', 'scrollbind' on and 'wrap' off. 1329071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1330071d4279SBram Moolenaar diff_win_options(old_curwin, TRUE); 1331071d4279SBram Moolenaar 1332071d4279SBram Moolenaar if (newname != NULL) 1333071d4279SBram Moolenaar { 13345d18efecSBram Moolenaar // do a ":file filename.new" on the patched buffer 1335071d4279SBram Moolenaar eap->arg = newname; 1336071d4279SBram Moolenaar ex_file(eap); 1337071d4279SBram Moolenaar 13385d18efecSBram Moolenaar // Do filetype detection with the new name. 1339910f66f9SBram Moolenaar if (au_has_group((char_u *)"filetypedetect")) 1340071d4279SBram Moolenaar do_cmdline_cmd((char_u *)":doau filetypedetect BufRead"); 1341071d4279SBram Moolenaar } 1342071d4279SBram Moolenaar } 1343071d4279SBram Moolenaar } 13446ec0a6c4SBram Moolenaar } 1345071d4279SBram Moolenaar 1346071d4279SBram Moolenaar theend: 1347071d4279SBram Moolenaar if (tmp_orig != NULL) 1348071d4279SBram Moolenaar mch_remove(tmp_orig); 1349071d4279SBram Moolenaar vim_free(tmp_orig); 1350071d4279SBram Moolenaar if (tmp_new != NULL) 1351071d4279SBram Moolenaar mch_remove(tmp_new); 1352071d4279SBram Moolenaar vim_free(tmp_new); 1353071d4279SBram Moolenaar vim_free(newname); 1354071d4279SBram Moolenaar vim_free(buf); 1355071d4279SBram Moolenaar #ifdef UNIX 1356071d4279SBram Moolenaar vim_free(fullname); 1357071d4279SBram Moolenaar #endif 1358a95ab321SBram Moolenaar vim_free(esc_name); 1359071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1360071d4279SBram Moolenaar vim_free(browseFile); 1361e1004401SBram Moolenaar cmdmod.cmod_flags = save_cmod_flags; 1362071d4279SBram Moolenaar #endif 1363071d4279SBram Moolenaar } 1364071d4279SBram Moolenaar 1365071d4279SBram Moolenaar /* 1366071d4279SBram Moolenaar * Split the window and edit another file, setting options to show the diffs. 1367071d4279SBram Moolenaar */ 1368071d4279SBram Moolenaar void 13697454a06eSBram Moolenaar ex_diffsplit(exarg_T *eap) 1370071d4279SBram Moolenaar { 1371071d4279SBram Moolenaar win_T *old_curwin = curwin; 13727c0a2f36SBram Moolenaar bufref_T old_curbuf; 1373071d4279SBram Moolenaar 13747c0a2f36SBram Moolenaar set_bufref(&old_curbuf, curbuf); 1375071d4279SBram Moolenaar #ifdef FEAT_GUI 1376071d4279SBram Moolenaar need_mouse_correct = TRUE; 1377071d4279SBram Moolenaar #endif 13785d18efecSBram Moolenaar // Need to compute w_fraction when no redraw happened yet. 137946328f9aSBram Moolenaar validate_cursor(); 138046328f9aSBram Moolenaar set_fraction(curwin); 138146328f9aSBram Moolenaar 13825d18efecSBram Moolenaar // don't use a new tab page, each tab page has its own diffs 1383e1004401SBram Moolenaar cmdmod.cmod_tab = 0; 138480a94a58SBram Moolenaar 1385c4675a19SBram Moolenaar if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) 1386071d4279SBram Moolenaar { 13875d18efecSBram Moolenaar // Pretend it was a ":split fname" command 1388071d4279SBram Moolenaar eap->cmdidx = CMD_split; 13894be06f9eSBram Moolenaar curwin->w_p_diff = TRUE; 1390071d4279SBram Moolenaar do_exedit(eap, old_curwin); 1391071d4279SBram Moolenaar 13925d18efecSBram Moolenaar if (curwin != old_curwin) // split must have worked 1393071d4279SBram Moolenaar { 13945d18efecSBram Moolenaar // Set 'diff', 'scrollbind' on and 'wrap' off. 1395071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1396f29a82dcSBram Moolenaar if (win_valid(old_curwin)) 1397f29a82dcSBram Moolenaar { 1398071d4279SBram Moolenaar diff_win_options(old_curwin, TRUE); 1399f29a82dcSBram Moolenaar 14007c0a2f36SBram Moolenaar if (bufref_valid(&old_curbuf)) 14015d18efecSBram Moolenaar // Move the cursor position to that of the old window. 1402f29a82dcSBram Moolenaar curwin->w_cursor.lnum = diff_get_corresponding_line( 1403025e3e0bSBram Moolenaar old_curbuf.br_buf, old_curwin->w_cursor.lnum); 1404f29a82dcSBram Moolenaar } 14055d18efecSBram Moolenaar // Now that lines are folded scroll to show the cursor at the same 14065d18efecSBram Moolenaar // relative position. 140746328f9aSBram Moolenaar scroll_to_fraction(curwin, curwin->w_height); 1408071d4279SBram Moolenaar } 1409071d4279SBram Moolenaar } 1410071d4279SBram Moolenaar } 1411071d4279SBram Moolenaar 1412071d4279SBram Moolenaar /* 141384a05accSBram Moolenaar * Set options to show diffs for the current window. 1414071d4279SBram Moolenaar */ 1415071d4279SBram Moolenaar void 14167454a06eSBram Moolenaar ex_diffthis(exarg_T *eap UNUSED) 1417071d4279SBram Moolenaar { 14185d18efecSBram Moolenaar // Set 'diff', 'scrollbind' on and 'wrap' off. 1419071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1420071d4279SBram Moolenaar } 1421071d4279SBram Moolenaar 142204f62f88SBram Moolenaar static void 142304f62f88SBram Moolenaar set_diff_option(win_T *wp, int value) 142404f62f88SBram Moolenaar { 142504f62f88SBram Moolenaar win_T *old_curwin = curwin; 142604f62f88SBram Moolenaar 142704f62f88SBram Moolenaar curwin = wp; 142804f62f88SBram Moolenaar curbuf = curwin->w_buffer; 142904f62f88SBram Moolenaar ++curbuf_lock; 143004f62f88SBram Moolenaar set_option_value((char_u *)"diff", (long)value, NULL, OPT_LOCAL); 143104f62f88SBram Moolenaar --curbuf_lock; 143204f62f88SBram Moolenaar curwin = old_curwin; 143304f62f88SBram Moolenaar curbuf = curwin->w_buffer; 143404f62f88SBram Moolenaar } 143504f62f88SBram Moolenaar 1436071d4279SBram Moolenaar /* 1437071d4279SBram Moolenaar * Set options in window "wp" for diff mode. 1438071d4279SBram Moolenaar */ 1439071d4279SBram Moolenaar void 14407454a06eSBram Moolenaar diff_win_options( 14417454a06eSBram Moolenaar win_T *wp, 14425d18efecSBram Moolenaar int addbuf) // Add buffer to diff. 1443071d4279SBram Moolenaar { 1444f4d7f944SBram Moolenaar # ifdef FEAT_FOLDING 1445f4d7f944SBram Moolenaar win_T *old_curwin = curwin; 1446f4d7f944SBram Moolenaar 14475d18efecSBram Moolenaar // close the manually opened folds 1448f4d7f944SBram Moolenaar curwin = wp; 1449f4d7f944SBram Moolenaar newFoldLevel(); 1450f4d7f944SBram Moolenaar curwin = old_curwin; 1451f4d7f944SBram Moolenaar # endif 1452f4d7f944SBram Moolenaar 14535d18efecSBram Moolenaar // Use 'scrollbind' and 'cursorbind' when available 145443929964SBram Moolenaar if (!wp->w_p_diff) 1455a87aa806SBram Moolenaar wp->w_p_scb_save = wp->w_p_scb; 14563368ea21SBram Moolenaar wp->w_p_scb = TRUE; 145743929964SBram Moolenaar if (!wp->w_p_diff) 1458a87aa806SBram Moolenaar wp->w_p_crb_save = wp->w_p_crb; 1459860cae1cSBram Moolenaar wp->w_p_crb = TRUE; 14604223d43cSBram Moolenaar if (!(diff_flags & DIFF_FOLLOWWRAP)) 14614223d43cSBram Moolenaar { 146243929964SBram Moolenaar if (!wp->w_p_diff) 1463a87aa806SBram Moolenaar wp->w_p_wrap_save = wp->w_p_wrap; 1464071d4279SBram Moolenaar wp->w_p_wrap = FALSE; 14654223d43cSBram Moolenaar } 1466071d4279SBram Moolenaar # ifdef FEAT_FOLDING 146743929964SBram Moolenaar if (!wp->w_p_diff) 146843929964SBram Moolenaar { 146943929964SBram Moolenaar if (wp->w_p_diff_saved) 147043929964SBram Moolenaar free_string_option(wp->w_p_fdm_save); 1471a87aa806SBram Moolenaar wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); 147243929964SBram Moolenaar } 147320c023aeSBram Moolenaar set_string_option_direct_in_win(wp, (char_u *)"fdm", -1, (char_u *)"diff", 14745e3cb7e8SBram Moolenaar OPT_LOCAL|OPT_FREE, 0); 147543929964SBram Moolenaar if (!wp->w_p_diff) 1476a87aa806SBram Moolenaar { 1477a87aa806SBram Moolenaar wp->w_p_fdc_save = wp->w_p_fdc; 1478a87aa806SBram Moolenaar wp->w_p_fen_save = wp->w_p_fen; 1479a87aa806SBram Moolenaar wp->w_p_fdl_save = wp->w_p_fdl; 1480a87aa806SBram Moolenaar } 1481c4675a19SBram Moolenaar wp->w_p_fdc = diff_foldcolumn; 1482071d4279SBram Moolenaar wp->w_p_fen = TRUE; 1483071d4279SBram Moolenaar wp->w_p_fdl = 0; 1484071d4279SBram Moolenaar foldUpdateAll(wp); 14855d18efecSBram Moolenaar // make sure topline is not halfway a fold 14862df6dcc5SBram Moolenaar changed_window_setting_win(wp); 1487071d4279SBram Moolenaar # endif 1488071d4279SBram Moolenaar if (vim_strchr(p_sbo, 'h') == NULL) 1489071d4279SBram Moolenaar do_cmdline_cmd((char_u *)"set sbo+=hor"); 14905d18efecSBram Moolenaar // Save the current values, to be restored in ex_diffoff(). 1491a87aa806SBram Moolenaar wp->w_p_diff_saved = TRUE; 1492071d4279SBram Moolenaar 149304f62f88SBram Moolenaar set_diff_option(wp, TRUE); 149443929964SBram Moolenaar 1495071d4279SBram Moolenaar if (addbuf) 1496071d4279SBram Moolenaar diff_buf_add(wp->w_buffer); 1497071d4279SBram Moolenaar redraw_win_later(wp, NOT_VALID); 1498071d4279SBram Moolenaar } 1499071d4279SBram Moolenaar 1500071d4279SBram Moolenaar /* 15012df6dcc5SBram Moolenaar * Set options not to show diffs. For the current window or all windows. 1502f740b29aSBram Moolenaar * Only in the current tab page. 15032df6dcc5SBram Moolenaar */ 15042df6dcc5SBram Moolenaar void 15057454a06eSBram Moolenaar ex_diffoff(exarg_T *eap) 15062df6dcc5SBram Moolenaar { 15072df6dcc5SBram Moolenaar win_T *wp; 15082df6dcc5SBram Moolenaar int diffwin = FALSE; 15092df6dcc5SBram Moolenaar 151029323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 15112df6dcc5SBram Moolenaar { 151200462ffbSBram Moolenaar if (eap->forceit ? wp->w_p_diff : wp == curwin) 15132df6dcc5SBram Moolenaar { 15145d18efecSBram Moolenaar // Set 'diff' off. If option values were saved in 15155d18efecSBram Moolenaar // diff_win_options(), restore the ones whose settings seem to have 15165d18efecSBram Moolenaar // been left over from diff mode. 151704f62f88SBram Moolenaar set_diff_option(wp, FALSE); 1518a87aa806SBram Moolenaar 151943929964SBram Moolenaar if (wp->w_p_diff_saved) 152043929964SBram Moolenaar { 152143929964SBram Moolenaar 1522a87aa806SBram Moolenaar if (wp->w_p_scb) 152343929964SBram Moolenaar wp->w_p_scb = wp->w_p_scb_save; 1524a87aa806SBram Moolenaar if (wp->w_p_crb) 152543929964SBram Moolenaar wp->w_p_crb = wp->w_p_crb_save; 15264223d43cSBram Moolenaar if (!(diff_flags & DIFF_FOLLOWWRAP)) 15274223d43cSBram Moolenaar { 1528a87aa806SBram Moolenaar if (!wp->w_p_wrap) 152943929964SBram Moolenaar wp->w_p_wrap = wp->w_p_wrap_save; 15304223d43cSBram Moolenaar } 15312df6dcc5SBram Moolenaar #ifdef FEAT_FOLDING 1532a87aa806SBram Moolenaar free_string_option(wp->w_p_fdm); 153379a213d6SBram Moolenaar wp->w_p_fdm = vim_strsave( 153479a213d6SBram Moolenaar *wp->w_p_fdm_save ? wp->w_p_fdm_save : (char_u*)"manual"); 153543929964SBram Moolenaar 1536a87aa806SBram Moolenaar if (wp->w_p_fdc == diff_foldcolumn) 153743929964SBram Moolenaar wp->w_p_fdc = wp->w_p_fdc_save; 153843929964SBram Moolenaar if (wp->w_p_fdl == 0) 1539a87aa806SBram Moolenaar wp->w_p_fdl = wp->w_p_fdl_save; 154033ca6bf2SBram Moolenaar 15415d18efecSBram Moolenaar // Only restore 'foldenable' when 'foldmethod' is not 15425d18efecSBram Moolenaar // "manual", otherwise we continue to show the diff folds. 154343929964SBram Moolenaar if (wp->w_p_fen) 154443929964SBram Moolenaar wp->w_p_fen = foldmethodIsManual(wp) ? FALSE 154543929964SBram Moolenaar : wp->w_p_fen_save; 154633ca6bf2SBram Moolenaar 15472df6dcc5SBram Moolenaar foldUpdateAll(wp); 15482df6dcc5SBram Moolenaar #endif 154943929964SBram Moolenaar } 15505d18efecSBram Moolenaar // remove filler lines 1551e67d546fSBram Moolenaar wp->w_topfill = 0; 1552e67d546fSBram Moolenaar 15535d18efecSBram Moolenaar // make sure topline is not halfway a fold and cursor is 15545d18efecSBram Moolenaar // invalidated 1555e67d546fSBram Moolenaar changed_window_setting_win(wp); 155643929964SBram Moolenaar 15575d18efecSBram Moolenaar // Note: 'sbo' is not restored, it's a global option. 15582df6dcc5SBram Moolenaar diff_buf_adjust(wp); 15592df6dcc5SBram Moolenaar } 15602df6dcc5SBram Moolenaar diffwin |= wp->w_p_diff; 15612df6dcc5SBram Moolenaar } 15622df6dcc5SBram Moolenaar 15635d18efecSBram Moolenaar // Also remove hidden buffers from the list. 156425ea0544SBram Moolenaar if (eap->forceit) 156525ea0544SBram Moolenaar diff_buf_clear(); 156625ea0544SBram Moolenaar 1567c8234779SBram Moolenaar if (!diffwin) 1568c8234779SBram Moolenaar { 1569c8234779SBram Moolenaar diff_need_update = FALSE; 1570c8234779SBram Moolenaar curtab->tp_diff_invalid = FALSE; 1571c8234779SBram Moolenaar curtab->tp_diff_update = FALSE; 1572c8234779SBram Moolenaar diff_clear(curtab); 1573c8234779SBram Moolenaar } 1574c8234779SBram Moolenaar 15755d18efecSBram Moolenaar // Remove "hor" from from 'scrollopt' if there are no diff windows left. 15762df6dcc5SBram Moolenaar if (!diffwin && vim_strchr(p_sbo, 'h') != NULL) 15772df6dcc5SBram Moolenaar do_cmdline_cmd((char_u *)"set sbo-=hor"); 15782df6dcc5SBram Moolenaar } 15792df6dcc5SBram Moolenaar 15802df6dcc5SBram Moolenaar /* 1581071d4279SBram Moolenaar * Read the diff output and add each entry to the diff list. 1582071d4279SBram Moolenaar */ 1583071d4279SBram Moolenaar static void 15847454a06eSBram Moolenaar diff_read( 1585e828b762SBram Moolenaar int idx_orig, // idx of original file 1586e828b762SBram Moolenaar int idx_new, // idx of new file 1587e828b762SBram Moolenaar diffout_T *dout) // diff output 1588071d4279SBram Moolenaar { 1589e828b762SBram Moolenaar FILE *fd = NULL; 1590e828b762SBram Moolenaar int line_idx = 0; 1591071d4279SBram Moolenaar diff_T *dprev = NULL; 159249d7bf13SBram Moolenaar diff_T *dp = curtab->tp_first_diff; 1593071d4279SBram Moolenaar diff_T *dn, *dpl; 15945d18efecSBram Moolenaar char_u linebuf[LBUFLEN]; // only need to hold the diff line 1595e828b762SBram Moolenaar char_u *line; 1596071d4279SBram Moolenaar long off; 1597071d4279SBram Moolenaar int i; 1598071d4279SBram Moolenaar linenr_T lnum_orig, lnum_new; 1599071d4279SBram Moolenaar long count_orig, count_new; 16005d18efecSBram Moolenaar int notset = TRUE; // block "*dp" not set yet 1601e828b762SBram Moolenaar enum { 1602e828b762SBram Moolenaar DIFF_ED, 1603e828b762SBram Moolenaar DIFF_UNIFIED, 1604e828b762SBram Moolenaar DIFF_NONE 1605e828b762SBram Moolenaar } diffstyle = DIFF_NONE; 1606071d4279SBram Moolenaar 1607e828b762SBram Moolenaar if (dout->dout_fname == NULL) 1608e828b762SBram Moolenaar { 1609e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 1610e828b762SBram Moolenaar } 1611e828b762SBram Moolenaar else 1612e828b762SBram Moolenaar { 1613e828b762SBram Moolenaar fd = mch_fopen((char *)dout->dout_fname, "r"); 1614071d4279SBram Moolenaar if (fd == NULL) 1615071d4279SBram Moolenaar { 1616f9e3e09fSBram Moolenaar emsg(_("E98: Cannot read diff output")); 1617071d4279SBram Moolenaar return; 1618071d4279SBram Moolenaar } 1619e828b762SBram Moolenaar } 1620071d4279SBram Moolenaar 1621071d4279SBram Moolenaar for (;;) 1622071d4279SBram Moolenaar { 1623e828b762SBram Moolenaar if (fd == NULL) 1624e828b762SBram Moolenaar { 1625e828b762SBram Moolenaar if (line_idx >= dout->dout_ga.ga_len) 1626e828b762SBram Moolenaar break; // did last line 1627e828b762SBram Moolenaar line = ((char_u **)dout->dout_ga.ga_data)[line_idx++]; 1628e828b762SBram Moolenaar } 1629e828b762SBram Moolenaar else 1630e828b762SBram Moolenaar { 163100590740SBram Moolenaar if (vim_fgets(linebuf, LBUFLEN, fd)) 1632e828b762SBram Moolenaar break; // end of file 1633e828b762SBram Moolenaar line = linebuf; 1634071d4279SBram Moolenaar } 1635071d4279SBram Moolenaar 1636e828b762SBram Moolenaar if (diffstyle == DIFF_NONE) 1637e828b762SBram Moolenaar { 1638e828b762SBram Moolenaar // Determine diff style. 1639e828b762SBram Moolenaar // ed like diff looks like this: 1640e828b762SBram Moolenaar // {first}[,{last}]c{first}[,{last}] 1641e828b762SBram Moolenaar // {first}a{first}[,{last}] 1642e828b762SBram Moolenaar // {first}[,{last}]d{first} 1643e828b762SBram Moolenaar // 1644e828b762SBram Moolenaar // unified diff looks like this: 1645e828b762SBram Moolenaar // --- file1 2018-03-20 13:23:35.783153140 +0100 1646e828b762SBram Moolenaar // +++ file2 2018-03-20 13:23:41.183156066 +0100 1647e828b762SBram Moolenaar // @@ -1,3 +1,5 @@ 1648e828b762SBram Moolenaar if (isdigit(*line)) 1649e828b762SBram Moolenaar diffstyle = DIFF_ED; 1650e828b762SBram Moolenaar else if ((STRNCMP(line, "@@ ", 3) == 0)) 1651e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 1652e828b762SBram Moolenaar else if ((STRNCMP(line, "--- ", 4) == 0) 165300590740SBram Moolenaar && (vim_fgets(linebuf, LBUFLEN, fd) == 0) 1654e828b762SBram Moolenaar && (STRNCMP(line, "+++ ", 4) == 0) 165500590740SBram Moolenaar && (vim_fgets(linebuf, LBUFLEN, fd) == 0) 1656e828b762SBram Moolenaar && (STRNCMP(line, "@@ ", 3) == 0)) 1657e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 16583b8defd0SBram Moolenaar else 16593b8defd0SBram Moolenaar // Format not recognized yet, skip over this line. Cygwin diff 16603b8defd0SBram Moolenaar // may put a warning at the start of the file. 16613b8defd0SBram Moolenaar continue; 1662e828b762SBram Moolenaar } 1663e828b762SBram Moolenaar 1664e828b762SBram Moolenaar if (diffstyle == DIFF_ED) 1665e828b762SBram Moolenaar { 1666e828b762SBram Moolenaar if (!isdigit(*line)) 1667e828b762SBram Moolenaar continue; // not the start of a diff block 1668e828b762SBram Moolenaar if (parse_diff_ed(line, &lnum_orig, &count_orig, 1669e828b762SBram Moolenaar &lnum_new, &count_new) == FAIL) 1670e828b762SBram Moolenaar continue; 1671e828b762SBram Moolenaar } 1672e828b762SBram Moolenaar else if (diffstyle == DIFF_UNIFIED) 1673e828b762SBram Moolenaar { 1674e828b762SBram Moolenaar if (STRNCMP(line, "@@ ", 3) != 0) 1675e828b762SBram Moolenaar continue; // not the start of a diff block 1676e828b762SBram Moolenaar if (parse_diff_unified(line, &lnum_orig, &count_orig, 1677e828b762SBram Moolenaar &lnum_new, &count_new) == FAIL) 1678e828b762SBram Moolenaar continue; 1679e828b762SBram Moolenaar } 1680e828b762SBram Moolenaar else 1681e828b762SBram Moolenaar { 1682f9e3e09fSBram Moolenaar emsg(_("E959: Invalid diff format.")); 1683e828b762SBram Moolenaar break; 1684e828b762SBram Moolenaar } 1685e828b762SBram Moolenaar 1686e828b762SBram Moolenaar // Go over blocks before the change, for which orig and new are equal. 1687e828b762SBram Moolenaar // Copy blocks from orig to new. 1688071d4279SBram Moolenaar while (dp != NULL 1689071d4279SBram Moolenaar && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) 1690071d4279SBram Moolenaar { 1691071d4279SBram Moolenaar if (notset) 1692071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, idx_new); 1693071d4279SBram Moolenaar dprev = dp; 1694071d4279SBram Moolenaar dp = dp->df_next; 1695071d4279SBram Moolenaar notset = TRUE; 1696071d4279SBram Moolenaar } 1697071d4279SBram Moolenaar 1698071d4279SBram Moolenaar if (dp != NULL 1699071d4279SBram Moolenaar && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig] 1700071d4279SBram Moolenaar && lnum_orig + count_orig >= dp->df_lnum[idx_orig]) 1701071d4279SBram Moolenaar { 1702e828b762SBram Moolenaar // New block overlaps with existing block(s). 1703e828b762SBram Moolenaar // First find last block that overlaps. 1704071d4279SBram Moolenaar for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) 1705071d4279SBram Moolenaar if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) 1706071d4279SBram Moolenaar break; 1707071d4279SBram Moolenaar 1708e828b762SBram Moolenaar // If the newly found block starts before the old one, set the 1709e828b762SBram Moolenaar // start back a number of lines. 1710071d4279SBram Moolenaar off = dp->df_lnum[idx_orig] - lnum_orig; 1711071d4279SBram Moolenaar if (off > 0) 1712071d4279SBram Moolenaar { 1713071d4279SBram Moolenaar for (i = idx_orig; i < idx_new; ++i) 171449d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1715071d4279SBram Moolenaar dp->df_lnum[i] -= off; 1716071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new; 1717071d4279SBram Moolenaar dp->df_count[idx_new] = count_new; 1718071d4279SBram Moolenaar } 1719071d4279SBram Moolenaar else if (notset) 1720071d4279SBram Moolenaar { 1721e828b762SBram Moolenaar // new block inside existing one, adjust new block 1722071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new + off; 1723071d4279SBram Moolenaar dp->df_count[idx_new] = count_new - off; 1724071d4279SBram Moolenaar } 1725071d4279SBram Moolenaar else 1726e828b762SBram Moolenaar // second overlap of new block with existing block 1727bb8f88bbSBram Moolenaar dp->df_count[idx_new] += count_new - count_orig 1728bb8f88bbSBram Moolenaar + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig] 1729bb8f88bbSBram Moolenaar - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]); 1730071d4279SBram Moolenaar 1731e828b762SBram Moolenaar // Adjust the size of the block to include all the lines to the 1732e828b762SBram Moolenaar // end of the existing block or the new diff, whatever ends last. 1733071d4279SBram Moolenaar off = (lnum_orig + count_orig) 1734071d4279SBram Moolenaar - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); 1735071d4279SBram Moolenaar if (off < 0) 1736071d4279SBram Moolenaar { 1737e828b762SBram Moolenaar // new change ends in existing block, adjust the end if not 1738e828b762SBram Moolenaar // done already 1739071d4279SBram Moolenaar if (notset) 1740071d4279SBram Moolenaar dp->df_count[idx_new] += -off; 1741071d4279SBram Moolenaar off = 0; 1742071d4279SBram Moolenaar } 1743d4b96bc6SBram Moolenaar for (i = idx_orig; i < idx_new; ++i) 174449d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1745071d4279SBram Moolenaar dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] 1746071d4279SBram Moolenaar - dp->df_lnum[i] + off; 1747071d4279SBram Moolenaar 1748e828b762SBram Moolenaar // Delete the diff blocks that have been merged into one. 1749071d4279SBram Moolenaar dn = dp->df_next; 1750071d4279SBram Moolenaar dp->df_next = dpl->df_next; 1751071d4279SBram Moolenaar while (dn != dp->df_next) 1752071d4279SBram Moolenaar { 1753071d4279SBram Moolenaar dpl = dn->df_next; 1754071d4279SBram Moolenaar vim_free(dn); 1755071d4279SBram Moolenaar dn = dpl; 1756071d4279SBram Moolenaar } 1757071d4279SBram Moolenaar } 1758071d4279SBram Moolenaar else 1759071d4279SBram Moolenaar { 1760e828b762SBram Moolenaar // Allocate a new diffblock. 176149d7bf13SBram Moolenaar dp = diff_alloc_new(curtab, dprev, dp); 1762071d4279SBram Moolenaar if (dp == NULL) 1763eb3593b3SBram Moolenaar goto done; 1764071d4279SBram Moolenaar 1765071d4279SBram Moolenaar dp->df_lnum[idx_orig] = lnum_orig; 1766071d4279SBram Moolenaar dp->df_count[idx_orig] = count_orig; 1767071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new; 1768071d4279SBram Moolenaar dp->df_count[idx_new] = count_new; 1769071d4279SBram Moolenaar 1770e828b762SBram Moolenaar // Set values for other buffers, these must be equal to the 1771e828b762SBram Moolenaar // original buffer, otherwise there would have been a change 1772e828b762SBram Moolenaar // already. 1773071d4279SBram Moolenaar for (i = idx_orig + 1; i < idx_new; ++i) 177449d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1775071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, i); 1776071d4279SBram Moolenaar } 1777e828b762SBram Moolenaar notset = FALSE; // "*dp" has been set 1778071d4279SBram Moolenaar } 1779071d4279SBram Moolenaar 1780e828b762SBram Moolenaar // for remaining diff blocks orig and new are equal 1781071d4279SBram Moolenaar while (dp != NULL) 1782071d4279SBram Moolenaar { 1783071d4279SBram Moolenaar if (notset) 1784071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, idx_new); 1785071d4279SBram Moolenaar dprev = dp; 1786071d4279SBram Moolenaar dp = dp->df_next; 1787071d4279SBram Moolenaar notset = TRUE; 1788071d4279SBram Moolenaar } 1789071d4279SBram Moolenaar 1790eb3593b3SBram Moolenaar done: 1791e828b762SBram Moolenaar if (fd != NULL) 1792071d4279SBram Moolenaar fclose(fd); 1793071d4279SBram Moolenaar } 1794071d4279SBram Moolenaar 1795071d4279SBram Moolenaar /* 1796071d4279SBram Moolenaar * Copy an entry at "dp" from "idx_orig" to "idx_new". 1797071d4279SBram Moolenaar */ 1798071d4279SBram Moolenaar static void 17997454a06eSBram Moolenaar diff_copy_entry( 18007454a06eSBram Moolenaar diff_T *dprev, 18017454a06eSBram Moolenaar diff_T *dp, 18027454a06eSBram Moolenaar int idx_orig, 18037454a06eSBram Moolenaar int idx_new) 1804071d4279SBram Moolenaar { 1805071d4279SBram Moolenaar long off; 1806071d4279SBram Moolenaar 1807071d4279SBram Moolenaar if (dprev == NULL) 1808071d4279SBram Moolenaar off = 0; 1809071d4279SBram Moolenaar else 1810071d4279SBram Moolenaar off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig]) 1811071d4279SBram Moolenaar - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]); 1812071d4279SBram Moolenaar dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off; 1813071d4279SBram Moolenaar dp->df_count[idx_new] = dp->df_count[idx_orig]; 1814071d4279SBram Moolenaar } 1815071d4279SBram Moolenaar 1816071d4279SBram Moolenaar /* 181749d7bf13SBram Moolenaar * Clear the list of diffblocks for tab page "tp". 1818071d4279SBram Moolenaar */ 1819ea408854SBram Moolenaar void 18207454a06eSBram Moolenaar diff_clear(tabpage_T *tp) 1821071d4279SBram Moolenaar { 1822071d4279SBram Moolenaar diff_T *p, *next_p; 1823071d4279SBram Moolenaar 182449d7bf13SBram Moolenaar for (p = tp->tp_first_diff; p != NULL; p = next_p) 1825071d4279SBram Moolenaar { 1826071d4279SBram Moolenaar next_p = p->df_next; 1827071d4279SBram Moolenaar vim_free(p); 1828071d4279SBram Moolenaar } 182949d7bf13SBram Moolenaar tp->tp_first_diff = NULL; 1830071d4279SBram Moolenaar } 1831071d4279SBram Moolenaar 1832071d4279SBram Moolenaar /* 1833071d4279SBram Moolenaar * Check diff status for line "lnum" in buffer "buf": 1834071d4279SBram Moolenaar * Returns 0 for nothing special 1835071d4279SBram Moolenaar * Returns -1 for a line that should be highlighted as changed. 1836071d4279SBram Moolenaar * Returns -2 for a line that should be highlighted as added/deleted. 1837071d4279SBram Moolenaar * Returns > 0 for inserting that many filler lines above it (never happens 1838071d4279SBram Moolenaar * when 'diffopt' doesn't contain "filler"). 1839071d4279SBram Moolenaar * This should only be used for windows where 'diff' is set. 1840071d4279SBram Moolenaar */ 1841071d4279SBram Moolenaar int 18427454a06eSBram Moolenaar diff_check(win_T *wp, linenr_T lnum) 1843071d4279SBram Moolenaar { 18445d18efecSBram Moolenaar int idx; // index in tp_diffbuf[] for this buffer 1845071d4279SBram Moolenaar diff_T *dp; 1846071d4279SBram Moolenaar int maxcount; 1847071d4279SBram Moolenaar int i; 1848071d4279SBram Moolenaar buf_T *buf = wp->w_buffer; 1849071d4279SBram Moolenaar int cmp; 1850071d4279SBram Moolenaar 185149d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 18525d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 1853071d4279SBram Moolenaar 18545d18efecSBram Moolenaar if (curtab->tp_first_diff == NULL || !wp->w_p_diff) // no diffs at all 1855071d4279SBram Moolenaar return 0; 1856071d4279SBram Moolenaar 18575d18efecSBram Moolenaar // safety check: "lnum" must be a buffer line 1858071d4279SBram Moolenaar if (lnum < 1 || lnum > buf->b_ml.ml_line_count + 1) 1859071d4279SBram Moolenaar return 0; 1860071d4279SBram Moolenaar 1861071d4279SBram Moolenaar idx = diff_buf_idx(buf); 1862071d4279SBram Moolenaar if (idx == DB_COUNT) 18635d18efecSBram Moolenaar return 0; // no diffs for buffer "buf" 1864071d4279SBram Moolenaar 1865071d4279SBram Moolenaar #ifdef FEAT_FOLDING 18665d18efecSBram Moolenaar // A closed fold never has filler lines. 1867071d4279SBram Moolenaar if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL)) 1868071d4279SBram Moolenaar return 0; 1869071d4279SBram Moolenaar #endif 1870071d4279SBram Moolenaar 18715d18efecSBram Moolenaar // search for a change that includes "lnum" in the list of diffblocks. 1872aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 1873071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 1874071d4279SBram Moolenaar break; 1875071d4279SBram Moolenaar if (dp == NULL || lnum < dp->df_lnum[idx]) 1876071d4279SBram Moolenaar return 0; 1877071d4279SBram Moolenaar 1878071d4279SBram Moolenaar if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) 1879071d4279SBram Moolenaar { 1880071d4279SBram Moolenaar int zero = FALSE; 1881071d4279SBram Moolenaar 18825d18efecSBram Moolenaar // Changed or inserted line. If the other buffers have a count of 18835d18efecSBram Moolenaar // zero, the lines were inserted. If the other buffers have the same 18845d18efecSBram Moolenaar // count, check if the lines are identical. 1885071d4279SBram Moolenaar cmp = FALSE; 1886071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 188749d7bf13SBram Moolenaar if (i != idx && curtab->tp_diffbuf[i] != NULL) 1888071d4279SBram Moolenaar { 1889071d4279SBram Moolenaar if (dp->df_count[i] == 0) 1890071d4279SBram Moolenaar zero = TRUE; 1891071d4279SBram Moolenaar else 1892071d4279SBram Moolenaar { 1893071d4279SBram Moolenaar if (dp->df_count[i] != dp->df_count[idx]) 18945d18efecSBram Moolenaar return -1; // nr of lines changed. 1895071d4279SBram Moolenaar cmp = TRUE; 1896071d4279SBram Moolenaar } 1897071d4279SBram Moolenaar } 1898071d4279SBram Moolenaar if (cmp) 1899071d4279SBram Moolenaar { 19005d18efecSBram Moolenaar // Compare all lines. If they are equal the lines were inserted 19015d18efecSBram Moolenaar // in some buffers, deleted in others, but not changed. 1902071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 19037c0a2f36SBram Moolenaar if (i != idx && curtab->tp_diffbuf[i] != NULL 19047c0a2f36SBram Moolenaar && dp->df_count[i] != 0) 1905071d4279SBram Moolenaar if (!diff_equal_entry(dp, idx, i)) 1906071d4279SBram Moolenaar return -1; 1907071d4279SBram Moolenaar } 19085d18efecSBram Moolenaar // If there is no buffer with zero lines then there is no difference 19095d18efecSBram Moolenaar // any longer. Happens when making a change (or undo) that removes 19105d18efecSBram Moolenaar // the difference. Can't remove the entry here, we might be halfway 19115d18efecSBram Moolenaar // updating the window. Just report the text as unchanged. Other 19125d18efecSBram Moolenaar // windows might still show the change though. 1913071d4279SBram Moolenaar if (zero == FALSE) 1914071d4279SBram Moolenaar return 0; 1915071d4279SBram Moolenaar return -2; 1916071d4279SBram Moolenaar } 1917071d4279SBram Moolenaar 19185d18efecSBram Moolenaar // If 'diffopt' doesn't contain "filler", return 0. 1919071d4279SBram Moolenaar if (!(diff_flags & DIFF_FILLER)) 1920071d4279SBram Moolenaar return 0; 1921071d4279SBram Moolenaar 19225d18efecSBram Moolenaar // Insert filler lines above the line just below the change. Will return 19235d18efecSBram Moolenaar // 0 when this buf had the max count. 1924071d4279SBram Moolenaar maxcount = 0; 1925071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 192649d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && dp->df_count[i] > maxcount) 1927071d4279SBram Moolenaar maxcount = dp->df_count[i]; 1928071d4279SBram Moolenaar return maxcount - dp->df_count[idx]; 1929071d4279SBram Moolenaar } 1930071d4279SBram Moolenaar 1931071d4279SBram Moolenaar /* 1932071d4279SBram Moolenaar * Compare two entries in diff "*dp" and return TRUE if they are equal. 1933071d4279SBram Moolenaar */ 1934071d4279SBram Moolenaar static int 19357454a06eSBram Moolenaar diff_equal_entry(diff_T *dp, int idx1, int idx2) 1936071d4279SBram Moolenaar { 1937071d4279SBram Moolenaar int i; 1938071d4279SBram Moolenaar char_u *line; 1939071d4279SBram Moolenaar int cmp; 1940071d4279SBram Moolenaar 1941071d4279SBram Moolenaar if (dp->df_count[idx1] != dp->df_count[idx2]) 1942071d4279SBram Moolenaar return FALSE; 194349d7bf13SBram Moolenaar if (diff_check_sanity(curtab, dp) == FAIL) 1944071d4279SBram Moolenaar return FALSE; 1945071d4279SBram Moolenaar for (i = 0; i < dp->df_count[idx1]; ++i) 1946071d4279SBram Moolenaar { 194749d7bf13SBram Moolenaar line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1], 1948071d4279SBram Moolenaar dp->df_lnum[idx1] + i, FALSE)); 1949071d4279SBram Moolenaar if (line == NULL) 1950071d4279SBram Moolenaar return FALSE; 195149d7bf13SBram Moolenaar cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], 1952071d4279SBram Moolenaar dp->df_lnum[idx2] + i, FALSE)); 1953071d4279SBram Moolenaar vim_free(line); 1954071d4279SBram Moolenaar if (cmp != 0) 1955071d4279SBram Moolenaar return FALSE; 1956071d4279SBram Moolenaar } 1957071d4279SBram Moolenaar return TRUE; 1958071d4279SBram Moolenaar } 1959071d4279SBram Moolenaar 1960071d4279SBram Moolenaar /* 1961ae96b8d0SBram Moolenaar * Compare the characters at "p1" and "p2". If they are equal (possibly 1962ae96b8d0SBram Moolenaar * ignoring case) return TRUE and set "len" to the number of bytes. 1963ae96b8d0SBram Moolenaar */ 1964ae96b8d0SBram Moolenaar static int 1965ae96b8d0SBram Moolenaar diff_equal_char(char_u *p1, char_u *p2, int *len) 1966ae96b8d0SBram Moolenaar { 1967ae96b8d0SBram Moolenaar int l = (*mb_ptr2len)(p1); 1968ae96b8d0SBram Moolenaar 1969ae96b8d0SBram Moolenaar if (l != (*mb_ptr2len)(p2)) 1970ae96b8d0SBram Moolenaar return FALSE; 1971ae96b8d0SBram Moolenaar if (l > 1) 1972ae96b8d0SBram Moolenaar { 1973ae96b8d0SBram Moolenaar if (STRNCMP(p1, p2, l) != 0 1974ae96b8d0SBram Moolenaar && (!enc_utf8 1975ae96b8d0SBram Moolenaar || !(diff_flags & DIFF_ICASE) 1976ae96b8d0SBram Moolenaar || utf_fold(utf_ptr2char(p1)) 1977ae96b8d0SBram Moolenaar != utf_fold(utf_ptr2char(p2)))) 1978ae96b8d0SBram Moolenaar return FALSE; 1979ae96b8d0SBram Moolenaar *len = l; 1980ae96b8d0SBram Moolenaar } 1981ae96b8d0SBram Moolenaar else 1982ae96b8d0SBram Moolenaar { 1983ae96b8d0SBram Moolenaar if ((*p1 != *p2) 1984ae96b8d0SBram Moolenaar && (!(diff_flags & DIFF_ICASE) 1985ae96b8d0SBram Moolenaar || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) 1986ae96b8d0SBram Moolenaar return FALSE; 1987ae96b8d0SBram Moolenaar *len = 1; 1988ae96b8d0SBram Moolenaar } 1989ae96b8d0SBram Moolenaar return TRUE; 1990ae96b8d0SBram Moolenaar } 1991ae96b8d0SBram Moolenaar 1992ae96b8d0SBram Moolenaar /* 1993071d4279SBram Moolenaar * Compare strings "s1" and "s2" according to 'diffopt'. 1994071d4279SBram Moolenaar * Return non-zero when they are different. 1995071d4279SBram Moolenaar */ 1996071d4279SBram Moolenaar static int 19977454a06eSBram Moolenaar diff_cmp(char_u *s1, char_u *s2) 1998071d4279SBram Moolenaar { 1999071d4279SBram Moolenaar char_u *p1, *p2; 2000071d4279SBram Moolenaar int l; 2001071d4279SBram Moolenaar 2002785fc656SBram Moolenaar if ((diff_flags & DIFF_IBLANK) 2003785fc656SBram Moolenaar && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) 2004785fc656SBram Moolenaar return 0; 2005785fc656SBram Moolenaar 2006785fc656SBram Moolenaar if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) 2007071d4279SBram Moolenaar return STRCMP(s1, s2); 2008785fc656SBram Moolenaar if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) 2009071d4279SBram Moolenaar return MB_STRICMP(s1, s2); 2010071d4279SBram Moolenaar 2011071d4279SBram Moolenaar p1 = s1; 2012071d4279SBram Moolenaar p2 = s2; 2013785fc656SBram Moolenaar 2014785fc656SBram Moolenaar // Ignore white space changes and possibly ignore case. 2015071d4279SBram Moolenaar while (*p1 != NUL && *p2 != NUL) 2016071d4279SBram Moolenaar { 2017785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 2018785fc656SBram Moolenaar && VIM_ISWHITE(*p1) && VIM_ISWHITE(*p2)) 2019785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2020785fc656SBram Moolenaar && (VIM_ISWHITE(*p1) || VIM_ISWHITE(*p2)))) 2021071d4279SBram Moolenaar { 2022071d4279SBram Moolenaar p1 = skipwhite(p1); 2023071d4279SBram Moolenaar p2 = skipwhite(p2); 2024071d4279SBram Moolenaar } 2025071d4279SBram Moolenaar else 2026071d4279SBram Moolenaar { 2027ae96b8d0SBram Moolenaar if (!diff_equal_char(p1, p2, &l)) 2028071d4279SBram Moolenaar break; 2029071d4279SBram Moolenaar p1 += l; 2030071d4279SBram Moolenaar p2 += l; 2031071d4279SBram Moolenaar } 2032071d4279SBram Moolenaar } 2033071d4279SBram Moolenaar 2034785fc656SBram Moolenaar // Ignore trailing white space. 2035071d4279SBram Moolenaar p1 = skipwhite(p1); 2036071d4279SBram Moolenaar p2 = skipwhite(p2); 2037071d4279SBram Moolenaar if (*p1 != NUL || *p2 != NUL) 2038071d4279SBram Moolenaar return 1; 2039071d4279SBram Moolenaar return 0; 2040071d4279SBram Moolenaar } 2041071d4279SBram Moolenaar 2042071d4279SBram Moolenaar /* 2043071d4279SBram Moolenaar * Return the number of filler lines above "lnum". 2044071d4279SBram Moolenaar */ 2045071d4279SBram Moolenaar int 20467454a06eSBram Moolenaar diff_check_fill(win_T *wp, linenr_T lnum) 2047071d4279SBram Moolenaar { 2048071d4279SBram Moolenaar int n; 2049071d4279SBram Moolenaar 20505d18efecSBram Moolenaar // be quick when there are no filler lines 2051071d4279SBram Moolenaar if (!(diff_flags & DIFF_FILLER)) 2052071d4279SBram Moolenaar return 0; 2053071d4279SBram Moolenaar n = diff_check(wp, lnum); 2054071d4279SBram Moolenaar if (n <= 0) 2055071d4279SBram Moolenaar return 0; 2056071d4279SBram Moolenaar return n; 2057071d4279SBram Moolenaar } 2058071d4279SBram Moolenaar 2059071d4279SBram Moolenaar /* 2060071d4279SBram Moolenaar * Set the topline of "towin" to match the position in "fromwin", so that they 2061071d4279SBram Moolenaar * show the same diff'ed lines. 2062071d4279SBram Moolenaar */ 2063071d4279SBram Moolenaar void 20647454a06eSBram Moolenaar diff_set_topline(win_T *fromwin, win_T *towin) 2065071d4279SBram Moolenaar { 2066bb8f88bbSBram Moolenaar buf_T *frombuf = fromwin->w_buffer; 2067071d4279SBram Moolenaar linenr_T lnum = fromwin->w_topline; 2068bb8f88bbSBram Moolenaar int fromidx; 2069bb8f88bbSBram Moolenaar int toidx; 2070071d4279SBram Moolenaar diff_T *dp; 2071bb8f88bbSBram Moolenaar int max_count; 2072071d4279SBram Moolenaar int i; 2073071d4279SBram Moolenaar 2074bb8f88bbSBram Moolenaar fromidx = diff_buf_idx(frombuf); 2075bb8f88bbSBram Moolenaar if (fromidx == DB_COUNT) 20765d18efecSBram Moolenaar return; // safety check 2077071d4279SBram Moolenaar 207849d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 20795d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 2080071d4279SBram Moolenaar 2081071d4279SBram Moolenaar towin->w_topfill = 0; 2082071d4279SBram Moolenaar 20835d18efecSBram Moolenaar // search for a change that includes "lnum" in the list of diffblocks. 2084aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 2085bb8f88bbSBram Moolenaar if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) 2086071d4279SBram Moolenaar break; 2087071d4279SBram Moolenaar if (dp == NULL) 2088071d4279SBram Moolenaar { 20895d18efecSBram Moolenaar // After last change, compute topline relative to end of file; no 20905d18efecSBram Moolenaar // filler lines. 2091071d4279SBram Moolenaar towin->w_topline = towin->w_buffer->b_ml.ml_line_count 2092bb8f88bbSBram Moolenaar - (frombuf->b_ml.ml_line_count - lnum); 2093071d4279SBram Moolenaar } 2094071d4279SBram Moolenaar else 2095071d4279SBram Moolenaar { 20965d18efecSBram Moolenaar // Find index for "towin". 2097bb8f88bbSBram Moolenaar toidx = diff_buf_idx(towin->w_buffer); 2098bb8f88bbSBram Moolenaar if (toidx == DB_COUNT) 20995d18efecSBram Moolenaar return; // safety check 2100071d4279SBram Moolenaar 2101bb8f88bbSBram Moolenaar towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]); 2102bb8f88bbSBram Moolenaar if (lnum >= dp->df_lnum[fromidx]) 2103071d4279SBram Moolenaar { 21045d18efecSBram Moolenaar // Inside a change: compute filler lines. With three or more 21055d18efecSBram Moolenaar // buffers we need to know the largest count. 2106bb8f88bbSBram Moolenaar max_count = 0; 2107bb8f88bbSBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2108bb8f88bbSBram Moolenaar if (curtab->tp_diffbuf[i] != NULL 2109bb8f88bbSBram Moolenaar && max_count < dp->df_count[i]) 2110bb8f88bbSBram Moolenaar max_count = dp->df_count[i]; 2111bb8f88bbSBram Moolenaar 2112bb8f88bbSBram Moolenaar if (dp->df_count[toidx] == dp->df_count[fromidx]) 2113bb8f88bbSBram Moolenaar { 21145d18efecSBram Moolenaar // same number of lines: use same filler count 2115071d4279SBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2116bb8f88bbSBram Moolenaar } 2117bb8f88bbSBram Moolenaar else if (dp->df_count[toidx] > dp->df_count[fromidx]) 2118071d4279SBram Moolenaar { 2119bb8f88bbSBram Moolenaar if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) 2120bb8f88bbSBram Moolenaar { 21215d18efecSBram Moolenaar // more lines in towin and fromwin doesn't show diff 21225d18efecSBram Moolenaar // lines, only filler lines 2123bb8f88bbSBram Moolenaar if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) 2124bb8f88bbSBram Moolenaar { 21255d18efecSBram Moolenaar // towin also only shows filler lines 2126bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] 2127bb8f88bbSBram Moolenaar + dp->df_count[toidx]; 2128bb8f88bbSBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2129071d4279SBram Moolenaar } 2130071d4279SBram Moolenaar else 21315d18efecSBram Moolenaar // towin still has some diff lines to show 2132bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] 2133bb8f88bbSBram Moolenaar + max_count - fromwin->w_topfill; 2134bb8f88bbSBram Moolenaar } 2135bb8f88bbSBram Moolenaar } 2136bb8f88bbSBram Moolenaar else if (towin->w_topline >= dp->df_lnum[toidx] 2137bb8f88bbSBram Moolenaar + dp->df_count[toidx]) 2138071d4279SBram Moolenaar { 21395d18efecSBram Moolenaar // less lines in towin and no diff lines to show: compute 21405d18efecSBram Moolenaar // filler lines 2141bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx]; 2142071d4279SBram Moolenaar if (diff_flags & DIFF_FILLER) 2143bb8f88bbSBram Moolenaar { 2144bb8f88bbSBram Moolenaar if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) 21455d18efecSBram Moolenaar // fromwin is also out of diff lines 2146bb8f88bbSBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2147bb8f88bbSBram Moolenaar else 21485d18efecSBram Moolenaar // fromwin has some diff lines 2149bb8f88bbSBram Moolenaar towin->w_topfill = dp->df_lnum[fromidx] 2150bb8f88bbSBram Moolenaar + max_count - lnum; 2151071d4279SBram Moolenaar } 2152071d4279SBram Moolenaar } 2153071d4279SBram Moolenaar } 2154071d4279SBram Moolenaar } 2155071d4279SBram Moolenaar 21565d18efecSBram Moolenaar // safety check (if diff info gets outdated strange things may happen) 2157071d4279SBram Moolenaar towin->w_botfill = FALSE; 2158071d4279SBram Moolenaar if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) 2159071d4279SBram Moolenaar { 2160071d4279SBram Moolenaar towin->w_topline = towin->w_buffer->b_ml.ml_line_count; 2161071d4279SBram Moolenaar towin->w_botfill = TRUE; 2162071d4279SBram Moolenaar } 2163071d4279SBram Moolenaar if (towin->w_topline < 1) 2164071d4279SBram Moolenaar { 2165071d4279SBram Moolenaar towin->w_topline = 1; 2166071d4279SBram Moolenaar towin->w_topfill = 0; 2167071d4279SBram Moolenaar } 2168071d4279SBram Moolenaar 21695d18efecSBram Moolenaar // When w_topline changes need to recompute w_botline and cursor position 2170071d4279SBram Moolenaar invalidate_botline_win(towin); 2171071d4279SBram Moolenaar changed_line_abv_curs_win(towin); 2172071d4279SBram Moolenaar 2173071d4279SBram Moolenaar check_topfill(towin, FALSE); 2174071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2175071d4279SBram Moolenaar (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, 2176071d4279SBram Moolenaar NULL, TRUE, NULL); 2177071d4279SBram Moolenaar #endif 2178071d4279SBram Moolenaar } 2179071d4279SBram Moolenaar 2180071d4279SBram Moolenaar /* 2181071d4279SBram Moolenaar * This is called when 'diffopt' is changed. 2182071d4279SBram Moolenaar */ 2183071d4279SBram Moolenaar int 21847454a06eSBram Moolenaar diffopt_changed(void) 2185071d4279SBram Moolenaar { 2186071d4279SBram Moolenaar char_u *p; 2187071d4279SBram Moolenaar int diff_context_new = 6; 2188071d4279SBram Moolenaar int diff_flags_new = 0; 2189c4675a19SBram Moolenaar int diff_foldcolumn_new = 2; 2190e828b762SBram Moolenaar long diff_algorithm_new = 0; 2191b6fc7285SBram Moolenaar long diff_indent_heuristic = 0; 219249d7bf13SBram Moolenaar tabpage_T *tp; 2193071d4279SBram Moolenaar 2194071d4279SBram Moolenaar p = p_dip; 2195071d4279SBram Moolenaar while (*p != NUL) 2196071d4279SBram Moolenaar { 2197071d4279SBram Moolenaar if (STRNCMP(p, "filler", 6) == 0) 2198071d4279SBram Moolenaar { 2199071d4279SBram Moolenaar p += 6; 2200071d4279SBram Moolenaar diff_flags_new |= DIFF_FILLER; 2201071d4279SBram Moolenaar } 2202071d4279SBram Moolenaar else if (STRNCMP(p, "context:", 8) == 0 && VIM_ISDIGIT(p[8])) 2203071d4279SBram Moolenaar { 2204071d4279SBram Moolenaar p += 8; 2205071d4279SBram Moolenaar diff_context_new = getdigits(&p); 2206071d4279SBram Moolenaar } 2207785fc656SBram Moolenaar else if (STRNCMP(p, "iblank", 6) == 0) 2208785fc656SBram Moolenaar { 2209785fc656SBram Moolenaar p += 6; 2210785fc656SBram Moolenaar diff_flags_new |= DIFF_IBLANK; 2211785fc656SBram Moolenaar } 2212071d4279SBram Moolenaar else if (STRNCMP(p, "icase", 5) == 0) 2213071d4279SBram Moolenaar { 2214071d4279SBram Moolenaar p += 5; 2215071d4279SBram Moolenaar diff_flags_new |= DIFF_ICASE; 2216071d4279SBram Moolenaar } 2217785fc656SBram Moolenaar else if (STRNCMP(p, "iwhiteall", 9) == 0) 2218785fc656SBram Moolenaar { 2219785fc656SBram Moolenaar p += 9; 2220785fc656SBram Moolenaar diff_flags_new |= DIFF_IWHITEALL; 2221785fc656SBram Moolenaar } 2222785fc656SBram Moolenaar else if (STRNCMP(p, "iwhiteeol", 9) == 0) 2223785fc656SBram Moolenaar { 2224785fc656SBram Moolenaar p += 9; 2225785fc656SBram Moolenaar diff_flags_new |= DIFF_IWHITEEOL; 2226785fc656SBram Moolenaar } 2227071d4279SBram Moolenaar else if (STRNCMP(p, "iwhite", 6) == 0) 2228071d4279SBram Moolenaar { 2229071d4279SBram Moolenaar p += 6; 2230071d4279SBram Moolenaar diff_flags_new |= DIFF_IWHITE; 2231071d4279SBram Moolenaar } 2232c4675a19SBram Moolenaar else if (STRNCMP(p, "horizontal", 10) == 0) 2233c4675a19SBram Moolenaar { 2234c4675a19SBram Moolenaar p += 10; 2235c4675a19SBram Moolenaar diff_flags_new |= DIFF_HORIZONTAL; 2236c4675a19SBram Moolenaar } 2237c4675a19SBram Moolenaar else if (STRNCMP(p, "vertical", 8) == 0) 2238c4675a19SBram Moolenaar { 2239c4675a19SBram Moolenaar p += 8; 2240c4675a19SBram Moolenaar diff_flags_new |= DIFF_VERTICAL; 2241c4675a19SBram Moolenaar } 2242c4675a19SBram Moolenaar else if (STRNCMP(p, "foldcolumn:", 11) == 0 && VIM_ISDIGIT(p[11])) 2243c4675a19SBram Moolenaar { 2244c4675a19SBram Moolenaar p += 11; 2245c4675a19SBram Moolenaar diff_foldcolumn_new = getdigits(&p); 2246c4675a19SBram Moolenaar } 224797ce4192SBram Moolenaar else if (STRNCMP(p, "hiddenoff", 9) == 0) 224897ce4192SBram Moolenaar { 224997ce4192SBram Moolenaar p += 9; 225097ce4192SBram Moolenaar diff_flags_new |= DIFF_HIDDEN_OFF; 225197ce4192SBram Moolenaar } 2252c8234779SBram Moolenaar else if (STRNCMP(p, "closeoff", 8) == 0) 2253c8234779SBram Moolenaar { 2254c8234779SBram Moolenaar p += 8; 2255c8234779SBram Moolenaar diff_flags_new |= DIFF_CLOSE_OFF; 2256c8234779SBram Moolenaar } 22574223d43cSBram Moolenaar else if (STRNCMP(p, "followwrap", 10) == 0) 22584223d43cSBram Moolenaar { 22594223d43cSBram Moolenaar p += 10; 22604223d43cSBram Moolenaar diff_flags_new |= DIFF_FOLLOWWRAP; 22614223d43cSBram Moolenaar } 2262e828b762SBram Moolenaar else if (STRNCMP(p, "indent-heuristic", 16) == 0) 2263e828b762SBram Moolenaar { 2264e828b762SBram Moolenaar p += 16; 2265b6fc7285SBram Moolenaar diff_indent_heuristic = XDF_INDENT_HEURISTIC; 2266e828b762SBram Moolenaar } 2267e828b762SBram Moolenaar else if (STRNCMP(p, "internal", 8) == 0) 2268e828b762SBram Moolenaar { 2269e828b762SBram Moolenaar p += 8; 2270e828b762SBram Moolenaar diff_flags_new |= DIFF_INTERNAL; 2271e828b762SBram Moolenaar } 2272e828b762SBram Moolenaar else if (STRNCMP(p, "algorithm:", 10) == 0) 2273e828b762SBram Moolenaar { 2274e828b762SBram Moolenaar p += 10; 2275e828b762SBram Moolenaar if (STRNCMP(p, "myers", 5) == 0) 2276e828b762SBram Moolenaar { 2277e828b762SBram Moolenaar p += 5; 2278e828b762SBram Moolenaar diff_algorithm_new = 0; 2279e828b762SBram Moolenaar } 2280e828b762SBram Moolenaar else if (STRNCMP(p, "minimal", 7) == 0) 2281e828b762SBram Moolenaar { 2282e828b762SBram Moolenaar p += 7; 2283e828b762SBram Moolenaar diff_algorithm_new = XDF_NEED_MINIMAL; 2284e828b762SBram Moolenaar } 2285e828b762SBram Moolenaar else if (STRNCMP(p, "patience", 8) == 0) 2286e828b762SBram Moolenaar { 2287e828b762SBram Moolenaar p += 8; 2288e828b762SBram Moolenaar diff_algorithm_new = XDF_PATIENCE_DIFF; 2289e828b762SBram Moolenaar } 2290e828b762SBram Moolenaar else if (STRNCMP(p, "histogram", 9) == 0) 2291e828b762SBram Moolenaar { 2292e828b762SBram Moolenaar p += 9; 2293e828b762SBram Moolenaar diff_algorithm_new = XDF_HISTOGRAM_DIFF; 2294e828b762SBram Moolenaar } 2295d0721058SBram Moolenaar else 2296d0721058SBram Moolenaar return FAIL; 2297e828b762SBram Moolenaar } 2298e828b762SBram Moolenaar 2299071d4279SBram Moolenaar if (*p != ',' && *p != NUL) 2300071d4279SBram Moolenaar return FAIL; 2301071d4279SBram Moolenaar if (*p == ',') 2302071d4279SBram Moolenaar ++p; 2303071d4279SBram Moolenaar } 2304071d4279SBram Moolenaar 2305b6fc7285SBram Moolenaar diff_algorithm_new |= diff_indent_heuristic; 2306b6fc7285SBram Moolenaar 23075d18efecSBram Moolenaar // Can't have both "horizontal" and "vertical". 2308c4675a19SBram Moolenaar if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) 2309c4675a19SBram Moolenaar return FAIL; 2310c4675a19SBram Moolenaar 2311198fa066SBram Moolenaar // If flags were added or removed, or the algorithm was changed, need to 2312198fa066SBram Moolenaar // update the diff. 2313e828b762SBram Moolenaar if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) 231429323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 231549d7bf13SBram Moolenaar tp->tp_diff_invalid = TRUE; 2316071d4279SBram Moolenaar 2317071d4279SBram Moolenaar diff_flags = diff_flags_new; 2318b9ddda6cSBram Moolenaar diff_context = diff_context_new == 0 ? 1 : diff_context_new; 2319c4675a19SBram Moolenaar diff_foldcolumn = diff_foldcolumn_new; 2320e828b762SBram Moolenaar diff_algorithm = diff_algorithm_new; 2321071d4279SBram Moolenaar 2322071d4279SBram Moolenaar diff_redraw(TRUE); 2323071d4279SBram Moolenaar 23245d18efecSBram Moolenaar // recompute the scroll binding with the new option value, may 23255d18efecSBram Moolenaar // remove or add filler lines 2326071d4279SBram Moolenaar check_scrollbind((linenr_T)0, 0L); 2327071d4279SBram Moolenaar 2328071d4279SBram Moolenaar return OK; 2329071d4279SBram Moolenaar } 2330071d4279SBram Moolenaar 2331071d4279SBram Moolenaar /* 2332c4675a19SBram Moolenaar * Return TRUE if 'diffopt' contains "horizontal". 2333c4675a19SBram Moolenaar */ 2334c4675a19SBram Moolenaar int 23357454a06eSBram Moolenaar diffopt_horizontal(void) 2336c4675a19SBram Moolenaar { 2337c4675a19SBram Moolenaar return (diff_flags & DIFF_HORIZONTAL) != 0; 2338c4675a19SBram Moolenaar } 2339c4675a19SBram Moolenaar 2340c4675a19SBram Moolenaar /* 234197ce4192SBram Moolenaar * Return TRUE if 'diffopt' contains "hiddenoff". 234297ce4192SBram Moolenaar */ 234397ce4192SBram Moolenaar int 234497ce4192SBram Moolenaar diffopt_hiddenoff(void) 234597ce4192SBram Moolenaar { 234697ce4192SBram Moolenaar return (diff_flags & DIFF_HIDDEN_OFF) != 0; 234797ce4192SBram Moolenaar } 234897ce4192SBram Moolenaar 234997ce4192SBram Moolenaar /* 2350c8234779SBram Moolenaar * Return TRUE if 'diffopt' contains "closeoff". 2351c8234779SBram Moolenaar */ 2352c8234779SBram Moolenaar int 2353c8234779SBram Moolenaar diffopt_closeoff(void) 2354c8234779SBram Moolenaar { 2355c8234779SBram Moolenaar return (diff_flags & DIFF_CLOSE_OFF) != 0; 2356c8234779SBram Moolenaar } 2357c8234779SBram Moolenaar 2358c8234779SBram Moolenaar /* 2359071d4279SBram Moolenaar * Find the difference within a changed line. 2360071d4279SBram Moolenaar * Returns TRUE if the line was added, no other buffer has it. 2361071d4279SBram Moolenaar */ 2362071d4279SBram Moolenaar int 23637454a06eSBram Moolenaar diff_find_change( 23647454a06eSBram Moolenaar win_T *wp, 23657454a06eSBram Moolenaar linenr_T lnum, 23665d18efecSBram Moolenaar int *startp, // first char of the change 23675d18efecSBram Moolenaar int *endp) // last char of the change 2368071d4279SBram Moolenaar { 2369071d4279SBram Moolenaar char_u *line_org; 2370071d4279SBram Moolenaar char_u *line_new; 2371071d4279SBram Moolenaar int i; 23729e54a0e7SBram Moolenaar int si_org, si_new; 23739e54a0e7SBram Moolenaar int ei_org, ei_new; 2374071d4279SBram Moolenaar diff_T *dp; 2375071d4279SBram Moolenaar int idx; 2376071d4279SBram Moolenaar int off; 2377071d4279SBram Moolenaar int added = TRUE; 2378da22b8ccSBram Moolenaar char_u *p1, *p2; 2379da22b8ccSBram Moolenaar int l; 2380071d4279SBram Moolenaar 23815d18efecSBram Moolenaar // Make a copy of the line, the next ml_get() will invalidate it. 2382071d4279SBram Moolenaar line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); 2383071d4279SBram Moolenaar if (line_org == NULL) 2384071d4279SBram Moolenaar return FALSE; 2385071d4279SBram Moolenaar 2386071d4279SBram Moolenaar idx = diff_buf_idx(wp->w_buffer); 23875d18efecSBram Moolenaar if (idx == DB_COUNT) // cannot happen 2388fa3491a0SBram Moolenaar { 2389fa3491a0SBram Moolenaar vim_free(line_org); 2390071d4279SBram Moolenaar return FALSE; 2391fa3491a0SBram Moolenaar } 2392071d4279SBram Moolenaar 23935d18efecSBram Moolenaar // search for a change that includes "lnum" in the list of diffblocks. 2394aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 2395071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 2396071d4279SBram Moolenaar break; 239749d7bf13SBram Moolenaar if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL) 2398fa3491a0SBram Moolenaar { 2399fa3491a0SBram Moolenaar vim_free(line_org); 2400071d4279SBram Moolenaar return FALSE; 2401fa3491a0SBram Moolenaar } 2402071d4279SBram Moolenaar 2403071d4279SBram Moolenaar off = lnum - dp->df_lnum[idx]; 2404071d4279SBram Moolenaar 2405071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 240649d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && i != idx) 2407071d4279SBram Moolenaar { 24085d18efecSBram Moolenaar // Skip lines that are not in the other change (filler lines). 2409071d4279SBram Moolenaar if (off >= dp->df_count[i]) 2410071d4279SBram Moolenaar continue; 2411071d4279SBram Moolenaar added = FALSE; 24129e54a0e7SBram Moolenaar line_new = ml_get_buf(curtab->tp_diffbuf[i], 24139e54a0e7SBram Moolenaar dp->df_lnum[i] + off, FALSE); 2414071d4279SBram Moolenaar 24155d18efecSBram Moolenaar // Search for start of difference 24169e54a0e7SBram Moolenaar si_org = si_new = 0; 24179e54a0e7SBram Moolenaar while (line_org[si_org] != NUL) 24189e54a0e7SBram Moolenaar { 2419785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 24201c465444SBram Moolenaar && VIM_ISWHITE(line_org[si_org]) 24211c465444SBram Moolenaar && VIM_ISWHITE(line_new[si_new])) 2422785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2423785fc656SBram Moolenaar && (VIM_ISWHITE(line_org[si_org]) 2424785fc656SBram Moolenaar || VIM_ISWHITE(line_new[si_new])))) 24259e54a0e7SBram Moolenaar { 2426a93fa7eeSBram Moolenaar si_org = (int)(skipwhite(line_org + si_org) - line_org); 2427a93fa7eeSBram Moolenaar si_new = (int)(skipwhite(line_new + si_new) - line_new); 24289e54a0e7SBram Moolenaar } 24299e54a0e7SBram Moolenaar else 24309e54a0e7SBram Moolenaar { 2431da22b8ccSBram Moolenaar if (!diff_equal_char(line_org + si_org, line_new + si_new, 2432da22b8ccSBram Moolenaar &l)) 24339e54a0e7SBram Moolenaar break; 2434da22b8ccSBram Moolenaar si_org += l; 2435da22b8ccSBram Moolenaar si_new += l; 24369e54a0e7SBram Moolenaar } 24379e54a0e7SBram Moolenaar } 2438071d4279SBram Moolenaar if (has_mbyte) 2439071d4279SBram Moolenaar { 24405d18efecSBram Moolenaar // Move back to first byte of character in both lines (may 24415d18efecSBram Moolenaar // have "nn^" in line_org and "n^ in line_new). 24429e54a0e7SBram Moolenaar si_org -= (*mb_head_off)(line_org, line_org + si_org); 24439e54a0e7SBram Moolenaar si_new -= (*mb_head_off)(line_new, line_new + si_new); 2444071d4279SBram Moolenaar } 24459e54a0e7SBram Moolenaar if (*startp > si_org) 24469e54a0e7SBram Moolenaar *startp = si_org; 2447071d4279SBram Moolenaar 24485d18efecSBram Moolenaar // Search for end of difference, if any. 24499e54a0e7SBram Moolenaar if (line_org[si_org] != NUL || line_new[si_new] != NUL) 2450071d4279SBram Moolenaar { 2451071d4279SBram Moolenaar ei_org = (int)STRLEN(line_org); 2452071d4279SBram Moolenaar ei_new = (int)STRLEN(line_new); 24539e54a0e7SBram Moolenaar while (ei_org >= *startp && ei_new >= si_new 24549e54a0e7SBram Moolenaar && ei_org >= 0 && ei_new >= 0) 2455071d4279SBram Moolenaar { 2456785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 24571c465444SBram Moolenaar && VIM_ISWHITE(line_org[ei_org]) 24581c465444SBram Moolenaar && VIM_ISWHITE(line_new[ei_new])) 2459785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2460785fc656SBram Moolenaar && (VIM_ISWHITE(line_org[ei_org]) 2461785fc656SBram Moolenaar || VIM_ISWHITE(line_new[ei_new])))) 24629e54a0e7SBram Moolenaar { 24639e54a0e7SBram Moolenaar while (ei_org >= *startp 24641c465444SBram Moolenaar && VIM_ISWHITE(line_org[ei_org])) 24659e54a0e7SBram Moolenaar --ei_org; 24669e54a0e7SBram Moolenaar while (ei_new >= si_new 24671c465444SBram Moolenaar && VIM_ISWHITE(line_new[ei_new])) 24689e54a0e7SBram Moolenaar --ei_new; 24699e54a0e7SBram Moolenaar } 24709e54a0e7SBram Moolenaar else 24719e54a0e7SBram Moolenaar { 2472da22b8ccSBram Moolenaar p1 = line_org + ei_org; 2473da22b8ccSBram Moolenaar p2 = line_new + ei_new; 2474da22b8ccSBram Moolenaar p1 -= (*mb_head_off)(line_org, p1); 2475da22b8ccSBram Moolenaar p2 -= (*mb_head_off)(line_new, p2); 2476da22b8ccSBram Moolenaar if (!diff_equal_char(p1, p2, &l)) 24779e54a0e7SBram Moolenaar break; 2478da22b8ccSBram Moolenaar ei_org -= l; 2479da22b8ccSBram Moolenaar ei_new -= l; 2480071d4279SBram Moolenaar } 24819e54a0e7SBram Moolenaar } 2482071d4279SBram Moolenaar if (*endp < ei_org) 2483071d4279SBram Moolenaar *endp = ei_org; 2484071d4279SBram Moolenaar } 2485071d4279SBram Moolenaar } 2486071d4279SBram Moolenaar 2487071d4279SBram Moolenaar vim_free(line_org); 2488071d4279SBram Moolenaar return added; 2489071d4279SBram Moolenaar } 2490071d4279SBram Moolenaar 2491071d4279SBram Moolenaar #if defined(FEAT_FOLDING) || defined(PROTO) 2492071d4279SBram Moolenaar /* 2493071d4279SBram Moolenaar * Return TRUE if line "lnum" is not close to a diff block, this line should 2494071d4279SBram Moolenaar * be in a fold. 2495071d4279SBram Moolenaar * Return FALSE if there are no diff blocks at all in this window. 2496071d4279SBram Moolenaar */ 2497071d4279SBram Moolenaar int 24987454a06eSBram Moolenaar diff_infold(win_T *wp, linenr_T lnum) 2499071d4279SBram Moolenaar { 2500071d4279SBram Moolenaar int i; 2501071d4279SBram Moolenaar int idx = -1; 2502071d4279SBram Moolenaar int other = FALSE; 2503071d4279SBram Moolenaar diff_T *dp; 2504071d4279SBram Moolenaar 25055d18efecSBram Moolenaar // Return if 'diff' isn't set. 2506071d4279SBram Moolenaar if (!wp->w_p_diff) 2507071d4279SBram Moolenaar return FALSE; 2508071d4279SBram Moolenaar 2509071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2510071d4279SBram Moolenaar { 251149d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] == wp->w_buffer) 2512071d4279SBram Moolenaar idx = i; 251349d7bf13SBram Moolenaar else if (curtab->tp_diffbuf[i] != NULL) 2514071d4279SBram Moolenaar other = TRUE; 2515071d4279SBram Moolenaar } 2516071d4279SBram Moolenaar 25175d18efecSBram Moolenaar // return here if there are no diffs in the window 2518071d4279SBram Moolenaar if (idx == -1 || !other) 2519071d4279SBram Moolenaar return FALSE; 2520071d4279SBram Moolenaar 252149d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 25225d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 2523071d4279SBram Moolenaar 25245d18efecSBram Moolenaar // Return if there are no diff blocks. All lines will be folded. 252549d7bf13SBram Moolenaar if (curtab->tp_first_diff == NULL) 2526071d4279SBram Moolenaar return TRUE; 2527071d4279SBram Moolenaar 2528aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 2529071d4279SBram Moolenaar { 25305d18efecSBram Moolenaar // If this change is below the line there can't be any further match. 2531071d4279SBram Moolenaar if (dp->df_lnum[idx] - diff_context > lnum) 2532071d4279SBram Moolenaar break; 25335d18efecSBram Moolenaar // If this change ends before the line we have a match. 2534071d4279SBram Moolenaar if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) 2535071d4279SBram Moolenaar return FALSE; 2536071d4279SBram Moolenaar } 2537071d4279SBram Moolenaar return TRUE; 2538071d4279SBram Moolenaar } 2539071d4279SBram Moolenaar #endif 2540071d4279SBram Moolenaar 2541071d4279SBram Moolenaar /* 2542071d4279SBram Moolenaar * "dp" and "do" commands. 2543071d4279SBram Moolenaar */ 2544071d4279SBram Moolenaar void 25457454a06eSBram Moolenaar nv_diffgetput(int put, long count) 2546071d4279SBram Moolenaar { 2547071d4279SBram Moolenaar exarg_T ea; 25486a64365cSBram Moolenaar char_u buf[30]; 2549071d4279SBram Moolenaar 2550f273245fSBram Moolenaar #ifdef FEAT_JOB_CHANNEL 2551f273245fSBram Moolenaar if (bt_prompt(curbuf)) 2552f273245fSBram Moolenaar { 2553f273245fSBram Moolenaar vim_beep(BO_OPER); 2554f273245fSBram Moolenaar return; 2555f273245fSBram Moolenaar } 2556f273245fSBram Moolenaar #endif 25576a64365cSBram Moolenaar if (count == 0) 2558071d4279SBram Moolenaar ea.arg = (char_u *)""; 25596a64365cSBram Moolenaar else 25606a64365cSBram Moolenaar { 25616a64365cSBram Moolenaar vim_snprintf((char *)buf, 30, "%ld", count); 25626a64365cSBram Moolenaar ea.arg = buf; 25636a64365cSBram Moolenaar } 2564071d4279SBram Moolenaar if (put) 2565071d4279SBram Moolenaar ea.cmdidx = CMD_diffput; 2566071d4279SBram Moolenaar else 2567071d4279SBram Moolenaar ea.cmdidx = CMD_diffget; 2568071d4279SBram Moolenaar ea.addr_count = 0; 2569071d4279SBram Moolenaar ea.line1 = curwin->w_cursor.lnum; 2570071d4279SBram Moolenaar ea.line2 = curwin->w_cursor.lnum; 2571071d4279SBram Moolenaar ex_diffgetput(&ea); 2572071d4279SBram Moolenaar } 2573071d4279SBram Moolenaar 2574071d4279SBram Moolenaar /* 2575071d4279SBram Moolenaar * ":diffget" 2576071d4279SBram Moolenaar * ":diffput" 2577071d4279SBram Moolenaar */ 2578071d4279SBram Moolenaar void 25797454a06eSBram Moolenaar ex_diffgetput(exarg_T *eap) 2580071d4279SBram Moolenaar { 2581071d4279SBram Moolenaar linenr_T lnum; 2582071d4279SBram Moolenaar int count; 2583071d4279SBram Moolenaar linenr_T off = 0; 2584071d4279SBram Moolenaar diff_T *dp; 2585071d4279SBram Moolenaar diff_T *dprev; 2586071d4279SBram Moolenaar diff_T *dfree; 2587071d4279SBram Moolenaar int idx_cur; 2588071d4279SBram Moolenaar int idx_other; 2589071d4279SBram Moolenaar int idx_from; 2590071d4279SBram Moolenaar int idx_to; 2591071d4279SBram Moolenaar int i; 2592071d4279SBram Moolenaar int added; 2593071d4279SBram Moolenaar char_u *p; 2594071d4279SBram Moolenaar aco_save_T aco; 2595071d4279SBram Moolenaar buf_T *buf; 2596071d4279SBram Moolenaar int start_skip, end_skip; 2597071d4279SBram Moolenaar int new_count; 2598280f126eSBram Moolenaar int buf_empty; 2599602eb74dSBram Moolenaar int found_not_ma = FALSE; 2600071d4279SBram Moolenaar 26015d18efecSBram Moolenaar // Find the current buffer in the list of diff buffers. 2602071d4279SBram Moolenaar idx_cur = diff_buf_idx(curbuf); 2603071d4279SBram Moolenaar if (idx_cur == DB_COUNT) 2604071d4279SBram Moolenaar { 2605f9e3e09fSBram Moolenaar emsg(_("E99: Current buffer is not in diff mode")); 2606071d4279SBram Moolenaar return; 2607071d4279SBram Moolenaar } 2608071d4279SBram Moolenaar 2609071d4279SBram Moolenaar if (*eap->arg == NUL) 2610071d4279SBram Moolenaar { 26115d18efecSBram Moolenaar // No argument: Find the other buffer in the list of diff buffers. 2612071d4279SBram Moolenaar for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) 261349d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_other] != curbuf 2614602eb74dSBram Moolenaar && curtab->tp_diffbuf[idx_other] != NULL) 2615602eb74dSBram Moolenaar { 2616602eb74dSBram Moolenaar if (eap->cmdidx != CMD_diffput 2617602eb74dSBram Moolenaar || curtab->tp_diffbuf[idx_other]->b_p_ma) 2618071d4279SBram Moolenaar break; 2619602eb74dSBram Moolenaar found_not_ma = TRUE; 2620602eb74dSBram Moolenaar } 2621071d4279SBram Moolenaar if (idx_other == DB_COUNT) 2622071d4279SBram Moolenaar { 2623602eb74dSBram Moolenaar if (found_not_ma) 2624f9e3e09fSBram Moolenaar emsg(_("E793: No other buffer in diff mode is modifiable")); 2625602eb74dSBram Moolenaar else 2626f9e3e09fSBram Moolenaar emsg(_("E100: No other buffer in diff mode")); 2627071d4279SBram Moolenaar return; 2628071d4279SBram Moolenaar } 2629071d4279SBram Moolenaar 26305d18efecSBram Moolenaar // Check that there isn't a third buffer in the list 2631071d4279SBram Moolenaar for (i = idx_other + 1; i < DB_COUNT; ++i) 263249d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != curbuf 263349d7bf13SBram Moolenaar && curtab->tp_diffbuf[i] != NULL 263449d7bf13SBram Moolenaar && (eap->cmdidx != CMD_diffput || curtab->tp_diffbuf[i]->b_p_ma)) 2635071d4279SBram Moolenaar { 2636f9e3e09fSBram Moolenaar emsg(_("E101: More than two buffers in diff mode, don't know which one to use")); 2637071d4279SBram Moolenaar return; 2638071d4279SBram Moolenaar } 2639071d4279SBram Moolenaar } 2640071d4279SBram Moolenaar else 2641071d4279SBram Moolenaar { 26425d18efecSBram Moolenaar // Buffer number or pattern given. Ignore trailing white space. 2643071d4279SBram Moolenaar p = eap->arg + STRLEN(eap->arg); 26441c465444SBram Moolenaar while (p > eap->arg && VIM_ISWHITE(p[-1])) 2645071d4279SBram Moolenaar --p; 2646071d4279SBram Moolenaar for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) 2647071d4279SBram Moolenaar ; 26485d18efecSBram Moolenaar if (eap->arg + i == p) // digits only 2649071d4279SBram Moolenaar i = atol((char *)eap->arg); 2650071d4279SBram Moolenaar else 2651071d4279SBram Moolenaar { 26520c279bbbSBram Moolenaar i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE); 2653071d4279SBram Moolenaar if (i < 0) 26545d18efecSBram Moolenaar return; // error message already given 2655071d4279SBram Moolenaar } 2656071d4279SBram Moolenaar buf = buflist_findnr(i); 2657071d4279SBram Moolenaar if (buf == NULL) 2658071d4279SBram Moolenaar { 2659f9e3e09fSBram Moolenaar semsg(_("E102: Can't find buffer \"%s\""), eap->arg); 2660071d4279SBram Moolenaar return; 2661071d4279SBram Moolenaar } 26625cc6a6e7SBram Moolenaar if (buf == curbuf) 26635d18efecSBram Moolenaar return; // nothing to do 2664071d4279SBram Moolenaar idx_other = diff_buf_idx(buf); 2665071d4279SBram Moolenaar if (idx_other == DB_COUNT) 2666071d4279SBram Moolenaar { 2667f9e3e09fSBram Moolenaar semsg(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg); 2668071d4279SBram Moolenaar return; 2669071d4279SBram Moolenaar } 2670071d4279SBram Moolenaar } 2671071d4279SBram Moolenaar 2672071d4279SBram Moolenaar diff_busy = TRUE; 2673071d4279SBram Moolenaar 26745d18efecSBram Moolenaar // When no range given include the line above or below the cursor. 2675071d4279SBram Moolenaar if (eap->addr_count == 0) 2676071d4279SBram Moolenaar { 26775d18efecSBram Moolenaar // Make it possible that ":diffget" on the last line gets line below 26785d18efecSBram Moolenaar // the cursor line when there is no difference above the cursor. 2679071d4279SBram Moolenaar if (eap->cmdidx == CMD_diffget 2680071d4279SBram Moolenaar && eap->line1 == curbuf->b_ml.ml_line_count 2681071d4279SBram Moolenaar && diff_check(curwin, eap->line1) == 0 2682071d4279SBram Moolenaar && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0)) 2683071d4279SBram Moolenaar ++eap->line2; 2684071d4279SBram Moolenaar else if (eap->line1 > 0) 2685071d4279SBram Moolenaar --eap->line1; 2686071d4279SBram Moolenaar } 2687071d4279SBram Moolenaar 2688071d4279SBram Moolenaar if (eap->cmdidx == CMD_diffget) 2689071d4279SBram Moolenaar { 2690071d4279SBram Moolenaar idx_from = idx_other; 2691071d4279SBram Moolenaar idx_to = idx_cur; 2692071d4279SBram Moolenaar } 2693071d4279SBram Moolenaar else 2694071d4279SBram Moolenaar { 2695071d4279SBram Moolenaar idx_from = idx_cur; 2696071d4279SBram Moolenaar idx_to = idx_other; 26975d18efecSBram Moolenaar // Need to make the other buffer the current buffer to be able to make 26985d18efecSBram Moolenaar // changes in it. 26995d18efecSBram Moolenaar // set curwin/curbuf to buf and save a few things 270049d7bf13SBram Moolenaar aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); 2701071d4279SBram Moolenaar } 2702071d4279SBram Moolenaar 27035d18efecSBram Moolenaar // May give the warning for a changed buffer here, which can trigger the 27045d18efecSBram Moolenaar // FileChangedRO autocommand, which may do nasty things and mess 27055d18efecSBram Moolenaar // everything up. 2706910f66f9SBram Moolenaar if (!curbuf->b_changed) 2707910f66f9SBram Moolenaar { 2708910f66f9SBram Moolenaar change_warning(0); 2709910f66f9SBram Moolenaar if (diff_buf_idx(curbuf) != idx_to) 2710910f66f9SBram Moolenaar { 2711f9e3e09fSBram Moolenaar emsg(_("E787: Buffer changed unexpectedly")); 2712d2b58c0aSBram Moolenaar goto theend; 2713910f66f9SBram Moolenaar } 2714910f66f9SBram Moolenaar } 2715910f66f9SBram Moolenaar 2716071d4279SBram Moolenaar dprev = NULL; 271749d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; ) 2718071d4279SBram Moolenaar { 2719071d4279SBram Moolenaar if (dp->df_lnum[idx_cur] > eap->line2 + off) 27205d18efecSBram Moolenaar break; // past the range that was specified 2721071d4279SBram Moolenaar 2722071d4279SBram Moolenaar dfree = NULL; 2723071d4279SBram Moolenaar lnum = dp->df_lnum[idx_to]; 2724071d4279SBram Moolenaar count = dp->df_count[idx_to]; 2725071d4279SBram Moolenaar if (dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off 2726071d4279SBram Moolenaar && u_save(lnum - 1, lnum + count) != FAIL) 2727071d4279SBram Moolenaar { 27285d18efecSBram Moolenaar // Inside the specified range and saving for undo worked. 2729071d4279SBram Moolenaar start_skip = 0; 2730071d4279SBram Moolenaar end_skip = 0; 2731071d4279SBram Moolenaar if (eap->addr_count > 0) 2732071d4279SBram Moolenaar { 27335d18efecSBram Moolenaar // A range was specified: check if lines need to be skipped. 2734071d4279SBram Moolenaar start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; 2735071d4279SBram Moolenaar if (start_skip > 0) 2736071d4279SBram Moolenaar { 27375d18efecSBram Moolenaar // range starts below start of current diff block 2738071d4279SBram Moolenaar if (start_skip > count) 2739071d4279SBram Moolenaar { 2740071d4279SBram Moolenaar lnum += count; 2741071d4279SBram Moolenaar count = 0; 2742071d4279SBram Moolenaar } 2743071d4279SBram Moolenaar else 2744071d4279SBram Moolenaar { 2745071d4279SBram Moolenaar count -= start_skip; 2746071d4279SBram Moolenaar lnum += start_skip; 2747071d4279SBram Moolenaar } 2748071d4279SBram Moolenaar } 2749071d4279SBram Moolenaar else 2750071d4279SBram Moolenaar start_skip = 0; 2751071d4279SBram Moolenaar 2752071d4279SBram Moolenaar end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 2753071d4279SBram Moolenaar - (eap->line2 + off); 2754071d4279SBram Moolenaar if (end_skip > 0) 2755071d4279SBram Moolenaar { 27565d18efecSBram Moolenaar // range ends above end of current/from diff block 27575d18efecSBram Moolenaar if (idx_cur == idx_from) // :diffput 2758071d4279SBram Moolenaar { 2759071d4279SBram Moolenaar i = dp->df_count[idx_cur] - start_skip - end_skip; 2760071d4279SBram Moolenaar if (count > i) 2761071d4279SBram Moolenaar count = i; 2762071d4279SBram Moolenaar } 27635d18efecSBram Moolenaar else // :diffget 2764071d4279SBram Moolenaar { 2765071d4279SBram Moolenaar count -= end_skip; 2766071d4279SBram Moolenaar end_skip = dp->df_count[idx_from] - start_skip - count; 2767071d4279SBram Moolenaar if (end_skip < 0) 2768071d4279SBram Moolenaar end_skip = 0; 2769071d4279SBram Moolenaar } 2770071d4279SBram Moolenaar } 2771071d4279SBram Moolenaar else 2772071d4279SBram Moolenaar end_skip = 0; 2773071d4279SBram Moolenaar } 2774071d4279SBram Moolenaar 2775b5aedf3eSBram Moolenaar buf_empty = BUFEMPTY(); 2776071d4279SBram Moolenaar added = 0; 2777071d4279SBram Moolenaar for (i = 0; i < count; ++i) 2778071d4279SBram Moolenaar { 27795d18efecSBram Moolenaar // remember deleting the last line of the buffer 2780280f126eSBram Moolenaar buf_empty = curbuf->b_ml.ml_line_count == 1; 2781ca70c07bSBram Moolenaar ml_delete(lnum); 2782071d4279SBram Moolenaar --added; 2783071d4279SBram Moolenaar } 2784071d4279SBram Moolenaar for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) 2785071d4279SBram Moolenaar { 2786071d4279SBram Moolenaar linenr_T nr; 2787071d4279SBram Moolenaar 2788071d4279SBram Moolenaar nr = dp->df_lnum[idx_from] + start_skip + i; 278949d7bf13SBram Moolenaar if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) 2790071d4279SBram Moolenaar break; 2791910f66f9SBram Moolenaar p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], 2792910f66f9SBram Moolenaar nr, FALSE)); 2793071d4279SBram Moolenaar if (p != NULL) 2794071d4279SBram Moolenaar { 2795071d4279SBram Moolenaar ml_append(lnum + i - 1, p, 0, FALSE); 2796071d4279SBram Moolenaar vim_free(p); 2797071d4279SBram Moolenaar ++added; 2798280f126eSBram Moolenaar if (buf_empty && curbuf->b_ml.ml_line_count == 2) 2799280f126eSBram Moolenaar { 28005d18efecSBram Moolenaar // Added the first line into an empty buffer, need to 28015d18efecSBram Moolenaar // delete the dummy empty line. 2802280f126eSBram Moolenaar buf_empty = FALSE; 2803ca70c07bSBram Moolenaar ml_delete((linenr_T)2); 2804280f126eSBram Moolenaar } 2805071d4279SBram Moolenaar } 2806071d4279SBram Moolenaar } 2807071d4279SBram Moolenaar new_count = dp->df_count[idx_to] + added; 2808071d4279SBram Moolenaar dp->df_count[idx_to] = new_count; 2809071d4279SBram Moolenaar 2810071d4279SBram Moolenaar if (start_skip == 0 && end_skip == 0) 2811071d4279SBram Moolenaar { 28125d18efecSBram Moolenaar // Check if there are any other buffers and if the diff is 28135d18efecSBram Moolenaar // equal in them. 2814071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2815910f66f9SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && i != idx_from 2816910f66f9SBram Moolenaar && i != idx_to 2817071d4279SBram Moolenaar && !diff_equal_entry(dp, idx_from, i)) 2818071d4279SBram Moolenaar break; 2819071d4279SBram Moolenaar if (i == DB_COUNT) 2820071d4279SBram Moolenaar { 28215d18efecSBram Moolenaar // delete the diff entry, the buffers are now equal here 2822071d4279SBram Moolenaar dfree = dp; 2823071d4279SBram Moolenaar dp = dp->df_next; 2824071d4279SBram Moolenaar if (dprev == NULL) 282549d7bf13SBram Moolenaar curtab->tp_first_diff = dp; 2826071d4279SBram Moolenaar else 2827071d4279SBram Moolenaar dprev->df_next = dp; 2828071d4279SBram Moolenaar } 2829071d4279SBram Moolenaar } 2830071d4279SBram Moolenaar 28315d18efecSBram Moolenaar // Adjust marks. This will change the following entries! 2832071d4279SBram Moolenaar if (added != 0) 2833071d4279SBram Moolenaar { 2834071d4279SBram Moolenaar mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added); 2835071d4279SBram Moolenaar if (curwin->w_cursor.lnum >= lnum) 2836071d4279SBram Moolenaar { 28375d18efecSBram Moolenaar // Adjust the cursor position if it's in/after the changed 28385d18efecSBram Moolenaar // lines. 2839071d4279SBram Moolenaar if (curwin->w_cursor.lnum >= lnum + count) 2840071d4279SBram Moolenaar curwin->w_cursor.lnum += added; 2841071d4279SBram Moolenaar else if (added < 0) 2842071d4279SBram Moolenaar curwin->w_cursor.lnum = lnum; 2843071d4279SBram Moolenaar } 2844071d4279SBram Moolenaar } 2845071d4279SBram Moolenaar changed_lines(lnum, 0, lnum + count, (long)added); 2846071d4279SBram Moolenaar 2847071d4279SBram Moolenaar if (dfree != NULL) 2848071d4279SBram Moolenaar { 28495d18efecSBram Moolenaar // Diff is deleted, update folds in other windows. 2850071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2851071d4279SBram Moolenaar diff_fold_update(dfree, idx_to); 2852071d4279SBram Moolenaar #endif 2853071d4279SBram Moolenaar vim_free(dfree); 2854071d4279SBram Moolenaar } 2855071d4279SBram Moolenaar else 28565d18efecSBram Moolenaar // mark_adjust() may have changed the count in a wrong way 2857071d4279SBram Moolenaar dp->df_count[idx_to] = new_count; 2858071d4279SBram Moolenaar 28595d18efecSBram Moolenaar // When changing the current buffer, keep track of line numbers 2860071d4279SBram Moolenaar if (idx_cur == idx_to) 2861071d4279SBram Moolenaar off += added; 2862071d4279SBram Moolenaar } 2863071d4279SBram Moolenaar 28645d18efecSBram Moolenaar // If before the range or not deleted, go to next diff. 2865071d4279SBram Moolenaar if (dfree == NULL) 2866071d4279SBram Moolenaar { 2867071d4279SBram Moolenaar dprev = dp; 2868071d4279SBram Moolenaar dp = dp->df_next; 2869071d4279SBram Moolenaar } 2870071d4279SBram Moolenaar } 2871071d4279SBram Moolenaar 28725d18efecSBram Moolenaar // restore curwin/curbuf and a few other things 2873a9d52e3bSBram Moolenaar if (eap->cmdidx != CMD_diffget) 2874071d4279SBram Moolenaar { 28755d18efecSBram Moolenaar // Syncing undo only works for the current buffer, but we change 28765d18efecSBram Moolenaar // another buffer. Sync undo if the command was typed. This isn't 28775d18efecSBram Moolenaar // 100% right when ":diffput" is used in a function or mapping. 2878071d4279SBram Moolenaar if (KeyTyped) 2879779b74b2SBram Moolenaar u_sync(FALSE); 2880071d4279SBram Moolenaar aucmd_restbuf(&aco); 2881071d4279SBram Moolenaar } 2882071d4279SBram Moolenaar 2883d2b58c0aSBram Moolenaar theend: 2884071d4279SBram Moolenaar diff_busy = FALSE; 2885d2b58c0aSBram Moolenaar if (diff_need_update) 2886d2b58c0aSBram Moolenaar ex_diffupdate(NULL); 2887df77cef9SBram Moolenaar 28885f57bdcaSBram Moolenaar // Check that the cursor is on a valid character and update its 2889198fa066SBram Moolenaar // position. When there were filler lines the topline has become 2890198fa066SBram Moolenaar // invalid. 2891071d4279SBram Moolenaar check_cursor(); 2892071d4279SBram Moolenaar changed_line_abv_curs(); 2893071d4279SBram Moolenaar 2894df77cef9SBram Moolenaar if (diff_need_update) 2895df77cef9SBram Moolenaar // redraw already done by ex_diffupdate() 2896df77cef9SBram Moolenaar diff_need_update = FALSE; 2897df77cef9SBram Moolenaar else 2898df77cef9SBram Moolenaar { 2899198fa066SBram Moolenaar // Also need to redraw the other buffers. 2900071d4279SBram Moolenaar diff_redraw(FALSE); 2901198fa066SBram Moolenaar apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); 2902198fa066SBram Moolenaar } 2903071d4279SBram Moolenaar } 2904071d4279SBram Moolenaar 2905071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2906071d4279SBram Moolenaar /* 2907071d4279SBram Moolenaar * Update folds for all diff buffers for entry "dp". 2908071d4279SBram Moolenaar * Skip buffer with index "skip_idx". 2909071d4279SBram Moolenaar * When there are no diffs, all folds are removed. 2910071d4279SBram Moolenaar */ 2911071d4279SBram Moolenaar static void 29127454a06eSBram Moolenaar diff_fold_update(diff_T *dp, int skip_idx) 2913071d4279SBram Moolenaar { 2914071d4279SBram Moolenaar int i; 2915071d4279SBram Moolenaar win_T *wp; 2916071d4279SBram Moolenaar 291729323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 2918071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 291949d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] == wp->w_buffer && i != skip_idx) 2920071d4279SBram Moolenaar foldUpdate(wp, dp->df_lnum[i], 2921071d4279SBram Moolenaar dp->df_lnum[i] + dp->df_count[i]); 2922071d4279SBram Moolenaar } 2923071d4279SBram Moolenaar #endif 2924071d4279SBram Moolenaar 2925071d4279SBram Moolenaar /* 2926071d4279SBram Moolenaar * Return TRUE if buffer "buf" is in diff-mode. 2927071d4279SBram Moolenaar */ 2928071d4279SBram Moolenaar int 29297454a06eSBram Moolenaar diff_mode_buf(buf_T *buf) 2930071d4279SBram Moolenaar { 293149d7bf13SBram Moolenaar tabpage_T *tp; 293249d7bf13SBram Moolenaar 293329323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 293449d7bf13SBram Moolenaar if (diff_buf_idx_tp(buf, tp) != DB_COUNT) 293549d7bf13SBram Moolenaar return TRUE; 293649d7bf13SBram Moolenaar return FALSE; 2937071d4279SBram Moolenaar } 2938071d4279SBram Moolenaar 2939071d4279SBram Moolenaar /* 2940071d4279SBram Moolenaar * Move "count" times in direction "dir" to the next diff block. 2941071d4279SBram Moolenaar * Return FAIL if there isn't such a diff block. 2942071d4279SBram Moolenaar */ 2943071d4279SBram Moolenaar int 29447454a06eSBram Moolenaar diff_move_to(int dir, long count) 2945071d4279SBram Moolenaar { 2946071d4279SBram Moolenaar int idx; 2947071d4279SBram Moolenaar linenr_T lnum = curwin->w_cursor.lnum; 2948071d4279SBram Moolenaar diff_T *dp; 2949071d4279SBram Moolenaar 2950071d4279SBram Moolenaar idx = diff_buf_idx(curbuf); 295149d7bf13SBram Moolenaar if (idx == DB_COUNT || curtab->tp_first_diff == NULL) 2952071d4279SBram Moolenaar return FAIL; 2953071d4279SBram Moolenaar 295449d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 29555d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 2956071d4279SBram Moolenaar 29575d18efecSBram Moolenaar if (curtab->tp_first_diff == NULL) // no diffs today 2958071d4279SBram Moolenaar return FAIL; 2959071d4279SBram Moolenaar 2960071d4279SBram Moolenaar while (--count >= 0) 2961071d4279SBram Moolenaar { 29625d18efecSBram Moolenaar // Check if already before first diff. 296349d7bf13SBram Moolenaar if (dir == BACKWARD && lnum <= curtab->tp_first_diff->df_lnum[idx]) 2964071d4279SBram Moolenaar break; 2965071d4279SBram Moolenaar 296649d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; ; dp = dp->df_next) 2967071d4279SBram Moolenaar { 2968071d4279SBram Moolenaar if (dp == NULL) 2969071d4279SBram Moolenaar break; 2970071d4279SBram Moolenaar if ((dir == FORWARD && lnum < dp->df_lnum[idx]) 2971071d4279SBram Moolenaar || (dir == BACKWARD 2972071d4279SBram Moolenaar && (dp->df_next == NULL 2973071d4279SBram Moolenaar || lnum <= dp->df_next->df_lnum[idx]))) 2974071d4279SBram Moolenaar { 2975071d4279SBram Moolenaar lnum = dp->df_lnum[idx]; 2976071d4279SBram Moolenaar break; 2977071d4279SBram Moolenaar } 2978071d4279SBram Moolenaar } 2979071d4279SBram Moolenaar } 2980071d4279SBram Moolenaar 29815d18efecSBram Moolenaar // don't end up past the end of the file 2982071d4279SBram Moolenaar if (lnum > curbuf->b_ml.ml_line_count) 2983071d4279SBram Moolenaar lnum = curbuf->b_ml.ml_line_count; 2984071d4279SBram Moolenaar 29855d18efecSBram Moolenaar // When the cursor didn't move at all we fail. 2986071d4279SBram Moolenaar if (lnum == curwin->w_cursor.lnum) 2987071d4279SBram Moolenaar return FAIL; 2988071d4279SBram Moolenaar 2989071d4279SBram Moolenaar setpcmark(); 2990071d4279SBram Moolenaar curwin->w_cursor.lnum = lnum; 2991071d4279SBram Moolenaar curwin->w_cursor.col = 0; 2992071d4279SBram Moolenaar 2993071d4279SBram Moolenaar return OK; 2994071d4279SBram Moolenaar } 2995071d4279SBram Moolenaar 2996025e3e0bSBram Moolenaar /* 2997025e3e0bSBram Moolenaar * Return the line number in the current window that is closest to "lnum1" in 2998025e3e0bSBram Moolenaar * "buf1" in diff mode. 2999025e3e0bSBram Moolenaar */ 3000025e3e0bSBram Moolenaar static linenr_T 3001025e3e0bSBram Moolenaar diff_get_corresponding_line_int( 30027454a06eSBram Moolenaar buf_T *buf1, 3003025e3e0bSBram Moolenaar linenr_T lnum1) 3004860cae1cSBram Moolenaar { 3005860cae1cSBram Moolenaar int idx1; 3006860cae1cSBram Moolenaar int idx2; 3007860cae1cSBram Moolenaar diff_T *dp; 3008860cae1cSBram Moolenaar int baseline = 0; 3009860cae1cSBram Moolenaar 3010860cae1cSBram Moolenaar idx1 = diff_buf_idx(buf1); 3011025e3e0bSBram Moolenaar idx2 = diff_buf_idx(curbuf); 3012860cae1cSBram Moolenaar if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL) 3013860cae1cSBram Moolenaar return lnum1; 3014860cae1cSBram Moolenaar 3015860cae1cSBram Moolenaar if (curtab->tp_diff_invalid) 30165d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 3017860cae1cSBram Moolenaar 30185d18efecSBram Moolenaar if (curtab->tp_first_diff == NULL) // no diffs today 3019860cae1cSBram Moolenaar return lnum1; 3020860cae1cSBram Moolenaar 3021aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 3022860cae1cSBram Moolenaar { 3023860cae1cSBram Moolenaar if (dp->df_lnum[idx1] > lnum1) 3024025e3e0bSBram Moolenaar return lnum1 - baseline; 3025025e3e0bSBram Moolenaar if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) 3026860cae1cSBram Moolenaar { 30275d18efecSBram Moolenaar // Inside the diffblock 3028860cae1cSBram Moolenaar baseline = lnum1 - dp->df_lnum[idx1]; 3029860cae1cSBram Moolenaar if (baseline > dp->df_count[idx2]) 3030860cae1cSBram Moolenaar baseline = dp->df_count[idx2]; 3031860cae1cSBram Moolenaar 3032860cae1cSBram Moolenaar return dp->df_lnum[idx2] + baseline; 3033860cae1cSBram Moolenaar } 3034025e3e0bSBram Moolenaar if ( (dp->df_lnum[idx1] == lnum1) 3035860cae1cSBram Moolenaar && (dp->df_count[idx1] == 0) 3036025e3e0bSBram Moolenaar && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) 3037025e3e0bSBram Moolenaar && ((dp->df_lnum[idx2] + dp->df_count[idx2]) 3038025e3e0bSBram Moolenaar > curwin->w_cursor.lnum)) 3039860cae1cSBram Moolenaar /* 3040860cae1cSBram Moolenaar * Special case: if the cursor is just after a zero-count 3041860cae1cSBram Moolenaar * block (i.e. all filler) and the target cursor is already 3042860cae1cSBram Moolenaar * inside the corresponding block, leave the target cursor 3043860cae1cSBram Moolenaar * unmoved. This makes repeated CTRL-W W operations work 3044860cae1cSBram Moolenaar * as expected. 3045860cae1cSBram Moolenaar */ 3046025e3e0bSBram Moolenaar return curwin->w_cursor.lnum; 3047860cae1cSBram Moolenaar baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) 3048860cae1cSBram Moolenaar - (dp->df_lnum[idx2] + dp->df_count[idx2]); 3049860cae1cSBram Moolenaar } 3050860cae1cSBram Moolenaar 30515d18efecSBram Moolenaar // If we get here then the cursor is after the last diff 3052025e3e0bSBram Moolenaar return lnum1 - baseline; 3053025e3e0bSBram Moolenaar } 3054860cae1cSBram Moolenaar 3055025e3e0bSBram Moolenaar /* 3056025e3e0bSBram Moolenaar * Return the line number in the current window that is closest to "lnum1" in 3057025e3e0bSBram Moolenaar * "buf1" in diff mode. Checks the line number to be valid. 3058025e3e0bSBram Moolenaar */ 3059025e3e0bSBram Moolenaar linenr_T 3060025e3e0bSBram Moolenaar diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) 3061025e3e0bSBram Moolenaar { 3062025e3e0bSBram Moolenaar linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); 3063025e3e0bSBram Moolenaar 30645d18efecSBram Moolenaar // don't end up past the end of the file 3065025e3e0bSBram Moolenaar if (lnum > curbuf->b_ml.ml_line_count) 3066025e3e0bSBram Moolenaar return curbuf->b_ml.ml_line_count; 3067025e3e0bSBram Moolenaar return lnum; 3068860cae1cSBram Moolenaar } 3069860cae1cSBram Moolenaar 3070071d4279SBram Moolenaar /* 3071071d4279SBram Moolenaar * For line "lnum" in the current window find the equivalent lnum in window 3072071d4279SBram Moolenaar * "wp", compensating for inserted/deleted lines. 3073071d4279SBram Moolenaar */ 3074071d4279SBram Moolenaar linenr_T 30757454a06eSBram Moolenaar diff_lnum_win(linenr_T lnum, win_T *wp) 3076071d4279SBram Moolenaar { 3077071d4279SBram Moolenaar diff_T *dp; 3078071d4279SBram Moolenaar int idx; 3079071d4279SBram Moolenaar int i; 3080071d4279SBram Moolenaar linenr_T n; 3081071d4279SBram Moolenaar 3082071d4279SBram Moolenaar idx = diff_buf_idx(curbuf); 30835d18efecSBram Moolenaar if (idx == DB_COUNT) // safety check 3084071d4279SBram Moolenaar return (linenr_T)0; 3085071d4279SBram Moolenaar 308649d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 30875d18efecSBram Moolenaar ex_diffupdate(NULL); // update after a big change 3088071d4279SBram Moolenaar 30895d18efecSBram Moolenaar // search for a change that includes "lnum" in the list of diffblocks. 3090aeea7215SBram Moolenaar FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) 3091071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 3092071d4279SBram Moolenaar break; 3093071d4279SBram Moolenaar 30945d18efecSBram Moolenaar // When after the last change, compute relative to the last line number. 3095071d4279SBram Moolenaar if (dp == NULL) 3096071d4279SBram Moolenaar return wp->w_buffer->b_ml.ml_line_count 3097071d4279SBram Moolenaar - (curbuf->b_ml.ml_line_count - lnum); 3098071d4279SBram Moolenaar 30995d18efecSBram Moolenaar // Find index for "wp". 3100071d4279SBram Moolenaar i = diff_buf_idx(wp->w_buffer); 31015d18efecSBram Moolenaar if (i == DB_COUNT) // safety check 3102071d4279SBram Moolenaar return (linenr_T)0; 3103071d4279SBram Moolenaar 3104071d4279SBram Moolenaar n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); 3105071d4279SBram Moolenaar if (n > dp->df_lnum[i] + dp->df_count[i]) 3106071d4279SBram Moolenaar n = dp->df_lnum[i] + dp->df_count[i]; 3107071d4279SBram Moolenaar return n; 3108071d4279SBram Moolenaar } 3109071d4279SBram Moolenaar 3110e828b762SBram Moolenaar /* 3111e828b762SBram Moolenaar * Handle an ED style diff line. 3112e828b762SBram Moolenaar * Return FAIL if the line does not contain diff info. 3113e828b762SBram Moolenaar */ 3114e828b762SBram Moolenaar static int 3115e828b762SBram Moolenaar parse_diff_ed( 3116e828b762SBram Moolenaar char_u *line, 3117e828b762SBram Moolenaar linenr_T *lnum_orig, 3118e828b762SBram Moolenaar long *count_orig, 3119e828b762SBram Moolenaar linenr_T *lnum_new, 3120e828b762SBram Moolenaar long *count_new) 3121e828b762SBram Moolenaar { 3122e828b762SBram Moolenaar char_u *p; 3123e828b762SBram Moolenaar long f1, l1, f2, l2; 3124e828b762SBram Moolenaar int difftype; 3125e828b762SBram Moolenaar 3126e828b762SBram Moolenaar // The line must be one of three formats: 3127e828b762SBram Moolenaar // change: {first}[,{last}]c{first}[,{last}] 3128e828b762SBram Moolenaar // append: {first}a{first}[,{last}] 3129e828b762SBram Moolenaar // delete: {first}[,{last}]d{first} 3130e828b762SBram Moolenaar p = line; 3131e828b762SBram Moolenaar f1 = getdigits(&p); 3132e828b762SBram Moolenaar if (*p == ',') 3133e828b762SBram Moolenaar { 3134e828b762SBram Moolenaar ++p; 3135e828b762SBram Moolenaar l1 = getdigits(&p); 3136e828b762SBram Moolenaar } 3137e828b762SBram Moolenaar else 3138e828b762SBram Moolenaar l1 = f1; 3139e828b762SBram Moolenaar if (*p != 'a' && *p != 'c' && *p != 'd') 3140e828b762SBram Moolenaar return FAIL; // invalid diff format 3141e828b762SBram Moolenaar difftype = *p++; 3142e828b762SBram Moolenaar f2 = getdigits(&p); 3143e828b762SBram Moolenaar if (*p == ',') 3144e828b762SBram Moolenaar { 3145e828b762SBram Moolenaar ++p; 3146e828b762SBram Moolenaar l2 = getdigits(&p); 3147e828b762SBram Moolenaar } 3148e828b762SBram Moolenaar else 3149e828b762SBram Moolenaar l2 = f2; 3150e828b762SBram Moolenaar if (l1 < f1 || l2 < f2) 3151e828b762SBram Moolenaar return FAIL; 3152e828b762SBram Moolenaar 3153e828b762SBram Moolenaar if (difftype == 'a') 3154e828b762SBram Moolenaar { 3155e828b762SBram Moolenaar *lnum_orig = f1 + 1; 3156e828b762SBram Moolenaar *count_orig = 0; 3157e828b762SBram Moolenaar } 3158e828b762SBram Moolenaar else 3159e828b762SBram Moolenaar { 3160e828b762SBram Moolenaar *lnum_orig = f1; 3161e828b762SBram Moolenaar *count_orig = l1 - f1 + 1; 3162e828b762SBram Moolenaar } 3163e828b762SBram Moolenaar if (difftype == 'd') 3164e828b762SBram Moolenaar { 3165e828b762SBram Moolenaar *lnum_new = f2 + 1; 3166e828b762SBram Moolenaar *count_new = 0; 3167e828b762SBram Moolenaar } 3168e828b762SBram Moolenaar else 3169e828b762SBram Moolenaar { 3170e828b762SBram Moolenaar *lnum_new = f2; 3171e828b762SBram Moolenaar *count_new = l2 - f2 + 1; 3172e828b762SBram Moolenaar } 3173e828b762SBram Moolenaar return OK; 3174e828b762SBram Moolenaar } 3175e828b762SBram Moolenaar 3176e828b762SBram Moolenaar /* 3177e828b762SBram Moolenaar * Parses unified diff with zero(!) context lines. 3178e828b762SBram Moolenaar * Return FAIL if there is no diff information in "line". 3179e828b762SBram Moolenaar */ 3180e828b762SBram Moolenaar static int 3181e828b762SBram Moolenaar parse_diff_unified( 3182e828b762SBram Moolenaar char_u *line, 3183e828b762SBram Moolenaar linenr_T *lnum_orig, 3184e828b762SBram Moolenaar long *count_orig, 3185e828b762SBram Moolenaar linenr_T *lnum_new, 3186e828b762SBram Moolenaar long *count_new) 3187e828b762SBram Moolenaar { 3188e828b762SBram Moolenaar char_u *p; 3189e828b762SBram Moolenaar long oldline, oldcount, newline, newcount; 3190e828b762SBram Moolenaar 3191e828b762SBram Moolenaar // Parse unified diff hunk header: 3192e828b762SBram Moolenaar // @@ -oldline,oldcount +newline,newcount @@ 3193e828b762SBram Moolenaar p = line; 3194e828b762SBram Moolenaar if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') 3195e828b762SBram Moolenaar { 3196e828b762SBram Moolenaar oldline = getdigits(&p); 3197e828b762SBram Moolenaar if (*p == ',') 3198e828b762SBram Moolenaar { 3199e828b762SBram Moolenaar ++p; 3200e828b762SBram Moolenaar oldcount = getdigits(&p); 3201e828b762SBram Moolenaar } 3202e828b762SBram Moolenaar else 3203e828b762SBram Moolenaar oldcount = 1; 3204e828b762SBram Moolenaar if (*p++ == ' ' && *p++ == '+') 3205e828b762SBram Moolenaar { 3206e828b762SBram Moolenaar newline = getdigits(&p); 3207e828b762SBram Moolenaar if (*p == ',') 3208e828b762SBram Moolenaar { 3209e828b762SBram Moolenaar ++p; 3210e828b762SBram Moolenaar newcount = getdigits(&p); 3211e828b762SBram Moolenaar } 3212e828b762SBram Moolenaar else 3213e828b762SBram Moolenaar newcount = 1; 3214e828b762SBram Moolenaar } 3215e828b762SBram Moolenaar else 3216e828b762SBram Moolenaar return FAIL; // invalid diff format 3217e828b762SBram Moolenaar 3218e828b762SBram Moolenaar if (oldcount == 0) 3219e828b762SBram Moolenaar oldline += 1; 3220e828b762SBram Moolenaar if (newcount == 0) 3221e828b762SBram Moolenaar newline += 1; 3222e828b762SBram Moolenaar if (newline == 0) 3223e828b762SBram Moolenaar newline = 1; 3224e828b762SBram Moolenaar 3225e828b762SBram Moolenaar *lnum_orig = oldline; 3226e828b762SBram Moolenaar *count_orig = oldcount; 3227e828b762SBram Moolenaar *lnum_new = newline; 3228e828b762SBram Moolenaar *count_new = newcount; 3229e828b762SBram Moolenaar 3230e828b762SBram Moolenaar return OK; 3231e828b762SBram Moolenaar } 3232e828b762SBram Moolenaar 3233e828b762SBram Moolenaar return FAIL; 3234e828b762SBram Moolenaar } 3235e828b762SBram Moolenaar 3236e828b762SBram Moolenaar /* 3237e828b762SBram Moolenaar * Callback function for the xdl_diff() function. 3238e828b762SBram Moolenaar * Stores the diff output in a grow array. 3239e828b762SBram Moolenaar */ 3240e828b762SBram Moolenaar static int 3241e828b762SBram Moolenaar xdiff_out(void *priv, mmbuffer_t *mb, int nbuf) 3242e828b762SBram Moolenaar { 3243e828b762SBram Moolenaar diffout_T *dout = (diffout_T *)priv; 3244e828b762SBram Moolenaar char_u *p; 3245e828b762SBram Moolenaar 3246f080d70aSBram Moolenaar // The header line always comes by itself, text lines in at least two 3247f080d70aSBram Moolenaar // parts. We drop the text part. 3248f080d70aSBram Moolenaar if (nbuf > 1) 3249f080d70aSBram Moolenaar return 0; 3250f080d70aSBram Moolenaar 3251f080d70aSBram Moolenaar // sanity check 3252f080d70aSBram Moolenaar if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) 3253f080d70aSBram Moolenaar return 0; 3254f080d70aSBram Moolenaar 3255e828b762SBram Moolenaar if (ga_grow(&dout->dout_ga, 1) == FAIL) 3256e828b762SBram Moolenaar return -1; 3257f080d70aSBram Moolenaar p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size); 3258e828b762SBram Moolenaar if (p == NULL) 3259e828b762SBram Moolenaar return -1; 3260e828b762SBram Moolenaar ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; 3261e828b762SBram Moolenaar return 0; 3262e828b762SBram Moolenaar } 3263e828b762SBram Moolenaar 3264af7645d3SBram Moolenaar #endif // FEAT_DIFF 3265af7645d3SBram Moolenaar 3266af7645d3SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO) 3267af7645d3SBram Moolenaar 3268af7645d3SBram Moolenaar /* 3269af7645d3SBram Moolenaar * "diff_filler()" function 3270af7645d3SBram Moolenaar */ 3271af7645d3SBram Moolenaar void 3272af7645d3SBram Moolenaar f_diff_filler(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 3273af7645d3SBram Moolenaar { 3274af7645d3SBram Moolenaar #ifdef FEAT_DIFF 3275*4490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL) 3276*4490ec4eSYegappan Lakshmanan return; 3277*4490ec4eSYegappan Lakshmanan 3278af7645d3SBram Moolenaar rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); 3279af7645d3SBram Moolenaar #endif 3280af7645d3SBram Moolenaar } 3281af7645d3SBram Moolenaar 3282af7645d3SBram Moolenaar /* 3283af7645d3SBram Moolenaar * "diff_hlID()" function 3284af7645d3SBram Moolenaar */ 3285af7645d3SBram Moolenaar void 3286af7645d3SBram Moolenaar f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 3287af7645d3SBram Moolenaar { 3288af7645d3SBram Moolenaar #ifdef FEAT_DIFF 3289a9a7c0c6SYegappan Lakshmanan linenr_T lnum; 3290af7645d3SBram Moolenaar static linenr_T prev_lnum = 0; 3291af7645d3SBram Moolenaar static varnumber_T changedtick = 0; 3292af7645d3SBram Moolenaar static int fnum = 0; 3293af7645d3SBram Moolenaar static int change_start = 0; 3294af7645d3SBram Moolenaar static int change_end = 0; 3295af7645d3SBram Moolenaar static hlf_T hlID = (hlf_T)0; 3296af7645d3SBram Moolenaar int filler_lines; 3297af7645d3SBram Moolenaar int col; 3298af7645d3SBram Moolenaar 3299a9a7c0c6SYegappan Lakshmanan if (in_vim9script() 3300cd917207SYegappan Lakshmanan && (check_for_lnum_arg(argvars,0) == FAIL 3301a9a7c0c6SYegappan Lakshmanan || check_for_number_arg(argvars, 1) == FAIL)) 3302a9a7c0c6SYegappan Lakshmanan return; 3303a9a7c0c6SYegappan Lakshmanan 3304a9a7c0c6SYegappan Lakshmanan lnum = tv_get_lnum(argvars); 33055d18efecSBram Moolenaar if (lnum < 0) // ignore type error in {lnum} arg 3306af7645d3SBram Moolenaar lnum = 0; 3307af7645d3SBram Moolenaar if (lnum != prev_lnum 3308af7645d3SBram Moolenaar || changedtick != CHANGEDTICK(curbuf) 3309af7645d3SBram Moolenaar || fnum != curbuf->b_fnum) 3310af7645d3SBram Moolenaar { 33115d18efecSBram Moolenaar // New line, buffer, change: need to get the values. 3312af7645d3SBram Moolenaar filler_lines = diff_check(curwin, lnum); 3313af7645d3SBram Moolenaar if (filler_lines < 0) 3314af7645d3SBram Moolenaar { 3315af7645d3SBram Moolenaar if (filler_lines == -1) 3316af7645d3SBram Moolenaar { 3317af7645d3SBram Moolenaar change_start = MAXCOL; 3318af7645d3SBram Moolenaar change_end = -1; 3319af7645d3SBram Moolenaar if (diff_find_change(curwin, lnum, &change_start, &change_end)) 33205d18efecSBram Moolenaar hlID = HLF_ADD; // added line 3321af7645d3SBram Moolenaar else 33225d18efecSBram Moolenaar hlID = HLF_CHD; // changed line 3323af7645d3SBram Moolenaar } 3324af7645d3SBram Moolenaar else 33255d18efecSBram Moolenaar hlID = HLF_ADD; // added line 3326af7645d3SBram Moolenaar } 3327af7645d3SBram Moolenaar else 3328af7645d3SBram Moolenaar hlID = (hlf_T)0; 3329af7645d3SBram Moolenaar prev_lnum = lnum; 3330af7645d3SBram Moolenaar changedtick = CHANGEDTICK(curbuf); 3331af7645d3SBram Moolenaar fnum = curbuf->b_fnum; 3332af7645d3SBram Moolenaar } 3333af7645d3SBram Moolenaar 3334af7645d3SBram Moolenaar if (hlID == HLF_CHD || hlID == HLF_TXD) 3335af7645d3SBram Moolenaar { 33365d18efecSBram Moolenaar col = tv_get_number(&argvars[1]) - 1; // ignore type error in {col} 3337af7645d3SBram Moolenaar if (col >= change_start && col <= change_end) 33385d18efecSBram Moolenaar hlID = HLF_TXD; // changed text 3339af7645d3SBram Moolenaar else 33405d18efecSBram Moolenaar hlID = HLF_CHD; // changed line 3341af7645d3SBram Moolenaar } 3342af7645d3SBram Moolenaar rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; 3343af7645d3SBram Moolenaar #endif 3344af7645d3SBram Moolenaar } 3345af7645d3SBram Moolenaar 3346af7645d3SBram Moolenaar #endif 3347