git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* A failed attempt to integrate diff-highlight to the core
@ 2015-12-31 12:37 Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 1/7] diff.c: keep all word diff structs together Nguyễn Thái Ngọc Duy
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This is mostly for the record. The patches are not as important as the
result in this mail. But if you want to try out, you can.

If you don't know, the script diff-highlight in contrib can highlight
changes between two nearly identical lines, pointing out what
characters are different. It can improve readability a lot in certain
cases (e.g. when you wrap _() around strings).

I approached it differently. I took --color-words and reformatted its
output in unified diff format. So you'll see +/- lines like a normal
diff, but deleted and added words are also highlighted.

The idea seems to fit well into diff-words machinery. But the output
does not, especially in cases where there are many deleted/added words
in a chunk. It could look.. patchy, like somebody throwing colors on a
wall (especially when you do --highlight-words=.). You can try out and
see.  git-log works so you can see lots of diff.

I don't know, maybe the idea can be improved to have something closer
to diff-highlight. I suppose we can select similar lines and diff
them, instead of diff the entire chunk in one go. I might revisit it
at some point in future, but for now, it's a failure.

Nguyễn Thái Ngọc Duy (7):
  diff.c: keep all word diff structs together
  diff.c: refactor diff_words_append()
  diff --color-words: another special diff case
  diff.c: refactor fn_out_diff_words_write_helper()
  diff: unified diff with colored words, step 1, unified diff only
  diff.c: add new arguments to emit_line_0()
  diff --highlight-words: actually highlight words

 diff.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 diff.h |   3 +-
 2 files changed, 363 insertions(+), 40 deletions(-)

-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/7] diff.c: keep all word diff structs together
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 2/7] diff.c: refactor diff_words_append() Nguyễn Thái Ngọc Duy
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/diff.c b/diff.c
index 80eb0c2..dfbed41 100644
--- a/diff.c
+++ b/diff.c
@@ -759,17 +759,6 @@ struct diff_words_buffer {
 	int orig_nr, orig_alloc;
 };
 
-static void diff_words_append(char *line, unsigned long len,
-		struct diff_words_buffer *buffer)
-{
-	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
-	line++;
-	len--;
-	memcpy(buffer->text.ptr + buffer->text.size, line, len);
-	buffer->text.size += len;
-	buffer->text.ptr[buffer->text.size] = '\0';
-}
-
 struct diff_words_style_elem {
 	const char *prefix;
 	const char *suffix;
@@ -799,6 +788,17 @@ struct diff_words_data {
 	struct diff_words_style *style;
 };
 
+static void diff_words_append(char *line, unsigned long len,
+		struct diff_words_buffer *buffer)
+{
+	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
+	line++;
+	len--;
+	memcpy(buffer->text.ptr + buffer->text.size, line, len);
+	buffer->text.size += len;
+	buffer->text.ptr[buffer->text.size] = '\0';
+}
+
 static int fn_out_diff_words_write_helper(FILE *fp,
 					  struct diff_words_style_elem *st_el,
 					  const char *newline,
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/7] diff.c: refactor diff_words_append()
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 1/7] diff.c: keep all word diff structs together Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 3/7] diff --color-words: another special diff case Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/diff.c b/diff.c
index dfbed41..8af1df1 100644
--- a/diff.c
+++ b/diff.c
@@ -788,9 +788,17 @@ struct diff_words_data {
 	struct diff_words_style *style;
 };
 
-static void diff_words_append(char *line, unsigned long len,
-		struct diff_words_buffer *buffer)
+static void diff_words_append(struct diff_words_data *diff_words,
+			      char *line, unsigned long len)
 {
+	struct diff_words_buffer *buffer;
+
+	if (line[0] == '-')
+		buffer = &diff_words->minus;
+	else {
+		assert(line[0] == '+');
+		buffer = &diff_words->plus;
+	}
 	ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
 	line++;
 	len--;
@@ -1252,13 +1260,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 	}
 
 	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);
