Make --color-words work well with --graph

'--color-words' algorithm can be described as:

  1. collect a the minus/plus lines of a diff hunk, divided into
     minus-lines and plus-lines;

  2. break both minus-lines and plus-lines into words and
     place them into two mmfile_t with one word for each line;

  3. use xdiff to run diff on the two mmfile_t to get the words level diff;

And for the common parts of the both file, we output the plus side text.
diff_words->current_plus is used to trace the current position of the plus file
which printed. diff_words->last_minus is used to trace the last minus word
printed.

For '--graph' to work with '--color-words', we need to output the graph prefix
on each line of color words output. Generally, there are two conditions on
which we should output the prefix.

  1. diff_words->last_minus == 0 &&
     diff_words->current_plus == diff_words->plus.text.ptr

     that is: the plus text must start as a new line, and if there is no minus
     word printed, a graph prefix must be printed.

  2. diff_words->current_plus > diff_words->plus.text.ptr &&
     *(diff_words->current_plus - 1) == '\n'

     that is: a graph prefix must be printed following a '\n'

Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Bo Yang 2010-05-29 23:32:06 +08:00 committed by Junio C Hamano
parent b5a4de9d50
commit 4297c0aeb5

121
diff.c
View File

@ -622,7 +622,8 @@ struct diff_words_style diff_words_styles[] = {
struct diff_words_data { struct diff_words_data {
struct diff_words_buffer minus, plus; struct diff_words_buffer minus, plus;
const char *current_plus; const char *current_plus;
FILE *file; int last_minus;
struct diff_options *opt;
regex_t *word_regex; regex_t *word_regex;
enum diff_words_type type; enum diff_words_type type;
struct diff_words_style *style; struct diff_words_style *style;
@ -631,10 +632,15 @@ struct diff_words_data {
static int fn_out_diff_words_write_helper(FILE *fp, static int fn_out_diff_words_write_helper(FILE *fp,
struct diff_words_style_elem *st_el, struct diff_words_style_elem *st_el,
const char *newline, const char *newline,
size_t count, const char *buf) size_t count, const char *buf,
const char *line_prefix)
{ {
int print = 0;
while (count) { while (count) {
char *p = memchr(buf, '\n', count); char *p = memchr(buf, '\n', count);
if (print)
fputs(line_prefix, fp);
if (p != buf) { if (p != buf) {
if (st_el->color && fputs(st_el->color, fp) < 0) if (st_el->color && fputs(st_el->color, fp) < 0)
return -1; return -1;
@ -652,21 +658,74 @@ static int fn_out_diff_words_write_helper(FILE *fp,
return -1; return -1;
count -= p + 1 - buf; count -= p + 1 - buf;
buf = p + 1; buf = p + 1;
print = 1;
} }
return 0; return 0;
} }
/*
* '--color-words' algorithm can be described as:
*
* 1. collect a the minus/plus lines of a diff hunk, divided into
* minus-lines and plus-lines;
*
* 2. break both minus-lines and plus-lines into words and
* place them into two mmfile_t with one word for each line;
*
* 3. use xdiff to run diff on the two mmfile_t to get the words level diff;
*
* And for the common parts of the both file, we output the plus side text.
* diff_words->current_plus is used to trace the current position of the plus file
* which printed. diff_words->last_minus is used to trace the last minus word
* printed.
*
* For '--graph' to work with '--color-words', we need to output the graph prefix
* on each line of color words output. Generally, there are two conditions on
* which we should output the prefix.
*
* 1. diff_words->last_minus == 0 &&
* diff_words->current_plus == diff_words->plus.text.ptr
*
* that is: the plus text must start as a new line, and if there is no minus
* word printed, a graph prefix must be printed.
*
* 2. diff_words->current_plus > diff_words->plus.text.ptr &&
* *(diff_words->current_plus - 1) == '\n'
*
* that is: a graph prefix must be printed following a '\n'
*/
static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
{
if ((diff_words->last_minus == 0 &&
diff_words->current_plus == diff_words->plus.text.ptr) ||
(diff_words->current_plus > diff_words->plus.text.ptr &&
*(diff_words->current_plus - 1) == '\n')) {
return 1;
} else {
return 0;
}
}
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{ {
struct diff_words_data *diff_words = priv; struct diff_words_data *diff_words = priv;
struct diff_words_style *style = diff_words->style; struct diff_words_style *style = diff_words->style;
int minus_first, minus_len, plus_first, plus_len; int minus_first, minus_len, plus_first, plus_len;
const char *minus_begin, *minus_end, *plus_begin, *plus_end; const char *minus_begin, *minus_end, *plus_begin, *plus_end;
struct diff_options *opt = diff_words->opt;
struct strbuf *msgbuf;
char *line_prefix = "";
if (line[0] != '@' || parse_hunk_header(line, len, if (line[0] != '@' || parse_hunk_header(line, len,
&minus_first, &minus_len, &plus_first, &plus_len)) &minus_first, &minus_len, &plus_first, &plus_len))
return; return;
assert(opt);
if (opt->output_prefix) {
msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
line_prefix = msgbuf->buf;
}
/* POSIX requires that first be decremented by one if len == 0... */ /* POSIX requires that first be decremented by one if len == 0... */
if (minus_len) { if (minus_len) {
minus_begin = diff_words->minus.orig[minus_first].begin; 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)
} else } else
plus_begin = plus_end = diff_words->plus.orig[plus_first].end; plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
if (diff_words->current_plus != plus_begin) if (color_words_output_graph_prefix(diff_words)) {
fn_out_diff_words_write_helper(diff_words->file, fputs(line_prefix, diff_words->opt->file);
}
if (diff_words->current_plus != plus_begin) {
fn_out_diff_words_write_helper(diff_words->opt->file,
&style->ctx, style->newline, &style->ctx, style->newline,
plus_begin - diff_words->current_plus, plus_begin - diff_words->current_plus,
diff_words->current_plus); diff_words->current_plus, line_prefix);
if (minus_begin != minus_end) if (*(plus_begin - 1) == '\n')
fn_out_diff_words_write_helper(diff_words->file, fputs(line_prefix, diff_words->opt->file);
}
if (minus_begin != minus_end) {
fn_out_diff_words_write_helper(diff_words->opt->file,
&style->old, style->newline, &style->old, style->newline,
minus_end - minus_begin, minus_begin); minus_end - minus_begin, minus_begin,
if (plus_begin != plus_end) line_prefix);
fn_out_diff_words_write_helper(diff_words->file, }
if (plus_begin != plus_end) {
fn_out_diff_words_write_helper(diff_words->opt->file,
&style->new, style->newline, &style->new, style->newline,
plus_end - plus_begin, plus_begin); plus_end - plus_begin, plus_begin,
line_prefix);
}
diff_words->current_plus = plus_end; diff_words->current_plus = plus_end;
diff_words->last_minus = minus_first;
} }
/* This function starts looking at *begin, and returns 0 iff a word was found. */ /* 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)
mmfile_t minus, plus; mmfile_t minus, plus;
struct diff_words_style *style = diff_words->style; struct diff_words_style *style = diff_words->style;
struct diff_options *opt = diff_words->opt;
struct strbuf *msgbuf;
char *line_prefix = "";
assert(opt);
if (opt->output_prefix) {
msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
line_prefix = msgbuf->buf;
}
/* special case: only removal */ /* special case: only removal */
if (!diff_words->plus.text.size) { if (!diff_words->plus.text.size) {
fn_out_diff_words_write_helper(diff_words->file, fputs(line_prefix, diff_words->opt->file);
fn_out_diff_words_write_helper(diff_words->opt->file,
&style->old, style->newline, &style->old, style->newline,
diff_words->minus.text.size, diff_words->minus.text.ptr); diff_words->minus.text.size,
diff_words->minus.text.ptr, line_prefix);
diff_words->minus.text.size = 0; diff_words->minus.text.size = 0;
return; return;
} }
diff_words->current_plus = diff_words->plus.text.ptr; diff_words->current_plus = diff_words->plus.text.ptr;
diff_words->last_minus = 0;
memset(&xpp, 0, sizeof(xpp)); memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg)); memset(&xecfg, 0, sizeof(xecfg));
@ -800,11 +883,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
free(minus.ptr); free(minus.ptr);
free(plus.ptr); free(plus.ptr);
if (diff_words->current_plus != diff_words->plus.text.ptr + if (diff_words->current_plus != diff_words->plus.text.ptr +
diff_words->plus.text.size) diff_words->plus.text.size) {
fn_out_diff_words_write_helper(diff_words->file, if (color_words_output_graph_prefix(diff_words))
fputs(line_prefix, diff_words->opt->file);
fn_out_diff_words_write_helper(diff_words->opt->file,
&style->ctx, style->newline, &style->ctx, style->newline,
diff_words->plus.text.ptr + diff_words->plus.text.size diff_words->plus.text.ptr + diff_words->plus.text.size
- diff_words->current_plus, diff_words->current_plus); - diff_words->current_plus, diff_words->current_plus,
line_prefix);
}
diff_words->minus.text.size = diff_words->plus.text.size = 0; diff_words->minus.text.size = diff_words->plus.text.size = 0;
} }
@ -1902,8 +1989,8 @@ static void builtin_diff(const char *name_a,
ecbdata.diff_words = ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data)); xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
ecbdata.diff_words->type = o->word_diff; ecbdata.diff_words->type = o->word_diff;
ecbdata.diff_words->opt = o;
if (!o->word_regex) if (!o->word_regex)
o->word_regex = userdiff_word_regex(one); o->word_regex = userdiff_word_regex(one);
if (!o->word_regex) if (!o->word_regex)