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