1 /* vim:set ts=8 sts=4 sw=4: 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 or three buffers. 12 */ 13 14 #include "vim.h" 15 16 #if defined(FEAT_DIFF) || defined(PROTO) 17 18 #define DB_COUNT 4 /* up to four buffers can be diff'ed */ 19 20 /* 21 * Each diffblock defines where a block of lines starts in each of the buffers 22 * and how many lines it occupies in that buffer. When the lines are missing 23 * in the buffer the df_count[] is zero. This is all counted in 24 * buffer lines. 25 * There is always at least one unchanged line in between the diffs. 26 * Otherwise it would have been included in the diff above or below it. 27 * df_lnum[] + df_count[] is the lnum below the change. When in one buffer 28 * lines have been inserted, in the other buffer df_lnum[] is the line below 29 * the insertion and df_count[] is zero. When appending lines at the end of 30 * the buffer, df_lnum[] is one beyond the end! 31 * This is using a linked list, because the number of differences is expected 32 * to be reasonable small. The list is sorted on lnum. 33 */ 34 typedef struct diffblock diff_T; 35 struct diffblock 36 { 37 diff_T *df_next; 38 linenr_T df_lnum[DB_COUNT]; /* line number in buffer */ 39 linenr_T df_count[DB_COUNT]; /* nr of inserted/changed lines */ 40 }; 41 42 static diff_T *first_diff = NULL; 43 44 static buf_T *(diffbuf[DB_COUNT]); 45 46 static int diff_invalid = TRUE; /* list of diffs is outdated */ 47 48 static int diff_busy = FALSE; /* ex_diffgetput() is busy */ 49 50 /* flags obtained from the 'diffopt' option */ 51 #define DIFF_FILLER 1 /* display filler lines */ 52 #define DIFF_ICASE 2 /* ignore case */ 53 #define DIFF_IWHITE 4 /* ignore change in white space */ 54 static int diff_flags = DIFF_FILLER; 55 56 #define LBUFLEN 50 /* length of line in diff file */ 57 58 static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it 59 doesn't work, MAYBE when not checked yet */ 60 #if defined(MSWIN) || defined(MSDOS) 61 static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE 62 when it doesn't work, MAYBE when not 63 checked yet */ 64 #endif 65 66 static int diff_buf_idx __ARGS((buf_T *buf)); 67 static void diff_check_unchanged __ARGS((diff_T *dp)); 68 static int diff_check_sanity __ARGS((diff_T *dp)); 69 static void diff_redraw __ARGS((int dofold)); 70 static int diff_write __ARGS((buf_T *buf, char_u *fname)); 71 static void diff_file __ARGS((char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff)); 72 static void diff_clear __ARGS((void)); 73 static int diff_equal_entry __ARGS((diff_T *dp, int idx1, int idx2)); 74 static int diff_cmp __ARGS((char_u *s1, char_u *s2)); 75 #ifdef FEAT_FOLDING 76 static void diff_fold_update __ARGS((diff_T *dp, int skip_idx)); 77 #endif 78 static void diff_read __ARGS((int idx_orig, int idx_new, char_u *fname)); 79 static void diff_copy_entry __ARGS((diff_T *dprev, diff_T *dp, int idx_orig, int idx_new)); 80 static diff_T *diff_alloc_new __ARGS((diff_T *dprev, diff_T *dp)); 81 82 #ifndef USE_CR 83 # define tag_fgets vim_fgets 84 #endif 85 86 /* 87 * Call this when a new buffer is being edited in the current window. curbuf 88 * must already have been set. 89 * Marks the current buffer as being part of the diff and requireing updating. 90 * This must be done before any autocmd, because a command the uses info 91 * about the screen contents. 92 */ 93 void 94 diff_new_buffer() 95 { 96 if (curwin->w_p_diff) 97 diff_buf_add(curbuf); 98 } 99 100 /* 101 * Called when deleting or unloading a buffer: No longer make a diff with it. 102 * Also called when 'diff' is reset in the last window showing a diff for a 103 * buffer. 104 */ 105 void 106 diff_buf_delete(buf) 107 buf_T *buf; 108 { 109 int i; 110 111 i = diff_buf_idx(buf); 112 if (i != DB_COUNT) 113 { 114 diffbuf[i] = NULL; 115 diff_invalid = TRUE; 116 } 117 } 118 119 /* 120 * Check if the current buffer should be added to or removed from the list of 121 * diff buffers. 122 */ 123 void 124 diff_buf_adjust(win) 125 win_T *win; 126 { 127 win_T *wp; 128 129 if (!win->w_p_diff) 130 { 131 /* When there is no window showing a diff for this buffer, remove 132 * it from the diffs. */ 133 for (wp = firstwin; wp != NULL; wp = wp->w_next) 134 if (wp->w_buffer == win->w_buffer && wp->w_p_diff) 135 break; 136 if (wp == NULL) 137 diff_buf_delete(win->w_buffer); 138 } 139 else 140 diff_buf_add(win->w_buffer); 141 } 142 143 /* 144 * Add a buffer to make diffs for. 145 */ 146 void 147 diff_buf_add(buf) 148 buf_T *buf; 149 { 150 int i; 151 152 if (diff_buf_idx(buf) != DB_COUNT) 153 return; /* It's already there. */ 154 155 for (i = 0; i < DB_COUNT; ++i) 156 if (diffbuf[i] == NULL) 157 { 158 diffbuf[i] = buf; 159 diff_invalid = TRUE; 160 return; 161 } 162 163 EMSGN(_("E96: Can not diff more than %ld buffers"), DB_COUNT); 164 } 165 166 /* 167 * Find buffer "buf" in the list of diff buffers. 168 * Return its index or DB_COUNT if not found. 169 */ 170 static int 171 diff_buf_idx(buf) 172 buf_T *buf; 173 { 174 int idx; 175 176 for (idx = 0; idx < DB_COUNT; ++idx) 177 if (diffbuf[idx] == buf) 178 break; 179 return idx; 180 } 181 182 /* 183 * Mark the diff info as invalid, it will be updated when info is requested. 184 */ 185 void 186 diff_invalidate() 187 { 188 if (curwin->w_p_diff) 189 { 190 diff_invalid = TRUE; 191 diff_redraw(TRUE); 192 } 193 } 194 195 /* 196 * Called by mark_adjust(): update line numbers. 197 * This attempts to update the changes as much as possible: 198 * When inserting/deleting lines outside of existing change blocks, create a 199 * new change block and update the line numbers in following blocks. 200 * When inserting/deleting lines in existing change blocks, update them. 201 */ 202 void 203 diff_mark_adjust(line1, line2, amount, amount_after) 204 linenr_T line1; 205 linenr_T line2; 206 long amount; 207 long amount_after; 208 { 209 diff_T *dp; 210 diff_T *dprev; 211 diff_T *dnext; 212 int idx; 213 int i; 214 int inserted, deleted; 215 int n, off; 216 linenr_T last; 217 linenr_T lnum_deleted = line1; /* lnum of remaining deletion */ 218 int check_unchanged; 219 220 /* Find the index for the current buffer. */ 221 idx = diff_buf_idx(curbuf); 222 if (idx == DB_COUNT) 223 return; /* This buffer doesn't have diffs. */ 224 225 if (line2 == MAXLNUM) 226 { 227 /* mark_adjust(99, MAXLNUM, 9, 0): insert lines */ 228 inserted = amount; 229 deleted = 0; 230 } 231 else if (amount_after > 0) 232 { 233 /* mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines*/ 234 inserted = amount_after; 235 deleted = 0; 236 } 237 else 238 { 239 /* mark_adjust(98, 99, MAXLNUM, -2): delete lines */ 240 inserted = 0; 241 deleted = -amount_after; 242 } 243 244 dprev = NULL; 245 dp = first_diff; 246 for (;;) 247 { 248 /* If the change is after the previous diff block and before the next 249 * diff block, thus not touching an existing change, create a new diff 250 * block. Don't do this when ex_diffgetput() is busy. */ 251 if ((dp == NULL || dp->df_lnum[idx] - 1 > line2 252 || (line2 == MAXLNUM && dp->df_lnum[idx] > line1)) 253 && (dprev == NULL 254 || dprev->df_lnum[idx] + dprev->df_count[idx] < line1) 255 && !diff_busy) 256 { 257 dnext = diff_alloc_new(dprev, dp); 258 if (dnext == NULL) 259 return; 260 261 dnext->df_lnum[idx] = line1; 262 dnext->df_count[idx] = inserted; 263 for (i = 0; i < DB_COUNT; ++i) 264 if (diffbuf[i] != NULL && i != idx) 265 { 266 if (dprev == NULL) 267 dnext->df_lnum[i] = line1; 268 else 269 dnext->df_lnum[i] = line1 270 + (dprev->df_lnum[i] + dprev->df_count[i]) 271 - (dprev->df_lnum[idx] + dprev->df_count[idx]); 272 dnext->df_count[i] = deleted; 273 } 274 } 275 276 /* if at end of the list, quit */ 277 if (dp == NULL) 278 break; 279 280 /* 281 * Check for these situations: 282 * 1 2 3 283 * 1 2 3 284 * line1 2 3 4 5 285 * 2 3 4 5 286 * 2 3 4 5 287 * line2 2 3 4 5 288 * 3 5 6 289 * 3 5 6 290 */ 291 /* compute last line of this change */ 292 last = dp->df_lnum[idx] + dp->df_count[idx] - 1; 293 294 /* 1. change completely above line1: nothing to do */ 295 if (last >= line1 - 1) 296 { 297 /* 6. change below line2: only adjust for amount_after; also when 298 * "deleted" became zero when deleted all lines between two diffs */ 299 if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) 300 { 301 if (amount_after == 0) 302 break; /* nothing left to change */ 303 dp->df_lnum[idx] += amount_after; 304 } 305 else 306 { 307 check_unchanged = FALSE; 308 309 /* 2. 3. 4. 5.: inserted/deleted lines touching this diff. */ 310 if (deleted > 0) 311 { 312 if (dp->df_lnum[idx] >= line1) 313 { 314 off = dp->df_lnum[idx] - lnum_deleted; 315 if (last <= line2) 316 { 317 /* 4. delete all lines of diff */ 318 if (dp->df_next != NULL 319 && dp->df_next->df_lnum[idx] - 1 <= line2) 320 { 321 /* delete continues in next diff, only do 322 * lines until that one */ 323 n = dp->df_next->df_lnum[idx] - lnum_deleted; 324 deleted -= n; 325 n -= dp->df_count[idx]; 326 lnum_deleted = dp->df_next->df_lnum[idx]; 327 } 328 else 329 n = deleted - dp->df_count[idx]; 330 dp->df_count[idx] = 0; 331 } 332 else 333 { 334 /* 5. delete lines at or just before top of diff */ 335 n = off; 336 dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; 337 check_unchanged = TRUE; 338 } 339 dp->df_lnum[idx] = line1; 340 } 341 else 342 { 343 off = 0; 344 if (last < line2) 345 { 346 /* 2. delete at end of of diff */ 347 dp->df_count[idx] -= last - lnum_deleted + 1; 348 if (dp->df_next != NULL 349 && dp->df_next->df_lnum[idx] - 1 <= line2) 350 { 351 /* delete continues in next diff, only do 352 * lines until that one */ 353 n = dp->df_next->df_lnum[idx] - 1 - last; 354 deleted -= dp->df_next->df_lnum[idx] 355 - lnum_deleted; 356 lnum_deleted = dp->df_next->df_lnum[idx]; 357 } 358 else 359 n = line2 - last; 360 check_unchanged = TRUE; 361 } 362 else 363 { 364 /* 3. delete lines inside the diff */ 365 n = 0; 366 dp->df_count[idx] -= deleted; 367 } 368 } 369 370 for (i = 0; i < DB_COUNT; ++i) 371 if (diffbuf[i] != NULL && i != idx) 372 { 373 dp->df_lnum[i] -= off; 374 dp->df_count[i] += n; 375 } 376 } 377 else 378 { 379 if (dp->df_lnum[idx] <= line1) 380 { 381 /* inserted lines somewhere in this diff */ 382 dp->df_count[idx] += inserted; 383 check_unchanged = TRUE; 384 } 385 else 386 /* inserted lines somewhere above this diff */ 387 dp->df_lnum[idx] += inserted; 388 } 389 390 if (check_unchanged) 391 /* Check if inserted lines are equal, may reduce the 392 * size of the diff. TODO: also check for equal lines 393 * in the middle and perhaps split the block. */ 394 diff_check_unchanged(dp); 395 } 396 } 397 398 /* check if this block touches the previous one, may merge them. */ 399 if (dprev != NULL && dprev->df_lnum[idx] + dprev->df_count[idx] 400 == dp->df_lnum[idx]) 401 { 402 for (i = 0; i < DB_COUNT; ++i) 403 if (diffbuf[i] != NULL) 404 dprev->df_count[i] += dp->df_count[i]; 405 dprev->df_next = dp->df_next; 406 vim_free(dp); 407 dp = dprev->df_next; 408 } 409 else 410 { 411 /* Advance to next entry. */ 412 dprev = dp; 413 dp = dp->df_next; 414 } 415 } 416 417 dprev = NULL; 418 dp = first_diff; 419 while (dp != NULL) 420 { 421 /* All counts are zero, remove this entry. */ 422 for (i = 0; i < DB_COUNT; ++i) 423 if (diffbuf[i] != NULL && dp->df_count[i] != 0) 424 break; 425 if (i == DB_COUNT) 426 { 427 dnext = dp->df_next; 428 vim_free(dp); 429 dp = dnext; 430 if (dprev == NULL) 431 first_diff = dnext; 432 else 433 dprev->df_next = dnext; 434 } 435 else 436 { 437 /* Advance to next entry. */ 438 dprev = dp; 439 dp = dp->df_next; 440 } 441 442 } 443 diff_redraw(TRUE); 444 445 /* Recompute the scroll binding, may remove or add filler lines (e.g., 446 * when adding lines above w_topline). */ 447 check_scrollbind((linenr_T)0, 0L); 448 } 449 450 /* 451 * Allocate a new diff block and link it between "dprev" and "dp". 452 */ 453 static diff_T * 454 diff_alloc_new(dprev, dp) 455 diff_T *dprev; 456 diff_T *dp; 457 { 458 diff_T *dnew; 459 460 dnew = (diff_T *)alloc((unsigned)sizeof(diff_T)); 461 if (dnew != NULL) 462 { 463 dnew->df_next = dp; 464 if (dprev == NULL) 465 first_diff = dnew; 466 else 467 dprev->df_next = dnew; 468 } 469 return dnew; 470 } 471 472 /* 473 * Check if the diff block "dp" can be made smaller for lines at the start and 474 * end that are equal. Called after inserting lines. 475 * This may result in a change where all buffers have zero lines, the caller 476 * must take care of removing it. 477 */ 478 static void 479 diff_check_unchanged(dp) 480 diff_T *dp; 481 { 482 int i_org; 483 int i_new; 484 int off_org, off_new; 485 char_u *line_org; 486 int dir = FORWARD; 487 488 /* Find the first buffers, use it as the original, compare the other 489 * buffer lines against this one. */ 490 for (i_org = 0; i_org < DB_COUNT; ++i_org) 491 if (diffbuf[i_org] != NULL) 492 break; 493 if (i_org == DB_COUNT) /* safety check */ 494 return; 495 496 if (diff_check_sanity(dp) == FAIL) 497 return; 498 499 /* First check lines at the top, then at the bottom. */ 500 off_org = 0; 501 off_new = 0; 502 for (;;) 503 { 504 /* Repeat until a line is found which is different or the number of 505 * lines has become zero. */ 506 while (dp->df_count[i_org] > 0) 507 { 508 /* Copy the line, the next ml_get() will invalidate it. */ 509 if (dir == BACKWARD) 510 off_org = dp->df_count[i_org] - 1; 511 line_org = vim_strsave(ml_get_buf(diffbuf[i_org], 512 dp->df_lnum[i_org] + off_org, FALSE)); 513 if (line_org == NULL) 514 return; 515 for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) 516 { 517 if (diffbuf[i_new] == NULL) 518 continue; 519 if (dir == BACKWARD) 520 off_new = dp->df_count[i_new] - 1; 521 /* if other buffer doesn't have this line, it was inserted */ 522 if (off_new < 0 || off_new >= dp->df_count[i_new]) 523 break; 524 if (diff_cmp(line_org, ml_get_buf(diffbuf[i_new], 525 dp->df_lnum[i_new] + off_new, FALSE)) != 0) 526 break; 527 } 528 vim_free(line_org); 529 530 /* Stop when a line isn't equal in all diff buffers. */ 531 if (i_new != DB_COUNT) 532 break; 533 534 /* Line matched in all buffers, remove it from the diff. */ 535 for (i_new = i_org; i_new < DB_COUNT; ++i_new) 536 if (diffbuf[i_new] != NULL) 537 { 538 if (dir == FORWARD) 539 ++dp->df_lnum[i_new]; 540 --dp->df_count[i_new]; 541 } 542 } 543 if (dir == BACKWARD) 544 break; 545 dir = BACKWARD; 546 } 547 } 548 549 /* 550 * Check if a diff block doesn't contain invalid line numbers. 551 * This can happen when the diff program returns invalid results. 552 */ 553 static int 554 diff_check_sanity(dp) 555 diff_T *dp; 556 { 557 int i; 558 559 for (i = 0; i < DB_COUNT; ++i) 560 if (diffbuf[i] != NULL) 561 if (dp->df_lnum[i] + dp->df_count[i] - 1 562 > diffbuf[i]->b_ml.ml_line_count) 563 return FAIL; 564 return OK; 565 } 566 567 /* 568 * Mark all diff buffers for redraw. 569 */ 570 static void 571 diff_redraw(dofold) 572 int dofold; /* also recompute the folds */ 573 { 574 win_T *wp; 575 int n; 576 577 for (wp = firstwin; wp != NULL; wp = wp->w_next) 578 if (wp->w_p_diff) 579 { 580 redraw_win_later(wp, NOT_VALID); 581 #ifdef FEAT_FOLDING 582 if (dofold && foldmethodIsDiff(wp)) 583 foldUpdateAll(wp); 584 #endif 585 /* A change may have made filler lines invalid, need to take care 586 * of that for other windows. */ 587 if (wp != curwin && wp->w_topfill > 0) 588 { 589 n = diff_check(wp, wp->w_topline); 590 if (wp->w_topfill > n) 591 wp->w_topfill = (n < 0 ? 0 : n); 592 } 593 } 594 } 595 596 /* 597 * Write buffer "buf" to file "name". 598 * Always use 'fileformat' set to "unix". 599 * Return FAIL for failure 600 */ 601 static int 602 diff_write(buf, fname) 603 buf_T *buf; 604 char_u *fname; 605 { 606 int r; 607 char_u *save_ff; 608 609 save_ff = buf->b_p_ff; 610 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); 611 r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, 612 NULL, FALSE, FALSE, FALSE, TRUE); 613 free_string_option(buf->b_p_ff); 614 buf->b_p_ff = save_ff; 615 return r; 616 } 617 618 /* 619 * Completely update the diffs for the buffers involved. 620 * This uses the ordinary "diff" command. 621 * The buffers are written to a file, also for unmodified buffers (the file 622 * could have been produced by autocommands, e.g. the netrw plugin). 623 */ 624 /*ARGSUSED*/ 625 void 626 ex_diffupdate(eap) 627 exarg_T *eap; 628 { 629 buf_T *buf; 630 int idx_orig; 631 int idx_new; 632 char_u *tmp_orig; 633 char_u *tmp_new; 634 char_u *tmp_diff; 635 FILE *fd; 636 int ok; 637 638 /* Delete all diffblocks. */ 639 diff_clear(); 640 diff_invalid = FALSE; 641 642 /* Use the first buffer as the original text. */ 643 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) 644 if (diffbuf[idx_orig] != NULL) 645 break; 646 if (idx_orig == DB_COUNT) 647 return; 648 649 /* Only need to do something when there is another buffer. */ 650 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 651 if (diffbuf[idx_new] != NULL) 652 break; 653 if (idx_new == DB_COUNT) 654 return; 655 656 /* We need three temp file names. */ 657 tmp_orig = vim_tempname('o'); 658 tmp_new = vim_tempname('n'); 659 tmp_diff = vim_tempname('d'); 660 if (tmp_orig == NULL || tmp_new == NULL || tmp_diff == NULL) 661 goto theend; 662 663 /* 664 * Do a quick test if "diff" really works. Otherwise it looks like there 665 * are no differences. Can't use the return value, it's non-zero when 666 * there are differences. 667 * May try twice, first with "-a" and then without. 668 */ 669 for (;;) 670 { 671 ok = FALSE; 672 fd = fopen((char *)tmp_orig, "w"); 673 if (fd != NULL) 674 { 675 fwrite("line1\n", (size_t)6, (size_t)1, fd); 676 fclose(fd); 677 fd = fopen((char *)tmp_new, "w"); 678 if (fd != NULL) 679 { 680 fwrite("line2\n", (size_t)6, (size_t)1, fd); 681 fclose(fd); 682 diff_file(tmp_orig, tmp_new, tmp_diff); 683 fd = fopen((char *)tmp_diff, "r"); 684 if (fd != NULL) 685 { 686 char_u linebuf[LBUFLEN]; 687 688 for (;;) 689 { 690 /* There must be a line that contains "1c1". */ 691 if (tag_fgets(linebuf, LBUFLEN, fd)) 692 break; 693 if (STRNCMP(linebuf, "1c1", 3) == 0) 694 ok = TRUE; 695 } 696 fclose(fd); 697 } 698 mch_remove(tmp_diff); 699 mch_remove(tmp_new); 700 } 701 mch_remove(tmp_orig); 702 } 703 704 #ifdef FEAT_EVAL 705 /* When using 'diffexpr' break here. */ 706 if (*p_dex != NUL) 707 break; 708 #endif 709 710 #if defined(MSWIN) || defined(MSDOS) 711 /* If the "-a" argument works, also check if "--binary" works. */ 712 if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE) 713 { 714 diff_a_works = TRUE; 715 diff_bin_works = TRUE; 716 continue; 717 } 718 if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE) 719 { 720 /* Tried --binary, but it failed. "-a" works though. */ 721 diff_bin_works = FALSE; 722 ok = TRUE; 723 } 724 #endif 725 726 /* If we checked if "-a" works already, break here. */ 727 if (diff_a_works != MAYBE) 728 break; 729 diff_a_works = ok; 730 731 /* If "-a" works break here, otherwise retry without "-a". */ 732 if (ok) 733 break; 734 } 735 if (!ok) 736 { 737 EMSG(_("E97: Cannot create diffs")); 738 diff_a_works = MAYBE; 739 #if defined(MSWIN) || defined(MSDOS) 740 diff_bin_works = MAYBE; 741 #endif 742 goto theend; 743 } 744 745 /* Write the first buffer to a tempfile. */ 746 buf = diffbuf[idx_orig]; 747 if (diff_write(buf, tmp_orig) == FAIL) 748 goto theend; 749 750 /* Make a difference between the first buffer and every other. */ 751 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 752 { 753 buf = diffbuf[idx_new]; 754 if (buf == NULL) 755 continue; 756 if (diff_write(buf, tmp_new) == FAIL) 757 continue; 758 diff_file(tmp_orig, tmp_new, tmp_diff); 759 760 /* Read the diff output and add each entry to the diff list. */ 761 diff_read(idx_orig, idx_new, tmp_diff); 762 mch_remove(tmp_diff); 763 mch_remove(tmp_new); 764 } 765 mch_remove(tmp_orig); 766 767 diff_redraw(TRUE); 768 769 theend: 770 vim_free(tmp_orig); 771 vim_free(tmp_new); 772 vim_free(tmp_diff); 773 } 774 775 /* 776 * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". 777 */ 778 static void 779 diff_file(tmp_orig, tmp_new, tmp_diff) 780 char_u *tmp_orig; 781 char_u *tmp_new; 782 char_u *tmp_diff; 783 { 784 char_u *cmd; 785 786 #ifdef FEAT_EVAL 787 if (*p_dex != NUL) 788 /* Use 'diffexpr' to generate the diff file. */ 789 eval_diff(tmp_orig, tmp_new, tmp_diff); 790 else 791 #endif 792 { 793 cmd = alloc((unsigned)(STRLEN(tmp_orig) + STRLEN(tmp_new) 794 + STRLEN(tmp_diff) + STRLEN(p_srr) + 27)); 795 if (cmd != NULL) 796 { 797 /* Build the diff command and execute it. Always use -a, binary 798 * differences are of no use. Ignore errors, diff returns 799 * non-zero when differences have been found. */ 800 sprintf((char *)cmd, "diff %s%s%s%s%s %s", 801 diff_a_works == FALSE ? "" : "-a ", 802 #if defined(MSWIN) || defined(MSDOS) 803 diff_bin_works == TRUE ? "--binary " : "", 804 #else 805 "", 806 #endif 807 (diff_flags & DIFF_IWHITE) ? "-b " : "", 808 (diff_flags & DIFF_ICASE) ? "-i " : "", 809 tmp_orig, tmp_new); 810 append_redir(cmd, p_srr, tmp_diff); 811 (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT); 812 vim_free(cmd); 813 } 814 } 815 } 816 817 /* 818 * Create a new version of a file from the current buffer and a diff file. 819 * The buffer is written to a file, also for unmodified buffers (the file 820 * could have been produced by autocommands, e.g. the netrw plugin). 821 */ 822 void 823 ex_diffpatch(eap) 824 exarg_T *eap; 825 { 826 char_u *tmp_orig; /* name of original temp file */ 827 char_u *tmp_new; /* name of patched temp file */ 828 char_u *buf = NULL; 829 win_T *old_curwin = curwin; 830 char_u *newname = NULL; /* name of patched file buffer */ 831 #ifdef UNIX 832 char_u dirbuf[MAXPATHL]; 833 char_u *fullname = NULL; 834 #endif 835 #ifdef FEAT_BROWSE 836 char_u *browseFile = NULL; 837 int browse_flag = cmdmod.browse; 838 #endif 839 840 #ifdef FEAT_BROWSE 841 if (cmdmod.browse) 842 { 843 browseFile = do_browse(0, (char_u *)_("Patch file"), 844 eap->arg, NULL, NULL, BROWSE_FILTER_ALL_FILES, NULL); 845 if (browseFile == NULL) 846 return; /* operation cancelled */ 847 eap->arg = browseFile; 848 cmdmod.browse = FALSE; /* don't let do_ecmd() browse again */ 849 } 850 #endif 851 852 /* We need two temp file names. */ 853 tmp_orig = vim_tempname('o'); 854 tmp_new = vim_tempname('n'); 855 if (tmp_orig == NULL || tmp_new == NULL) 856 goto theend; 857 858 /* Write the current buffer to "tmp_orig". */ 859 if (buf_write(curbuf, tmp_orig, NULL, 860 (linenr_T)1, curbuf->b_ml.ml_line_count, 861 NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) 862 goto theend; 863 864 #ifdef UNIX 865 /* Get the absolute path of the patchfile, changing directory below. */ 866 fullname = FullName_save(eap->arg, FALSE); 867 #endif 868 buf = alloc((unsigned)(STRLEN(tmp_orig) + ( 869 # ifdef UNIX 870 fullname != NULL ? STRLEN(fullname) : 871 # endif 872 STRLEN(eap->arg)) + STRLEN(tmp_new) + 16)); 873 if (buf == NULL) 874 goto theend; 875 876 #ifdef UNIX 877 /* Temporaraly chdir to /tmp, to avoid patching files in the current 878 * directory when the patch file contains more than one patch. When we 879 * have our own temp dir use that instead, it will be cleaned up when we 880 * exit (any .rej files created). Don't change directory if we can't 881 * return to the current. */ 882 if (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0) 883 dirbuf[0] = NUL; 884 else 885 { 886 # ifdef TEMPDIRNAMES 887 if (vim_tempdir != NULL) 888 mch_chdir((char *)vim_tempdir); 889 else 890 # endif 891 mch_chdir("/tmp"); 892 shorten_fnames(TRUE); 893 } 894 #endif 895 896 #ifdef FEAT_EVAL 897 if (*p_pex != NUL) 898 /* Use 'patchexpr' to generate the new file. */ 899 eval_patch(tmp_orig, 900 # ifdef UNIX 901 fullname != NULL ? fullname : 902 # endif 903 eap->arg, tmp_new); 904 else 905 #endif 906 { 907 /* Build the patch command and execute it. Ignore errors. Switch to 908 * cooked mode to allow the user to respond to prompts. */ 909 sprintf((char *)buf, "patch -o %s %s < \"%s\"", tmp_new, tmp_orig, 910 # ifdef UNIX 911 fullname != NULL ? fullname : 912 # endif 913 eap->arg); 914 (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED); 915 } 916 917 #ifdef UNIX 918 if (dirbuf[0] != NUL) 919 { 920 if (mch_chdir((char *)dirbuf) != 0) 921 EMSG(_(e_prev_dir)); 922 shorten_fnames(TRUE); 923 } 924 #endif 925 926 /* patch probably has written over the screen */ 927 redraw_later(CLEAR); 928 929 /* Delete any .orig or .rej file created. */ 930 STRCPY(buf, tmp_new); 931 STRCAT(buf, ".orig"); 932 mch_remove(buf); 933 STRCPY(buf, tmp_new); 934 STRCAT(buf, ".rej"); 935 mch_remove(buf); 936 937 if (curbuf->b_fname != NULL) 938 { 939 newname = vim_strnsave(curbuf->b_fname, 940 (int)(STRLEN(curbuf->b_fname) + 4)); 941 if (newname != NULL) 942 STRCAT(newname, ".new"); 943 } 944 945 #ifdef FEAT_GUI 946 need_mouse_correct = TRUE; 947 #endif 948 if (win_split(0, 0) != FAIL) 949 { 950 /* Pretend it was a ":split fname" command */ 951 eap->cmdidx = CMD_split; 952 eap->arg = tmp_new; 953 do_exedit(eap, old_curwin); 954 955 if (curwin != old_curwin) /* split must have worked */ 956 { 957 /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 958 diff_win_options(curwin, TRUE); 959 diff_win_options(old_curwin, TRUE); 960 961 if (newname != NULL) 962 { 963 /* do a ":file filename.new" on the patched buffer */ 964 eap->arg = newname; 965 ex_file(eap); 966 967 #ifdef FEAT_AUTOCMD 968 /* Do filetype detection with the new name. */ 969 do_cmdline_cmd((char_u *)":doau filetypedetect BufRead"); 970 #endif 971 } 972 } 973 } 974 975 theend: 976 if (tmp_orig != NULL) 977 mch_remove(tmp_orig); 978 vim_free(tmp_orig); 979 if (tmp_new != NULL) 980 mch_remove(tmp_new); 981 vim_free(tmp_new); 982 vim_free(newname); 983 vim_free(buf); 984 #ifdef UNIX 985 vim_free(fullname); 986 #endif 987 #ifdef FEAT_BROWSE 988 vim_free(browseFile); 989 cmdmod.browse = browse_flag; 990 #endif 991 } 992 993 /* 994 * Split the window and edit another file, setting options to show the diffs. 995 */ 996 void 997 ex_diffsplit(eap) 998 exarg_T *eap; 999 { 1000 win_T *old_curwin = curwin; 1001 1002 #ifdef FEAT_GUI 1003 need_mouse_correct = TRUE; 1004 #endif 1005 if (win_split(0, 0) != FAIL) 1006 { 1007 /* Pretend it was a ":split fname" command */ 1008 eap->cmdidx = CMD_split; 1009 do_exedit(eap, old_curwin); 1010 1011 if (curwin != old_curwin) /* split must have worked */ 1012 { 1013 /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 1014 diff_win_options(curwin, TRUE); 1015 diff_win_options(old_curwin, TRUE); 1016 } 1017 } 1018 } 1019 1020 /* 1021 * Set options to show difs for the current window. 1022 */ 1023 /*ARGSUSED*/ 1024 void 1025 ex_diffthis(eap) 1026 exarg_T *eap; 1027 { 1028 /* Set 'diff', 'scrollbind' on and 'wrap' off. */ 1029 diff_win_options(curwin, TRUE); 1030 } 1031 1032 /* 1033 * Set options in window "wp" for diff mode. 1034 */ 1035 void 1036 diff_win_options(wp, addbuf) 1037 win_T *wp; 1038 int addbuf; /* Add buffer to diff. */ 1039 { 1040 wp->w_p_diff = TRUE; 1041 wp->w_p_scb = TRUE; 1042 wp->w_p_wrap = FALSE; 1043 # ifdef FEAT_FOLDING 1044 { 1045 win_T *old_curwin = curwin; 1046 1047 curwin = wp; 1048 curbuf = curwin->w_buffer; 1049 set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", 1050 OPT_LOCAL|OPT_FREE); 1051 curwin = old_curwin; 1052 curbuf = curwin->w_buffer; 1053 wp->w_p_fdc = 2; 1054 wp->w_p_fen = TRUE; 1055 wp->w_p_fdl = 0; 1056 foldUpdateAll(wp); 1057 /* make sure topline is not halfway a fold */ 1058 changed_window_setting_win(wp); 1059 } 1060 # endif 1061 #ifdef FEAT_SCROLLBIND 1062 if (vim_strchr(p_sbo, 'h') == NULL) 1063 do_cmdline_cmd((char_u *)"set sbo+=hor"); 1064 #endif 1065 1066 if (addbuf) 1067 diff_buf_add(wp->w_buffer); 1068 redraw_win_later(wp, NOT_VALID); 1069 } 1070 1071 /* 1072 * Set options not to show diffs. For the current window or all windows. 1073 */ 1074 void 1075 ex_diffoff(eap) 1076 exarg_T *eap; 1077 { 1078 win_T *wp; 1079 win_T *old_curwin = curwin; 1080 #ifdef FEAT_SCROLLBIND 1081 int diffwin = FALSE; 1082 #endif 1083 1084 for (wp = firstwin; wp != NULL; wp = wp->w_next) 1085 { 1086 if (wp == curwin || eap->forceit) 1087 { 1088 /* Set 'diff', 'scrollbind' off and 'wrap' on. */ 1089 wp->w_p_diff = FALSE; 1090 wp->w_p_scb = FALSE; 1091 wp->w_p_wrap = TRUE; 1092 #ifdef FEAT_FOLDING 1093 curwin = wp; 1094 curbuf = curwin->w_buffer; 1095 set_string_option_direct((char_u *)"fdm", -1, 1096 (char_u *)"manual", OPT_LOCAL|OPT_FREE); 1097 curwin = old_curwin; 1098 curbuf = curwin->w_buffer; 1099 wp->w_p_fdc = 0; 1100 wp->w_p_fen = FALSE; 1101 wp->w_p_fdl = 0; 1102 foldUpdateAll(wp); 1103 /* make sure topline is not halfway a fold */ 1104 changed_window_setting_win(wp); 1105 #endif 1106 diff_buf_adjust(wp); 1107 } 1108 #ifdef FEAT_SCROLLBIND 1109 diffwin |= wp->w_p_diff; 1110 #endif 1111 } 1112 1113 #ifdef FEAT_SCROLLBIND 1114 /* Remove "hor" from from 'scrollopt' if there are no diff windows left. */ 1115 if (!diffwin && vim_strchr(p_sbo, 'h') != NULL) 1116 do_cmdline_cmd((char_u *)"set sbo-=hor"); 1117 #endif 1118 } 1119 1120 /* 1121 * Read the diff output and add each entry to the diff list. 1122 */ 1123 static void 1124 diff_read(idx_orig, idx_new, fname) 1125 int idx_orig; /* idx of original file */ 1126 int idx_new; /* idx of new file */ 1127 char_u *fname; /* name of diff output file */ 1128 { 1129 FILE *fd; 1130 diff_T *dprev = NULL; 1131 diff_T *dp = first_diff; 1132 diff_T *dn, *dpl; 1133 long f1, l1, f2, l2; 1134 char_u linebuf[LBUFLEN]; /* only need to hold the diff line */ 1135 int difftype; 1136 char_u *p; 1137 long off; 1138 int i; 1139 linenr_T lnum_orig, lnum_new; 1140 long count_orig, count_new; 1141 int notset = TRUE; /* block "*dp" not set yet */ 1142 1143 fd = fopen((char *)fname, "r"); 1144 if (fd == NULL) 1145 { 1146 EMSG(_("E98: Cannot read diff output")); 1147 return; 1148 } 1149 1150 for (;;) 1151 { 1152 if (tag_fgets(linebuf, LBUFLEN, fd)) 1153 break; /* end of file */ 1154 if (!isdigit(*linebuf)) 1155 continue; /* not the start of a diff block */ 1156 1157 /* This line must be one of three formats: 1158 * {first}[,{last}]c{first}[,{last}] 1159 * {first}a{first}[,{last}] 1160 * {first}[,{last}]d{first} 1161 */ 1162 p = linebuf; 1163 f1 = getdigits(&p); 1164 if (*p == ',') 1165 { 1166 ++p; 1167 l1 = getdigits(&p); 1168 } 1169 else 1170 l1 = f1; 1171 if (*p != 'a' && *p != 'c' && *p != 'd') 1172 continue; /* invalid diff format */ 1173 difftype = *p++; 1174 f2 = getdigits(&p); 1175 if (*p == ',') 1176 { 1177 ++p; 1178 l2 = getdigits(&p); 1179 } 1180 else 1181 l2 = f2; 1182 if (l1 < f1 || l2 < f2) 1183 continue; /* invalid line range */ 1184 1185 if (difftype == 'a') 1186 { 1187 lnum_orig = f1 + 1; 1188 count_orig = 0; 1189 } 1190 else 1191 { 1192 lnum_orig = f1; 1193 count_orig = l1 - f1 + 1; 1194 } 1195 if (difftype == 'd') 1196 { 1197 lnum_new = f2 + 1; 1198 count_new = 0; 1199 } 1200 else 1201 { 1202 lnum_new = f2; 1203 count_new = l2 - f2 + 1; 1204 } 1205 1206 /* Go over blocks before the change, for which orig and new are equal. 1207 * Copy blocks from orig to new. */ 1208 while (dp != NULL 1209 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) 1210 { 1211 if (notset) 1212 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1213 dprev = dp; 1214 dp = dp->df_next; 1215 notset = TRUE; 1216 } 1217 1218 if (dp != NULL 1219 && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig] 1220 && lnum_orig + count_orig >= dp->df_lnum[idx_orig]) 1221 { 1222 /* New block overlaps with existing block(s). 1223 * First find last block that overlaps. */ 1224 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) 1225 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) 1226 break; 1227 1228 /* If the newly found block starts before the old one, set the 1229 * start back a number of lines. */ 1230 off = dp->df_lnum[idx_orig] - lnum_orig; 1231 if (off > 0) 1232 { 1233 for (i = idx_orig; i < idx_new; ++i) 1234 if (diffbuf[i] != NULL) 1235 dp->df_lnum[i] -= off; 1236 dp->df_lnum[idx_new] = lnum_new; 1237 dp->df_count[idx_new] = count_new; 1238 } 1239 else if (notset) 1240 { 1241 /* new block inside existing one, adjust new block */ 1242 dp->df_lnum[idx_new] = lnum_new + off; 1243 dp->df_count[idx_new] = count_new - off; 1244 } 1245 else 1246 /* second overlap of new block with existing block */ 1247 dp->df_count[idx_new] += count_new - count_orig; 1248 1249 /* Adjust the size of the block to include all the lines to the 1250 * end of the existing block or the new diff, whatever ends last. */ 1251 off = (lnum_orig + count_orig) 1252 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); 1253 if (off < 0) 1254 { 1255 /* new change ends in existing block, adjust the end if not 1256 * done already */ 1257 if (notset) 1258 dp->df_count[idx_new] += -off; 1259 off = 0; 1260 } 1261 for (i = idx_orig; i < idx_new + !notset; ++i) 1262 if (diffbuf[i] != NULL) 1263 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] 1264 - dp->df_lnum[i] + off; 1265 1266 /* Delete the diff blocks that have been merged into one. */ 1267 dn = dp->df_next; 1268 dp->df_next = dpl->df_next; 1269 while (dn != dp->df_next) 1270 { 1271 dpl = dn->df_next; 1272 vim_free(dn); 1273 dn = dpl; 1274 } 1275 } 1276 else 1277 { 1278 /* Allocate a new diffblock. */ 1279 dp = diff_alloc_new(dprev, dp); 1280 if (dp == NULL) 1281 return; 1282 1283 dp->df_lnum[idx_orig] = lnum_orig; 1284 dp->df_count[idx_orig] = count_orig; 1285 dp->df_lnum[idx_new] = lnum_new; 1286 dp->df_count[idx_new] = count_new; 1287 1288 /* Set values for other buffers, these must be equal to the 1289 * original buffer, otherwise there would have been a change 1290 * already. */ 1291 for (i = idx_orig + 1; i < idx_new; ++i) 1292 if (diffbuf[i] != NULL) 1293 diff_copy_entry(dprev, dp, idx_orig, i); 1294 } 1295 notset = FALSE; /* "*dp" has been set */ 1296 } 1297 1298 /* for remaining diff blocks orig and new are equal */ 1299 while (dp != NULL) 1300 { 1301 if (notset) 1302 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1303 dprev = dp; 1304 dp = dp->df_next; 1305 notset = TRUE; 1306 } 1307 1308 fclose(fd); 1309 } 1310 1311 /* 1312 * Copy an entry at "dp" from "idx_orig" to "idx_new". 1313 */ 1314 static void 1315 diff_copy_entry(dprev, dp, idx_orig, idx_new) 1316 diff_T *dprev; 1317 diff_T *dp; 1318 int idx_orig; 1319 int idx_new; 1320 { 1321 long off; 1322 1323 if (dprev == NULL) 1324 off = 0; 1325 else 1326 off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig]) 1327 - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]); 1328 dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off; 1329 dp->df_count[idx_new] = dp->df_count[idx_orig]; 1330 } 1331 1332 /* 1333 * Clear the list of diffblocks. 1334 */ 1335 static void 1336 diff_clear() 1337 { 1338 diff_T *p, *next_p; 1339 1340 for (p = first_diff; p != NULL; p = next_p) 1341 { 1342 next_p = p->df_next; 1343 vim_free(p); 1344 } 1345 first_diff = NULL; 1346 } 1347 1348 /* 1349 * Check diff status for line "lnum" in buffer "buf": 1350 * Returns 0 for nothing special 1351 * Returns -1 for a line that should be highlighted as changed. 1352 * Returns -2 for a line that should be highlighted as added/deleted. 1353 * Returns > 0 for inserting that many filler lines above it (never happens 1354 * when 'diffopt' doesn't contain "filler"). 1355 * This should only be used for windows where 'diff' is set. 1356 */ 1357 int 1358 diff_check(wp, lnum) 1359 win_T *wp; 1360 linenr_T lnum; 1361 { 1362 int idx; /* index in diffbuf[] for this buffer */ 1363 diff_T *dp; 1364 int maxcount; 1365 int i; 1366 buf_T *buf = wp->w_buffer; 1367 int cmp; 1368 1369 if (diff_invalid) 1370 ex_diffupdate(NULL); /* update after a big change */ 1371 1372 if (first_diff == NULL || !wp->w_p_diff) /* no diffs at all */ 1373 return 0; 1374 1375 /* safety check: "lnum" must be a buffer line */ 1376 if (lnum < 1 || lnum > buf->b_ml.ml_line_count + 1) 1377 return 0; 1378 1379 idx = diff_buf_idx(buf); 1380 if (idx == DB_COUNT) 1381 return 0; /* no diffs for buffer "buf" */ 1382 1383 #ifdef FEAT_FOLDING 1384 /* A closed fold never has filler lines. */ 1385 if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL)) 1386 return 0; 1387 #endif 1388 1389 /* search for a change that includes "lnum" in the list of diffblocks. */ 1390 for (dp = first_diff; dp != NULL; dp = dp->df_next) 1391 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 1392 break; 1393 if (dp == NULL || lnum < dp->df_lnum[idx]) 1394 return 0; 1395 1396 if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) 1397 { 1398 int zero = FALSE; 1399 1400 /* Changed or inserted line. If the other buffers have a count of 1401 * zero, the lines were inserted. If the other buffers have the same 1402 * count, check if the lines are identical. */ 1403 cmp = FALSE; 1404 for (i = 0; i < DB_COUNT; ++i) 1405 if (i != idx && diffbuf[i] != NULL) 1406 { 1407 if (dp->df_count[i] == 0) 1408 zero = TRUE; 1409 else 1410 { 1411 if (dp->df_count[i] != dp->df_count[idx]) 1412 return -1; /* nr of lines changed. */ 1413 cmp = TRUE; 1414 } 1415 } 1416 if (cmp) 1417 { 1418 /* Compare all lines. If they are equal the lines were inserted 1419 * in some buffers, deleted in others, but not changed. */ 1420 for (i = 0; i < DB_COUNT; ++i) 1421 if (i != idx && diffbuf[i] != NULL && dp->df_count[i] != 0) 1422 if (!diff_equal_entry(dp, idx, i)) 1423 return -1; 1424 } 1425 /* If there is no buffer with zero lines then there is no difference 1426 * any longer. Happens when making a change (or undo) that removes 1427 * the difference. Can't remove the entry here, we might be halfway 1428 * updating the window. Just report the text as unchanged. Other 1429 * windows might still show the change though. */ 1430 if (zero == FALSE) 1431 return 0; 1432 return -2; 1433 } 1434 1435 /* If 'diffopt' doesn't contain "filler", return 0. */ 1436 if (!(diff_flags & DIFF_FILLER)) 1437 return 0; 1438 1439 /* Insert filler lines above the line just below the change. Will return 1440 * 0 when this buf had the max count. */ 1441 maxcount = 0; 1442 for (i = 0; i < DB_COUNT; ++i) 1443 if (diffbuf[i] != NULL && dp->df_count[i] > maxcount) 1444 maxcount = dp->df_count[i]; 1445 return maxcount - dp->df_count[idx]; 1446 } 1447 1448 /* 1449 * Compare two entries in diff "*dp" and return TRUE if they are equal. 1450 */ 1451 static int 1452 diff_equal_entry(dp, idx1, idx2) 1453 diff_T *dp; 1454 int idx1; 1455 int idx2; 1456 { 1457 int i; 1458 char_u *line; 1459 int cmp; 1460 1461 if (dp->df_count[idx1] != dp->df_count[idx2]) 1462 return FALSE; 1463 if (diff_check_sanity(dp) == FAIL) 1464 return FALSE; 1465 for (i = 0; i < dp->df_count[idx1]; ++i) 1466 { 1467 line = vim_strsave(ml_get_buf(diffbuf[idx1], 1468 dp->df_lnum[idx1] + i, FALSE)); 1469 if (line == NULL) 1470 return FALSE; 1471 cmp = diff_cmp(line, ml_get_buf(diffbuf[idx2], 1472 dp->df_lnum[idx2] + i, FALSE)); 1473 vim_free(line); 1474 if (cmp != 0) 1475 return FALSE; 1476 } 1477 return TRUE; 1478 } 1479 1480 /* 1481 * Compare strings "s1" and "s2" according to 'diffopt'. 1482 * Return non-zero when they are different. 1483 */ 1484 static int 1485 diff_cmp(s1, s2) 1486 char_u *s1; 1487 char_u *s2; 1488 { 1489 char_u *p1, *p2; 1490 #ifdef FEAT_MBYTE 1491 int l; 1492 #endif 1493 1494 if ((diff_flags & (DIFF_ICASE | DIFF_IWHITE)) == 0) 1495 return STRCMP(s1, s2); 1496 if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) 1497 return MB_STRICMP(s1, s2); 1498 1499 /* Ignore white space changes and possibly ignore case. */ 1500 p1 = s1; 1501 p2 = s2; 1502 while (*p1 != NUL && *p2 != NUL) 1503 { 1504 if (vim_iswhite(*p1) && vim_iswhite(*p2)) 1505 { 1506 p1 = skipwhite(p1); 1507 p2 = skipwhite(p2); 1508 } 1509 else 1510 { 1511 #ifdef FEAT_MBYTE 1512 l = (*mb_ptr2len_check)(p1); 1513 if (l != (*mb_ptr2len_check)(p2)) 1514 break; 1515 if (l > 1) 1516 { 1517 if (STRNCMP(p1, p2, l) != 0 1518 && (!enc_utf8 1519 || !(diff_flags & DIFF_ICASE) 1520 || utf_fold(utf_ptr2char(p1)) 1521 != utf_fold(utf_ptr2char(p2)))) 1522 break; 1523 p1 += l; 1524 p2 += l; 1525 } 1526 else 1527 #endif 1528 { 1529 if (*p1 != *p2 && (!(diff_flags & DIFF_ICASE) 1530 || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) 1531 break; 1532 ++p1; 1533 ++p2; 1534 } 1535 } 1536 } 1537 1538 /* Ignore trailing white space. */ 1539 p1 = skipwhite(p1); 1540 p2 = skipwhite(p2); 1541 if (*p1 != NUL || *p2 != NUL) 1542 return 1; 1543 return 0; 1544 } 1545 1546 /* 1547 * Return the number of filler lines above "lnum". 1548 */ 1549 int 1550 diff_check_fill(wp, lnum) 1551 win_T *wp; 1552 linenr_T lnum; 1553 { 1554 int n; 1555 1556 /* be quick when there are no filler lines */ 1557 if (!(diff_flags & DIFF_FILLER)) 1558 return 0; 1559 n = diff_check(wp, lnum); 1560 if (n <= 0) 1561 return 0; 1562 return n; 1563 } 1564 1565 /* 1566 * Set the topline of "towin" to match the position in "fromwin", so that they 1567 * show the same diff'ed lines. 1568 */ 1569 void 1570 diff_set_topline(fromwin, towin) 1571 win_T *fromwin; 1572 win_T *towin; 1573 { 1574 buf_T *buf = fromwin->w_buffer; 1575 linenr_T lnum = fromwin->w_topline; 1576 int idx; 1577 diff_T *dp; 1578 int i; 1579 1580 idx = diff_buf_idx(buf); 1581 if (idx == DB_COUNT) 1582 return; /* safety check */ 1583 1584 if (diff_invalid) 1585 ex_diffupdate(NULL); /* update after a big change */ 1586 1587 towin->w_topfill = 0; 1588 1589 /* search for a change that includes "lnum" in the list of diffblocks. */ 1590 for (dp = first_diff; dp != NULL; dp = dp->df_next) 1591 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 1592 break; 1593 if (dp == NULL) 1594 { 1595 /* After last change, compute topline relative to end of file; no 1596 * filler lines. */ 1597 towin->w_topline = towin->w_buffer->b_ml.ml_line_count 1598 - (buf->b_ml.ml_line_count - lnum); 1599 } 1600 else 1601 { 1602 /* Find index for "towin". */ 1603 i = diff_buf_idx(towin->w_buffer); 1604 if (i == DB_COUNT) 1605 return; /* safety check */ 1606 1607 towin->w_topline = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); 1608 if (lnum >= dp->df_lnum[idx]) 1609 { 1610 /* Inside a change: compute filler lines. */ 1611 if (dp->df_count[i] == dp->df_count[idx]) 1612 towin->w_topfill = fromwin->w_topfill; 1613 else if (dp->df_count[i] > dp->df_count[idx]) 1614 { 1615 if (lnum == dp->df_lnum[idx] + dp->df_count[idx]) 1616 towin->w_topline = dp->df_lnum[i] + dp->df_count[i] 1617 - fromwin->w_topfill; 1618 } 1619 else 1620 { 1621 if (towin->w_topline >= dp->df_lnum[i] + dp->df_count[i]) 1622 { 1623 if (diff_flags & DIFF_FILLER) 1624 towin->w_topfill = dp->df_lnum[idx] 1625 + dp->df_count[idx] - lnum; 1626 towin->w_topline = dp->df_lnum[i] + dp->df_count[i]; 1627 } 1628 } 1629 } 1630 } 1631 1632 /* safety check (if diff info gets outdated strange things may happen) */ 1633 towin->w_botfill = FALSE; 1634 if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) 1635 { 1636 towin->w_topline = towin->w_buffer->b_ml.ml_line_count; 1637 towin->w_botfill = TRUE; 1638 } 1639 if (towin->w_topline < 1) 1640 { 1641 towin->w_topline = 1; 1642 towin->w_topfill = 0; 1643 } 1644 1645 /* When w_topline changes need to recompute w_botline and cursor position */ 1646 invalidate_botline_win(towin); 1647 changed_line_abv_curs_win(towin); 1648 1649 check_topfill(towin, FALSE); 1650 #ifdef FEAT_FOLDING 1651 (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, 1652 NULL, TRUE, NULL); 1653 #endif 1654 } 1655 1656 /* 1657 * This is called when 'diffopt' is changed. 1658 */ 1659 int 1660 diffopt_changed() 1661 { 1662 char_u *p; 1663 int diff_context_new = 6; 1664 int diff_flags_new = 0; 1665 1666 p = p_dip; 1667 while (*p != NUL) 1668 { 1669 if (STRNCMP(p, "filler", 6) == 0) 1670 { 1671 p += 6; 1672 diff_flags_new |= DIFF_FILLER; 1673 } 1674 else if (STRNCMP(p, "context:", 8) == 0 && VIM_ISDIGIT(p[8])) 1675 { 1676 p += 8; 1677 diff_context_new = getdigits(&p); 1678 } 1679 else if (STRNCMP(p, "icase", 5) == 0) 1680 { 1681 p += 5; 1682 diff_flags_new |= DIFF_ICASE; 1683 } 1684 else if (STRNCMP(p, "iwhite", 6) == 0) 1685 { 1686 p += 6; 1687 diff_flags_new |= DIFF_IWHITE; 1688 } 1689 if (*p != ',' && *p != NUL) 1690 return FAIL; 1691 if (*p == ',') 1692 ++p; 1693 } 1694 1695 /* If "icase" or "iwhite" was added or removed, need to update the diff. */ 1696 if (diff_flags != diff_flags_new) 1697 diff_invalid = TRUE; 1698 1699 diff_flags = diff_flags_new; 1700 diff_context = diff_context_new; 1701 1702 diff_redraw(TRUE); 1703 1704 /* recompute the scroll binding with the new option value, may 1705 * remove or add filler lines */ 1706 check_scrollbind((linenr_T)0, 0L); 1707 1708 return OK; 1709 } 1710 1711 /* 1712 * Find the difference within a changed line. 1713 * Returns TRUE if the line was added, no other buffer has it. 1714 */ 1715 int 1716 diff_find_change(wp, lnum, startp, endp) 1717 win_T *wp; 1718 linenr_T lnum; 1719 int *startp; /* first char of the change */ 1720 int *endp; /* last char of the change */ 1721 { 1722 char_u *line_org; 1723 char_u *line_new; 1724 int i; 1725 int si, ei_org, ei_new; 1726 diff_T *dp; 1727 int idx; 1728 int off; 1729 int added = TRUE; 1730 1731 /* Make a copy of the line, the next ml_get() will invalidate it. */ 1732 line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); 1733 if (line_org == NULL) 1734 return FALSE; 1735 1736 idx = diff_buf_idx(wp->w_buffer); 1737 if (idx == DB_COUNT) /* cannot happen */ 1738 return FALSE; 1739 1740 /* search for a change that includes "lnum" in the list of diffblocks. */ 1741 for (dp = first_diff; dp != NULL; dp = dp->df_next) 1742 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 1743 break; 1744 if (dp == NULL || diff_check_sanity(dp) == FAIL) 1745 return FALSE; 1746 1747 off = lnum - dp->df_lnum[idx]; 1748 1749 for (i = 0; i < DB_COUNT; ++i) 1750 if (diffbuf[i] != NULL && i != idx) 1751 { 1752 /* Skip lines that are not in the other change (filler lines). */ 1753 if (off >= dp->df_count[i]) 1754 continue; 1755 added = FALSE; 1756 line_new = ml_get_buf(diffbuf[i], dp->df_lnum[i] + off, FALSE); 1757 1758 /* Search for start of difference */ 1759 for (si = 0; line_org[si] != NUL && line_org[si] == line_new[si]; ) 1760 ++si; 1761 #ifdef FEAT_MBYTE 1762 if (has_mbyte) 1763 { 1764 /* Move back to first byte of character in both lines (may 1765 * have "nn^" in line_org and "n^ in line_new). */ 1766 si -= (*mb_head_off)(line_org, line_org + si); 1767 si -= (*mb_head_off)(line_new, line_new + si); 1768 } 1769 #endif 1770 if (*startp > si) 1771 *startp = si; 1772 1773 /* Search for end of difference, if any. */ 1774 if (line_org[si] != NUL || line_new[si] != NUL) 1775 { 1776 ei_org = (int)STRLEN(line_org); 1777 ei_new = (int)STRLEN(line_new); 1778 while (ei_org >= *startp && ei_new >= *startp 1779 && ei_org >= 0 && ei_new >= 0 1780 && line_org[ei_org] == line_new[ei_new]) 1781 { 1782 --ei_org; 1783 --ei_new; 1784 } 1785 if (*endp < ei_org) 1786 *endp = ei_org; 1787 } 1788 } 1789 1790 vim_free(line_org); 1791 return added; 1792 } 1793 1794 #if defined(FEAT_FOLDING) || defined(PROTO) 1795 /* 1796 * Return TRUE if line "lnum" is not close to a diff block, this line should 1797 * be in a fold. 1798 * Return FALSE if there are no diff blocks at all in this window. 1799 */ 1800 int 1801 diff_infold(wp, lnum) 1802 win_T *wp; 1803 linenr_T lnum; 1804 { 1805 int i; 1806 int idx = -1; 1807 int other = FALSE; 1808 diff_T *dp; 1809 1810 /* Return if 'diff' isn't set. */ 1811 if (!wp->w_p_diff) 1812 return FALSE; 1813 1814 for (i = 0; i < DB_COUNT; ++i) 1815 { 1816 if (diffbuf[i] == wp->w_buffer) 1817 idx = i; 1818 else if (diffbuf[i] != NULL) 1819 other = TRUE; 1820 } 1821 1822 /* return here if there are no diffs in the window */ 1823 if (idx == -1 || !other) 1824 return FALSE; 1825 1826 if (diff_invalid) 1827 ex_diffupdate(NULL); /* update after a big change */ 1828 1829 /* Return if there are no diff blocks. All lines will be folded. */ 1830 if (first_diff == NULL) 1831 return TRUE; 1832 1833 for (dp = first_diff; dp != NULL; dp = dp->df_next) 1834 { 1835 /* If this change is below the line there can't be any further match. */ 1836 if (dp->df_lnum[idx] - diff_context > lnum) 1837 break; 1838 /* If this change ends before the line we have a match. */ 1839 if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) 1840 return FALSE; 1841 } 1842 return TRUE; 1843 } 1844 #endif 1845 1846 /* 1847 * "dp" and "do" commands. 1848 */ 1849 void 1850 nv_diffgetput(put) 1851 int put; 1852 { 1853 exarg_T ea; 1854 1855 ea.arg = (char_u *)""; 1856 if (put) 1857 ea.cmdidx = CMD_diffput; 1858 else 1859 ea.cmdidx = CMD_diffget; 1860 ea.addr_count = 0; 1861 ea.line1 = curwin->w_cursor.lnum; 1862 ea.line2 = curwin->w_cursor.lnum; 1863 ex_diffgetput(&ea); 1864 } 1865 1866 /* 1867 * ":diffget" 1868 * ":diffput" 1869 */ 1870 void 1871 ex_diffgetput(eap) 1872 exarg_T *eap; 1873 { 1874 linenr_T lnum; 1875 int count; 1876 linenr_T off = 0; 1877 diff_T *dp; 1878 diff_T *dprev; 1879 diff_T *dfree; 1880 int idx_cur; 1881 int idx_other; 1882 int idx_from; 1883 int idx_to; 1884 int i; 1885 int added; 1886 char_u *p; 1887 aco_save_T aco; 1888 buf_T *buf; 1889 int start_skip, end_skip; 1890 int new_count; 1891 1892 /* Find the current buffer in the list of diff buffers. */ 1893 idx_cur = diff_buf_idx(curbuf); 1894 if (idx_cur == DB_COUNT) 1895 { 1896 EMSG(_("E99: Current buffer is not in diff mode")); 1897 return; 1898 } 1899 1900 if (*eap->arg == NUL) 1901 { 1902 /* No argument: Find the other buffer in the list of diff buffers. */ 1903 for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) 1904 if (diffbuf[idx_other] != curbuf && diffbuf[idx_other] != NULL) 1905 break; 1906 if (idx_other == DB_COUNT) 1907 { 1908 EMSG(_("E100: No other buffer in diff mode")); 1909 return; 1910 } 1911 1912 /* Check that there isn't a third buffer in the list */ 1913 for (i = idx_other + 1; i < DB_COUNT; ++i) 1914 if (diffbuf[i] != curbuf && diffbuf[i] != NULL) 1915 { 1916 EMSG(_("E101: More than two buffers in diff mode, don't know which one to use")); 1917 return; 1918 } 1919 } 1920 else 1921 { 1922 /* Buffer number or pattern given. Ignore trailing white space. */ 1923 p = eap->arg + STRLEN(eap->arg); 1924 while (p > eap->arg && vim_iswhite(p[-1])) 1925 --p; 1926 for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) 1927 ; 1928 if (eap->arg + i == p) /* digits only */ 1929 i = atol((char *)eap->arg); 1930 else 1931 { 1932 i = buflist_findpat(eap->arg, p, FALSE, TRUE); 1933 if (i < 0) 1934 return; /* error message already given */ 1935 } 1936 buf = buflist_findnr(i); 1937 if (buf == NULL) 1938 { 1939 EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg); 1940 return; 1941 } 1942 idx_other = diff_buf_idx(buf); 1943 if (idx_other == DB_COUNT) 1944 { 1945 EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg); 1946 return; 1947 } 1948 } 1949 1950 diff_busy = TRUE; 1951 1952 /* When no range given include the line above or below the cursor. */ 1953 if (eap->addr_count == 0) 1954 { 1955 /* Make it possible that ":diffget" on the last line gets line below 1956 * the cursor line when there is no difference above the cursor. */ 1957 if (eap->cmdidx == CMD_diffget 1958 && eap->line1 == curbuf->b_ml.ml_line_count 1959 && diff_check(curwin, eap->line1) == 0 1960 && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0)) 1961 ++eap->line2; 1962 else if (eap->line1 > 0) 1963 --eap->line1; 1964 } 1965 1966 if (eap->cmdidx == CMD_diffget) 1967 { 1968 idx_from = idx_other; 1969 idx_to = idx_cur; 1970 } 1971 else 1972 { 1973 idx_from = idx_cur; 1974 idx_to = idx_other; 1975 /* Need to make the other buffer the current buffer to be able to make 1976 * changes in it. */ 1977 /* set curwin/curbuf to buf and save a few things */ 1978 aucmd_prepbuf(&aco, diffbuf[idx_other]); 1979 } 1980 1981 dprev = NULL; 1982 for (dp = first_diff; dp != NULL; ) 1983 { 1984 if (dp->df_lnum[idx_cur] > eap->line2 + off) 1985 break; /* past the range that was specified */ 1986 1987 dfree = NULL; 1988 lnum = dp->df_lnum[idx_to]; 1989 count = dp->df_count[idx_to]; 1990 if (dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off 1991 && u_save(lnum - 1, lnum + count) != FAIL) 1992 { 1993 /* Inside the specified range and saving for undo worked. */ 1994 start_skip = 0; 1995 end_skip = 0; 1996 if (eap->addr_count > 0) 1997 { 1998 /* A range was specified: check if lines need to be skipped. */ 1999 start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; 2000 if (start_skip > 0) 2001 { 2002 /* range starts below start of current diff block */ 2003 if (start_skip > count) 2004 { 2005 lnum += count; 2006 count = 0; 2007 } 2008 else 2009 { 2010 count -= start_skip; 2011 lnum += start_skip; 2012 } 2013 } 2014 else 2015 start_skip = 0; 2016 2017 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 2018 - (eap->line2 + off); 2019 if (end_skip > 0) 2020 { 2021 /* range ends above end of current/from diff block */ 2022 if (idx_cur == idx_from) /* :diffput */ 2023 { 2024 i = dp->df_count[idx_cur] - start_skip - end_skip; 2025 if (count > i) 2026 count = i; 2027 } 2028 else /* :diffget */ 2029 { 2030 count -= end_skip; 2031 end_skip = dp->df_count[idx_from] - start_skip - count; 2032 if (end_skip < 0) 2033 end_skip = 0; 2034 } 2035 } 2036 else 2037 end_skip = 0; 2038 } 2039 2040 added = 0; 2041 for (i = 0; i < count; ++i) 2042 { 2043 ml_delete(lnum, FALSE); 2044 --added; 2045 } 2046 for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) 2047 { 2048 linenr_T nr; 2049 2050 nr = dp->df_lnum[idx_from] + start_skip + i; 2051 if (nr > diffbuf[idx_from]->b_ml.ml_line_count) 2052 break; 2053 p = vim_strsave(ml_get_buf(diffbuf[idx_from], nr, FALSE)); 2054 if (p != NULL) 2055 { 2056 ml_append(lnum + i - 1, p, 0, FALSE); 2057 vim_free(p); 2058 ++added; 2059 } 2060 } 2061 new_count = dp->df_count[idx_to] + added; 2062 dp->df_count[idx_to] = new_count; 2063 2064 if (start_skip == 0 && end_skip == 0) 2065 { 2066 /* Check if there are any other buffers and if the diff is 2067 * equal in them. */ 2068 for (i = 0; i < DB_COUNT; ++i) 2069 if (diffbuf[i] != NULL && i != idx_from && i != idx_to 2070 && !diff_equal_entry(dp, idx_from, i)) 2071 break; 2072 if (i == DB_COUNT) 2073 { 2074 /* delete the diff entry, the buffers are now equal here */ 2075 dfree = dp; 2076 dp = dp->df_next; 2077 if (dprev == NULL) 2078 first_diff = dp; 2079 else 2080 dprev->df_next = dp; 2081 } 2082 } 2083 2084 /* Adjust marks. This will change the following entries! */ 2085 if (added != 0) 2086 { 2087 mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added); 2088 if (curwin->w_cursor.lnum >= lnum) 2089 { 2090 /* Adjust the cursor position if it's in/after the changed 2091 * lines. */ 2092 if (curwin->w_cursor.lnum >= lnum + count) 2093 curwin->w_cursor.lnum += added; 2094 else if (added < 0) 2095 curwin->w_cursor.lnum = lnum; 2096 } 2097 } 2098 changed_lines(lnum, 0, lnum + count, (long)added); 2099 2100 if (dfree != NULL) 2101 { 2102 /* Diff is deleted, update folds in other windows. */ 2103 #ifdef FEAT_FOLDING 2104 diff_fold_update(dfree, idx_to); 2105 #endif 2106 vim_free(dfree); 2107 } 2108 else 2109 /* mark_adjust() may have changed the count in a wrong way */ 2110 dp->df_count[idx_to] = new_count; 2111 2112 /* When changing the current buffer, keep track of line numbers */ 2113 if (idx_cur == idx_to) 2114 off += added; 2115 } 2116 2117 /* If before the range or not deleted, go to next diff. */ 2118 if (dfree == NULL) 2119 { 2120 dprev = dp; 2121 dp = dp->df_next; 2122 } 2123 } 2124 2125 /* restore curwin/curbuf and a few other things */ 2126 if (idx_other == idx_to) 2127 { 2128 /* Syncing undo only works for the current buffer, but we change 2129 * another buffer. Sync undo if the command was typed. This isn't 2130 * 100% right when ":diffput" is used in a function or mapping. */ 2131 if (KeyTyped) 2132 u_sync(); 2133 aucmd_restbuf(&aco); 2134 } 2135 2136 diff_busy = FALSE; 2137 2138 /* Check that the cursor is on a valid character and update it's position. 2139 * When there were filler lines the topline has become invalid. */ 2140 check_cursor(); 2141 changed_line_abv_curs(); 2142 2143 /* Also need to redraw the other buffers. */ 2144 diff_redraw(FALSE); 2145 } 2146 2147 #ifdef FEAT_FOLDING 2148 /* 2149 * Update folds for all diff buffers for entry "dp". 2150 * Skip buffer with index "skip_idx". 2151 * When there are no diffs, all folds are removed. 2152 */ 2153 static void 2154 diff_fold_update(dp, skip_idx) 2155 diff_T *dp; 2156 int skip_idx; 2157 { 2158 int i; 2159 win_T *wp; 2160 2161 for (wp = firstwin; wp != NULL; wp = wp->w_next) 2162 for (i = 0; i < DB_COUNT; ++i) 2163 if (diffbuf[i] == wp->w_buffer && i != skip_idx) 2164 foldUpdate(wp, dp->df_lnum[i], 2165 dp->df_lnum[i] + dp->df_count[i]); 2166 } 2167 #endif 2168 2169 /* 2170 * Return TRUE if buffer "buf" is in diff-mode. 2171 */ 2172 int 2173 diff_mode_buf(buf) 2174 buf_T *buf; 2175 { 2176 return diff_buf_idx(buf) != DB_COUNT; 2177 } 2178 2179 /* 2180 * Move "count" times in direction "dir" to the next diff block. 2181 * Return FAIL if there isn't such a diff block. 2182 */ 2183 int 2184 diff_move_to(dir, count) 2185 int dir; 2186 long count; 2187 { 2188 int idx; 2189 linenr_T lnum = curwin->w_cursor.lnum; 2190 diff_T *dp; 2191 2192 idx = diff_buf_idx(curbuf); 2193 if (idx == DB_COUNT || first_diff == NULL) 2194 return FAIL; 2195 2196 if (diff_invalid) 2197 ex_diffupdate(NULL); /* update after a big change */ 2198 2199 if (first_diff == NULL) /* no diffs today */ 2200 return FAIL; 2201 2202 while (--count >= 0) 2203 { 2204 /* Check if already before first diff. */ 2205 if (dir == BACKWARD && lnum <= first_diff->df_lnum[idx]) 2206 break; 2207 2208 for (dp = first_diff; ; dp = dp->df_next) 2209 { 2210 if (dp == NULL) 2211 break; 2212 if ((dir == FORWARD && lnum < dp->df_lnum[idx]) 2213 || (dir == BACKWARD 2214 && (dp->df_next == NULL 2215 || lnum <= dp->df_next->df_lnum[idx]))) 2216 { 2217 lnum = dp->df_lnum[idx]; 2218 break; 2219 } 2220 } 2221 } 2222 2223 /* don't end up past the end of the file */ 2224 if (lnum > curbuf->b_ml.ml_line_count) 2225 lnum = curbuf->b_ml.ml_line_count; 2226 2227 /* When the cursor didn't move at all we fail. */ 2228 if (lnum == curwin->w_cursor.lnum) 2229 return FAIL; 2230 2231 setpcmark(); 2232 curwin->w_cursor.lnum = lnum; 2233 curwin->w_cursor.col = 0; 2234 2235 return OK; 2236 } 2237 2238 #if defined(FEAT_FOLDING) || defined(PROTO) 2239 /* 2240 * For line "lnum" in the current window find the equivalent lnum in window 2241 * "wp", compensating for inserted/deleted lines. 2242 */ 2243 linenr_T 2244 diff_lnum_win(lnum, wp) 2245 linenr_T lnum; 2246 win_T *wp; 2247 { 2248 diff_T *dp; 2249 int idx; 2250 int i; 2251 linenr_T n; 2252 2253 idx = diff_buf_idx(curbuf); 2254 if (idx == DB_COUNT) /* safety check */ 2255 return (linenr_T)0; 2256 2257 if (diff_invalid) 2258 ex_diffupdate(NULL); /* update after a big change */ 2259 2260 /* search for a change that includes "lnum" in the list of diffblocks. */ 2261 for (dp = first_diff; dp != NULL; dp = dp->df_next) 2262 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) 2263 break; 2264 2265 /* When after the last change, compute relative to the last line number. */ 2266 if (dp == NULL) 2267 return wp->w_buffer->b_ml.ml_line_count 2268 - (curbuf->b_ml.ml_line_count - lnum); 2269 2270 /* Find index for "wp". */ 2271 i = diff_buf_idx(wp->w_buffer); 2272 if (i == DB_COUNT) /* safety check */ 2273 return (linenr_T)0; 2274 2275 n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); 2276 if (n > dp->df_lnum[i] + dp->df_count[i]) 2277 n = dp->df_lnum[i] + dp->df_count[i]; 2278 return n; 2279 } 2280 #endif 2281 2282 #endif /* FEAT_DIFF */ 2283