Browse Source

Add the --color-words option to the diff options family

With this option, the changed words are shown inline. For example,
if a file containing "This is foo" is changed to "This is bar", the diff
will now show "This is " in plain text, "foo" in red, and "bar" in green.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
maint
Johannes Schindelin 19 years ago committed by Junio C Hamano
parent
commit
f59a59e22f
  1. 3
      Documentation/diff-options.txt
  2. 178
      diff.c
  3. 3
      diff.h

3
Documentation/diff-options.txt

@ -36,6 +36,9 @@ @@ -36,6 +36,9 @@
Turn off colored diff, even when the configuration file
gives the default to color output.

--color-words::
Show colored word diff, i.e. color words which have changed.

--no-renames::
Turn off rename detection, even when the configuration
file gives the default to do so.

178
diff.c

@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) @@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
return 0;
}

struct diff_words_buffer {
mmfile_t text;
long alloc;
long current; /* output pointer */
int suppressed_newline;
};

static void diff_words_append(char *line, unsigned long len,
struct diff_words_buffer *buffer)
{
if (buffer->text.size + len > buffer->alloc) {
buffer->alloc = (buffer->text.size + len) * 3 / 2;
buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
}
line++;
len--;
memcpy(buffer->text.ptr + buffer->text.size, line, len);
buffer->text.size += len;
}

struct diff_words_data {
struct xdiff_emit_state xm;
struct diff_words_buffer minus, plus;
};

static void print_word(struct diff_words_buffer *buffer, int len, int color,
int suppress_newline)
{
const char *ptr;
int eol = 0;

if (len == 0)
return;

ptr = buffer->text.ptr + buffer->current;
buffer->current += len;

if (ptr[len - 1] == '\n') {
eol = 1;
len--;
}

fputs(diff_get_color(1, color), stdout);
fwrite(ptr, len, 1, stdout);
fputs(diff_get_color(1, DIFF_RESET), stdout);

if (eol) {
if (suppress_newline)
buffer->suppressed_newline = 1;
else
putchar('\n');
}
}

static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
struct diff_words_data *diff_words = priv;

if (diff_words->minus.suppressed_newline) {
if (line[0] != '+')
putchar('\n');
diff_words->minus.suppressed_newline = 0;
}

len--;
switch (line[0]) {
case '-':
print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
break;
case '+':
print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
break;
case ' ':
print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
diff_words->minus.current += len;
break;
}
}

/* this executes the word diff on the accumulated buffers */
static void diff_words_show(struct diff_words_data *diff_words)
{
xpparam_t xpp;
xdemitconf_t xecfg;
xdemitcb_t ecb;
mmfile_t minus, plus;
int i;

minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
for (i = 0; i < minus.size; i++)
if (isspace(minus.ptr[i]))
minus.ptr[i] = '\n';
diff_words->minus.current = 0;

plus.size = diff_words->plus.text.size;
plus.ptr = xmalloc(plus.size);
memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
for (i = 0; i < plus.size; i++)
if (isspace(plus.ptr[i]))
plus.ptr[i] = '\n';
diff_words->plus.current = 0;

xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diff_words;
diff_words->xm.consume = fn_out_diff_words_aux;
xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);

free(minus.ptr);
free(plus.ptr);
diff_words->minus.text.size = diff_words->plus.text.size = 0;

if (diff_words->minus.suppressed_newline) {
putchar('\n');
diff_words->minus.suppressed_newline = 0;
}
}

struct emit_callback {
struct xdiff_emit_state xm;
int nparents, color_diff;
const char **label_path;
struct diff_words_data *diff_words;
};

static void free_diff_words_data(struct emit_callback *ecbdata)
{
if (ecbdata->diff_words) {
/* flush buffers */
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);

if (ecbdata->diff_words->minus.text.ptr)
free (ecbdata->diff_words->minus.text.ptr);
if (ecbdata->diff_words->plus.text.ptr)
free (ecbdata->diff_words->plus.text.ptr);
free(ecbdata->diff_words);
ecbdata->diff_words = NULL;
}
}

const char *diff_get_color(int diff_use_color, enum color_diff ix)
{
if (diff_use_color)
@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) @@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
else {
int nparents = ecbdata->nparents;
int color = DIFF_PLAIN;
for (i = 0; i < nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
if (ecbdata->diff_words && nparents != 1)
/* fall back to normal diff */
free_diff_words_data(ecbdata);
if (ecbdata->diff_words) {
if (line[0] == '-') {
diff_words_append(line, len,
&ecbdata->diff_words->minus);
return;
} else if (line[0] == '+') {
diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
}
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
line++;
len--;
} else
for (i = 0; i < nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
set = diff_get_color(ecbdata->color_diff, color);
}
if (len > 0 && line[len-1] == '\n')
@ -836,7 +995,12 @@ static void builtin_diff(const char *name_a, @@ -836,7 +995,12 @@ static void builtin_diff(const char *name_a,
ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
ecbdata.xm.consume = fn_out_consume;
if (o->color_diff_words)
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
if (o->color_diff_words)
free_diff_words_data(&ecbdata);
}

free_ab_and_return:
@ -1697,6 +1861,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) @@ -1697,6 +1861,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(arg, "--color-words"))
options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
else

3
diff.h

@ -46,7 +46,8 @@ struct diff_options { @@ -46,7 +46,8 @@ struct diff_options {
full_index:1,
silent_on_remove:1,
find_copies_harder:1,
color_diff:1;
color_diff:1,
color_diff_words:1;
int context;
int break_opt;
int detect_rename;

Loading…
Cancel
Save