diff.c (129247B)
1 /// @file diff.c 2 /// 3 /// Code for diff'ing two, three or four buffers. 4 /// 5 /// There are three ways to diff: 6 /// - Shell out to an external diff program, using files. 7 /// - Use the compiled-in xdiff library. 8 /// - Let 'diffexpr' do the work, using files. 9 10 #include <assert.h> 11 #include <ctype.h> 12 #include <inttypes.h> 13 #include <stdbool.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "auto/config.h" 19 #include "nvim/ascii_defs.h" 20 #include "nvim/autocmd.h" 21 #include "nvim/autocmd_defs.h" 22 #include "nvim/buffer.h" 23 #include "nvim/bufwrite.h" 24 #include "nvim/change.h" 25 #include "nvim/charset.h" 26 #include "nvim/cursor.h" 27 #include "nvim/decoration.h" 28 #include "nvim/diff.h" 29 #include "nvim/drawscreen.h" 30 #include "nvim/errors.h" 31 #include "nvim/eval/typval.h" 32 #include "nvim/eval/vars.h" 33 #include "nvim/ex_cmds.h" 34 #include "nvim/ex_cmds_defs.h" 35 #include "nvim/ex_docmd.h" 36 #include "nvim/extmark.h" 37 #include "nvim/extmark_defs.h" 38 #include "nvim/fileio.h" 39 #include "nvim/fold.h" 40 #include "nvim/garray.h" 41 #include "nvim/garray_defs.h" 42 #include "nvim/gettext_defs.h" 43 #include "nvim/globals.h" 44 #include "nvim/linematch.h" 45 #include "nvim/mark.h" 46 #include "nvim/mbyte.h" 47 #include "nvim/mbyte_defs.h" 48 #include "nvim/memline.h" 49 #include "nvim/memline_defs.h" 50 #include "nvim/memory.h" 51 #include "nvim/message.h" 52 #include "nvim/move.h" 53 #include "nvim/normal.h" 54 #include "nvim/option.h" 55 #include "nvim/option_defs.h" 56 #include "nvim/option_vars.h" 57 #include "nvim/optionstr.h" 58 #include "nvim/os/fs.h" 59 #include "nvim/os/fs_defs.h" 60 #include "nvim/os/os.h" 61 #include "nvim/os/os_defs.h" 62 #include "nvim/os/shell.h" 63 #include "nvim/path.h" 64 #include "nvim/pos_defs.h" 65 #include "nvim/strings.h" 66 #include "nvim/types_defs.h" 67 #include "nvim/ui.h" 68 #include "nvim/undo.h" 69 #include "nvim/vim_defs.h" 70 #include "nvim/window.h" 71 #include "xdiff/xdiff.h" 72 73 static bool diff_busy = false; // using diff structs, don't change them 74 static bool diff_need_update = false; // ex_diffupdate needs to be called 75 76 // Flags obtained from the 'diffopt' option 77 #define DIFF_FILLER 0x001 // display filler lines 78 #define DIFF_IBLANK 0x002 // ignore empty lines 79 #define DIFF_ICASE 0x004 // ignore case 80 #define DIFF_IWHITE 0x008 // ignore change in white space 81 #define DIFF_IWHITEALL 0x010 // ignore all white space changes 82 #define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL 83 #define DIFF_HORIZONTAL 0x040 // horizontal splits 84 #define DIFF_VERTICAL 0x080 // vertical splits 85 #define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden 86 #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm 87 #define DIFF_CLOSE_OFF 0x400 // diffoff when closing window 88 #define DIFF_FOLLOWWRAP 0x800 // follow the wrap option 89 #define DIFF_LINEMATCH 0x1000 // match most similar lines within diff 90 #define DIFF_INLINE_NONE 0x2000 // no inline highlight 91 #define DIFF_INLINE_SIMPLE 0x4000 // inline highlight with simple algorithm 92 #define DIFF_INLINE_CHAR 0x8000 // inline highlight with character diff 93 #define DIFF_INLINE_WORD 0x10000 // inline highlight with word diff 94 #define DIFF_ANCHOR 0x20000 // use 'diffanchors' to anchor the diff 95 #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) 96 #define ALL_INLINE (DIFF_INLINE_NONE | DIFF_INLINE_SIMPLE | DIFF_INLINE_CHAR | DIFF_INLINE_WORD) 97 #define ALL_INLINE_DIFF (DIFF_INLINE_CHAR | DIFF_INLINE_WORD) 98 static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF 99 | DIFF_LINEMATCH | DIFF_INLINE_CHAR; 100 101 static int diff_algorithm = XDF_INDENT_HEURISTIC; 102 static int linematch_lines = 40; 103 104 #define LBUFLEN 50 // length of line in diff file 105 106 // Max file size xdiff is equipped to deal with. The value (1GB - 1MB) comes 107 // from Git's implementation. 108 #define MAX_XDIFF_SIZE (1024L * 1024 * 1023) 109 110 // kTrue when "diff -a" works, kFalse when it doesn't work, 111 // kNone when not checked yet 112 static TriState diff_a_works = kNone; 113 114 enum { MAX_DIFF_ANCHORS = 20, }; 115 116 // used for diff input 117 typedef struct { 118 char *din_fname; // used for external diff 119 mmfile_t din_mmfile; // used for internal diff 120 } diffin_T; 121 122 // used for diff result 123 typedef struct { 124 char *dout_fname; // used for external diff 125 garray_T dout_ga; // used for internal diff 126 } diffout_T; 127 128 // used for recording hunks from xdiff 129 typedef struct { 130 linenr_T lnum_orig; 131 int count_orig; 132 linenr_T lnum_new; 133 int count_new; 134 } diffhunk_T; 135 136 // two diff inputs and one result 137 typedef struct { 138 diffin_T dio_orig; // original file input 139 diffin_T dio_new; // new file input 140 diffout_T dio_diff; // diff result 141 int dio_internal; // using internal diff 142 } diffio_T; 143 144 typedef enum { 145 DIFF_ED, 146 DIFF_UNIFIED, 147 DIFF_NONE, 148 } diffstyle_T; 149 150 #include "diff.c.generated.h" 151 152 #define FOR_ALL_DIFFBLOCKS_IN_TAB(tp, dp) \ 153 for ((dp) = (tp)->tp_first_diff; (dp) != NULL; (dp) = (dp)->df_next) 154 155 static void clear_diffblock(diff_T *dp) 156 { 157 ga_clear(&dp->df_changes); 158 xfree(dp); 159 } 160 161 /// Called when deleting or unloading a buffer: No longer make a diff with it. 162 /// 163 /// @param buf 164 void diff_buf_delete(buf_T *buf) 165 { 166 FOR_ALL_TABS(tp) { 167 int i = diff_buf_idx(buf, tp); 168 169 if (i != DB_COUNT) { 170 tp->tp_diffbuf[i] = NULL; 171 tp->tp_diff_invalid = true; 172 173 if (tp == curtab) { 174 // don't redraw right away, more might change or buffer state 175 // is invalid right now 176 need_diff_redraw = true; 177 redraw_later(curwin, UPD_VALID); 178 } 179 } 180 } 181 } 182 183 /// Check if the current buffer should be added to or removed from the list of 184 /// diff buffers. 185 /// 186 /// @param win 187 void diff_buf_adjust(win_T *win) 188 { 189 if (!win->w_p_diff) { 190 // When there is no window showing a diff for this buffer, remove 191 // it from the diffs. 192 bool found_win = false; 193 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 194 if ((wp->w_buffer == win->w_buffer) && wp->w_p_diff) { 195 found_win = true; 196 } 197 } 198 199 if (!found_win) { 200 int i = diff_buf_idx(win->w_buffer, curtab); 201 if (i != DB_COUNT) { 202 curtab->tp_diffbuf[i] = NULL; 203 curtab->tp_diff_invalid = true; 204 diff_redraw(true); 205 } 206 } 207 } else { 208 diff_buf_add(win->w_buffer); 209 } 210 } 211 212 /// Add a buffer to make diffs for. 213 /// 214 /// Call this when a new buffer is being edited in the current window where 215 /// 'diff' is set. 216 /// Marks the current buffer as being part of the diff and requiring updating. 217 /// This must be done before any autocmd, because a command may use info 218 /// about the screen contents. 219 /// 220 /// @param buf The buffer to add. 221 void diff_buf_add(buf_T *buf) 222 { 223 if (diff_buf_idx(buf, curtab) != DB_COUNT) { 224 // It's already there. 225 return; 226 } 227 228 for (int i = 0; i < DB_COUNT; i++) { 229 if (curtab->tp_diffbuf[i] == NULL) { 230 curtab->tp_diffbuf[i] = buf; 231 curtab->tp_diff_invalid = true; 232 diff_redraw(true); 233 return; 234 } 235 } 236 237 semsg(_("E96: Cannot diff more than %d buffers"), DB_COUNT); 238 } 239 240 /// Remove all buffers to make diffs for. 241 static void diff_buf_clear(void) 242 { 243 for (int i = 0; i < DB_COUNT; i++) { 244 if (curtab->tp_diffbuf[i] != NULL) { 245 curtab->tp_diffbuf[i] = NULL; 246 curtab->tp_diff_invalid = true; 247 diff_redraw(true); 248 } 249 } 250 } 251 252 /// Find buffer "buf" in the list of diff buffers for tab page "tp". 253 /// 254 /// @param buf 255 /// @param tp 256 /// 257 /// @return its index or DB_COUNT if not found. 258 static int diff_buf_idx(buf_T *buf, tabpage_T *tp) 259 { 260 int idx; 261 for (idx = 0; idx < DB_COUNT; idx++) { 262 if (tp->tp_diffbuf[idx] == buf) { 263 break; 264 } 265 } 266 return idx; 267 } 268 269 /// Mark the diff info involving buffer "buf" as invalid, it will be updated 270 /// when info is requested. 271 /// 272 /// @param buf 273 void diff_invalidate(buf_T *buf) 274 { 275 FOR_ALL_TABS(tp) { 276 int i = diff_buf_idx(buf, tp); 277 if (i != DB_COUNT) { 278 tp->tp_diff_invalid = true; 279 if (tp == curtab) { 280 diff_redraw(true); 281 } 282 } 283 } 284 } 285 286 /// Called by mark_adjust(): update line numbers in "buf". 287 /// 288 /// @param line1 289 /// @param line2 290 /// @param amount 291 /// @param amount_after 292 void diff_mark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount, 293 linenr_T amount_after) 294 { 295 // Handle all tab pages that use "buf" in a diff. 296 FOR_ALL_TABS(tp) { 297 int idx = diff_buf_idx(buf, tp); 298 if (idx != DB_COUNT) { 299 diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after); 300 } 301 } 302 } 303 304 /// Update line numbers in tab page "tp" for the buffer with index "idx". 305 /// 306 /// This attempts to update the changes as much as possible: 307 /// When inserting/deleting lines outside of existing change blocks, create a 308 /// new change block and update the line numbers in following blocks. 309 /// When inserting/deleting lines in existing change blocks, update them. 310 /// 311 /// @param tp 312 /// @param idx 313 /// @param line1 314 /// @param line2 315 /// @param amount 316 /// @amount_after 317 static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, 318 linenr_T amount, linenr_T amount_after) 319 { 320 if (diff_internal()) { 321 // Will update diffs before redrawing. Set _invalid to update the 322 // diffs themselves, set _update to also update folds properly just 323 // before redrawing. 324 // Do update marks here, it is needed for :%diffput. 325 tp->tp_diff_invalid = true; 326 tp->tp_diff_update = true; 327 } 328 329 linenr_T inserted; 330 linenr_T deleted; 331 if (line2 == MAXLNUM) { 332 // mark_adjust(99, MAXLNUM, 9, 0): insert lines 333 inserted = amount; 334 deleted = 0; 335 } else if (amount_after > 0) { 336 // mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines 337 inserted = amount_after; 338 deleted = 0; 339 } else { 340 // mark_adjust(98, 99, MAXLNUM, -2): delete lines 341 inserted = 0; 342 deleted = -amount_after; 343 } 344 345 diff_T *dprev = NULL; 346 diff_T *dp = tp->tp_first_diff; 347 348 linenr_T lnum_deleted = line1; // lnum of remaining deletion 349 while (true) { 350 // If the change is after the previous diff block and before the next 351 // diff block, thus not touching an existing change, create a new diff 352 // block. Don't do this when ex_diffgetput() is busy. 353 if (((dp == NULL) 354 || (dp->df_lnum[idx] - 1 > line2) 355 || ((line2 == MAXLNUM) && (dp->df_lnum[idx] > line1))) 356 && ((dprev == NULL) 357 || (dprev->df_lnum[idx] + dprev->df_count[idx] < line1)) 358 && !diff_busy) { 359 diff_T *dnext = diff_alloc_new(tp, dprev, dp); 360 361 dnext->df_lnum[idx] = line1; 362 dnext->df_count[idx] = inserted; 363 for (int i = 0; i < DB_COUNT; i++) { 364 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { 365 if (dprev == NULL) { 366 dnext->df_lnum[i] = line1; 367 } else { 368 dnext->df_lnum[i] = line1 369 + (dprev->df_lnum[i] + dprev->df_count[i]) 370 - (dprev->df_lnum[idx] + dprev->df_count[idx]); 371 } 372 dnext->df_count[i] = deleted; 373 } 374 } 375 } 376 377 // if at end of the list, quit 378 if (dp == NULL) { 379 break; 380 } 381 382 // Check for these situations: 383 // 1 2 3 384 // 1 2 3 385 // line1 2 3 4 5 386 // 2 3 4 5 387 // 2 3 4 5 388 // line2 2 3 4 5 389 // 3 5 6 390 // 3 5 6 391 392 // compute last line of this change 393 linenr_T last = dp->df_lnum[idx] + dp->df_count[idx] - 1; 394 395 // 1. change completely above line1: nothing to do 396 if (last >= line1 - 1) { 397 if (diff_busy) { 398 // Currently in the middle of updating diff blocks. All we want 399 // is to adjust the line numbers and nothing else. 400 if (dp->df_lnum[idx] > line2) { 401 dp->df_lnum[idx] += amount_after; 402 } 403 404 // Advance to next entry. 405 dprev = dp; 406 dp = dp->df_next; 407 continue; 408 } 409 410 // 6. change below line2: only adjust for amount_after; also when 411 // "deleted" became zero when deleted all lines between two diffs. 412 if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) { 413 if (amount_after == 0) { 414 // nothing left to change 415 break; 416 } 417 dp->df_lnum[idx] += amount_after; 418 } else { 419 bool check_unchanged = false; 420 421 // 2. 3. 4. 5.: inserted/deleted lines touching this diff. 422 if (deleted > 0) { 423 linenr_T n; 424 linenr_T off = 0; 425 if (dp->df_lnum[idx] >= line1) { 426 if (last <= line2) { 427 // 4. delete all lines of diff 428 if ((dp->df_next != NULL) 429 && (dp->df_next->df_lnum[idx] - 1 <= line2)) { 430 // delete continues in next diff, only do 431 // lines until that one 432 n = dp->df_next->df_lnum[idx] - lnum_deleted; 433 deleted -= n; 434 n -= dp->df_count[idx]; 435 lnum_deleted = dp->df_next->df_lnum[idx]; 436 } else { 437 n = deleted - dp->df_count[idx]; 438 } 439 dp->df_count[idx] = 0; 440 } else { 441 // 5. delete lines at or just before top of diff 442 off = dp->df_lnum[idx] - lnum_deleted; 443 n = off; 444 dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; 445 check_unchanged = true; 446 } 447 dp->df_lnum[idx] = line1; 448 } else { 449 if (last < line2) { 450 // 2. delete at end of diff 451 dp->df_count[idx] -= last - lnum_deleted + 1; 452 453 if ((dp->df_next != NULL) 454 && (dp->df_next->df_lnum[idx] - 1 <= line2)) { 455 // delete continues in next diff, only do 456 // lines until that one 457 n = dp->df_next->df_lnum[idx] - 1 - last; 458 deleted -= dp->df_next->df_lnum[idx] - lnum_deleted; 459 lnum_deleted = dp->df_next->df_lnum[idx]; 460 } else { 461 n = line2 - last; 462 } 463 check_unchanged = true; 464 } else { 465 // 3. delete lines inside the diff 466 n = 0; 467 dp->df_count[idx] -= deleted; 468 } 469 } 470 471 for (int i = 0; i < DB_COUNT; i++) { 472 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { 473 if (dp->df_lnum[i] > off) { 474 dp->df_lnum[i] -= off; 475 } else { 476 dp->df_lnum[i] = 1; 477 } 478 dp->df_count[i] += n; 479 } 480 } 481 } else { 482 if (dp->df_lnum[idx] <= line1) { 483 // inserted lines somewhere in this diff 484 dp->df_count[idx] += inserted; 485 check_unchanged = true; 486 } else { 487 // inserted lines somewhere above this diff 488 dp->df_lnum[idx] += inserted; 489 } 490 } 491 492 if (check_unchanged) { 493 // Check if inserted lines are equal, may reduce the size of the 494 // diff. 495 // 496 // TODO(unknown): also check for equal lines in the middle and perhaps split 497 // the block. 498 diff_check_unchanged(tp, dp); 499 } 500 } 501 } 502 503 // check if this block touches the previous one, may merge them. 504 if ((dprev != NULL) && !dp->is_linematched && !diff_busy 505 && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) { 506 for (int i = 0; i < DB_COUNT; i++) { 507 if (tp->tp_diffbuf[i] != NULL) { 508 dprev->df_count[i] += dp->df_count[i]; 509 } 510 } 511 dp = diff_free(tp, dprev, dp); 512 } else { 513 // Advance to next entry. 514 dprev = dp; 515 dp = dp->df_next; 516 } 517 } 518 519 dprev = NULL; 520 dp = tp->tp_first_diff; 521 522 while (dp != NULL) { 523 // All counts are zero, remove this entry. 524 int i; 525 for (i = 0; i < DB_COUNT; i++) { 526 if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) { 527 break; 528 } 529 } 530 531 if (i == DB_COUNT) { 532 dp = diff_free(tp, dprev, dp); 533 } else { 534 // Advance to next entry. 535 dprev = dp; 536 dp = dp->df_next; 537 } 538 } 539 540 if (tp == curtab) { 541 // Don't redraw right away, this updates the diffs, which can be slow. 542 need_diff_redraw = true; 543 544 // Need to recompute the scroll binding, may remove or add filler 545 // lines (e.g., when adding lines above w_topline). But it's slow when 546 // making many changes, postpone until redrawing. 547 diff_need_scrollbind = true; 548 } 549 } 550 551 /// Allocate a new diff block and link it between "dprev" and "dp". 552 /// 553 /// @param tp 554 /// @param dprev 555 /// @param dp 556 /// 557 /// @return The new diff block. 558 static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) 559 { 560 diff_T *dnew = xcalloc(1, sizeof(*dnew)); 561 562 dnew->is_linematched = false; 563 dnew->df_next = dp; 564 if (dprev == NULL) { 565 tp->tp_first_diff = dnew; 566 } else { 567 dprev->df_next = dnew; 568 } 569 570 dnew->has_changes = false; 571 ga_init(&dnew->df_changes, sizeof(diffline_change_T), 20); 572 return dnew; 573 } 574 575 static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp) 576 { 577 diff_T *ret = dp->df_next; 578 clear_diffblock(dp); 579 580 if (dprev == NULL) { 581 tp->tp_first_diff = ret; 582 } else { 583 dprev->df_next = ret; 584 } 585 586 return ret; 587 } 588 589 /// Check if the diff block "dp" can be made smaller for lines at the start and 590 /// end that are equal. Called after inserting lines. 591 /// 592 /// This may result in a change where all buffers have zero lines, the caller 593 /// must take care of removing it. 594 /// 595 /// @param tp 596 /// @param dp 597 static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) 598 { 599 // Find the first buffers, use it as the original, compare the other 600 // buffer lines against this one. 601 int i_org; 602 for (i_org = 0; i_org < DB_COUNT; i_org++) { 603 if (tp->tp_diffbuf[i_org] != NULL) { 604 break; 605 } 606 } 607 608 // safety check 609 if (i_org == DB_COUNT) { 610 return; 611 } 612 613 if (diff_check_sanity(tp, dp) == FAIL) { 614 return; 615 } 616 617 // First check lines at the top, then at the bottom. 618 linenr_T off_org = 0; 619 linenr_T off_new = 0; 620 int dir = FORWARD; 621 while (true) { 622 // Repeat until a line is found which is different or the number of 623 // lines has become zero. 624 while (dp->df_count[i_org] > 0) { 625 // Copy the line, the next ml_get() will invalidate it. 626 if (dir == BACKWARD) { 627 off_org = dp->df_count[i_org] - 1; 628 } 629 char *line_org = xstrdup(ml_get_buf(tp->tp_diffbuf[i_org], dp->df_lnum[i_org] + off_org)); 630 631 int i_new; 632 for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) { 633 if (tp->tp_diffbuf[i_new] == NULL) { 634 continue; 635 } 636 637 if (dir == BACKWARD) { 638 off_new = dp->df_count[i_new] - 1; 639 } 640 641 // if other buffer doesn't have this line, it was inserted 642 if ((off_new < 0) || (off_new >= dp->df_count[i_new])) { 643 break; 644 } 645 646 if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], 647 dp->df_lnum[i_new] + off_new)) != 0) { 648 break; 649 } 650 } 651 xfree(line_org); 652 653 // Stop when a line isn't equal in all diff buffers. 654 if (i_new != DB_COUNT) { 655 break; 656 } 657 658 // Line matched in all buffers, remove it from the diff. 659 for (i_new = i_org; i_new < DB_COUNT; i_new++) { 660 if (tp->tp_diffbuf[i_new] != NULL) { 661 if (dir == FORWARD) { 662 dp->df_lnum[i_new]++; 663 } 664 dp->df_count[i_new]--; 665 } 666 } 667 } 668 669 if (dir == BACKWARD) { 670 break; 671 } 672 dir = BACKWARD; 673 } 674 } 675 676 /// Check if a diff block doesn't contain invalid line numbers. 677 /// This can happen when the diff program returns invalid results. 678 /// 679 /// @param tp 680 /// @param dp 681 /// 682 /// @return OK if the diff block doesn't contain invalid line numbers. 683 static int diff_check_sanity(tabpage_T *tp, diff_T *dp) 684 { 685 for (int i = 0; i < DB_COUNT; i++) { 686 if (tp->tp_diffbuf[i] != NULL) { 687 if (dp->df_lnum[i] + dp->df_count[i] - 1 688 > tp->tp_diffbuf[i]->b_ml.ml_line_count) { 689 return FAIL; 690 } 691 } 692 } 693 return OK; 694 } 695 696 /// Mark all diff buffers in the current tab page for redraw. 697 /// 698 /// @param dofold Also recompute the folds 699 void diff_redraw(bool dofold) 700 { 701 win_T *wp_other = NULL; 702 bool used_max_fill_other = false; 703 bool used_max_fill_curwin = false; 704 705 need_diff_redraw = false; 706 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 707 // when closing windows or wiping buffers skip invalid window 708 if (!wp->w_p_diff || !buf_valid(wp->w_buffer)) { 709 continue; 710 } 711 712 redraw_later(wp, UPD_SOME_VALID); 713 if (wp != curwin) { 714 wp_other = wp; 715 } 716 if (dofold && foldmethodIsDiff(wp)) { 717 foldUpdateAll(wp); 718 } 719 720 // A change may have made filler lines invalid, need to take care of 721 // that for other windows. 722 int n = diff_check_fill(wp, wp->w_topline); 723 724 if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) { 725 if (wp->w_topfill > n) { 726 wp->w_topfill = MAX(n, 0); 727 } else if ((n > 0) && (n > wp->w_topfill)) { 728 wp->w_topfill = n; 729 if (wp == curwin) { 730 used_max_fill_curwin = true; 731 } else if (wp_other != NULL) { 732 used_max_fill_other = true; 733 } 734 } 735 check_topfill(wp, false); 736 } 737 } 738 739 if (wp_other != NULL && curwin->w_p_scb) { 740 if (used_max_fill_curwin) { 741 // The current window was set to use the maximum number of filler 742 // lines, may need to reduce them. 743 diff_set_topline(wp_other, curwin); 744 } else if (used_max_fill_other) { 745 // The other window was set to use the maximum number of filler 746 // lines, may need to reduce them. 747 diff_set_topline(curwin, wp_other); 748 } 749 } 750 } 751 752 static void clear_diffin(diffin_T *din) 753 { 754 if (din->din_fname == NULL) { 755 XFREE_CLEAR(din->din_mmfile.ptr); 756 } else { 757 os_remove(din->din_fname); 758 } 759 } 760 761 static void clear_diffout(diffout_T *dout) 762 { 763 if (dout->dout_fname == NULL) { 764 ga_clear(&dout->dout_ga); 765 } else { 766 os_remove(dout->dout_fname); 767 } 768 } 769 770 /// Write buffer "buf" to a memory buffer. 771 /// 772 /// @param buf 773 /// @param din 774 /// 775 /// @return FAIL for failure. 776 static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end) 777 { 778 if (end < 0) { 779 end = buf->b_ml.ml_line_count; 780 } 781 782 if (buf->b_ml.ml_flags & ML_EMPTY || end < start) { 783 m->ptr = NULL; 784 m->size = 0; 785 return OK; 786 } 787 788 size_t len = 0; 789 790 // xdiff requires one big block of memory with all the text. 791 for (linenr_T lnum = start; lnum <= end; lnum++) { 792 len += (size_t)ml_get_buf_len(buf, lnum) + 1; 793 } 794 char *ptr = xmalloc(len); 795 m->ptr = ptr; 796 m->size = (int)len; 797 798 len = 0; 799 for (linenr_T lnum = start; lnum <= end; lnum++) { 800 char *s = ml_get_buf(buf, lnum); 801 if (diff_flags & DIFF_ICASE) { 802 while (*s != NUL) { 803 int c; 804 int c_len = 1; 805 char cbuf[MB_MAXBYTES + 1]; 806 807 if (*s == NL) { 808 c = NUL; 809 } else { 810 // xdiff doesn't support ignoring case, fold-case the text. 811 c = utf_ptr2char(s); 812 c_len = utf_char2len(c); 813 c = utf_fold(c); 814 } 815 const int orig_len = utfc_ptr2len(s); 816 817 if (utf_char2bytes(c, cbuf) != c_len) { 818 // TODO(Bram): handle byte length difference 819 // One example is â„« (3 bytes) and Ã¥ (2 bytes). 820 memmove(ptr + len, s, (size_t)orig_len); 821 } else { 822 memmove(ptr + len, cbuf, (size_t)c_len); 823 if (orig_len > c_len) { 824 // Copy remaining composing characters 825 memmove(ptr + len + c_len, s + c_len, (size_t)(orig_len - c_len)); 826 } 827 } 828 829 s += orig_len; 830 len += (size_t)orig_len; 831 } 832 } else { 833 size_t slen = strlen(s); 834 memmove(ptr + len, s, slen); 835 // NUL is represented as NL; convert 836 memchrsub(ptr + len, NL, NUL, slen); 837 len += slen; 838 } 839 ptr[len++] = NL; 840 } 841 return OK; 842 } 843 844 /// Write buffer "buf" to file or memory buffer. 845 /// 846 /// Always use 'fileformat' set to "unix". 847 /// 848 /// @param buf 849 /// @param din 850 /// 851 /// @return FAIL for failure 852 static int diff_write(buf_T *buf, diffin_T *din, linenr_T start, linenr_T end) 853 { 854 if (din->din_fname == NULL) { 855 return diff_write_buffer(buf, &din->din_mmfile, start, end); 856 } 857 858 // Writing the diff buffers may trigger changes in the window structure 859 // via aucmd_prepbuf()/aucmd_restbuf() commands. 860 // This may cause recursively calling winframe_remove() which is not safe and causes 861 // use after free, so let's stop it here. 862 if (frames_locked()) { 863 return FAIL; 864 } 865 866 if (end < 0) { 867 end = buf->b_ml.ml_line_count; 868 } 869 870 // Always use 'fileformat' set to "unix". 871 int save_ml_flags = buf->b_ml.ml_flags; 872 char *save_ff = buf->b_p_ff; 873 buf->b_p_ff = xstrdup("unix"); 874 const bool save_cmod_flags = cmdmod.cmod_flags; 875 // Writing the buffer is an implementation detail of performing the diff, 876 // so it shouldn't update the '[ and '] marks. 877 cmdmod.cmod_flags |= CMOD_LOCKMARKS; 878 if (end < start) { 879 // The line range specifies a completely empty file. 880 end = start; 881 buf->b_ml.ml_flags |= ML_EMPTY; 882 } 883 int r = buf_write(buf, din->din_fname, NULL, 884 start, end, 885 NULL, false, false, false, true); 886 cmdmod.cmod_flags = save_cmod_flags; 887 free_string_option(buf->b_p_ff); 888 buf->b_p_ff = save_ff; 889 buf->b_ml.ml_flags = (buf->b_ml.ml_flags & ~ML_EMPTY) | (save_ml_flags & ML_EMPTY); 890 return r; 891 } 892 893 static int lnum_compare(const void *s1, const void *s2) 894 { 895 linenr_T lnum1 = *(linenr_T *)s1; 896 linenr_T lnum2 = *(linenr_T *)s2; 897 if (lnum1 < lnum2) { 898 return -1; 899 } 900 if (lnum1 > lnum2) { 901 return 1; 902 } 903 return 0; 904 } 905 906 /// Update the diffs for all buffers involved. 907 /// 908 /// @param dio 909 /// @param idx_orig 910 /// @param eap can be NULL 911 static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) 912 { 913 if (dio->dio_internal) { 914 ga_init(&dio->dio_diff.dout_ga, sizeof(diffhunk_T), 100); 915 } else { 916 // We need three temp file names. 917 dio->dio_orig.din_fname = vim_tempname(); 918 dio->dio_new.din_fname = vim_tempname(); 919 dio->dio_diff.dout_fname = vim_tempname(); 920 if (dio->dio_orig.din_fname == NULL 921 || dio->dio_new.din_fname == NULL 922 || dio->dio_diff.dout_fname == NULL) { 923 goto theend; 924 } 925 // Check external diff is actually working. 926 if (check_external_diff(dio) == FAIL) { 927 goto theend; 928 } 929 } 930 931 // :diffupdate! 932 if (eap != NULL && eap->forceit) { 933 for (int idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { 934 buf_T *buf = curtab->tp_diffbuf[idx_new]; 935 if (buf_valid(buf)) { 936 buf_check_timestamp(buf); 937 } 938 } 939 } 940 941 // Parse and sort diff anchors if enabled 942 int num_anchors = INT_MAX; 943 linenr_T anchors[DB_COUNT][MAX_DIFF_ANCHORS]; 944 CLEAR_FIELD(anchors); 945 if (diff_flags & DIFF_ANCHOR) { 946 for (int idx = 0; idx < DB_COUNT; idx++) { 947 if (curtab->tp_diffbuf[idx] == NULL) { 948 continue; 949 } 950 int buf_num_anchors = 0; 951 if (parse_diffanchors(false, 952 curtab->tp_diffbuf[idx], 953 anchors[idx], 954 &buf_num_anchors) != OK) { 955 emsg(_(e_failed_to_find_all_diff_anchors)); 956 num_anchors = 0; 957 CLEAR_FIELD(anchors); 958 break; 959 } 960 if (buf_num_anchors < num_anchors) { 961 num_anchors = buf_num_anchors; 962 } 963 964 if (buf_num_anchors > 0) { 965 qsort((void *)anchors[idx], 966 (size_t)buf_num_anchors, 967 sizeof(linenr_T), 968 lnum_compare); 969 } 970 } 971 } 972 if (num_anchors == INT_MAX) { 973 num_anchors = 0; 974 } 975 976 // Split the files into multiple sections by anchors. Each section starts 977 // from one anchor (inclusive) and ends at the next anchor (exclusive). 978 // Diff each section separately before combining the results. If we don't 979 // have any anchors, we will have one big section of the entire file. 980 for (int anchor_i = 0; anchor_i <= num_anchors; anchor_i++) { 981 diff_T *orig_diff = NULL; 982 if (anchor_i != 0) { 983 orig_diff = curtab->tp_first_diff; 984 curtab->tp_first_diff = NULL; 985 } 986 linenr_T lnum_start = (anchor_i == 0) ? 1 : anchors[idx_orig][anchor_i - 1]; 987 linenr_T lnum_end = (anchor_i == num_anchors) ? -1 : anchors[idx_orig][anchor_i] - 1; 988 989 // Write the first buffer to a tempfile or mmfile_t. 990 buf_T *buf = curtab->tp_diffbuf[idx_orig]; 991 if (diff_write(buf, &dio->dio_orig, lnum_start, lnum_end) == FAIL) { 992 if (orig_diff != NULL) { 993 // Clean up in-progress diff blocks 994 curtab->tp_first_diff = orig_diff; 995 diff_clear(curtab); 996 } 997 goto theend; 998 } 999 1000 // Make a difference between the first buffer and every other. 1001 for (int idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { 1002 buf = curtab->tp_diffbuf[idx_new]; 1003 if (buf == NULL || buf->b_ml.ml_mfp == NULL) { 1004 continue; // skip buffer that isn't loaded 1005 } 1006 lnum_start = anchor_i == 0 ? 1 : anchors[idx_new][anchor_i - 1]; 1007 lnum_end = anchor_i == num_anchors ? -1 : anchors[idx_new][anchor_i] - 1; 1008 1009 // Write the other buffer and diff with the first one. 1010 if (diff_write(buf, &dio->dio_new, lnum_start, lnum_end) == FAIL) { 1011 continue; 1012 } 1013 if (diff_file(dio) == FAIL) { 1014 continue; 1015 } 1016 1017 // Read the diff output and add each entry to the diff list. 1018 diff_read(idx_orig, idx_new, dio); 1019 1020 clear_diffin(&dio->dio_new); 1021 clear_diffout(&dio->dio_diff); 1022 } 1023 clear_diffin(&dio->dio_orig); 1024 1025 if (anchor_i != 0) { 1026 // Combine the new diff blocks with the existing ones 1027 for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 1028 for (int idx = 0; idx < DB_COUNT; idx++) { 1029 if (anchors[idx][anchor_i - 1] > 0) { 1030 dp->df_lnum[idx] += anchors[idx][anchor_i - 1] - 1; 1031 } 1032 } 1033 } 1034 if (orig_diff != NULL) { 1035 diff_T *last_diff = orig_diff; 1036 while (last_diff->df_next != NULL) { 1037 last_diff = last_diff->df_next; 1038 } 1039 last_diff->df_next = curtab->tp_first_diff; 1040 curtab->tp_first_diff = orig_diff; 1041 } 1042 } 1043 } 1044 1045 theend: 1046 xfree(dio->dio_orig.din_fname); 1047 xfree(dio->dio_new.din_fname); 1048 xfree(dio->dio_diff.dout_fname); 1049 } 1050 1051 /// Return true if the options are set to use the internal diff library. 1052 /// Note that if the internal diff failed for one of the buffers, the external 1053 /// diff will be used anyway. 1054 int diff_internal(void) 1055 FUNC_ATTR_PURE 1056 { 1057 return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; 1058 } 1059 1060 /// Completely update the diffs for the buffers involved. 1061 /// 1062 /// When using the external "diff" command the buffers are written to a file, 1063 /// also for unmodified buffers (the file could have been produced by 1064 /// autocommands, e.g. the netrw plugin). 1065 /// 1066 /// @param eap can be NULL 1067 void ex_diffupdate(exarg_T *eap) 1068 { 1069 if (diff_busy) { 1070 diff_need_update = true; 1071 return; 1072 } 1073 1074 int had_diffs = curtab->tp_first_diff != NULL; 1075 1076 // Delete all diffblocks. 1077 diff_clear(curtab); 1078 curtab->tp_diff_invalid = false; 1079 1080 // Use the first buffer as the original text. 1081 int idx_orig; 1082 for (idx_orig = 0; idx_orig < DB_COUNT; idx_orig++) { 1083 if (curtab->tp_diffbuf[idx_orig] != NULL) { 1084 break; 1085 } 1086 } 1087 1088 if (idx_orig == DB_COUNT) { 1089 goto theend; 1090 } 1091 1092 // Only need to do something when there is another buffer. 1093 int idx_new; 1094 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { 1095 if (curtab->tp_diffbuf[idx_new] != NULL) { 1096 break; 1097 } 1098 } 1099 1100 if (idx_new == DB_COUNT) { 1101 goto theend; 1102 } 1103 1104 // Only use the internal method if it did not fail for one of the buffers. 1105 diffio_T diffio = { 0 }; 1106 diffio.dio_internal = diff_internal(); 1107 1108 diff_try_update(&diffio, idx_orig, eap); 1109 1110 // force updating cursor position on screen 1111 curwin->w_valid_cursor.lnum = 0; 1112 1113 theend: 1114 // A redraw is needed if there were diffs and they were cleared, or there 1115 // are diffs now, which means they got updated. 1116 if (had_diffs || curtab->tp_first_diff != NULL) { 1117 diff_redraw(true); 1118 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); 1119 } 1120 } 1121 1122 /// Do a quick test if "diff" really works. Otherwise it looks like there 1123 /// are no differences. Can't use the return value, it's non-zero when 1124 /// there are differences. 1125 static int check_external_diff(diffio_T *diffio) 1126 { 1127 // May try twice, first with "-a" and then without. 1128 bool io_error = false; 1129 TriState ok = kFalse; 1130 while (true) { 1131 ok = kFalse; 1132 FILE *fd = os_fopen(diffio->dio_orig.din_fname, "w"); 1133 1134 if (fd == NULL) { 1135 io_error = true; 1136 } else { 1137 if (fwrite("line1\n", 6, 1, fd) != 1) { 1138 io_error = true; 1139 } 1140 fclose(fd); 1141 fd = os_fopen(diffio->dio_new.din_fname, "w"); 1142 1143 if (fd == NULL) { 1144 io_error = true; 1145 } else { 1146 if (fwrite("line2\n", 6, 1, fd) != 1) { 1147 io_error = true; 1148 } 1149 fclose(fd); 1150 fd = diff_file(diffio) == OK 1151 ? os_fopen(diffio->dio_diff.dout_fname, "r") 1152 : NULL; 1153 1154 if (fd == NULL) { 1155 io_error = true; 1156 } else { 1157 char linebuf[LBUFLEN]; 1158 1159 while (true) { 1160 // For normal diff there must be a line that contains 1161 // "1c1". For unified diff "@@ -1 +1 @@". 1162 if (vim_fgets(linebuf, LBUFLEN, fd)) { 1163 break; 1164 } 1165 1166 if (strncmp(linebuf, "1c1", 3) == 0 1167 || strncmp(linebuf, "@@ -1 +1 @@", 11) == 0) { 1168 ok = kTrue; 1169 } 1170 } 1171 fclose(fd); 1172 } 1173 os_remove(diffio->dio_diff.dout_fname); 1174 os_remove(diffio->dio_new.din_fname); 1175 } 1176 os_remove(diffio->dio_orig.din_fname); 1177 } 1178 1179 // When using 'diffexpr' break here. 1180 if (*p_dex != NUL) { 1181 break; 1182 } 1183 1184 // If we checked if "-a" works already, break here. 1185 if (diff_a_works != kNone) { 1186 break; 1187 } 1188 diff_a_works = ok; 1189 1190 // If "-a" works break here, otherwise retry without "-a". 1191 if (ok) { 1192 break; 1193 } 1194 } 1195 1196 if (!ok) { 1197 if (io_error) { 1198 emsg(_("E810: Cannot read or write temp files")); 1199 } 1200 emsg(_("E97: Cannot create diffs")); 1201 diff_a_works = kNone; 1202 return FAIL; 1203 } 1204 return OK; 1205 } 1206 1207 /// Invoke the xdiff function. 1208 static int diff_file_internal(diffio_T *diffio) 1209 { 1210 xpparam_t param; 1211 xdemitconf_t emit_cfg; 1212 xdemitcb_t emit_cb; 1213 1214 CLEAR_FIELD(param); 1215 CLEAR_FIELD(emit_cfg); 1216 CLEAR_FIELD(emit_cb); 1217 1218 param.flags = (unsigned long)diff_algorithm; 1219 1220 if (diff_flags & DIFF_IWHITE) { 1221 param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; 1222 } 1223 if (diff_flags & DIFF_IWHITEALL) { 1224 param.flags |= XDF_IGNORE_WHITESPACE; 1225 } 1226 if (diff_flags & DIFF_IWHITEEOL) { 1227 param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; 1228 } 1229 if (diff_flags & DIFF_IBLANK) { 1230 param.flags |= XDF_IGNORE_BLANK_LINES; 1231 } 1232 1233 emit_cfg.ctxlen = 0; // don't need any diff_context here 1234 emit_cb.priv = &diffio->dio_diff; 1235 emit_cfg.hunk_func = xdiff_out; 1236 if (diffio->dio_orig.din_mmfile.size > MAX_XDIFF_SIZE 1237 || diffio->dio_new.din_mmfile.size > MAX_XDIFF_SIZE) { 1238 emsg(_(e_problem_creating_internal_diff)); 1239 return FAIL; 1240 } 1241 if (xdl_diff(&diffio->dio_orig.din_mmfile, 1242 &diffio->dio_new.din_mmfile, 1243 ¶m, &emit_cfg, &emit_cb) < 0) { 1244 emsg(_(e_problem_creating_internal_diff)); 1245 return FAIL; 1246 } 1247 return OK; 1248 } 1249 1250 /// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". 1251 /// 1252 /// @param dio 1253 /// 1254 /// @return OK or FAIL 1255 static int diff_file(diffio_T *dio) 1256 { 1257 char *tmp_orig = dio->dio_orig.din_fname; 1258 char *tmp_new = dio->dio_new.din_fname; 1259 char *tmp_diff = dio->dio_diff.dout_fname; 1260 if (*p_dex != NUL) { 1261 // Use 'diffexpr' to generate the diff file. 1262 eval_diff(tmp_orig, tmp_new, tmp_diff); 1263 return OK; 1264 } 1265 // Use xdiff for generating the diff. 1266 if (dio->dio_internal) { 1267 return diff_file_internal(dio); 1268 } 1269 1270 const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) 1271 + strlen(p_srr) + 27); 1272 char *const cmd = xmalloc(len); 1273 1274 // We don't want $DIFF_OPTIONS to get in the way. 1275 if (os_env_exists("DIFF_OPTIONS", true)) { 1276 os_unsetenv("DIFF_OPTIONS"); 1277 } 1278 1279 // Build the diff command and execute it. Always use -a, binary 1280 // differences are of no use. Ignore errors, diff returns 1281 // non-zero when differences have been found. 1282 vim_snprintf(cmd, len, "diff %s%s%s%s%s%s%s%s %s", 1283 diff_a_works == kFalse ? "" : "-a ", 1284 "", 1285 (diff_flags & DIFF_IWHITE) ? "-b " : "", 1286 (diff_flags & DIFF_IWHITEALL) ? "-w " : "", 1287 (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", 1288 (diff_flags & DIFF_IBLANK) ? "-B " : "", 1289 (diff_flags & DIFF_ICASE) ? "-i " : "", 1290 tmp_orig, tmp_new); 1291 append_redir(cmd, len, p_srr, tmp_diff); 1292 block_autocmds(); // Avoid ShellCmdPost stuff 1293 call_shell(cmd, 1294 kShellOptFilter | kShellOptSilent | kShellOptDoOut, 1295 NULL); 1296 unblock_autocmds(); 1297 xfree(cmd); 1298 return OK; 1299 } 1300 1301 /// Create a new version of a file from the current buffer and a diff file. 1302 /// 1303 /// The buffer is written to a file, also for unmodified buffers (the file 1304 /// could have been produced by autocommands, e.g. the netrw plugin). 1305 /// 1306 /// @param eap 1307 void ex_diffpatch(exarg_T *eap) 1308 { 1309 char *buf = NULL; 1310 win_T *old_curwin = curwin; 1311 char *newname = NULL; // name of patched file buffer 1312 char *esc_name = NULL; 1313 1314 #ifdef UNIX 1315 char *fullname = NULL; 1316 #endif 1317 1318 // We need two temp file names. 1319 // Name of original temp file. 1320 char *tmp_orig = vim_tempname(); 1321 // Name of patched temp file. 1322 char *tmp_new = vim_tempname(); 1323 1324 if ((tmp_orig == NULL) || (tmp_new == NULL)) { 1325 goto theend; 1326 } 1327 1328 // Write the current buffer to "tmp_orig". 1329 if (buf_write(curbuf, tmp_orig, NULL, 1330 1, curbuf->b_ml.ml_line_count, 1331 NULL, false, false, false, true) == FAIL) { 1332 goto theend; 1333 } 1334 1335 #ifdef UNIX 1336 // Get the absolute path of the patchfile, changing directory below. 1337 fullname = FullName_save(eap->arg, false); 1338 esc_name = vim_strsave_shellescape(fullname != NULL ? fullname : eap->arg, true, true); 1339 #else 1340 esc_name = vim_strsave_shellescape(eap->arg, true, true); 1341 #endif 1342 size_t buflen = strlen(tmp_orig) + strlen(esc_name) + strlen(tmp_new) + 16; 1343 buf = xmalloc(buflen); 1344 1345 #ifdef UNIX 1346 char dirbuf[MAXPATHL]; 1347 // Temporarily chdir to /tmp, to avoid patching files in the current 1348 // directory when the patch file contains more than one patch. When we 1349 // have our own temp dir use that instead, it will be cleaned up when we 1350 // exit (any .rej files created). Don't change directory if we can't 1351 // return to the current. 1352 if ((os_dirname(dirbuf, MAXPATHL) != OK) 1353 || (os_chdir(dirbuf) != 0)) { 1354 dirbuf[0] = NUL; 1355 } else { 1356 char *tempdir = vim_gettempdir(); 1357 if (tempdir == NULL) { 1358 tempdir = "/tmp"; 1359 } 1360 os_chdir(tempdir); 1361 shorten_fnames(true); 1362 } 1363 #endif 1364 1365 if (*p_pex != NUL) { 1366 // Use 'patchexpr' to generate the new file. 1367 #ifdef UNIX 1368 eval_patch(tmp_orig, (fullname != NULL ? fullname : eap->arg), tmp_new); 1369 #else 1370 eval_patch(tmp_orig, eap->arg, tmp_new); 1371 #endif 1372 } else { 1373 // Build the patch command and execute it. Ignore errors. 1374 vim_snprintf(buf, buflen, "patch -o %s %s < %s", 1375 tmp_new, tmp_orig, esc_name); 1376 block_autocmds(); // Avoid ShellCmdPost stuff 1377 call_shell(buf, kShellOptFilter, NULL); 1378 unblock_autocmds(); 1379 } 1380 1381 #ifdef UNIX 1382 if (dirbuf[0] != NUL) { 1383 if (os_chdir(dirbuf) != 0) { 1384 emsg(_(e_prev_dir)); 1385 } 1386 shorten_fnames(true); 1387 } 1388 #endif 1389 1390 // Delete any .orig or .rej file created. 1391 STRCPY(buf, tmp_new); 1392 strcat(buf, ".orig"); 1393 os_remove(buf); 1394 STRCPY(buf, tmp_new); 1395 strcat(buf, ".rej"); 1396 os_remove(buf); 1397 1398 // Only continue if the output file was created. 1399 FileInfo file_info; 1400 bool info_ok = os_fileinfo(tmp_new, &file_info); 1401 uint64_t filesize = os_fileinfo_size(&file_info); 1402 if (!info_ok || filesize == 0) { 1403 emsg(_("E816: Cannot read patch output")); 1404 } else { 1405 if (curbuf->b_fname != NULL) { 1406 newname = xstrnsave(curbuf->b_fname, strlen(curbuf->b_fname) + 4); 1407 strcat(newname, ".new"); 1408 } 1409 1410 // don't use a new tab page, each tab page has its own diffs 1411 cmdmod.cmod_tab = 0; 1412 1413 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { 1414 // Pretend it was a ":split fname" command 1415 eap->cmdidx = CMD_split; 1416 eap->arg = tmp_new; 1417 do_exedit(eap, old_curwin); 1418 1419 // check that split worked and editing tmp_new 1420 if ((curwin != old_curwin) && win_valid(old_curwin)) { 1421 // Set 'diff', 'scrollbind' on and 'wrap' off. 1422 diff_win_options(curwin, true); 1423 diff_win_options(old_curwin, true); 1424 1425 if (newname != NULL) { 1426 // do a ":file filename.new" on the patched buffer 1427 eap->arg = newname; 1428 ex_file(eap); 1429 1430 // Do filetype detection with the new name. 1431 if (augroup_exists("filetypedetect")) { 1432 do_cmdline_cmd(":doau filetypedetect BufRead"); 1433 } 1434 } 1435 } 1436 } 1437 } 1438 1439 theend: 1440 if (tmp_orig != NULL) { 1441 os_remove(tmp_orig); 1442 } 1443 xfree(tmp_orig); 1444 1445 if (tmp_new != NULL) { 1446 os_remove(tmp_new); 1447 } 1448 xfree(tmp_new); 1449 xfree(newname); 1450 xfree(buf); 1451 #ifdef UNIX 1452 xfree(fullname); 1453 #endif 1454 xfree(esc_name); 1455 } 1456 1457 /// Split the window and edit another file, setting options to show the diffs. 1458 /// 1459 /// @param eap 1460 void ex_diffsplit(exarg_T *eap) 1461 { 1462 win_T *old_curwin = curwin; 1463 bufref_T old_curbuf; 1464 set_bufref(&old_curbuf, curbuf); 1465 1466 // Need to compute w_fraction when no redraw happened yet. 1467 validate_cursor(curwin); 1468 set_fraction(curwin); 1469 1470 // don't use a new tab page, each tab page has its own diffs 1471 cmdmod.cmod_tab = 0; 1472 1473 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) == FAIL) { 1474 return; 1475 } 1476 1477 // Pretend it was a ":split fname" command 1478 eap->cmdidx = CMD_split; 1479 curwin->w_p_diff = true; 1480 do_exedit(eap, old_curwin); 1481 1482 if (curwin == old_curwin) { // split didn't work 1483 return; 1484 } 1485 1486 // Set 'diff', 'scrollbind' on and 'wrap' off. 1487 diff_win_options(curwin, true); 1488 if (win_valid(old_curwin)) { 1489 diff_win_options(old_curwin, true); 1490 1491 if (bufref_valid(&old_curbuf)) { 1492 // Move the cursor position to that of the old window. 1493 curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf, 1494 old_curwin->w_cursor.lnum); 1495 } 1496 } 1497 // Now that lines are folded scroll to show the cursor at the same 1498 // relative position. 1499 scroll_to_fraction(curwin, curwin->w_height); 1500 } 1501 1502 // Set options to show diffs for the current window. 1503 void ex_diffthis(exarg_T *eap) 1504 { 1505 // Set 'diff', 'scrollbind' on and 'wrap' off. 1506 diff_win_options(curwin, true); 1507 } 1508 1509 static void set_diff_option(win_T *wp, bool value) 1510 { 1511 win_T *old_curwin = curwin; 1512 1513 curwin = wp; 1514 curbuf = curwin->w_buffer; 1515 curbuf->b_ro_locked++; 1516 set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(value), OPT_LOCAL); 1517 curbuf->b_ro_locked--; 1518 curwin = old_curwin; 1519 curbuf = curwin->w_buffer; 1520 } 1521 1522 /// Set options in window "wp" for diff mode. 1523 /// 1524 /// @param addbuf Add buffer to diff. 1525 void diff_win_options(win_T *wp, bool addbuf) 1526 { 1527 win_T *old_curwin = curwin; 1528 1529 // close the manually opened folds 1530 curwin = wp; 1531 newFoldLevel(); 1532 curwin = old_curwin; 1533 1534 // Use 'scrollbind' and 'cursorbind' when available 1535 if (!wp->w_p_diff) { 1536 wp->w_p_scb_save = wp->w_p_scb; 1537 } 1538 wp->w_p_scb = true; 1539 1540 if (!wp->w_p_diff) { 1541 wp->w_p_crb_save = wp->w_p_crb; 1542 } 1543 wp->w_p_crb = true; 1544 if (!(diff_flags & DIFF_FOLLOWWRAP)) { 1545 if (!wp->w_p_diff) { 1546 wp->w_p_wrap_save = wp->w_p_wrap; 1547 } 1548 wp->w_p_wrap = false; 1549 wp->w_skipcol = 0; 1550 } 1551 1552 if (!wp->w_p_diff) { 1553 if (wp->w_p_diff_saved) { 1554 free_string_option(wp->w_p_fdm_save); 1555 } 1556 wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); 1557 } 1558 set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, 1559 kOptScopeWin, wp); 1560 1561 if (!wp->w_p_diff) { 1562 wp->w_p_fen_save = wp->w_p_fen; 1563 wp->w_p_fdl_save = wp->w_p_fdl; 1564 1565 if (wp->w_p_diff_saved) { 1566 free_string_option(wp->w_p_fdc_save); 1567 } 1568 wp->w_p_fdc_save = xstrdup(wp->w_p_fdc); 1569 } 1570 free_string_option(wp->w_p_fdc); 1571 wp->w_p_fdc = xstrdup("2"); 1572 assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9); 1573 snprintf(wp->w_p_fdc, strlen(wp->w_p_fdc) + 1, "%d", diff_foldcolumn); 1574 wp->w_p_fen = true; 1575 wp->w_p_fdl = 0; 1576 foldUpdateAll(wp); 1577 1578 // make sure topline is not halfway through a fold 1579 changed_window_setting(wp); 1580 if (vim_strchr(p_sbo, 'h') == NULL) { 1581 do_cmdline_cmd("set sbo+=hor"); 1582 } 1583 1584 // Save the current values, to be restored in ex_diffoff(). 1585 wp->w_p_diff_saved = true; 1586 1587 set_diff_option(wp, true); 1588 1589 if (addbuf) { 1590 diff_buf_add(wp->w_buffer); 1591 } 1592 redraw_later(wp, UPD_NOT_VALID); 1593 } 1594 1595 /// Set options not to show diffs. For the current window or all windows. 1596 /// Only in the current tab page. 1597 /// 1598 /// @param eap 1599 void ex_diffoff(exarg_T *eap) 1600 { 1601 bool diffwin = false; 1602 1603 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 1604 if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { 1605 // Set 'diff' off. If option values were saved in 1606 // diff_win_options(), restore the ones whose settings seem to have 1607 // been left over from diff mode. 1608 set_diff_option(wp, false); 1609 1610 if (wp->w_p_diff_saved) { 1611 if (wp->w_p_scb) { 1612 wp->w_p_scb = wp->w_p_scb_save; 1613 } 1614 1615 if (wp->w_p_crb) { 1616 wp->w_p_crb = wp->w_p_crb_save; 1617 } 1618 if (!(diff_flags & DIFF_FOLLOWWRAP)) { 1619 if (!wp->w_p_wrap && wp->w_p_wrap_save) { 1620 wp->w_p_wrap = true; 1621 wp->w_leftcol = 0; 1622 } 1623 } 1624 free_string_option(wp->w_p_fdm); 1625 wp->w_p_fdm = xstrdup(*wp->w_p_fdm_save ? wp->w_p_fdm_save : "manual"); 1626 free_string_option(wp->w_p_fdc); 1627 wp->w_p_fdc = xstrdup(*wp->w_p_fdc_save ? wp->w_p_fdc_save : "0"); 1628 1629 if (wp->w_p_fdl == 0) { 1630 wp->w_p_fdl = wp->w_p_fdl_save; 1631 } 1632 // Only restore 'foldenable' when 'foldmethod' is not 1633 // "manual", otherwise we continue to show the diff folds. 1634 if (wp->w_p_fen) { 1635 wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save; 1636 } 1637 1638 foldUpdateAll(wp); 1639 } 1640 // remove filler lines 1641 wp->w_topfill = 0; 1642 1643 // make sure topline is not halfway a fold and cursor is 1644 // invalidated 1645 changed_window_setting(wp); 1646 1647 // Note: 'sbo' is not restored, it's a global option. 1648 diff_buf_adjust(wp); 1649 } 1650 diffwin |= wp->w_p_diff; 1651 } 1652 1653 // Also remove hidden buffers from the list. 1654 if (eap->forceit) { 1655 diff_buf_clear(); 1656 } 1657 1658 if (!diffwin) { 1659 diff_need_update = false; 1660 curtab->tp_diff_invalid = false; 1661 curtab->tp_diff_update = false; 1662 diff_clear(curtab); 1663 } 1664 1665 // Remove "hor" from 'scrollopt' if there are no diff windows left. 1666 if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) { 1667 do_cmdline_cmd("set sbo-=hor"); 1668 } 1669 } 1670 1671 static bool extract_hunk_internal(diffout_T *dout, diffhunk_T *hunk, int *line_idx) 1672 { 1673 bool eof = *line_idx >= dout->dout_ga.ga_len; 1674 if (!eof) { 1675 *hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[(*line_idx)++]; 1676 } 1677 return eof; 1678 } 1679 1680 // Extract hunk by parsing the diff output from file and calculate the diffstyle. 1681 static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) 1682 { 1683 while (true) { 1684 char line[LBUFLEN]; // only need to hold the diff line 1685 if (vim_fgets(line, LBUFLEN, fd)) { 1686 return true; // end of file 1687 } 1688 1689 if (*diffstyle == DIFF_NONE) { 1690 // Determine diff style. 1691 // ed like diff looks like this: 1692 // {first}[,{last}]c{first}[,{last}] 1693 // {first}a{first}[,{last}] 1694 // {first}[,{last}]d{first} 1695 // 1696 // unified diff looks like this: 1697 // --- file1 2018-03-20 13:23:35.783153140 +0100 1698 // +++ file2 2018-03-20 13:23:41.183156066 +0100 1699 // @@ -1,3 +1,5 @@ 1700 if (isdigit((uint8_t)(*line))) { 1701 *diffstyle = DIFF_ED; 1702 } else if ((strncmp(line, "@@ ", 3) == 0)) { 1703 *diffstyle = DIFF_UNIFIED; 1704 } else if ((strncmp(line, "--- ", 4) == 0) 1705 && (vim_fgets(line, LBUFLEN, fd) == 0) 1706 && (strncmp(line, "+++ ", 4) == 0) 1707 && (vim_fgets(line, LBUFLEN, fd) == 0) 1708 && (strncmp(line, "@@ ", 3) == 0)) { 1709 *diffstyle = DIFF_UNIFIED; 1710 } else { 1711 // Format not recognized yet, skip over this line. Cygwin diff 1712 // may put a warning at the start of the file. 1713 continue; 1714 } 1715 } 1716 1717 if (*diffstyle == DIFF_ED) { 1718 if (!isdigit((uint8_t)(*line))) { 1719 continue; // not the start of a diff block 1720 } 1721 if (parse_diff_ed(line, hunk) == FAIL) { 1722 continue; 1723 } 1724 } else { 1725 assert(*diffstyle == DIFF_UNIFIED); 1726 if (strncmp(line, "@@ ", 3) != 0) { 1727 continue; // not the start of a diff block 1728 } 1729 if (parse_diff_unified(line, hunk) == FAIL) { 1730 continue; 1731 } 1732 } 1733 1734 // Successfully parsed diff output, can return 1735 return false; 1736 } 1737 } 1738 1739 static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_new, diffhunk_T *hunk, 1740 bool *notsetp) 1741 { 1742 diff_T *dp = *dpp; 1743 diff_T *dprev = *dprevp; 1744 1745 // Go over blocks before the change, for which orig and new are equal. 1746 // Copy blocks from orig to new. 1747 while (dp != NULL 1748 && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { 1749 if (*notsetp) { 1750 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1751 } 1752 dprev = dp; 1753 dp = dp->df_next; 1754 *notsetp = true; 1755 } 1756 1757 if ((dp != NULL) 1758 && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) 1759 && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { 1760 // New block overlaps with existing block(s). 1761 // First find last block that overlaps. 1762 diff_T *dpl; 1763 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { 1764 if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { 1765 break; 1766 } 1767 } 1768 1769 // If the newly found block starts before the old one, set the 1770 // start back a number of lines. 1771 linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig; 1772 1773 if (off > 0) { 1774 for (int i = idx_orig; i < idx_new; i++) { 1775 if (curtab->tp_diffbuf[i] != NULL) { 1776 dp->df_lnum[i] -= off; 1777 dp->df_count[i] += off; 1778 } 1779 } 1780 dp->df_lnum[idx_new] = hunk->lnum_new; 1781 dp->df_count[idx_new] = (linenr_T)hunk->count_new; 1782 } else if (*notsetp) { 1783 // new block inside existing one, adjust new block 1784 dp->df_lnum[idx_new] = hunk->lnum_new + off; 1785 dp->df_count[idx_new] = (linenr_T)hunk->count_new - off; 1786 } else { 1787 // second overlap of new block with existing block 1788 1789 // if this hunk has different orig/new counts, adjust 1790 // the diff block size first. When we handled the first hunk we 1791 // would have expanded it to fit, without knowing that this 1792 // hunk exists 1793 int orig_size_in_dp = MIN(hunk->count_orig, 1794 dp->df_lnum[idx_orig] + 1795 dp->df_count[idx_orig] - hunk->lnum_orig); 1796 int size_diff = hunk->count_new - orig_size_in_dp; 1797 dp->df_count[idx_new] += size_diff; 1798 1799 // grow existing block to include the overlap completely 1800 off = hunk->lnum_new + hunk->count_new 1801 - (dp->df_lnum[idx_new] + dp->df_count[idx_new]); 1802 if (off > 0) { 1803 dp->df_count[idx_new] += off; 1804 } 1805 } 1806 1807 // Adjust the size of the block to include all the lines to the 1808 // end of the existing block or the new diff, whatever ends last. 1809 off = (hunk->lnum_orig + (linenr_T)hunk->count_orig) 1810 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); 1811 1812 if (off < 0) { 1813 // new change ends in existing block, adjust the end. We only 1814 // need to do this once per block or we will over-adjust. 1815 if (*notsetp || dp != dpl) { 1816 // adjusting by 'off' here is only correct if 1817 // there is not another hunk in this block. we 1818 // adjust for this when we encounter a second 1819 // overlap later. 1820 dp->df_count[idx_new] += -off; 1821 } 1822 off = 0; 1823 } 1824 1825 for (int i = idx_orig; i < idx_new; i++) { 1826 if (curtab->tp_diffbuf[i] != NULL) { 1827 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] 1828 - dp->df_lnum[i] + off; 1829 } 1830 } 1831 1832 // Delete the diff blocks that have been merged into one. 1833 diff_T *dn = dp->df_next; 1834 dp->df_next = dpl->df_next; 1835 1836 while (dn != dp->df_next) { 1837 dpl = dn->df_next; 1838 clear_diffblock(dn); 1839 dn = dpl; 1840 } 1841 } else { 1842 // Allocate a new diffblock. 1843 dp = diff_alloc_new(curtab, dprev, dp); 1844 1845 dp->df_lnum[idx_orig] = hunk->lnum_orig; 1846 dp->df_count[idx_orig] = (linenr_T)hunk->count_orig; 1847 dp->df_lnum[idx_new] = hunk->lnum_new; 1848 dp->df_count[idx_new] = (linenr_T)hunk->count_new; 1849 1850 // Set values for other buffers, these must be equal to the 1851 // original buffer, otherwise there would have been a change 1852 // already. 1853 for (int i = idx_orig + 1; i < idx_new; i++) { 1854 if (curtab->tp_diffbuf[i] != NULL) { 1855 diff_copy_entry(dprev, dp, idx_orig, i); 1856 } 1857 } 1858 } 1859 *notsetp = false; // "*dp" has been set 1860 *dpp = dp; 1861 *dprevp = dprev; 1862 } 1863 1864 /// Read the diff output and add each entry to the diff list. 1865 /// 1866 /// @param idx_orig idx of original file 1867 /// @param idx_new idx of new file 1868 /// @dout diff output 1869 static void diff_read(int idx_orig, int idx_new, diffio_T *dio) 1870 { 1871 FILE *fd = NULL; 1872 int line_hunk_idx = 0; // line or hunk index 1873 diff_T *dprev = NULL; 1874 diff_T *dp = curtab->tp_first_diff; 1875 diffout_T *dout = &dio->dio_diff; 1876 bool notset = true; // block "*dp" not set yet 1877 diffstyle_T diffstyle = DIFF_NONE; 1878 1879 if (!dio->dio_internal) { 1880 fd = os_fopen(dout->dout_fname, "r"); 1881 if (fd == NULL) { 1882 emsg(_("E98: Cannot read diff output")); 1883 return; 1884 } 1885 } 1886 1887 while (true) { 1888 diffhunk_T hunk = { 0 }; 1889 bool eof = dio->dio_internal 1890 ? extract_hunk_internal(dout, &hunk, &line_hunk_idx) 1891 : extract_hunk(fd, &hunk, &diffstyle); 1892 1893 if (eof) { 1894 break; 1895 } 1896 1897 process_hunk(&dp, &dprev, idx_orig, idx_new, &hunk, ¬set); 1898 } 1899 1900 // for remaining diff blocks orig and new are equal 1901 while (dp != NULL) { 1902 if (notset) { 1903 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1904 } 1905 dprev = dp; 1906 dp = dp->df_next; 1907 notset = true; 1908 } 1909 1910 if (fd != NULL) { 1911 fclose(fd); 1912 } 1913 } 1914 1915 /// Copy an entry at "dp" from "idx_orig" to "idx_new". 1916 /// 1917 /// @param dprev 1918 /// @param dp 1919 /// @param idx_orig 1920 /// @param idx_new 1921 static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new) 1922 { 1923 linenr_T off; 1924 1925 if (dprev == NULL) { 1926 off = 0; 1927 } else { 1928 off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig]) 1929 - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]); 1930 } 1931 dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off; 1932 dp->df_count[idx_new] = dp->df_count[idx_orig]; 1933 } 1934 1935 /// Clear the list of diffblocks for tab page "tp". 1936 /// 1937 /// @param tp 1938 void diff_clear(tabpage_T *tp) 1939 FUNC_ATTR_NONNULL_ALL 1940 { 1941 diff_T *next_p; 1942 for (diff_T *p = tp->tp_first_diff; p != NULL; p = next_p) { 1943 next_p = p->df_next; 1944 clear_diffblock(p); 1945 } 1946 tp->tp_first_diff = NULL; 1947 } 1948 1949 /// Return true if the options are set to use diff linematch. 1950 bool diff_linematch(diff_T *dp) 1951 { 1952 if (!(diff_flags & DIFF_LINEMATCH)) { 1953 return false; 1954 } 1955 // are there more than three diff buffers? 1956 int tsize = 0; 1957 for (int i = 0; i < DB_COUNT; i++) { 1958 if (curtab->tp_diffbuf[i] != NULL) { 1959 // for the rare case (bug?) that the count of a diff block is negative, do 1960 // not run the algorithm because this will try to allocate a negative 1961 // amount of space and crash 1962 if (dp->df_count[i] < 0) { 1963 return false; 1964 } 1965 tsize += dp->df_count[i]; 1966 } 1967 } 1968 // avoid allocating a huge array because it will lag 1969 return tsize <= linematch_lines; 1970 } 1971 1972 static int get_max_diff_length(const diff_T *dp) 1973 { 1974 int maxlength = 0; 1975 for (int k = 0; k < DB_COUNT; k++) { 1976 if (curtab->tp_diffbuf[k] != NULL) { 1977 if (dp->df_count[k] > maxlength) { 1978 maxlength = dp->df_count[k]; 1979 } 1980 } 1981 } 1982 return maxlength; 1983 } 1984 1985 /// Find the first diff block that includes the specified line. Also find the 1986 /// next diff block that's not in the current chain of adjacent blocks that are 1987 /// all touching each other directly. 1988 static void find_top_diff_block(diff_T **thistopdiff, diff_T **next_adjacent_blocks, int fromidx, 1989 int topline) 1990 { 1991 diff_T *topdiff = NULL; 1992 diff_T *localtopdiff = NULL; 1993 int topdiffchange = 0; 1994 1995 for (topdiff = curtab->tp_first_diff; topdiff != NULL; topdiff = topdiff->df_next) { 1996 // set the top of the current overlapping diff block set as we 1997 // iterate through all of the sets of overlapping diff blocks 1998 if (!localtopdiff || topdiffchange) { 1999 localtopdiff = topdiff; 2000 topdiffchange = 0; 2001 } 2002 2003 // check if the fromwin topline is matched by the current diff. if so, 2004 // set it to the top of the diff block 2005 if (topline >= topdiff->df_lnum[fromidx] && topline <= 2006 (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) { 2007 // this line is inside the current diff block, so we will save the 2008 // top block of the set of blocks to refer to later 2009 if ((*thistopdiff) == NULL) { 2010 (*thistopdiff) = localtopdiff; 2011 } 2012 } 2013 2014 // check if the next set of overlapping diff blocks is next 2015 if (!(topdiff->df_next && (topdiff->df_next->df_lnum[fromidx] == 2016 (topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])))) { 2017 // mark that the next diff block is belongs to a different set of 2018 // overlapping diff blocks 2019 topdiffchange = 1; 2020 2021 // if we already have found that the line number is inside a diff block, 2022 // set the marker of the next block and finish the iteration 2023 if (*thistopdiff) { 2024 (*next_adjacent_blocks) = topdiff->df_next; 2025 break; 2026 } 2027 } 2028 } 2029 } 2030 2031 /// Calculates topline/topfill of a target diff window to fit the source diff window. 2032 static void calculate_topfill_and_topline(const int fromidx, const int toidx, const 2033 int from_topline, const int from_topfill, int *topfill, 2034 linenr_T *topline) 2035 { 2036 // find the position from the top of the diff block, and the next diff 2037 // block that's no longer adjacent to the current block. "Adjacency" means 2038 // a chain of diff blocks that are directly touching each other, allowed by 2039 // linematch and diff anchors. 2040 diff_T *thistopdiff = NULL; 2041 diff_T *next_adjacent_blocks = NULL; 2042 int virtual_lines_passed = 0; 2043 2044 find_top_diff_block(&thistopdiff, &next_adjacent_blocks, fromidx, from_topline); 2045 2046 // count the virtual lines (either filler or concrete line) that have been 2047 // passed in the source buffer. There could be multiple diff blocks if 2048 // there are adjacent empty blocks (count == 0 at fromidx). 2049 2050 diff_T *curdif = thistopdiff; 2051 while (curdif && (curdif->df_lnum[fromidx] + curdif->df_count[fromidx]) 2052 <= from_topline) { 2053 virtual_lines_passed += get_max_diff_length(curdif); 2054 2055 curdif = curdif->df_next; 2056 } 2057 2058 if (curdif != next_adjacent_blocks) { 2059 virtual_lines_passed += from_topline - curdif->df_lnum[fromidx]; 2060 } 2061 virtual_lines_passed -= from_topfill; 2062 2063 // clamp negative values in case from_topfill hasn't been updated yet and 2064 // is larger than total virtual lines, which could happen when setting 2065 // diffopt multiple times 2066 if (virtual_lines_passed < 0) { 2067 virtual_lines_passed = 0; 2068 } 2069 2070 // move the same amount of virtual lines in the target buffer to find the 2071 // cursor's line number 2072 int curlinenum_to 2073 = thistopdiff != NULL // this should not be null, but just for safety 2074 ? thistopdiff->df_lnum[toidx] : 1; 2075 2076 int virt_lines_left = virtual_lines_passed; 2077 curdif = thistopdiff; 2078 while (virt_lines_left > 0 && curdif != NULL && curdif != next_adjacent_blocks) { 2079 curlinenum_to += MIN(virt_lines_left, curdif->df_count[toidx]); 2080 virt_lines_left -= MIN(virt_lines_left, get_max_diff_length(curdif)); 2081 curdif = curdif->df_next; 2082 } 2083 2084 // count the total number of virtual lines between the top diff block and 2085 // the found line in the target buffer 2086 int max_virt_lines = 0; 2087 for (diff_T *dp = thistopdiff; dp != NULL; dp = dp->df_next) { 2088 if (dp->df_lnum[toidx] + dp->df_count[toidx] <= curlinenum_to) { 2089 max_virt_lines += get_max_diff_length(dp); 2090 } else { 2091 if (dp->df_lnum[toidx] <= curlinenum_to) { 2092 max_virt_lines += curlinenum_to - dp->df_lnum[toidx]; 2093 } 2094 break; 2095 } 2096 } 2097 2098 if (diff_flags & DIFF_FILLER) { 2099 // should always be non-negative as max_virt_lines is larger 2100 (*topfill) = max_virt_lines - virtual_lines_passed; 2101 } 2102 (*topline) = curlinenum_to; 2103 } 2104 2105 // Apply results from the linematch algorithm and apply to 'dp' by splitting it into multiple 2106 // adjacent diff blocks. 2107 static void apply_linematch_results(diff_T *dp, size_t decisions_length, const int *decisions) 2108 { 2109 // get the start line number here in each diff buffer, and then increment 2110 int line_numbers[DB_COUNT]; 2111 int outputmap[DB_COUNT]; 2112 size_t ndiffs = 0; 2113 for (int i = 0; i < DB_COUNT; i++) { 2114 if (curtab->tp_diffbuf[i] != NULL) { 2115 line_numbers[i] = dp->df_lnum[i]; 2116 dp->df_count[i] = 0; 2117 2118 // Keep track of the index of the diff buffer we are using here. 2119 // We will use this to write the output of the algorithm to 2120 // diff_T structs at the correct indexes 2121 outputmap[ndiffs] = i; 2122 ndiffs++; 2123 } 2124 } 2125 2126 // write the diffs starting with the current diff block 2127 diff_T *dp_s = dp; 2128 for (size_t i = 0; i < decisions_length; i++) { 2129 // Don't allocate on first iter since we can reuse the initial diffblock 2130 if (i != 0 && (decisions[i - 1] != decisions[i])) { 2131 // create new sub diff blocks to segment the original diff block which we 2132 // further divided by running the linematch algorithm 2133 dp_s = diff_alloc_new(curtab, dp_s, dp_s->df_next); 2134 dp_s->is_linematched = true; 2135 for (int j = 0; j < DB_COUNT; j++) { 2136 if (curtab->tp_diffbuf[j] != NULL) { 2137 dp_s->df_lnum[j] = line_numbers[j]; 2138 dp_s->df_count[j] = 0; 2139 } 2140 } 2141 } 2142 for (size_t j = 0; j < ndiffs; j++) { 2143 if (decisions[i] & (1 << j)) { 2144 // will need to use the map here 2145 dp_s->df_count[outputmap[j]]++; 2146 line_numbers[outputmap[j]]++; 2147 } 2148 } 2149 } 2150 dp->is_linematched = true; 2151 } 2152 2153 static void run_linematch_algorithm(diff_T *dp) 2154 { 2155 // define buffers for diff algorithm 2156 mmfile_t diffbufs_mm[DB_COUNT]; 2157 const mmfile_t *diffbufs[DB_COUNT]; 2158 int diff_length[DB_COUNT]; 2159 size_t ndiffs = 0; 2160 for (int i = 0; i < DB_COUNT; i++) { 2161 if (curtab->tp_diffbuf[i] != NULL) { 2162 if (dp->df_count[i] > 0) { 2163 // write the contents of the entire buffer to 2164 // diffbufs_mm[diffbuffers_count] 2165 diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs], 2166 dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1); 2167 } else { 2168 diffbufs_mm[ndiffs].size = 0; 2169 diffbufs_mm[ndiffs].ptr = NULL; 2170 } 2171 2172 diffbufs[ndiffs] = &diffbufs_mm[ndiffs]; 2173 2174 // keep track of the length of this diff block to pass it to the linematch 2175 // algorithm 2176 diff_length[ndiffs] = dp->df_count[i]; 2177 2178 // increment the amount of diff buffers we are passing to the algorithm 2179 ndiffs++; 2180 } 2181 } 2182 2183 // we will get the output of the linematch algorithm in the format of an array 2184 // of integers (*decisions) and the length of that array (decisions_length) 2185 int *decisions = NULL; 2186 const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0; 2187 size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite); 2188 2189 for (size_t i = 0; i < ndiffs; i++) { 2190 XFREE_CLEAR(diffbufs_mm[i].ptr); 2191 } 2192 2193 apply_linematch_results(dp, decisions_length, decisions); 2194 2195 xfree(decisions); 2196 } 2197 2198 /// Check diff status for line "lnum" in buffer "buf": 2199 /// 2200 /// Returns > 0 for inserting that many filler lines above it (never happens 2201 /// when 'diffopt' doesn't contain "filler"). Otherwise returns 0. 2202 /// 2203 /// "linestatus" (can be NULL) will be set to: 2204 /// 0 for nothing special. 2205 /// -1 for a line that should be highlighted as changed. 2206 /// -2 for a line that should be highlighted as added/deleted. 2207 /// 2208 /// This should only be used for windows where 'diff' is set. 2209 /// 2210 /// Note that it's possible for a changed/added/deleted line to also have filler 2211 /// lines above it. This happens when using linematch or using diff anchors (at 2212 /// the anchored lines). 2213 /// 2214 /// @param wp 2215 /// @param lnum 2216 /// @param[out] linestatus 2217 /// 2218 /// @return diff status. 2219 int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus) 2220 { 2221 buf_T *buf = wp->w_buffer; 2222 2223 if (linestatus != NULL) { 2224 *linestatus = 0; 2225 } 2226 2227 if (curtab->tp_diff_invalid) { 2228 // update after a big change 2229 ex_diffupdate(NULL); 2230 } 2231 2232 // no diffs at all 2233 if ((curtab->tp_first_diff == NULL) || !wp->w_p_diff) { 2234 return 0; 2235 } 2236 2237 // safety check: "lnum" must be a buffer line 2238 if ((lnum < 1) || (lnum > buf->b_ml.ml_line_count + 1)) { 2239 return 0; 2240 } 2241 2242 int idx = diff_buf_idx(buf, curtab); // index in tp_diffbuf[] for this buffer 2243 2244 if (idx == DB_COUNT) { 2245 // no diffs for buffer "buf" 2246 return 0; 2247 } 2248 2249 // A closed fold never has filler lines. 2250 if (hasFolding(wp, lnum, NULL, NULL) || decor_conceal_line(wp, lnum - 1, false)) { 2251 return 0; 2252 } 2253 2254 // search for a change that includes "lnum" in the list of diffblocks. 2255 diff_T *dp; 2256 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 2257 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { 2258 break; 2259 } 2260 } 2261 2262 if ((dp == NULL) || (lnum < dp->df_lnum[idx])) { 2263 return 0; 2264 } 2265 2266 // Don't run linematch when lnum is offscreen. 2267 // Useful for scrollbind calculations which need to count all the filler lines 2268 // above the screen. 2269 if (lnum >= wp->w_topline && lnum < wp->w_botline 2270 && !dp->is_linematched && diff_linematch(dp) 2271 && diff_check_sanity(curtab, dp)) { 2272 run_linematch_algorithm(dp); 2273 } 2274 2275 // Insert filler lines above the line just below the change. Will return 0 2276 // when this buf had the max count. 2277 int num_fill = 0; 2278 while (lnum == dp->df_lnum[idx] + dp->df_count[idx]) { 2279 // Only calculate fill lines if 'diffopt' contains "filler". Otherwise 2280 // returns 0 filler lines. 2281 if (diff_flags & DIFF_FILLER) { 2282 int maxcount = get_max_diff_length(dp); 2283 num_fill += maxcount - dp->df_count[idx]; 2284 } 2285 2286 // If there are adjacent blocks (e.g. linematch or anchor), loop 2287 // through them. It's possible for multiple adjacent blocks to 2288 // contribute to filler lines. 2289 // This also helps us find the last diff block in the list of adjacent 2290 // blocks which is necessary when it is a change/inserted line right 2291 // after added lines. 2292 if (dp->df_next != NULL 2293 && lnum >= dp->df_next->df_lnum[idx] 2294 && lnum <= dp->df_next->df_lnum[idx] + dp->df_next->df_count[idx]) { 2295 dp = dp->df_next; 2296 } else { 2297 break; 2298 } 2299 } 2300 2301 if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { 2302 bool zero = false; 2303 2304 // Changed or inserted line. If the other buffers have a count of 2305 // zero, the lines were inserted. If the other buffers have the same 2306 // count, check if the lines are identical. 2307 bool cmp = false; 2308 2309 for (int i = 0; i < DB_COUNT; i++) { 2310 if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { 2311 if (dp->df_count[i] == 0) { 2312 zero = true; 2313 } else { 2314 if (dp->df_count[i] != dp->df_count[idx]) { 2315 if (linestatus) { 2316 *linestatus = -1; // nr of lines changed. 2317 } 2318 return num_fill; 2319 } 2320 cmp = true; 2321 } 2322 } 2323 } 2324 2325 if (cmp) { 2326 // Compare all lines. If they are equal the lines were inserted 2327 // in some buffers, deleted in others, but not changed. 2328 for (int i = 0; i < DB_COUNT; i++) { 2329 if ((i != idx) 2330 && (curtab->tp_diffbuf[i] != NULL) 2331 && (dp->df_count[i] != 0)) { 2332 if (!diff_equal_entry(dp, idx, i)) { 2333 if (linestatus) { 2334 *linestatus = -1; 2335 } 2336 return num_fill; 2337 } 2338 } 2339 } 2340 } 2341 2342 // If there is no buffer with zero lines then there is no difference 2343 // any longer. Happens when making a change (or undo) that removes 2344 // the difference. Can't remove the entry here, we might be halfway 2345 // through updating the window. Just report the text as unchanged. 2346 // Other windows might still show the change though. 2347 if (!zero) { 2348 return num_fill; 2349 } 2350 if (linestatus) { 2351 *linestatus = -2; 2352 } 2353 return num_fill; 2354 } 2355 return num_fill; 2356 } 2357 2358 /// See diff_check_with_linestatus 2359 int diff_check_fill(win_T *wp, linenr_T lnum) 2360 { 2361 // be quick when there are no filler lines 2362 if (!(diff_flags & DIFF_FILLER)) { 2363 return 0; 2364 } 2365 int n = diff_check_with_linestatus(wp, lnum, NULL); 2366 return MAX(n, 0); 2367 } 2368 2369 /// Compare two entries in diff "dp" and return true if they are equal. 2370 /// 2371 /// @param dp diff 2372 /// @param idx1 first entry in diff "dp" 2373 /// @param idx2 second entry in diff "dp" 2374 /// 2375 /// @return true if two entries are equal. 2376 static bool diff_equal_entry(diff_T *dp, int idx1, int idx2) 2377 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) 2378 { 2379 if (dp->df_count[idx1] != dp->df_count[idx2]) { 2380 return false; 2381 } 2382 2383 if (diff_check_sanity(curtab, dp) == FAIL) { 2384 return false; 2385 } 2386 2387 for (int i = 0; i < dp->df_count[idx1]; i++) { 2388 char *line = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx1], dp->df_lnum[idx1] + i)); 2389 2390 int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], dp->df_lnum[idx2] + i)); 2391 xfree(line); 2392 2393 if (cmp != 0) { 2394 return false; 2395 } 2396 } 2397 return true; 2398 } 2399 2400 // Compare the characters at "p1" and "p2". If they are equal (possibly 2401 // ignoring case) return true and set "len" to the number of bytes. 2402 static bool diff_equal_char(const char *const p1, const char *const p2, int *const len) 2403 { 2404 const int l = utfc_ptr2len(p1); 2405 2406 if (l != utfc_ptr2len(p2)) { 2407 return false; 2408 } 2409 if (l > 1) { 2410 if (strncmp(p1, p2, (size_t)l) != 0 2411 && (!(diff_flags & DIFF_ICASE) 2412 || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) { 2413 return false; 2414 } 2415 *len = l; 2416 } else { 2417 if ((*p1 != *p2) 2418 && (!(diff_flags & DIFF_ICASE) 2419 || TOLOWER_LOC((uint8_t)(*p1)) != TOLOWER_LOC((uint8_t)(*p2)))) { 2420 return false; 2421 } 2422 *len = 1; 2423 } 2424 return true; 2425 } 2426 2427 /// Compare strings "s1" and "s2" according to 'diffopt'. 2428 /// Return non-zero when they are different. 2429 /// 2430 /// @param s1 The first string 2431 /// @param s2 The second string 2432 /// 2433 /// @return on-zero if the two strings are different. 2434 static int diff_cmp(char *s1, char *s2) 2435 { 2436 if ((diff_flags & DIFF_IBLANK) 2437 && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) { 2438 return 0; 2439 } 2440 2441 if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) { 2442 return strcmp(s1, s2); 2443 } 2444 2445 if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) { 2446 return mb_stricmp(s1, s2); 2447 } 2448 2449 char *p1 = s1; 2450 char *p2 = s2; 2451 2452 // Ignore white space changes and possibly ignore case. 2453 while (*p1 != NUL && *p2 != NUL) { 2454 if (((diff_flags & DIFF_IWHITE) 2455 && ascii_iswhite(*p1) && ascii_iswhite(*p2)) 2456 || ((diff_flags & DIFF_IWHITEALL) 2457 && (ascii_iswhite(*p1) || ascii_iswhite(*p2)))) { 2458 p1 = skipwhite(p1); 2459 p2 = skipwhite(p2); 2460 } else { 2461 int l; 2462 if (!diff_equal_char(p1, p2, &l)) { 2463 break; 2464 } 2465 p1 += l; 2466 p2 += l; 2467 } 2468 } 2469 2470 // Ignore trailing white space. 2471 p1 = skipwhite(p1); 2472 p2 = skipwhite(p2); 2473 2474 if ((*p1 != NUL) || (*p2 != NUL)) { 2475 return 1; 2476 } 2477 return 0; 2478 } 2479 2480 /// Set the topline of "towin" to match the position in "fromwin", so that they 2481 /// show the same diff'ed lines. 2482 /// 2483 /// @param fromwin 2484 /// @param towin 2485 void diff_set_topline(win_T *fromwin, win_T *towin) 2486 { 2487 buf_T *frombuf = fromwin->w_buffer; 2488 2489 int fromidx = diff_buf_idx(frombuf, curtab); 2490 if (fromidx == DB_COUNT) { 2491 // safety check 2492 return; 2493 } 2494 2495 if (curtab->tp_diff_invalid) { 2496 // update after a big change 2497 ex_diffupdate(NULL); 2498 } 2499 linenr_T lnum = fromwin->w_topline; 2500 towin->w_topfill = 0; 2501 2502 // search for a change that includes "lnum" in the list of diffblocks. 2503 diff_T *dp; 2504 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 2505 if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) { 2506 break; 2507 } 2508 } 2509 2510 if (dp == NULL) { 2511 // After last change, compute topline relative to end of file; no 2512 // filler lines. 2513 towin->w_topline = towin->w_buffer->b_ml.ml_line_count 2514 - (frombuf->b_ml.ml_line_count - lnum); 2515 } else { 2516 // Find index for "towin". 2517 int toidx = diff_buf_idx(towin->w_buffer, curtab); 2518 2519 if (toidx == DB_COUNT) { 2520 // safety check 2521 return; 2522 } 2523 towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]); 2524 2525 if (lnum >= dp->df_lnum[fromidx]) { 2526 calculate_topfill_and_topline(fromidx, toidx, 2527 fromwin->w_topline, fromwin->w_topfill, 2528 &towin->w_topfill, &towin->w_topline); 2529 } 2530 } 2531 2532 // safety check (if diff info gets outdated strange things may happen) 2533 towin->w_botfill = false; 2534 2535 if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) { 2536 towin->w_topline = towin->w_buffer->b_ml.ml_line_count; 2537 towin->w_botfill = true; 2538 } 2539 2540 if (towin->w_topline < 1) { 2541 towin->w_topline = 1; 2542 towin->w_topfill = 0; 2543 } 2544 2545 // When w_topline changes need to recompute w_botline and cursor position 2546 invalidate_botline_win(towin); 2547 changed_line_abv_curs_win(towin); 2548 2549 check_topfill(towin, false); 2550 hasFolding(towin, towin->w_topline, &towin->w_topline, NULL); 2551 } 2552 2553 /// Parse the diff anchors. If "check_only" is set, will only make sure the 2554 /// syntax is correct. 2555 static int parse_diffanchors(bool check_only, buf_T *buf, linenr_T *anchors, int *num_anchors) 2556 { 2557 int i; 2558 char *dia = (*buf->b_p_dia == NUL) ? p_dia : buf->b_p_dia; 2559 2560 buf_T *orig_curbuf = curbuf; 2561 win_T *orig_curwin = curwin; 2562 2563 win_T *bufwin = NULL; 2564 if (check_only) { 2565 bufwin = curwin; 2566 } else { 2567 // Find the first window tied to this buffer and ignore the rest. Will 2568 // only matter for window-specific addresses like `.` or `''`. 2569 for (bufwin = firstwin; bufwin != NULL; bufwin = bufwin->w_next) { 2570 if (bufwin->w_buffer == buf && bufwin->w_p_diff) { 2571 break; 2572 } 2573 } 2574 if (bufwin == NULL && *dia != NUL) { 2575 // The buffer is hidden. Currently this is not supported due to the 2576 // edge cases of needing to decide if an address is window-specific 2577 // or not. We could add more checks in the future so we can detect 2578 // whether an address relies on curwin to make this more fleixble. 2579 emsg(_(e_diff_anchors_with_hidden_windows)); 2580 return FAIL; 2581 } 2582 } 2583 2584 for (i = 0; i < MAX_DIFF_ANCHORS && *dia != NUL; i++) { 2585 if (*dia == ',') { // don't allow empty values 2586 return FAIL; 2587 } 2588 2589 curbuf = buf; 2590 curwin = bufwin; 2591 const char *errormsg = NULL; 2592 linenr_T lnum = get_address(NULL, &dia, ADDR_LINES, check_only, true, false, 1, &errormsg); 2593 curbuf = orig_curbuf; 2594 curwin = orig_curwin; 2595 2596 if (errormsg != NULL) { 2597 emsg(errormsg); 2598 } 2599 if (dia == NULL) { // error detected 2600 return FAIL; 2601 } 2602 if (*dia != ',' && *dia != NUL) { 2603 return FAIL; 2604 } 2605 2606 if (!check_only 2607 && (lnum == MAXLNUM || lnum <= 0 || lnum > buf->b_ml.ml_line_count + 1)) { 2608 emsg(_(e_invrange)); 2609 return FAIL; 2610 } 2611 2612 if (anchors != NULL) { 2613 anchors[i] = lnum; 2614 } 2615 2616 if (*dia == ',') { 2617 dia++; 2618 } 2619 } 2620 if (i == MAX_DIFF_ANCHORS && *dia != NUL) { 2621 semsg(_(e_cannot_have_more_than_nr_diff_anchors), MAX_DIFF_ANCHORS); 2622 return FAIL; 2623 } 2624 if (num_anchors != NULL) { 2625 *num_anchors = i; 2626 } 2627 return OK; 2628 } 2629 2630 /// This is called when 'diffanchors' is changed. 2631 int diffanchors_changed(bool buflocal) 2632 { 2633 int result = parse_diffanchors(true, curbuf, NULL, NULL); 2634 if (result == OK && (diff_flags & DIFF_ANCHOR)) { 2635 FOR_ALL_TABS(tp) { 2636 if (!buflocal) { 2637 tp->tp_diff_invalid = true; 2638 } else { 2639 for (int idx = 0; idx < DB_COUNT; idx++) { 2640 if (tp->tp_diffbuf[idx] == curbuf) { 2641 tp->tp_diff_invalid = true; 2642 break; 2643 } 2644 } 2645 } 2646 } 2647 } 2648 return result; 2649 } 2650 2651 /// This is called when 'diffopt' is changed. 2652 /// 2653 /// @return 2654 int diffopt_changed(void) 2655 { 2656 int diff_context_new = 6; 2657 int linematch_lines_new = 0; 2658 int diff_flags_new = 0; 2659 int diff_foldcolumn_new = 2; 2660 int diff_algorithm_new = 0; 2661 int diff_indent_heuristic = 0; 2662 2663 char *p = p_dip; 2664 while (*p != NUL) { 2665 // Note: Keep this in sync with opt_dip_values. 2666 if (strncmp(p, "filler", 6) == 0) { 2667 p += 6; 2668 diff_flags_new |= DIFF_FILLER; 2669 } else if (strncmp(p, "anchor", 6) == 0) { 2670 p += 6; 2671 diff_flags_new |= DIFF_ANCHOR; 2672 } else if ((strncmp(p, "context:", 8) == 0) && ascii_isdigit(p[8])) { 2673 p += 8; 2674 diff_context_new = getdigits_int(&p, false, diff_context_new); 2675 } else if (strncmp(p, "iblank", 6) == 0) { 2676 p += 6; 2677 diff_flags_new |= DIFF_IBLANK; 2678 } else if (strncmp(p, "icase", 5) == 0) { 2679 p += 5; 2680 diff_flags_new |= DIFF_ICASE; 2681 } else if (strncmp(p, "iwhiteall", 9) == 0) { 2682 p += 9; 2683 diff_flags_new |= DIFF_IWHITEALL; 2684 } else if (strncmp(p, "iwhiteeol", 9) == 0) { 2685 p += 9; 2686 diff_flags_new |= DIFF_IWHITEEOL; 2687 } else if (strncmp(p, "iwhite", 6) == 0) { 2688 p += 6; 2689 diff_flags_new |= DIFF_IWHITE; 2690 } else if (strncmp(p, "horizontal", 10) == 0) { 2691 p += 10; 2692 diff_flags_new |= DIFF_HORIZONTAL; 2693 } else if (strncmp(p, "vertical", 8) == 0) { 2694 p += 8; 2695 diff_flags_new |= DIFF_VERTICAL; 2696 } else if ((strncmp(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) { 2697 p += 11; 2698 diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new); 2699 } else if (strncmp(p, "hiddenoff", 9) == 0) { 2700 p += 9; 2701 diff_flags_new |= DIFF_HIDDEN_OFF; 2702 } else if (strncmp(p, "closeoff", 8) == 0) { 2703 p += 8; 2704 diff_flags_new |= DIFF_CLOSE_OFF; 2705 } else if (strncmp(p, "followwrap", 10) == 0) { 2706 p += 10; 2707 diff_flags_new |= DIFF_FOLLOWWRAP; 2708 } else if (strncmp(p, "indent-heuristic", 16) == 0) { 2709 p += 16; 2710 diff_indent_heuristic = XDF_INDENT_HEURISTIC; 2711 } else if (strncmp(p, "internal", 8) == 0) { 2712 p += 8; 2713 diff_flags_new |= DIFF_INTERNAL; 2714 } else if (strncmp(p, "algorithm:", 10) == 0) { 2715 // Note: Keep this in sync with opt_dip_algorithm_values. 2716 p += 10; 2717 if (strncmp(p, "myers", 5) == 0) { 2718 p += 5; 2719 diff_algorithm_new = 0; 2720 } else if (strncmp(p, "minimal", 7) == 0) { 2721 p += 7; 2722 diff_algorithm_new = XDF_NEED_MINIMAL; 2723 } else if (strncmp(p, "patience", 8) == 0) { 2724 p += 8; 2725 diff_algorithm_new = XDF_PATIENCE_DIFF; 2726 } else if (strncmp(p, "histogram", 9) == 0) { 2727 p += 9; 2728 diff_algorithm_new = XDF_HISTOGRAM_DIFF; 2729 } else { 2730 return FAIL; 2731 } 2732 } else if (strncmp(p, "inline:", 7) == 0) { 2733 // Note: Keep this in sync with opt_dip_inline_values. 2734 p += 7; 2735 if (strncmp(p, "none", 4) == 0) { 2736 p += 4; 2737 diff_flags_new &= ~(ALL_INLINE); 2738 diff_flags_new |= DIFF_INLINE_NONE; 2739 } else if (strncmp(p, "simple", 6) == 0) { 2740 p += 6; 2741 diff_flags_new &= ~(ALL_INLINE); 2742 diff_flags_new |= DIFF_INLINE_SIMPLE; 2743 } else if (strncmp(p, "char", 4) == 0) { 2744 p += 4; 2745 diff_flags_new &= ~(ALL_INLINE); 2746 diff_flags_new |= DIFF_INLINE_CHAR; 2747 } else if (strncmp(p, "word", 4) == 0) { 2748 p += 4; 2749 diff_flags_new &= ~(ALL_INLINE); 2750 diff_flags_new |= DIFF_INLINE_WORD; 2751 } else { 2752 return FAIL; 2753 } 2754 } else if ((strncmp(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) { 2755 p += 10; 2756 linematch_lines_new = getdigits_int(&p, false, linematch_lines_new); 2757 diff_flags_new |= DIFF_LINEMATCH; 2758 2759 // linematch does not make sense without filler set 2760 diff_flags_new |= DIFF_FILLER; 2761 } 2762 2763 if ((*p != ',') && (*p != NUL)) { 2764 return FAIL; 2765 } 2766 2767 if (*p == ',') { 2768 p++; 2769 } 2770 } 2771 2772 diff_algorithm_new |= diff_indent_heuristic; 2773 2774 // Can't have both "horizontal" and "vertical". 2775 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) { 2776 return FAIL; 2777 } 2778 2779 // If flags were added or removed, or the algorithm was changed, need to 2780 // update the diff. 2781 if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) { 2782 FOR_ALL_TABS(tp) { 2783 tp->tp_diff_invalid = true; 2784 } 2785 } 2786 2787 diff_flags = diff_flags_new; 2788 diff_context = diff_context_new == 0 ? 1 : diff_context_new; 2789 linematch_lines = linematch_lines_new; 2790 diff_foldcolumn = diff_foldcolumn_new; 2791 diff_algorithm = diff_algorithm_new; 2792 2793 diff_redraw(true); 2794 2795 // recompute the scroll binding with the new option value, may 2796 // remove or add filler lines 2797 check_scrollbind(0, 0); 2798 return OK; 2799 } 2800 2801 /// Check that "diffopt" contains "horizontal". 2802 bool diffopt_horizontal(void) 2803 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 2804 { 2805 return (diff_flags & DIFF_HORIZONTAL) != 0; 2806 } 2807 2808 // Return true if 'diffopt' contains "hiddenoff". 2809 bool diffopt_hiddenoff(void) 2810 FUNC_ATTR_PURE 2811 { 2812 return (diff_flags & DIFF_HIDDEN_OFF) != 0; 2813 } 2814 2815 // Return true if 'diffopt' contains "closeoff". 2816 bool diffopt_closeoff(void) 2817 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 2818 { 2819 return (diff_flags & DIFF_CLOSE_OFF) != 0; 2820 } 2821 2822 // Return true if 'diffopt' contains "filler". 2823 bool diffopt_filler(void) 2824 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 2825 { 2826 return (diff_flags & DIFF_FILLER) != 0; 2827 } 2828 2829 /// Called when a line has been updated. Used for updating inline diff in Insert 2830 /// mode without waiting for global diff update later. 2831 void diff_update_line(linenr_T lnum) 2832 { 2833 if (!(diff_flags & ALL_INLINE_DIFF)) { 2834 // We only care if we are doing inline-diff where we cache the diff results 2835 return; 2836 } 2837 2838 int idx = diff_buf_idx(curbuf, curtab); 2839 if (idx == DB_COUNT) { 2840 return; 2841 } 2842 diff_T *dp; 2843 FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) { 2844 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { 2845 break; 2846 } 2847 } 2848 2849 // clear the inline change cache as it's invalid 2850 if (dp != NULL) { 2851 dp->has_changes = false; 2852 dp->df_changes.ga_len = 0; 2853 } 2854 } 2855 2856 /// used for simple inline diff algorithm 2857 static diffline_change_T simple_diffline_change; 2858 2859 /// Parse a diffline struct and returns the [start,end] byte offsets 2860 /// 2861 /// Returns true if this change was added, no other buffer has it. 2862 bool diff_change_parse(diffline_T *diffline, diffline_change_T *change, int *change_start, 2863 int *change_end) 2864 { 2865 if (change->dc_start_lnum_off[diffline->bufidx] < diffline->lineoff) { 2866 *change_start = 0; 2867 } else { 2868 *change_start = change->dc_start[diffline->bufidx]; 2869 } 2870 if (change->dc_end_lnum_off[diffline->bufidx] > diffline->lineoff) { 2871 *change_end = INT_MAX; 2872 } else { 2873 *change_end = change->dc_end[diffline->bufidx]; 2874 } 2875 2876 if (change == &simple_diffline_change) { 2877 // This is what we returned from simple inline diff. We always consider 2878 // the range to be changed, rather than added for now. 2879 return false; 2880 } 2881 2882 // Find out whether this is an addition. Note that for multi buffer diff, 2883 // to tell whether lines are additions we check whether all the other diff 2884 // lines are identical (in diff_check_with_linestatus). If so, we mark them 2885 // as add. We don't do that for inline diff here for simplicity. 2886 for (int i = 0; i < DB_COUNT; i++) { 2887 if (i == diffline->bufidx) { 2888 continue; 2889 } 2890 if (change->dc_start[i] != change->dc_end[i] 2891 || change->dc_end_lnum_off[i] != change->dc_start_lnum_off[i]) { 2892 return false; 2893 } 2894 } 2895 return true; 2896 } 2897 2898 /// Find the difference within a changed line and returns [startp,endp] byte 2899 /// positions. Performs a simple algorithm by finding a single range in the 2900 /// middle. 2901 /// 2902 /// If diffopt has DIFF_INLINE_NONE set, then this will only calculate the return 2903 /// value (added or changed), but startp/endp will not be calculated. 2904 /// 2905 /// @param wp window whose current buffer to check 2906 /// @param lnum line number to check within the buffer 2907 /// @param startp first char of the change 2908 /// @param endp last char of the change 2909 /// 2910 /// @return true if the line was added, no other buffer has it. 2911 static bool diff_find_change_simple(win_T *wp, linenr_T lnum, const diff_T *dp, int idx, 2912 int *startp, int *endp) 2913 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL 2914 { 2915 char *line_org; 2916 if (diff_flags & DIFF_INLINE_NONE) { 2917 // We only care about the return value, not the actual string comparisons. 2918 line_org = NULL; 2919 } else { 2920 // Make a copy of the line, the next ml_get() will invalidate it. 2921 line_org = xstrdup(ml_get_buf(wp->w_buffer, lnum)); 2922 } 2923 2924 int si_org; 2925 int si_new; 2926 int ei_org; 2927 int ei_new; 2928 bool added = true; 2929 2930 linenr_T off = lnum - dp->df_lnum[idx]; 2931 for (int i = 0; i < DB_COUNT; i++) { 2932 if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) { 2933 // Skip lines that are not in the other change (filler lines). 2934 if (off >= dp->df_count[i]) { 2935 continue; 2936 } 2937 added = false; 2938 if (diff_flags & DIFF_INLINE_NONE) { 2939 break; // early terminate as we only care about the return value 2940 } 2941 2942 char *line_new = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off); 2943 2944 // Search for start of difference 2945 si_org = si_new = 0; 2946 2947 while (line_org[si_org] != NUL) { 2948 if (((diff_flags & DIFF_IWHITE) 2949 && ascii_iswhite(line_org[si_org]) 2950 && ascii_iswhite(line_new[si_new])) 2951 || ((diff_flags & DIFF_IWHITEALL) 2952 && (ascii_iswhite(line_org[si_org]) 2953 || ascii_iswhite(line_new[si_new])))) { 2954 si_org = (int)(skipwhite(line_org + si_org) - line_org); 2955 si_new = (int)(skipwhite(line_new + si_new) - line_new); 2956 } else { 2957 int l; 2958 if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) { 2959 break; 2960 } 2961 si_org += l; 2962 si_new += l; 2963 } 2964 } 2965 2966 // Move back to first byte of character in both lines (may 2967 // have "nn^" in line_org and "n^ in line_new). 2968 si_org -= utf_head_off(line_org, line_org + si_org); 2969 si_new -= utf_head_off(line_new, line_new + si_new); 2970 2971 *startp = MIN(*startp, si_org); 2972 2973 // Search for end of difference, if any. 2974 if ((line_org[si_org] != NUL) || (line_new[si_new] != NUL)) { 2975 ei_org = (int)strlen(line_org); 2976 ei_new = (int)strlen(line_new); 2977 2978 while (ei_org >= *startp 2979 && ei_new >= si_new 2980 && ei_org >= 0 2981 && ei_new >= 0) { 2982 if (((diff_flags & DIFF_IWHITE) 2983 && ascii_iswhite(line_org[ei_org]) 2984 && ascii_iswhite(line_new[ei_new])) 2985 || ((diff_flags & DIFF_IWHITEALL) 2986 && (ascii_iswhite(line_org[ei_org]) 2987 || ascii_iswhite(line_new[ei_new])))) { 2988 while (ei_org >= *startp && ascii_iswhite(line_org[ei_org])) { 2989 ei_org--; 2990 } 2991 2992 while (ei_new >= si_new && ascii_iswhite(line_new[ei_new])) { 2993 ei_new--; 2994 } 2995 } else { 2996 const char *p1 = line_org + ei_org; 2997 const char *p2 = line_new + ei_new; 2998 2999 p1 -= utf_head_off(line_org, p1); 3000 p2 -= utf_head_off(line_new, p2); 3001 3002 int l; 3003 if (!diff_equal_char(p1, p2, &l)) { 3004 break; 3005 } 3006 ei_org -= l; 3007 ei_new -= l; 3008 } 3009 } 3010 3011 *endp = MAX(*endp, ei_org); 3012 } 3013 } 3014 } 3015 3016 xfree(line_org); 3017 return added; 3018 } 3019 3020 /// Mapping used for mapping from temporary mmfile created for inline diff back 3021 /// to original buffer's line/col. 3022 typedef struct { 3023 colnr_T byte_start; 3024 colnr_T num_bytes; 3025 int lineoff; 3026 } linemap_entry_T; 3027 3028 /// Refine inline character-wise diff blocks to create a more human readable 3029 /// highlight. Otherwise a naive diff under existing algorithms tends to create 3030 /// a messy output with lots of small gaps. 3031 /// It does this by merging adjacent long diff blocks if they are only separated 3032 /// by a couple characters. 3033 /// These are done by heuristics and can be further tuned. 3034 static void diff_refine_inline_char_highlight(diff_T *dp_orig, garray_T *linemap, int idx1) 3035 { 3036 // Perform multiple passes so that newly merged blocks will now be long 3037 // enough which may cause other previously unmerged gaps to be merged as 3038 // well. 3039 int pass = 1; 3040 do { 3041 bool has_unmerged_gaps = false; 3042 bool has_merged_gaps = false; 3043 diff_T *dp = dp_orig; 3044 while (dp != NULL && dp->df_next != NULL) { 3045 // Only use first buffer to calculate the gap because the gap is 3046 // unchanged text, which would be the same in all buffers. 3047 if (dp->df_lnum[idx1] + dp->df_count[idx1] - 1 >= linemap[idx1].ga_len 3048 || dp->df_next->df_lnum[idx1] - 1 >= linemap[idx1].ga_len) { 3049 dp = dp->df_next; 3050 continue; 3051 } 3052 3053 // If the gap occurs over different lines, don't consider it 3054 linemap_entry_T *entry1 = 3055 &((linemap_entry_T *)linemap[idx1].ga_data)[dp->df_lnum[idx1] 3056 + dp->df_count[idx1] - 1]; 3057 linemap_entry_T *entry2 = 3058 &((linemap_entry_T *)linemap[idx1].ga_data)[dp->df_next->df_lnum[idx1] - 1]; 3059 if (entry1->lineoff != entry2->lineoff) { 3060 dp = dp->df_next; 3061 continue; 3062 } 3063 3064 linenr_T gap = dp->df_next->df_lnum[idx1] - (dp->df_lnum[idx1] + dp->df_count[idx1]); 3065 if (gap <= 3) { 3066 linenr_T max_df_count = 0; 3067 for (int i = 0; i < DB_COUNT; i++) { 3068 max_df_count = MAX(max_df_count, dp->df_count[i] + dp->df_next->df_count[i]); 3069 } 3070 3071 if (max_df_count >= gap * 4) { 3072 // Merge current block with the next one. Don't advance the 3073 // pointer so we try the same merged block against the next 3074 // one. 3075 for (int i = 0; i < DB_COUNT; i++) { 3076 dp->df_count[i] = dp->df_next->df_lnum[i] 3077 + dp->df_next->df_count[i] - dp->df_lnum[i]; 3078 } 3079 diff_T *dp_next = dp->df_next; 3080 dp->df_next = dp_next->df_next; 3081 clear_diffblock(dp_next); 3082 has_merged_gaps = true; 3083 continue; 3084 } else { 3085 has_unmerged_gaps = true; 3086 } 3087 } 3088 dp = dp->df_next; 3089 } 3090 if (!has_unmerged_gaps || !has_merged_gaps) { 3091 break; 3092 } 3093 } while (pass++ < 4); // use limited number of passes to avoid excessive looping 3094 } 3095 3096 /// Find the inline difference within a diff block among different buffers. Do 3097 /// this by splitting each block's content into characters or words, and then 3098 /// use internal xdiff to calculate the per-character/word diff. The result is 3099 /// stored in dp instead of returned by the function. 3100 static void diff_find_change_inline_diff(diff_T *dp) 3101 { 3102 const int save_diff_algorithm = diff_algorithm; 3103 3104 diffio_T dio = { 0 }; 3105 ga_init(&dio.dio_diff.dout_ga, sizeof(diffhunk_T), 1000); 3106 3107 // inline diff only supports internal algo 3108 dio.dio_internal = true; 3109 3110 // always use indent-heuristics to slide diff splits along 3111 // whitespace 3112 diff_algorithm |= XDF_INDENT_HEURISTIC; 3113 3114 // diff_read() has an implicit dependency on curtab->tp_first_diff 3115 diff_T *orig_diff = curtab->tp_first_diff; 3116 curtab->tp_first_diff = NULL; 3117 3118 // diff_read() also uses curtab->tp_diffbuf to determine what's an active 3119 // buffer 3120 buf_T *(orig_diffbuf[DB_COUNT]); 3121 memcpy(orig_diffbuf, curtab->tp_diffbuf, sizeof(orig_diffbuf)); 3122 3123 garray_T linemap[DB_COUNT]; 3124 garray_T file1_str; 3125 garray_T file2_str; 3126 3127 // Buffers to populate mmfile 1/2 that would be passed to xdiff as memory 3128 // files. Use a grow array as it is not obvious how much exact space we 3129 // need. 3130 ga_init(&file1_str, 1, 1024); 3131 ga_init(&file2_str, 1, 1024); 3132 3133 // Line map to map from generated mmfiles' line numbers back to original 3134 // diff blocks' locations. Need this even for char diff because not all 3135 // characters are 1-byte long / ASCII. 3136 for (int i = 0; i < DB_COUNT; i++) { 3137 ga_init(&linemap[i], sizeof(linemap_entry_T), 128); 3138 } 3139 3140 int file1_idx = -1; 3141 for (int i = 0; i < DB_COUNT; i++) { 3142 dio.dio_diff.dout_ga.ga_len = 0; 3143 3144 buf_T *buf = curtab->tp_diffbuf[i]; 3145 if (buf == NULL || buf->b_ml.ml_mfp == NULL) { 3146 continue; // skip buffer that isn't loaded 3147 } 3148 if (dp->df_count[i] == 0) { 3149 // skip buffers that don't have any texts in this block so we don't 3150 // end up marking the entire block as modified in multi-buffer diff 3151 curtab->tp_diffbuf[i] = NULL; 3152 continue; 3153 } 3154 3155 if (file1_idx == -1) { 3156 file1_idx = i; 3157 } 3158 3159 garray_T *curstr = (file1_idx != i) ? &file2_str : &file1_str; 3160 3161 linenr_T numlines = 0; 3162 curstr->ga_len = 0; 3163 3164 // Split each line into chars/words and populate fake file buffer as 3165 // newline-delimited tokens as that's what xdiff requires. 3166 for (int off = 0; off < dp->df_count[i]; off++) { 3167 char *curline = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off); 3168 3169 bool in_keyword = false; 3170 3171 // iwhiteeol support vars 3172 bool last_white = false; 3173 int eol_ga_len = -1; 3174 int eol_linemap_len = -1; 3175 int eol_numlines = -1; 3176 3177 char *s = curline; 3178 while (*s != NUL) { 3179 bool new_in_keyword = false; 3180 if (diff_flags & DIFF_INLINE_WORD) { 3181 // Always use the first buffer's 'iskeyword' to have a 3182 // consistent diff. 3183 // For multibyte chars, only treat alphanumeric chars 3184 // (class 2) as "word", as other classes such as emojis and 3185 // CJK ideographs do not usually benefit from word diff as 3186 // Vim doesn't have a good way to segment them. 3187 new_in_keyword = (mb_get_class_tab(s, curtab->tp_diffbuf[file1_idx]->b_chartab) == 2); 3188 } 3189 if (in_keyword && !new_in_keyword) { 3190 ga_append(curstr, NL); 3191 numlines++; 3192 } 3193 3194 if (ascii_iswhite(*s)) { 3195 if (diff_flags & DIFF_IWHITEALL) { 3196 in_keyword = false; 3197 s = skipwhite(s); 3198 continue; 3199 } else if ((diff_flags & DIFF_IWHITEEOL) || (diff_flags & DIFF_IWHITE)) { 3200 if (!last_white) { 3201 eol_ga_len = curstr->ga_len; 3202 eol_linemap_len = linemap[i].ga_len; 3203 eol_numlines = numlines; 3204 last_white = true; 3205 } 3206 } 3207 } else { 3208 if ((diff_flags & DIFF_IWHITEEOL) || (diff_flags & DIFF_IWHITE)) { 3209 last_white = false; 3210 eol_ga_len = -1; 3211 eol_linemap_len = -1; 3212 eol_numlines = -1; 3213 } 3214 } 3215 3216 int char_len = 1; 3217 if (*s == NL) { 3218 // NL is internal substitute for NUL 3219 ga_append(curstr, NUL); 3220 } else { 3221 char_len = utfc_ptr2len(s); 3222 3223 if (ascii_iswhite(*s) && (diff_flags & DIFF_IWHITE)) { 3224 // Treat the entire white space span as a single char. 3225 char_len = (int)(skipwhite(s) - s); 3226 } 3227 3228 if (diff_flags & DIFF_ICASE) { 3229 // xdiff doesn't support ignoring case, fold-case the text manually. 3230 int c = utf_ptr2char(s); 3231 int c_len = utf_char2len(c); 3232 c = utf_fold(c); 3233 char cbuf[MB_MAXBYTES + 1]; 3234 int c_fold_len = utf_char2bytes(c, cbuf); 3235 ga_concat_len(curstr, cbuf, (size_t)c_fold_len); 3236 if (char_len > c_len) { 3237 // There may be remaining composing characters. Write those back in. 3238 // Composing characters don't need case folding. 3239 ga_concat_len(curstr, s + c_len, (size_t)(char_len - c_len)); 3240 } 3241 } else { 3242 ga_concat_len(curstr, s, (size_t)char_len); 3243 } 3244 } 3245 3246 if (!new_in_keyword) { 3247 ga_append(curstr, NL); 3248 numlines++; 3249 } 3250 3251 if (!new_in_keyword || (new_in_keyword && !in_keyword)) { 3252 // create a new mapping entry from the xdiff mmfile back to 3253 // original line/col. 3254 linemap_entry_T linemap_entry = { 3255 .lineoff = off, 3256 .byte_start = (colnr_T)(s - curline), 3257 .num_bytes = char_len, 3258 }; 3259 GA_APPEND(linemap_entry_T, &linemap[i], linemap_entry); 3260 } else { 3261 // Still inside a keyword. Just increment byte count but 3262 // don't make a new entry. 3263 // linemap always has at least one entry here 3264 ((linemap_entry_T *)linemap[i].ga_data)[linemap[i].ga_len - 1].num_bytes += char_len; 3265 } 3266 3267 in_keyword = new_in_keyword; 3268 s += char_len; 3269 } 3270 if (in_keyword) { 3271 ga_append(curstr, NL); 3272 numlines++; 3273 } 3274 3275 if ((diff_flags & DIFF_IWHITEEOL) || (diff_flags & DIFF_IWHITE)) { 3276 // Need to trim trailing whitespace. Do this simply by 3277 // resetting arrays back to before we encountered them. 3278 if (eol_ga_len != -1) { 3279 curstr->ga_len = eol_ga_len; 3280 linemap[i].ga_len = eol_linemap_len; 3281 numlines = eol_numlines; 3282 } 3283 } 3284 3285 if (!(diff_flags & DIFF_IWHITEALL)) { 3286 // Add an empty line token mapped to the end-of-line in the 3287 // original file. This helps diff newline differences among 3288 // files, which will be visualized when using 'list' as the eol 3289 // listchar will be highlighted. 3290 ga_append(curstr, NL); 3291 numlines++; 3292 3293 linemap_entry_T linemap_entry = { 3294 .lineoff = off, 3295 .byte_start = (colnr_T)(s - curline), 3296 .num_bytes = sizeof(NL), 3297 }; 3298 GA_APPEND(linemap_entry_T, &linemap[i], linemap_entry); 3299 } 3300 } 3301 3302 if (file1_idx != i) { 3303 dio.dio_new.din_mmfile.ptr = (char *)curstr->ga_data; 3304 dio.dio_new.din_mmfile.size = curstr->ga_len; 3305 } else { 3306 dio.dio_orig.din_mmfile.ptr = (char *)curstr->ga_data; 3307 dio.dio_orig.din_mmfile.size = curstr->ga_len; 3308 } 3309 if (file1_idx != i) { 3310 // Perform diff with first file and read the results 3311 int diff_status = diff_file_internal(&dio); 3312 if (diff_status == FAIL) { 3313 goto done; 3314 } 3315 3316 diff_read(0, i, &dio); 3317 clear_diffout(&dio.dio_diff); 3318 } 3319 } 3320 diff_T *new_diff = curtab->tp_first_diff; 3321 3322 if (diff_flags & DIFF_INLINE_CHAR && file1_idx != -1) { 3323 diff_refine_inline_char_highlight(new_diff, linemap, file1_idx); 3324 } 3325 3326 // After the diff, use the linemap to obtain the original line/col of the 3327 // changes and cache them in dp. 3328 dp->df_changes.ga_len = 0; // this should already be zero 3329 for (; new_diff != NULL; new_diff = new_diff->df_next) { 3330 diffline_change_T change = { 0 }; 3331 for (int i = 0; i < DB_COUNT; i++) { 3332 if (new_diff->df_lnum[i] <= 0) { // should never be < 0. Checking just for safety 3333 continue; 3334 } 3335 linenr_T diff_lnum = new_diff->df_lnum[i] - 1; // use zero-index 3336 linenr_T diff_lnum_end = diff_lnum + new_diff->df_count[i]; 3337 3338 if (diff_lnum >= linemap[i].ga_len) { 3339 change.dc_start[i] = MAXCOL; 3340 change.dc_start_lnum_off[i] = INT_MAX; 3341 } else { 3342 change.dc_start[i] = ((linemap_entry_T *)linemap[i].ga_data)[diff_lnum].byte_start; 3343 change.dc_start_lnum_off[i] = ((linemap_entry_T *)linemap[i].ga_data)[diff_lnum].lineoff; 3344 } 3345 3346 if (diff_lnum == diff_lnum_end) { 3347 change.dc_end[i] = change.dc_start[i]; 3348 change.dc_end_lnum_off[i] = change.dc_start_lnum_off[i]; 3349 } else if (diff_lnum_end - 1 >= linemap[i].ga_len) { 3350 change.dc_end[i] = MAXCOL; 3351 change.dc_end_lnum_off[i] = INT_MAX; 3352 } else { 3353 change.dc_end[i] = ((linemap_entry_T *)linemap[i].ga_data)[diff_lnum_end - 1].byte_start + 3354 ((linemap_entry_T *)linemap[i].ga_data)[diff_lnum_end - 1].num_bytes; 3355 change.dc_end_lnum_off[i] = ((linemap_entry_T *)linemap[i].ga_data)[diff_lnum_end - 3356 1].lineoff; 3357 } 3358 } 3359 GA_APPEND(diffline_change_T, &dp->df_changes, change); 3360 } 3361 3362 done: 3363 diff_algorithm = save_diff_algorithm; 3364 3365 dp->has_changes = true; 3366 3367 diff_clear(curtab); 3368 curtab->tp_first_diff = orig_diff; 3369 memcpy(curtab->tp_diffbuf, orig_diffbuf, sizeof(orig_diffbuf)); 3370 3371 ga_clear(&file1_str); 3372 ga_clear(&file2_str); 3373 // No need to clear dio.dio_orig/dio_new because they were referencing 3374 // strings that are now cleared. 3375 clear_diffout(&dio.dio_diff); 3376 for (int i = 0; i < DB_COUNT; i++) { 3377 ga_clear(&linemap[i]); 3378 } 3379 } 3380 3381 /// Find the difference within a changed line. 3382 /// Returns true if the line was added, no other buffer has it. 3383 bool diff_find_change(win_T *wp, linenr_T lnum, diffline_T *diffline) 3384 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL 3385 { 3386 int idx = diff_buf_idx(wp->w_buffer, curtab); 3387 if (idx == DB_COUNT) { // cannot happen 3388 return false; 3389 } 3390 3391 // search for a change that includes "lnum" in the list of diffblocks. 3392 diff_T *dp; 3393 FOR_ALL_DIFFBLOCKS_IN_TAB(curtab, dp) { 3394 if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { 3395 break; 3396 } 3397 } 3398 if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL) { 3399 return false; 3400 } 3401 3402 int off = lnum - dp->df_lnum[idx]; 3403 3404 if (!(diff_flags & ALL_INLINE_DIFF)) { 3405 // Use simple algorithm 3406 int change_start = MAXCOL; // first col of changed area 3407 int change_end = -1; // last col of changed area 3408 3409 int ret = diff_find_change_simple(wp, lnum, dp, idx, &change_start, &change_end); 3410 3411 // convert from inclusive end to exclusive end per diffline's contract 3412 change_end += 1; 3413 3414 // Create a mock diffline struct. We always only have one so no need to 3415 // allocate memory. 3416 CLEAR_FIELD(simple_diffline_change); 3417 diffline->changes = &simple_diffline_change; 3418 diffline->num_changes = 1; 3419 diffline->bufidx = idx; 3420 diffline->lineoff = lnum - dp->df_lnum[idx]; 3421 3422 simple_diffline_change.dc_start[idx] = change_start; 3423 simple_diffline_change.dc_end[idx] = change_end; 3424 simple_diffline_change.dc_start_lnum_off[idx] = off; 3425 simple_diffline_change.dc_end_lnum_off[idx] = off; 3426 return ret; 3427 } 3428 3429 // Use inline diff algorithm. 3430 // The diff changes are usually cached so we check that first. 3431 if (!dp->has_changes) { 3432 diff_find_change_inline_diff(dp); 3433 } 3434 3435 garray_T *changes = &dp->df_changes; 3436 3437 // Use linear search to find the first change for this line. We could 3438 // optimize this to use binary search, but there should usually be a 3439 // limited number of inline changes per diff block, and limited number of 3440 // diff blocks shown on screen, so it is not necessary. 3441 int num_changes = 0; 3442 int change_idx = 0; 3443 diffline->changes = NULL; 3444 for (change_idx = 0; change_idx < changes->ga_len; change_idx++) { 3445 diffline_change_T *change = 3446 &((diffline_change_T *)dp->df_changes.ga_data)[change_idx]; 3447 if (change->dc_end_lnum_off[idx] < off) { 3448 continue; 3449 } 3450 if (change->dc_start_lnum_off[idx] > off) { 3451 break; 3452 } 3453 if (diffline->changes == NULL) { 3454 diffline->changes = change; 3455 } 3456 num_changes++; 3457 } 3458 diffline->num_changes = num_changes; 3459 diffline->bufidx = idx; 3460 diffline->lineoff = off; 3461 3462 // Detect simple cases of added lines in the end within a diff block. This 3463 // has to be the last change of this diff block, and all other buffers are 3464 // considering this to be an addition past their last line. Other scenarios 3465 // will be considered a changed line instead. 3466 bool added = false; 3467 if (num_changes == 1 && change_idx == dp->df_changes.ga_len) { 3468 added = true; 3469 for (int i = 0; i < DB_COUNT; i++) { 3470 if (idx == i) { 3471 continue; 3472 } 3473 if (curtab->tp_diffbuf[i] == NULL) { 3474 continue; 3475 } 3476 diffline_change_T *change = 3477 &((diffline_change_T *)dp->df_changes.ga_data)[dp->df_changes.ga_len - 1]; 3478 if (change->dc_start_lnum_off[i] != INT_MAX) { 3479 added = false; 3480 break; 3481 } 3482 } 3483 } 3484 return added; 3485 } 3486 3487 /// Check that line "lnum" is not close to a diff block, this line should 3488 /// be in a fold. 3489 /// 3490 /// @param wp window containing the buffer to check 3491 /// @param lnum line number to check within the buffer 3492 /// 3493 /// @return false if there are no diff blocks at all in this window. 3494 bool diff_infold(win_T *wp, linenr_T lnum) 3495 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) 3496 { 3497 // Return if 'diff' isn't set. 3498 if (!wp->w_p_diff) { 3499 return false; 3500 } 3501 3502 int idx = -1; 3503 bool other = false; 3504 for (int i = 0; i < DB_COUNT; i++) { 3505 if (curtab->tp_diffbuf[i] == wp->w_buffer) { 3506 idx = i; 3507 } else if (curtab->tp_diffbuf[i] != NULL) { 3508 other = true; 3509 } 3510 } 3511 3512 // return here if there are no diffs in the window 3513 if ((idx == -1) || !other) { 3514 return false; 3515 } 3516 3517 if (curtab->tp_diff_invalid) { 3518 // update after a big change 3519 ex_diffupdate(NULL); 3520 } 3521 3522 // Return if there are no diff blocks. All lines will be folded. 3523 if (curtab->tp_first_diff == NULL) { 3524 return true; 3525 } 3526 3527 for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 3528 // If this change is below the line there can't be any further match. 3529 if (dp->df_lnum[idx] - diff_context > lnum) { 3530 break; 3531 } 3532 3533 // If this change ends before the line we have a match. 3534 if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) { 3535 return false; 3536 } 3537 } 3538 return true; 3539 } 3540 3541 /// "dp" and "do" commands. 3542 void nv_diffgetput(bool put, size_t count) 3543 { 3544 if (bt_prompt(curbuf)) { 3545 vim_beep(kOptBoFlagOperator); 3546 return; 3547 } 3548 3549 exarg_T ea; 3550 char buf[30]; 3551 if (count == 0) { 3552 ea.arg = ""; 3553 } else { 3554 vim_snprintf(buf, sizeof(buf), "%zu", count); 3555 ea.arg = buf; 3556 } 3557 3558 if (put) { 3559 ea.cmdidx = CMD_diffput; 3560 } else { 3561 ea.cmdidx = CMD_diffget; 3562 } 3563 3564 ea.addr_count = 0; 3565 ea.line1 = curwin->w_cursor.lnum; 3566 ea.line2 = curwin->w_cursor.lnum; 3567 ex_diffgetput(&ea); 3568 } 3569 3570 /// Return true if "diff" appears in the list of diff blocks of the current tab. 3571 static bool valid_diff(diff_T *diff) 3572 { 3573 for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 3574 if (dp == diff) { 3575 return true; 3576 } 3577 } 3578 return false; 3579 } 3580 3581 /// ":diffget" and ":diffput" 3582 /// 3583 /// @param eap 3584 void ex_diffgetput(exarg_T *eap) 3585 { 3586 int idx_other; 3587 3588 // Find the current buffer in the list of diff buffers. 3589 int idx_cur = diff_buf_idx(curbuf, curtab); 3590 if (idx_cur == DB_COUNT) { 3591 emsg(_("E99: Current buffer is not in diff mode")); 3592 return; 3593 } 3594 3595 if (*eap->arg == NUL) { 3596 bool found_not_ma = false; 3597 // No argument: Find the other buffer in the list of diff buffers. 3598 for (idx_other = 0; idx_other < DB_COUNT; idx_other++) { 3599 if ((curtab->tp_diffbuf[idx_other] != curbuf) 3600 && (curtab->tp_diffbuf[idx_other] != NULL)) { 3601 if ((eap->cmdidx != CMD_diffput) 3602 || MODIFIABLE(curtab->tp_diffbuf[idx_other])) { 3603 break; 3604 } 3605 found_not_ma = true; 3606 } 3607 } 3608 3609 if (idx_other == DB_COUNT) { 3610 if (found_not_ma) { 3611 emsg(_("E793: No other buffer in diff mode is modifiable")); 3612 } else { 3613 emsg(_("E100: No other buffer in diff mode")); 3614 } 3615 return; 3616 } 3617 3618 // Check that there isn't a third buffer in the list 3619 for (int i = idx_other + 1; i < DB_COUNT; i++) { 3620 if ((curtab->tp_diffbuf[i] != curbuf) 3621 && (curtab->tp_diffbuf[i] != NULL) 3622 && ((eap->cmdidx != CMD_diffput) 3623 || MODIFIABLE(curtab->tp_diffbuf[i]))) { 3624 emsg(_("E101: More than two buffers in diff mode, don't know " 3625 "which one to use")); 3626 return; 3627 } 3628 } 3629 } else { 3630 // Buffer number or pattern given. Ignore trailing white space. 3631 char *p = eap->arg + strlen(eap->arg); 3632 while (p > eap->arg && ascii_iswhite(p[-1])) { 3633 p--; 3634 } 3635 3636 int i; 3637 for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {} 3638 3639 if (eap->arg + i == p) { 3640 // digits only 3641 i = (int)atol(eap->arg); 3642 } else { 3643 i = buflist_findpat(eap->arg, p, false, true, false); 3644 3645 if (i < 0) { 3646 // error message already given 3647 return; 3648 } 3649 } 3650 buf_T *buf = buflist_findnr(i); 3651 3652 if (buf == NULL) { 3653 semsg(_("E102: Can't find buffer \"%s\""), eap->arg); 3654 return; 3655 } 3656 3657 if (buf == curbuf) { 3658 // nothing to do 3659 return; 3660 } 3661 idx_other = diff_buf_idx(buf, curtab); 3662 3663 if (idx_other == DB_COUNT) { 3664 semsg(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg); 3665 return; 3666 } 3667 } 3668 3669 diff_busy = true; 3670 3671 // When no range given include the line above or below the cursor. 3672 if (eap->addr_count == 0) { 3673 // Make it possible that ":diffget" on the last line gets line below 3674 // the cursor line when there is no difference above the cursor. 3675 int linestatus = 0; 3676 if (eap->line1 == curbuf->b_ml.ml_line_count 3677 && (diff_check_with_linestatus(curwin, eap->line1, &linestatus) == 0 3678 && linestatus == 0) 3679 && (eap->line1 == 1 3680 || (diff_check_with_linestatus(curwin, eap->line1 - 1, &linestatus) >= 0 3681 && linestatus == 0))) { 3682 eap->line2++; 3683 } else if (eap->line1 > 0) { 3684 eap->line1--; 3685 } 3686 } 3687 3688 aco_save_T aco; 3689 3690 if (eap->cmdidx != CMD_diffget) { 3691 // Need to make the other buffer the current buffer to be able to make 3692 // changes in it. 3693 3694 // Set curwin/curbuf to buf and save a few things. 3695 aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); 3696 } 3697 3698 const int idx_from = eap->cmdidx == CMD_diffget ? idx_other : idx_cur; 3699 const int idx_to = eap->cmdidx == CMD_diffget ? idx_cur : idx_other; 3700 3701 // May give the warning for a changed buffer here, which can trigger the 3702 // FileChangedRO autocommand, which may do nasty things and mess 3703 // everything up. 3704 if (!curbuf->b_changed) { 3705 change_warning(curbuf, 0); 3706 if (diff_buf_idx(curbuf, curtab) != idx_to) { 3707 emsg(_("E787: Buffer changed unexpectedly")); 3708 goto theend; 3709 } 3710 } 3711 3712 diffgetput(eap->addr_count, idx_cur, idx_from, idx_to, eap->line1, eap->line2); 3713 3714 // restore curwin/curbuf and a few other things 3715 if (eap->cmdidx != CMD_diffget) { 3716 // Syncing undo only works for the current buffer, but we change 3717 // another buffer. Sync undo if the command was typed. This isn't 3718 // 100% right when ":diffput" is used in a function or mapping. 3719 if (KeyTyped) { 3720 u_sync(false); 3721 } 3722 aucmd_restbuf(&aco); 3723 } 3724 3725 theend: 3726 diff_busy = false; 3727 3728 if (diff_need_update) { 3729 ex_diffupdate(NULL); 3730 } 3731 3732 // Check that the cursor is on a valid character and update its 3733 // position. When there were filler lines the topline has become 3734 // invalid. 3735 check_cursor(curwin); 3736 changed_line_abv_curs(); 3737 3738 // If all diffs are gone, update folds in all diff windows. 3739 if (curtab->tp_first_diff == NULL) { 3740 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 3741 if (wp->w_p_diff && wp->w_p_fdm[0] == 'd' && wp->w_p_fen) { 3742 foldUpdateAll(wp); 3743 } 3744 } 3745 } 3746 3747 if (diff_need_update) { 3748 // redraw already done by ex_diffupdate() 3749 diff_need_update = false; 3750 } else { 3751 // Also need to redraw the other buffers. 3752 diff_redraw(false); 3753 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); 3754 } 3755 } 3756 3757 /// Apply diffget/diffput to buffers and diffblocks 3758 /// 3759 /// @param idx_cur index of "curbuf" before aucmd_prepbuf() in the list of diff buffers 3760 /// @param idx_from index of the buffer to read from in the list of diff buffers 3761 /// @param idx_to index of the buffer to modify in the list of diff buffers 3762 static void diffgetput(const int addr_count, const int idx_cur, const int idx_from, 3763 const int idx_to, const linenr_T line1, const linenr_T line2) 3764 { 3765 linenr_T off = 0; 3766 diff_T *dprev = NULL; 3767 3768 for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) { 3769 if (!addr_count) { 3770 // Handle the case with adjacent diff blocks (e.g. using linematch 3771 // or anchors) at/above the cursor. Since a range wasn't specified, 3772 // we just want to grab one diff block rather than all of them in 3773 // the vicinity. 3774 while (dp->df_next 3775 && dp->df_next->df_lnum[idx_cur] == dp->df_lnum[idx_cur] + dp->df_count[idx_cur] 3776 && dp->df_next->df_lnum[idx_cur] == line1 + off + 1) { 3777 dprev = dp; 3778 dp = dp->df_next; 3779 } 3780 } 3781 3782 if (dp->df_lnum[idx_cur] > line2 + off) { 3783 // past the range that was specified 3784 break; 3785 } 3786 diff_T dfree = { 0 }; 3787 bool did_free = false; 3788 linenr_T lnum = dp->df_lnum[idx_to]; 3789 linenr_T count = dp->df_count[idx_to]; 3790 3791 if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > line1 + off) 3792 && (u_save(lnum - 1, lnum + count) != FAIL)) { 3793 // Inside the specified range and saving for undo worked. 3794 linenr_T start_skip = 0; 3795 linenr_T end_skip = 0; 3796 3797 if (addr_count > 0) { 3798 // A range was specified: check if lines need to be skipped. 3799 start_skip = line1 + off - dp->df_lnum[idx_cur]; 3800 if (start_skip > 0) { 3801 // range starts below start of current diff block 3802 if (start_skip > count) { 3803 lnum += count; 3804 count = 0; 3805 } else { 3806 count -= start_skip; 3807 lnum += start_skip; 3808 } 3809 } else { 3810 start_skip = 0; 3811 } 3812 3813 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 3814 - (line2 + off); 3815 3816 if (end_skip > 0) { 3817 // range ends above end of current/from diff block 3818 if (idx_cur == idx_from) { 3819 // :diffput 3820 count = MIN(count, dp->df_count[idx_cur] - start_skip - end_skip); 3821 } else { 3822 // :diffget 3823 count -= end_skip; 3824 end_skip = MAX(dp->df_count[idx_from] - start_skip - count, 0); 3825 } 3826 } else { 3827 end_skip = 0; 3828 } 3829 } 3830 3831 bool buf_empty = buf_is_empty(curbuf); 3832 int added = 0; 3833 3834 for (int i = 0; i < count; i++) { 3835 // remember deleting the last line of the buffer 3836 buf_empty = curbuf->b_ml.ml_line_count == 1; 3837 if (ml_delete(lnum) == OK) { 3838 added--; 3839 } 3840 } 3841 3842 for (int i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { 3843 linenr_T nr = dp->df_lnum[idx_from] + start_skip + i; 3844 if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { 3845 break; 3846 } 3847 char *p = xstrdup(ml_get_buf(curtab->tp_diffbuf[idx_from], nr)); 3848 ml_append(lnum + i - 1, p, 0, false); 3849 xfree(p); 3850 added++; 3851 if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) { 3852 // Added the first line into an empty buffer, need to 3853 // delete the dummy empty line. 3854 // This has a side effect of incrementing curbuf->deleted_bytes, 3855 // which results in inaccurate reporting of the byte count of 3856 // previous contents in buffer-update events. 3857 buf_empty = false; 3858 ml_delete(2); 3859 } 3860 } 3861 linenr_T new_count = dp->df_count[idx_to] + added; 3862 dp->df_count[idx_to] = new_count; 3863 3864 if ((start_skip == 0) && (end_skip == 0)) { 3865 // Check if there are any other buffers and if the diff is 3866 // equal in them. 3867 int i; 3868 for (i = 0; i < DB_COUNT; i++) { 3869 if ((curtab->tp_diffbuf[i] != NULL) 3870 && (i != idx_from) 3871 && (i != idx_to) 3872 && !diff_equal_entry(dp, idx_from, i)) { 3873 break; 3874 } 3875 } 3876 3877 if (i == DB_COUNT) { 3878 // delete the diff entry, the buffers are now equal here 3879 dfree = *dp; 3880 did_free = true; 3881 dp = diff_free(curtab, dprev, dp); 3882 } 3883 } 3884 3885 if (added != 0) { 3886 // Adjust marks. This will change the following entries! 3887 mark_adjust(lnum, lnum + count - 1, MAXLNUM, added, kExtmarkNOOP); 3888 if (curwin->w_cursor.lnum >= lnum) { 3889 // Adjust the cursor position if it's in/after the changed 3890 // lines. 3891 if (curwin->w_cursor.lnum >= lnum + count) { 3892 curwin->w_cursor.lnum += added; 3893 // When the buffer was previously empty, the cursor may 3894 // now be beyond the last line, so clamp cursor lnum. 3895 curwin->w_cursor.lnum = MIN(curwin->w_cursor.lnum, 3896 curbuf->b_ml.ml_line_count); 3897 } else if (added < 0) { 3898 curwin->w_cursor.lnum = lnum; 3899 } 3900 } 3901 } 3902 extmark_adjust(curbuf, lnum, lnum + count - 1, MAXLNUM, added, kExtmarkUndo); 3903 changed_lines(curbuf, lnum, 0, lnum + count, added, true); 3904 3905 if (did_free) { 3906 // Diff is deleted, update folds in other windows. 3907 diff_fold_update(&dfree, idx_to); 3908 } 3909 3910 // mark_adjust() may have made "dp" invalid. We don't know where 3911 // to continue then, bail out. 3912 if (added != 0 && !valid_diff(dp)) { 3913 break; 3914 } 3915 3916 if (!did_free) { 3917 // mark_adjust() may have changed the count in a wrong way 3918 dp->df_count[idx_to] = new_count; 3919 } 3920 3921 // When changing the current buffer, keep track of line numbers 3922 if (idx_cur == idx_to) { 3923 off += added; 3924 } 3925 } 3926 3927 // If before the range or not deleted, go to next diff. 3928 if (!did_free) { 3929 dprev = dp; 3930 dp = dp->df_next; 3931 } 3932 } 3933 } 3934 3935 /// Update folds for all diff buffers for entry "dp". 3936 /// 3937 /// Skip buffer with index "skip_idx". 3938 /// When there are no diffs, all folds are removed. 3939 /// 3940 /// @param dp 3941 /// @param skip_idx 3942 static void diff_fold_update(diff_T *dp, int skip_idx) 3943 { 3944 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 3945 for (int i = 0; i < DB_COUNT; i++) { 3946 if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) { 3947 foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]); 3948 } 3949 } 3950 } 3951 } 3952 3953 /// Checks that the buffer is in diff-mode. 3954 /// 3955 /// @param buf buffer to check. 3956 bool diff_mode_buf(buf_T *buf) 3957 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) 3958 { 3959 FOR_ALL_TABS(tp) { 3960 if (diff_buf_idx(buf, tp) != DB_COUNT) { 3961 return true; 3962 } 3963 } 3964 return false; 3965 } 3966 3967 /// Move "count" times in direction "dir" to the next diff block. 3968 /// 3969 /// @param dir 3970 /// @param count 3971 /// 3972 /// @return FAIL if there isn't such a diff block. 3973 int diff_move_to(int dir, int count) 3974 { 3975 linenr_T lnum = curwin->w_cursor.lnum; 3976 int idx = diff_buf_idx(curbuf, curtab); 3977 if ((idx == DB_COUNT) || (curtab->tp_first_diff == NULL)) { 3978 return FAIL; 3979 } 3980 3981 if (curtab->tp_diff_invalid) { 3982 // update after a big change 3983 ex_diffupdate(NULL); 3984 } 3985 3986 if (curtab->tp_first_diff == NULL) { 3987 // no diffs today 3988 return FAIL; 3989 } 3990 3991 while (--count >= 0) { 3992 // Check if already before first diff. 3993 if ((dir == BACKWARD) && (lnum <= curtab->tp_first_diff->df_lnum[idx])) { 3994 break; 3995 } 3996 3997 diff_T *dp; 3998 for (dp = curtab->tp_first_diff;; dp = dp->df_next) { 3999 if (dp == NULL) { 4000 break; 4001 } 4002 4003 if (((dir == FORWARD) && (lnum < dp->df_lnum[idx])) 4004 || ((dir == BACKWARD) 4005 && ((dp->df_next == NULL) 4006 || (lnum <= dp->df_next->df_lnum[idx])))) { 4007 lnum = dp->df_lnum[idx]; 4008 break; 4009 } 4010 } 4011 } 4012 4013 // don't end up past the end of the file 4014 lnum = MIN(lnum, curbuf->b_ml.ml_line_count); 4015 4016 // When the cursor didn't move at all we fail. 4017 if (lnum == curwin->w_cursor.lnum) { 4018 return FAIL; 4019 } 4020 4021 setpcmark(); 4022 curwin->w_cursor.lnum = lnum; 4023 curwin->w_cursor.col = 0; 4024 4025 return OK; 4026 } 4027 4028 /// Return the line number in the current window that is closest to "lnum1" in 4029 /// "buf1" in diff mode. 4030 static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1) 4031 { 4032 linenr_T baseline = 0; 4033 4034 int idx1 = diff_buf_idx(buf1, curtab); 4035 int idx2 = diff_buf_idx(curbuf, curtab); 4036 4037 if ((idx1 == DB_COUNT) 4038 || (idx2 == DB_COUNT) 4039 || (curtab->tp_first_diff == NULL)) { 4040 return lnum1; 4041 } 4042 4043 if (curtab->tp_diff_invalid) { 4044 // update after a big change 4045 ex_diffupdate(NULL); 4046 } 4047 4048 if (curtab->tp_first_diff == NULL) { 4049 // no diffs today 4050 return lnum1; 4051 } 4052 4053 for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 4054 if (dp->df_lnum[idx1] > lnum1) { 4055 return lnum1 - baseline; 4056 } 4057 if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { 4058 // Inside the diffblock 4059 baseline = lnum1 - dp->df_lnum[idx1]; 4060 baseline = MIN(baseline, dp->df_count[idx2]); 4061 4062 return dp->df_lnum[idx2] + baseline; 4063 } 4064 if ((dp->df_lnum[idx1] == lnum1) 4065 && (dp->df_count[idx1] == 0) 4066 && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) 4067 && ((dp->df_lnum[idx2] + dp->df_count[idx2]) 4068 > curwin->w_cursor.lnum)) { 4069 // Special case: if the cursor is just after a zero-count 4070 // block (i.e. all filler) and the target cursor is already 4071 // inside the corresponding block, leave the target cursor 4072 // unmoved. This makes repeated CTRL-W W operations work 4073 // as expected. 4074 return curwin->w_cursor.lnum; 4075 } 4076 baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) 4077 - (dp->df_lnum[idx2] + dp->df_count[idx2]); 4078 } 4079 4080 // If we get here then the cursor is after the last diff 4081 return lnum1 - baseline; 4082 } 4083 4084 /// Finds the corresponding line in a diff. 4085 /// 4086 /// @param buf1 4087 /// @param lnum1 4088 /// 4089 /// @return The corresponding line. 4090 linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) 4091 { 4092 linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); 4093 4094 // don't end up past the end of the file 4095 return MIN(lnum, curbuf->b_ml.ml_line_count); 4096 } 4097 4098 /// For line "lnum" in the current window find the equivalent lnum in window 4099 /// "wp", compensating for inserted/deleted lines. 4100 linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) 4101 { 4102 diff_T *dp; 4103 4104 int idx = diff_buf_idx(curbuf, curtab); 4105 4106 if (idx == DB_COUNT) { 4107 // safety check 4108 return 0; 4109 } 4110 4111 if (curtab->tp_diff_invalid) { 4112 // update after a big change 4113 ex_diffupdate(NULL); 4114 } 4115 4116 // search for a change that includes "lnum" in the list of diffblocks. 4117 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { 4118 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { 4119 break; 4120 } 4121 } 4122 4123 // When after the last change, compute relative to the last line number. 4124 if (dp == NULL) { 4125 return wp->w_buffer->b_ml.ml_line_count 4126 - (curbuf->b_ml.ml_line_count - lnum); 4127 } 4128 4129 // Find index for "wp". 4130 int i = diff_buf_idx(wp->w_buffer, curtab); 4131 4132 if (i == DB_COUNT) { 4133 // safety check 4134 return 0; 4135 } 4136 4137 linenr_T n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); 4138 return MIN(n, dp->df_lnum[i] + dp->df_count[i]); 4139 } 4140 4141 /// Handle an ED style diff line. 4142 /// 4143 /// @return FAIL if the line does not contain diff info. 4144 static int parse_diff_ed(char *line, diffhunk_T *hunk) 4145 { 4146 int l1, l2; 4147 4148 // The line must be one of three formats: 4149 // change: {first}[,{last}]c{first}[,{last}] 4150 // append: {first}a{first}[,{last}] 4151 // delete: {first}[,{last}]d{first} 4152 char *p = line; 4153 linenr_T f1 = getdigits_int32(&p, true, 0); 4154 if (*p == ',') { 4155 p++; 4156 l1 = getdigits_int(&p, true, 0); 4157 } else { 4158 l1 = f1; 4159 } 4160 if (*p != 'a' && *p != 'c' && *p != 'd') { 4161 return FAIL; // invalid diff format 4162 } 4163 int difftype = (uint8_t)(*p++); 4164 int f2 = getdigits_int(&p, true, 0); 4165 if (*p == ',') { 4166 p++; 4167 l2 = getdigits_int(&p, true, 0); 4168 } else { 4169 l2 = f2; 4170 } 4171 if (l1 < f1 || l2 < f2) { 4172 return FAIL; 4173 } 4174 4175 if (difftype == 'a') { 4176 hunk->lnum_orig = f1 + 1; 4177 hunk->count_orig = 0; 4178 } else { 4179 hunk->lnum_orig = f1; 4180 hunk->count_orig = l1 - f1 + 1; 4181 } 4182 if (difftype == 'd') { 4183 hunk->lnum_new = (linenr_T)f2 + 1; 4184 hunk->count_new = 0; 4185 } else { 4186 hunk->lnum_new = (linenr_T)f2; 4187 hunk->count_new = l2 - f2 + 1; 4188 } 4189 return OK; 4190 } 4191 4192 /// Parses unified diff with zero(!) context lines. 4193 /// Return FAIL if there is no diff information in "line". 4194 static int parse_diff_unified(char *line, diffhunk_T *hunk) 4195 { 4196 // Parse unified diff hunk header: 4197 // @@ -oldline,oldcount +newline,newcount @@ 4198 char *p = line; 4199 if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { 4200 int oldcount; 4201 linenr_T newline; 4202 int newcount; 4203 linenr_T oldline = getdigits_int32(&p, true, 0); 4204 if (*p == ',') { 4205 p++; 4206 oldcount = getdigits_int(&p, true, 0); 4207 } else { 4208 oldcount = 1; 4209 } 4210 if (*p++ == ' ' && *p++ == '+') { 4211 newline = getdigits_int(&p, true, 0); 4212 if (*p == ',') { 4213 p++; 4214 newcount = getdigits_int(&p, true, 0); 4215 } else { 4216 newcount = 1; 4217 } 4218 } else { 4219 return FAIL; // invalid diff format 4220 } 4221 4222 if (oldcount == 0) { 4223 oldline += 1; 4224 } 4225 if (newcount == 0) { 4226 newline += 1; 4227 } 4228 if (newline == 0) { 4229 newline = 1; 4230 } 4231 4232 hunk->lnum_orig = oldline; 4233 hunk->count_orig = oldcount; 4234 hunk->lnum_new = newline; 4235 hunk->count_new = newcount; 4236 4237 return OK; 4238 } 4239 4240 return FAIL; 4241 } 4242 4243 /// Callback function for the xdl_diff() function. 4244 /// Stores the diff output in a grow array. 4245 static int xdiff_out(int start_a, int count_a, int start_b, int count_b, void *priv) 4246 { 4247 diffout_T *dout = (diffout_T *)priv; 4248 GA_APPEND(diffhunk_T, &(dout->dout_ga), ((diffhunk_T){ 4249 .lnum_orig = (linenr_T)start_a + 1, 4250 .count_orig = count_a, 4251 .lnum_new = (linenr_T)start_b + 1, 4252 .count_new = count_b, 4253 })); 4254 return 0; 4255 } 4256 4257 /// "diff_filler()" function 4258 void f_diff_filler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4259 { 4260 rettv->vval.v_number = MAX(0, diff_check_fill(curwin, tv_get_lnum(argvars))); 4261 } 4262 4263 /// "diff_hlID()" function 4264 void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 4265 { 4266 static linenr_T prev_lnum = 0; 4267 static varnumber_T changedtick = 0; 4268 static int fnum = 0; 4269 static int prev_diff_flags = 0; 4270 static int change_start = 0; 4271 static int change_end = 0; 4272 static hlf_T hlID = (hlf_T)0; 4273 4274 diffline_T diffline = { 0 }; 4275 // Remember the results if using simple since it's recalculated per 4276 // call. Otherwise just call diff_find_change() every time since 4277 // internally the result is cached internally. 4278 const bool cache_results = !(diff_flags & ALL_INLINE_DIFF); 4279 4280 linenr_T lnum = tv_get_lnum(argvars); 4281 if (lnum < 0) { // ignore type error in {lnum} arg 4282 lnum = 0; 4283 } 4284 if (!cache_results 4285 || lnum != prev_lnum 4286 || changedtick != buf_get_changedtick(curbuf) 4287 || fnum != curbuf->b_fnum 4288 || diff_flags != prev_diff_flags) { 4289 // New line, buffer, change: need to get the values. 4290 int linestatus = 0; 4291 diff_check_with_linestatus(curwin, lnum, &linestatus); 4292 if (linestatus < 0) { 4293 if (linestatus == -1) { 4294 change_start = MAXCOL; 4295 change_end = -1; 4296 if (diff_find_change(curwin, lnum, &diffline)) { 4297 hlID = HLF_ADD; // added line 4298 } else { 4299 hlID = HLF_CHD; // changed line 4300 if (diffline.num_changes > 0 && cache_results) { 4301 change_start = diffline.changes[0].dc_start[diffline.bufidx]; 4302 change_end = diffline.changes[0].dc_end[diffline.bufidx]; 4303 } 4304 } 4305 } else { 4306 hlID = HLF_ADD; // added line 4307 } 4308 } else { 4309 hlID = (hlf_T)0; 4310 } 4311 4312 if (cache_results) { 4313 prev_lnum = lnum; 4314 changedtick = buf_get_changedtick(curbuf); 4315 fnum = curbuf->b_fnum; 4316 prev_diff_flags = diff_flags; 4317 } 4318 } 4319 4320 if (hlID == HLF_CHD || hlID == HLF_TXD) { 4321 int col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. 4322 if (cache_results) { 4323 if (col >= change_start && col < change_end) { 4324 hlID = HLF_TXD; // Changed text. 4325 } else { 4326 hlID = HLF_CHD; // Changed line. 4327 } 4328 } else { 4329 hlID = HLF_CHD; 4330 for (int i = 0; i < diffline.num_changes; i++) { 4331 bool added = diff_change_parse(&diffline, &diffline.changes[i], 4332 &change_start, &change_end); 4333 if (col >= change_start && col < change_end) { 4334 hlID = added ? HLF_TXA : HLF_TXD; 4335 break; 4336 } 4337 if (col < change_start) { 4338 // the remaining changes are past this column and not relevant 4339 break; 4340 } 4341 } 4342 } 4343 } 4344 rettv->vval.v_number = hlID; 4345 }