Mercurial > vim
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(¶m, 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 ¶m, &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 */ |