{"thread":{"id":"5031","subject":"[RFC] Add the --color-words option to the diff options family","startedAt":"2006-07-28T21:56:15Z","lastAt":"2006-07-31T11:00:04Z","messageCount":5,"participants":["Johannes Schindelin","Matthias Lederhofer","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"24318","messageId":"Pine.LNX.4.63.0607282354450.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5031","inReplyTo":null,"subject":"[RFC] Add the --color-words option to the diff options family","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-28T21:56:15Z","receivedAt":"2006-07-28T21:56:15Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith this option, the changed words are shown inline. For example,\nif a file containing \"This is foo\" is changed to \"This is bar\", the diff\nwill now show \"This is \" in plain text, \"foo\" in red, and \"bar\" in green.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\n---\n\n\tI am probably the only person who finds it useful, but there\n\tis a high coolness factor attached to it.\n\n\tAnd the libxdiff library actually made it very easy to do. (Yes, \n\tthere are two nested calls to xdiff...)\n\n Documentation/diff-options.txt |    3 +\n diff.c                         |  178 +++++++++++++++++++++++++++++++++++++++-\n diff.h                         |    3 -\n 3 files changed, 177 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 47ba9a4..b5d9763 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -36,6 +36,9 @@\n \tTurn off colored diff, even when the configuration file\n \tgives the default to color output.\n \n+--color-words::\n+\tShow colored word diff, i.e. color words which have changed.\n+\n --no-renames::\n \tTurn off rename detection, even when the configuration\n \tfile gives the default to do so.\ndiff --git a/diff.c b/diff.c\nindex 6198a61..e910971 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, str\n \treturn 0;\n }\n \n+struct diff_words_buffer {\n+\tmmfile_t text;\n+\tlong alloc;\n+\tlong current; /* output pointer */\n+\tint suppressed_newline;\n+};\n+\n+static void diff_words_append(char *line, unsigned long len,\n+\t\tstruct diff_words_buffer *buffer)\n+{\n+\tif (buffer->text.size + len > buffer->alloc) {\n+\t\tbuffer->alloc = (buffer->text.size + len) * 3 / 2;\n+\t\tbuffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);\n+\t}\n+\tline++;\n+\tlen--;\n+\tmemcpy(buffer->text.ptr + buffer->text.size, line, len);\n+\tbuffer->text.size += len;\n+}\n+\n+struct diff_words_data {\n+\tstruct xdiff_emit_state xm;\n+\tstruct diff_words_buffer minus, plus;\n+};\n+\n+static void print_word(struct diff_words_buffer *buffer, int len, int color,\n+\t\tint suppress_newline)\n+{\n+\tconst char *ptr;\n+\tint eol = 0;\n+\n+\tif (len == 0)\n+\t\treturn;\n+\n+\tptr  = buffer->text.ptr + buffer->current;\n+\tbuffer->current += len;\n+\n+\tif (ptr[len - 1] == '\\n') {\n+\t\teol = 1;\n+\t\tlen--;\n+\t}\n+\n+\tfputs(diff_get_color(1, color), stdout);\n+\tfwrite(ptr, len, 1, stdout);\n+\tfputs(diff_get_color(1, DIFF_RESET), stdout);\n+\n+\tif (eol) {\n+\t\tif (suppress_newline)\n+\t\t\tbuffer->suppressed_newline = 1;\n+\t\telse\n+\t\t\tputchar('\\n');\n+\t}\n+}\n+\n+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n+{\n+\tstruct diff_words_data *diff_words = priv;\n+\n+\tif (diff_words->minus.suppressed_newline) {\n+\t\tif (line[0] != '+')\n+\t\t\tputchar('\\n');\n+\t\tdiff_words->minus.suppressed_newline = 0;\n+\t}\n+\n+\tlen--;\n+\tswitch (line[0]) {\n+\t\tcase '-':\n+\t\t\tprint_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);\n+\t\t\tbreak;\n+\t\tcase '+':\n+\t\t\tprint_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);\n+\t\t\tbreak;\n+\t\tcase ' ':\n+\t\t\tprint_word(&diff_words->plus, len, DIFF_PLAIN, 0);\n+\t\t\tdiff_words->minus.current += len;\n+\t\t\tbreak;\n+\t}\n+}\n+\n+/* this executes the word diff on the accumulated buffers */\n+static void diff_words_show(struct diff_words_data *diff_words)\n+{\n+\txpparam_t xpp;\n+\txdemitconf_t xecfg;\n+\txdemitcb_t ecb;\n+\tmmfile_t minus, plus;\n+\tint i;\n+\n+\tminus.size = diff_words->minus.text.size;\n+\tminus.ptr = xmalloc(minus.size);\n+\tmemcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);\n+\tfor (i = 0; i < minus.size; i++)\n+\t\tif (isspace(minus.ptr[i]))\n+\t\t\tminus.ptr[i] = '\\n';\n+\tdiff_words->minus.current = 0;\n+\n+\tplus.size = diff_words->plus.text.size;\n+\tplus.ptr = xmalloc(plus.size);\n+\tmemcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);\n+\tfor (i = 0; i < plus.size; i++)\n+\t\tif (isspace(plus.ptr[i]))\n+\t\t\tplus.ptr[i] = '\\n';\n+\tdiff_words->plus.current = 0;\n+\n+\txpp.flags = XDF_NEED_MINIMAL;\n+\txecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;\n+\txecfg.flags = 0;\n+\tecb.outf = xdiff_outf;\n+\tecb.priv = diff_words;\n+\tdiff_words->xm.consume = fn_out_diff_words_aux;\n+\txdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);\n+\n+\tfree(minus.ptr);\n+\tfree(plus.ptr);\n+\tdiff_words->minus.text.size = diff_words->plus.text.size = 0;\n+\n+\tif (diff_words->minus.suppressed_newline) {\n+\t\tputchar('\\n');\n+\t\tdiff_words->minus.suppressed_newline = 0;\n+\t}\n+}\n+\n struct emit_callback {\n \tstruct xdiff_emit_state xm;\n \tint nparents, color_diff;\n \tconst char **label_path;\n+\tstruct diff_words_data *diff_words;\n };\n \n+static void free_diff_words_data(struct emit_callback *ecbdata)\n+{\n+\tif (ecbdata->diff_words) {\n+\t\t/* flush buffers */\n+\t\tif (ecbdata->diff_words->minus.text.size ||\n+\t\t\t\tecbdata->diff_words->plus.text.size)\n+\t\t\tdiff_words_show(ecbdata->diff_words);\n+\n+\t\tif (ecbdata->diff_words->minus.text.ptr)\n+\t\t\tfree (ecbdata->diff_words->minus.text.ptr);\n+\t\tif (ecbdata->diff_words->plus.text.ptr)\n+\t\t\tfree (ecbdata->diff_words->plus.text.ptr);\n+\t\tfree(ecbdata->diff_words);\n+\t\tecbdata->diff_words = NULL;\n+\t}\n+}\n+\n const char *diff_get_color(int diff_use_color, enum color_diff ix)\n {\n \tif (diff_use_color)\n@@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, c\n \telse {\n \t\tint nparents = ecbdata->nparents;\n \t\tint color = DIFF_PLAIN;\n-\t\tfor (i = 0; i < nparents && len; i++) {\n-\t\t\tif (line[i] == '-')\n-\t\t\t\tcolor = DIFF_FILE_OLD;\n-\t\t\telse if (line[i] == '+')\n-\t\t\t\tcolor = DIFF_FILE_NEW;\n-\t\t}\n+\t\tif (ecbdata->diff_words && nparents != 1)\n+\t\t\t/* fall back to normal diff */\n+\t\t\tfree_diff_words_data(ecbdata);\n+\t\tif (ecbdata->diff_words) {\n+\t\t\tif (line[0] == '-') {\n+\t\t\t\tdiff_words_append(line, len,\n+\t\t\t\t\t\t&ecbdata->diff_words->minus);\n+\t\t\t\treturn;\n+\t\t\t} else if (line[0] == '+') {\n+\t\t\t\tdiff_words_append(line, len,\n+\t\t\t\t\t\t&ecbdata->diff_words->plus);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tif (ecbdata->diff_words->minus.text.size ||\n+\t\t\t\t\tecbdata->diff_words->plus.text.size)\n+\t\t\t\tdiff_words_show(ecbdata->diff_words);\n+\t\t\tline++;\n+\t\t\tlen--;\n+\t\t} else\n+\t\t\tfor (i = 0; i < nparents && len; i++) {\n+\t\t\t\tif (line[i] == '-')\n+\t\t\t\t\tcolor = DIFF_FILE_OLD;\n+\t\t\t\telse if (line[i] == '+')\n+\t\t\t\t\tcolor = DIFF_FILE_NEW;\n+\t\t\t}\n \t\tset = diff_get_color(ecbdata->color_diff, color);\n \t}\n \tif (len > 0 && line[len-1] == '\\n')\n@@ -836,7 +995,12 @@ static void builtin_diff(const char *nam\n \t\tecb.outf = xdiff_outf;\n \t\tecb.priv = &ecbdata;\n \t\tecbdata.xm.consume = fn_out_consume;\n+\t\tif (o->color_diff_words)\n+\t\t\tecbdata.diff_words =\n+\t\t\t\txcalloc(1, sizeof(struct diff_words_data));\n \t\txdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);\n+\t\tif (o->color_diff_words)\n+\t\t\tfree_diff_words_data(&ecbdata);\n \t}\n \n  free_ab_and_return:\n@@ -1712,6 +1876,8 @@ int diff_opt_parse(struct diff_options *\n \t\toptions->xdl_opts |= XDF_IGNORE_WHITESPACE;\n \telse if (!strcmp(arg, \"-b\") || !strcmp(arg, \"--ignore-space-change\"))\n \t\toptions->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;\n+\telse if (!strcmp(arg, \"--color-words\"))\n+\t\toptions->color_diff = options->color_diff_words = 1;\n \telse if (!strcmp(arg, \"--no-renames\"))\n \t\toptions->detect_rename = 0;\n \telse\ndiff --git a/diff.h b/diff.h\nindex 0d32830..51c163b 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -46,7 +46,8 @@ struct diff_options {\n \t\t full_index:1,\n \t\t silent_on_remove:1,\n \t\t find_copies_harder:1,\n-\t\t color_diff:1;\n+\t\t color_diff:1,\n+\t\t color_diff_words:1;\n \tint context;\n \tint break_opt;\n \tint detect_rename;\n-- \n1.4.2.rc2.g8b063-dirty\n"},{"id":"24358","messageId":"E1G6zc4-0007PC-9j@moooo.ath.cx","threadId":"5031","inReplyTo":"Pine.LNX.4.63.0607282354450.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [RFC] Add the --color-words option to the diff options family","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2006-07-30T00:57:00Z","receivedAt":"2006-07-30T00:57:00Z","isPatch":false,"sender":{"key":"matled@gmx.net","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> With this option, the changed words are shown inline. For example,\n> if a file containing \"This is foo\" is changed to \"This is bar\", the diff\n> will now show \"This is \" in plain text, \"foo\" in red, and \"bar\" in green.\n> \n> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> \n> ---\n> \n> \tI am probably the only person who finds it useful, but there\n> \tis a high coolness factor attached to it.\n> \n> \tAnd the libxdiff library actually made it very easy to do. (Yes, \n> \tthere are two nested calls to xdiff...)\nI'd probably find it more useful if it would still show two lines but\nonly the relevant part in red/green.\n"},{"id":"24365","messageId":"Pine.LNX.4.63.0607301135060.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5031","inReplyTo":"E1G6zc4-0007PC-9j@moooo.ath.cx","subject":"Re: [RFC] Add the --color-words option to the diff options family","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-30T09:36:25Z","receivedAt":"2006-07-30T09:36:25Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 30 Jul 2006, Matthias Lederhofer wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n>\n> > With this option, the changed words are shown inline. For example,\n> > if a file containing \"This is foo\" is changed to \"This is bar\", the diff\n> > will now show \"This is \" in plain text, \"foo\" in red, and \"bar\" in green.\n>\n> I'd probably find it more useful if it would still show two lines but\n> only the relevant part in red/green.\n\nI could do that if you want, but it is a little more involved. Besides, in \nmy use case, which is text with _long_ lines, it would be less useful, so \nI would make it a switch.\n\nCiao,\nDscho\n"},{"id":"24425","messageId":"7vbqr66o4e.fsf@assigned-by-dhcp.cox.net","threadId":"5031","inReplyTo":"Pine.LNX.4.63.0607282354450.29667@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [RFC] Add the --color-words option to the diff options family","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-31T10:54:25Z","receivedAt":"2006-07-31T10:54:25Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> With this option, the changed words are shown inline. For example,\n> if a file containing \"This is foo\" is changed to \"This is bar\", the diff\n> will now show \"This is \" in plain text, \"foo\" in red, and \"bar\" in green.\n>\n> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nThis looks sooooooooooo strange (and I do not particularly like\ncolours, so I am biased).  We might want to disable it when the\noutput would not be colored under diff.color = auto, at least,\nbut if the user asks to shoot himself in the foot that is fine\nas well ;-).\n"},{"id":"24426","messageId":"Pine.LNX.4.63.0607311257000.29667@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5031","inReplyTo":"7vbqr66o4e.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] Add the --color-words option to the diff options family","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-07-31T11:00:04Z","receivedAt":"2006-07-31T11:00:04Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 31 Jul 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > With this option, the changed words are shown inline. For example,\n> > if a file containing \"This is foo\" is changed to \"This is bar\", the diff\n> > will now show \"This is \" in plain text, \"foo\" in red, and \"bar\" in green.\n> >\n> > Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> \n> This looks sooooooooooo strange (and I do not particularly like\n> colours, so I am biased).\n\nI thought so, too, after playing around with wdiff. But I do use it \nregularly, and it is helpful. It is my equivalent to the word processor \noption to show all changes (yes, live and in colour).\n\n> We might want to disable it when the output would not be colored under \n> diff.color = auto, at least, but if the user asks to shoot himself in \n> the foot that is fine as well ;-).\n\nAt the moment, this is _only_ enabled when the user explicitely asks for \nit: no --color-words, no word colours. Do you want to be able to say \n\"git-diff --color-words | patch -p1 -R\"?\n\nCiao,\nDscho\n"}]}