302
ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
303
else if (!strcmp(sb.buf, "ignore-all-space"))
304
ret |= XDF_IGNORE_WHITESPACE;
305
+ else if (!strcmp(sb.buf, "allow-indentation-change"))
306
+ ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
307
else
308
error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
309
310
strbuf_release(&sb);
311
}
312
313
+ if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
314
+ (ret & XDF_WHITESPACE_FLAGS))
315
+ die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
316
+
317
string_list_clear(&l, 0);
318
319
return ret;
743
struct hashmap_entry ent;
744
const struct emitted_diff_symbol *es;
745
struct moved_entry *next_line;
746
+ struct ws_delta *wsd;
747
+};
748
+
749
+/**
750
+ * The struct ws_delta holds white space differences between moved lines, i.e.
751
+ * between '+' and '-' lines that have been detected to be a move.
752
+ * The string contains the difference in leading white spaces, before the
753
+ * rest of the line is compared using the white space config for move
754
+ * coloring. The current_longer indicates if the first string in the
755
+ * comparision is longer than the second.
756
+ */
757
+struct ws_delta {
758
+ char *string;
759
+ unsigned int current_longer : 1;
760
};
761
+#define WS_DELTA_INIT { NULL, 0 }
762
+
763
+static int compute_ws_delta(const struct emitted_diff_symbol *a,
764
+ const struct emitted_diff_symbol *b,
765
+ struct ws_delta *out)
766
+{
767
+ const struct emitted_diff_symbol *longer = a->len > b->len ? a : b;
768
+ const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
769
+ int d = longer->len - shorter->len;
770
+
771
+ out->string = xmemdupz(longer->line, d);
772
+ out->current_longer = (a == longer);
773
+
774
+ return !strncmp(longer->line + d, shorter->line, shorter->len);
775
+}
776
+
777
+static int cmp_in_block_with_wsd(const struct diff_options *o,
778
+ const struct moved_entry *cur,
779
+ const struct moved_entry *match,
780
+ struct moved_entry *pmb,
781
+ int n)
782
+{
783
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
784
+ int al = cur->es->len, cl = l->len;
785
+ const char *a = cur->es->line,
786
+ *b = match->es->line,
787
+ *c = l->line;
788
+
789
+ int wslen;
790
+
791
+ /*
792
+ * We need to check if 'cur' is equal to 'match'.
793
+ * As those are from the same (+/-) side, we do not need to adjust for
794
+ * indent changes. However these were found using fuzzy matching
795
+ * so we do have to check if they are equal.
796
+ */
797
+ if (strcmp(a, b))
798
+ return 1;
799
+
800
+ if (!pmb->wsd)
801
+ /*
802
+ * No white space delta was carried forward? This can happen
803
+ * when we exit early in this function and do not carry
804
+ * forward ws.
805
+ */
806
+ return 1;
807
+
808
+ /*
809
+ * The indent changes of the block are known and carried forward in
810
+ * pmb->wsd; however we need to check if the indent changes of the
811
+ * current line are still the same as before.
812
+ *
813
+ * To do so we need to compare 'l' to 'cur', adjusting the
814
+ * one of them for the white spaces, depending which was longer.
815
+ */
816
+
817
+ wslen = strlen(pmb->wsd->string);
818
+ if (pmb->wsd->current_longer) {
819
+ c += wslen;
820
+ cl -= wslen;
821
+ } else {
822
+ a += wslen;
823
+ al -= wslen;
824
+ }
825
+
826
+ if (strcmp(a, c))
827
+ return 1;
828
+
829
+ return 0;
830
+}
831
832
static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
833
const void *entry,
840
unsigned flags = diffopt->color_moved_ws_handling
841
& XDF_WHITESPACE_FLAGS;
842
843
+ if (diffopt->color_moved_ws_handling &
844
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
845
+ /*
846
+ * As there is not specific white space config given,
847
+ * we'd need to check for a new block, so ignore all
848
+ * white space. The setup of the white space
849
+ * configuration for the next block is done else where
850
+ */
851
+ flags |= XDF_IGNORE_WHITESPACE;
852
+
853
return !xdiff_compare_lines(a->es->line, a->es->len,
854
b->es->line, b->es->len,
855
flags);
865
ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
866
ret->es = l;
867
ret->next_line = NULL;
868
+ ret->wsd = NULL;
869
870
return ret;
871
}
921
}
922
}
923
924
+static void pmb_advance_or_null_multi_match(struct diff_options *o,
925
+ struct moved_entry *match,
926
+ struct hashmap *hm,
927
+ struct moved_entry **pmb,
928
+ int pmb_nr, int n)
929
+{
930
+ int i;
931
+ char *got_match = xcalloc(1, pmb_nr);
932
+
933
+ for (; match; match = hashmap_get_next(hm, match)) {
934
+ for (i = 0; i < pmb_nr; i++) {
935
+ struct moved_entry *prev = pmb[i];
936
+ struct moved_entry *cur = (prev && prev->next_line) ?
937
+ prev->next_line : NULL;
938
+ if (!cur)
939
+ continue;
940
+ if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
941
+ got_match[i] |= 1;
942
+ }
943
+ }
944
+
945
+ for (i = 0; i < pmb_nr; i++) {
946
+ if (got_match[i]) {
947
+ /* Carry the white space delta forward */
948
+ pmb[i]->next_line->wsd = pmb[i]->wsd;
949
+ pmb[i] = pmb[i]->next_line;
950
+ } else
951
+ pmb[i] = NULL;
952
+ }
953
+}
954
+
955
static int shrink_potential_moved_blocks(struct moved_entry **pmb,
956
int pmb_nr)
957
{
969
970
if (lp < pmb_nr && rp > -1 && lp < rp) {
971
pmb[lp] = pmb[rp];
972
+ if (pmb[rp]->wsd) {
973
+ free(pmb[rp]->wsd->string);
974
+ FREE_AND_NULL(pmb[rp]->wsd);
975
+ }
976
pmb[rp] = NULL;
977
rp--;
978
lp++;
1060
if (o->color_moved == COLOR_MOVED_PLAIN)
1061
continue;
1062
927
- pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
1063
+ if (o->color_moved_ws_handling &
1064
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
1065
+ pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
1066
+ else
1067
+ pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
1068
1069
pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
1070
1075
*/
1076
for (; match; match = hashmap_get_next(hm, match)) {
1077
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
938
- pmb[pmb_nr++] = match;
1078
+ if (o->color_moved_ws_handling &
1079
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
1080
+ struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
1081
+ if (compute_ws_delta(l, match->es, wsd)) {
1082
+ match->wsd = wsd;
1083
+ pmb[pmb_nr++] = match;
1084
+ } else
1085
+ free(wsd);
1086
+ } else {
1087
+ pmb[pmb_nr++] = match;
1088
+ }
1089
}
1090
1091
flipped_block = (flipped_block + 1) % 2;
5733
if (o->color_moved) {
5734
struct hashmap add_lines, del_lines;
5735
5736
+ if (o->color_moved_ws_handling &
5737
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
5738
+ o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
5739
+
5740
hashmap_init(&del_lines, moved_entry_cmp, o, 0);
5741
hashmap_init(&add_lines, moved_entry_cmp, o, 0);
5742