{"thread":{"id":"28637","subject":"[PATCH 1/2] xdiff: factor out get_func_line()","startedAt":"2011-10-09T11:34:49Z","lastAt":"2011-10-09T11:36:57Z","messageCount":2,"participants":["René Scharfe"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"177271","messageId":"4E9186D9.4060805@lsrfire.ath.cx","threadId":"28637","inReplyTo":null,"subject":"[PATCH 1/2] xdiff: factor out get_func_line()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-10-09T11:34:49Z","receivedAt":"2011-10-09T11:34:49Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Move the code to search for a function line to be shown in the hunk\nheader into its own function and to make returning the length-limited\nresult string easier, introduce struct func_line.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n xdiff/xemit.c |   43 +++++++++++++++++++++++++++----------------\n 1 files changed, 27 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 277e2ee..64eb17a 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -100,14 +100,35 @@ static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \treturn 0;\n }\n \n+struct func_line {\n+\tlong len;\n+\tchar buf[80];\n+};\n+\n+static void get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n+\t\t\t  struct func_line *func_line, long start, long limit)\n+{\n+\tfind_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;\n+\tlong l, size = sizeof(func_line->buf);\n+\tchar *buf = func_line->buf;\n+\n+\tfor (l = start; l > limit && 0 <= l; l--) {\n+\t\tconst char *rec;\n+\t\tlong reclen = xdl_get_rec(&xe->xdf1, l, &rec);\n+\t\tlong len = ff(rec, reclen, buf, size, xecfg->find_func_priv);\n+\t\tif (len >= 0) {\n+\t\t\tfunc_line->len = len;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t  xdemitconf_t const *xecfg) {\n \tlong s1, s2, e1, e2, lctx;\n \txdchange_t *xch, *xche;\n-\tchar funcbuf[80];\n-\tlong funclen = 0;\n \tlong funclineprev = -1;\n-\tfind_func_t ff = xecfg->find_func ?  xecfg->find_func : def_ff;\n+\tstruct func_line func_line = { 0 };\n \n \tif (xecfg->flags & XDL_EMIT_COMMON)\n \t\treturn xdl_emit_common(xe, xscr, ecb, xecfg);\n@@ -130,22 +151,12 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t */\n \n \t\tif (xecfg->flags & XDL_EMIT_FUNCNAMES) {\n-\t\t\tlong l;\n-\t\t\tfor (l = s1 - 1; l >= 0 && l > funclineprev; l--) {\n-\t\t\t\tconst char *rec;\n-\t\t\t\tlong reclen = xdl_get_rec(&xe->xdf1, l, &rec);\n-\t\t\t\tlong newfunclen = ff(rec, reclen, funcbuf,\n-\t\t\t\t\t\t     sizeof(funcbuf),\n-\t\t\t\t\t\t     xecfg->find_func_priv);\n-\t\t\t\tif (newfunclen >= 0) {\n-\t\t\t\t\tfunclen = newfunclen;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n+\t\t\tget_func_line(xe, xecfg, &func_line,\n+\t\t\t\t      s1 - 1, funclineprev);\n \t\t\tfunclineprev = s1 - 1;\n \t\t}\n \t\tif (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,\n-\t\t\t\t      funcbuf, funclen, ecb) < 0)\n+\t\t\t\t      func_line.buf, func_line.len, ecb) < 0)\n \t\t\treturn -1;\n \n \t\t/*\n-- \n1.7.7\n"},{"id":"177273","messageId":"4E918759.8020409@lsrfire.ath.cx","threadId":"28637","inReplyTo":"4E9186D9.4060805@lsrfire.ath.cx","subject":"[PATCH 2/2] diff: add option to show whole functions as context","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-10-09T11:36:57Z","receivedAt":"2011-10-09T11:36:57Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Add the option -W/--function-context to git diff.  It is similar to\nthe same option of git grep and expands the context of change hunks\nso that the whole surrounding function is shown.  This \"natural\"\ncontext can allow changes to be understood better.\n\nNote: GNU patch doesn't like diffs generated with the new option;\nit seems to expect context lines to be the same before and after\nchanges.  git apply doesn't complain.\n\nThis implementation has the same shortcoming as the one in grep,\nnamely that there is no way to explicitly find the end of a\nfunction.  That means that a few lines of extra context are shown,\nright up to the next recognized function begins.  It's already\nuseful in its current form, though.\n\n\nThe function get_func_line() in xdiff/xemit.c is extended to work\nforward as well as backward to find post-context as well as\npre-context.  It returns the position of the first found matching\nline.  The func_line parameter is made optional, as we don't need\nit for -W.\n\nThe enhanced function is then used in xdl_emit_diff() to extend\nthe context as needed.  If the added context overlaps with the\nnext change, it is merged into the current hunk.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/diff-options.txt   |    4 ++\n diff.c                           |    8 +++\n diff.h                           |    1 +\n t/t4051-diff-function-context.sh |   92 ++++++++++++++++++++++++++++++++++++++\n xdiff/xdiff.h                    |    1 +\n xdiff/xemit.c                    |   54 ++++++++++++++++++++---\n 6 files changed, 154 insertions(+), 6 deletions(-)\n create mode 100644 t/t4051-diff-function-context.sh\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex b620b3a..1ce4ae3 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -404,6 +404,10 @@ endif::git-format-patch[]\n \tShow the context between diff hunks, up to the specified number\n \tof lines, thereby fusing hunks that are close to each other.\n \n+-W::\n+--function-context::\n+\tShow whole surrounding functions of changes.\n+\n ifndef::git-format-patch[]\n --exit-code::\n \tMake the program exit with codes similar to diff(1).\ndiff --git a/diff.c b/diff.c\nindex fcc0078..42c4891 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2169,6 +2169,8 @@ static void builtin_diff(const char *name_a,\n \t\txecfg.ctxlen = o->context;\n \t\txecfg.interhunkctxlen = o->interhunkcontext;\n \t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n+\t\tif (DIFF_OPT_TST(o, FUNCCONTEXT))\n+\t\t\txecfg.flags |= XDL_EMIT_FUNCCONTEXT;\n \t\tif (pe)\n \t\t\txdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);\n \t\tif (!diffopts)\n@@ -3530,6 +3532,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \telse if (opt_arg(arg, '\\0', \"inter-hunk-context\",\n \t\t\t &options->interhunkcontext))\n \t\t;\n+\telse if (!strcmp(arg, \"-W\"))\n+\t\tDIFF_OPT_SET(options, FUNCCONTEXT);\n+\telse if (!strcmp(arg, \"--function-context\"))\n+\t\tDIFF_OPT_SET(options, FUNCCONTEXT);\n+\telse if (!strcmp(arg, \"--no-function-context\"))\n+\t\tDIFF_OPT_CLR(options, FUNCCONTEXT);\n \telse if ((argcount = parse_long_opt(\"output\", av, &optarg))) {\n \t\toptions->file = fopen(optarg, \"w\");\n \t\tif (!options->file)\ndiff --git a/diff.h b/diff.h\nindex 8c66b59..0c51724 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -79,6 +79,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n #define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n+#define DIFF_OPT_FUNCCONTEXT         (1 << 29)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh\nnew file mode 100644\nindex 0000000..001d678\n--- /dev/null\n+++ b/t/t4051-diff-function-context.sh\n@@ -0,0 +1,92 @@\n+#!/bin/sh\n+\n+test_description='diff function context'\n+\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/diff-lib.sh\n+\n+\n+cat <<\\EOF >hello.c\n+#include <stdio.h>\n+\n+static int a(void)\n+{\n+\t/*\n+\t * Dummy.\n+\t */\n+}\n+\n+static int hello_world(void)\n+{\n+\t/* Classic. */\n+\tprintf(\"Hello world.\\n\");\n+\n+\t/* Success! */\n+\treturn 0;\n+}\n+static int b(void)\n+{\n+\t/*\n+\t * Dummy, too.\n+\t */\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\ta();\n+\tb();\n+\treturn hello_world();\n+}\n+EOF\n+\n+test_expect_success 'setup' '\n+\tgit add hello.c &&\n+\ttest_tick &&\n+\tgit commit -m initial &&\n+\n+\tgrep -v Classic <hello.c >hello.c.new &&\n+\tmv hello.c.new hello.c\n+'\n+\n+cat <<\\EOF >expected\n+diff --git a/hello.c b/hello.c\n+--- a/hello.c\n++++ b/hello.c\n+@@ -10,8 +10,7 @@ static int a(void)\n+ static int hello_world(void)\n+ {\n+-\t/* Classic. */\n+ \tprintf(\"Hello world.\\n\");\n+ \n+ \t/* Success! */\n+ \treturn 0;\n+ }\n+EOF\n+\n+test_expect_success 'diff -U0 -W' '\n+\tgit diff -U0 -W >actual &&\n+\tcompare_diff_patch actual expected\n+'\n+\n+cat <<\\EOF >expected\n+diff --git a/hello.c b/hello.c\n+--- a/hello.c\n++++ b/hello.c\n+@@ -9,9 +9,8 @@ static int a(void)\n+ \n+ static int hello_world(void)\n+ {\n+-\t/* Classic. */\n+ \tprintf(\"Hello world.\\n\");\n+ \n+ \t/* Success! */\n+ \treturn 0;\n+ }\n+EOF\n+\n+test_expect_success 'diff -W' '\n+\tgit diff -W >actual &&\n+\tcompare_diff_patch actual expected\n+'\n+\n+test_done\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 4beb10c..00d36c3 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -43,6 +43,7 @@ extern \"C\" {\n \n #define XDL_EMIT_FUNCNAMES (1 << 0)\n #define XDL_EMIT_COMMON (1 << 1)\n+#define XDL_EMIT_FUNCCONTEXT (1 << 2)\n \n #define XDL_MMB_READONLY (1 << 0)\n \ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 64eb17a..2e669c3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -105,22 +105,27 @@ struct func_line {\n \tchar buf[80];\n };\n \n-static void get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \t\t\t  struct func_line *func_line, long start, long limit)\n {\n \tfind_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;\n-\tlong l, size = sizeof(func_line->buf);\n-\tchar *buf = func_line->buf;\n+\tlong l, size, step = (start > limit) ? -1 : 1;\n+\tchar *buf, dummy[1];\n \n-\tfor (l = start; l > limit && 0 <= l; l--) {\n+\tbuf = func_line ? func_line->buf : dummy;\n+\tsize = func_line ? sizeof(func_line->buf) : sizeof(dummy);\n+\n+\tfor (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {\n \t\tconst char *rec;\n \t\tlong reclen = xdl_get_rec(&xe->xdf1, l, &rec);\n \t\tlong len = ff(rec, reclen, buf, size, xecfg->find_func_priv);\n \t\tif (len >= 0) {\n-\t\t\tfunc_line->len = len;\n-\t\t\tbreak;\n+\t\t\tif (func_line)\n+\t\t\t\tfunc_line->len = len;\n+\t\t\treturn l;\n \t\t}\n \t}\n+\treturn -1;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n@@ -139,6 +144,17 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\ts1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);\n \t\ts2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);\n \n+\t\tif (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {\n+\t\t\tlong fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);\n+\t\t\tif (fs1 < 0)\n+\t\t\t\tfs1 = 0;\n+\t\t\tif (fs1 < s1) {\n+\t\t\t\ts2 -= s1 - fs1;\n+\t\t\t\ts1 = fs1;\n+\t\t\t}\n+\t\t}\n+\n+ again:\n \t\tlctx = xecfg->ctxlen;\n \t\tlctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));\n \t\tlctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));\n@@ -146,6 +162,32 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\te1 = xche->i1 + xche->chg1 + lctx;\n \t\te2 = xche->i2 + xche->chg2 + lctx;\n \n+\t\tif (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {\n+\t\t\tlong fe1 = get_func_line(xe, xecfg, NULL,\n+\t\t\t\t\t\t xche->i1 + xche->chg1,\n+\t\t\t\t\t\t xe->xdf1.nrec);\n+\t\t\tif (fe1 < 0)\n+\t\t\t\tfe1 = xe->xdf1.nrec;\n+\t\t\tif (fe1 > e1) {\n+\t\t\t\te2 += fe1 - e1;\n+\t\t\t\te1 = fe1;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Overlap with next change?  Then include it\n+\t\t\t * in the current hunk and start over to find\n+\t\t\t * its new end.\n+\t\t\t */\n+\t\t\tif (xche->next) {\n+\t\t\t\tlong l = xche->next->i1;\n+\t\t\t\tif (l <= e1 ||\n+\t\t\t\t    get_func_line(xe, xecfg, NULL, l, e1) < 0) {\n+\t\t\t\t\txche = xche->next;\n+\t\t\t\t\tgoto again;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n \t\t/*\n \t\t * Emit current hunk header.\n \t\t */\n-- \n1.7.7\n"}]}