comparison src/diff.c @ 14696:195e8b1fcbbf v8.1.0360

patch 8.1.0360: using an external diff program is slow and inflexible commit https://github.com/vim/vim/commit/e828b7621cf9065a3582be0c4dd1e0e846e335bf Author: Bram Moolenaar <Bram@vim.org> Date: Mon Sep 10 17:51:58 2018 +0200 patch 8.1.0360: using an external diff program is slow and inflexible Problem: Using an external diff program is slow and inflexible. Solution: Include the xdiff library. (Christian Brabandt, closes https://github.com/vim/vim/issues/2732) Use it by default.
author Christian Brabandt <cb@256bit.org>
date Mon, 10 Sep 2018 18:00:06 +0200
parents dc67449d648c
children 973d3f4d8017
comparison
equal deleted inserted replaced
14695:b178e2039b2d 14696:195e8b1fcbbf
7 * See README.txt for an overview of the Vim source code. 7 * See README.txt for an overview of the Vim source code.
8 */ 8 */
9 9
10 /* 10 /*
11 * diff.c: code for diff'ing two, three or four buffers. 11 * diff.c: code for diff'ing two, three or four buffers.
12 *
13 * There are three ways to diff:
14 * - Shell out to an external diff program, using files.
15 * - Use the compiled-in xdiff library.
16 * - Let 'diffexpr' do the work, using files.
12 */ 17 */
13 18
14 #include "vim.h" 19 #include "vim.h"
20 #include "xdiff/xdiff.h"
15 21
16 #if defined(FEAT_DIFF) || defined(PROTO) 22 #if defined(FEAT_DIFF) || defined(PROTO)
17 23
18 static int diff_busy = FALSE; /* ex_diffgetput() is busy */ 24 static int diff_busy = FALSE; /* ex_diffgetput() is busy */
19 25
20 /* flags obtained from the 'diffopt' option */ 26 /* flags obtained from the 'diffopt' option */
21 #define DIFF_FILLER 1 /* display filler lines */ 27 #define DIFF_FILLER 1 // display filler lines
22 #define DIFF_ICASE 2 /* ignore case */ 28 #define DIFF_ICASE 2 // ignore case
23 #define DIFF_IWHITE 4 /* ignore change in white space */ 29 #define DIFF_IWHITE 4 // ignore change in white space
24 #define DIFF_HORIZONTAL 8 /* horizontal splits */ 30 #define DIFF_HORIZONTAL 8 // horizontal splits
25 #define DIFF_VERTICAL 16 /* vertical splits */ 31 #define DIFF_VERTICAL 16 // vertical splits
26 #define DIFF_HIDDEN_OFF 32 /* diffoff when hidden */ 32 #define DIFF_HIDDEN_OFF 32 // diffoff when hidden
33 #define DIFF_INTERNAL 64 // use internal xdiff algorithm
27 static int diff_flags = DIFF_FILLER; 34 static int diff_flags = DIFF_FILLER;
35
36 static long diff_algorithm = 0;
28 37
29 #define LBUFLEN 50 /* length of line in diff file */ 38 #define LBUFLEN 50 /* length of line in diff file */
30 39
31 static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it 40 static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it
32 doesn't work, MAYBE when not checked yet */ 41 doesn't work, MAYBE when not checked yet */
34 static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE 43 static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE
35 when it doesn't work, MAYBE when not 44 when it doesn't work, MAYBE when not
36 checked yet */ 45 checked yet */
37 #endif 46 #endif
38 47
48 // used for diff input
49 typedef struct {
50 char_u *din_fname; // used for external diff
51 mmfile_t din_mmfile; // used for internal diff
52 } diffin_T;
53
54 // used for diff result
55 typedef struct {
56 char_u *dout_fname; // used for external diff
57 garray_T dout_ga; // used for internal diff
58 } diffout_T;
59
60 // two diff inputs and one result
61 typedef struct {
62 diffin_T dio_orig; // original file input
63 diffin_T dio_new; // new file input
64 diffout_T dio_diff; // diff result
65 int dio_internal; // using internal diff
66 } diffio_T;
67
39 static int diff_buf_idx(buf_T *buf); 68 static int diff_buf_idx(buf_T *buf);
40 static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp); 69 static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp);
41 static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after); 70 static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after);
42 static void diff_check_unchanged(tabpage_T *tp, diff_T *dp); 71 static void diff_check_unchanged(tabpage_T *tp, diff_T *dp);
43 static int diff_check_sanity(tabpage_T *tp, diff_T *dp); 72 static int diff_check_sanity(tabpage_T *tp, diff_T *dp);
44 static void diff_redraw(int dofold); 73 static void diff_redraw(int dofold);
45 static int diff_write(buf_T *buf, char_u *fname); 74 static int check_external_diff(diffio_T *diffio);
46 static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff); 75 static int diff_file(diffio_T *diffio);
47 static int diff_equal_entry(diff_T *dp, int idx1, int idx2); 76 static int diff_equal_entry(diff_T *dp, int idx1, int idx2);
48 static int diff_cmp(char_u *s1, char_u *s2); 77 static int diff_cmp(char_u *s1, char_u *s2);
49 #ifdef FEAT_FOLDING 78 #ifdef FEAT_FOLDING
50 static void diff_fold_update(diff_T *dp, int skip_idx); 79 static void diff_fold_update(diff_T *dp, int skip_idx);
51 #endif 80 #endif
52 static void diff_read(int idx_orig, int idx_new, char_u *fname); 81 static void diff_read(int idx_orig, int idx_new, diffout_T *fname);
53 static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new); 82 static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new);
54 static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp); 83 static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp);
84 static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new);
85 static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_orig, linenr_T *lnum_new, long *count_new);
86 static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf);
55 87
56 #ifndef USE_CR 88 #ifndef USE_CR
57 # define tag_fgets vim_fgets 89 # define tag_fgets vim_fgets
58 #endif 90 #endif
59 91
629 check_topfill(wp, FALSE); 661 check_topfill(wp, FALSE);
630 } 662 }
631 } 663 }
632 } 664 }
633 665
634 /* 666 static void
635 * Write buffer "buf" to file "name". 667 clear_diffin(diffin_T *din)
636 * Always use 'fileformat' set to "unix". 668 {
637 * Return FAIL for failure 669 if (din->din_fname == NULL)
670 {
671 vim_free(din->din_mmfile.ptr);
672 din->din_mmfile.ptr = NULL;
673 }
674 else
675 mch_remove(din->din_fname);
676 }
677
678 static void
679 clear_diffout(diffout_T *dout)
680 {
681 if (dout->dout_fname == NULL)
682 ga_clear_strings(&dout->dout_ga);
683 else
684 mch_remove(dout->dout_fname);
685 }
686
687 /*
688 * Write buffer "buf" to a memory buffer.
689 * Return FAIL for failure.
638 */ 690 */
639 static int 691 static int
640 diff_write(buf_T *buf, char_u *fname) 692 diff_write_buffer(buf_T *buf, diffin_T *din)
693 {
694 linenr_T lnum;
695 char_u *s;
696 long len = 0;
697 char_u *ptr;
698
699 // xdiff requires one big block of memory with all the text.
700 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
701 len += STRLEN(ml_get_buf(buf, lnum, FALSE)) + 1;
702 ptr = lalloc(len, TRUE);
703 if (ptr == NULL)
704 {
705 // Allocating memory failed. This can happen, because we try to read
706 // the whole buffer text into memory. Set the failed flag, the diff
707 // will be retried with external diff. The flag is never reset.
708 buf->b_diff_failed = TRUE;
709 if (p_verbose > 0)
710 {
711 verbose_enter();
712 smsg((char_u *)
713 _("Not enough memory to use internal diff for buffer \"%s\""),
714 buf->b_fname);
715 verbose_leave();
716 }
717 return FAIL;
718 }
719 din->din_mmfile.ptr = (char *)ptr;
720 din->din_mmfile.size = len;
721
722 len = 0;
723 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
724 {
725 for (s = ml_get_buf(buf, lnum, FALSE); *s != NUL; )
726 {
727 if (diff_flags & DIFF_ICASE)
728 {
729 int c;
730
731 // xdiff doesn't support ignoring case, fold-case the text.
732 #ifdef FEAT_MBYTE
733 int orig_len;
734 char_u cbuf[MB_MAXBYTES + 1];
735
736 c = PTR2CHAR(s);
737 c = enc_utf8 ? utf_fold(c) : MB_TOLOWER(c);
738 orig_len = MB_PTR2LEN(s);
739 if (mb_char2bytes(c, cbuf) != orig_len)
740 // TODO: handle byte length difference
741 mch_memmove(ptr + len, s, orig_len);
742 else
743 mch_memmove(ptr + len, cbuf, orig_len);
744
745 s += orig_len;
746 len += orig_len;
747 #else
748 c = *s++;
749 ptr[len++] = TOLOWER_LOC(c);
750 #endif
751 }
752 else
753 ptr[len++] = *s++;
754 }
755 ptr[len++] = NL;
756 }
757 return OK;
758 }
759
760 /*
761 * Write buffer "buf" to file or memory buffer.
762 * Return FAIL for failure.
763 */
764 static int
765 diff_write(buf_T *buf, diffin_T *din)
641 { 766 {
642 int r; 767 int r;
643 char_u *save_ff; 768 char_u *save_ff;
644 769
770 if (din->din_fname == NULL)
771 return diff_write_buffer(buf, din);
772
773 // Always use 'fileformat' set to "unix".
645 save_ff = buf->b_p_ff; 774 save_ff = buf->b_p_ff;
646 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); 775 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
647 r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, 776 r = buf_write(buf, din->din_fname, NULL,
648 NULL, FALSE, FALSE, FALSE, TRUE); 777 (linenr_T)1, buf->b_ml.ml_line_count,
778 NULL, FALSE, FALSE, FALSE, TRUE);
649 free_string_option(buf->b_p_ff); 779 free_string_option(buf->b_p_ff);
650 buf->b_p_ff = save_ff; 780 buf->b_p_ff = save_ff;
651 return r; 781 return r;
782 }
783
784 /*
785 * Update the diffs for all buffers involved.
786 */
787 static void
788 diff_try_update(
789 diffio_T *dio,
790 int idx_orig,
791 exarg_T *eap) // "eap" can be NULL
792 {
793 buf_T *buf;
794 int idx_new;
795
796 if (dio->dio_internal)
797 {
798 ga_init2(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
799 }
800 else
801 {
802 // We need three temp file names.
803 dio->dio_orig.din_fname = vim_tempname('o', TRUE);
804 dio->dio_new.din_fname = vim_tempname('n', TRUE);
805 dio->dio_diff.dout_fname = vim_tempname('d', TRUE);
806 if (dio->dio_orig.din_fname == NULL
807 || dio->dio_new.din_fname == NULL
808 || dio->dio_diff.dout_fname == NULL)
809 goto theend;
810 }
811
812 // Check external diff is actually working.
813 if (!dio->dio_internal && check_external_diff(dio) == FAIL)
814 goto theend;
815
816 // :diffupdate!
817 if (eap != NULL && eap->forceit)
818 for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new)
819 {
820 buf = curtab->tp_diffbuf[idx_new];
821 if (buf_valid(buf))
822 buf_check_timestamp(buf, FALSE);
823 }
824
825 // Write the first buffer to a tempfile or mmfile_t.
826 buf = curtab->tp_diffbuf[idx_orig];
827 if (diff_write(buf, &dio->dio_orig) == FAIL)
828 goto theend;
829
830 // Make a difference between the first buffer and every other.
831 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
832 {
833 buf = curtab->tp_diffbuf[idx_new];
834 if (buf == NULL || buf->b_ml.ml_mfp == NULL)
835 continue; // skip buffer that isn't loaded
836
837 // Write the other buffer and diff with the first one.
838 if (diff_write(buf, &dio->dio_new) == FAIL)
839 continue;
840 if (diff_file(dio) == FAIL)
841 continue;
842
843 // Read the diff output and add each entry to the diff list.
844 diff_read(idx_orig, idx_new, &dio->dio_diff);
845
846 clear_diffin(&dio->dio_new);
847 clear_diffout(&dio->dio_diff);
848 }
849 clear_diffin(&dio->dio_orig);
850
851 theend:
852 vim_free(dio->dio_orig.din_fname);
853 vim_free(dio->dio_new.din_fname);
854 vim_free(dio->dio_diff.dout_fname);
855 }
856
857 /*
858 * Return TRUE if the options are set to use the internal diff library.
859 * Note that if the internal diff failed for one of the buffers, the external
860 * diff will be used anyway.
861 */
862 static int
863 diff_internal(void)
864 {
865 return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL;
866 }
867
868 /*
869 * Return TRUE if the internal diff failed for one of the diff buffers.
870 */
871 static int
872 diff_internal_failed(void)
873 {
874 int idx;
875
876 // Only need to do something when there is another buffer.
877 for (idx = 0; idx < DB_COUNT; ++idx)
878 if (curtab->tp_diffbuf[idx] != NULL
879 && curtab->tp_diffbuf[idx]->b_diff_failed)
880 return TRUE;
881 return FALSE;
652 } 882 }
653 883
654 /* 884 /*
655 * Completely update the diffs for the buffers involved. 885 * Completely update the diffs for the buffers involved.
656 * This uses the ordinary "diff" command. 886 * This uses the ordinary "diff" command.
657 * The buffers are written to a file, also for unmodified buffers (the file 887 * The buffers are written to a file, also for unmodified buffers (the file
658 * could have been produced by autocommands, e.g. the netrw plugin). 888 * could have been produced by autocommands, e.g. the netrw plugin).
659 */ 889 */
660 void 890 void
661 ex_diffupdate( 891 ex_diffupdate(exarg_T *eap) // "eap" can be NULL
662 exarg_T *eap) /* can be NULL */ 892 {
663 {
664 buf_T *buf;
665 int idx_orig; 893 int idx_orig;
666 int idx_new; 894 int idx_new;
667 char_u *tmp_orig; 895 diffio_T diffio;
668 char_u *tmp_new; 896
669 char_u *tmp_diff; 897 // Delete all diffblocks.
670 FILE *fd;
671 int ok;
672 int io_error = FALSE;
673
674 /* Delete all diffblocks. */
675 diff_clear(curtab); 898 diff_clear(curtab);
676 curtab->tp_diff_invalid = FALSE; 899 curtab->tp_diff_invalid = FALSE;
677 900
678 /* Use the first buffer as the original text. */ 901 // Use the first buffer as the original text.
679 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) 902 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig)
680 if (curtab->tp_diffbuf[idx_orig] != NULL) 903 if (curtab->tp_diffbuf[idx_orig] != NULL)
681 break; 904 break;
682 if (idx_orig == DB_COUNT) 905 if (idx_orig == DB_COUNT)
683 return; 906 return;
684 907
685 /* Only need to do something when there is another buffer. */ 908 // Only need to do something when there is another buffer.
686 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 909 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
687 if (curtab->tp_diffbuf[idx_new] != NULL) 910 if (curtab->tp_diffbuf[idx_new] != NULL)
688 break; 911 break;
689 if (idx_new == DB_COUNT) 912 if (idx_new == DB_COUNT)
690 return; 913 return;
691 914
692 /* We need three temp file names. */ 915 // Only use the internal method if it did not fail for one of the buffers.
693 tmp_orig = vim_tempname('o', TRUE); 916 vim_memset(&diffio, 0, sizeof(diffio));
694 tmp_new = vim_tempname('n', TRUE); 917 diffio.dio_internal = diff_internal() && !diff_internal_failed();
695 tmp_diff = vim_tempname('d', TRUE); 918
696 if (tmp_orig == NULL || tmp_new == NULL || tmp_diff == NULL) 919 diff_try_update(&diffio, idx_orig, eap);
697 goto theend; 920 if (diffio.dio_internal && diff_internal_failed())
698 921 {
699 /* 922 // Internal diff failed, use external diff instead.
700 * Do a quick test if "diff" really works. Otherwise it looks like there 923 vim_memset(&diffio, 0, sizeof(diffio));
701 * are no differences. Can't use the return value, it's non-zero when 924 diff_try_update(&diffio, idx_orig, eap);
702 * there are differences. 925 }
703 * May try twice, first with "-a" and then without. 926
704 */ 927 // force updating cursor position on screen
928 curwin->w_valid_cursor.lnum = 0;
929
930 diff_redraw(TRUE);
931 }
932
933 /*
934 * Do a quick test if "diff" really works. Otherwise it looks like there
935 * are no differences. Can't use the return value, it's non-zero when
936 * there are differences.
937 */
938 static int
939 check_external_diff(diffio_T *diffio)
940 {
941 FILE *fd;
942 int ok;
943 int io_error = FALSE;
944
945 // May try twice, first with "-a" and then without.
705 for (;;) 946 for (;;)
706 { 947 {
707 ok = FALSE; 948 ok = FALSE;
708 fd = mch_fopen((char *)tmp_orig, "w"); 949 fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w");
709 if (fd == NULL) 950 if (fd == NULL)
710 io_error = TRUE; 951 io_error = TRUE;
711 else 952 else
712 { 953 {
713 if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) 954 if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1)
714 io_error = TRUE; 955 io_error = TRUE;
715 fclose(fd); 956 fclose(fd);
716 fd = mch_fopen((char *)tmp_new, "w"); 957 fd = mch_fopen((char *)diffio->dio_new.din_fname, "w");
717 if (fd == NULL) 958 if (fd == NULL)
718 io_error = TRUE; 959 io_error = TRUE;
719 else 960 else
720 { 961 {
721 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) 962 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1)
722 io_error = TRUE; 963 io_error = TRUE;
723 fclose(fd); 964 fclose(fd);
724 diff_file(tmp_orig, tmp_new, tmp_diff); 965 fd = NULL;
725 fd = mch_fopen((char *)tmp_diff, "r"); 966 if (diff_file(diffio) == OK)
967 fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r");
726 if (fd == NULL) 968 if (fd == NULL)
727 io_error = TRUE; 969 io_error = TRUE;
728 else 970 else
729 { 971 {
730 char_u linebuf[LBUFLEN]; 972 char_u linebuf[LBUFLEN];
737 if (STRNCMP(linebuf, "1c1", 3) == 0) 979 if (STRNCMP(linebuf, "1c1", 3) == 0)
738 ok = TRUE; 980 ok = TRUE;
739 } 981 }
740 fclose(fd); 982 fclose(fd);
741 } 983 }
742 mch_remove(tmp_diff); 984 mch_remove(diffio->dio_diff.dout_fname);
743 mch_remove(tmp_new); 985 mch_remove(diffio->dio_new.din_fname);
744 } 986 }
745 mch_remove(tmp_orig); 987 mch_remove(diffio->dio_orig.din_fname);
746 } 988 }
747 989
748 #ifdef FEAT_EVAL 990 #ifdef FEAT_EVAL
749 /* When using 'diffexpr' break here. */ 991 /* When using 'diffexpr' break here. */
750 if (*p_dex != NUL) 992 if (*p_dex != NUL)
783 EMSG(_("E97: Cannot create diffs")); 1025 EMSG(_("E97: Cannot create diffs"));
784 diff_a_works = MAYBE; 1026 diff_a_works = MAYBE;
785 #if defined(MSWIN) 1027 #if defined(MSWIN)
786 diff_bin_works = MAYBE; 1028 diff_bin_works = MAYBE;
787 #endif 1029 #endif
788 goto theend; 1030 return FAIL;
789 } 1031 }
790 1032 return OK;
791 /* :diffupdate! */ 1033 }
792 if (eap != NULL && eap->forceit) 1034
793 for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) 1035 /*
794 { 1036 * Invoke the xdiff function.
795 buf = curtab->tp_diffbuf[idx_new]; 1037 */
796 if (buf_valid(buf)) 1038 static int
797 buf_check_timestamp(buf, FALSE); 1039 diff_file_internal(diffio_T *diffio)
798 } 1040 {
799 1041 xpparam_t param;
800 /* Write the first buffer to a tempfile. */ 1042 xdemitconf_t emit_cfg;
801 buf = curtab->tp_diffbuf[idx_orig]; 1043 xdemitcb_t emit_cb;
802 if (diff_write(buf, tmp_orig) == FAIL) 1044
803 goto theend; 1045 vim_memset(&param, 0, sizeof(param));
804 1046 vim_memset(&emit_cfg, 0, sizeof(emit_cfg));
805 /* Make a difference between the first buffer and every other. */ 1047 vim_memset(&emit_cb, 0, sizeof(emit_cb));
806 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) 1048
807 { 1049 param.flags = diff_algorithm;
808 buf = curtab->tp_diffbuf[idx_new]; 1050
809 if (buf == NULL || buf->b_ml.ml_mfp == NULL) 1051 if (diff_flags & DIFF_IWHITE)
810 continue; /* skip buffer that isn't loaded */ 1052 param.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
811 if (diff_write(buf, tmp_new) == FAIL) 1053
812 continue; 1054 emit_cfg.ctxlen = 0; // don't need any diff_context here
813 diff_file(tmp_orig, tmp_new, tmp_diff); 1055 emit_cb.priv = &diffio->dio_diff;
814 1056 emit_cb.outf = xdiff_out;
815 /* Read the diff output and add each entry to the diff list. */ 1057 if (xdl_diff(&diffio->dio_orig.din_mmfile,
816 diff_read(idx_orig, idx_new, tmp_diff); 1058 &diffio->dio_new.din_mmfile,
817 mch_remove(tmp_diff); 1059 &param, &emit_cfg, &emit_cb) < 0)
818 mch_remove(tmp_new); 1060 {
819 } 1061 EMSG(_("E960: Problem creating the internal diff"));
820 mch_remove(tmp_orig); 1062 return FAIL;
821 1063 }
822 /* force updating cursor position on screen */ 1064 return OK;
823 curwin->w_valid_cursor.lnum = 0;
824
825 diff_redraw(TRUE);
826
827 theend:
828 vim_free(tmp_orig);
829 vim_free(tmp_new);
830 vim_free(tmp_diff);
831 } 1065 }
832 1066
833 /* 1067 /*
834 * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". 1068 * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff".
835 */ 1069 * return OK or FAIL;
836 static void 1070 */
837 diff_file( 1071 static int
838 char_u *tmp_orig, 1072 diff_file(diffio_T *dio)
839 char_u *tmp_new,
840 char_u *tmp_diff)
841 { 1073 {
842 char_u *cmd; 1074 char_u *cmd;
843 size_t len; 1075 size_t len;
1076 char_u *tmp_orig = dio->dio_orig.din_fname;
1077 char_u *tmp_new = dio->dio_new.din_fname;
1078 char_u *tmp_diff = dio->dio_diff.dout_fname;
844 1079
845 #ifdef FEAT_EVAL 1080 #ifdef FEAT_EVAL
846 if (*p_dex != NUL) 1081 if (*p_dex != NUL)
847 /* Use 'diffexpr' to generate the diff file. */ 1082 {
1083 // Use 'diffexpr' to generate the diff file.
848 eval_diff(tmp_orig, tmp_new, tmp_diff); 1084 eval_diff(tmp_orig, tmp_new, tmp_diff);
1085 return OK;
1086 }
849 else 1087 else
850 #endif 1088 #endif
1089 // Use xdiff for generating the diff.
1090 if (dio->dio_internal)
1091 {
1092 return diff_file_internal(dio);
1093 }
1094 else
851 { 1095 {
852 len = STRLEN(tmp_orig) + STRLEN(tmp_new) 1096 len = STRLEN(tmp_orig) + STRLEN(tmp_new)
853 + STRLEN(tmp_diff) + STRLEN(p_srr) + 27; 1097 + STRLEN(tmp_diff) + STRLEN(p_srr) + 27;
854 cmd = alloc((unsigned)len); 1098 cmd = alloc((unsigned)len);
855 if (cmd != NULL) 1099 if (cmd == NULL)
856 { 1100 return FAIL;
857 /* We don't want $DIFF_OPTIONS to get in the way. */ 1101
858 if (getenv("DIFF_OPTIONS")) 1102 // We don't want $DIFF_OPTIONS to get in the way.
859 vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)""); 1103 if (getenv("DIFF_OPTIONS"))
860 1104 vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)"");
861 /* Build the diff command and execute it. Always use -a, binary 1105
862 * differences are of no use. Ignore errors, diff returns 1106 // Build the diff command and execute it. Always use -a, binary
863 * non-zero when differences have been found. */ 1107 // differences are of no use. Ignore errors, diff returns
864 vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s", 1108 // non-zero when differences have been found.
865 diff_a_works == FALSE ? "" : "-a ", 1109 vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s",
1110 diff_a_works == FALSE ? "" : "-a ",
866 #if defined(MSWIN) 1111 #if defined(MSWIN)
867 diff_bin_works == TRUE ? "--binary " : "", 1112 diff_bin_works == TRUE ? "--binary " : "",
868 #else 1113 #else
869 "", 1114 "",
870 #endif 1115 #endif
871 (diff_flags & DIFF_IWHITE) ? "-b " : "", 1116 (diff_flags & DIFF_IWHITE) ? "-b " : "",
872 (diff_flags & DIFF_ICASE) ? "-i " : "", 1117 (diff_flags & DIFF_ICASE) ? "-i " : "",
873 tmp_orig, tmp_new); 1118 tmp_orig, tmp_new);
874 append_redir(cmd, (int)len, p_srr, tmp_diff); 1119 append_redir(cmd, (int)len, p_srr, tmp_diff);
875 block_autocmds(); /* Avoid ShellCmdPost stuff */ 1120 block_autocmds(); // avoid ShellCmdPost stuff
876 (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT); 1121 (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT);
877 unblock_autocmds(); 1122 unblock_autocmds();
878 vim_free(cmd); 1123 vim_free(cmd);
879 } 1124 return OK;
880 } 1125 }
881 } 1126 }
882 1127
883 /* 1128 /*
884 * Create a new version of a file from the current buffer and a diff file. 1129 * Create a new version of a file from the current buffer and a diff file.
1280 /* 1525 /*
1281 * Read the diff output and add each entry to the diff list. 1526 * Read the diff output and add each entry to the diff list.
1282 */ 1527 */
1283 static void 1528 static void
1284 diff_read( 1529 diff_read(
1285 int idx_orig, /* idx of original file */ 1530 int idx_orig, // idx of original file
1286 int idx_new, /* idx of new file */ 1531 int idx_new, // idx of new file
1287 char_u *fname) /* name of diff output file */ 1532 diffout_T *dout) // diff output
1288 { 1533 {
1289 FILE *fd; 1534 FILE *fd = NULL;
1535 int line_idx = 0;
1290 diff_T *dprev = NULL; 1536 diff_T *dprev = NULL;
1291 diff_T *dp = curtab->tp_first_diff; 1537 diff_T *dp = curtab->tp_first_diff;
1292 diff_T *dn, *dpl; 1538 diff_T *dn, *dpl;
1293 long f1, l1, f2, l2;
1294 char_u linebuf[LBUFLEN]; /* only need to hold the diff line */ 1539 char_u linebuf[LBUFLEN]; /* only need to hold the diff line */
1295 int difftype; 1540 char_u *line;
1296 char_u *p;
1297 long off; 1541 long off;
1298 int i; 1542 int i;
1299 linenr_T lnum_orig, lnum_new; 1543 linenr_T lnum_orig, lnum_new;
1300 long count_orig, count_new; 1544 long count_orig, count_new;
1301 int notset = TRUE; /* block "*dp" not set yet */ 1545 int notset = TRUE; /* block "*dp" not set yet */
1302 1546 enum {
1303 fd = mch_fopen((char *)fname, "r"); 1547 DIFF_ED,
1304 if (fd == NULL) 1548 DIFF_UNIFIED,
1305 { 1549 DIFF_NONE
1306 EMSG(_("E98: Cannot read diff output")); 1550 } diffstyle = DIFF_NONE;
1307 return; 1551
1552 if (dout->dout_fname == NULL)
1553 {
1554 diffstyle = DIFF_UNIFIED;
1555 }
1556 else
1557 {
1558 fd = mch_fopen((char *)dout->dout_fname, "r");
1559 if (fd == NULL)
1560 {
1561 EMSG(_("E98: Cannot read diff output"));
1562 return;
1563 }
1308 } 1564 }
1309 1565
1310 for (;;) 1566 for (;;)
1311 { 1567 {
1312 if (tag_fgets(linebuf, LBUFLEN, fd)) 1568 if (fd == NULL)
1313 break; /* end of file */ 1569 {
1314 if (!isdigit(*linebuf)) 1570 if (line_idx >= dout->dout_ga.ga_len)
1315 continue; /* not the start of a diff block */ 1571 break; // did last line
1316 1572 line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
1317 /* This line must be one of three formats:
1318 * {first}[,{last}]c{first}[,{last}]
1319 * {first}a{first}[,{last}]
1320 * {first}[,{last}]d{first}
1321 */
1322 p = linebuf;
1323 f1 = getdigits(&p);
1324 if (*p == ',')
1325 {
1326 ++p;
1327 l1 = getdigits(&p);
1328 } 1573 }
1329 else 1574 else
1330 l1 = f1; 1575 {
1331 if (*p != 'a' && *p != 'c' && *p != 'd') 1576 if (tag_fgets(linebuf, LBUFLEN, fd))
1332 continue; /* invalid diff format */ 1577 break; // end of file
1333 difftype = *p++; 1578 line = linebuf;
1334 f2 = getdigits(&p); 1579 }
1335 if (*p == ',') 1580
1336 { 1581 if (diffstyle == DIFF_NONE)
1337 ++p; 1582 {
1338 l2 = getdigits(&p); 1583 // Determine diff style.
1584 // ed like diff looks like this:
1585 // {first}[,{last}]c{first}[,{last}]
1586 // {first}a{first}[,{last}]
1587 // {first}[,{last}]d{first}
1588 //
1589 // unified diff looks like this:
1590 // --- file1 2018-03-20 13:23:35.783153140 +0100
1591 // +++ file2 2018-03-20 13:23:41.183156066 +0100
1592 // @@ -1,3 +1,5 @@
1593 if (isdigit(*line))
1594 diffstyle = DIFF_ED;
1595 else if ((STRNCMP(line, "@@ ", 3) == 0))
1596 diffstyle = DIFF_UNIFIED;
1597 else if ((STRNCMP(line, "--- ", 4) == 0)
1598 && (tag_fgets(linebuf, LBUFLEN, fd) == 0)
1599 && (STRNCMP(line, "+++ ", 4) == 0)
1600 && (tag_fgets(linebuf, LBUFLEN, fd) == 0)
1601 && (STRNCMP(line, "@@ ", 3) == 0))
1602 diffstyle = DIFF_UNIFIED;
1603 }
1604
1605 if (diffstyle == DIFF_ED)
1606 {
1607 if (!isdigit(*line))
1608 continue; // not the start of a diff block
1609 if (parse_diff_ed(line, &lnum_orig, &count_orig,
1610 &lnum_new, &count_new) == FAIL)
1611 continue;
1612 }
1613 else if (diffstyle == DIFF_UNIFIED)
1614 {
1615 if (STRNCMP(line, "@@ ", 3) != 0)
1616 continue; // not the start of a diff block
1617 if (parse_diff_unified(line, &lnum_orig, &count_orig,
1618 &lnum_new, &count_new) == FAIL)
1619 continue;
1339 } 1620 }
1340 else 1621 else
1341 l2 = f2; 1622 {
1342 if (l1 < f1 || l2 < f2) 1623 EMSG(_("E959: Invalid diff format."));
1343 continue; /* invalid line range */ 1624 break;
1344 1625 }
1345 if (difftype == 'a') 1626
1346 { 1627 // Go over blocks before the change, for which orig and new are equal.
1347 lnum_orig = f1 + 1; 1628 // Copy blocks from orig to new.
1348 count_orig = 0;
1349 }
1350 else
1351 {
1352 lnum_orig = f1;
1353 count_orig = l1 - f1 + 1;
1354 }
1355 if (difftype == 'd')
1356 {
1357 lnum_new = f2 + 1;
1358 count_new = 0;
1359 }
1360 else
1361 {
1362 lnum_new = f2;
1363 count_new = l2 - f2 + 1;
1364 }
1365
1366 /* Go over blocks before the change, for which orig and new are equal.
1367 * Copy blocks from orig to new. */
1368 while (dp != NULL 1629 while (dp != NULL
1369 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) 1630 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
1370 { 1631 {
1371 if (notset) 1632 if (notset)
1372 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1633 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1377 1638
1378 if (dp != NULL 1639 if (dp != NULL
1379 && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig] 1640 && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]
1380 && lnum_orig + count_orig >= dp->df_lnum[idx_orig]) 1641 && lnum_orig + count_orig >= dp->df_lnum[idx_orig])
1381 { 1642 {
1382 /* New block overlaps with existing block(s). 1643 // New block overlaps with existing block(s).
1383 * First find last block that overlaps. */ 1644 // First find last block that overlaps.
1384 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) 1645 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next)
1385 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) 1646 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig])
1386 break; 1647 break;
1387 1648
1388 /* If the newly found block starts before the old one, set the 1649 // If the newly found block starts before the old one, set the
1389 * start back a number of lines. */ 1650 // start back a number of lines.
1390 off = dp->df_lnum[idx_orig] - lnum_orig; 1651 off = dp->df_lnum[idx_orig] - lnum_orig;
1391 if (off > 0) 1652 if (off > 0)
1392 { 1653 {
1393 for (i = idx_orig; i < idx_new; ++i) 1654 for (i = idx_orig; i < idx_new; ++i)
1394 if (curtab->tp_diffbuf[i] != NULL) 1655 if (curtab->tp_diffbuf[i] != NULL)
1396 dp->df_lnum[idx_new] = lnum_new; 1657 dp->df_lnum[idx_new] = lnum_new;
1397 dp->df_count[idx_new] = count_new; 1658 dp->df_count[idx_new] = count_new;
1398 } 1659 }
1399 else if (notset) 1660 else if (notset)
1400 { 1661 {
1401 /* new block inside existing one, adjust new block */ 1662 // new block inside existing one, adjust new block
1402 dp->df_lnum[idx_new] = lnum_new + off; 1663 dp->df_lnum[idx_new] = lnum_new + off;
1403 dp->df_count[idx_new] = count_new - off; 1664 dp->df_count[idx_new] = count_new - off;
1404 } 1665 }
1405 else 1666 else
1406 /* second overlap of new block with existing block */ 1667 // second overlap of new block with existing block
1407 dp->df_count[idx_new] += count_new - count_orig 1668 dp->df_count[idx_new] += count_new - count_orig
1408 + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig] 1669 + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]
1409 - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]); 1670 - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]);
1410 1671
1411 /* Adjust the size of the block to include all the lines to the 1672 // Adjust the size of the block to include all the lines to the
1412 * end of the existing block or the new diff, whatever ends last. */ 1673 // end of the existing block or the new diff, whatever ends last.
1413 off = (lnum_orig + count_orig) 1674 off = (lnum_orig + count_orig)
1414 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); 1675 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
1415 if (off < 0) 1676 if (off < 0)
1416 { 1677 {
1417 /* new change ends in existing block, adjust the end if not 1678 // new change ends in existing block, adjust the end if not
1418 * done already */ 1679 // done already
1419 if (notset) 1680 if (notset)
1420 dp->df_count[idx_new] += -off; 1681 dp->df_count[idx_new] += -off;
1421 off = 0; 1682 off = 0;
1422 } 1683 }
1423 for (i = idx_orig; i < idx_new; ++i) 1684 for (i = idx_orig; i < idx_new; ++i)
1424 if (curtab->tp_diffbuf[i] != NULL) 1685 if (curtab->tp_diffbuf[i] != NULL)
1425 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] 1686 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
1426 - dp->df_lnum[i] + off; 1687 - dp->df_lnum[i] + off;
1427 1688
1428 /* Delete the diff blocks that have been merged into one. */ 1689 // Delete the diff blocks that have been merged into one.
1429 dn = dp->df_next; 1690 dn = dp->df_next;
1430 dp->df_next = dpl->df_next; 1691 dp->df_next = dpl->df_next;
1431 while (dn != dp->df_next) 1692 while (dn != dp->df_next)
1432 { 1693 {
1433 dpl = dn->df_next; 1694 dpl = dn->df_next;
1435 dn = dpl; 1696 dn = dpl;
1436 } 1697 }
1437 } 1698 }
1438 else 1699 else
1439 { 1700 {
1440 /* Allocate a new diffblock. */ 1701 // Allocate a new diffblock.
1441 dp = diff_alloc_new(curtab, dprev, dp); 1702 dp = diff_alloc_new(curtab, dprev, dp);
1442 if (dp == NULL) 1703 if (dp == NULL)
1443 goto done; 1704 goto done;
1444 1705
1445 dp->df_lnum[idx_orig] = lnum_orig; 1706 dp->df_lnum[idx_orig] = lnum_orig;
1446 dp->df_count[idx_orig] = count_orig; 1707 dp->df_count[idx_orig] = count_orig;
1447 dp->df_lnum[idx_new] = lnum_new; 1708 dp->df_lnum[idx_new] = lnum_new;
1448 dp->df_count[idx_new] = count_new; 1709 dp->df_count[idx_new] = count_new;
1449 1710
1450 /* Set values for other buffers, these must be equal to the 1711 // Set values for other buffers, these must be equal to the
1451 * original buffer, otherwise there would have been a change 1712 // original buffer, otherwise there would have been a change
1452 * already. */ 1713 // already.
1453 for (i = idx_orig + 1; i < idx_new; ++i) 1714 for (i = idx_orig + 1; i < idx_new; ++i)
1454 if (curtab->tp_diffbuf[i] != NULL) 1715 if (curtab->tp_diffbuf[i] != NULL)
1455 diff_copy_entry(dprev, dp, idx_orig, i); 1716 diff_copy_entry(dprev, dp, idx_orig, i);
1456 } 1717 }
1457 notset = FALSE; /* "*dp" has been set */ 1718 notset = FALSE; // "*dp" has been set
1458 } 1719 }
1459 1720
1460 /* for remaining diff blocks orig and new are equal */ 1721 // for remaining diff blocks orig and new are equal
1461 while (dp != NULL) 1722 while (dp != NULL)
1462 { 1723 {
1463 if (notset) 1724 if (notset)
1464 diff_copy_entry(dprev, dp, idx_orig, idx_new); 1725 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1465 dprev = dp; 1726 dprev = dp;
1466 dp = dp->df_next; 1727 dp = dp->df_next;
1467 notset = TRUE; 1728 notset = TRUE;
1468 } 1729 }
1469 1730
1470 done: 1731 done:
1471 fclose(fd); 1732 if (fd != NULL)
1733 fclose(fd);
1472 } 1734 }
1473 1735
1474 /* 1736 /*
1475 * Copy an entry at "dp" from "idx_orig" to "idx_new". 1737 * Copy an entry at "dp" from "idx_orig" to "idx_new".
1476 */ 1738 */
1858 { 2120 {
1859 char_u *p; 2121 char_u *p;
1860 int diff_context_new = 6; 2122 int diff_context_new = 6;
1861 int diff_flags_new = 0; 2123 int diff_flags_new = 0;
1862 int diff_foldcolumn_new = 2; 2124 int diff_foldcolumn_new = 2;
2125 long diff_algorithm_new = 0;
1863 tabpage_T *tp; 2126 tabpage_T *tp;
1864 2127
1865 p = p_dip; 2128 p = p_dip;
1866 while (*p != NUL) 2129 while (*p != NUL)
1867 { 2130 {
1903 else if (STRNCMP(p, "hiddenoff", 9) == 0) 2166 else if (STRNCMP(p, "hiddenoff", 9) == 0)
1904 { 2167 {
1905 p += 9; 2168 p += 9;
1906 diff_flags_new |= DIFF_HIDDEN_OFF; 2169 diff_flags_new |= DIFF_HIDDEN_OFF;
1907 } 2170 }
2171 else if (STRNCMP(p, "indent-heuristic", 16) == 0)
2172 {
2173 p += 16;
2174 diff_algorithm_new |= XDF_INDENT_HEURISTIC;
2175 }
2176 else if (STRNCMP(p, "internal", 8) == 0)
2177 {
2178 p += 8;
2179 diff_flags_new |= DIFF_INTERNAL;
2180 }
2181 else if (STRNCMP(p, "algorithm:", 10) == 0)
2182 {
2183 p += 10;
2184 if (STRNCMP(p, "myers", 5) == 0)
2185 {
2186 p += 5;
2187 diff_algorithm_new = 0;
2188 }
2189 else if (STRNCMP(p, "minimal", 7) == 0)
2190 {
2191 p += 7;
2192 diff_algorithm_new = XDF_NEED_MINIMAL;
2193 }
2194 else if (STRNCMP(p, "patience", 8) == 0)
2195 {
2196 p += 8;
2197 diff_algorithm_new = XDF_PATIENCE_DIFF;
2198 }
2199 else if (STRNCMP(p, "histogram", 9) == 0)
2200 {
2201 p += 9;
2202 diff_algorithm_new = XDF_HISTOGRAM_DIFF;
2203 }
2204 }
2205
1908 if (*p != ',' && *p != NUL) 2206 if (*p != ',' && *p != NUL)
1909 return FAIL; 2207 return FAIL;
1910 if (*p == ',') 2208 if (*p == ',')
1911 ++p; 2209 ++p;
1912 } 2210 }
1914 /* Can't have both "horizontal" and "vertical". */ 2212 /* Can't have both "horizontal" and "vertical". */
1915 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) 2213 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL))
1916 return FAIL; 2214 return FAIL;
1917 2215
1918 /* If "icase" or "iwhite" was added or removed, need to update the diff. */ 2216 /* If "icase" or "iwhite" was added or removed, need to update the diff. */
1919 if (diff_flags != diff_flags_new) 2217 if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new)
1920 FOR_ALL_TABPAGES(tp) 2218 FOR_ALL_TABPAGES(tp)
1921 tp->tp_diff_invalid = TRUE; 2219 tp->tp_diff_invalid = TRUE;
1922 2220
1923 diff_flags = diff_flags_new; 2221 diff_flags = diff_flags_new;
1924 diff_context = diff_context_new; 2222 diff_context = diff_context_new;
1925 diff_foldcolumn = diff_foldcolumn_new; 2223 diff_foldcolumn = diff_foldcolumn_new;
2224 diff_algorithm = diff_algorithm_new;
1926 2225
1927 diff_redraw(TRUE); 2226 diff_redraw(TRUE);
1928 2227
1929 /* recompute the scroll binding with the new option value, may 2228 /* recompute the scroll binding with the new option value, may
1930 * remove or add filler lines */ 2229 * remove or add filler lines */
2688 if (n > dp->df_lnum[i] + dp->df_count[i]) 2987 if (n > dp->df_lnum[i] + dp->df_count[i])
2689 n = dp->df_lnum[i] + dp->df_count[i]; 2988 n = dp->df_lnum[i] + dp->df_count[i];
2690 return n; 2989 return n;
2691 } 2990 }
2692 2991
2992 /*
2993 * Handle an ED style diff line.
2994 * Return FAIL if the line does not contain diff info.
2995 */
2996 static int
2997 parse_diff_ed(
2998 char_u *line,
2999 linenr_T *lnum_orig,
3000 long *count_orig,
3001 linenr_T *lnum_new,
3002 long *count_new)
3003 {
3004 char_u *p;
3005 long f1, l1, f2, l2;
3006 int difftype;
3007
3008 // The line must be one of three formats:
3009 // change: {first}[,{last}]c{first}[,{last}]
3010 // append: {first}a{first}[,{last}]
3011 // delete: {first}[,{last}]d{first}
3012 p = line;
3013 f1 = getdigits(&p);
3014 if (*p == ',')
3015 {
3016 ++p;
3017 l1 = getdigits(&p);
3018 }
3019 else
3020 l1 = f1;
3021 if (*p != 'a' && *p != 'c' && *p != 'd')
3022 return FAIL; // invalid diff format
3023 difftype = *p++;
3024 f2 = getdigits(&p);
3025 if (*p == ',')
3026 {
3027 ++p;
3028 l2 = getdigits(&p);
3029 }
3030 else
3031 l2 = f2;
3032 if (l1 < f1 || l2 < f2)
3033 return FAIL;
3034
3035 if (difftype == 'a')
3036 {
3037 *lnum_orig = f1 + 1;
3038 *count_orig = 0;
3039 }
3040 else
3041 {
3042 *lnum_orig = f1;
3043 *count_orig = l1 - f1 + 1;
3044 }
3045 if (difftype == 'd')
3046 {
3047 *lnum_new = f2 + 1;
3048 *count_new = 0;
3049 }
3050 else
3051 {
3052 *lnum_new = f2;
3053 *count_new = l2 - f2 + 1;
3054 }
3055 return OK;
3056 }
3057
3058 /*
3059 * Parses unified diff with zero(!) context lines.
3060 * Return FAIL if there is no diff information in "line".
3061 */
3062 static int
3063 parse_diff_unified(
3064 char_u *line,
3065 linenr_T *lnum_orig,
3066 long *count_orig,
3067 linenr_T *lnum_new,
3068 long *count_new)
3069 {
3070 char_u *p;
3071 long oldline, oldcount, newline, newcount;
3072
3073 // Parse unified diff hunk header:
3074 // @@ -oldline,oldcount +newline,newcount @@
3075 p = line;
3076 if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-')
3077 {
3078 oldline = getdigits(&p);
3079 if (*p == ',')
3080 {
3081 ++p;
3082 oldcount = getdigits(&p);
3083 }
3084 else
3085 oldcount = 1;
3086 if (*p++ == ' ' && *p++ == '+')
3087 {
3088 newline = getdigits(&p);
3089 if (*p == ',')
3090 {
3091 ++p;
3092 newcount = getdigits(&p);
3093 }
3094 else
3095 newcount = 1;
3096 }
3097 else
3098 return FAIL; // invalid diff format
3099
3100 if (oldcount == 0)
3101 oldline += 1;
3102 if (newcount == 0)
3103 newline += 1;
3104 if (newline == 0)
3105 newline = 1;
3106
3107 *lnum_orig = oldline;
3108 *count_orig = oldcount;
3109 *lnum_new = newline;
3110 *count_new = newcount;
3111
3112 return OK;
3113 }
3114
3115 return FAIL;
3116 }
3117
3118 /*
3119 * Callback function for the xdl_diff() function.
3120 * Stores the diff output in a grow array.
3121 */
3122 static int
3123 xdiff_out(void *priv, mmbuffer_t *mb, int nbuf)
3124 {
3125 diffout_T *dout = (diffout_T *)priv;
3126 int i;
3127 char_u *p;
3128
3129 for (i = 0; i < nbuf; i++)
3130 {
3131 // We are only interested in the header lines, skip text lines.
3132 if (STRNCMP(mb[i].ptr, "@@ ", 3) != 0)
3133 continue;
3134 if (ga_grow(&dout->dout_ga, 1) == FAIL)
3135 return -1;
3136 p = vim_strnsave((char_u *)mb[i].ptr, mb[i].size);
3137 if (p == NULL)
3138 return -1;
3139 ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
3140 }
3141 return 0;
3142 }
3143
2693 #endif /* FEAT_DIFF */ 3144 #endif /* FEAT_DIFF */