+		if (line[0] == '-' || line[0] == '+') {
+			diff_words_append(ecbdata->diff_words, line, len);
 			return;
 		} else if (starts_with(line, "\\ ")) {
 			/*
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/7] diff --color-words: another special diff case
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 1/7] diff.c: keep all word diff structs together Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 2/7] diff.c: refactor diff_words_append() Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 4/7] diff.c: refactor fn_out_diff_words_write_helper() Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/diff.c b/diff.c
index 8af1df1..1354368 100644
--- a/diff.c
+++ b/diff.c
@@ -1037,6 +1037,16 @@ static void diff_words_show(struct diff_words_data *diff_words)
 		diff_words->minus.text.size = 0;
 		return;
 	}
+	/* special case: only addition */
+	if (!diff_words->minus.text.size) {
+		fputs(line_prefix, diff_words->opt->file);
+		fn_out_diff_words_write_helper(diff_words->opt->file,
+			&style->new, style->newline,
+			diff_words->plus.text.size,
+			diff_words->plus.text.ptr, line_prefix);
+		diff_words->plus.text.size = 0;
+		return;
+	}
 
 	diff_words->current_plus = diff_words->plus.text.ptr;
 	diff_words->last_minus = 0;
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/7] diff.c: refactor fn_out_diff_words_write_helper()
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2015-12-31 12:37 ` [PATCH 3/7] diff --color-words: another special diff case Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 5/7] diff: unified diff with colored words, step 1, unified diff only Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/diff.c b/diff.c
index 1354368..8a9e42f 100644
--- a/diff.c
+++ b/diff.c
@@ -807,12 +807,13 @@ static void diff_words_append(struct diff_words_data *diff_words,
 	buffer->text.ptr[buffer->text.size] = '\0';
 }
 
-static int fn_out_diff_words_write_helper(FILE *fp,
+static int fn_out_diff_words_write_helper(struct diff_words_data *dw,
 					  struct diff_words_style_elem *st_el,
 					  const char *newline,
 					  size_t count, const char *buf,
 					  const char *line_prefix)
 {
+	FILE *fp = dw->opt->file;
 	int print = 0;
 
 	while (count) {
@@ -919,7 +920,7 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 		fputs(line_prefix, diff_words->opt->file);
 	}
 	if (diff_words->current_plus != plus_begin) {
-		fn_out_diff_words_write_helper(diff_words->opt->file,
+		fn_out_diff_words_write_helper(diff_words,
 				&style->ctx, style->newline,
 				plus_begin - diff_words->current_plus,
 				diff_words->current_plus, line_prefix);
@@ -927,13 +928,13 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 			fputs(line_prefix, diff_words->opt->file);
 	}
 	if (minus_begin != minus_end) {
-		fn_out_diff_words_write_helper(diff_words->opt->file,
+		fn_out_diff_words_write_helper(diff_words,
 				&style->old, style->newline,
 				minus_end - minus_begin, minus_begin,
 				line_prefix);
 	}
 	if (plus_begin != plus_end) {
-		fn_out_diff_words_write_helper(diff_words->opt->file,
+		fn_out_diff_words_write_helper(diff_words,
 				&style->new, style->newline,
 				plus_end - plus_begin, plus_begin,
 				line_prefix);
@@ -1030,7 +1031,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	/* special case: only removal */
 	if (!diff_words->plus.text.size) {
 		fputs(line_prefix, diff_words->opt->file);
-		fn_out_diff_words_write_helper(diff_words->opt->file,
+		fn_out_diff_words_write_helper(diff_words,
 			&style->old, style->newline,
 			diff_words->minus.text.size,
 			diff_words->minus.text.ptr, line_prefix);
@@ -1040,7 +1041,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	/* special case: only addition */
 	if (!diff_words->minus.text.size) {
 		fputs(line_prefix, diff_words->opt->file);
-		fn_out_diff_words_write_helper(diff_words->opt->file,
+		fn_out_diff_words_write_helper(diff_words,
 			&style->new, style->newline,
 			diff_words->plus.text.size,
 			diff_words->plus.text.ptr, line_prefix);
@@ -1067,7 +1068,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
 			diff_words->plus.text.size) {
 		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,
+		fn_out_diff_words_write_helper(diff_words,
 			&style->ctx, style->newline,
 			diff_words->plus.text.ptr + diff_words->plus.text.size
 			- diff_words->current_plus, diff_words->current_plus,
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/7] diff: unified diff with colored words, step 1, unified diff only
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2015-12-31 12:37 ` [PATCH 4/7] diff.c: refactor fn_out_diff_words_write_helper() Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 6/7] diff.c: add new arguments to emit_line_0() Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 7/7] diff --highlight-words: actually highlight words Nguyễn Thái Ngọc Duy
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

