@matheusbsbs / matheus-bagliano / commits / 80c49c3de2
Fork 0

color-words: make regex configurable via attributes

Make the --color-words splitting regular expression configurable via the diff driver's 'wordregex' attribute. The user can then set the driver on a file in .gitattributes. If a regex is given on the command line, it overrides the driver's setting. We also provide built-in regexes for the languages that already had funcname patterns, and add an appropriate diff driver entry for C/++. (The patterns are designed to run UTF-8 sequences into a single chunk to make sure they remain readable.) Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Thomas Rast committed Jan 17, 2009 at 17:29 UTC 80c49c3de2d5a3aa12b0980a65f1163c8aef0c16
6 files changed +135 -15
Documentation/diff-options.txt
+4
@@ -102,6 +102,10 @@ differences. You may want to append `|[^[:space:]]` to your regular
102 expression to make sure that it matches all non-whitespace characters.
103 A match that contains a newline is silently truncated(!) at the
104 newline.
105 ++
106 +The regex can also be set via a diff driver, see
107 +linkgit:gitattributes[1]; giving it explicitly overrides any diff
108 +driver setting.
109
110 --no-renames::
111 Turn off rename detection, even when the configuration
Documentation/gitattributes.txt
+21
@@ -317,6 +317,8 @@ patterns are available:
317
318 - `bibtex` suitable for files with BibTeX coded references.
319
320 +- `cpp` suitable for source code in the C and C++ languages.
321 +
322 - `html` suitable for HTML/XHTML documents.
323
324 - `java` suitable for source code in the Java language.
@@ -334,6 +336,25 @@ patterns are available:
336 - `tex` suitable for source code for LaTeX documents.
337
338
339 +Customizing word diff
340 +^^^^^^^^^^^^^^^^^^^^^
341 +
342 +You can customize the rules that `git diff --color-words` uses to
343 +split words in a line, by specifying an appropriate regular expression
344 +in the "diff.*.wordregex" configuration variable. For example, in TeX
345 +a backslash followed by a sequence of letters forms a command, but
346 +several such commands can be run together without intervening
347 +whitespace. To separate them, use a regular expression such as
348 +
349 +------------------------
350 +[diff "tex"]
351 + wordregex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"
352 +------------------------
353 +
354 +A built-in pattern is provided for all languages listed in the
355 +previous section.
356 +
357 +
358 Performing text diffs of binary files
359 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
360
diff.c
+10
@@ -1380,6 +1380,12 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe
1380 return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
1381 }
1382
1383 +static const char *userdiff_word_regex(struct diff_filespec *one)
1384 +{
1385 + diff_filespec_load_driver(one);
1386 + return one->driver->word_regex;
1387 +}
1388 +
1389 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
1390 {
1391 if (!options->a_prefix)
@@ -1540,6 +1546,10 @@ static void builtin_diff(const char *name_a,
1546 ecbdata.diff_words =
1547 xcalloc(1, sizeof(struct diff_words_data));
1548 ecbdata.diff_words->file = o->file;
1549 + if (!o->word_regex)
1550 + o->word_regex = userdiff_word_regex(one);
1551 + if (!o->word_regex)
1552 + o->word_regex = userdiff_word_regex(two);
1553 if (o->word_regex) {
1554 ecbdata.diff_words->word_regex = (regex_t *)
1555 xmalloc(sizeof(regex_t));
t/t4034-diff-words.sh
+36
@@ -84,6 +84,41 @@ test_expect_success 'word diff with a regular expression' '
84
85 '
86
87 +test_expect_success 'set a diff driver' '
88 + git config diff.testdriver.wordregex "[^[:space:]]" &&
89 + cat <<EOF > .gitattributes
90 +pre diff=testdriver
91 +post diff=testdriver
92 +EOF
93 +'
94 +
95 +test_expect_success 'option overrides default' '
96 +
97 + word_diff --color-words="[a-z]+"
98 +
99 +'
100 +
101 +cat > expect <<\EOF
102 +<WHITE>diff --git a/pre b/post<RESET>
103 +<WHITE>index 330b04f..5ed8eff 100644<RESET>
104 +<WHITE>--- a/pre<RESET>
105 +<WHITE>+++ b/post<RESET>
106 +<BROWN>@@ -1,3 +1,7 @@<RESET>
107 +h(4)<GREEN>,hh[44]<RESET>
108 +<RESET>
109 +a = b + c<RESET>
110 +
111 +<GREEN>aa = a<RESET>
112 +
113 +<GREEN>aeff = aeff * ( aaa )<RESET>
114 +EOF
115 +
116 +test_expect_success 'use default supplied by driver' '
117 +
118 + word_diff --color-words
119 +
120 +'
121 +
122 echo 'aaa (aaa)' > pre
123 echo 'aaa (aaa) aaa' > post
124
@@ -100,6 +135,7 @@ test_expect_success 'test parsing words for newline' '
135
136 word_diff --color-words="a+"
137
138 +
139 '
140
141 echo '(:' > pre
userdiff.c
+63 -15
@@ -6,14 +6,20 @@ static struct userdiff_driver *drivers;
6 static int ndrivers;
7 static int drivers_alloc;
8
9 -#define FUNCNAME(name, pattern) \
10 - { name, NULL, -1, { pattern, REG_EXTENDED } }
9 +#define PATTERNS(name, pattern, wordregex) \
10 + { name, NULL, -1, { pattern, REG_EXTENDED }, wordregex }
11 static struct userdiff_driver builtin_drivers[] = {
12 -FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"),
13 -FUNCNAME("java",
12 +PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
13 + "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
14 +PATTERNS("java",
15 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
15 - "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"),
16 -FUNCNAME("objc",
16 + "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
17 + "[a-zA-Z_][a-zA-Z0-9_]*"
18 + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
19 + "|[-+*/<>%&^|=!]="
20 + "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
21 + "|[^[:space:]]|[\x80-\xff]+"),
22 +PATTERNS("objc",
23 /* Negate C statements that can look like functions */
24 "!^[ \t]*(do|for|if|else|return|switch|while)\n"
25 /* Objective-C methods */
@@ -21,20 +27,60 @@ FUNCNAME("objc",
27 /* C functions */
28 "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
29 /* Objective-C class/protocol definitions */
24 - "^(@(implementation|interface|protocol)[ \t].*)$"),
25 -FUNCNAME("pascal",
30 + "^(@(implementation|interface|protocol)[ \t].*)$",
31 + /* -- */
32 + "[a-zA-Z_][a-zA-Z0-9_]*"
33 + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
34 + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
35 + "|[^[:space:]]|[\x80-\xff]+"),
36 +PATTERNS("pascal",
37 "^((procedure|function|constructor|destructor|interface|"
38 "implementation|initialization|finalization)[ \t]*.*)$"
39 "\n"
29 - "^(.*=[ \t]*(class|record).*)$"),
30 -FUNCNAME("php", "^[\t ]*((function|class).*)"),
31 -FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"),
32 -FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"),
33 -FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"),
34 -FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"),
40 + "^(.*=[ \t]*(class|record).*)$",
41 + /* -- */
42 + "[a-zA-Z_][a-zA-Z0-9_]*"
43 + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
44 + "|<>|<=|>=|:=|\\.\\."
45 + "|[^[:space:]]|[\x80-\xff]+"),
46 +PATTERNS("php", "^[\t ]*((function|class).*)",
47 + /* -- */
48 + "[a-zA-Z_][a-zA-Z0-9_]*"
49 + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
50 + "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
51 + "|[^[:space:]]|[\x80-\xff]+"),
52 +PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
53 + /* -- */
54 + "[a-zA-Z_][a-zA-Z0-9_]*"
55 + "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
56 + "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
57 + "|[^[:space:]|[\x80-\xff]+"),
58 + /* -- */
59 +PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
60 + /* -- */
61 + "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
62 + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
63 + "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
64 + "|[^[:space:]|[\x80-\xff]+"),
65 +PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
66 + "[={}\"]|[^={}\" \t]+"),
67 +PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
68 + "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
69 +PATTERNS("cpp",
70 + /* Jump targets or access declarations */
71 + "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
72 + /* C/++ functions/methods at top level */
73 + "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
74 + /* compound type at top level */
75 + "^((struct|class|enum)[^;]*)$",
76 + /* -- */
77 + "[a-zA-Z_][a-zA-Z0-9_]*"
78 + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
79 + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
80 + "|[^[:space:]]|[\x80-\xff]+"),
81 { "default", NULL, -1, { NULL, 0 } },
82 };
37 -#undef FUNCNAME
83 +#undef PATTERNS
84
85 static struct userdiff_driver driver_true = {
86 "diff=true",
@@ -134,6 +180,8 @@ int userdiff_config(const char *k, const char *v)
180 return parse_string(&drv->external, k, v);
181 if ((drv = parse_driver(k, v, "textconv")))
182 return parse_string(&drv->textconv, k, v);
183 + if ((drv = parse_driver(k, v, "wordregex")))
184 + return parse_string(&drv->word_regex, k, v);
185
186 return 0;
187 }
userdiff.h
+1
@@ -11,6 +11,7 @@ struct userdiff_driver {
11 const char *external;
12 int binary;
13 struct userdiff_funcname funcname;
14 + const char *word_regex;
15 const char *textconv;
16 };
17