@@ -622,7 +622,8 @@ struct diff_words_style diff_words_styles[] = {
622622struct diff_words_data {
623623 struct diff_words_buffer minus , plus ;
624624 const char * current_plus ;
625- FILE * file ;
625+ int last_minus ;
626+ struct diff_options * opt ;
626627 regex_t * word_regex ;
627628 enum diff_words_type type ;
628629 struct diff_words_style * style ;
@@ -631,10 +632,15 @@ struct diff_words_data {
631632static int fn_out_diff_words_write_helper (FILE * fp ,
632633 struct diff_words_style_elem * st_el ,
633634 const char * newline ,
634- size_t count , const char * buf )
635+ size_t count , const char * buf ,
636+ const char * line_prefix )
635637{
638+ int print = 0 ;
639+
636640 while (count ) {
637641 char * p = memchr (buf , '\n' , count );
642+ if (print )
643+ fputs (line_prefix , fp );
638644 if (p != buf ) {
639645 if (st_el -> color && fputs (st_el -> color , fp ) < 0 )
640646 return -1 ;
@@ -652,21 +658,74 @@ static int fn_out_diff_words_write_helper(FILE *fp,
652658 return -1 ;
653659 count -= p + 1 - buf ;
654660 buf = p + 1 ;
661+ print = 1 ;
655662 }
656663 return 0 ;
657664}
658665
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+
659709static void fn_out_diff_words_aux (void * priv , char * line , unsigned long len )
660710{
661711 struct diff_words_data * diff_words = priv ;
662712 struct diff_words_style * style = diff_words -> style ;
663713 int minus_first , minus_len , plus_first , plus_len ;
664714 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 = "" ;
665718
666719 if (line [0 ] != '@' || parse_hunk_header (line , len ,
667720 & minus_first , & minus_len , & plus_first , & plus_len ))
668721 return ;
669722
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+
670729 /* POSIX requires that first be decremented by one if len == 0... */
671730 if (minus_len ) {
672731 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)
682741 } else
683742 plus_begin = plus_end = diff_words -> plus .orig [plus_first ].end ;
684743
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 ,
687749 & style -> ctx , style -> newline ,
688750 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 ,
692757 & 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 ,
696763 & style -> new , style -> newline ,
697- plus_end - plus_begin , plus_begin );
764+ plus_end - plus_begin , plus_begin ,
765+ line_prefix );
766+ }
698767
699768 diff_words -> current_plus = plus_end ;
769+ diff_words -> last_minus = minus_first ;
700770}
701771
702772/* 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)
777847 mmfile_t minus , plus ;
778848 struct diff_words_style * style = diff_words -> style ;
779849
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+
780860 /* special case: only removal */
781861 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 ,
783864 & 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 );
785867 diff_words -> minus .text .size = 0 ;
786868 return ;
787869 }
788870
789871 diff_words -> current_plus = diff_words -> plus .text .ptr ;
872+ diff_words -> last_minus = 0 ;
790873
791874 memset (& xpp , 0 , sizeof (xpp ));
792875 memset (& xecfg , 0 , sizeof (xecfg ));
@@ -800,11 +883,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
800883 free (minus .ptr );
801884 free (plus .ptr );
802885 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 ,
805890 & style -> ctx , style -> newline ,
806891 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+ }
808895 diff_words -> minus .text .size = diff_words -> plus .text .size = 0 ;
809896}
810897
@@ -1902,8 +1989,8 @@ static void builtin_diff(const char *name_a,
19021989
19031990 ecbdata .diff_words =
19041991 xcalloc (1 , sizeof (struct diff_words_data ));
1905- ecbdata .diff_words -> file = o -> file ;
19061992 ecbdata .diff_words -> type = o -> word_diff ;
1993+ ecbdata .diff_words -> opt = o ;
19071994 if (!o -> word_regex )
19081995 o -> word_regex = userdiff_word_regex (one );
19091996 if (!o -> word_regex )
0 commit comments