@@ -622,7 +622,8 @@ struct diff_words_style diff_words_styles[] = {
622
622
struct diff_words_data {
623
623
struct diff_words_buffer minus , plus ;
624
624
const char * current_plus ;
625
- FILE * file ;
625
+ int last_minus ;
626
+ struct diff_options * opt ;
626
627
regex_t * word_regex ;
627
628
enum diff_words_type type ;
628
629
struct diff_words_style * style ;
@@ -631,10 +632,15 @@ struct diff_words_data {
631
632
static int fn_out_diff_words_write_helper (FILE * fp ,
632
633
struct diff_words_style_elem * st_el ,
633
634
const char * newline ,
634
- size_t count , const char * buf )
635
+ size_t count , const char * buf ,
636
+ const char * line_prefix )
635
637
{
638
+ int print = 0 ;
639
+
636
640
while (count ) {
637
641
char * p = memchr (buf , '\n' , count );
642
+ if (print )
643
+ fputs (line_prefix , fp );
638
644
if (p != buf ) {
639
645
if (st_el -> color && fputs (st_el -> color , fp ) < 0 )
640
646
return -1 ;
@@ -652,21 +658,74 @@ static int fn_out_diff_words_write_helper(FILE *fp,
652
658
return -1 ;
653
659
count -= p + 1 - buf ;
654
660
buf = p + 1 ;
661
+ print = 1 ;
655
662
}
656
663
return 0 ;
657
664
}
658
665
666
+ /*
667
+ * '--color-words' algorithm can be described as:
668
+ *
669
+ * 1. collect a the minus/plus lines of a diff hunk, divided into
670
+ * minus-lines and plus-lines;
671
+ *
672
+ * 2. break both minus-lines and plus-lines into words and
673
+ * place them into two mmfile_t with one word for each line;
674
+ *
675
+ * 3. use xdiff to run diff on the two mmfile_t to get the words level diff;
676
+ *
677
+ * And for the common parts of the both file, we output the plus side text.
678
+ * diff_words->current_plus is used to trace the current position of the plus file
679
+ * which printed. diff_words->last_minus is used to trace the last minus word
680
+ * printed.
681
+ *
682
+ * For '--graph' to work with '--color-words', we need to output the graph prefix
683
+ * on each line of color words output. Generally, there are two conditions on
684
+ * which we should output the prefix.
685
+ *
686
+ * 1. diff_words->last_minus == 0 &&
687
+ * diff_words->current_plus == diff_words->plus.text.ptr
688
+ *
689
+ * that is: the plus text must start as a new line, and if there is no minus
690
+ * word printed, a graph prefix must be printed.
691
+ *
692
+ * 2. diff_words->current_plus > diff_words->plus.text.ptr &&
693
+ * *(diff_words->current_plus - 1) == '\n'
694
+ *
695
+ * that is: a graph prefix must be printed following a '\n'
696
+ */
697
+ static int color_words_output_graph_prefix (struct diff_words_data * diff_words )
698
+ {
699
+ if ((diff_words -> last_minus == 0 &&
700
+ diff_words -> current_plus == diff_words -> plus .text .ptr ) ||
701
+ (diff_words -> current_plus > diff_words -> plus .text .ptr &&
702
+ * (diff_words -> current_plus - 1 ) == '\n' )) {
703
+ return 1 ;
704
+ } else {
705
+ return 0 ;
706
+ }
707
+ }
708
+
659
709
static void fn_out_diff_words_aux (void * priv , char * line , unsigned long len )
660
710
{
661
711
struct diff_words_data * diff_words = priv ;
662
712
struct diff_words_style * style = diff_words -> style ;
663
713
int minus_first , minus_len , plus_first , plus_len ;
664
714
const char * minus_begin , * minus_end , * plus_begin , * plus_end ;
715
+ struct diff_options * opt = diff_words -> opt ;
716
+ struct strbuf * msgbuf ;
717
+ char * line_prefix = "" ;
665
718
666
719
if (line [0 ] != '@' || parse_hunk_header (line , len ,
667
720
& minus_first , & minus_len , & plus_first , & plus_len ))
668
721
return ;
669
722
723
+ assert (opt );
724
+ if (opt -> output_prefix ) {
725
+ msgbuf = opt -> output_prefix (opt , opt -> output_prefix_data );
726
+ line_prefix = msgbuf -> buf ;
727
+ }
728
+
670
729
/* POSIX requires that first be decremented by one if len == 0... */
671
730
if (minus_len ) {
672
731
minus_begin = diff_words -> minus .orig [minus_first ].begin ;
@@ -682,21 +741,32 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
682
741
} else
683
742
plus_begin = plus_end = diff_words -> plus .orig [plus_first ].end ;
684
743
685
- if (diff_words -> current_plus != plus_begin )
686
- fn_out_diff_words_write_helper (diff_words -> file ,
744
+ if (color_words_output_graph_prefix (diff_words )) {
745
+ fputs (line_prefix , diff_words -> opt -> file );
746
+ }
747
+ if (diff_words -> current_plus != plus_begin ) {
748
+ fn_out_diff_words_write_helper (diff_words -> opt -> file ,
687
749
& style -> ctx , style -> newline ,
688
750
plus_begin - diff_words -> current_plus ,
689
- diff_words -> current_plus );
690
- if (minus_begin != minus_end )
691
- fn_out_diff_words_write_helper (diff_words -> file ,
751
+ diff_words -> current_plus , line_prefix );
752
+ if (* (plus_begin - 1 ) == '\n' )
753
+ fputs (line_prefix , diff_words -> opt -> file );
754
+ }
755
+ if (minus_begin != minus_end ) {
756
+ fn_out_diff_words_write_helper (diff_words -> opt -> file ,
692
757
& style -> old , style -> newline ,
693
- minus_end - minus_begin , minus_begin );
694
- if (plus_begin != plus_end )
695
- fn_out_diff_words_write_helper (diff_words -> file ,
758
+ minus_end - minus_begin , minus_begin ,
759
+ line_prefix );
760
+ }
761
+ if (plus_begin != plus_end ) {
762
+ fn_out_diff_words_write_helper (diff_words -> opt -> file ,
696
763
& style -> new , style -> newline ,
697
- plus_end - plus_begin , plus_begin );
764
+ plus_end - plus_begin , plus_begin ,
765
+ line_prefix );
766
+ }
698
767
699
768
diff_words -> current_plus = plus_end ;
769
+ diff_words -> last_minus = minus_first ;
700
770
}
701
771
702
772
/* This function starts looking at *begin, and returns 0 iff a word was found. */
@@ -777,16 +847,29 @@ static void diff_words_show(struct diff_words_data *diff_words)
777
847
mmfile_t minus , plus ;
778
848
struct diff_words_style * style = diff_words -> style ;
779
849
850
+ struct diff_options * opt = diff_words -> opt ;
851
+ struct strbuf * msgbuf ;
852
+ char * line_prefix = "" ;
853
+
854
+ assert (opt );
855
+ if (opt -> output_prefix ) {
856
+ msgbuf = opt -> output_prefix (opt , opt -> output_prefix_data );
857
+ line_prefix = msgbuf -> buf ;
858
+ }
859
+
780
860
/* special case: only removal */
781
861
if (!diff_words -> plus .text .size ) {
782
- fn_out_diff_words_write_helper (diff_words -> file ,
862
+ fputs (line_prefix , diff_words -> opt -> file );
863
+ fn_out_diff_words_write_helper (diff_words -> opt -> file ,
783
864
& style -> old , style -> newline ,
784
- diff_words -> minus .text .size , diff_words -> minus .text .ptr );
865
+ diff_words -> minus .text .size ,
866
+ diff_words -> minus .text .ptr , line_prefix );
785
867
diff_words -> minus .text .size = 0 ;
786
868
return ;
787
869
}
788
870
789
871
diff_words -> current_plus = diff_words -> plus .text .ptr ;
872
+ diff_words -> last_minus = 0 ;
790
873
791
874
memset (& xpp , 0 , sizeof (xpp ));
792
875
memset (& xecfg , 0 , sizeof (xecfg ));
@@ -800,11 +883,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
800
883
free (minus .ptr );
801
884
free (plus .ptr );
802
885
if (diff_words -> current_plus != diff_words -> plus .text .ptr +
803
- diff_words -> plus .text .size )
804
- fn_out_diff_words_write_helper (diff_words -> file ,
886
+ diff_words -> plus .text .size ) {
887
+ if (color_words_output_graph_prefix (diff_words ))
888
+ fputs (line_prefix , diff_words -> opt -> file );
889
+ fn_out_diff_words_write_helper (diff_words -> opt -> file ,
805
890
& style -> ctx , style -> newline ,
806
891
diff_words -> plus .text .ptr + diff_words -> plus .text .size
807
- - diff_words -> current_plus , diff_words -> current_plus );
892
+ - diff_words -> current_plus , diff_words -> current_plus ,
893
+ line_prefix );
894
+ }
808
895
diff_words -> minus .text .size = diff_words -> plus .text .size = 0 ;
809
896
}
810
897
@@ -1902,8 +1989,8 @@ static void builtin_diff(const char *name_a,
1902
1989
1903
1990
ecbdata .diff_words =
1904
1991
xcalloc (1 , sizeof (struct diff_words_data ));
1905
- ecbdata .diff_words -> file = o -> file ;
1906
1992
ecbdata .diff_words -> type = o -> word_diff ;
1993
+ ecbdata .diff_words -> opt = o ;
1907
1994
if (!o -> word_regex )
1908
1995
o -> word_regex = userdiff_word_regex (one );
1909
1996
if (!o -> word_regex )
0 commit comments