The goal is to produce a unified diff, but with changed words colored
differently. A new diff-words mode is added that can keep track of both
lines and words of each chunk. The marks then are post processed and
each line is output in unified format. The actual word coloring comes in
the next patch.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 diff.h |   3 +-
 2 files changed, 255 insertions(+), 4 deletions(-)

diff --git a/diff.c b/diff.c
index 8a9e42f..3b7317e 100644
--- a/diff.c
+++ b/diff.c
@@ -440,6 +440,23 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
 	ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
 }
 
+#define TAG_BEGIN_WORD(tag) \
+	((tag) == TAG_BEGIN_OLD_WORD || \
+	 (tag) == TAG_BEGIN_NEW_WORD)
+
+enum pointer_tag { /* order is important because it's used in sorting */
+	TAG_END_WORD,
+	TAG_END_LINE,
+	TAG_BEGIN_LINE,
+	TAG_BEGIN_OLD_WORD,
+	TAG_BEGIN_NEW_WORD
+};
+
+struct tagged_pointer {
+	const char *str;
+	enum pointer_tag tag;
+};
+
 static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
 			int first, const char *line, int len)
 {
@@ -757,6 +774,10 @@ struct diff_words_buffer {
 		const char *begin, *end;
 	} *orig;
 	int orig_nr, orig_alloc;
+	unsigned long *line;
+	int line_nr, line_alloc;
+	struct tagged_pointer *mark;
+	int mark_nr, mark_alloc;
 };
 
 struct diff_words_style_elem {
@@ -772,6 +793,8 @@ struct diff_words_style {
 	const char *newline;
 };
 
+static struct diff_words_style diff_words_unified_style;
+
 static struct diff_words_style diff_words_styles[] = {
 	{ DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
 	{ DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
@@ -803,6 +826,13 @@ static void diff_words_append(struct diff_words_data *diff_words,
 	line++;
 	len--;
 	memcpy(buffer->text.ptr + buffer->text.size, line, len);
+	if (diff_words->type == DIFF_WORDS_UNIFIED) {
+		unsigned long *l;
+		ALLOC_GROW(buffer->line, (buffer->line_nr + 1) * 2, buffer->line_alloc);
+		l = buffer->line + (buffer->line_nr++) * 2;
+		l[0] = buffer->text.size;
+		l[1] = l[0] + len;
+	}
 	buffer->text.size += len;
 	buffer->text.ptr[buffer->text.size] = '\0';
 }
@@ -816,6 +846,40 @@ static int fn_out_diff_words_write_helper(struct diff_words_data *dw,
 	FILE *fp = dw->opt->file;
 	int print = 0;
 
+	if (dw->type == DIFF_WORDS_UNIFIED) {
+		struct diff_words_style *st = &diff_words_unified_style;
+		struct diff_words_buffer *b;
+		enum pointer_tag tag;
+		struct tagged_pointer *tp;
+
+		if (st_el == &st->ctx)
+			return 0;
+		else if (st_el == &st->old)
+			tag = TAG_BEGIN_OLD_WORD;
+		else if (st_el == &st->new)
+			tag = TAG_BEGIN_NEW_WORD;
+		else
+			return -1;
+
+		if (buf >= dw->minus.text.ptr &&
+		    buf < dw->minus.text.ptr + dw->minus.text.size)
+			b = &dw->minus;
+		else if (buf >= dw->plus.text.ptr &&
+			 buf < dw->plus.text.ptr + dw->plus.text.size)
+			b = &dw->plus;
+		else
+			return -1;
+
+		ALLOC_GROW(b->mark, b->mark_nr + 2, b->mark_alloc);
+		tp = b->mark + b->mark_nr;
+		tp[0].str = buf;
+		tp[0].tag = tag;
+		tp[1].str = buf + count;
+		tp[1].tag = TAG_END_WORD;
+		b->mark_nr += 2;
+		return 0;
+	}
+
 	while (count) {
 		char *p = memchr(buf, '\n', count);
 		if (print)
@@ -1014,6 +1078,23 @@ static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
 	}
 }
 
+static void diff_words_add_line(struct diff_words_buffer *buffer)
+{
+	int i;
+
+	ALLOC_GROW(buffer->mark, buffer->line_nr * 2, buffer->mark_alloc);
+	for (i = 0; i < buffer->line_nr; i++) {
+		struct tagged_pointer *tp = buffer->mark + buffer->mark_nr;
+		tp->str = buffer->text.ptr + buffer->line[i * 2];
+		tp->tag = TAG_BEGIN_LINE;
+		tp++;
+		tp->str = buffer->text.ptr + buffer->line[i * 2 + 1];
+		tp->tag = TAG_END_LINE;
+		buffer->mark_nr += 2;
+	}
+	buffer->line_nr = 0;
+}
+
 /* this executes the word diff on the accumulated buffers */
 static void diff_words_show(struct diff_words_data *diff_words)
 {
@@ -1025,6 +1106,16 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	struct diff_options *opt = diff_words->opt;
 	const char *line_prefix;
 
+	if (diff_words->type == DIFF_WORDS_UNIFIED) {
+		/*
+		 * line marks are collected in line[] array as offsets
+		 * because the "text" buffer can be reallocated. Now
+		 * it's safe to convert line[] to mark[].
+		 */
+		diff_words_add_line(&diff_words->minus);
+		diff_words_add_line(&diff_words->plus);
+	}
+
 	assert(opt);
 	line_prefix = diff_line_prefix(opt);
 
@@ -1077,12 +1168,152 @@ static void diff_words_show(struct diff_words_data *diff_words)
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
+static int tagptrcmp(const void *a_, const void *b_)
+{
+	const struct tagged_pointer *a = a_;
+	const struct tagged_pointer *b = b_;
+	return a->str == b->str ? a->tag > b->tag : a->str > b->str;
+}
+
+static void diff_words_buffer_finalize_marks(struct diff_words_buffer *b)
+{
+	struct tagged_pointer *dst, *src, *next, *end, *cur_word;
+	int i, dst_nr, dst_alloc;
+
+	/* Join consecutive same-type words */
+	end = b->mark + b->mark_nr;
+	for (src = b->mark; src < end; src++) {
+		next = src + 2;
+		while (next + 2 <= end &&
+		       TAG_BEGIN_WORD(src[0].tag) &&
+		       TAG_BEGIN_WORD(next[0].tag) &&
+		       src[0].tag == next[0].tag &&
+		       src[1].tag == TAG_END_WORD &&
+		       next[1].tag == TAG_END_WORD &&
+		       src[1].str == next[0].str) {
+			src[1] = next[1];
+			memcpy(next, next + 2, sizeof(*next) * (end - next - 2));
+			end -= 2;
+		}
+	}
+	b->mark_nr = end - b->mark;
+
+	/*
+	 * Simplify one-word chunks. Not that at this point we have
+	 * all line marks (in correct order), then all word marks.
+	 */
+	if (b->mark_nr >= 4 &&
+	    b->mark[0].tag == TAG_BEGIN_LINE &&
+	    TAG_BEGIN_WORD(b->mark[b->mark_nr - 2].tag) &&
+	    b->mark[0].str == b->mark[b->mark_nr - 2].str &&
+	    b->mark[b->mark_nr - 1].tag == TAG_END_WORD &&
+	    b->mark[b->mark_nr - 3].tag == TAG_END_LINE &&
+	    b->mark[b->mark_nr - 1].str == b->mark[b->mark_nr - 3].str) {
+		b->mark_nr -= 2;
+		return;
+	}
+
+	/* Move words into lines */
+	qsort(b->mark, b->mark_nr, sizeof(*b->mark), tagptrcmp);
+
+	/* Split words that span across lines */
+	cur_word = NULL;
+	dst = NULL;
+	dst_nr = 0;
+	dst_alloc = 0;
+	ALLOC_GROW(dst, b->mark_nr, dst_alloc);
+	for (i = 0; i < b->mark_nr; i++) {
+		struct tagged_pointer *src = b->mark + i;
+
+		switch (src->tag) {
+		case TAG_BEGIN_OLD_WORD:
+		case TAG_BEGIN_NEW_WORD:
+			ALLOC_GROW(dst, dst_nr + 1, dst_alloc);
+			dst[dst_nr++] = *src;
+			cur_word = src;
+			break;
+
+		case TAG_END_WORD:
+			ALLOC_GROW(dst, dst_nr + 1, dst_alloc);
+			dst[dst_nr++] = *src;
+			cur_word = NULL;
+			break;
+
+		case TAG_BEGIN_LINE:
+			ALLOC_GROW(dst, dst_nr + 2, dst_alloc);
+			dst[dst_nr++] = *src;
+			if (cur_word) {
+				dst[dst_nr].tag = cur_word->tag;
+				dst[dst_nr].str = src->str;
+				dst_nr++;
+			}
+			break;
+
+		case TAG_END_LINE:
+			ALLOC_GROW(dst, dst_nr + 2, dst_alloc);
+			if (cur_word) {
+				dst[dst_nr].tag = TAG_END_WORD;
+				dst[dst_nr].str = src->str;
+				dst_nr++;
+			}
+			dst[dst_nr++] = *src;
+			break;
+		}
+	}
+
+	free(b->mark);
+	b->mark = dst;
+	b->mark_nr = dst_nr;
+	b->mark_alloc = dst_alloc;
+}
+
+static void diff_words_flush_unified(struct emit_callback *ecb,
+				     enum color_diff color,
+				     unsigned ws_error_highlight,
+				     char sign)
+{
+	const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
+	struct diff_words_data *dw = ecb->diff_words;
+	struct diff_words_buffer *b;
+	struct tagged_pointer *begin_line, *end, *end_line;
+
+	switch (sign) {
+	case '-':
+		b = &dw->minus;
+		break;
+	case '+':
+		b = &dw->plus;
+		break;
+	default:
+		return;
+	}
+
+	if (!b->mark_nr)
+		return;
+
+	diff_words_buffer_finalize_marks(b);
+	end = b->mark + b->mark_nr;
+	for (begin_line = b->mark; begin_line < end; begin_line = end_line + 1) {
+		assert(begin_line->tag == TAG_BEGIN_LINE);
+		end_line = begin_line;
+		while (end_line < end && end_line->tag != TAG_END_LINE)
+			end_line++;
+		assert(end_line->tag == TAG_END_LINE);
+		emit_line_checked(reset, ecb, begin_line->str,
+				  end_line->str - begin_line->str,
+				  color, ws_error_highlight, sign);
+	}
+	b->mark_nr = 0;
+}
+
 /* In "color-words" mode, show word-diff of words accumulated in the buffer */
 static void diff_words_flush(struct emit_callback *ecbdata)
 {
 	if (ecbdata->diff_words->minus.text.size ||
 	    ecbdata->diff_words->plus.text.size)
 		diff_words_show(ecbdata->diff_words);
+	diff_words_flush_unified(ecbdata, DIFF_FILE_OLD, WSEH_OLD, '-');
+	diff_words_flush_unified(ecbdata, DIFF_FILE_NEW, WSEH_NEW, '+');
 }
 
 static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1133,6 +1364,10 @@ static void init_diff_words_data(struct emit_callback *ecbdata,
 			die ("Invalid regular expression: %s",
 			     o->word_regex);
 	}
+	if (o->word_diff == DIFF_WORDS_UNIFIED) {
+		ecbdata->diff_words->style = &diff_words_unified_style;
+		return;
+	}
 	for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
 		if (o->word_diff == diff_words_styles[i].type) {
 			ecbdata->diff_words->style =
@@ -1155,8 +1390,12 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
 		free (ecbdata->diff_words->opt);
 		free (ecbdata->diff_words->minus.text.ptr);
 		free (ecbdata->diff_words->minus.orig);
+		free (ecbdata->diff_words->minus.line);
+		free (ecbdata->diff_words->minus.mark);
 		free (ecbdata->diff_words->plus.text.ptr);
 		free (ecbdata->diff_words->plus.orig);
+		free (ecbdata->diff_words->plus.line);
+		free (ecbdata->diff_words->plus.mark);
 		if (ecbdata->diff_words->word_regex) {
 			regfree(ecbdata->diff_words->word_regex);
 			free(ecbdata->diff_words->word_regex);
@@ -1274,7 +1513,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 		if (line[0] == '-' || line[0] == '+') {
 			diff_words_append(ecbdata->diff_words, line, len);
 			return;
-		} else if (starts_with(line, "\\ ")) {
+		} else if (ecbdata->diff_words->type != DIFF_WORDS_UNIFIED &&
+			   starts_with(line, "\\ ")) {
 			/*
 			 * Eat the "no newline at eof" marker as if we
 			 * saw a "+" or "-" line with nothing on it,
@@ -1288,7 +1528,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 		if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
 			emit_line(ecbdata->opt, context, reset, line, len);
 			fputs("~\n", ecbdata->opt->file);
-		} else {
+			return;
+		} else if (ecbdata->diff_words->type != DIFF_WORDS_UNIFIED) {
 			/*
 			 * Skip the prefix character, if any.  With
 			 * diff_suppress_blank_empty, there may be
@@ -1299,8 +1540,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 			      len--;
 			}
 			emit_line(ecbdata->opt, context, reset, line, len);
+			return;
 		}
-		return;
 	}
 
 	switch (line[0]) {
@@ -3859,6 +4100,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 		options->word_diff = DIFF_WORDS_COLOR;
 		options->word_regex = arg;
 	}
+	else if (!strcmp(arg, "--highlight-words")) {
+		options->use_color = 1;
+		options->word_diff = DIFF_WORDS_UNIFIED;
+	}
+	else if (skip_prefix(arg, "--highlight-words=", &arg)) {
+		options->use_color = 1;
+		options->word_diff = DIFF_WORDS_UNIFIED;
+		options->word_regex = arg;
+	}
 	else if (!strcmp(arg, "--word-diff")) {
 		if (options->word_diff == DIFF_WORDS_NONE)
 			options->word_diff = DIFF_WORDS_PLAIN;
diff --git a/diff.h b/diff.h
index f7208ad..85c469b 100644
--- a/diff.h
+++ b/diff.h
@@ -107,7 +107,8 @@ enum diff_words_type {
 	DIFF_WORDS_NONE = 0,
 	DIFF_WORDS_PORCELAIN,
 	DIFF_WORDS_PLAIN,
-	DIFF_WORDS_COLOR
+	DIFF_WORDS_COLOR,
+	DIFF_WORDS_UNIFIED
 };
 
 struct diff_options {
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/7] diff.c: add new arguments to emit_line_0()
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2015-12-31 12:37 ` [PATCH 5/7] diff: unified diff with colored words, step 1, unified diff only Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  2015-12-31 12:37 ` [PATCH 7/7] diff --highlight-words: actually highlight words Nguyễn Thái Ngọc Duy
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This patch is no-op, to reduce noise in the next one.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/diff.c b/diff.c
index 3b7317e..47d22e3 100644
--- a/diff.c
+++ b/diff.c
@@ -458,7 +458,9 @@ struct tagged_pointer {
 };
 
 static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
-			int first, const char *line, int len)
+			int first, const char *line, int len,
+			struct tagged_pointer *begin,
+			struct tagged_pointer *end)
 {
 	int has_trailing_newline, has_trailing_carriage_return;
 	int nofirst;
@@ -497,7 +499,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
 static void emit_line(struct diff_options *o, const char *set, const char *reset,
 		      const char *line, int len)
 {
-	emit_line_0(o, set, reset, line[0], line+1, len-1);
+	emit_line_0(o, set, reset, line[0], line+1, len-1, NULL, NULL);
 }
 
 static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
@@ -516,7 +518,9 @@ static void emit_line_checked(const char *reset,
 			      const char *line, int len,
 			      enum color_diff color,
 			      unsigned ws_error_highlight,
-			      char sign)
+			      char sign,
+			      struct tagged_pointer *begin,
+			      struct tagged_pointer *end)
 {
 	const char *set = diff_get_color(ecbdata->color_diff, color);
 	const char *ws = NULL;
@@ -528,13 +532,16 @@ static void emit_line_checked(const char *reset,
 	}
 
 	if (!ws)
-		emit_line_0(ecbdata->opt, set, reset, sign, line, len);
+		emit_line_0(ecbdata->opt, set, reset, sign,
+			    line, len, begin, end);
 	else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
 		/* Blank line at EOF - paint '+' as well */
-		emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
+		emit_line_0(ecbdata->opt, ws, reset, sign,
+			    line, len, begin, end);
 	else {
 		/* Emit just the prefix, then the rest. */
-		emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
+		emit_line_0(ecbdata->opt, set, reset, sign,
+			    "", 0, NULL, NULL);
 		ws_check_emit(line, len, ecbdata->ws_rule,
 			      ecbdata->opt->file, set, reset, ws);
 	}
@@ -545,7 +552,8 @@ static void emit_add_line(const char *reset,
 			  const char *line, int len)
 {
 	emit_line_checked(reset, ecbdata, line, len,
-			  DIFF_FILE_NEW, WSEH_NEW, '+');
+			  DIFF_FILE_NEW, WSEH_NEW, '+',
+			  NULL, NULL);
 }
 
 static void emit_del_line(const char *reset,
@@ -553,7 +561,8 @@ static void emit_del_line(const char *reset,
 			  const char *line, int len)
 {
 	emit_line_checked(reset, ecbdata, line, len,
-			  DIFF_FILE_OLD, WSEH_OLD, '-');
+			  DIFF_FILE_OLD, WSEH_OLD, '-',
+			  NULL, NULL);
 }
 
 static void emit_context_line(const char *reset,
@@ -561,7 +570,8 @@ static void emit_context_line(const char *reset,
 			      const char *line, int len)
 {
 	emit_line_checked(reset, ecbdata, line, len,
-			  DIFF_CONTEXT, WSEH_CONTEXT, ' ');
+			  DIFF_CONTEXT, WSEH_CONTEXT, ' ',
+			  NULL, NULL);
 }
 
 static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -682,7 +692,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
 						     DIFF_CONTEXT);
 		putc('\n', ecb->opt->file);
 		emit_line_0(ecb->opt, context, reset, '\\',
-			    nneof, strlen(nneof));
+			    nneof, strlen(nneof), NULL, NULL);
 	}
 }
 
@@ -1301,7 +1311,8 @@ static void diff_words_flush_unified(struct emit_callback *ecb,
 		assert(end_line->tag == TAG_END_LINE);
 		emit_line_checked(reset, ecb, begin_line->str,
 				  end_line->str - begin_line->str,
-				  color, ws_error_highlight, sign);
+				  color, ws_error_highlight, sign,
+				  NULL, NULL);
 	}
 	b->mark_nr = 0;
 }
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 7/7] diff --highlight-words: actually highlight words
  2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
                   ` (5 preceding siblings ...)
  2015-12-31 12:37 ` [PATCH 6/7] diff.c: add new arguments to emit_line_0() Nguyễn Thái Ngọc Duy
