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 27071d4279SBram 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 38785fc656SBram Moolenaar #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) 39274cea35SBram Moolenaar static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; 40071d4279SBram Moolenaar 41e828b762SBram Moolenaar static long diff_algorithm = 0; 42e828b762SBram Moolenaar 43071d4279SBram Moolenaar #define LBUFLEN 50 /* length of line in diff file */ 44071d4279SBram Moolenaar 45071d4279SBram Moolenaar static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it 46071d4279SBram Moolenaar doesn't work, MAYBE when not checked yet */ 4748e330afSBram Moolenaar #if defined(MSWIN) 48071d4279SBram Moolenaar static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE 49071d4279SBram Moolenaar when it doesn't work, MAYBE when not 50071d4279SBram Moolenaar checked yet */ 51071d4279SBram Moolenaar #endif 52071d4279SBram Moolenaar 53e828b762SBram Moolenaar // used for diff input 54e828b762SBram Moolenaar typedef struct { 55e828b762SBram Moolenaar char_u *din_fname; // used for external diff 56e828b762SBram Moolenaar mmfile_t din_mmfile; // used for internal diff 57e828b762SBram Moolenaar } diffin_T; 58e828b762SBram Moolenaar 59e828b762SBram Moolenaar // used for diff result 60e828b762SBram Moolenaar typedef struct { 61e828b762SBram Moolenaar char_u *dout_fname; // used for external diff 62e828b762SBram Moolenaar garray_T dout_ga; // used for internal diff 63e828b762SBram Moolenaar } diffout_T; 64e828b762SBram Moolenaar 65e828b762SBram Moolenaar // two diff inputs and one result 66e828b762SBram Moolenaar typedef struct { 67e828b762SBram Moolenaar diffin_T dio_orig; // original file input 68e828b762SBram Moolenaar diffin_T dio_new; // new file input 69e828b762SBram Moolenaar diffout_T dio_diff; // diff result 70e828b762SBram Moolenaar int dio_internal; // using internal diff 71e828b762SBram Moolenaar } diffio_T; 72e828b762SBram Moolenaar 73f28dbceaSBram Moolenaar static int diff_buf_idx(buf_T *buf); 74f28dbceaSBram Moolenaar static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp); 75f28dbceaSBram Moolenaar static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after); 76f28dbceaSBram Moolenaar static void diff_check_unchanged(tabpage_T *tp, diff_T *dp); 77f28dbceaSBram Moolenaar static int diff_check_sanity(tabpage_T *tp, diff_T *dp); 78f28dbceaSBram Moolenaar static void diff_redraw(int dofold); 79e828b762SBram Moolenaar static int check_external_diff(diffio_T *diffio); 80e828b762SBram Moolenaar static int diff_file(diffio_T *diffio); 81f28dbceaSBram Moolenaar static int diff_equal_entry(diff_T *dp, int idx1, int idx2); 82f28dbceaSBram Moolenaar static int diff_cmp(char_u *s1, char_u *s2); 83071d4279SBram Moolenaar #ifdef FEAT_FOLDING 84f28dbceaSBram Moolenaar static void diff_fold_update(diff_T *dp, int skip_idx); 85071d4279SBram Moolenaar #endif 86e828b762SBram Moolenaar static void diff_read(int idx_orig, int idx_new, diffout_T *fname); 87f28dbceaSBram Moolenaar static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new); 88f28dbceaSBram Moolenaar static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp); 89e828b762SBram Moolenaar static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); 90e828b762SBram Moolenaar static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new); 91e828b762SBram Moolenaar static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf); 92071d4279SBram Moolenaar 93071d4279SBram Moolenaar #ifndef USE_CR 94071d4279SBram Moolenaar # define tag_fgets vim_fgets 95071d4279SBram Moolenaar #endif 96071d4279SBram 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 { 1312df6dcc5SBram Moolenaar /* When there is no window showing a diff for this buffer, remove 1322df6dcc5SBram 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) 165071d4279SBram 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 176071d4279SBram Moolenaar EMSGN(_("E96: Cannot diff more than %ld 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 26149d7bf13SBram 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; 293071d4279SBram 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 { 308071d4279SBram 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 { 314071d4279SBram 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 { 320071d4279SBram 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 { 329071d4279SBram Moolenaar /* If the change is after the previous diff block and before the next 330071d4279SBram Moolenaar * diff block, thus not touching an existing change, create a new diff 331071d4279SBram 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 357071d4279SBram 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 */ 372071d4279SBram Moolenaar /* compute last line of this change */ 373071d4279SBram Moolenaar last = dp->df_lnum[idx] + dp->df_count[idx] - 1; 374071d4279SBram Moolenaar 375071d4279SBram Moolenaar /* 1. change completely above line1: nothing to do */ 376071d4279SBram Moolenaar if (last >= line1 - 1) 377071d4279SBram Moolenaar { 378071d4279SBram Moolenaar /* 6. change below line2: only adjust for amount_after; also when 379071d4279SBram 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) 383071d4279SBram 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 390071d4279SBram 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 { 398071d4279SBram 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 { 402071d4279SBram Moolenaar /* delete continues in next diff, only do 403071d4279SBram 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 { 415071d4279SBram 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 { 427071d4279SBram Moolenaar /* 2. delete at end of 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 { 432071d4279SBram Moolenaar /* delete continues in next diff, only do 433071d4279SBram 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 { 445071d4279SBram 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 { 462071d4279SBram Moolenaar /* inserted lines somewhere in this diff */ 463071d4279SBram Moolenaar dp->df_count[idx] += inserted; 464071d4279SBram Moolenaar check_unchanged = TRUE; 465071d4279SBram Moolenaar } 466071d4279SBram Moolenaar else 467071d4279SBram Moolenaar /* inserted lines somewhere above this diff */ 468071d4279SBram Moolenaar dp->df_lnum[idx] += inserted; 469071d4279SBram Moolenaar } 470071d4279SBram Moolenaar 471071d4279SBram Moolenaar if (check_unchanged) 472071d4279SBram Moolenaar /* Check if inserted lines are equal, may reduce the 473071d4279SBram Moolenaar * size of the diff. TODO: also check for equal lines 474071d4279SBram Moolenaar * in the middle and perhaps split the block. */ 47549d7bf13SBram Moolenaar diff_check_unchanged(tp, dp); 476071d4279SBram Moolenaar } 477071d4279SBram Moolenaar } 478071d4279SBram Moolenaar 479071d4279SBram 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 { 492071d4279SBram 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 { 502071d4279SBram 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 { 518071d4279SBram 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 { 527071d4279SBram Moolenaar diff_redraw(TRUE); 528071d4279SBram Moolenaar 52949d7bf13SBram Moolenaar /* Need to recompute the scroll binding, may remove or add filler 53049d7bf13SBram Moolenaar * lines (e.g., when adding lines above w_topline). But it's slow when 53149d7bf13SBram Moolenaar * making many changes, postpone until redrawing. */ 53266fa271aSBram Moolenaar diff_need_scrollbind = TRUE; 533071d4279SBram Moolenaar } 53449d7bf13SBram Moolenaar } 535071d4279SBram Moolenaar 536071d4279SBram Moolenaar /* 537071d4279SBram Moolenaar * Allocate a new diff block and link it between "dprev" and "dp". 538071d4279SBram Moolenaar */ 539071d4279SBram Moolenaar static diff_T * 5407454a06eSBram Moolenaar diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) 541071d4279SBram Moolenaar { 542071d4279SBram Moolenaar diff_T *dnew; 543071d4279SBram Moolenaar 544071d4279SBram Moolenaar dnew = (diff_T *)alloc((unsigned)sizeof(diff_T)); 545071d4279SBram Moolenaar if (dnew != NULL) 546071d4279SBram Moolenaar { 547071d4279SBram Moolenaar dnew->df_next = dp; 548071d4279SBram Moolenaar if (dprev == NULL) 54949d7bf13SBram Moolenaar tp->tp_first_diff = dnew; 550071d4279SBram Moolenaar else 551071d4279SBram Moolenaar dprev->df_next = dnew; 552071d4279SBram Moolenaar } 553071d4279SBram Moolenaar return dnew; 554071d4279SBram Moolenaar } 555071d4279SBram Moolenaar 556071d4279SBram Moolenaar /* 557071d4279SBram Moolenaar * Check if the diff block "dp" can be made smaller for lines at the start and 558071d4279SBram Moolenaar * end that are equal. Called after inserting lines. 559071d4279SBram Moolenaar * This may result in a change where all buffers have zero lines, the caller 560071d4279SBram Moolenaar * must take care of removing it. 561071d4279SBram Moolenaar */ 562071d4279SBram Moolenaar static void 5637454a06eSBram Moolenaar diff_check_unchanged(tabpage_T *tp, diff_T *dp) 564071d4279SBram Moolenaar { 565071d4279SBram Moolenaar int i_org; 566071d4279SBram Moolenaar int i_new; 567071d4279SBram Moolenaar int off_org, off_new; 568071d4279SBram Moolenaar char_u *line_org; 569071d4279SBram Moolenaar int dir = FORWARD; 570071d4279SBram Moolenaar 571071d4279SBram Moolenaar /* Find the first buffers, use it as the original, compare the other 572071d4279SBram Moolenaar * buffer lines against this one. */ 573071d4279SBram Moolenaar for (i_org = 0; i_org < DB_COUNT; ++i_org) 57449d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_org] != NULL) 575071d4279SBram Moolenaar break; 576071d4279SBram Moolenaar if (i_org == DB_COUNT) /* safety check */ 577071d4279SBram Moolenaar return; 578071d4279SBram Moolenaar 57949d7bf13SBram Moolenaar if (diff_check_sanity(tp, dp) == FAIL) 580071d4279SBram Moolenaar return; 581071d4279SBram Moolenaar 582071d4279SBram Moolenaar /* First check lines at the top, then at the bottom. */ 583071d4279SBram Moolenaar off_org = 0; 584071d4279SBram Moolenaar off_new = 0; 585071d4279SBram Moolenaar for (;;) 586071d4279SBram Moolenaar { 587071d4279SBram Moolenaar /* Repeat until a line is found which is different or the number of 588071d4279SBram Moolenaar * lines has become zero. */ 589071d4279SBram Moolenaar while (dp->df_count[i_org] > 0) 590071d4279SBram Moolenaar { 591071d4279SBram Moolenaar /* Copy the line, the next ml_get() will invalidate it. */ 592071d4279SBram Moolenaar if (dir == BACKWARD) 593071d4279SBram Moolenaar off_org = dp->df_count[i_org] - 1; 59449d7bf13SBram Moolenaar line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], 595071d4279SBram Moolenaar dp->df_lnum[i_org] + off_org, FALSE)); 596071d4279SBram Moolenaar if (line_org == NULL) 597071d4279SBram Moolenaar return; 598071d4279SBram Moolenaar for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) 599071d4279SBram Moolenaar { 60049d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_new] == NULL) 601071d4279SBram Moolenaar continue; 602071d4279SBram Moolenaar if (dir == BACKWARD) 603071d4279SBram Moolenaar off_new = dp->df_count[i_new] - 1; 604071d4279SBram Moolenaar /* if other buffer doesn't have this line, it was inserted */ 605071d4279SBram Moolenaar if (off_new < 0 || off_new >= dp->df_count[i_new]) 606071d4279SBram Moolenaar break; 60749d7bf13SBram Moolenaar if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], 608071d4279SBram Moolenaar dp->df_lnum[i_new] + off_new, FALSE)) != 0) 609071d4279SBram Moolenaar break; 610071d4279SBram Moolenaar } 611071d4279SBram Moolenaar vim_free(line_org); 612071d4279SBram Moolenaar 613071d4279SBram Moolenaar /* Stop when a line isn't equal in all diff buffers. */ 614071d4279SBram Moolenaar if (i_new != DB_COUNT) 615071d4279SBram Moolenaar break; 616071d4279SBram Moolenaar 617071d4279SBram Moolenaar /* Line matched in all buffers, remove it from the diff. */ 618071d4279SBram Moolenaar for (i_new = i_org; i_new < DB_COUNT; ++i_new) 61949d7bf13SBram Moolenaar if (tp->tp_diffbuf[i_new] != NULL) 620071d4279SBram Moolenaar { 621071d4279SBram Moolenaar if (dir == FORWARD) 622071d4279SBram Moolenaar ++dp->df_lnum[i_new]; 623071d4279SBram Moolenaar --dp->df_count[i_new]; 624071d4279SBram Moolenaar } 625071d4279SBram Moolenaar } 626071d4279SBram Moolenaar if (dir == BACKWARD) 627071d4279SBram Moolenaar break; 628071d4279SBram Moolenaar dir = BACKWARD; 629071d4279SBram Moolenaar } 630071d4279SBram Moolenaar } 631071d4279SBram Moolenaar 632071d4279SBram Moolenaar /* 633071d4279SBram Moolenaar * Check if a diff block doesn't contain invalid line numbers. 634071d4279SBram Moolenaar * This can happen when the diff program returns invalid results. 635071d4279SBram Moolenaar */ 636071d4279SBram Moolenaar static int 6377454a06eSBram Moolenaar diff_check_sanity(tabpage_T *tp, diff_T *dp) 638071d4279SBram Moolenaar { 639071d4279SBram Moolenaar int i; 640071d4279SBram Moolenaar 641071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 64249d7bf13SBram Moolenaar if (tp->tp_diffbuf[i] != NULL) 643071d4279SBram Moolenaar if (dp->df_lnum[i] + dp->df_count[i] - 1 64449d7bf13SBram Moolenaar > tp->tp_diffbuf[i]->b_ml.ml_line_count) 645071d4279SBram Moolenaar return FAIL; 646071d4279SBram Moolenaar return OK; 647071d4279SBram Moolenaar } 648071d4279SBram Moolenaar 649071d4279SBram Moolenaar /* 65049d7bf13SBram Moolenaar * Mark all diff buffers in the current tab page for redraw. 651071d4279SBram Moolenaar */ 652071d4279SBram Moolenaar static void 6537454a06eSBram Moolenaar diff_redraw( 654e3521d9cSBram Moolenaar int dofold) // also recompute the folds 655071d4279SBram Moolenaar { 656071d4279SBram Moolenaar win_T *wp; 657071d4279SBram Moolenaar int n; 658071d4279SBram Moolenaar 65929323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 660071d4279SBram Moolenaar if (wp->w_p_diff) 661071d4279SBram Moolenaar { 66260f8377eSBram Moolenaar redraw_win_later(wp, SOME_VALID); 663071d4279SBram Moolenaar #ifdef FEAT_FOLDING 664071d4279SBram Moolenaar if (dofold && foldmethodIsDiff(wp)) 665071d4279SBram Moolenaar foldUpdateAll(wp); 666071d4279SBram Moolenaar #endif 667071d4279SBram Moolenaar /* A change may have made filler lines invalid, need to take care 668071d4279SBram Moolenaar * of that for other windows. */ 669071d4279SBram Moolenaar n = diff_check(wp, wp->w_topline); 670a80888d2SBram Moolenaar if ((wp != curwin && wp->w_topfill > 0) || n > 0) 671a80888d2SBram Moolenaar { 672071d4279SBram Moolenaar if (wp->w_topfill > n) 673071d4279SBram Moolenaar wp->w_topfill = (n < 0 ? 0 : n); 674a80888d2SBram Moolenaar else if (n > 0 && n > wp->w_topfill) 675a80888d2SBram Moolenaar wp->w_topfill = n; 676846a2ff5SBram Moolenaar check_topfill(wp, FALSE); 677071d4279SBram Moolenaar } 678071d4279SBram Moolenaar } 679071d4279SBram Moolenaar } 680071d4279SBram Moolenaar 681e828b762SBram Moolenaar static void 682e828b762SBram Moolenaar clear_diffin(diffin_T *din) 683e828b762SBram Moolenaar { 684e828b762SBram Moolenaar if (din->din_fname == NULL) 685e828b762SBram Moolenaar { 686e828b762SBram Moolenaar vim_free(din->din_mmfile.ptr); 687e828b762SBram Moolenaar din->din_mmfile.ptr = NULL; 688e828b762SBram Moolenaar } 689e828b762SBram Moolenaar else 690e828b762SBram Moolenaar mch_remove(din->din_fname); 691e828b762SBram Moolenaar } 692e828b762SBram Moolenaar 693e828b762SBram Moolenaar static void 694e828b762SBram Moolenaar clear_diffout(diffout_T *dout) 695e828b762SBram Moolenaar { 696e828b762SBram Moolenaar if (dout->dout_fname == NULL) 697e828b762SBram Moolenaar ga_clear_strings(&dout->dout_ga); 698e828b762SBram Moolenaar else 699e828b762SBram Moolenaar mch_remove(dout->dout_fname); 700e828b762SBram Moolenaar } 701e828b762SBram Moolenaar 702071d4279SBram Moolenaar /* 703e828b762SBram Moolenaar * Write buffer "buf" to a memory buffer. 704e828b762SBram Moolenaar * Return FAIL for failure. 705071d4279SBram Moolenaar */ 706071d4279SBram Moolenaar static int 707e828b762SBram Moolenaar diff_write_buffer(buf_T *buf, diffin_T *din) 708e828b762SBram Moolenaar { 709e828b762SBram Moolenaar linenr_T lnum; 710e828b762SBram Moolenaar char_u *s; 711e828b762SBram Moolenaar long len = 0; 712e828b762SBram Moolenaar char_u *ptr; 713e828b762SBram Moolenaar 714e828b762SBram Moolenaar // xdiff requires one big block of memory with all the text. 715e828b762SBram Moolenaar for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) 7166e272accSBram Moolenaar len += (long)STRLEN(ml_get_buf(buf, lnum, FALSE)) + 1; 717e828b762SBram Moolenaar ptr = lalloc(len, TRUE); 718e828b762SBram Moolenaar if (ptr == NULL) 719e828b762SBram Moolenaar { 720e828b762SBram Moolenaar // Allocating memory failed. This can happen, because we try to read 721e828b762SBram Moolenaar // the whole buffer text into memory. Set the failed flag, the diff 722e828b762SBram Moolenaar // will be retried with external diff. The flag is never reset. 723e828b762SBram Moolenaar buf->b_diff_failed = TRUE; 724e828b762SBram Moolenaar if (p_verbose > 0) 725e828b762SBram Moolenaar { 726e828b762SBram Moolenaar verbose_enter(); 727e828b762SBram Moolenaar smsg((char_u *) 728e828b762SBram Moolenaar _("Not enough memory to use internal diff for buffer \"%s\""), 729e828b762SBram Moolenaar buf->b_fname); 730e828b762SBram Moolenaar verbose_leave(); 731e828b762SBram Moolenaar } 732e828b762SBram Moolenaar return FAIL; 733e828b762SBram Moolenaar } 734e828b762SBram Moolenaar din->din_mmfile.ptr = (char *)ptr; 735e828b762SBram Moolenaar din->din_mmfile.size = len; 736e828b762SBram Moolenaar 737e828b762SBram Moolenaar len = 0; 738e828b762SBram Moolenaar for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) 739e828b762SBram Moolenaar { 740e828b762SBram Moolenaar for (s = ml_get_buf(buf, lnum, FALSE); *s != NUL; ) 741e828b762SBram Moolenaar { 742e828b762SBram Moolenaar if (diff_flags & DIFF_ICASE) 743e828b762SBram Moolenaar { 744e828b762SBram Moolenaar int c; 745e828b762SBram Moolenaar 746e828b762SBram Moolenaar // xdiff doesn't support ignoring case, fold-case the text. 747e828b762SBram Moolenaar #ifdef FEAT_MBYTE 748e828b762SBram Moolenaar int orig_len; 749e828b762SBram Moolenaar char_u cbuf[MB_MAXBYTES + 1]; 750e828b762SBram Moolenaar 751e828b762SBram Moolenaar c = PTR2CHAR(s); 752e828b762SBram Moolenaar c = enc_utf8 ? utf_fold(c) : MB_TOLOWER(c); 753e828b762SBram Moolenaar orig_len = MB_PTR2LEN(s); 754e828b762SBram Moolenaar if (mb_char2bytes(c, cbuf) != orig_len) 755e828b762SBram Moolenaar // TODO: handle byte length difference 756e828b762SBram Moolenaar mch_memmove(ptr + len, s, orig_len); 757e828b762SBram Moolenaar else 758e828b762SBram Moolenaar mch_memmove(ptr + len, cbuf, orig_len); 759e828b762SBram Moolenaar 760e828b762SBram Moolenaar s += orig_len; 761e828b762SBram Moolenaar len += orig_len; 762e828b762SBram Moolenaar #else 763e828b762SBram Moolenaar c = *s++; 764e828b762SBram Moolenaar ptr[len++] = TOLOWER_LOC(c); 765e828b762SBram Moolenaar #endif 766e828b762SBram Moolenaar } 767e828b762SBram Moolenaar else 768e828b762SBram Moolenaar ptr[len++] = *s++; 769e828b762SBram Moolenaar } 770e828b762SBram Moolenaar ptr[len++] = NL; 771e828b762SBram Moolenaar } 772e828b762SBram Moolenaar return OK; 773e828b762SBram Moolenaar } 774e828b762SBram Moolenaar 775e828b762SBram Moolenaar /* 776e828b762SBram Moolenaar * Write buffer "buf" to file or memory buffer. 777e828b762SBram Moolenaar * Return FAIL for failure. 778e828b762SBram Moolenaar */ 779e828b762SBram Moolenaar static int 780e828b762SBram Moolenaar diff_write(buf_T *buf, diffin_T *din) 781071d4279SBram Moolenaar { 782071d4279SBram Moolenaar int r; 783071d4279SBram Moolenaar char_u *save_ff; 784071d4279SBram Moolenaar 785e828b762SBram Moolenaar if (din->din_fname == NULL) 786e828b762SBram Moolenaar return diff_write_buffer(buf, din); 787e828b762SBram Moolenaar 788e828b762SBram Moolenaar // Always use 'fileformat' set to "unix". 789071d4279SBram Moolenaar save_ff = buf->b_p_ff; 790071d4279SBram Moolenaar buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); 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); 794071d4279SBram Moolenaar free_string_option(buf->b_p_ff); 795071d4279SBram Moolenaar buf->b_p_ff = save_ff; 796071d4279SBram Moolenaar return r; 797071d4279SBram Moolenaar } 798071d4279SBram Moolenaar 799071d4279SBram Moolenaar /* 800e828b762SBram Moolenaar * Update the diffs for all buffers involved. 801e828b762SBram Moolenaar */ 802e828b762SBram Moolenaar static void 803e828b762SBram Moolenaar diff_try_update( 804e828b762SBram Moolenaar diffio_T *dio, 805e828b762SBram Moolenaar int idx_orig, 806e828b762SBram Moolenaar exarg_T *eap) // "eap" can be NULL 807e828b762SBram Moolenaar { 808e828b762SBram Moolenaar buf_T *buf; 809e828b762SBram Moolenaar int idx_new; 810e828b762SBram Moolenaar 811e828b762SBram Moolenaar if (dio->dio_internal) 812e828b762SBram Moolenaar { 813e828b762SBram Moolenaar ga_init2(&dio->dio_diff.dout_ga, sizeof(char *), 1000); 814e828b762SBram Moolenaar } 815e828b762SBram Moolenaar else 816e828b762SBram Moolenaar { 817e828b762SBram Moolenaar // We need three temp file names. 818e828b762SBram Moolenaar dio->dio_orig.din_fname = vim_tempname('o', TRUE); 819e828b762SBram Moolenaar dio->dio_new.din_fname = vim_tempname('n', TRUE); 820e828b762SBram Moolenaar dio->dio_diff.dout_fname = vim_tempname('d', TRUE); 821e828b762SBram Moolenaar if (dio->dio_orig.din_fname == NULL 822e828b762SBram Moolenaar || dio->dio_new.din_fname == NULL 823e828b762SBram Moolenaar || dio->dio_diff.dout_fname == NULL) 824e828b762SBram Moolenaar goto theend; 825e828b762SBram Moolenaar } 826e828b762SBram Moolenaar 827e828b762SBram Moolenaar // Check external diff is actually working. 828e828b762SBram Moolenaar if (!dio->dio_internal && check_external_diff(dio) == FAIL) 829e828b762SBram Moolenaar goto theend; 830e828b762SBram Moolenaar 831e828b762SBram Moolenaar // :diffupdate! 832e828b762SBram Moolenaar if (eap != NULL && eap->forceit) 833e828b762SBram Moolenaar for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) 834e828b762SBram Moolenaar { 835e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_new]; 836e828b762SBram Moolenaar if (buf_valid(buf)) 837e828b762SBram Moolenaar buf_check_timestamp(buf, FALSE); 838e828b762SBram Moolenaar } 839e828b762SBram Moolenaar 840e828b762SBram Moolenaar // Write the first buffer to a tempfile or mmfile_t. 841e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_orig]; 842e828b762SBram Moolenaar if (diff_write(buf, &dio->dio_orig) == FAIL) 843e828b762SBram Moolenaar goto theend; 844e828b762SBram Moolenaar 845e828b762SBram Moolenaar // Make a difference between the first buffer and every other. 846e828b762SBram Moolenaar for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 847e828b762SBram Moolenaar { 848e828b762SBram Moolenaar buf = curtab->tp_diffbuf[idx_new]; 849e828b762SBram Moolenaar if (buf == NULL || buf->b_ml.ml_mfp == NULL) 850e828b762SBram Moolenaar continue; // skip buffer that isn't loaded 851e828b762SBram Moolenaar 852e828b762SBram Moolenaar // Write the other buffer and diff with the first one. 853e828b762SBram Moolenaar if (diff_write(buf, &dio->dio_new) == FAIL) 854e828b762SBram Moolenaar continue; 855e828b762SBram Moolenaar if (diff_file(dio) == FAIL) 856e828b762SBram Moolenaar continue; 857e828b762SBram Moolenaar 858e828b762SBram Moolenaar // Read the diff output and add each entry to the diff list. 859e828b762SBram Moolenaar diff_read(idx_orig, idx_new, &dio->dio_diff); 860e828b762SBram Moolenaar 861e828b762SBram Moolenaar clear_diffin(&dio->dio_new); 862e828b762SBram Moolenaar clear_diffout(&dio->dio_diff); 863e828b762SBram Moolenaar } 864e828b762SBram Moolenaar clear_diffin(&dio->dio_orig); 865e828b762SBram Moolenaar 866e828b762SBram Moolenaar theend: 867e828b762SBram Moolenaar vim_free(dio->dio_orig.din_fname); 868e828b762SBram Moolenaar vim_free(dio->dio_new.din_fname); 869e828b762SBram Moolenaar vim_free(dio->dio_diff.dout_fname); 870e828b762SBram Moolenaar } 871e828b762SBram Moolenaar 872e828b762SBram Moolenaar /* 873e828b762SBram Moolenaar * Return TRUE if the options are set to use the internal diff library. 874e828b762SBram Moolenaar * Note that if the internal diff failed for one of the buffers, the external 875e828b762SBram Moolenaar * diff will be used anyway. 876e828b762SBram Moolenaar */ 877e3521d9cSBram Moolenaar int 878e828b762SBram Moolenaar diff_internal(void) 879e828b762SBram Moolenaar { 880e828b762SBram Moolenaar return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; 881e828b762SBram Moolenaar } 882e828b762SBram Moolenaar 883e828b762SBram Moolenaar /* 884e828b762SBram Moolenaar * Return TRUE if the internal diff failed for one of the diff buffers. 885e828b762SBram Moolenaar */ 886e828b762SBram Moolenaar static int 887e828b762SBram Moolenaar diff_internal_failed(void) 888e828b762SBram Moolenaar { 889e828b762SBram Moolenaar int idx; 890e828b762SBram Moolenaar 891e828b762SBram Moolenaar // Only need to do something when there is another buffer. 892e828b762SBram Moolenaar for (idx = 0; idx < DB_COUNT; ++idx) 893e828b762SBram Moolenaar if (curtab->tp_diffbuf[idx] != NULL 894e828b762SBram Moolenaar && curtab->tp_diffbuf[idx]->b_diff_failed) 895e828b762SBram Moolenaar return TRUE; 896e828b762SBram Moolenaar return FALSE; 897e828b762SBram Moolenaar } 898e828b762SBram Moolenaar 899e828b762SBram Moolenaar /* 900071d4279SBram Moolenaar * Completely update the diffs for the buffers involved. 901e3521d9cSBram Moolenaar * When using the external "diff" command the buffers are written to a file, 902e3521d9cSBram Moolenaar * also for unmodified buffers (the file could have been produced by 903e3521d9cSBram Moolenaar * autocommands, e.g. the netrw plugin). 904071d4279SBram Moolenaar */ 905071d4279SBram Moolenaar void 906e828b762SBram Moolenaar ex_diffupdate(exarg_T *eap) // "eap" can be NULL 907071d4279SBram Moolenaar { 908071d4279SBram Moolenaar int idx_orig; 909071d4279SBram Moolenaar int idx_new; 910e828b762SBram Moolenaar diffio_T diffio; 911198fa066SBram Moolenaar int had_diffs = curtab->tp_first_diff != NULL; 912071d4279SBram Moolenaar 913d2b58c0aSBram Moolenaar if (diff_busy) 914d2b58c0aSBram Moolenaar { 915d2b58c0aSBram Moolenaar diff_need_update = TRUE; 916d2b58c0aSBram Moolenaar return; 917d2b58c0aSBram Moolenaar } 918d2b58c0aSBram Moolenaar 919e828b762SBram Moolenaar // Delete all diffblocks. 92049d7bf13SBram Moolenaar diff_clear(curtab); 92149d7bf13SBram Moolenaar curtab->tp_diff_invalid = FALSE; 922071d4279SBram Moolenaar 923e828b762SBram Moolenaar // Use the first buffer as the original text. 924071d4279SBram Moolenaar for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) 92549d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_orig] != NULL) 926071d4279SBram Moolenaar break; 927071d4279SBram Moolenaar if (idx_orig == DB_COUNT) 928198fa066SBram Moolenaar goto theend; 929071d4279SBram Moolenaar 930e828b762SBram Moolenaar // Only need to do something when there is another buffer. 931071d4279SBram Moolenaar for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 93249d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_new] != NULL) 933071d4279SBram Moolenaar break; 934071d4279SBram Moolenaar if (idx_new == DB_COUNT) 935198fa066SBram Moolenaar goto theend; 936071d4279SBram Moolenaar 937e828b762SBram Moolenaar // Only use the internal method if it did not fail for one of the buffers. 938e828b762SBram Moolenaar vim_memset(&diffio, 0, sizeof(diffio)); 939e828b762SBram Moolenaar diffio.dio_internal = diff_internal() && !diff_internal_failed(); 940e828b762SBram Moolenaar 941e828b762SBram Moolenaar diff_try_update(&diffio, idx_orig, eap); 942e828b762SBram Moolenaar if (diffio.dio_internal && diff_internal_failed()) 943e828b762SBram Moolenaar { 944e828b762SBram Moolenaar // Internal diff failed, use external diff instead. 945e828b762SBram Moolenaar vim_memset(&diffio, 0, sizeof(diffio)); 946e828b762SBram Moolenaar diff_try_update(&diffio, idx_orig, eap); 947e828b762SBram Moolenaar } 948e828b762SBram Moolenaar 949e828b762SBram Moolenaar // force updating cursor position on screen 950e828b762SBram Moolenaar curwin->w_valid_cursor.lnum = 0; 951e828b762SBram Moolenaar 952198fa066SBram Moolenaar theend: 953198fa066SBram Moolenaar // A redraw is needed if there were diffs and they were cleared, or there 954198fa066SBram Moolenaar // are diffs now, which means they got updated. 955198fa066SBram Moolenaar if (had_diffs || curtab->tp_first_diff != NULL) 956198fa066SBram Moolenaar { 957e828b762SBram Moolenaar diff_redraw(TRUE); 958e8fa05b5SBram Moolenaar apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); 959e828b762SBram Moolenaar } 960198fa066SBram Moolenaar } 961071d4279SBram Moolenaar 962071d4279SBram Moolenaar /* 963071d4279SBram Moolenaar * Do a quick test if "diff" really works. Otherwise it looks like there 964071d4279SBram Moolenaar * are no differences. Can't use the return value, it's non-zero when 965071d4279SBram Moolenaar * there are differences. 966071d4279SBram Moolenaar */ 967e828b762SBram Moolenaar static int 968e828b762SBram Moolenaar check_external_diff(diffio_T *diffio) 969e828b762SBram Moolenaar { 970e828b762SBram Moolenaar FILE *fd; 971e828b762SBram Moolenaar int ok; 972e828b762SBram Moolenaar int io_error = FALSE; 973e828b762SBram Moolenaar 974e828b762SBram Moolenaar // May try twice, first with "-a" and then without. 975071d4279SBram Moolenaar for (;;) 976071d4279SBram Moolenaar { 977071d4279SBram Moolenaar ok = FALSE; 978e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w"); 979fe86f2d7SBram Moolenaar if (fd == NULL) 980fe86f2d7SBram Moolenaar io_error = TRUE; 981fe86f2d7SBram Moolenaar else 982071d4279SBram Moolenaar { 983fe86f2d7SBram Moolenaar if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) 984fe86f2d7SBram Moolenaar io_error = TRUE; 985071d4279SBram Moolenaar fclose(fd); 986e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_new.din_fname, "w"); 987fe86f2d7SBram Moolenaar if (fd == NULL) 988fe86f2d7SBram Moolenaar io_error = TRUE; 989fe86f2d7SBram Moolenaar else 990071d4279SBram Moolenaar { 991fe86f2d7SBram Moolenaar if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) 992fe86f2d7SBram Moolenaar io_error = TRUE; 993071d4279SBram Moolenaar fclose(fd); 994e828b762SBram Moolenaar fd = NULL; 995e828b762SBram Moolenaar if (diff_file(diffio) == OK) 996e828b762SBram Moolenaar fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r"); 997fe86f2d7SBram Moolenaar if (fd == NULL) 998fe86f2d7SBram Moolenaar io_error = TRUE; 999fe86f2d7SBram Moolenaar else 1000071d4279SBram Moolenaar { 1001071d4279SBram Moolenaar char_u linebuf[LBUFLEN]; 1002071d4279SBram Moolenaar 1003071d4279SBram Moolenaar for (;;) 1004071d4279SBram Moolenaar { 1005071d4279SBram Moolenaar /* There must be a line that contains "1c1". */ 1006071d4279SBram Moolenaar if (tag_fgets(linebuf, LBUFLEN, fd)) 1007071d4279SBram Moolenaar break; 1008071d4279SBram Moolenaar if (STRNCMP(linebuf, "1c1", 3) == 0) 1009071d4279SBram Moolenaar ok = TRUE; 1010071d4279SBram Moolenaar } 1011071d4279SBram Moolenaar fclose(fd); 1012071d4279SBram Moolenaar } 1013e828b762SBram Moolenaar mch_remove(diffio->dio_diff.dout_fname); 1014e828b762SBram Moolenaar mch_remove(diffio->dio_new.din_fname); 1015071d4279SBram Moolenaar } 1016e828b762SBram Moolenaar mch_remove(diffio->dio_orig.din_fname); 1017071d4279SBram Moolenaar } 1018071d4279SBram Moolenaar 1019071d4279SBram Moolenaar #ifdef FEAT_EVAL 1020071d4279SBram Moolenaar /* When using 'diffexpr' break here. */ 1021071d4279SBram Moolenaar if (*p_dex != NUL) 1022071d4279SBram Moolenaar break; 1023071d4279SBram Moolenaar #endif 1024071d4279SBram Moolenaar 102548e330afSBram Moolenaar #if defined(MSWIN) 1026071d4279SBram Moolenaar /* If the "-a" argument works, also check if "--binary" works. */ 1027071d4279SBram Moolenaar if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE) 1028071d4279SBram Moolenaar { 1029071d4279SBram Moolenaar diff_a_works = TRUE; 1030071d4279SBram Moolenaar diff_bin_works = TRUE; 1031071d4279SBram Moolenaar continue; 1032071d4279SBram Moolenaar } 1033071d4279SBram Moolenaar if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE) 1034071d4279SBram Moolenaar { 1035071d4279SBram Moolenaar /* Tried --binary, but it failed. "-a" works though. */ 1036071d4279SBram Moolenaar diff_bin_works = FALSE; 1037071d4279SBram Moolenaar ok = TRUE; 1038071d4279SBram Moolenaar } 1039071d4279SBram Moolenaar #endif 1040071d4279SBram Moolenaar 1041071d4279SBram Moolenaar /* If we checked if "-a" works already, break here. */ 1042071d4279SBram Moolenaar if (diff_a_works != MAYBE) 1043071d4279SBram Moolenaar break; 1044071d4279SBram Moolenaar diff_a_works = ok; 1045071d4279SBram Moolenaar 1046071d4279SBram Moolenaar /* If "-a" works break here, otherwise retry without "-a". */ 1047071d4279SBram Moolenaar if (ok) 1048071d4279SBram Moolenaar break; 1049071d4279SBram Moolenaar } 1050071d4279SBram Moolenaar if (!ok) 1051071d4279SBram Moolenaar { 1052fe86f2d7SBram Moolenaar if (io_error) 1053fe86f2d7SBram Moolenaar EMSG(_("E810: Cannot read or write temp files")); 1054071d4279SBram Moolenaar EMSG(_("E97: Cannot create diffs")); 1055071d4279SBram Moolenaar diff_a_works = MAYBE; 105648e330afSBram Moolenaar #if defined(MSWIN) 1057071d4279SBram Moolenaar diff_bin_works = MAYBE; 1058071d4279SBram Moolenaar #endif 1059e828b762SBram Moolenaar return FAIL; 1060e828b762SBram Moolenaar } 1061e828b762SBram Moolenaar return OK; 1062071d4279SBram Moolenaar } 1063071d4279SBram Moolenaar 1064e828b762SBram Moolenaar /* 1065e828b762SBram Moolenaar * Invoke the xdiff function. 1066e828b762SBram Moolenaar */ 1067e828b762SBram Moolenaar static int 1068e828b762SBram Moolenaar diff_file_internal(diffio_T *diffio) 1069bd1d5608SBram Moolenaar { 1070e828b762SBram Moolenaar xpparam_t param; 1071e828b762SBram Moolenaar xdemitconf_t emit_cfg; 1072e828b762SBram Moolenaar xdemitcb_t emit_cb; 1073bd1d5608SBram Moolenaar 1074e828b762SBram Moolenaar vim_memset(¶m, 0, sizeof(param)); 1075e828b762SBram Moolenaar vim_memset(&emit_cfg, 0, sizeof(emit_cfg)); 1076e828b762SBram Moolenaar vim_memset(&emit_cb, 0, sizeof(emit_cb)); 1077071d4279SBram Moolenaar 1078e828b762SBram Moolenaar param.flags = diff_algorithm; 1079e828b762SBram Moolenaar 1080e828b762SBram Moolenaar if (diff_flags & DIFF_IWHITE) 1081e828b762SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; 1082785fc656SBram Moolenaar if (diff_flags & DIFF_IWHITEALL) 1083785fc656SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE; 1084785fc656SBram Moolenaar if (diff_flags & DIFF_IWHITEEOL) 1085785fc656SBram Moolenaar param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; 1086785fc656SBram Moolenaar if (diff_flags & DIFF_IBLANK) 1087785fc656SBram Moolenaar param.flags |= XDF_IGNORE_BLANK_LINES; 1088e828b762SBram Moolenaar 1089e828b762SBram Moolenaar emit_cfg.ctxlen = 0; // don't need any diff_context here 1090e828b762SBram Moolenaar emit_cb.priv = &diffio->dio_diff; 1091e828b762SBram Moolenaar emit_cb.outf = xdiff_out; 1092e828b762SBram Moolenaar if (xdl_diff(&diffio->dio_orig.din_mmfile, 1093e828b762SBram Moolenaar &diffio->dio_new.din_mmfile, 1094e828b762SBram Moolenaar ¶m, &emit_cfg, &emit_cb) < 0) 1095071d4279SBram Moolenaar { 1096e828b762SBram Moolenaar EMSG(_("E960: Problem creating the internal diff")); 1097e828b762SBram Moolenaar return FAIL; 1098071d4279SBram Moolenaar } 1099e828b762SBram Moolenaar return OK; 1100071d4279SBram Moolenaar } 1101071d4279SBram Moolenaar 1102071d4279SBram Moolenaar /* 1103071d4279SBram Moolenaar * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". 1104e828b762SBram Moolenaar * return OK or FAIL; 1105071d4279SBram Moolenaar */ 1106e828b762SBram Moolenaar static int 1107e828b762SBram Moolenaar diff_file(diffio_T *dio) 1108071d4279SBram Moolenaar { 1109071d4279SBram Moolenaar char_u *cmd; 11105fd0ca70SBram Moolenaar size_t len; 1111e828b762SBram Moolenaar char_u *tmp_orig = dio->dio_orig.din_fname; 1112e828b762SBram Moolenaar char_u *tmp_new = dio->dio_new.din_fname; 1113e828b762SBram Moolenaar char_u *tmp_diff = dio->dio_diff.dout_fname; 1114071d4279SBram Moolenaar 1115071d4279SBram Moolenaar #ifdef FEAT_EVAL 1116071d4279SBram Moolenaar if (*p_dex != NUL) 1117e828b762SBram Moolenaar { 1118e828b762SBram Moolenaar // Use 'diffexpr' to generate the diff file. 1119071d4279SBram Moolenaar eval_diff(tmp_orig, tmp_new, tmp_diff); 1120e828b762SBram Moolenaar return OK; 1121e828b762SBram Moolenaar } 1122071d4279SBram Moolenaar else 1123071d4279SBram Moolenaar #endif 1124e828b762SBram Moolenaar // Use xdiff for generating the diff. 1125e828b762SBram Moolenaar if (dio->dio_internal) 1126e828b762SBram Moolenaar { 1127e828b762SBram Moolenaar return diff_file_internal(dio); 1128e828b762SBram Moolenaar } 1129e828b762SBram Moolenaar else 1130071d4279SBram Moolenaar { 11315fd0ca70SBram Moolenaar len = STRLEN(tmp_orig) + STRLEN(tmp_new) 11325fd0ca70SBram Moolenaar + STRLEN(tmp_diff) + STRLEN(p_srr) + 27; 11335fd0ca70SBram Moolenaar cmd = alloc((unsigned)len); 1134e828b762SBram Moolenaar if (cmd == NULL) 1135e828b762SBram Moolenaar return FAIL; 1136e828b762SBram Moolenaar 1137e828b762SBram Moolenaar // We don't want $DIFF_OPTIONS to get in the way. 113895fb60acSBram Moolenaar if (getenv("DIFF_OPTIONS")) 113995fb60acSBram Moolenaar vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)""); 114095fb60acSBram Moolenaar 1141e828b762SBram Moolenaar // Build the diff command and execute it. Always use -a, binary 1142e828b762SBram Moolenaar // differences are of no use. Ignore errors, diff returns 1143e828b762SBram Moolenaar // non-zero when differences have been found. 1144785fc656SBram Moolenaar vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s", 1145071d4279SBram Moolenaar diff_a_works == FALSE ? "" : "-a ", 114648e330afSBram Moolenaar #if defined(MSWIN) 1147071d4279SBram Moolenaar diff_bin_works == TRUE ? "--binary " : "", 1148071d4279SBram Moolenaar #else 1149071d4279SBram Moolenaar "", 1150071d4279SBram Moolenaar #endif 1151071d4279SBram Moolenaar (diff_flags & DIFF_IWHITE) ? "-b " : "", 1152785fc656SBram Moolenaar (diff_flags & DIFF_IWHITEALL) ? "-w " : "", 1153785fc656SBram Moolenaar (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", 1154785fc656SBram Moolenaar (diff_flags & DIFF_IBLANK) ? "-B " : "", 1155071d4279SBram Moolenaar (diff_flags & DIFF_ICASE) ? "-i " : "", 1156071d4279SBram Moolenaar tmp_orig, tmp_new); 11575fd0ca70SBram Moolenaar append_redir(cmd, (int)len, p_srr, tmp_diff); 1158e828b762SBram Moolenaar block_autocmds(); // avoid ShellCmdPost stuff 1159071d4279SBram Moolenaar (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT); 116078ab331eSBram Moolenaar unblock_autocmds(); 1161071d4279SBram Moolenaar vim_free(cmd); 1162e828b762SBram Moolenaar return OK; 1163071d4279SBram Moolenaar } 1164071d4279SBram Moolenaar } 1165071d4279SBram Moolenaar 1166071d4279SBram Moolenaar /* 1167071d4279SBram Moolenaar * Create a new version of a file from the current buffer and a diff file. 1168071d4279SBram Moolenaar * The buffer is written to a file, also for unmodified buffers (the file 1169071d4279SBram Moolenaar * could have been produced by autocommands, e.g. the netrw plugin). 1170071d4279SBram Moolenaar */ 1171071d4279SBram Moolenaar void 11727454a06eSBram Moolenaar ex_diffpatch(exarg_T *eap) 1173071d4279SBram Moolenaar { 1174071d4279SBram Moolenaar char_u *tmp_orig; /* name of original temp file */ 1175071d4279SBram Moolenaar char_u *tmp_new; /* name of patched temp file */ 1176071d4279SBram Moolenaar char_u *buf = NULL; 11775fd0ca70SBram Moolenaar size_t buflen; 1178071d4279SBram Moolenaar win_T *old_curwin = curwin; 1179071d4279SBram Moolenaar char_u *newname = NULL; /* name of patched file buffer */ 1180071d4279SBram Moolenaar #ifdef UNIX 1181071d4279SBram Moolenaar char_u dirbuf[MAXPATHL]; 1182071d4279SBram Moolenaar char_u *fullname = NULL; 1183071d4279SBram Moolenaar #endif 1184071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1185071d4279SBram Moolenaar char_u *browseFile = NULL; 1186071d4279SBram Moolenaar int browse_flag = cmdmod.browse; 1187071d4279SBram Moolenaar #endif 11888767f52fSBram Moolenaar stat_T st; 1189a95ab321SBram Moolenaar char_u *esc_name = NULL; 1190071d4279SBram Moolenaar 1191071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1192071d4279SBram Moolenaar if (cmdmod.browse) 1193071d4279SBram Moolenaar { 11947171abeaSBram Moolenaar browseFile = do_browse(0, (char_u *)_("Patch file"), 1195c36651b4SBram Moolenaar eap->arg, NULL, NULL, 1196c36651b4SBram Moolenaar (char_u *)_(BROWSE_FILTER_ALL_FILES), NULL); 1197071d4279SBram Moolenaar if (browseFile == NULL) 1198071d4279SBram Moolenaar return; /* operation cancelled */ 1199071d4279SBram Moolenaar eap->arg = browseFile; 1200071d4279SBram Moolenaar cmdmod.browse = FALSE; /* don't let do_ecmd() browse again */ 1201071d4279SBram Moolenaar } 1202071d4279SBram Moolenaar #endif 1203071d4279SBram Moolenaar 1204071d4279SBram Moolenaar /* We need two temp file names. */ 1205e5c421cfSBram Moolenaar tmp_orig = vim_tempname('o', FALSE); 1206e5c421cfSBram Moolenaar tmp_new = vim_tempname('n', FALSE); 1207071d4279SBram Moolenaar if (tmp_orig == NULL || tmp_new == NULL) 1208071d4279SBram Moolenaar goto theend; 1209071d4279SBram Moolenaar 1210071d4279SBram Moolenaar /* Write the current buffer to "tmp_orig". */ 1211071d4279SBram Moolenaar if (buf_write(curbuf, tmp_orig, NULL, 1212071d4279SBram Moolenaar (linenr_T)1, curbuf->b_ml.ml_line_count, 1213071d4279SBram Moolenaar NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) 1214071d4279SBram Moolenaar goto theend; 1215071d4279SBram Moolenaar 1216071d4279SBram Moolenaar #ifdef UNIX 1217071d4279SBram Moolenaar /* Get the absolute path of the patchfile, changing directory below. */ 1218071d4279SBram Moolenaar fullname = FullName_save(eap->arg, FALSE); 1219071d4279SBram Moolenaar #endif 1220a95ab321SBram Moolenaar esc_name = vim_strsave_shellescape( 1221071d4279SBram Moolenaar # ifdef UNIX 1222a95ab321SBram Moolenaar fullname != NULL ? fullname : 1223071d4279SBram Moolenaar # endif 1224a95ab321SBram Moolenaar eap->arg, TRUE, TRUE); 1225a95ab321SBram Moolenaar if (esc_name == NULL) 1226a95ab321SBram Moolenaar goto theend; 1227a95ab321SBram Moolenaar buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16; 12285fd0ca70SBram Moolenaar buf = alloc((unsigned)buflen); 1229071d4279SBram Moolenaar if (buf == NULL) 1230071d4279SBram Moolenaar goto theend; 1231071d4279SBram Moolenaar 1232071d4279SBram Moolenaar #ifdef UNIX 12335cc6a6e7SBram Moolenaar /* Temporarily chdir to /tmp, to avoid patching files in the current 1234071d4279SBram Moolenaar * directory when the patch file contains more than one patch. When we 1235071d4279SBram Moolenaar * have our own temp dir use that instead, it will be cleaned up when we 1236071d4279SBram Moolenaar * exit (any .rej files created). Don't change directory if we can't 1237071d4279SBram Moolenaar * return to the current. */ 1238071d4279SBram Moolenaar if (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0) 1239071d4279SBram Moolenaar dirbuf[0] = NUL; 1240071d4279SBram Moolenaar else 1241071d4279SBram Moolenaar { 1242071d4279SBram Moolenaar # ifdef TEMPDIRNAMES 1243071d4279SBram Moolenaar if (vim_tempdir != NULL) 124442335f50SBram Moolenaar vim_ignored = mch_chdir((char *)vim_tempdir); 1245071d4279SBram Moolenaar else 1246071d4279SBram Moolenaar # endif 124742335f50SBram Moolenaar vim_ignored = mch_chdir("/tmp"); 1248071d4279SBram Moolenaar shorten_fnames(TRUE); 1249071d4279SBram Moolenaar } 1250071d4279SBram Moolenaar #endif 1251071d4279SBram Moolenaar 1252071d4279SBram Moolenaar #ifdef FEAT_EVAL 1253071d4279SBram Moolenaar if (*p_pex != NUL) 1254071d4279SBram Moolenaar /* Use 'patchexpr' to generate the new file. */ 1255071d4279SBram Moolenaar eval_patch(tmp_orig, 1256071d4279SBram Moolenaar # ifdef UNIX 1257071d4279SBram Moolenaar fullname != NULL ? fullname : 1258071d4279SBram Moolenaar # endif 1259071d4279SBram Moolenaar eap->arg, tmp_new); 1260071d4279SBram Moolenaar else 1261071d4279SBram Moolenaar #endif 1262071d4279SBram Moolenaar { 1263071d4279SBram Moolenaar /* Build the patch command and execute it. Ignore errors. Switch to 1264071d4279SBram Moolenaar * cooked mode to allow the user to respond to prompts. */ 1265a95ab321SBram Moolenaar vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", 1266a95ab321SBram Moolenaar tmp_new, tmp_orig, esc_name); 126778ab331eSBram Moolenaar block_autocmds(); /* Avoid ShellCmdPost stuff */ 1268071d4279SBram Moolenaar (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED); 126978ab331eSBram Moolenaar unblock_autocmds(); 1270071d4279SBram Moolenaar } 1271071d4279SBram Moolenaar 1272071d4279SBram Moolenaar #ifdef UNIX 1273071d4279SBram Moolenaar if (dirbuf[0] != NUL) 1274071d4279SBram Moolenaar { 1275071d4279SBram Moolenaar if (mch_chdir((char *)dirbuf) != 0) 1276071d4279SBram Moolenaar EMSG(_(e_prev_dir)); 1277071d4279SBram Moolenaar shorten_fnames(TRUE); 1278071d4279SBram Moolenaar } 1279071d4279SBram Moolenaar #endif 1280071d4279SBram Moolenaar 1281071d4279SBram Moolenaar /* patch probably has written over the screen */ 1282071d4279SBram Moolenaar redraw_later(CLEAR); 1283071d4279SBram Moolenaar 1284071d4279SBram Moolenaar /* Delete any .orig or .rej file created. */ 1285071d4279SBram Moolenaar STRCPY(buf, tmp_new); 1286071d4279SBram Moolenaar STRCAT(buf, ".orig"); 1287071d4279SBram Moolenaar mch_remove(buf); 1288071d4279SBram Moolenaar STRCPY(buf, tmp_new); 1289071d4279SBram Moolenaar STRCAT(buf, ".rej"); 1290071d4279SBram Moolenaar mch_remove(buf); 1291071d4279SBram Moolenaar 12926ec0a6c4SBram Moolenaar /* Only continue if the output file was created. */ 12936ec0a6c4SBram Moolenaar if (mch_stat((char *)tmp_new, &st) < 0 || st.st_size == 0) 12946ec0a6c4SBram Moolenaar EMSG(_("E816: Cannot read patch output")); 12956ec0a6c4SBram Moolenaar else 12966ec0a6c4SBram Moolenaar { 1297071d4279SBram Moolenaar if (curbuf->b_fname != NULL) 1298071d4279SBram Moolenaar { 1299071d4279SBram Moolenaar newname = vim_strnsave(curbuf->b_fname, 1300071d4279SBram Moolenaar (int)(STRLEN(curbuf->b_fname) + 4)); 1301071d4279SBram Moolenaar if (newname != NULL) 1302071d4279SBram Moolenaar STRCAT(newname, ".new"); 1303071d4279SBram Moolenaar } 1304071d4279SBram Moolenaar 1305071d4279SBram Moolenaar #ifdef FEAT_GUI 1306071d4279SBram Moolenaar need_mouse_correct = TRUE; 1307071d4279SBram Moolenaar #endif 130880a94a58SBram Moolenaar /* don't use a new tab page, each tab page has its own diffs */ 130980a94a58SBram Moolenaar cmdmod.tab = 0; 131080a94a58SBram Moolenaar 1311c4675a19SBram Moolenaar if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) 1312071d4279SBram Moolenaar { 1313071d4279SBram Moolenaar /* Pretend it was a ":split fname" command */ 1314071d4279SBram Moolenaar eap->cmdidx = CMD_split; 1315071d4279SBram Moolenaar eap->arg = tmp_new; 1316071d4279SBram Moolenaar do_exedit(eap, old_curwin); 1317071d4279SBram Moolenaar 13186ec0a6c4SBram Moolenaar /* check that split worked and editing tmp_new */ 13196ec0a6c4SBram Moolenaar if (curwin != old_curwin && win_valid(old_curwin)) 1320071d4279SBram Moolenaar { 1321071d4279SBram Moolenaar /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 1322071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1323071d4279SBram Moolenaar diff_win_options(old_curwin, TRUE); 1324071d4279SBram Moolenaar 1325071d4279SBram Moolenaar if (newname != NULL) 1326071d4279SBram Moolenaar { 1327071d4279SBram Moolenaar /* do a ":file filename.new" on the patched buffer */ 1328071d4279SBram Moolenaar eap->arg = newname; 1329071d4279SBram Moolenaar ex_file(eap); 1330071d4279SBram Moolenaar 1331071d4279SBram Moolenaar /* Do filetype detection with the new name. */ 1332910f66f9SBram Moolenaar if (au_has_group((char_u *)"filetypedetect")) 1333071d4279SBram Moolenaar do_cmdline_cmd((char_u *)":doau filetypedetect BufRead"); 1334071d4279SBram Moolenaar } 1335071d4279SBram Moolenaar } 1336071d4279SBram Moolenaar } 13376ec0a6c4SBram Moolenaar } 1338071d4279SBram Moolenaar 1339071d4279SBram Moolenaar theend: 1340071d4279SBram Moolenaar if (tmp_orig != NULL) 1341071d4279SBram Moolenaar mch_remove(tmp_orig); 1342071d4279SBram Moolenaar vim_free(tmp_orig); 1343071d4279SBram Moolenaar if (tmp_new != NULL) 1344071d4279SBram Moolenaar mch_remove(tmp_new); 1345071d4279SBram Moolenaar vim_free(tmp_new); 1346071d4279SBram Moolenaar vim_free(newname); 1347071d4279SBram Moolenaar vim_free(buf); 1348071d4279SBram Moolenaar #ifdef UNIX 1349071d4279SBram Moolenaar vim_free(fullname); 1350071d4279SBram Moolenaar #endif 1351a95ab321SBram Moolenaar vim_free(esc_name); 1352071d4279SBram Moolenaar #ifdef FEAT_BROWSE 1353071d4279SBram Moolenaar vim_free(browseFile); 1354071d4279SBram Moolenaar cmdmod.browse = browse_flag; 1355071d4279SBram Moolenaar #endif 1356071d4279SBram Moolenaar } 1357071d4279SBram Moolenaar 1358071d4279SBram Moolenaar /* 1359071d4279SBram Moolenaar * Split the window and edit another file, setting options to show the diffs. 1360071d4279SBram Moolenaar */ 1361071d4279SBram Moolenaar void 13627454a06eSBram Moolenaar ex_diffsplit(exarg_T *eap) 1363071d4279SBram Moolenaar { 1364071d4279SBram Moolenaar win_T *old_curwin = curwin; 13657c0a2f36SBram Moolenaar bufref_T old_curbuf; 1366071d4279SBram Moolenaar 13677c0a2f36SBram Moolenaar set_bufref(&old_curbuf, curbuf); 1368071d4279SBram Moolenaar #ifdef FEAT_GUI 1369071d4279SBram Moolenaar need_mouse_correct = TRUE; 1370071d4279SBram Moolenaar #endif 137146328f9aSBram Moolenaar /* Need to compute w_fraction when no redraw happened yet. */ 137246328f9aSBram Moolenaar validate_cursor(); 137346328f9aSBram Moolenaar set_fraction(curwin); 137446328f9aSBram Moolenaar 137580a94a58SBram Moolenaar /* don't use a new tab page, each tab page has its own diffs */ 137680a94a58SBram Moolenaar cmdmod.tab = 0; 137780a94a58SBram Moolenaar 1378c4675a19SBram Moolenaar if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) 1379071d4279SBram Moolenaar { 1380071d4279SBram Moolenaar /* Pretend it was a ":split fname" command */ 1381071d4279SBram Moolenaar eap->cmdidx = CMD_split; 13824be06f9eSBram Moolenaar curwin->w_p_diff = TRUE; 1383071d4279SBram Moolenaar do_exedit(eap, old_curwin); 1384071d4279SBram Moolenaar 1385071d4279SBram Moolenaar if (curwin != old_curwin) /* split must have worked */ 1386071d4279SBram Moolenaar { 1387071d4279SBram Moolenaar /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 1388071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1389f29a82dcSBram Moolenaar if (win_valid(old_curwin)) 1390f29a82dcSBram Moolenaar { 1391071d4279SBram Moolenaar diff_win_options(old_curwin, TRUE); 1392f29a82dcSBram Moolenaar 13937c0a2f36SBram Moolenaar if (bufref_valid(&old_curbuf)) 1394f29a82dcSBram Moolenaar /* Move the cursor position to that of the old window. */ 1395f29a82dcSBram Moolenaar curwin->w_cursor.lnum = diff_get_corresponding_line( 1396025e3e0bSBram Moolenaar old_curbuf.br_buf, old_curwin->w_cursor.lnum); 1397f29a82dcSBram Moolenaar } 139846328f9aSBram Moolenaar /* Now that lines are folded scroll to show the cursor at the same 139946328f9aSBram Moolenaar * relative position. */ 140046328f9aSBram Moolenaar scroll_to_fraction(curwin, curwin->w_height); 1401071d4279SBram Moolenaar } 1402071d4279SBram Moolenaar } 1403071d4279SBram Moolenaar } 1404071d4279SBram Moolenaar 1405071d4279SBram Moolenaar /* 140684a05accSBram Moolenaar * Set options to show diffs for the current window. 1407071d4279SBram Moolenaar */ 1408071d4279SBram Moolenaar void 14097454a06eSBram Moolenaar ex_diffthis(exarg_T *eap UNUSED) 1410071d4279SBram Moolenaar { 1411071d4279SBram Moolenaar /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 1412071d4279SBram Moolenaar diff_win_options(curwin, TRUE); 1413071d4279SBram Moolenaar } 1414071d4279SBram Moolenaar 141504f62f88SBram Moolenaar static void 141604f62f88SBram Moolenaar set_diff_option(win_T *wp, int value) 141704f62f88SBram Moolenaar { 141804f62f88SBram Moolenaar win_T *old_curwin = curwin; 141904f62f88SBram Moolenaar 142004f62f88SBram Moolenaar curwin = wp; 142104f62f88SBram Moolenaar curbuf = curwin->w_buffer; 142204f62f88SBram Moolenaar ++curbuf_lock; 142304f62f88SBram Moolenaar set_option_value((char_u *)"diff", (long)value, NULL, OPT_LOCAL); 142404f62f88SBram Moolenaar --curbuf_lock; 142504f62f88SBram Moolenaar curwin = old_curwin; 142604f62f88SBram Moolenaar curbuf = curwin->w_buffer; 142704f62f88SBram Moolenaar } 142804f62f88SBram Moolenaar 1429071d4279SBram Moolenaar /* 1430071d4279SBram Moolenaar * Set options in window "wp" for diff mode. 1431071d4279SBram Moolenaar */ 1432071d4279SBram Moolenaar void 14337454a06eSBram Moolenaar diff_win_options( 14347454a06eSBram Moolenaar win_T *wp, 14357454a06eSBram Moolenaar int addbuf) /* Add buffer to diff. */ 1436071d4279SBram Moolenaar { 1437f4d7f944SBram Moolenaar # ifdef FEAT_FOLDING 1438f4d7f944SBram Moolenaar win_T *old_curwin = curwin; 1439f4d7f944SBram Moolenaar 1440f4d7f944SBram Moolenaar /* close the manually opened folds */ 1441f4d7f944SBram Moolenaar curwin = wp; 1442f4d7f944SBram Moolenaar newFoldLevel(); 1443f4d7f944SBram Moolenaar curwin = old_curwin; 1444f4d7f944SBram Moolenaar # endif 1445f4d7f944SBram Moolenaar 14463368ea21SBram Moolenaar /* Use 'scrollbind' and 'cursorbind' when available */ 144743929964SBram Moolenaar if (!wp->w_p_diff) 1448a87aa806SBram Moolenaar wp->w_p_scb_save = wp->w_p_scb; 14493368ea21SBram Moolenaar wp->w_p_scb = TRUE; 145043929964SBram Moolenaar if (!wp->w_p_diff) 1451a87aa806SBram Moolenaar wp->w_p_crb_save = wp->w_p_crb; 1452860cae1cSBram Moolenaar wp->w_p_crb = TRUE; 145343929964SBram Moolenaar if (!wp->w_p_diff) 1454a87aa806SBram Moolenaar wp->w_p_wrap_save = wp->w_p_wrap; 1455071d4279SBram Moolenaar wp->w_p_wrap = FALSE; 1456071d4279SBram Moolenaar # ifdef FEAT_FOLDING 1457071d4279SBram Moolenaar curwin = wp; 1458071d4279SBram Moolenaar curbuf = curwin->w_buffer; 145943929964SBram Moolenaar if (!wp->w_p_diff) 146043929964SBram Moolenaar { 146143929964SBram Moolenaar if (wp->w_p_diff_saved) 146243929964SBram Moolenaar free_string_option(wp->w_p_fdm_save); 1463a87aa806SBram Moolenaar wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); 146443929964SBram Moolenaar } 1465071d4279SBram Moolenaar set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", 14665e3cb7e8SBram Moolenaar OPT_LOCAL|OPT_FREE, 0); 1467071d4279SBram Moolenaar curwin = old_curwin; 1468071d4279SBram Moolenaar curbuf = curwin->w_buffer; 146943929964SBram Moolenaar if (!wp->w_p_diff) 1470a87aa806SBram Moolenaar { 1471a87aa806SBram Moolenaar wp->w_p_fdc_save = wp->w_p_fdc; 1472a87aa806SBram Moolenaar wp->w_p_fen_save = wp->w_p_fen; 1473a87aa806SBram Moolenaar wp->w_p_fdl_save = wp->w_p_fdl; 1474a87aa806SBram Moolenaar } 1475c4675a19SBram Moolenaar wp->w_p_fdc = diff_foldcolumn; 1476071d4279SBram Moolenaar wp->w_p_fen = TRUE; 1477071d4279SBram Moolenaar wp->w_p_fdl = 0; 1478071d4279SBram Moolenaar foldUpdateAll(wp); 14792df6dcc5SBram Moolenaar /* make sure topline is not halfway a fold */ 14802df6dcc5SBram Moolenaar changed_window_setting_win(wp); 1481071d4279SBram Moolenaar # endif 1482071d4279SBram Moolenaar if (vim_strchr(p_sbo, 'h') == NULL) 1483071d4279SBram Moolenaar do_cmdline_cmd((char_u *)"set sbo+=hor"); 148404f62f88SBram Moolenaar /* Save the current values, to be restored in ex_diffoff(). */ 1485a87aa806SBram Moolenaar wp->w_p_diff_saved = TRUE; 1486071d4279SBram Moolenaar 148704f62f88SBram Moolenaar set_diff_option(wp, TRUE); 148843929964SBram Moolenaar 1489071d4279SBram Moolenaar if (addbuf) 1490071d4279SBram Moolenaar diff_buf_add(wp->w_buffer); 1491071d4279SBram Moolenaar redraw_win_later(wp, NOT_VALID); 1492071d4279SBram Moolenaar } 1493071d4279SBram Moolenaar 1494071d4279SBram Moolenaar /* 14952df6dcc5SBram Moolenaar * Set options not to show diffs. For the current window or all windows. 1496f740b29aSBram Moolenaar * Only in the current tab page. 14972df6dcc5SBram Moolenaar */ 14982df6dcc5SBram Moolenaar void 14997454a06eSBram Moolenaar ex_diffoff(exarg_T *eap) 15002df6dcc5SBram Moolenaar { 15012df6dcc5SBram Moolenaar win_T *wp; 15022df6dcc5SBram Moolenaar int diffwin = FALSE; 15032df6dcc5SBram Moolenaar 150429323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 15052df6dcc5SBram Moolenaar { 150600462ffbSBram Moolenaar if (eap->forceit ? wp->w_p_diff : wp == curwin) 15072df6dcc5SBram Moolenaar { 150843929964SBram Moolenaar /* Set 'diff' off. If option values were saved in 150943929964SBram Moolenaar * diff_win_options(), restore the ones whose settings seem to have 151043929964SBram Moolenaar * been left over from diff mode. */ 151104f62f88SBram Moolenaar set_diff_option(wp, FALSE); 1512a87aa806SBram Moolenaar 151343929964SBram Moolenaar if (wp->w_p_diff_saved) 151443929964SBram Moolenaar { 151543929964SBram Moolenaar 1516a87aa806SBram Moolenaar if (wp->w_p_scb) 151743929964SBram Moolenaar wp->w_p_scb = wp->w_p_scb_save; 1518a87aa806SBram Moolenaar if (wp->w_p_crb) 151943929964SBram Moolenaar wp->w_p_crb = wp->w_p_crb_save; 1520a87aa806SBram Moolenaar if (!wp->w_p_wrap) 152143929964SBram Moolenaar wp->w_p_wrap = wp->w_p_wrap_save; 15222df6dcc5SBram Moolenaar #ifdef FEAT_FOLDING 1523a87aa806SBram Moolenaar free_string_option(wp->w_p_fdm); 152479a213d6SBram Moolenaar wp->w_p_fdm = vim_strsave( 152579a213d6SBram Moolenaar *wp->w_p_fdm_save ? wp->w_p_fdm_save : (char_u*)"manual"); 152643929964SBram Moolenaar 1527a87aa806SBram Moolenaar if (wp->w_p_fdc == diff_foldcolumn) 152843929964SBram Moolenaar wp->w_p_fdc = wp->w_p_fdc_save; 152943929964SBram Moolenaar if (wp->w_p_fdl == 0) 1530a87aa806SBram Moolenaar wp->w_p_fdl = wp->w_p_fdl_save; 153133ca6bf2SBram Moolenaar 153233ca6bf2SBram Moolenaar /* Only restore 'foldenable' when 'foldmethod' is not 153333ca6bf2SBram Moolenaar * "manual", otherwise we continue to show the diff folds. */ 153443929964SBram Moolenaar if (wp->w_p_fen) 153543929964SBram Moolenaar wp->w_p_fen = foldmethodIsManual(wp) ? FALSE 153643929964SBram Moolenaar : wp->w_p_fen_save; 153733ca6bf2SBram Moolenaar 15382df6dcc5SBram Moolenaar foldUpdateAll(wp); 15392df6dcc5SBram Moolenaar #endif 154043929964SBram Moolenaar } 1541e67d546fSBram Moolenaar /* remove filler lines */ 1542e67d546fSBram Moolenaar wp->w_topfill = 0; 1543e67d546fSBram Moolenaar 1544e67d546fSBram Moolenaar /* make sure topline is not halfway a fold and cursor is 1545e67d546fSBram Moolenaar * invalidated */ 1546e67d546fSBram Moolenaar changed_window_setting_win(wp); 154743929964SBram Moolenaar 1548a87aa806SBram Moolenaar /* Note: 'sbo' is not restored, it's a global option. */ 15492df6dcc5SBram Moolenaar diff_buf_adjust(wp); 15502df6dcc5SBram Moolenaar } 15512df6dcc5SBram Moolenaar diffwin |= wp->w_p_diff; 15522df6dcc5SBram Moolenaar } 15532df6dcc5SBram Moolenaar 155425ea0544SBram Moolenaar /* Also remove hidden buffers from the list. */ 155525ea0544SBram Moolenaar if (eap->forceit) 155625ea0544SBram Moolenaar diff_buf_clear(); 155725ea0544SBram Moolenaar 15582df6dcc5SBram Moolenaar /* Remove "hor" from from 'scrollopt' if there are no diff windows left. */ 15592df6dcc5SBram Moolenaar if (!diffwin && vim_strchr(p_sbo, 'h') != NULL) 15602df6dcc5SBram Moolenaar do_cmdline_cmd((char_u *)"set sbo-=hor"); 15612df6dcc5SBram Moolenaar } 15622df6dcc5SBram Moolenaar 15632df6dcc5SBram Moolenaar /* 1564071d4279SBram Moolenaar * Read the diff output and add each entry to the diff list. 1565071d4279SBram Moolenaar */ 1566071d4279SBram Moolenaar static void 15677454a06eSBram Moolenaar diff_read( 1568e828b762SBram Moolenaar int idx_orig, // idx of original file 1569e828b762SBram Moolenaar int idx_new, // idx of new file 1570e828b762SBram Moolenaar diffout_T *dout) // diff output 1571071d4279SBram Moolenaar { 1572e828b762SBram Moolenaar FILE *fd = NULL; 1573e828b762SBram Moolenaar int line_idx = 0; 1574071d4279SBram Moolenaar diff_T *dprev = NULL; 157549d7bf13SBram Moolenaar diff_T *dp = curtab->tp_first_diff; 1576071d4279SBram Moolenaar diff_T *dn, *dpl; 1577071d4279SBram Moolenaar char_u linebuf[LBUFLEN]; /* only need to hold the diff line */ 1578e828b762SBram Moolenaar char_u *line; 1579071d4279SBram Moolenaar long off; 1580071d4279SBram Moolenaar int i; 1581071d4279SBram Moolenaar linenr_T lnum_orig, lnum_new; 1582071d4279SBram Moolenaar long count_orig, count_new; 1583071d4279SBram Moolenaar int notset = TRUE; /* block "*dp" not set yet */ 1584e828b762SBram Moolenaar enum { 1585e828b762SBram Moolenaar DIFF_ED, 1586e828b762SBram Moolenaar DIFF_UNIFIED, 1587e828b762SBram Moolenaar DIFF_NONE 1588e828b762SBram Moolenaar } diffstyle = DIFF_NONE; 1589071d4279SBram Moolenaar 1590e828b762SBram Moolenaar if (dout->dout_fname == NULL) 1591e828b762SBram Moolenaar { 1592e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 1593e828b762SBram Moolenaar } 1594e828b762SBram Moolenaar else 1595e828b762SBram Moolenaar { 1596e828b762SBram Moolenaar fd = mch_fopen((char *)dout->dout_fname, "r"); 1597071d4279SBram Moolenaar if (fd == NULL) 1598071d4279SBram Moolenaar { 1599071d4279SBram Moolenaar EMSG(_("E98: Cannot read diff output")); 1600071d4279SBram Moolenaar return; 1601071d4279SBram Moolenaar } 1602e828b762SBram Moolenaar } 1603071d4279SBram Moolenaar 1604071d4279SBram Moolenaar for (;;) 1605071d4279SBram Moolenaar { 1606e828b762SBram Moolenaar if (fd == NULL) 1607e828b762SBram Moolenaar { 1608e828b762SBram Moolenaar if (line_idx >= dout->dout_ga.ga_len) 1609e828b762SBram Moolenaar break; // did last line 1610e828b762SBram Moolenaar line = ((char_u **)dout->dout_ga.ga_data)[line_idx++]; 1611e828b762SBram Moolenaar } 1612e828b762SBram Moolenaar else 1613e828b762SBram Moolenaar { 1614071d4279SBram Moolenaar if (tag_fgets(linebuf, LBUFLEN, fd)) 1615e828b762SBram Moolenaar break; // end of file 1616e828b762SBram Moolenaar line = linebuf; 1617071d4279SBram Moolenaar } 1618071d4279SBram Moolenaar 1619e828b762SBram Moolenaar if (diffstyle == DIFF_NONE) 1620e828b762SBram Moolenaar { 1621e828b762SBram Moolenaar // Determine diff style. 1622e828b762SBram Moolenaar // ed like diff looks like this: 1623e828b762SBram Moolenaar // {first}[,{last}]c{first}[,{last}] 1624e828b762SBram Moolenaar // {first}a{first}[,{last}] 1625e828b762SBram Moolenaar // {first}[,{last}]d{first} 1626e828b762SBram Moolenaar // 1627e828b762SBram Moolenaar // unified diff looks like this: 1628e828b762SBram Moolenaar // --- file1 2018-03-20 13:23:35.783153140 +0100 1629e828b762SBram Moolenaar // +++ file2 2018-03-20 13:23:41.183156066 +0100 1630e828b762SBram Moolenaar // @@ -1,3 +1,5 @@ 1631e828b762SBram Moolenaar if (isdigit(*line)) 1632e828b762SBram Moolenaar diffstyle = DIFF_ED; 1633e828b762SBram Moolenaar else if ((STRNCMP(line, "@@ ", 3) == 0)) 1634e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 1635e828b762SBram Moolenaar else if ((STRNCMP(line, "--- ", 4) == 0) 1636e828b762SBram Moolenaar && (tag_fgets(linebuf, LBUFLEN, fd) == 0) 1637e828b762SBram Moolenaar && (STRNCMP(line, "+++ ", 4) == 0) 1638e828b762SBram Moolenaar && (tag_fgets(linebuf, LBUFLEN, fd) == 0) 1639e828b762SBram Moolenaar && (STRNCMP(line, "@@ ", 3) == 0)) 1640e828b762SBram Moolenaar diffstyle = DIFF_UNIFIED; 16413b8defd0SBram Moolenaar else 16423b8defd0SBram Moolenaar // Format not recognized yet, skip over this line. Cygwin diff 16433b8defd0SBram Moolenaar // may put a warning at the start of the file. 16443b8defd0SBram Moolenaar continue; 1645e828b762SBram Moolenaar } 1646e828b762SBram Moolenaar 1647e828b762SBram Moolenaar if (diffstyle == DIFF_ED) 1648e828b762SBram Moolenaar { 1649e828b762SBram Moolenaar if (!isdigit(*line)) 1650e828b762SBram Moolenaar continue; // not the start of a diff block 1651e828b762SBram Moolenaar if (parse_diff_ed(line, &lnum_orig, &count_orig, 1652e828b762SBram Moolenaar &lnum_new, &count_new) == FAIL) 1653e828b762SBram Moolenaar continue; 1654e828b762SBram Moolenaar } 1655e828b762SBram Moolenaar else if (diffstyle == DIFF_UNIFIED) 1656e828b762SBram Moolenaar { 1657e828b762SBram Moolenaar if (STRNCMP(line, "@@ ", 3) != 0) 1658e828b762SBram Moolenaar continue; // not the start of a diff block 1659e828b762SBram Moolenaar if (parse_diff_unified(line, &lnum_orig, &count_orig, 1660e828b762SBram Moolenaar &lnum_new, &count_new) == FAIL) 1661e828b762SBram Moolenaar continue; 1662e828b762SBram Moolenaar } 1663e828b762SBram Moolenaar else 1664e828b762SBram Moolenaar { 1665e828b762SBram Moolenaar EMSG(_("E959: Invalid diff format.")); 1666e828b762SBram Moolenaar break; 1667e828b762SBram Moolenaar } 1668e828b762SBram Moolenaar 1669e828b762SBram Moolenaar // Go over blocks before the change, for which orig and new are equal. 1670e828b762SBram Moolenaar // Copy blocks from orig to new. 1671071d4279SBram Moolenaar while (dp != NULL 1672071d4279SBram Moolenaar && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) 1673071d4279SBram Moolenaar { 1674071d4279SBram Moolenaar if (notset) 1675071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, idx_new); 1676071d4279SBram Moolenaar dprev = dp; 1677071d4279SBram Moolenaar dp = dp->df_next; 1678071d4279SBram Moolenaar notset = TRUE; 1679071d4279SBram Moolenaar } 1680071d4279SBram Moolenaar 1681071d4279SBram Moolenaar if (dp != NULL 1682071d4279SBram Moolenaar && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig] 1683071d4279SBram Moolenaar && lnum_orig + count_orig >= dp->df_lnum[idx_orig]) 1684071d4279SBram Moolenaar { 1685e828b762SBram Moolenaar // New block overlaps with existing block(s). 1686e828b762SBram Moolenaar // First find last block that overlaps. 1687071d4279SBram Moolenaar for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) 1688071d4279SBram Moolenaar if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) 1689071d4279SBram Moolenaar break; 1690071d4279SBram Moolenaar 1691e828b762SBram Moolenaar // If the newly found block starts before the old one, set the 1692e828b762SBram Moolenaar // start back a number of lines. 1693071d4279SBram Moolenaar off = dp->df_lnum[idx_orig] - lnum_orig; 1694071d4279SBram Moolenaar if (off > 0) 1695071d4279SBram Moolenaar { 1696071d4279SBram Moolenaar for (i = idx_orig; i < idx_new; ++i) 169749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1698071d4279SBram Moolenaar dp->df_lnum[i] -= off; 1699071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new; 1700071d4279SBram Moolenaar dp->df_count[idx_new] = count_new; 1701071d4279SBram Moolenaar } 1702071d4279SBram Moolenaar else if (notset) 1703071d4279SBram Moolenaar { 1704e828b762SBram Moolenaar // new block inside existing one, adjust new block 1705071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new + off; 1706071d4279SBram Moolenaar dp->df_count[idx_new] = count_new - off; 1707071d4279SBram Moolenaar } 1708071d4279SBram Moolenaar else 1709e828b762SBram Moolenaar // second overlap of new block with existing block 1710bb8f88bbSBram Moolenaar dp->df_count[idx_new] += count_new - count_orig 1711bb8f88bbSBram Moolenaar + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig] 1712bb8f88bbSBram Moolenaar - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]); 1713071d4279SBram Moolenaar 1714e828b762SBram Moolenaar // Adjust the size of the block to include all the lines to the 1715e828b762SBram Moolenaar // end of the existing block or the new diff, whatever ends last. 1716071d4279SBram Moolenaar off = (lnum_orig + count_orig) 1717071d4279SBram Moolenaar - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); 1718071d4279SBram Moolenaar if (off < 0) 1719071d4279SBram Moolenaar { 1720e828b762SBram Moolenaar // new change ends in existing block, adjust the end if not 1721e828b762SBram Moolenaar // done already 1722071d4279SBram Moolenaar if (notset) 1723071d4279SBram Moolenaar dp->df_count[idx_new] += -off; 1724071d4279SBram Moolenaar off = 0; 1725071d4279SBram Moolenaar } 1726d4b96bc6SBram Moolenaar for (i = idx_orig; i < idx_new; ++i) 172749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1728071d4279SBram Moolenaar dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] 1729071d4279SBram Moolenaar - dp->df_lnum[i] + off; 1730071d4279SBram Moolenaar 1731e828b762SBram Moolenaar // Delete the diff blocks that have been merged into one. 1732071d4279SBram Moolenaar dn = dp->df_next; 1733071d4279SBram Moolenaar dp->df_next = dpl->df_next; 1734071d4279SBram Moolenaar while (dn != dp->df_next) 1735071d4279SBram Moolenaar { 1736071d4279SBram Moolenaar dpl = dn->df_next; 1737071d4279SBram Moolenaar vim_free(dn); 1738071d4279SBram Moolenaar dn = dpl; 1739071d4279SBram Moolenaar } 1740071d4279SBram Moolenaar } 1741071d4279SBram Moolenaar else 1742071d4279SBram Moolenaar { 1743e828b762SBram Moolenaar // Allocate a new diffblock. 174449d7bf13SBram Moolenaar dp = diff_alloc_new(curtab, dprev, dp); 1745071d4279SBram Moolenaar if (dp == NULL) 1746eb3593b3SBram Moolenaar goto done; 1747071d4279SBram Moolenaar 1748071d4279SBram Moolenaar dp->df_lnum[idx_orig] = lnum_orig; 1749071d4279SBram Moolenaar dp->df_count[idx_orig] = count_orig; 1750071d4279SBram Moolenaar dp->df_lnum[idx_new] = lnum_new; 1751071d4279SBram Moolenaar dp->df_count[idx_new] = count_new; 1752071d4279SBram Moolenaar 1753e828b762SBram Moolenaar // Set values for other buffers, these must be equal to the 1754e828b762SBram Moolenaar // original buffer, otherwise there would have been a change 1755e828b762SBram Moolenaar // already. 1756071d4279SBram Moolenaar for (i = idx_orig + 1; i < idx_new; ++i) 175749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL) 1758071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, i); 1759071d4279SBram Moolenaar } 1760e828b762SBram Moolenaar notset = FALSE; // "*dp" has been set 1761071d4279SBram Moolenaar } 1762071d4279SBram Moolenaar 1763e828b762SBram Moolenaar // for remaining diff blocks orig and new are equal 1764071d4279SBram Moolenaar while (dp != NULL) 1765071d4279SBram Moolenaar { 1766071d4279SBram Moolenaar if (notset) 1767071d4279SBram Moolenaar diff_copy_entry(dprev, dp, idx_orig, idx_new); 1768071d4279SBram Moolenaar dprev = dp; 1769071d4279SBram Moolenaar dp = dp->df_next; 1770071d4279SBram Moolenaar notset = TRUE; 1771071d4279SBram Moolenaar } 1772071d4279SBram Moolenaar 1773eb3593b3SBram Moolenaar done: 1774e828b762SBram Moolenaar if (fd != NULL) 1775071d4279SBram Moolenaar fclose(fd); 1776071d4279SBram Moolenaar } 1777071d4279SBram Moolenaar 1778071d4279SBram Moolenaar /* 1779071d4279SBram Moolenaar * Copy an entry at "dp" from "idx_orig" to "idx_new". 1780071d4279SBram Moolenaar */ 1781071d4279SBram Moolenaar static void 17827454a06eSBram Moolenaar diff_copy_entry( 17837454a06eSBram Moolenaar diff_T *dprev, 17847454a06eSBram Moolenaar diff_T *dp, 17857454a06eSBram Moolenaar int idx_orig, 17867454a06eSBram Moolenaar int idx_new) 1787071d4279SBram Moolenaar { 1788071d4279SBram Moolenaar long off; 1789071d4279SBram Moolenaar 1790071d4279SBram Moolenaar if (dprev == NULL) 1791071d4279SBram Moolenaar off = 0; 1792071d4279SBram Moolenaar else 1793071d4279SBram Moolenaar off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig]) 1794071d4279SBram Moolenaar - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]); 1795071d4279SBram Moolenaar dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off; 1796071d4279SBram Moolenaar dp->df_count[idx_new] = dp->df_count[idx_orig]; 1797071d4279SBram Moolenaar } 1798071d4279SBram Moolenaar 1799071d4279SBram Moolenaar /* 180049d7bf13SBram Moolenaar * Clear the list of diffblocks for tab page "tp". 1801071d4279SBram Moolenaar */ 1802ea408854SBram Moolenaar void 18037454a06eSBram Moolenaar diff_clear(tabpage_T *tp) 1804071d4279SBram Moolenaar { 1805071d4279SBram Moolenaar diff_T *p, *next_p; 1806071d4279SBram Moolenaar 180749d7bf13SBram Moolenaar for (p = tp->tp_first_diff; p != NULL; p = next_p) 1808071d4279SBram Moolenaar { 1809071d4279SBram Moolenaar next_p = p->df_next; 1810071d4279SBram Moolenaar vim_free(p); 1811071d4279SBram Moolenaar } 181249d7bf13SBram Moolenaar tp->tp_first_diff = NULL; 1813071d4279SBram Moolenaar } 1814071d4279SBram Moolenaar 1815071d4279SBram Moolenaar /* 1816071d4279SBram Moolenaar * Check diff status for line "lnum" in buffer "buf": 1817071d4279SBram Moolenaar * Returns 0 for nothing special 1818071d4279SBram Moolenaar * Returns -1 for a line that should be highlighted as changed. 1819071d4279SBram Moolenaar * Returns -2 for a line that should be highlighted as added/deleted. 1820071d4279SBram Moolenaar * Returns > 0 for inserting that many filler lines above it (never happens 1821071d4279SBram Moolenaar * when 'diffopt' doesn't contain "filler"). 1822071d4279SBram Moolenaar * This should only be used for windows where 'diff' is set. 1823071d4279SBram Moolenaar */ 1824071d4279SBram Moolenaar int 18257454a06eSBram Moolenaar diff_check(win_T *wp, linenr_T lnum) 1826071d4279SBram Moolenaar { 182749d7bf13SBram Moolenaar int idx; /* index in tp_diffbuf[] for this buffer */ 1828071d4279SBram Moolenaar diff_T *dp; 1829071d4279SBram Moolenaar int maxcount; 1830071d4279SBram Moolenaar int i; 1831071d4279SBram Moolenaar buf_T *buf = wp->w_buffer; 1832071d4279SBram Moolenaar int cmp; 1833071d4279SBram Moolenaar 183449d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 1835071d4279SBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 1836071d4279SBram Moolenaar 183749d7bf13SBram Moolenaar if (curtab->tp_first_diff == NULL || !wp->w_p_diff) /* no diffs at all */ 1838071d4279SBram Moolenaar return 0; 1839071d4279SBram Moolenaar 1840071d4279SBram Moolenaar /* safety check: "lnum" must be a buffer line */ 1841071d4279SBram Moolenaar if (lnum < 1 || lnum > buf->b_ml.ml_line_count + 1) 1842071d4279SBram Moolenaar return 0; 1843071d4279SBram Moolenaar 1844071d4279SBram Moolenaar idx = diff_buf_idx(buf); 1845071d4279SBram Moolenaar if (idx == DB_COUNT) 1846071d4279SBram Moolenaar return 0; /* no diffs for buffer "buf" */ 1847071d4279SBram Moolenaar 1848071d4279SBram Moolenaar #ifdef FEAT_FOLDING 1849071d4279SBram Moolenaar /* A closed fold never has filler lines. */ 1850071d4279SBram Moolenaar if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL)) 1851071d4279SBram Moolenaar return 0; 1852071d4279SBram Moolenaar #endif 1853071d4279SBram Moolenaar 1854071d4279SBram Moolenaar /* search for a change that includes "lnum" in the list of diffblocks. */ 185549d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 1856071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 1857071d4279SBram Moolenaar break; 1858071d4279SBram Moolenaar if (dp == NULL || lnum < dp->df_lnum[idx]) 1859071d4279SBram Moolenaar return 0; 1860071d4279SBram Moolenaar 1861071d4279SBram Moolenaar if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) 1862071d4279SBram Moolenaar { 1863071d4279SBram Moolenaar int zero = FALSE; 1864071d4279SBram Moolenaar 1865071d4279SBram Moolenaar /* Changed or inserted line. If the other buffers have a count of 1866071d4279SBram Moolenaar * zero, the lines were inserted. If the other buffers have the same 1867071d4279SBram Moolenaar * count, check if the lines are identical. */ 1868071d4279SBram Moolenaar cmp = FALSE; 1869071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 187049d7bf13SBram Moolenaar if (i != idx && curtab->tp_diffbuf[i] != NULL) 1871071d4279SBram Moolenaar { 1872071d4279SBram Moolenaar if (dp->df_count[i] == 0) 1873071d4279SBram Moolenaar zero = TRUE; 1874071d4279SBram Moolenaar else 1875071d4279SBram Moolenaar { 1876071d4279SBram Moolenaar if (dp->df_count[i] != dp->df_count[idx]) 1877071d4279SBram Moolenaar return -1; /* nr of lines changed. */ 1878071d4279SBram Moolenaar cmp = TRUE; 1879071d4279SBram Moolenaar } 1880071d4279SBram Moolenaar } 1881071d4279SBram Moolenaar if (cmp) 1882071d4279SBram Moolenaar { 1883071d4279SBram Moolenaar /* Compare all lines. If they are equal the lines were inserted 1884071d4279SBram Moolenaar * in some buffers, deleted in others, but not changed. */ 1885071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 18867c0a2f36SBram Moolenaar if (i != idx && curtab->tp_diffbuf[i] != NULL 18877c0a2f36SBram Moolenaar && dp->df_count[i] != 0) 1888071d4279SBram Moolenaar if (!diff_equal_entry(dp, idx, i)) 1889071d4279SBram Moolenaar return -1; 1890071d4279SBram Moolenaar } 1891071d4279SBram Moolenaar /* If there is no buffer with zero lines then there is no difference 1892071d4279SBram Moolenaar * any longer. Happens when making a change (or undo) that removes 1893071d4279SBram Moolenaar * the difference. Can't remove the entry here, we might be halfway 1894071d4279SBram Moolenaar * updating the window. Just report the text as unchanged. Other 1895071d4279SBram Moolenaar * windows might still show the change though. */ 1896071d4279SBram Moolenaar if (zero == FALSE) 1897071d4279SBram Moolenaar return 0; 1898071d4279SBram Moolenaar return -2; 1899071d4279SBram Moolenaar } 1900071d4279SBram Moolenaar 1901071d4279SBram Moolenaar /* If 'diffopt' doesn't contain "filler", return 0. */ 1902071d4279SBram Moolenaar if (!(diff_flags & DIFF_FILLER)) 1903071d4279SBram Moolenaar return 0; 1904071d4279SBram Moolenaar 1905071d4279SBram Moolenaar /* Insert filler lines above the line just below the change. Will return 1906071d4279SBram Moolenaar * 0 when this buf had the max count. */ 1907071d4279SBram Moolenaar maxcount = 0; 1908071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 190949d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && dp->df_count[i] > maxcount) 1910071d4279SBram Moolenaar maxcount = dp->df_count[i]; 1911071d4279SBram Moolenaar return maxcount - dp->df_count[idx]; 1912071d4279SBram Moolenaar } 1913071d4279SBram Moolenaar 1914071d4279SBram Moolenaar /* 1915071d4279SBram Moolenaar * Compare two entries in diff "*dp" and return TRUE if they are equal. 1916071d4279SBram Moolenaar */ 1917071d4279SBram Moolenaar static int 19187454a06eSBram Moolenaar diff_equal_entry(diff_T *dp, int idx1, int idx2) 1919071d4279SBram Moolenaar { 1920071d4279SBram Moolenaar int i; 1921071d4279SBram Moolenaar char_u *line; 1922071d4279SBram Moolenaar int cmp; 1923071d4279SBram Moolenaar 1924071d4279SBram Moolenaar if (dp->df_count[idx1] != dp->df_count[idx2]) 1925071d4279SBram Moolenaar return FALSE; 192649d7bf13SBram Moolenaar if (diff_check_sanity(curtab, dp) == FAIL) 1927071d4279SBram Moolenaar return FALSE; 1928071d4279SBram Moolenaar for (i = 0; i < dp->df_count[idx1]; ++i) 1929071d4279SBram Moolenaar { 193049d7bf13SBram Moolenaar line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1], 1931071d4279SBram Moolenaar dp->df_lnum[idx1] + i, FALSE)); 1932071d4279SBram Moolenaar if (line == NULL) 1933071d4279SBram Moolenaar return FALSE; 193449d7bf13SBram Moolenaar cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], 1935071d4279SBram Moolenaar dp->df_lnum[idx2] + i, FALSE)); 1936071d4279SBram Moolenaar vim_free(line); 1937071d4279SBram Moolenaar if (cmp != 0) 1938071d4279SBram Moolenaar return FALSE; 1939071d4279SBram Moolenaar } 1940071d4279SBram Moolenaar return TRUE; 1941071d4279SBram Moolenaar } 1942071d4279SBram Moolenaar 1943071d4279SBram Moolenaar /* 1944ae96b8d0SBram Moolenaar * Compare the characters at "p1" and "p2". If they are equal (possibly 1945ae96b8d0SBram Moolenaar * ignoring case) return TRUE and set "len" to the number of bytes. 1946ae96b8d0SBram Moolenaar */ 1947ae96b8d0SBram Moolenaar static int 1948ae96b8d0SBram Moolenaar diff_equal_char(char_u *p1, char_u *p2, int *len) 1949ae96b8d0SBram Moolenaar { 1950ae96b8d0SBram Moolenaar #ifdef FEAT_MBYTE 1951ae96b8d0SBram Moolenaar int l = (*mb_ptr2len)(p1); 1952ae96b8d0SBram Moolenaar 1953ae96b8d0SBram Moolenaar if (l != (*mb_ptr2len)(p2)) 1954ae96b8d0SBram Moolenaar return FALSE; 1955ae96b8d0SBram Moolenaar if (l > 1) 1956ae96b8d0SBram Moolenaar { 1957ae96b8d0SBram Moolenaar if (STRNCMP(p1, p2, l) != 0 1958ae96b8d0SBram Moolenaar && (!enc_utf8 1959ae96b8d0SBram Moolenaar || !(diff_flags & DIFF_ICASE) 1960ae96b8d0SBram Moolenaar || utf_fold(utf_ptr2char(p1)) 1961ae96b8d0SBram Moolenaar != utf_fold(utf_ptr2char(p2)))) 1962ae96b8d0SBram Moolenaar return FALSE; 1963ae96b8d0SBram Moolenaar *len = l; 1964ae96b8d0SBram Moolenaar } 1965ae96b8d0SBram Moolenaar else 1966ae96b8d0SBram Moolenaar #endif 1967ae96b8d0SBram Moolenaar { 1968ae96b8d0SBram Moolenaar if ((*p1 != *p2) 1969ae96b8d0SBram Moolenaar && (!(diff_flags & DIFF_ICASE) 1970ae96b8d0SBram Moolenaar || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) 1971ae96b8d0SBram Moolenaar return FALSE; 1972ae96b8d0SBram Moolenaar *len = 1; 1973ae96b8d0SBram Moolenaar } 1974ae96b8d0SBram Moolenaar return TRUE; 1975ae96b8d0SBram Moolenaar } 1976ae96b8d0SBram Moolenaar 1977ae96b8d0SBram Moolenaar /* 1978071d4279SBram Moolenaar * Compare strings "s1" and "s2" according to 'diffopt'. 1979071d4279SBram Moolenaar * Return non-zero when they are different. 1980071d4279SBram Moolenaar */ 1981071d4279SBram Moolenaar static int 19827454a06eSBram Moolenaar diff_cmp(char_u *s1, char_u *s2) 1983071d4279SBram Moolenaar { 1984071d4279SBram Moolenaar char_u *p1, *p2; 1985071d4279SBram Moolenaar int l; 1986071d4279SBram Moolenaar 1987785fc656SBram Moolenaar if ((diff_flags & DIFF_IBLANK) 1988785fc656SBram Moolenaar && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) 1989785fc656SBram Moolenaar return 0; 1990785fc656SBram Moolenaar 1991785fc656SBram Moolenaar if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) 1992071d4279SBram Moolenaar return STRCMP(s1, s2); 1993785fc656SBram Moolenaar if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) 1994071d4279SBram Moolenaar return MB_STRICMP(s1, s2); 1995071d4279SBram Moolenaar 1996071d4279SBram Moolenaar p1 = s1; 1997071d4279SBram Moolenaar p2 = s2; 1998785fc656SBram Moolenaar 1999785fc656SBram Moolenaar // Ignore white space changes and possibly ignore case. 2000071d4279SBram Moolenaar while (*p1 != NUL && *p2 != NUL) 2001071d4279SBram Moolenaar { 2002785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 2003785fc656SBram Moolenaar && VIM_ISWHITE(*p1) && VIM_ISWHITE(*p2)) 2004785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2005785fc656SBram Moolenaar && (VIM_ISWHITE(*p1) || VIM_ISWHITE(*p2)))) 2006071d4279SBram Moolenaar { 2007071d4279SBram Moolenaar p1 = skipwhite(p1); 2008071d4279SBram Moolenaar p2 = skipwhite(p2); 2009071d4279SBram Moolenaar } 2010071d4279SBram Moolenaar else 2011071d4279SBram Moolenaar { 2012ae96b8d0SBram Moolenaar if (!diff_equal_char(p1, p2, &l)) 2013071d4279SBram Moolenaar break; 2014071d4279SBram Moolenaar p1 += l; 2015071d4279SBram Moolenaar p2 += l; 2016071d4279SBram Moolenaar } 2017071d4279SBram Moolenaar } 2018071d4279SBram Moolenaar 2019785fc656SBram Moolenaar // Ignore trailing white space. 2020071d4279SBram Moolenaar p1 = skipwhite(p1); 2021071d4279SBram Moolenaar p2 = skipwhite(p2); 2022071d4279SBram Moolenaar if (*p1 != NUL || *p2 != NUL) 2023071d4279SBram Moolenaar return 1; 2024071d4279SBram Moolenaar return 0; 2025071d4279SBram Moolenaar } 2026071d4279SBram Moolenaar 2027071d4279SBram Moolenaar /* 2028071d4279SBram Moolenaar * Return the number of filler lines above "lnum". 2029071d4279SBram Moolenaar */ 2030071d4279SBram Moolenaar int 20317454a06eSBram Moolenaar diff_check_fill(win_T *wp, linenr_T lnum) 2032071d4279SBram Moolenaar { 2033071d4279SBram Moolenaar int n; 2034071d4279SBram Moolenaar 2035071d4279SBram Moolenaar /* be quick when there are no filler lines */ 2036071d4279SBram Moolenaar if (!(diff_flags & DIFF_FILLER)) 2037071d4279SBram Moolenaar return 0; 2038071d4279SBram Moolenaar n = diff_check(wp, lnum); 2039071d4279SBram Moolenaar if (n <= 0) 2040071d4279SBram Moolenaar return 0; 2041071d4279SBram Moolenaar return n; 2042071d4279SBram Moolenaar } 2043071d4279SBram Moolenaar 2044071d4279SBram Moolenaar /* 2045071d4279SBram Moolenaar * Set the topline of "towin" to match the position in "fromwin", so that they 2046071d4279SBram Moolenaar * show the same diff'ed lines. 2047071d4279SBram Moolenaar */ 2048071d4279SBram Moolenaar void 20497454a06eSBram Moolenaar diff_set_topline(win_T *fromwin, win_T *towin) 2050071d4279SBram Moolenaar { 2051bb8f88bbSBram Moolenaar buf_T *frombuf = fromwin->w_buffer; 2052071d4279SBram Moolenaar linenr_T lnum = fromwin->w_topline; 2053bb8f88bbSBram Moolenaar int fromidx; 2054bb8f88bbSBram Moolenaar int toidx; 2055071d4279SBram Moolenaar diff_T *dp; 2056bb8f88bbSBram Moolenaar int max_count; 2057071d4279SBram Moolenaar int i; 2058071d4279SBram Moolenaar 2059bb8f88bbSBram Moolenaar fromidx = diff_buf_idx(frombuf); 2060bb8f88bbSBram Moolenaar if (fromidx == DB_COUNT) 2061071d4279SBram Moolenaar return; /* safety check */ 2062071d4279SBram Moolenaar 206349d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 2064071d4279SBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 2065071d4279SBram Moolenaar 2066071d4279SBram Moolenaar towin->w_topfill = 0; 2067071d4279SBram Moolenaar 2068071d4279SBram Moolenaar /* search for a change that includes "lnum" in the list of diffblocks. */ 206949d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 2070bb8f88bbSBram Moolenaar if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) 2071071d4279SBram Moolenaar break; 2072071d4279SBram Moolenaar if (dp == NULL) 2073071d4279SBram Moolenaar { 2074071d4279SBram Moolenaar /* After last change, compute topline relative to end of file; no 2075071d4279SBram Moolenaar * filler lines. */ 2076071d4279SBram Moolenaar towin->w_topline = towin->w_buffer->b_ml.ml_line_count 2077bb8f88bbSBram Moolenaar - (frombuf->b_ml.ml_line_count - lnum); 2078071d4279SBram Moolenaar } 2079071d4279SBram Moolenaar else 2080071d4279SBram Moolenaar { 2081071d4279SBram Moolenaar /* Find index for "towin". */ 2082bb8f88bbSBram Moolenaar toidx = diff_buf_idx(towin->w_buffer); 2083bb8f88bbSBram Moolenaar if (toidx == DB_COUNT) 2084071d4279SBram Moolenaar return; /* safety check */ 2085071d4279SBram Moolenaar 2086bb8f88bbSBram Moolenaar towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]); 2087bb8f88bbSBram Moolenaar if (lnum >= dp->df_lnum[fromidx]) 2088071d4279SBram Moolenaar { 2089bb8f88bbSBram Moolenaar /* Inside a change: compute filler lines. With three or more 2090bb8f88bbSBram Moolenaar * buffers we need to know the largest count. */ 2091bb8f88bbSBram Moolenaar max_count = 0; 2092bb8f88bbSBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2093bb8f88bbSBram Moolenaar if (curtab->tp_diffbuf[i] != NULL 2094bb8f88bbSBram Moolenaar && max_count < dp->df_count[i]) 2095bb8f88bbSBram Moolenaar max_count = dp->df_count[i]; 2096bb8f88bbSBram Moolenaar 2097bb8f88bbSBram Moolenaar if (dp->df_count[toidx] == dp->df_count[fromidx]) 2098bb8f88bbSBram Moolenaar { 2099bb8f88bbSBram Moolenaar /* same number of lines: use same filler count */ 2100071d4279SBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2101bb8f88bbSBram Moolenaar } 2102bb8f88bbSBram Moolenaar else if (dp->df_count[toidx] > dp->df_count[fromidx]) 2103071d4279SBram Moolenaar { 2104bb8f88bbSBram Moolenaar if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) 2105bb8f88bbSBram Moolenaar { 2106bb8f88bbSBram Moolenaar /* more lines in towin and fromwin doesn't show diff 2107bb8f88bbSBram Moolenaar * lines, only filler lines */ 2108bb8f88bbSBram Moolenaar if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) 2109bb8f88bbSBram Moolenaar { 2110bb8f88bbSBram Moolenaar /* towin also only shows filler lines */ 2111bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] 2112bb8f88bbSBram Moolenaar + dp->df_count[toidx]; 2113bb8f88bbSBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2114071d4279SBram Moolenaar } 2115071d4279SBram Moolenaar else 2116bb8f88bbSBram Moolenaar /* towin still has some diff lines to show */ 2117bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] 2118bb8f88bbSBram Moolenaar + max_count - fromwin->w_topfill; 2119bb8f88bbSBram Moolenaar } 2120bb8f88bbSBram Moolenaar } 2121bb8f88bbSBram Moolenaar else if (towin->w_topline >= dp->df_lnum[toidx] 2122bb8f88bbSBram Moolenaar + dp->df_count[toidx]) 2123071d4279SBram Moolenaar { 2124bb8f88bbSBram Moolenaar /* less lines in towin and no diff lines to show: compute 2125bb8f88bbSBram Moolenaar * filler lines */ 2126bb8f88bbSBram Moolenaar towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx]; 2127071d4279SBram Moolenaar if (diff_flags & DIFF_FILLER) 2128bb8f88bbSBram Moolenaar { 2129bb8f88bbSBram Moolenaar if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) 2130bb8f88bbSBram Moolenaar /* fromwin is also out of diff lines */ 2131bb8f88bbSBram Moolenaar towin->w_topfill = fromwin->w_topfill; 2132bb8f88bbSBram Moolenaar else 2133bb8f88bbSBram Moolenaar /* fromwin has some diff lines */ 2134bb8f88bbSBram Moolenaar towin->w_topfill = dp->df_lnum[fromidx] 2135bb8f88bbSBram Moolenaar + max_count - lnum; 2136071d4279SBram Moolenaar } 2137071d4279SBram Moolenaar } 2138071d4279SBram Moolenaar } 2139071d4279SBram Moolenaar } 2140071d4279SBram Moolenaar 2141071d4279SBram Moolenaar /* safety check (if diff info gets outdated strange things may happen) */ 2142071d4279SBram Moolenaar towin->w_botfill = FALSE; 2143071d4279SBram Moolenaar if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) 2144071d4279SBram Moolenaar { 2145071d4279SBram Moolenaar towin->w_topline = towin->w_buffer->b_ml.ml_line_count; 2146071d4279SBram Moolenaar towin->w_botfill = TRUE; 2147071d4279SBram Moolenaar } 2148071d4279SBram Moolenaar if (towin->w_topline < 1) 2149071d4279SBram Moolenaar { 2150071d4279SBram Moolenaar towin->w_topline = 1; 2151071d4279SBram Moolenaar towin->w_topfill = 0; 2152071d4279SBram Moolenaar } 2153071d4279SBram Moolenaar 2154071d4279SBram Moolenaar /* When w_topline changes need to recompute w_botline and cursor position */ 2155071d4279SBram Moolenaar invalidate_botline_win(towin); 2156071d4279SBram Moolenaar changed_line_abv_curs_win(towin); 2157071d4279SBram Moolenaar 2158071d4279SBram Moolenaar check_topfill(towin, FALSE); 2159071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2160071d4279SBram Moolenaar (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, 2161071d4279SBram Moolenaar NULL, TRUE, NULL); 2162071d4279SBram Moolenaar #endif 2163071d4279SBram Moolenaar } 2164071d4279SBram Moolenaar 2165071d4279SBram Moolenaar /* 2166071d4279SBram Moolenaar * This is called when 'diffopt' is changed. 2167071d4279SBram Moolenaar */ 2168071d4279SBram Moolenaar int 21697454a06eSBram Moolenaar diffopt_changed(void) 2170071d4279SBram Moolenaar { 2171071d4279SBram Moolenaar char_u *p; 2172071d4279SBram Moolenaar int diff_context_new = 6; 2173071d4279SBram Moolenaar int diff_flags_new = 0; 2174c4675a19SBram Moolenaar int diff_foldcolumn_new = 2; 2175e828b762SBram Moolenaar long diff_algorithm_new = 0; 217649d7bf13SBram Moolenaar tabpage_T *tp; 2177071d4279SBram Moolenaar 2178071d4279SBram Moolenaar p = p_dip; 2179071d4279SBram Moolenaar while (*p != NUL) 2180071d4279SBram Moolenaar { 2181071d4279SBram Moolenaar if (STRNCMP(p, "filler", 6) == 0) 2182071d4279SBram Moolenaar { 2183071d4279SBram Moolenaar p += 6; 2184071d4279SBram Moolenaar diff_flags_new |= DIFF_FILLER; 2185071d4279SBram Moolenaar } 2186071d4279SBram Moolenaar else if (STRNCMP(p, "context:", 8) == 0 && VIM_ISDIGIT(p[8])) 2187071d4279SBram Moolenaar { 2188071d4279SBram Moolenaar p += 8; 2189071d4279SBram Moolenaar diff_context_new = getdigits(&p); 2190071d4279SBram Moolenaar } 2191785fc656SBram Moolenaar else if (STRNCMP(p, "iblank", 6) == 0) 2192785fc656SBram Moolenaar { 2193785fc656SBram Moolenaar p += 6; 2194785fc656SBram Moolenaar diff_flags_new |= DIFF_IBLANK; 2195785fc656SBram Moolenaar } 2196071d4279SBram Moolenaar else if (STRNCMP(p, "icase", 5) == 0) 2197071d4279SBram Moolenaar { 2198071d4279SBram Moolenaar p += 5; 2199071d4279SBram Moolenaar diff_flags_new |= DIFF_ICASE; 2200071d4279SBram Moolenaar } 2201785fc656SBram Moolenaar else if (STRNCMP(p, "iwhiteall", 9) == 0) 2202785fc656SBram Moolenaar { 2203785fc656SBram Moolenaar p += 9; 2204785fc656SBram Moolenaar diff_flags_new |= DIFF_IWHITEALL; 2205785fc656SBram Moolenaar } 2206785fc656SBram Moolenaar else if (STRNCMP(p, "iwhiteeol", 9) == 0) 2207785fc656SBram Moolenaar { 2208785fc656SBram Moolenaar p += 9; 2209785fc656SBram Moolenaar diff_flags_new |= DIFF_IWHITEEOL; 2210785fc656SBram Moolenaar } 2211071d4279SBram Moolenaar else if (STRNCMP(p, "iwhite", 6) == 0) 2212071d4279SBram Moolenaar { 2213071d4279SBram Moolenaar p += 6; 2214071d4279SBram Moolenaar diff_flags_new |= DIFF_IWHITE; 2215071d4279SBram Moolenaar } 2216c4675a19SBram Moolenaar else if (STRNCMP(p, "horizontal", 10) == 0) 2217c4675a19SBram Moolenaar { 2218c4675a19SBram Moolenaar p += 10; 2219c4675a19SBram Moolenaar diff_flags_new |= DIFF_HORIZONTAL; 2220c4675a19SBram Moolenaar } 2221c4675a19SBram Moolenaar else if (STRNCMP(p, "vertical", 8) == 0) 2222c4675a19SBram Moolenaar { 2223c4675a19SBram Moolenaar p += 8; 2224c4675a19SBram Moolenaar diff_flags_new |= DIFF_VERTICAL; 2225c4675a19SBram Moolenaar } 2226c4675a19SBram Moolenaar else if (STRNCMP(p, "foldcolumn:", 11) == 0 && VIM_ISDIGIT(p[11])) 2227c4675a19SBram Moolenaar { 2228c4675a19SBram Moolenaar p += 11; 2229c4675a19SBram Moolenaar diff_foldcolumn_new = getdigits(&p); 2230c4675a19SBram Moolenaar } 223197ce4192SBram Moolenaar else if (STRNCMP(p, "hiddenoff", 9) == 0) 223297ce4192SBram Moolenaar { 223397ce4192SBram Moolenaar p += 9; 223497ce4192SBram Moolenaar diff_flags_new |= DIFF_HIDDEN_OFF; 223597ce4192SBram Moolenaar } 2236e828b762SBram Moolenaar else if (STRNCMP(p, "indent-heuristic", 16) == 0) 2237e828b762SBram Moolenaar { 2238e828b762SBram Moolenaar p += 16; 2239e828b762SBram Moolenaar diff_algorithm_new |= XDF_INDENT_HEURISTIC; 2240e828b762SBram Moolenaar } 2241e828b762SBram Moolenaar else if (STRNCMP(p, "internal", 8) == 0) 2242e828b762SBram Moolenaar { 2243e828b762SBram Moolenaar p += 8; 2244e828b762SBram Moolenaar diff_flags_new |= DIFF_INTERNAL; 2245e828b762SBram Moolenaar } 2246e828b762SBram Moolenaar else if (STRNCMP(p, "algorithm:", 10) == 0) 2247e828b762SBram Moolenaar { 2248e828b762SBram Moolenaar p += 10; 2249e828b762SBram Moolenaar if (STRNCMP(p, "myers", 5) == 0) 2250e828b762SBram Moolenaar { 2251e828b762SBram Moolenaar p += 5; 2252e828b762SBram Moolenaar diff_algorithm_new = 0; 2253e828b762SBram Moolenaar } 2254e828b762SBram Moolenaar else if (STRNCMP(p, "minimal", 7) == 0) 2255e828b762SBram Moolenaar { 2256e828b762SBram Moolenaar p += 7; 2257e828b762SBram Moolenaar diff_algorithm_new = XDF_NEED_MINIMAL; 2258e828b762SBram Moolenaar } 2259e828b762SBram Moolenaar else if (STRNCMP(p, "patience", 8) == 0) 2260e828b762SBram Moolenaar { 2261e828b762SBram Moolenaar p += 8; 2262e828b762SBram Moolenaar diff_algorithm_new = XDF_PATIENCE_DIFF; 2263e828b762SBram Moolenaar } 2264e828b762SBram Moolenaar else if (STRNCMP(p, "histogram", 9) == 0) 2265e828b762SBram Moolenaar { 2266e828b762SBram Moolenaar p += 9; 2267e828b762SBram Moolenaar diff_algorithm_new = XDF_HISTOGRAM_DIFF; 2268e828b762SBram Moolenaar } 2269e828b762SBram Moolenaar } 2270e828b762SBram Moolenaar 2271071d4279SBram Moolenaar if (*p != ',' && *p != NUL) 2272071d4279SBram Moolenaar return FAIL; 2273071d4279SBram Moolenaar if (*p == ',') 2274071d4279SBram Moolenaar ++p; 2275071d4279SBram Moolenaar } 2276071d4279SBram Moolenaar 2277c4675a19SBram Moolenaar /* Can't have both "horizontal" and "vertical". */ 2278c4675a19SBram Moolenaar if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) 2279c4675a19SBram Moolenaar return FAIL; 2280c4675a19SBram Moolenaar 2281198fa066SBram Moolenaar // If flags were added or removed, or the algorithm was changed, need to 2282198fa066SBram Moolenaar // update the diff. 2283e828b762SBram Moolenaar if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) 228429323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 228549d7bf13SBram Moolenaar tp->tp_diff_invalid = TRUE; 2286071d4279SBram Moolenaar 2287071d4279SBram Moolenaar diff_flags = diff_flags_new; 2288071d4279SBram Moolenaar diff_context = diff_context_new; 2289c4675a19SBram Moolenaar diff_foldcolumn = diff_foldcolumn_new; 2290e828b762SBram Moolenaar diff_algorithm = diff_algorithm_new; 2291071d4279SBram Moolenaar 2292071d4279SBram Moolenaar diff_redraw(TRUE); 2293071d4279SBram Moolenaar 2294071d4279SBram Moolenaar /* recompute the scroll binding with the new option value, may 2295071d4279SBram Moolenaar * remove or add filler lines */ 2296071d4279SBram Moolenaar check_scrollbind((linenr_T)0, 0L); 2297071d4279SBram Moolenaar 2298071d4279SBram Moolenaar return OK; 2299071d4279SBram Moolenaar } 2300071d4279SBram Moolenaar 2301071d4279SBram Moolenaar /* 2302c4675a19SBram Moolenaar * Return TRUE if 'diffopt' contains "horizontal". 2303c4675a19SBram Moolenaar */ 2304c4675a19SBram Moolenaar int 23057454a06eSBram Moolenaar diffopt_horizontal(void) 2306c4675a19SBram Moolenaar { 2307c4675a19SBram Moolenaar return (diff_flags & DIFF_HORIZONTAL) != 0; 2308c4675a19SBram Moolenaar } 2309c4675a19SBram Moolenaar 2310c4675a19SBram Moolenaar /* 231197ce4192SBram Moolenaar * Return TRUE if 'diffopt' contains "hiddenoff". 231297ce4192SBram Moolenaar */ 231397ce4192SBram Moolenaar int 231497ce4192SBram Moolenaar diffopt_hiddenoff(void) 231597ce4192SBram Moolenaar { 231697ce4192SBram Moolenaar return (diff_flags & DIFF_HIDDEN_OFF) != 0; 231797ce4192SBram Moolenaar } 231897ce4192SBram Moolenaar 231997ce4192SBram Moolenaar /* 2320071d4279SBram Moolenaar * Find the difference within a changed line. 2321071d4279SBram Moolenaar * Returns TRUE if the line was added, no other buffer has it. 2322071d4279SBram Moolenaar */ 2323071d4279SBram Moolenaar int 23247454a06eSBram Moolenaar diff_find_change( 23257454a06eSBram Moolenaar win_T *wp, 23267454a06eSBram Moolenaar linenr_T lnum, 23277454a06eSBram Moolenaar int *startp, /* first char of the change */ 23287454a06eSBram Moolenaar int *endp) /* last char of the change */ 2329071d4279SBram Moolenaar { 2330071d4279SBram Moolenaar char_u *line_org; 2331071d4279SBram Moolenaar char_u *line_new; 2332071d4279SBram Moolenaar int i; 23339e54a0e7SBram Moolenaar int si_org, si_new; 23349e54a0e7SBram Moolenaar int ei_org, ei_new; 2335071d4279SBram Moolenaar diff_T *dp; 2336071d4279SBram Moolenaar int idx; 2337071d4279SBram Moolenaar int off; 2338071d4279SBram Moolenaar int added = TRUE; 2339da22b8ccSBram Moolenaar char_u *p1, *p2; 2340da22b8ccSBram Moolenaar int l; 2341071d4279SBram Moolenaar 2342071d4279SBram Moolenaar /* Make a copy of the line, the next ml_get() will invalidate it. */ 2343071d4279SBram Moolenaar line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); 2344071d4279SBram Moolenaar if (line_org == NULL) 2345071d4279SBram Moolenaar return FALSE; 2346071d4279SBram Moolenaar 2347071d4279SBram Moolenaar idx = diff_buf_idx(wp->w_buffer); 2348071d4279SBram Moolenaar if (idx == DB_COUNT) /* cannot happen */ 2349fa3491a0SBram Moolenaar { 2350fa3491a0SBram Moolenaar vim_free(line_org); 2351071d4279SBram Moolenaar return FALSE; 2352fa3491a0SBram Moolenaar } 2353071d4279SBram Moolenaar 2354071d4279SBram Moolenaar /* search for a change that includes "lnum" in the list of diffblocks. */ 235549d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 2356071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 2357071d4279SBram Moolenaar break; 235849d7bf13SBram Moolenaar if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL) 2359fa3491a0SBram Moolenaar { 2360fa3491a0SBram Moolenaar vim_free(line_org); 2361071d4279SBram Moolenaar return FALSE; 2362fa3491a0SBram Moolenaar } 2363071d4279SBram Moolenaar 2364071d4279SBram Moolenaar off = lnum - dp->df_lnum[idx]; 2365071d4279SBram Moolenaar 2366071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 236749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && i != idx) 2368071d4279SBram Moolenaar { 2369071d4279SBram Moolenaar /* Skip lines that are not in the other change (filler lines). */ 2370071d4279SBram Moolenaar if (off >= dp->df_count[i]) 2371071d4279SBram Moolenaar continue; 2372071d4279SBram Moolenaar added = FALSE; 23739e54a0e7SBram Moolenaar line_new = ml_get_buf(curtab->tp_diffbuf[i], 23749e54a0e7SBram Moolenaar dp->df_lnum[i] + off, FALSE); 2375071d4279SBram Moolenaar 2376071d4279SBram Moolenaar /* Search for start of difference */ 23779e54a0e7SBram Moolenaar si_org = si_new = 0; 23789e54a0e7SBram Moolenaar while (line_org[si_org] != NUL) 23799e54a0e7SBram Moolenaar { 2380785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 23811c465444SBram Moolenaar && VIM_ISWHITE(line_org[si_org]) 23821c465444SBram Moolenaar && VIM_ISWHITE(line_new[si_new])) 2383785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2384785fc656SBram Moolenaar && (VIM_ISWHITE(line_org[si_org]) 2385785fc656SBram Moolenaar || VIM_ISWHITE(line_new[si_new])))) 23869e54a0e7SBram Moolenaar { 2387a93fa7eeSBram Moolenaar si_org = (int)(skipwhite(line_org + si_org) - line_org); 2388a93fa7eeSBram Moolenaar si_new = (int)(skipwhite(line_new + si_new) - line_new); 23899e54a0e7SBram Moolenaar } 23909e54a0e7SBram Moolenaar else 23919e54a0e7SBram Moolenaar { 2392da22b8ccSBram Moolenaar if (!diff_equal_char(line_org + si_org, line_new + si_new, 2393da22b8ccSBram Moolenaar &l)) 23949e54a0e7SBram Moolenaar break; 2395da22b8ccSBram Moolenaar si_org += l; 2396da22b8ccSBram Moolenaar si_new += l; 23979e54a0e7SBram Moolenaar } 23989e54a0e7SBram Moolenaar } 2399071d4279SBram Moolenaar #ifdef FEAT_MBYTE 2400071d4279SBram Moolenaar if (has_mbyte) 2401071d4279SBram Moolenaar { 2402071d4279SBram Moolenaar /* Move back to first byte of character in both lines (may 2403071d4279SBram Moolenaar * have "nn^" in line_org and "n^ in line_new). */ 24049e54a0e7SBram Moolenaar si_org -= (*mb_head_off)(line_org, line_org + si_org); 24059e54a0e7SBram Moolenaar si_new -= (*mb_head_off)(line_new, line_new + si_new); 2406071d4279SBram Moolenaar } 2407071d4279SBram Moolenaar #endif 24089e54a0e7SBram Moolenaar if (*startp > si_org) 24099e54a0e7SBram Moolenaar *startp = si_org; 2410071d4279SBram Moolenaar 2411071d4279SBram Moolenaar /* Search for end of difference, if any. */ 24129e54a0e7SBram Moolenaar if (line_org[si_org] != NUL || line_new[si_new] != NUL) 2413071d4279SBram Moolenaar { 2414071d4279SBram Moolenaar ei_org = (int)STRLEN(line_org); 2415071d4279SBram Moolenaar ei_new = (int)STRLEN(line_new); 24169e54a0e7SBram Moolenaar while (ei_org >= *startp && ei_new >= si_new 24179e54a0e7SBram Moolenaar && ei_org >= 0 && ei_new >= 0) 2418071d4279SBram Moolenaar { 2419785fc656SBram Moolenaar if (((diff_flags & DIFF_IWHITE) 24201c465444SBram Moolenaar && VIM_ISWHITE(line_org[ei_org]) 24211c465444SBram Moolenaar && VIM_ISWHITE(line_new[ei_new])) 2422785fc656SBram Moolenaar || ((diff_flags & DIFF_IWHITEALL) 2423785fc656SBram Moolenaar && (VIM_ISWHITE(line_org[ei_org]) 2424785fc656SBram Moolenaar || VIM_ISWHITE(line_new[ei_new])))) 24259e54a0e7SBram Moolenaar { 24269e54a0e7SBram Moolenaar while (ei_org >= *startp 24271c465444SBram Moolenaar && VIM_ISWHITE(line_org[ei_org])) 24289e54a0e7SBram Moolenaar --ei_org; 24299e54a0e7SBram Moolenaar while (ei_new >= si_new 24301c465444SBram Moolenaar && VIM_ISWHITE(line_new[ei_new])) 24319e54a0e7SBram Moolenaar --ei_new; 24329e54a0e7SBram Moolenaar } 24339e54a0e7SBram Moolenaar else 24349e54a0e7SBram Moolenaar { 2435da22b8ccSBram Moolenaar p1 = line_org + ei_org; 2436da22b8ccSBram Moolenaar p2 = line_new + ei_new; 2437da22b8ccSBram Moolenaar #ifdef FEAT_MBYTE 2438da22b8ccSBram Moolenaar p1 -= (*mb_head_off)(line_org, p1); 2439da22b8ccSBram Moolenaar p2 -= (*mb_head_off)(line_new, p2); 2440da22b8ccSBram Moolenaar #endif 2441da22b8ccSBram Moolenaar if (!diff_equal_char(p1, p2, &l)) 24429e54a0e7SBram Moolenaar break; 2443da22b8ccSBram Moolenaar ei_org -= l; 2444da22b8ccSBram Moolenaar ei_new -= l; 2445071d4279SBram Moolenaar } 24469e54a0e7SBram Moolenaar } 2447071d4279SBram Moolenaar if (*endp < ei_org) 2448071d4279SBram Moolenaar *endp = ei_org; 2449071d4279SBram Moolenaar } 2450071d4279SBram Moolenaar } 2451071d4279SBram Moolenaar 2452071d4279SBram Moolenaar vim_free(line_org); 2453071d4279SBram Moolenaar return added; 2454071d4279SBram Moolenaar } 2455071d4279SBram Moolenaar 2456071d4279SBram Moolenaar #if defined(FEAT_FOLDING) || defined(PROTO) 2457071d4279SBram Moolenaar /* 2458071d4279SBram Moolenaar * Return TRUE if line "lnum" is not close to a diff block, this line should 2459071d4279SBram Moolenaar * be in a fold. 2460071d4279SBram Moolenaar * Return FALSE if there are no diff blocks at all in this window. 2461071d4279SBram Moolenaar */ 2462071d4279SBram Moolenaar int 24637454a06eSBram Moolenaar diff_infold(win_T *wp, linenr_T lnum) 2464071d4279SBram Moolenaar { 2465071d4279SBram Moolenaar int i; 2466071d4279SBram Moolenaar int idx = -1; 2467071d4279SBram Moolenaar int other = FALSE; 2468071d4279SBram Moolenaar diff_T *dp; 2469071d4279SBram Moolenaar 2470071d4279SBram Moolenaar /* Return if 'diff' isn't set. */ 2471071d4279SBram Moolenaar if (!wp->w_p_diff) 2472071d4279SBram Moolenaar return FALSE; 2473071d4279SBram Moolenaar 2474071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2475071d4279SBram Moolenaar { 247649d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] == wp->w_buffer) 2477071d4279SBram Moolenaar idx = i; 247849d7bf13SBram Moolenaar else if (curtab->tp_diffbuf[i] != NULL) 2479071d4279SBram Moolenaar other = TRUE; 2480071d4279SBram Moolenaar } 2481071d4279SBram Moolenaar 2482071d4279SBram Moolenaar /* return here if there are no diffs in the window */ 2483071d4279SBram Moolenaar if (idx == -1 || !other) 2484071d4279SBram Moolenaar return FALSE; 2485071d4279SBram Moolenaar 248649d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 2487071d4279SBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 2488071d4279SBram Moolenaar 2489071d4279SBram Moolenaar /* Return if there are no diff blocks. All lines will be folded. */ 249049d7bf13SBram Moolenaar if (curtab->tp_first_diff == NULL) 2491071d4279SBram Moolenaar return TRUE; 2492071d4279SBram Moolenaar 249349d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 2494071d4279SBram Moolenaar { 2495071d4279SBram Moolenaar /* If this change is below the line there can't be any further match. */ 2496071d4279SBram Moolenaar if (dp->df_lnum[idx] - diff_context > lnum) 2497071d4279SBram Moolenaar break; 2498071d4279SBram Moolenaar /* If this change ends before the line we have a match. */ 2499071d4279SBram Moolenaar if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) 2500071d4279SBram Moolenaar return FALSE; 2501071d4279SBram Moolenaar } 2502071d4279SBram Moolenaar return TRUE; 2503071d4279SBram Moolenaar } 2504071d4279SBram Moolenaar #endif 2505071d4279SBram Moolenaar 2506071d4279SBram Moolenaar /* 2507071d4279SBram Moolenaar * "dp" and "do" commands. 2508071d4279SBram Moolenaar */ 2509071d4279SBram Moolenaar void 25107454a06eSBram Moolenaar nv_diffgetput(int put, long count) 2511071d4279SBram Moolenaar { 2512071d4279SBram Moolenaar exarg_T ea; 25136a64365cSBram Moolenaar char_u buf[30]; 2514071d4279SBram Moolenaar 2515f273245fSBram Moolenaar #ifdef FEAT_JOB_CHANNEL 2516f273245fSBram Moolenaar if (bt_prompt(curbuf)) 2517f273245fSBram Moolenaar { 2518f273245fSBram Moolenaar vim_beep(BO_OPER); 2519f273245fSBram Moolenaar return; 2520f273245fSBram Moolenaar } 2521f273245fSBram Moolenaar #endif 25226a64365cSBram Moolenaar if (count == 0) 2523071d4279SBram Moolenaar ea.arg = (char_u *)""; 25246a64365cSBram Moolenaar else 25256a64365cSBram Moolenaar { 25266a64365cSBram Moolenaar vim_snprintf((char *)buf, 30, "%ld", count); 25276a64365cSBram Moolenaar ea.arg = buf; 25286a64365cSBram Moolenaar } 2529071d4279SBram Moolenaar if (put) 2530071d4279SBram Moolenaar ea.cmdidx = CMD_diffput; 2531071d4279SBram Moolenaar else 2532071d4279SBram Moolenaar ea.cmdidx = CMD_diffget; 2533071d4279SBram Moolenaar ea.addr_count = 0; 2534071d4279SBram Moolenaar ea.line1 = curwin->w_cursor.lnum; 2535071d4279SBram Moolenaar ea.line2 = curwin->w_cursor.lnum; 2536071d4279SBram Moolenaar ex_diffgetput(&ea); 2537071d4279SBram Moolenaar } 2538071d4279SBram Moolenaar 2539071d4279SBram Moolenaar /* 2540071d4279SBram Moolenaar * ":diffget" 2541071d4279SBram Moolenaar * ":diffput" 2542071d4279SBram Moolenaar */ 2543071d4279SBram Moolenaar void 25447454a06eSBram Moolenaar ex_diffgetput(exarg_T *eap) 2545071d4279SBram Moolenaar { 2546071d4279SBram Moolenaar linenr_T lnum; 2547071d4279SBram Moolenaar int count; 2548071d4279SBram Moolenaar linenr_T off = 0; 2549071d4279SBram Moolenaar diff_T *dp; 2550071d4279SBram Moolenaar diff_T *dprev; 2551071d4279SBram Moolenaar diff_T *dfree; 2552071d4279SBram Moolenaar int idx_cur; 2553071d4279SBram Moolenaar int idx_other; 2554071d4279SBram Moolenaar int idx_from; 2555071d4279SBram Moolenaar int idx_to; 2556071d4279SBram Moolenaar int i; 2557071d4279SBram Moolenaar int added; 2558071d4279SBram Moolenaar char_u *p; 2559071d4279SBram Moolenaar aco_save_T aco; 2560071d4279SBram Moolenaar buf_T *buf; 2561071d4279SBram Moolenaar int start_skip, end_skip; 2562071d4279SBram Moolenaar int new_count; 2563280f126eSBram Moolenaar int buf_empty; 2564602eb74dSBram Moolenaar int found_not_ma = FALSE; 2565071d4279SBram Moolenaar 2566071d4279SBram Moolenaar /* Find the current buffer in the list of diff buffers. */ 2567071d4279SBram Moolenaar idx_cur = diff_buf_idx(curbuf); 2568071d4279SBram Moolenaar if (idx_cur == DB_COUNT) 2569071d4279SBram Moolenaar { 2570071d4279SBram Moolenaar EMSG(_("E99: Current buffer is not in diff mode")); 2571071d4279SBram Moolenaar return; 2572071d4279SBram Moolenaar } 2573071d4279SBram Moolenaar 2574071d4279SBram Moolenaar if (*eap->arg == NUL) 2575071d4279SBram Moolenaar { 2576071d4279SBram Moolenaar /* No argument: Find the other buffer in the list of diff buffers. */ 2577071d4279SBram Moolenaar for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) 257849d7bf13SBram Moolenaar if (curtab->tp_diffbuf[idx_other] != curbuf 2579602eb74dSBram Moolenaar && curtab->tp_diffbuf[idx_other] != NULL) 2580602eb74dSBram Moolenaar { 2581602eb74dSBram Moolenaar if (eap->cmdidx != CMD_diffput 2582602eb74dSBram Moolenaar || curtab->tp_diffbuf[idx_other]->b_p_ma) 2583071d4279SBram Moolenaar break; 2584602eb74dSBram Moolenaar found_not_ma = TRUE; 2585602eb74dSBram Moolenaar } 2586071d4279SBram Moolenaar if (idx_other == DB_COUNT) 2587071d4279SBram Moolenaar { 2588602eb74dSBram Moolenaar if (found_not_ma) 2589602eb74dSBram Moolenaar EMSG(_("E793: No other buffer in diff mode is modifiable")); 2590602eb74dSBram Moolenaar else 2591071d4279SBram Moolenaar EMSG(_("E100: No other buffer in diff mode")); 2592071d4279SBram Moolenaar return; 2593071d4279SBram Moolenaar } 2594071d4279SBram Moolenaar 2595071d4279SBram Moolenaar /* Check that there isn't a third buffer in the list */ 2596071d4279SBram Moolenaar for (i = idx_other + 1; i < DB_COUNT; ++i) 259749d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] != curbuf 259849d7bf13SBram Moolenaar && curtab->tp_diffbuf[i] != NULL 259949d7bf13SBram Moolenaar && (eap->cmdidx != CMD_diffput || curtab->tp_diffbuf[i]->b_p_ma)) 2600071d4279SBram Moolenaar { 2601071d4279SBram Moolenaar EMSG(_("E101: More than two buffers in diff mode, don't know which one to use")); 2602071d4279SBram Moolenaar return; 2603071d4279SBram Moolenaar } 2604071d4279SBram Moolenaar } 2605071d4279SBram Moolenaar else 2606071d4279SBram Moolenaar { 2607071d4279SBram Moolenaar /* Buffer number or pattern given. Ignore trailing white space. */ 2608071d4279SBram Moolenaar p = eap->arg + STRLEN(eap->arg); 26091c465444SBram Moolenaar while (p > eap->arg && VIM_ISWHITE(p[-1])) 2610071d4279SBram Moolenaar --p; 2611071d4279SBram Moolenaar for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) 2612071d4279SBram Moolenaar ; 2613071d4279SBram Moolenaar if (eap->arg + i == p) /* digits only */ 2614071d4279SBram Moolenaar i = atol((char *)eap->arg); 2615071d4279SBram Moolenaar else 2616071d4279SBram Moolenaar { 26170c279bbbSBram Moolenaar i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE); 2618071d4279SBram Moolenaar if (i < 0) 2619071d4279SBram Moolenaar return; /* error message already given */ 2620071d4279SBram Moolenaar } 2621071d4279SBram Moolenaar buf = buflist_findnr(i); 2622071d4279SBram Moolenaar if (buf == NULL) 2623071d4279SBram Moolenaar { 2624071d4279SBram Moolenaar EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg); 2625071d4279SBram Moolenaar return; 2626071d4279SBram Moolenaar } 26275cc6a6e7SBram Moolenaar if (buf == curbuf) 26285cc6a6e7SBram Moolenaar return; /* nothing to do */ 2629071d4279SBram Moolenaar idx_other = diff_buf_idx(buf); 2630071d4279SBram Moolenaar if (idx_other == DB_COUNT) 2631071d4279SBram Moolenaar { 2632071d4279SBram Moolenaar EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg); 2633071d4279SBram Moolenaar return; 2634071d4279SBram Moolenaar } 2635071d4279SBram Moolenaar } 2636071d4279SBram Moolenaar 2637071d4279SBram Moolenaar diff_busy = TRUE; 2638071d4279SBram Moolenaar 2639071d4279SBram Moolenaar /* When no range given include the line above or below the cursor. */ 2640071d4279SBram Moolenaar if (eap->addr_count == 0) 2641071d4279SBram Moolenaar { 2642071d4279SBram Moolenaar /* Make it possible that ":diffget" on the last line gets line below 2643071d4279SBram Moolenaar * the cursor line when there is no difference above the cursor. */ 2644071d4279SBram Moolenaar if (eap->cmdidx == CMD_diffget 2645071d4279SBram Moolenaar && eap->line1 == curbuf->b_ml.ml_line_count 2646071d4279SBram Moolenaar && diff_check(curwin, eap->line1) == 0 2647071d4279SBram Moolenaar && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0)) 2648071d4279SBram Moolenaar ++eap->line2; 2649071d4279SBram Moolenaar else if (eap->line1 > 0) 2650071d4279SBram Moolenaar --eap->line1; 2651071d4279SBram Moolenaar } 2652071d4279SBram Moolenaar 2653071d4279SBram Moolenaar if (eap->cmdidx == CMD_diffget) 2654071d4279SBram Moolenaar { 2655071d4279SBram Moolenaar idx_from = idx_other; 2656071d4279SBram Moolenaar idx_to = idx_cur; 2657071d4279SBram Moolenaar } 2658071d4279SBram Moolenaar else 2659071d4279SBram Moolenaar { 2660071d4279SBram Moolenaar idx_from = idx_cur; 2661071d4279SBram Moolenaar idx_to = idx_other; 2662071d4279SBram Moolenaar /* Need to make the other buffer the current buffer to be able to make 2663071d4279SBram Moolenaar * changes in it. */ 2664071d4279SBram Moolenaar /* set curwin/curbuf to buf and save a few things */ 266549d7bf13SBram Moolenaar aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); 2666071d4279SBram Moolenaar } 2667071d4279SBram Moolenaar 2668910f66f9SBram Moolenaar /* May give the warning for a changed buffer here, which can trigger the 2669910f66f9SBram Moolenaar * FileChangedRO autocommand, which may do nasty things and mess 2670910f66f9SBram Moolenaar * everything up. */ 2671910f66f9SBram Moolenaar if (!curbuf->b_changed) 2672910f66f9SBram Moolenaar { 2673910f66f9SBram Moolenaar change_warning(0); 2674910f66f9SBram Moolenaar if (diff_buf_idx(curbuf) != idx_to) 2675910f66f9SBram Moolenaar { 2676910f66f9SBram Moolenaar EMSG(_("E787: Buffer changed unexpectedly")); 2677d2b58c0aSBram Moolenaar goto theend; 2678910f66f9SBram Moolenaar } 2679910f66f9SBram Moolenaar } 2680910f66f9SBram Moolenaar 2681071d4279SBram Moolenaar dprev = NULL; 268249d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; ) 2683071d4279SBram Moolenaar { 2684071d4279SBram Moolenaar if (dp->df_lnum[idx_cur] > eap->line2 + off) 2685071d4279SBram Moolenaar break; /* past the range that was specified */ 2686071d4279SBram Moolenaar 2687071d4279SBram Moolenaar dfree = NULL; 2688071d4279SBram Moolenaar lnum = dp->df_lnum[idx_to]; 2689071d4279SBram Moolenaar count = dp->df_count[idx_to]; 2690071d4279SBram Moolenaar if (dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off 2691071d4279SBram Moolenaar && u_save(lnum - 1, lnum + count) != FAIL) 2692071d4279SBram Moolenaar { 2693071d4279SBram Moolenaar /* Inside the specified range and saving for undo worked. */ 2694071d4279SBram Moolenaar start_skip = 0; 2695071d4279SBram Moolenaar end_skip = 0; 2696071d4279SBram Moolenaar if (eap->addr_count > 0) 2697071d4279SBram Moolenaar { 2698071d4279SBram Moolenaar /* A range was specified: check if lines need to be skipped. */ 2699071d4279SBram Moolenaar start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; 2700071d4279SBram Moolenaar if (start_skip > 0) 2701071d4279SBram Moolenaar { 2702071d4279SBram Moolenaar /* range starts below start of current diff block */ 2703071d4279SBram Moolenaar if (start_skip > count) 2704071d4279SBram Moolenaar { 2705071d4279SBram Moolenaar lnum += count; 2706071d4279SBram Moolenaar count = 0; 2707071d4279SBram Moolenaar } 2708071d4279SBram Moolenaar else 2709071d4279SBram Moolenaar { 2710071d4279SBram Moolenaar count -= start_skip; 2711071d4279SBram Moolenaar lnum += start_skip; 2712071d4279SBram Moolenaar } 2713071d4279SBram Moolenaar } 2714071d4279SBram Moolenaar else 2715071d4279SBram Moolenaar start_skip = 0; 2716071d4279SBram Moolenaar 2717071d4279SBram Moolenaar end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 2718071d4279SBram Moolenaar - (eap->line2 + off); 2719071d4279SBram Moolenaar if (end_skip > 0) 2720071d4279SBram Moolenaar { 2721071d4279SBram Moolenaar /* range ends above end of current/from diff block */ 2722071d4279SBram Moolenaar if (idx_cur == idx_from) /* :diffput */ 2723071d4279SBram Moolenaar { 2724071d4279SBram Moolenaar i = dp->df_count[idx_cur] - start_skip - end_skip; 2725071d4279SBram Moolenaar if (count > i) 2726071d4279SBram Moolenaar count = i; 2727071d4279SBram Moolenaar } 2728071d4279SBram Moolenaar else /* :diffget */ 2729071d4279SBram Moolenaar { 2730071d4279SBram Moolenaar count -= end_skip; 2731071d4279SBram Moolenaar end_skip = dp->df_count[idx_from] - start_skip - count; 2732071d4279SBram Moolenaar if (end_skip < 0) 2733071d4279SBram Moolenaar end_skip = 0; 2734071d4279SBram Moolenaar } 2735071d4279SBram Moolenaar } 2736071d4279SBram Moolenaar else 2737071d4279SBram Moolenaar end_skip = 0; 2738071d4279SBram Moolenaar } 2739071d4279SBram Moolenaar 2740b5aedf3eSBram Moolenaar buf_empty = BUFEMPTY(); 2741071d4279SBram Moolenaar added = 0; 2742071d4279SBram Moolenaar for (i = 0; i < count; ++i) 2743071d4279SBram Moolenaar { 2744280f126eSBram Moolenaar /* remember deleting the last line of the buffer */ 2745280f126eSBram Moolenaar buf_empty = curbuf->b_ml.ml_line_count == 1; 2746071d4279SBram Moolenaar ml_delete(lnum, FALSE); 2747071d4279SBram Moolenaar --added; 2748071d4279SBram Moolenaar } 2749071d4279SBram Moolenaar for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) 2750071d4279SBram Moolenaar { 2751071d4279SBram Moolenaar linenr_T nr; 2752071d4279SBram Moolenaar 2753071d4279SBram Moolenaar nr = dp->df_lnum[idx_from] + start_skip + i; 275449d7bf13SBram Moolenaar if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) 2755071d4279SBram Moolenaar break; 2756910f66f9SBram Moolenaar p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], 2757910f66f9SBram Moolenaar nr, FALSE)); 2758071d4279SBram Moolenaar if (p != NULL) 2759071d4279SBram Moolenaar { 2760071d4279SBram Moolenaar ml_append(lnum + i - 1, p, 0, FALSE); 2761071d4279SBram Moolenaar vim_free(p); 2762071d4279SBram Moolenaar ++added; 2763280f126eSBram Moolenaar if (buf_empty && curbuf->b_ml.ml_line_count == 2) 2764280f126eSBram Moolenaar { 2765280f126eSBram Moolenaar /* Added the first line into an empty buffer, need to 2766280f126eSBram Moolenaar * delete the dummy empty line. */ 2767280f126eSBram Moolenaar buf_empty = FALSE; 2768280f126eSBram Moolenaar ml_delete((linenr_T)2, FALSE); 2769280f126eSBram Moolenaar } 2770071d4279SBram Moolenaar } 2771071d4279SBram Moolenaar } 2772071d4279SBram Moolenaar new_count = dp->df_count[idx_to] + added; 2773071d4279SBram Moolenaar dp->df_count[idx_to] = new_count; 2774071d4279SBram Moolenaar 2775071d4279SBram Moolenaar if (start_skip == 0 && end_skip == 0) 2776071d4279SBram Moolenaar { 2777071d4279SBram Moolenaar /* Check if there are any other buffers and if the diff is 2778071d4279SBram Moolenaar * equal in them. */ 2779071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 2780910f66f9SBram Moolenaar if (curtab->tp_diffbuf[i] != NULL && i != idx_from 2781910f66f9SBram Moolenaar && i != idx_to 2782071d4279SBram Moolenaar && !diff_equal_entry(dp, idx_from, i)) 2783071d4279SBram Moolenaar break; 2784071d4279SBram Moolenaar if (i == DB_COUNT) 2785071d4279SBram Moolenaar { 2786071d4279SBram Moolenaar /* delete the diff entry, the buffers are now equal here */ 2787071d4279SBram Moolenaar dfree = dp; 2788071d4279SBram Moolenaar dp = dp->df_next; 2789071d4279SBram Moolenaar if (dprev == NULL) 279049d7bf13SBram Moolenaar curtab->tp_first_diff = dp; 2791071d4279SBram Moolenaar else 2792071d4279SBram Moolenaar dprev->df_next = dp; 2793071d4279SBram Moolenaar } 2794071d4279SBram Moolenaar } 2795071d4279SBram Moolenaar 2796071d4279SBram Moolenaar /* Adjust marks. This will change the following entries! */ 2797071d4279SBram Moolenaar if (added != 0) 2798071d4279SBram Moolenaar { 2799071d4279SBram Moolenaar mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added); 2800071d4279SBram Moolenaar if (curwin->w_cursor.lnum >= lnum) 2801071d4279SBram Moolenaar { 2802071d4279SBram Moolenaar /* Adjust the cursor position if it's in/after the changed 2803071d4279SBram Moolenaar * lines. */ 2804071d4279SBram Moolenaar if (curwin->w_cursor.lnum >= lnum + count) 2805071d4279SBram Moolenaar curwin->w_cursor.lnum += added; 2806071d4279SBram Moolenaar else if (added < 0) 2807071d4279SBram Moolenaar curwin->w_cursor.lnum = lnum; 2808071d4279SBram Moolenaar } 2809071d4279SBram Moolenaar } 2810071d4279SBram Moolenaar changed_lines(lnum, 0, lnum + count, (long)added); 2811071d4279SBram Moolenaar 2812071d4279SBram Moolenaar if (dfree != NULL) 2813071d4279SBram Moolenaar { 2814071d4279SBram Moolenaar /* Diff is deleted, update folds in other windows. */ 2815071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2816071d4279SBram Moolenaar diff_fold_update(dfree, idx_to); 2817071d4279SBram Moolenaar #endif 2818071d4279SBram Moolenaar vim_free(dfree); 2819071d4279SBram Moolenaar } 2820071d4279SBram Moolenaar else 2821071d4279SBram Moolenaar /* mark_adjust() may have changed the count in a wrong way */ 2822071d4279SBram Moolenaar dp->df_count[idx_to] = new_count; 2823071d4279SBram Moolenaar 2824071d4279SBram Moolenaar /* When changing the current buffer, keep track of line numbers */ 2825071d4279SBram Moolenaar if (idx_cur == idx_to) 2826071d4279SBram Moolenaar off += added; 2827071d4279SBram Moolenaar } 2828071d4279SBram Moolenaar 2829071d4279SBram Moolenaar /* If before the range or not deleted, go to next diff. */ 2830071d4279SBram Moolenaar if (dfree == NULL) 2831071d4279SBram Moolenaar { 2832071d4279SBram Moolenaar dprev = dp; 2833071d4279SBram Moolenaar dp = dp->df_next; 2834071d4279SBram Moolenaar } 2835071d4279SBram Moolenaar } 2836071d4279SBram Moolenaar 2837071d4279SBram Moolenaar /* restore curwin/curbuf and a few other things */ 2838a9d52e3bSBram Moolenaar if (eap->cmdidx != CMD_diffget) 2839071d4279SBram Moolenaar { 2840071d4279SBram Moolenaar /* Syncing undo only works for the current buffer, but we change 2841071d4279SBram Moolenaar * another buffer. Sync undo if the command was typed. This isn't 2842071d4279SBram Moolenaar * 100% right when ":diffput" is used in a function or mapping. */ 2843071d4279SBram Moolenaar if (KeyTyped) 2844779b74b2SBram Moolenaar u_sync(FALSE); 2845071d4279SBram Moolenaar aucmd_restbuf(&aco); 2846071d4279SBram Moolenaar } 2847071d4279SBram Moolenaar 2848d2b58c0aSBram Moolenaar theend: 2849071d4279SBram Moolenaar diff_busy = FALSE; 2850d2b58c0aSBram Moolenaar if (diff_need_update) 2851d2b58c0aSBram Moolenaar ex_diffupdate(NULL); 2852df77cef9SBram Moolenaar 28535f57bdcaSBram Moolenaar // Check that the cursor is on a valid character and update its 2854198fa066SBram Moolenaar // position. When there were filler lines the topline has become 2855198fa066SBram Moolenaar // invalid. 2856071d4279SBram Moolenaar check_cursor(); 2857071d4279SBram Moolenaar changed_line_abv_curs(); 2858071d4279SBram Moolenaar 2859df77cef9SBram Moolenaar if (diff_need_update) 2860df77cef9SBram Moolenaar // redraw already done by ex_diffupdate() 2861df77cef9SBram Moolenaar diff_need_update = FALSE; 2862df77cef9SBram Moolenaar else 2863df77cef9SBram Moolenaar { 2864198fa066SBram Moolenaar // Also need to redraw the other buffers. 2865071d4279SBram Moolenaar diff_redraw(FALSE); 2866198fa066SBram Moolenaar apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, FALSE, curbuf); 2867198fa066SBram Moolenaar } 2868071d4279SBram Moolenaar } 2869071d4279SBram Moolenaar 2870071d4279SBram Moolenaar #ifdef FEAT_FOLDING 2871071d4279SBram Moolenaar /* 2872071d4279SBram Moolenaar * Update folds for all diff buffers for entry "dp". 2873071d4279SBram Moolenaar * Skip buffer with index "skip_idx". 2874071d4279SBram Moolenaar * When there are no diffs, all folds are removed. 2875071d4279SBram Moolenaar */ 2876071d4279SBram Moolenaar static void 28777454a06eSBram Moolenaar diff_fold_update(diff_T *dp, int skip_idx) 2878071d4279SBram Moolenaar { 2879071d4279SBram Moolenaar int i; 2880071d4279SBram Moolenaar win_T *wp; 2881071d4279SBram Moolenaar 288229323590SBram Moolenaar FOR_ALL_WINDOWS(wp) 2883071d4279SBram Moolenaar for (i = 0; i < DB_COUNT; ++i) 288449d7bf13SBram Moolenaar if (curtab->tp_diffbuf[i] == wp->w_buffer && i != skip_idx) 2885071d4279SBram Moolenaar foldUpdate(wp, dp->df_lnum[i], 2886071d4279SBram Moolenaar dp->df_lnum[i] + dp->df_count[i]); 2887071d4279SBram Moolenaar } 2888071d4279SBram Moolenaar #endif 2889071d4279SBram Moolenaar 2890071d4279SBram Moolenaar /* 2891071d4279SBram Moolenaar * Return TRUE if buffer "buf" is in diff-mode. 2892071d4279SBram Moolenaar */ 2893071d4279SBram Moolenaar int 28947454a06eSBram Moolenaar diff_mode_buf(buf_T *buf) 2895071d4279SBram Moolenaar { 289649d7bf13SBram Moolenaar tabpage_T *tp; 289749d7bf13SBram Moolenaar 289829323590SBram Moolenaar FOR_ALL_TABPAGES(tp) 289949d7bf13SBram Moolenaar if (diff_buf_idx_tp(buf, tp) != DB_COUNT) 290049d7bf13SBram Moolenaar return TRUE; 290149d7bf13SBram Moolenaar return FALSE; 2902071d4279SBram Moolenaar } 2903071d4279SBram Moolenaar 2904071d4279SBram Moolenaar /* 2905071d4279SBram Moolenaar * Move "count" times in direction "dir" to the next diff block. 2906071d4279SBram Moolenaar * Return FAIL if there isn't such a diff block. 2907071d4279SBram Moolenaar */ 2908071d4279SBram Moolenaar int 29097454a06eSBram Moolenaar diff_move_to(int dir, long count) 2910071d4279SBram Moolenaar { 2911071d4279SBram Moolenaar int idx; 2912071d4279SBram Moolenaar linenr_T lnum = curwin->w_cursor.lnum; 2913071d4279SBram Moolenaar diff_T *dp; 2914071d4279SBram Moolenaar 2915071d4279SBram Moolenaar idx = diff_buf_idx(curbuf); 291649d7bf13SBram Moolenaar if (idx == DB_COUNT || curtab->tp_first_diff == NULL) 2917071d4279SBram Moolenaar return FAIL; 2918071d4279SBram Moolenaar 291949d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 2920071d4279SBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 2921071d4279SBram Moolenaar 292249d7bf13SBram Moolenaar if (curtab->tp_first_diff == NULL) /* no diffs today */ 2923071d4279SBram Moolenaar return FAIL; 2924071d4279SBram Moolenaar 2925071d4279SBram Moolenaar while (--count >= 0) 2926071d4279SBram Moolenaar { 2927071d4279SBram Moolenaar /* Check if already before first diff. */ 292849d7bf13SBram Moolenaar if (dir == BACKWARD && lnum <= curtab->tp_first_diff->df_lnum[idx]) 2929071d4279SBram Moolenaar break; 2930071d4279SBram Moolenaar 293149d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; ; dp = dp->df_next) 2932071d4279SBram Moolenaar { 2933071d4279SBram Moolenaar if (dp == NULL) 2934071d4279SBram Moolenaar break; 2935071d4279SBram Moolenaar if ((dir == FORWARD && lnum < dp->df_lnum[idx]) 2936071d4279SBram Moolenaar || (dir == BACKWARD 2937071d4279SBram Moolenaar && (dp->df_next == NULL 2938071d4279SBram Moolenaar || lnum <= dp->df_next->df_lnum[idx]))) 2939071d4279SBram Moolenaar { 2940071d4279SBram Moolenaar lnum = dp->df_lnum[idx]; 2941071d4279SBram Moolenaar break; 2942071d4279SBram Moolenaar } 2943071d4279SBram Moolenaar } 2944071d4279SBram Moolenaar } 2945071d4279SBram Moolenaar 2946071d4279SBram Moolenaar /* don't end up past the end of the file */ 2947071d4279SBram Moolenaar if (lnum > curbuf->b_ml.ml_line_count) 2948071d4279SBram Moolenaar lnum = curbuf->b_ml.ml_line_count; 2949071d4279SBram Moolenaar 2950071d4279SBram Moolenaar /* When the cursor didn't move at all we fail. */ 2951071d4279SBram Moolenaar if (lnum == curwin->w_cursor.lnum) 2952071d4279SBram Moolenaar return FAIL; 2953071d4279SBram Moolenaar 2954071d4279SBram Moolenaar setpcmark(); 2955071d4279SBram Moolenaar curwin->w_cursor.lnum = lnum; 2956071d4279SBram Moolenaar curwin->w_cursor.col = 0; 2957071d4279SBram Moolenaar 2958071d4279SBram Moolenaar return OK; 2959071d4279SBram Moolenaar } 2960071d4279SBram Moolenaar 2961025e3e0bSBram Moolenaar /* 2962025e3e0bSBram Moolenaar * Return the line number in the current window that is closest to "lnum1" in 2963025e3e0bSBram Moolenaar * "buf1" in diff mode. 2964025e3e0bSBram Moolenaar */ 2965025e3e0bSBram Moolenaar static linenr_T 2966025e3e0bSBram Moolenaar diff_get_corresponding_line_int( 29677454a06eSBram Moolenaar buf_T *buf1, 2968025e3e0bSBram Moolenaar linenr_T lnum1) 2969860cae1cSBram Moolenaar { 2970860cae1cSBram Moolenaar int idx1; 2971860cae1cSBram Moolenaar int idx2; 2972860cae1cSBram Moolenaar diff_T *dp; 2973860cae1cSBram Moolenaar int baseline = 0; 2974860cae1cSBram Moolenaar 2975860cae1cSBram Moolenaar idx1 = diff_buf_idx(buf1); 2976025e3e0bSBram Moolenaar idx2 = diff_buf_idx(curbuf); 2977860cae1cSBram Moolenaar if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL) 2978860cae1cSBram Moolenaar return lnum1; 2979860cae1cSBram Moolenaar 2980860cae1cSBram Moolenaar if (curtab->tp_diff_invalid) 2981860cae1cSBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 2982860cae1cSBram Moolenaar 2983860cae1cSBram Moolenaar if (curtab->tp_first_diff == NULL) /* no diffs today */ 2984860cae1cSBram Moolenaar return lnum1; 2985860cae1cSBram Moolenaar 2986860cae1cSBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 2987860cae1cSBram Moolenaar { 2988860cae1cSBram Moolenaar if (dp->df_lnum[idx1] > lnum1) 2989025e3e0bSBram Moolenaar return lnum1 - baseline; 2990025e3e0bSBram Moolenaar if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) 2991860cae1cSBram Moolenaar { 2992860cae1cSBram Moolenaar /* Inside the diffblock */ 2993860cae1cSBram Moolenaar baseline = lnum1 - dp->df_lnum[idx1]; 2994860cae1cSBram Moolenaar if (baseline > dp->df_count[idx2]) 2995860cae1cSBram Moolenaar baseline = dp->df_count[idx2]; 2996860cae1cSBram Moolenaar 2997860cae1cSBram Moolenaar return dp->df_lnum[idx2] + baseline; 2998860cae1cSBram Moolenaar } 2999025e3e0bSBram Moolenaar if ( (dp->df_lnum[idx1] == lnum1) 3000860cae1cSBram Moolenaar && (dp->df_count[idx1] == 0) 3001025e3e0bSBram Moolenaar && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) 3002025e3e0bSBram Moolenaar && ((dp->df_lnum[idx2] + dp->df_count[idx2]) 3003025e3e0bSBram Moolenaar > curwin->w_cursor.lnum)) 3004860cae1cSBram Moolenaar /* 3005860cae1cSBram Moolenaar * Special case: if the cursor is just after a zero-count 3006860cae1cSBram Moolenaar * block (i.e. all filler) and the target cursor is already 3007860cae1cSBram Moolenaar * inside the corresponding block, leave the target cursor 3008860cae1cSBram Moolenaar * unmoved. This makes repeated CTRL-W W operations work 3009860cae1cSBram Moolenaar * as expected. 3010860cae1cSBram Moolenaar */ 3011025e3e0bSBram Moolenaar return curwin->w_cursor.lnum; 3012860cae1cSBram Moolenaar baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) 3013860cae1cSBram Moolenaar - (dp->df_lnum[idx2] + dp->df_count[idx2]); 3014860cae1cSBram Moolenaar } 3015860cae1cSBram Moolenaar 3016860cae1cSBram Moolenaar /* If we get here then the cursor is after the last diff */ 3017025e3e0bSBram Moolenaar return lnum1 - baseline; 3018025e3e0bSBram Moolenaar } 3019860cae1cSBram Moolenaar 3020025e3e0bSBram Moolenaar /* 3021025e3e0bSBram Moolenaar * Return the line number in the current window that is closest to "lnum1" in 3022025e3e0bSBram Moolenaar * "buf1" in diff mode. Checks the line number to be valid. 3023025e3e0bSBram Moolenaar */ 3024025e3e0bSBram Moolenaar linenr_T 3025025e3e0bSBram Moolenaar diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) 3026025e3e0bSBram Moolenaar { 3027025e3e0bSBram Moolenaar linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); 3028025e3e0bSBram Moolenaar 3029025e3e0bSBram Moolenaar /* don't end up past the end of the file */ 3030025e3e0bSBram Moolenaar if (lnum > curbuf->b_ml.ml_line_count) 3031025e3e0bSBram Moolenaar return curbuf->b_ml.ml_line_count; 3032025e3e0bSBram Moolenaar return lnum; 3033860cae1cSBram Moolenaar } 3034860cae1cSBram Moolenaar 3035071d4279SBram Moolenaar /* 3036071d4279SBram Moolenaar * For line "lnum" in the current window find the equivalent lnum in window 3037071d4279SBram Moolenaar * "wp", compensating for inserted/deleted lines. 3038071d4279SBram Moolenaar */ 3039071d4279SBram Moolenaar linenr_T 30407454a06eSBram Moolenaar diff_lnum_win(linenr_T lnum, win_T *wp) 3041071d4279SBram Moolenaar { 3042071d4279SBram Moolenaar diff_T *dp; 3043071d4279SBram Moolenaar int idx; 3044071d4279SBram Moolenaar int i; 3045071d4279SBram Moolenaar linenr_T n; 3046071d4279SBram Moolenaar 3047071d4279SBram Moolenaar idx = diff_buf_idx(curbuf); 3048071d4279SBram Moolenaar if (idx == DB_COUNT) /* safety check */ 3049071d4279SBram Moolenaar return (linenr_T)0; 3050071d4279SBram Moolenaar 305149d7bf13SBram Moolenaar if (curtab->tp_diff_invalid) 3052071d4279SBram Moolenaar ex_diffupdate(NULL); /* update after a big change */ 3053071d4279SBram Moolenaar 3054071d4279SBram Moolenaar /* search for a change that includes "lnum" in the list of diffblocks. */ 305549d7bf13SBram Moolenaar for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) 3056071d4279SBram Moolenaar if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 3057071d4279SBram Moolenaar break; 3058071d4279SBram Moolenaar 3059071d4279SBram Moolenaar /* When after the last change, compute relative to the last line number. */ 3060071d4279SBram Moolenaar if (dp == NULL) 3061071d4279SBram Moolenaar return wp->w_buffer->b_ml.ml_line_count 3062071d4279SBram Moolenaar - (curbuf->b_ml.ml_line_count - lnum); 3063071d4279SBram Moolenaar 3064071d4279SBram Moolenaar /* Find index for "wp". */ 3065071d4279SBram Moolenaar i = diff_buf_idx(wp->w_buffer); 3066071d4279SBram Moolenaar if (i == DB_COUNT) /* safety check */ 3067071d4279SBram Moolenaar return (linenr_T)0; 3068071d4279SBram Moolenaar 3069071d4279SBram Moolenaar n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); 3070071d4279SBram Moolenaar if (n > dp->df_lnum[i] + dp->df_count[i]) 3071071d4279SBram Moolenaar n = dp->df_lnum[i] + dp->df_count[i]; 3072071d4279SBram Moolenaar return n; 3073071d4279SBram Moolenaar } 3074071d4279SBram Moolenaar 3075e828b762SBram Moolenaar /* 3076e828b762SBram Moolenaar * Handle an ED style diff line. 3077e828b762SBram Moolenaar * Return FAIL if the line does not contain diff info. 3078e828b762SBram Moolenaar */ 3079e828b762SBram Moolenaar static int 3080e828b762SBram Moolenaar parse_diff_ed( 3081e828b762SBram Moolenaar char_u *line, 3082e828b762SBram Moolenaar linenr_T *lnum_orig, 3083e828b762SBram Moolenaar long *count_orig, 3084e828b762SBram Moolenaar linenr_T *lnum_new, 3085e828b762SBram Moolenaar long *count_new) 3086e828b762SBram Moolenaar { 3087e828b762SBram Moolenaar char_u *p; 3088e828b762SBram Moolenaar long f1, l1, f2, l2; 3089e828b762SBram Moolenaar int difftype; 3090e828b762SBram Moolenaar 3091e828b762SBram Moolenaar // The line must be one of three formats: 3092e828b762SBram Moolenaar // change: {first}[,{last}]c{first}[,{last}] 3093e828b762SBram Moolenaar // append: {first}a{first}[,{last}] 3094e828b762SBram Moolenaar // delete: {first}[,{last}]d{first} 3095e828b762SBram Moolenaar p = line; 3096e828b762SBram Moolenaar f1 = getdigits(&p); 3097e828b762SBram Moolenaar if (*p == ',') 3098e828b762SBram Moolenaar { 3099e828b762SBram Moolenaar ++p; 3100e828b762SBram Moolenaar l1 = getdigits(&p); 3101e828b762SBram Moolenaar } 3102e828b762SBram Moolenaar else 3103e828b762SBram Moolenaar l1 = f1; 3104e828b762SBram Moolenaar if (*p != 'a' && *p != 'c' && *p != 'd') 3105e828b762SBram Moolenaar return FAIL; // invalid diff format 3106e828b762SBram Moolenaar difftype = *p++; 3107e828b762SBram Moolenaar f2 = getdigits(&p); 3108e828b762SBram Moolenaar if (*p == ',') 3109e828b762SBram Moolenaar { 3110e828b762SBram Moolenaar ++p; 3111e828b762SBram Moolenaar l2 = getdigits(&p); 3112e828b762SBram Moolenaar } 3113e828b762SBram Moolenaar else 3114e828b762SBram Moolenaar l2 = f2; 3115e828b762SBram Moolenaar if (l1 < f1 || l2 < f2) 3116e828b762SBram Moolenaar return FAIL; 3117e828b762SBram Moolenaar 3118e828b762SBram Moolenaar if (difftype == 'a') 3119e828b762SBram Moolenaar { 3120e828b762SBram Moolenaar *lnum_orig = f1 + 1; 3121e828b762SBram Moolenaar *count_orig = 0; 3122e828b762SBram Moolenaar } 3123e828b762SBram Moolenaar else 3124e828b762SBram Moolenaar { 3125e828b762SBram Moolenaar *lnum_orig = f1; 3126e828b762SBram Moolenaar *count_orig = l1 - f1 + 1; 3127e828b762SBram Moolenaar } 3128e828b762SBram Moolenaar if (difftype == 'd') 3129e828b762SBram Moolenaar { 3130e828b762SBram Moolenaar *lnum_new = f2 + 1; 3131e828b762SBram Moolenaar *count_new = 0; 3132e828b762SBram Moolenaar } 3133e828b762SBram Moolenaar else 3134e828b762SBram Moolenaar { 3135e828b762SBram Moolenaar *lnum_new = f2; 3136e828b762SBram Moolenaar *count_new = l2 - f2 + 1; 3137e828b762SBram Moolenaar } 3138e828b762SBram Moolenaar return OK; 3139e828b762SBram Moolenaar } 3140e828b762SBram Moolenaar 3141e828b762SBram Moolenaar /* 3142e828b762SBram Moolenaar * Parses unified diff with zero(!) context lines. 3143e828b762SBram Moolenaar * Return FAIL if there is no diff information in "line". 3144e828b762SBram Moolenaar */ 3145e828b762SBram Moolenaar static int 3146e828b762SBram Moolenaar parse_diff_unified( 3147e828b762SBram Moolenaar char_u *line, 3148e828b762SBram Moolenaar linenr_T *lnum_orig, 3149e828b762SBram Moolenaar long *count_orig, 3150e828b762SBram Moolenaar linenr_T *lnum_new, 3151e828b762SBram Moolenaar long *count_new) 3152e828b762SBram Moolenaar { 3153e828b762SBram Moolenaar char_u *p; 3154e828b762SBram Moolenaar long oldline, oldcount, newline, newcount; 3155e828b762SBram Moolenaar 3156e828b762SBram Moolenaar // Parse unified diff hunk header: 3157e828b762SBram Moolenaar // @@ -oldline,oldcount +newline,newcount @@ 3158e828b762SBram Moolenaar p = line; 3159e828b762SBram Moolenaar if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') 3160e828b762SBram Moolenaar { 3161e828b762SBram Moolenaar oldline = getdigits(&p); 3162e828b762SBram Moolenaar if (*p == ',') 3163e828b762SBram Moolenaar { 3164e828b762SBram Moolenaar ++p; 3165e828b762SBram Moolenaar oldcount = getdigits(&p); 3166e828b762SBram Moolenaar } 3167e828b762SBram Moolenaar else 3168e828b762SBram Moolenaar oldcount = 1; 3169e828b762SBram Moolenaar if (*p++ == ' ' && *p++ == '+') 3170e828b762SBram Moolenaar { 3171e828b762SBram Moolenaar newline = getdigits(&p); 3172e828b762SBram Moolenaar if (*p == ',') 3173e828b762SBram Moolenaar { 3174e828b762SBram Moolenaar ++p; 3175e828b762SBram Moolenaar newcount = getdigits(&p); 3176e828b762SBram Moolenaar } 3177e828b762SBram Moolenaar else 3178e828b762SBram Moolenaar newcount = 1; 3179e828b762SBram Moolenaar } 3180e828b762SBram Moolenaar else 3181e828b762SBram Moolenaar return FAIL; // invalid diff format 3182e828b762SBram Moolenaar 3183e828b762SBram Moolenaar if (oldcount == 0) 3184e828b762SBram Moolenaar oldline += 1; 3185e828b762SBram Moolenaar if (newcount == 0) 3186e828b762SBram Moolenaar newline += 1; 3187e828b762SBram Moolenaar if (newline == 0) 3188e828b762SBram Moolenaar newline = 1; 3189e828b762SBram Moolenaar 3190e828b762SBram Moolenaar *lnum_orig = oldline; 3191e828b762SBram Moolenaar *count_orig = oldcount; 3192e828b762SBram Moolenaar *lnum_new = newline; 3193e828b762SBram Moolenaar *count_new = newcount; 3194e828b762SBram Moolenaar 3195e828b762SBram Moolenaar return OK; 3196e828b762SBram Moolenaar } 3197e828b762SBram Moolenaar 3198e828b762SBram Moolenaar return FAIL; 3199e828b762SBram Moolenaar } 3200e828b762SBram Moolenaar 3201e828b762SBram Moolenaar /* 3202e828b762SBram Moolenaar * Callback function for the xdl_diff() function. 3203e828b762SBram Moolenaar * Stores the diff output in a grow array. 3204e828b762SBram Moolenaar */ 3205e828b762SBram Moolenaar static int 3206e828b762SBram Moolenaar xdiff_out(void *priv, mmbuffer_t *mb, int nbuf) 3207e828b762SBram Moolenaar { 3208e828b762SBram Moolenaar diffout_T *dout = (diffout_T *)priv; 3209e828b762SBram Moolenaar char_u *p; 3210e828b762SBram Moolenaar 3211*f080d70aSBram Moolenaar // The header line always comes by itself, text lines in at least two 3212*f080d70aSBram Moolenaar // parts. We drop the text part. 3213*f080d70aSBram Moolenaar if (nbuf > 1) 3214*f080d70aSBram Moolenaar return 0; 3215*f080d70aSBram Moolenaar 3216*f080d70aSBram Moolenaar // sanity check 3217*f080d70aSBram Moolenaar if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) 3218*f080d70aSBram Moolenaar return 0; 3219*f080d70aSBram Moolenaar 3220e828b762SBram Moolenaar if (ga_grow(&dout->dout_ga, 1) == FAIL) 3221e828b762SBram Moolenaar return -1; 3222*f080d70aSBram Moolenaar p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size); 3223e828b762SBram Moolenaar if (p == NULL) 3224e828b762SBram Moolenaar return -1; 3225e828b762SBram Moolenaar ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; 3226e828b762SBram Moolenaar return 0; 3227e828b762SBram Moolenaar } 3228e828b762SBram Moolenaar 3229071d4279SBram Moolenaar #endif /* FEAT_DIFF */ 3230