@ 2015-12-31 12:37 ` Nguyễn Thái Ngọc Duy
  6 siblings, 0 replies; 8+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-12-31 12:37 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 diff.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 3 deletions(-)

diff --git a/diff.c b/diff.c
index 47d22e3..68a847d 100644
--- a/diff.c
+++ b/diff.c
@@ -457,6 +457,50 @@ struct tagged_pointer {
 	enum pointer_tag tag;
 };
 
+static void emit_tagged_line(struct diff_options *o, const char *set,
+			     const char *line, int len,
+			     struct tagged_pointer *begin,
+			     struct tagged_pointer *end)
+{
+	struct tagged_pointer *current;
+	FILE *file = o->file;
+	const char *last_color = set;
+
+	for (current = begin; current < end; current++) {
+		struct tagged_pointer *next = current + 1;
+		const char *start = current->str;
+		const char *end = next->str;
+		const char *color = NULL;
+
+		switch (current->tag) {
+		case TAG_END_WORD:
+			color = set;
+			break;
+
+		case TAG_BEGIN_OLD_WORD:
+			color = GIT_COLOR_BG_RED GIT_COLOR_YELLOW;
+			break;
+
+		case TAG_BEGIN_NEW_WORD:
+			color = GIT_COLOR_BG_GREEN GIT_COLOR_BLUE;
+			break;
+
+		default:
+			break;
+		}
+
+		if (color && color != last_color) {
+			if (color == set)
+				fputs(GIT_COLOR_RESET, file);
+			fputs(color, file);
+			last_color = color;
+		}
+		while (end > start && end[-1] == '\n')
+			end--;
+		fwrite(start, end - start, 1, file);
+	}
+}
+
 static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
 			int first, const char *line, int len,
 			struct tagged_pointer *begin,
@@ -487,7 +531,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
 		fputs(set, file);
 		if (!nofirst)
 			fputc(first, file);
-		fwrite(line, len, 1, file);
+		if (begin)
+			emit_tagged_line(o, set, line, len, begin, end);
+		else
+			fwrite(line, len, 1, file);
 		fputs(reset, file);
 	}
 	if (has_trailing_carriage_return)
@@ -531,7 +578,7 @@ static void emit_line_checked(const char *reset,
 			ws = NULL;
 	}
 
-	if (!ws)
+	if (!ws || begin)
 		emit_line_0(ecbdata->opt, set, reset, sign,
 			    line, len, begin, end);
 	else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
@@ -1312,7 +1359,7 @@ static void diff_words_flush_unified(struct emit_callback *ecb,
 		emit_line_checked(reset, ecb, begin_line->str,
 				  end_line->str - begin_line->str,
 				  color, ws_error_highlight, sign,
-				  NULL, NULL);
+				  begin_line, end_line);
 	}
 	b->mark_nr = 0;
 }
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-12-31 12:38 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-31 12:37 A failed attempt to integrate diff-highlight to the core Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 1/7] diff.c: keep all word diff structs together Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 2/7] diff.c: refactor diff_words_append() Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 3/7] diff --color-words: another special diff case Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 4/7] diff.c: refactor fn_out_diff_words_write_helper() Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 5/7] diff: unified diff with colored words, step 1, unified diff only Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 6/7] diff.c: add new arguments to emit_line_0() Nguyễn Thái Ngọc Duy
2015-12-31 12:37 ` [PATCH 7/7] diff --highlight-words: actually highlight words Nguyễn Thái Ngọc Duy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).