[PATCH] userdiff: add support for Emacs Lisp

Subsystems: documentation, the rest

STALE1943d

158 messages, 7 authors, 2021-04-09 · page 2 of 3 · open the first message on its own page

Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-15 20:07:35

On Mon, Feb 15 2021, Johannes Sixt wrote:
Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
quoted
Rewrite the hunk header test infrastructure introduced in
bfa7d01413 (t4018: an infrastructure to test hunk headers,
2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',
2014-03-31) for the whole series that commit was part of.
As noted in an earlier commit that change introduced the regression
of
not testing for the full hunk line, but just whether "RIGHT" appeared
on it[1]. A preceding commit fixed that specific issue, but we were
still left with the inflexibility of the approach described in the
now-deleted t/t4018/README.
I.e. to add any sort of new tests that used the existing test data
we'd either need to add more files like the recently added (but now
deleted) *.ctx) files, using the filesystem as our test datastructure,
or introduce more parsing for the custom file format we were growing
here.
Let's instead just move this over to using a custom test
function. This makes it trivial to add new tests by adding new
optional parameters to the function. Let's still keep the relevant
files in the "t/t4018/" subdirectory instead of adding ~1.5k
lines (and growing) to "t/t4018-diff-funcname.sh"
If this diff is viewed with "--color-moved=plain" we can see that
there's no changes to the lines being moved into the new *.sh files,
i.e. all the deletions are moves. I'm just adding boilerplate around
those existing lines.
The one-off refactoring was performed by an ad-hoc shellscript [2].
1. https://lore.kernel.org/git/87wnvbbf2y.fsf@evledraar.gmail.com/
2.
	#!/bin/sh
	set -ex
	git rm README*
	for t in $(git ls-files ':!*.ctx')
	do
		lang=$(echo $t | sed 's/-.*//')
		desc=$(echo $t | sed -E 's/^[^-]*-//' | tr - " ")
		if ! test -e $lang.sh
		then
			cat >$lang.sh <<-EOF
			#!/bin/sh
			#
			# See ../t4018-diff-funcname.sh's test_diff_funcname()
			#
			EOF
		else
			echo >>$lang.sh
	        fi
		(
	            printf "test_diff_funcname '%s: %s' \\" "$lang" "$desc"
	            echo
	            printf "\t8<<%sEOF_HUNK 9<<%sEOF_TEST\n" '\' '\'
	            cat $t.ctx
	            printf "EOF_HUNK\n"
	            cat $t
	            printf "EOF_TEST\n"
		) >>$lang.sh
		chmod +x $lang.sh
		git add $lang.sh
	        git rm $t $t.ctx
	done
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
quoted
diff --git a/t/t4018/bash.sh b/t/t4018/bash.sh
new file mode 100755
index 0000000000..69144d9144
--- /dev/null
+++ b/t/t4018/bash.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'bash: arithmetic function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() ((
+
+    ChangeMe = "$x" + "$y"
+))
+EOF_TEST
+
+test_diff_funcname 'bash: bashism style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    function InvalidSyntax{
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: bashism style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    :
+    echo 'ChangeMe'
+}
+EOF_TEST
[...]
That is not my dream of "simple". But I'm not a userdiff author
anymore, so...

I don't know, yet, where this is heading to what the advantage is. At
any rate,[...]
I originally started writing this because I noticed I could break the
userdiff.c patterns and still have all tests pass, i.e. if you screw up
the capture grouping you can go from:
    
    @@ -2,3 +2,3 @@ function        RIGHT   (       )       {
    @@ -2,3 +2,3 @@ RIGHT   (       )

to:
    
    @@ -2,3 +2,3 @@ function        RIGHT   (       )       {
    @@ -2,3 +2,3 @@          RIGHT  (       )
    
And we wouldn't care because we just "grep 'RIGHT'". In this case we
really care about the difference between "^[ \t]*(.*)$" and a broken
"^([ \t]*.*)$" so not having the tests structurally hide the difference
makes sense.
[...] "trivial to add new tests" was also the case when each test case
was in its own file[...]
"trivial to add new tests by adding new optional parameters to the
function". I.e. aside from the s/grep/test_cmp/ change in 09/27 the
existing tests were OK if you wanted to test exactly what they expected,
and no more.

I think it just makes sense to have a test helper function instead and
little bit of boilerplate, as seen e.g. in 14/27 and later in the series
we can add new test modes and set per-test config without needing the
top-level dispatch loop to be aware of it.
[...] Without the boilerplate!
I realize that's a matter of taste, i.e. when to come up with some
custom format v.s. writng a function.

FWIW as someone who didn't author the format I've come across it N times
over the years and each time ended up being more confused than when
reading any custom test function we have.

For those you can usually just look at the definition/arguments, whereas
this always required a careful read of t4018-diff-funcname.sh.

I also find it easier to have one ~160 line file in my editor than ~150
lines spread over 15 files, as in the recent addition of bash support in
2ff6c34612 (userdiff: support Bash, 2020-10-22).

It also depends on how you're counting boilerplate, if you're looking at
it as a patch on the ML it would be ~160 lines of bash.sh, v.s. ~150
lines of the same content, if we're counting the boilerplate diff of 6
lines for every new file :)

Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure

From: Johannes Sixt <hidden>
Date: 2021-02-15 20:30:08

Am 15.02.21 um 21:06 schrieb Ævar Arnfjörð Bjarmason:
On Mon, Feb 15 2021, Johannes Sixt wrote:
quoted
Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
quoted
+test_diff_funcname 'bash: bashism style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    function InvalidSyntax{
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: bashism style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    :
+    echo 'ChangeMe'
+}
+EOF_TEST
[...]
That is not my dream of "simple". But I'm not a userdiff author
anymore, so...

I don't know, yet, where this is heading to what the advantage is. At
any rate,[...]
I originally started writing this because I noticed I could break the
userdiff.c patterns and still have all tests pass, i.e. if you screw up
the capture grouping you can go from:
     
     @@ -2,3 +2,3 @@ function        RIGHT   (       )       {
     @@ -2,3 +2,3 @@ RIGHT   (       )

to:
     
     @@ -2,3 +2,3 @@ function        RIGHT   (       )       {
     @@ -2,3 +2,3 @@          RIGHT  (       )
     
And we wouldn't care because we just "grep 'RIGHT'". In this case we
really care about the difference between "^[ \t]*(.*)$" and a broken
"^([ \t]*.*)$" so not having the tests structurally hide the difference
makes sense.
But why care? The second version is not wrong. If it's not pretty, the 
pattern author will notice soon enough. I regard avoiding the regression 
you cite less important than the simplicity of adding new test cases.
quoted
[...] "trivial to add new tests" was also the case when each test case
was in its own file[...]
"trivial to add new tests by adding new optional parameters to the
function". I.e. aside from the s/grep/test_cmp/ change in 09/27 the
existing tests were OK if you wanted to test exactly what they expected,
and no more.
I did not understand what you meant with "new optional parameters" and I 
still do not and why it is desirable.
I think it just makes sense to have a test helper function instead and
little bit of boilerplate, as seen e.g. in 14/27 and later in the series
we can add new test modes and set per-test config without needing the
top-level dispatch loop to be aware of it.
quoted
[...] Without the boilerplate!
I realize that's a matter of taste, i.e. when to come up with some
custom format v.s. writng a function.

FWIW as someone who didn't author the format I've come across it N times
over the years and each time ended up being more confused than when
reading any custom test function we have.

For those you can usually just look at the definition/arguments, whereas
this always required a careful read of t4018-diff-funcname.sh.

I also find it easier to have one ~160 line file in my editor than ~150
lines spread over 15 files, as in the recent addition of bash support in
2ff6c34612 (userdiff: support Bash, 2020-10-22).
I wouldn't mind having all test cases in a single file. But then it 
should be one file in the language that is being tested, not shell 
script with foreign text between the lines. Adding test cases should be 
easy for authors; they should not have to be proficient in shell 
scripting (and knwo about 130 lines of CodingGuidelines).

-- Hannes

Re: [PATCH 1/2] diff: do not display hunk context under -W

From: René Scharfe. <hidden>
Date: 2021-02-15 21:19:55

Am 15.02.21 um 20:24 schrieb Ævar Arnfjörð Bjarmason:
On Mon, Feb 15 2021, René Scharfe. wrote:
quoted
Your reasoning applies to patches generated without -W as well.  If the
precontext contains a function line then the @@ line should not contain
a function comment.  However, e.g. with this:

-- snip --
cat >a <<EOF
func a

func b
1
2
3
EOF
sed 's/3/three/' <a >b
diff -up a b
-- snap --

... I get this:
--- a	2021-02-15 18:30:21.000000000 +0100
+++ b	2021-02-15 18:30:21.000000000 +0100
@@ -3,4 +3,4 @@ func a
 func b
 1
 2
-3
+three
So diff(1) shows the previous function line.  git diff does the same.

The behaviour of diff(1) and git diff does make sense to me: It's easy
to implement and the only downside is that it produces extra output in
some cases.

I can understand that users would rather have a tidy diff without
distractions, though.  So I like the output change you propose.
Does GNU diff have something like git's -W, both "diff -U 0 -F func a b"
and "diff -U 0 -p a b" don't extend the context window as we do.
Exactly my point: GNU diff doesn't have something like -W, but still can
show an unchanged function at the @@ line, because it searches upwards
starting just above the first shown line, and the name of the actually
changed function might be in one of the shown lines.
I don't think the patch I'm submitting here would make sense for GNU
diff, since there it just shows the context without being guaranteed to
show the full set of lines leading up to it under -W, but with Git diff
we do that, so I think it makes sense to omit the context.
Given the goal to show the name of the changed function it *would* make
sense to start searching at the first changed line instead.
quoted
However, I'm not sure it would be a good idea to clear @@ lines of hunks
generated without -W that have function lines in their precontext, even
though it would be a logical thing to do.
Yes, I don't think that's a good idea either. I think it only makes
sense under -W where the user explicitly asks "show me the function this
change was in", and we're (before this patch) showing different context
on the basis of emergent behavior.
Right, -W never needs diff(1)'s -p, while the case is less clear for
diffs generated without -W.

René

Re: [PATCH 2/2] diff: test and document -W interaction with -U<n>

From: Johannes Sixt <hidden>
Date: 2021-02-16 07:26:53

Am 15.02.21 um 16:50 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
  Documentation/diff-options.txt | 8 ++++++++
  t/t4018-diff-funcname.sh       | 5 +++++
  2 files changed, 13 insertions(+)
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 8ca59effa7..3c19c78616 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -88,6 +88,11 @@ endif::git-log[]
  --unified=<n>::
  	Generate diffs with <n> lines of context instead of
  	the usual three.
++
+Under `-W` generates diffs with at least <n> lines of context, if the
+number is lower than the context `-U<n>` would extend the diff to then
+`-U<n>` takes precedence.
+
How about (not as separate paragraph):

When combined with `--function-context`, this specifies the minimum of 
context lines.
quoted hunk
  ifndef::git-format-patch[]
  	Implies `--patch`.
  endif::git-format-patch[]
@@ -763,6 +768,9 @@ endif::git-format-patch[]
  When showing the whole function for context the "@@" context line
  itself will always be empty, since the context that would otherwise be
  shown there will be the first line of the hunk being shown.
++
+See the documentation for `-U<n>` above for how the two options
+interact.
How about

Use `-U<n>` to specify a minimum of context (default three lines).

so that readers do not have to search.

-- Hannes

Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-17 01:34:12

On Mon, Feb 15 2021, Johannes Sixt wrote:
Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
quoted
Simplify the test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) to use the --author support recently
added in 999cfc4f45 (test-lib functions: add --author support to
test_commit, 2021-01-12).
We also did not need the full fortran-external-function content,
let's
cut it down to just the important parts, and further modify it to
demonstrate that the fortran-specific userdiff function is in effect
by adding "WRONG" lines surrounding the "RIGHT" one.
The test also left behind a .gitattributes files, let's clean it up
with "test_when_finished".
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
  t/annotate-tests.sh | 36 +++++++++++++++---------------------
  1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 04a2c58594..4a86e0f349 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
  	check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
  '
  -test_expect_success 'setup -L :funcname with userdiff driver' '
-	echo "fortran-* diff=fortran" >.gitattributes &&
-	fortran_file=fortran-external-function &&
-	cat >$fortran_file <<-\EOF &&
+test_expect_success 'blame -L :funcname with userdiff driver' '
+	cat >file.template <<-\EOF &&
+	def WRONG begin end
  	function RIGHT(a, b) result(c)
+	int WRONG(void) {}
    	integer, intent(in) :: ChangeMe
-	integer, intent(in) :: b
-	integer, intent(out) :: c
-
-	c = a+b
-
-	end function RIGHT
  	EOF
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
-	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
-	mv "$fortran_file".tmp "$fortran_file" &&
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
-	git commit -m "change fortran file"
-'
  -test_expect_success 'blame -L :funcname with userdiff driver' '
-	check_count -f fortran-external-function -L:RIGHT A 7 B 1
+	fortran_file=file.f03 &&
+	test_when_finished "rm .gitattributes" &&
+	echo "$fortran_file diff=fortran" >.gitattributes &&
+
+	test_commit --author "A <A@test.git>" \
+		"add" $fortran_file \
+		"$(cat file.template)" &&
+	test_commit --author "B <B@test.git>" \
+		"change" $fortran_file \
+		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+	check_count -f $fortran_file -L:RIGHT A 3 B 1
  '
    test_expect_success 'setup incremental' '
I don't get the point. What do you need the tokens "WRONG" for when
they are not checked anywhere? Instead of adding unrelated lines (that
do not even look like Fortran), couldn't you just not remove some of
the others? In particular, the last one that contains "RIGHT" as well
may be useful to keep in order to show that the code is not confused
by it.
Isn't the point of the test to assert that we're using a userdiff driver
over the built-in xdiff rules here?

We can imagine that a change to its default heuristics would be to find
the first non-whitespace line, but it jumping over non-whitespace lines
in search of a fortran-looking line doesn't seem like it would ever
happen. Hence the WRONG lines.
Please place "$fortran_file" in dquotes on the check_count line.
Why do we need to dquote a convenience variable defined in the test
itself that'll never contain spaces or other funny things we'd get if we
had $(pwd) or whatever in there? It wouldn't hurt, but maybe I'm missing
some reason for why it's necessary or desired here.

Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test

From: Johannes Sixt <hidden>
Date: 2021-02-17 07:11:06

Am 17.02.21 um 02:33 schrieb Ævar Arnfjörð Bjarmason:
On Mon, Feb 15 2021, Johannes Sixt wrote:
quoted
Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
quoted
Simplify the test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) to use the --author support recently
added in 999cfc4f45 (test-lib functions: add --author support to
test_commit, 2021-01-12).
We also did not need the full fortran-external-function content,
let's
cut it down to just the important parts, and further modify it to
demonstrate that the fortran-specific userdiff function is in effect
by adding "WRONG" lines surrounding the "RIGHT" one.
The test also left behind a .gitattributes files, let's clean it up
with "test_when_finished".
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
   t/annotate-tests.sh | 36 +++++++++++++++---------------------
   1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 04a2c58594..4a86e0f349 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
   	check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
   '
   -test_expect_success 'setup -L :funcname with userdiff driver' '
-	echo "fortran-* diff=fortran" >.gitattributes &&
-	fortran_file=fortran-external-function &&
-	cat >$fortran_file <<-\EOF &&
+test_expect_success 'blame -L :funcname with userdiff driver' '
+	cat >file.template <<-\EOF &&
+	def WRONG begin end
   	function RIGHT(a, b) result(c)
+	int WRONG(void) {}
     	integer, intent(in) :: ChangeMe
-	integer, intent(in) :: b
-	integer, intent(out) :: c
-
-	c = a+b
-
-	end function RIGHT
   	EOF
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
-	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
-	mv "$fortran_file".tmp "$fortran_file" &&
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
-	git commit -m "change fortran file"
-'
   -test_expect_success 'blame -L :funcname with userdiff driver' '
-	check_count -f fortran-external-function -L:RIGHT A 7 B 1
+	fortran_file=file.f03 &&
+	test_when_finished "rm .gitattributes" &&
+	echo "$fortran_file diff=fortran" >.gitattributes &&
+
+	test_commit --author "A <A@test.git>" \
+		"add" $fortran_file \
+		"$(cat file.template)" &&
+	test_commit --author "B <B@test.git>" \
+		"change" $fortran_file \
+		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+	check_count -f $fortran_file -L:RIGHT A 3 B 1
   '
     test_expect_success 'setup incremental' '
I don't get the point. What do you need the tokens "WRONG" for when
they are not checked anywhere? Instead of adding unrelated lines (that
do not even look like Fortran), couldn't you just not remove some of
the others? In particular, the last one that contains "RIGHT" as well
may be useful to keep in order to show that the code is not confused
by it.
Isn't the point of the test to assert that we're using a userdiff driver
over the built-in xdiff rules here?

We can imagine that a change to its default heuristics would be to find
the first non-whitespace line, but it jumping over non-whitespace lines
in search of a fortran-looking line doesn't seem like it would ever
happen. Hence the WRONG lines.
Fair enough. A justification along these lines either as comment or in 
the commit message would have helped; the existing text in the commit 
message was not sufficient to get the point across. I was distracted by 
the capitalized "WRONG" token because we use a capitalized "RIGHT" token 
and check for it, so I expected that we also should check for "WRONG". 
Wouldn't it then just be sufficient to remove the blank lines from the 
exiting Fortran content?
quoted
Please place "$fortran_file" in dquotes on the check_count line.
I just noticed that there are more unquoted expansions above that line. 
(I'm just mentioning it for completeness.)

-- Hannes

[PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:52:56

Addresses feedback on v2. Since Junio & Johannes expressed a desire to
keep the existing test scheme in t4018/* it's still there, but it's
also possible to add *.sh tests in that directory to use the more
familiar test framework used elsewhere in the test suite.

The tests added here make use of it to e.g. supply custom -U<n>
arguments, set config before the tests etc.

I also improved that existing test support so you can have N tests in
one file with (mostly) the existing test syntax. See the "userdiff
tests: add a test with multiple tests in a LANG file" patch.

Range-diff below.

Ævar Arnfjörð Bjarmason (35):
  userdiff: refactor away the parse_bool() function
  userdiff style: re-order drivers in alphabetical order
  userdiff style: declare patterns with consistent style
  userdiff style: normalize pascal regex declaration
  userdiff: add and use for_each_userdiff_driver()
  userdiff tests: explicitly test "default" pattern
  userdiff tests: list builtin drivers via test-tool
  userdiff: remove support for "broken" tests
  blame tests: don't rely on t/t4018/ directory
  blame tests: simplify userdiff driver test
  userdiff tests: match full hunk headers
  userdiff tests: change setup loop to individual test setup
  userdiff tests: factor out test_diff_funcname() logic
  userdiff tests: add alternative hunk header test infrastructure
  userdiff tests: add a test with multiple tests in a LANG file
  userdiff tests: do config teardown in test_diff_funcname()
  userdiff tests: move custom patterns into one test file
  userdiff tests: remove hack for "RIGHT" token
  userdiff tests: do not do compile tests on "custom" pattern
  userdiff tests: assert that new built-in drivers have tests
  userdiff tests + docs: document & test "diff.<driver>.x?funcname"
  gitattributes doc: reword discussion of built-in userdiff patterns
  userdiff tests: move perl tests to perl.sh
  userdiff tests: move away from "RIGHT" in perl.sh
  gitattributes doc: document multi-line userdiff patterns
  userdiff tests: switch to -U0 by default
  userdiff tests: remove "funcname" from custom3 test
  userdiff tests: assert empty hunk header context on -U<large>
  userdiff tests: test for a bug in 1dbf0c0ad6c
  userdiff golang: simplify and correct matching regex
  userdiff golang: don't over-match intented constructs
  userdiff golang: add a rule to match "package"
  userdiff golang: match multi-line "const" and "import"
  userdiff tests: add basic test for ada
  userdiff tests: add basic test for ruby

 Documentation/config/diff.txt              |  11 ++
 Documentation/gitattributes.txt            |  46 ++++-
 Makefile                                   |   1 +
 t/annotate-tests.sh                        |  34 ++--
 t/helper/test-tool.c                       |   1 +
 t/helper/test-tool.h                       |   1 +
 t/helper/test-userdiff.c                   |  31 ++++
 t/t4018-diff-funcname.sh                   | 180 +++++++++++++-------
 t/t4018/README                             |  69 ++++++--
 t/t4018/ada.sh                             |  37 ++++
 t/t4018/bash-arithmetic-function           |   1 +
 t/t4018/bash-bashism-style-compact         |   1 +
 t/t4018/bash-bashism-style-function        |   1 +
 t/t4018/bash-bashism-style-whitespace      |   1 +
 t/t4018/bash-conditional-function          |   1 +
 t/t4018/bash-missing-parentheses           |   1 +
 t/t4018/bash-mixed-style-compact           |   1 +
 t/t4018/bash-mixed-style-function          |   1 +
 t/t4018/bash-nested-functions              |   1 +
 t/t4018/bash-other-characters              |   1 +
 t/t4018/bash-posix-style-compact           |   1 +
 t/t4018/bash-posix-style-function          |   1 +
 t/t4018/bash-posix-style-whitespace        |   1 +
 t/t4018/bash-subshell-function             |   1 +
 t/t4018/bash-trailing-comment              |   1 +
 t/t4018/cpp-c++-function                   |   1 +
 t/t4018/cpp-class-constructor              |   1 +
 t/t4018/cpp-class-constructor-mem-init     |   1 +
 t/t4018/cpp-class-definition               |   1 +
 t/t4018/cpp-class-definition-derived       |   1 +
 t/t4018/cpp-class-destructor               |   1 +
 t/t4018/cpp-function-returning-global-type |   1 +
 t/t4018/cpp-function-returning-nested      |   1 +
 t/t4018/cpp-function-returning-pointer     |   1 +
 t/t4018/cpp-function-returning-reference   |   1 +
 t/t4018/cpp-gnu-style-function             |   1 +
 t/t4018/cpp-namespace-definition           |   1 +
 t/t4018/cpp-operator-definition            |   1 +
 t/t4018/cpp-skip-access-specifiers         |   1 +
 t/t4018/cpp-skip-comment-block             |   1 +
 t/t4018/cpp-skip-labels                    |   1 +
 t/t4018/cpp-struct-definition              |   1 +
 t/t4018/cpp-struct-single-line             |   1 +
 t/t4018/cpp-template-function-definition   |   1 +
 t/t4018/cpp-union-definition               |   1 +
 t/t4018/cpp-void-c-function                |   1 +
 t/t4018/css-attribute-value-selector       |   1 +
 t/t4018/css-block-level-@-statements       |   1 +
 t/t4018/css-brace-in-col-1                 |   1 +
 t/t4018/css-class-selector                 |   1 +
 t/t4018/css-colon-eol                      |   1 +
 t/t4018/css-colon-selector                 |   1 +
 t/t4018/css-common                         |   1 +
 t/t4018/css-id-selector                    |   1 +
 t/t4018/css-long-selector-list             |   1 +
 t/t4018/css-prop-sans-indent               |   1 +
 t/t4018/css-root-selector                  |   1 +
 t/t4018/css-short-selector-list            |   1 +
 t/t4018/css-trailing-space                 |   1 +
 t/t4018/custom.sh                          | 183 ++++++++++++++++++++
 t/t4018/custom1-pattern                    |  17 --
 t/t4018/custom2-match-to-end-of-line       |   8 -
 t/t4018/custom3-alternation-in-pattern     |  17 --
 t/t4018/dts-labels                         |   1 +
 t/t4018/dts-node-unitless                  |   1 +
 t/t4018/dts-nodes                          |   1 +
 t/t4018/dts-nodes-boolean-prop             |   1 +
 t/t4018/dts-nodes-comment1                 |   1 +
 t/t4018/dts-nodes-comment2                 |   1 +
 t/t4018/dts-nodes-multiline-prop           |   1 +
 t/t4018/dts-reference                      |   1 +
 t/t4018/dts-root                           |   1 +
 t/t4018/dts-root-comment                   |   1 +
 t/t4018/elixir-do-not-pick-end             |   1 +
 t/t4018/elixir-ex-unit-test                |   1 +
 t/t4018/elixir-function                    |   1 +
 t/t4018/elixir-macro                       |   1 +
 t/t4018/elixir-module                      |   1 +
 t/t4018/elixir-module-func                 |   1 +
 t/t4018/elixir-nested-module               |   1 +
 t/t4018/elixir-private-function            |   1 +
 t/t4018/elixir-protocol                    |   1 +
 t/t4018/elixir-protocol-implementation     |   1 +
 t/t4018/fortran-block-data                 |   1 +
 t/t4018/fortran-comment                    |   1 +
 t/t4018/fortran-comment-keyword            |   1 +
 t/t4018/fortran-comment-legacy             |   1 +
 t/t4018/fortran-comment-legacy-star        |   1 +
 t/t4018/fortran-external-function          |   1 +
 t/t4018/fortran-external-subroutine        |   1 +
 t/t4018/fortran-module                     |   1 +
 t/t4018/fortran-module-procedure           |   1 +
 t/t4018/fortran-program                    |   1 +
 t/t4018/fountain-scene                     |   1 +
 t/t4018/golang                             | 148 ++++++++++++++++
 t/t4018/golang-complex-function            |   8 -
 t/t4018/golang-func                        |   4 -
 t/t4018/golang-interface                   |   4 -
 t/t4018/golang-long-func                   |   5 -
 t/t4018/golang-struct                      |   4 -
 t/t4018/java-class-member-function         |   1 +
 t/t4018/markdown-heading-indented          |   1 +
 t/t4018/markdown-heading-non-headings      |   1 +
 t/t4018/matlab-class-definition            |   1 +
 t/t4018/matlab-function                    |   1 +
 t/t4018/matlab-octave-section-1            |   1 +
 t/t4018/matlab-octave-section-2            |   1 +
 t/t4018/matlab-section                     |   1 +
 t/t4018/perl-skip-end-of-heredoc           |   8 -
 t/t4018/perl-skip-forward-decl             |  10 --
 t/t4018/perl-skip-sub-in-pod               |  18 --
 t/t4018/perl-sub-definition                |   4 -
 t/t4018/perl-sub-definition-kr-brace       |   4 -
 t/t4018/perl.sh                            |  93 +++++++++++
 t/t4018/php-abstract-class                 |   1 +
 t/t4018/php-abstract-method                |   1 +
 t/t4018/php-class                          |   1 +
 t/t4018/php-final-class                    |   1 +
 t/t4018/php-final-method                   |   1 +
 t/t4018/php-function                       |   1 +
 t/t4018/php-interface                      |   1 +
 t/t4018/php-method                         |   1 +
 t/t4018/php-trait                          |   1 +
 t/t4018/python-async-def                   |   1 +
 t/t4018/python-class                       |   1 +
 t/t4018/python-def                         |   1 +
 t/t4018/python-indented-async-def          |   1 +
 t/t4018/python-indented-class              |   1 +
 t/t4018/python-indented-def                |   1 +
 t/t4018/ruby.sh                            |  58 +++++++
 t/t4018/rust-fn                            |   1 +
 t/t4018/rust-impl                          |   1 +
 t/t4018/rust-macro-rules                   |   1 +
 t/t4018/rust-struct                        |   1 +
 t/t4018/rust-trait                         |   1 +
 userdiff.c                                 | 186 +++++++++++++--------
 userdiff.h                                 |  15 ++
 137 files changed, 1036 insertions(+), 277 deletions(-)
 create mode 100644 t/helper/test-userdiff.c
 create mode 100755 t/t4018/ada.sh
 create mode 100755 t/t4018/custom.sh
 delete mode 100644 t/t4018/custom1-pattern
 delete mode 100644 t/t4018/custom2-match-to-end-of-line
 delete mode 100644 t/t4018/custom3-alternation-in-pattern
 create mode 100644 t/t4018/golang
 delete mode 100644 t/t4018/golang-complex-function
 delete mode 100644 t/t4018/golang-func
 delete mode 100644 t/t4018/golang-interface
 delete mode 100644 t/t4018/golang-long-func
 delete mode 100644 t/t4018/golang-struct
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc
 delete mode 100644 t/t4018/perl-skip-forward-decl
 delete mode 100644 t/t4018/perl-skip-sub-in-pod
 delete mode 100644 t/t4018/perl-sub-definition
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace
 create mode 100755 t/t4018/perl.sh
 create mode 100755 t/t4018/ruby.sh

Range-diff:
 1:  305fc646d0d =  1:  0be132b05e2 userdiff: refactor away the parse_bool() function
 2:  989438c46ae =  2:  d1e00a739ac userdiff style: re-order drivers in alphabetical order
 3:  4c48e5532ce =  3:  b99bd158d45 userdiff style: declare patterns with consistent style
 4:  f41fa5b316f =  4:  9ce6d47021c userdiff style: normalize pascal regex declaration
 5:  0875d5205c1 =  5:  369fbdcee83 userdiff: add and use for_each_userdiff_driver()
 6:  638247d04d5 =  6:  70d62a97211 userdiff tests: explicitly test "default" pattern
 7:  219043a4881 !  7:  792421a2f8b userdiff tests: list builtin drivers via test-tool
    @@ t/helper/test-userdiff.c (new)
     +static int driver_cb(struct userdiff_driver *driver,
     +		     enum userdiff_driver_type type, void *priv)
     +{
    -+	puts(driver->name);
    ++	if (driver->funcname.pattern)
    ++		puts(driver->name);
     +	return 0;
     +}
     +
    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte
      . ./test-lib.sh
      
      test_expect_success 'setup' '
    -+	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
    -+	test -n "$builtin_drivers" &&
    ++	# Make sure additions to builtin_drivers are sorted
    ++	test_when_finished "rm builtin-drivers.sorted" &&
    ++	test-tool userdiff list-builtin-drivers >builtin-drivers &&
    ++	test_file_not_empty builtin-drivers &&
    ++	sort <builtin-drivers >builtin-drivers.sorted &&
    ++	test_cmp builtin-drivers.sorted builtin-drivers &&
     +
      	# a non-trivial custom pattern
      	git config diff.custom1.funcname "!static
    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
     -	rust
     -	tex
     -	default
    -+	$builtin_drivers
    ++	$(cat builtin-drivers)
      	custom1
      	custom2
      	custom3
 8:  eb66160aac7 =  8:  9081e2a152e userdiff: remove support for "broken" tests
 9:  c6c54039e27 <  -:  ----------- userdiff tests: match full hunk headers
10:  1c6ddf96f61 !  9:  d3652f95d5e blame tests: don't rely on t/t4018/ directory
    @@ Commit message
         with userdiff driver, 2020-11-01) so that the blame tests don't rely
         on stealing the contents of "t/t4018/fortran-external-function".
     
    -    I'm about to refactor that directory to delete that file, just moving
    -    the relevant test file here inline is the easiest solution, and I
    -    think also the most readable.
    +    I'm about to change that file in a subsequent commit. Just moving the
    +    relevant test file here inline is the easiest solution, and I think
    +    also the most readable.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
11:  8a883d87990 ! 10:  35d12779ea1 blame tests: simplify userdiff driver test
    @@ Commit message
         added in 999cfc4f45 (test-lib functions: add --author support to
         test_commit, 2021-01-12).
     
    -    We also did not need the full fortran-external-function content, let's
    +    We also did not need the full fortran-external-function content. Let's
         cut it down to just the important parts, and further modify it to
         demonstrate that the fortran-specific userdiff function is in effect
    -    by adding "WRONG" lines surrounding the "RIGHT" one.
    +    by adding "DO NOT MATCH ..." and "AS THE ..." lines surrounding the
    +    "RIGHT" one. This is to check that we're using the userdiff "fortran"
    +    driver, as opposed to the default driver.
     
         The test also left behind a .gitattributes files, let's clean it up
         with "test_when_finished".
    @@ t/annotate-tests.sh: test_expect_success 'blame -L ^:RE (absolute: end-of-file)'
     -	cat >$fortran_file <<-\EOF &&
     +test_expect_success 'blame -L :funcname with userdiff driver' '
     +	cat >file.template <<-\EOF &&
    -+	def WRONG begin end
    ++	DO NOT MATCH THIS LINE
      	function RIGHT(a, b) result(c)
    -+	int WRONG(void) {}
    ++	AS THE DEFAULT DRIVER WOULD
      
      	integer, intent(in) :: ChangeMe
     -	integer, intent(in) :: b
    @@ t/annotate-tests.sh: test_expect_success 'blame -L ^:RE (absolute: end-of-file)'
     +	echo "$fortran_file diff=fortran" >.gitattributes &&
     +
     +	test_commit --author "A [off-list ref]" \
    -+		"add" $fortran_file \
    ++		"add" "$fortran_file" \
     +		"$(cat file.template)" &&
     +	test_commit --author "B [off-list ref]" \
    -+		"change" $fortran_file \
    ++		"change" "$fortran_file" \
     +		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
    -+	check_count -f $fortran_file -L:RIGHT A 3 B 1
    ++	check_count -f "$fortran_file" -L:RIGHT A 3 B 1
      '
      
      test_expect_success 'setup incremental' '
12:  e56a7a6b5f4 <  -:  ----------- userdiff tests: rewrite hunk header test infrastructure
 -:  ----------- > 11:  4bd8a0daa25 userdiff tests: match full hunk headers
 -:  ----------- > 12:  d2d74476f2a userdiff tests: change setup loop to individual test setup
21:  9a18506aff8 ! 13:  8db95a69924 userdiff tests: factor out test_diff_funcname() logic
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
      ## t/t4018-diff-funcname.sh ##
    -@@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated' '
    - 	test_i18ngrep ": Last expression must not be negated:" msg
    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup hunk header tests' '
    + 	git -C t4018 add .
      '
      
     +do_change_me () {
    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'
     +	sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <$file
     +}
     +
    - test_diff_funcname () {
    - 	desc=$1
    - 	cat <&8 >arg.header &&
    -@@ t/t4018-diff-funcname.sh: test_diff_funcname () {
    - 		cp arg.test "$what" &&
    - 		cp arg.header expected &&
    - 		git add "$what" &&
    --		sed -e "s/ChangeMe/IWasChanged/" <"$what" >tmp &&
    --		mv tmp "$what"
    -+		do_change_me "$what"
    - 	' &&
    + # check each individual file
    + for i in $(git -C t4018 ls-files)
    + do
    +@@ t/t4018-diff-funcname.sh: do
      
    - 	test_expect_success "$desc" '
    - 		git diff -U1 "$what" >diff &&
    --		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <diff >actual &&
    -+		last_diff_context_line diff >actual &&
    - 		test_cmp expected actual
    - 	' &&
    + 		# add test file to the index
    + 		git add \"$i\" &&
    +-		# place modified file in the worktree
    +-		sed -e 's/ChangeMe/IWasChanged/' <\"t4018/$i.content\" >\"$i\"
    ++		do_change_me \"$i\"
    + 	"
      
    + 	test_expect_success "hunk header: $i" "
    + 		git diff -U1 $i >diff &&
    +-		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
    ++		last_diff_context_line diff >ctx &&
    + 		test_cmp t4018/$i.header ctx
    + 	"
    + done
 -:  ----------- > 14:  e64a00d020e userdiff tests: add alternative hunk header test infrastructure
 -:  ----------- > 15:  3dab65bf394 userdiff tests: add a test with multiple tests in a LANG file
13:  84d20a7cd0c ! 16:  6eff13d01d3 userdiff tests: do config teardown in test_diff_funcname()
    @@ Commit message
      ## t/t4018-diff-funcname.sh ##
     @@ t/t4018-diff-funcname.sh: test_diff_funcname () {
      		git diff -U1 "$what" >diff &&
    - 		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <diff >actual &&
    + 		last_diff_context_line diff >actual &&
      		test_cmp expected actual
     +	' &&
     +
14:  70fc9fa565b ! 17:  48f15aed4e4 userdiff tests: move custom patterns into one test file
    @@ Commit message
     
      ## t/t4018-diff-funcname.sh ##
     @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
    - 	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
    - 	test -n "$builtin_drivers" &&
    + 	sort <builtin-drivers >builtin-drivers.sorted &&
    + 	test_cmp builtin-drivers.sorted builtin-drivers &&
      
     -	# a non-trivial custom pattern
     -	git config diff.custom1.funcname "!static
    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
     @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
      
      diffpatterns="
    - 	$builtin_drivers
    + 	$(cat builtin-drivers)
     -	custom1
     -	custom2
     -	custom3
15:  8539d6d464e = 18:  11556fe0967 userdiff tests: remove hack for "RIGHT" token
16:  121e5d6dfaf ! 19:  1b96e89843c userdiff tests: do not do compile tests on "custom" pattern
    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
      '
      
     -diffpatterns="
    --	$builtin_drivers
    +-	$(cat builtin-drivers)
     -	custom
     -"
     -
     -for p in $diffpatterns
    -+for p in $builtin_drivers
    ++for p in $(cat builtin-drivers)
      do
      	test_expect_success "builtin $p pattern compiles" '
      		echo "*.java diff=$p" >.gitattributes &&
    -@@ t/t4018-diff-funcname.sh: test_diff_funcname () {
    - 	'
    - }
    +@@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated' '
    + 	test_i18ngrep ": Last expression must not be negated:" msg
    + '
      
    --for what in $diffpatterns
    -+for what in $builtin_drivers custom
    - do
    - 	test="$TEST_DIRECTORY/t4018/$what.sh"
    - 	if ! test -e "$test"
    ++diffpatterns="
    ++	$(cat builtin-drivers)
    ++	custom
    ++"
    ++
    + test_expect_success 'setup hunk header tests' '
    + 	for i in $diffpatterns
    + 	do
 -:  ----------- > 20:  d3cbfc4354d userdiff tests: assert that new built-in drivers have tests
17:  451b7ae453d = 21:  fd6c51ac6db userdiff tests + docs: document & test "diff.<driver>.x?funcname"
18:  5a402bb9bf1 = 22:  43d818be785 gitattributes doc: reword discussion of built-in userdiff patterns
 -:  ----------- > 23:  37d54d77755 userdiff tests: move perl tests to perl.sh
 -:  ----------- > 24:  b6f4f613857 userdiff tests: move away from "RIGHT" in perl.sh
19:  a3badb1a3ee ! 25:  a39ba8e2545 gitattributes doc: document multi-line userdiff patterns
    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha
     -There are built-in patterns shipped as part of git itself. A more
     -advanced version of the `tex` pattern discussed above is one of them.
     +Multiple patterns can be supplied by listing them one per line
    -+separated by `\n`. They will be matched one at a time from left to
    -+right. Do not supply a trailing "\n" for the last pattern. E.g.:
    ++separated by `\n`. They will be matched one line at a time, e.g.:
     +
     +------------------------
     +[diff "perl"]
     +	xfuncname = "!^=head\n^[^ ]+.*"
     +------------------------
     +
    -+Patterns in in a list of multiple that begin with "!" are negated. A
    -+matching negated pattern will cause the matched line to be
    ++Patterns in a list of multiple patterns that begin with "!" are
    ++negated. A matching negated pattern will cause the matched line to be
     +skipped. Use it to skip a later pattern that would otherwise match. It
     +is an error if one or more negated patterns aren't followed by a
     +non-negated pattern.
    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha
     +construct that will match a literal "!" without "!" being the first
     +character on that line, such as "[!]".
     +
    ++If the last pattern in a list of multiple patterns ends with "\n" it
    ++will be interpreted as an empty pattern, and will match the first
    ++empty line. It's almost always a logic error to provide a list of
    ++multiple patterns ending with "\n", but it's permitted in case you
    ++genuinely want to match an empty line.
    ++
     +If the pattern contains a `$1` capture it will be used instead of the
     +entire matching line (`$0`) to display the hunk header. This can be
     +used e.g. to strip whitespace from the beginning of the line, or to
    @@ t/t4018/custom.sh: ChangeMe
     +foo
     +EOF_HUNK
     +sub foo;
    ++
     +=head1
    ++
    ++ChangeMe
    ++
    ++EOF_TEST
    ++
    ++test_expect_success 'custom: multiple patterns ending with \n' '
    ++	git config diff.custom.xfuncname "!^=head
    ++^sub ([^;]+)
    ++"
    ++'
    ++
    ++test_diff_funcname 'custom: multiple patterns ending with \n' \
    ++	8<<\EOF_HUNK 9<<\EOF_TEST
    ++
    ++EOF_HUNK
    ++sub foo;
    ++
    ++=head1
    ++
     +ChangeMe
     +
     +EOF_TEST
     
      ## t/t4018/perl.sh ##
    -@@ t/t4018/perl.sh: sub RIGHT
    +@@ t/t4018/perl.sh: sub asub
      	print "ChangeMe\n";
      }
      EOF_TEST
    @@ t/t4018/perl.sh: sub RIGHT
     +EOF_HUNK
     +sub foo;
     +=head1
    -+ChangeMe
     +
    ++ChangeMe
     +EOF_TEST
 -:  ----------- > 26:  27394c6c2a4 userdiff tests: switch to -U0 by default
20:  1b46726e85f = 27:  91ab863a298 userdiff tests: remove "funcname" from custom3 test
22:  24548fb680e <  -:  ----------- userdiff tests: test hunk headers on accumulated files
23:  48f00a59d5e <  -:  ----------- userdiff tests: test hunk header selection with -U0
24:  05a01990c9c ! 28:  b68133ce5f7 userdiff tests: assert empty hunk header context on -U<large>
    @@ Commit message
     
         Assert the existing behavior that under -U<large> we'll show no hunk
         header context, where <large> takes us past the potential hunk header
    -    we'd have extracted. I'm just picking a number over nine thousand as a
    -    really large number we're unlikely to exceed in these tests.
    +    we'd have extracted.
    +
    +    I'm just picking a number over nine thousand as a really large number
    +    we're unlikely to exceed in these tests.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {
      		test_cmp expected actual
      	' &&
      
    -+	test_expect_success "$desc -U9001 (accumulated)" '
    -+		git diff -U9001 "$what".acc >diff &&
    -+		last_diff_context_line diff >actual.lines &&
    -+		tail -n 1 actual.lines >actual &&
    ++	test_expect_success "$desc -U9001" '
    ++		git diff -U9001 "$what" >diff &&
    ++		last_diff_context_line diff >actual &&
     +		echo >blank &&
     +		test_cmp blank actual
     +	' &&
25:  3d2f42d7041 <  -:  ----------- userdiff: match "package" in diff=golang
 -:  ----------- > 29:  9f3a7ca788b userdiff tests: test for a bug in 1dbf0c0ad6c
 -:  ----------- > 30:  43ee24e554b userdiff golang: simplify and correct matching regex
 -:  ----------- > 31:  70a2e7ca70b userdiff golang: don't over-match intented constructs
 -:  ----------- > 32:  6b942cd651b userdiff golang: add a rule to match "package"
 -:  ----------- > 33:  f45d35387d9 userdiff golang: match multi-line "const" and "import"
26:  b2e16ade06c ! 34:  c67c3e160f3 userdiff tests: add basic test for ada
    @@ Commit message
         1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada
         2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking
     
    + ## t/t4018-diff-funcname.sh ##
    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
    + 	# Do not add anything to this list. New built-in drivers should have
    + 	# tests
    + 	cat >drivers-no-tests <<-\EOF &&
    +-	ada
    + 	bibtex
    + 	csharp
    + 	html
    +
      ## t/t4018/ada.sh (new) ##
     @@
     +#!/bin/sh
27:  826b6f4d6ae ! 35:  e2aedd738ef userdiff tests: add basic test for ruby
    @@ Commit message
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
    + ## t/t4018-diff-funcname.sh ##
    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
    + 	html
    + 	objc
    + 	pascal
    +-	ruby
    + 	tex
    + 	EOF
    + 
    +
      ## t/t4018/ruby.sh (new) ##
     @@
     +#!/bin/sh
    @@ t/t4018/ruby.sh (new)
     +EOF_TEST
     +
     +test_diff_funcname 'ruby: picks first "class/module/def" before changed context' \
    -+	"class Two" \
    ++	'-U1' \
     +	8<<\EOF_HUNK 9<<\EOF_TEST
     +class One
     +EOF_HUNK
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 01/35] userdiff: refactor away the parse_bool() function

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:52:58

Since 6680a0874f (drop odd return value semantics from
userdiff_config, 2012-02-07) we have not cared about the return values
of parse_tristate() or git_config_bool() v.s. falling through in
userdiff_config(), so let's do so in those cases to make the code
easier to read.

Having a wrapper function for git_config_bool() dates back to
d9bae1a178 (diff: cache textconv output, 2010-04-01) and
122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of
which predated the change in 6680a0874f which made their return values
redundant.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c5..c147bcbb173 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,
 	return 0;
 }
 
-static int parse_tristate(int *b, const char *k, const char *v)
+static void parse_tristate(int *b, const char *k, const char *v)
 {
 	if (v && !strcasecmp(v, "auto"))
 		*b = -1;
 	else
 		*b = git_config_bool(k, v);
-	return 0;
-}
-
-static int parse_bool(int *b, const char *k, const char *v)
-{
-	*b = git_config_bool(k, v);
-	return 0;
 }
 
 int userdiff_config(const char *k, const char *v)
@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)
 		return parse_funcname(&drv->funcname, k, v, 0);
 	if (!strcmp(type, "xfuncname"))
 		return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
-	if (!strcmp(type, "binary"))
-		return parse_tristate(&drv->binary, k, v);
 	if (!strcmp(type, "command"))
 		return git_config_string(&drv->external, k, v);
 	if (!strcmp(type, "textconv"))
 		return git_config_string(&drv->textconv, k, v);
-	if (!strcmp(type, "cachetextconv"))
-		return parse_bool(&drv->textconv_want_cache, k, v);
 	if (!strcmp(type, "wordregex"))
 		return git_config_string(&drv->word_regex, k, v);
+	/* Don't care about the parse errors for these, fallthrough */
+	if (!strcmp(type, "cachetextconv"))
+		drv->textconv_want_cache = git_config_bool(k, v);
+	if (!strcmp(type, "binary"))
+		parse_tristate(&drv->binary, k, v);
 
 	return 0;
 }
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 02/35] userdiff style: re-order drivers in alphabetical order

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:52:58

Address some old code smell and move around the built-in userdiff
drivers so they're both in alphabetical order, and now in the same
order they appear in the gitattributes(5) documentation.

The two started drifting in be58e70dba (diff: unify external diff and
funcname parsing code, 2008-10-05), and then even further in
80c49c3de2 (color-words: make regex configurable via attributes,
2009-01-17) when the "cpp" pattern was added.

There are no functional changes here, and as --color-moved will show
only moved existing lines.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 76 +++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c147bcbb173..c92cbcc0540 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,6 +44,44 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 "[={}\"]|[^={}\" \t]+"),
+PATTERNS("cpp",
+	 /* Jump targets or access declarations */
+	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
+	 /* functions/methods, variables, and compounds at top level */
+	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+PATTERNS("csharp",
+	 /* Keywords */
+	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+	 /* Methods and constructors */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+	 /* Properties */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /* Type definitions */
+	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+	 /* Namespace */
+	 "^[ \t]*(namespace[ \t]+.*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+IPATTERN("css",
+	 "![:;][[:space:]]*$\n"
+	 "^[:[@.#]?[_a-z0-9].*$",
+	 /* -- */
+	 /*
+	  * This regex comes from W3C CSS specs. Should theoretically also
+	  * allow ISO 10646 characters U+00A0 and higher,
+	  * but they are not handled in this regex.
+	  */
+	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
+	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
+),
 PATTERNS("dts",
 	 "!;\n"
 	 "!=\n"
@@ -191,46 +229,8 @@ PATTERNS("rust",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
 	 "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-PATTERNS("cpp",
-	 /* Jump targets or access declarations */
-	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
-	 /* functions/methods, variables, and compounds at top level */
-	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
-PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
-	 /* Type definitions */
-	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
-	 /* Namespace */
-	 "^[ \t]*(namespace[ \t]+.*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-IPATTERN("css",
-	 "![:;][[:space:]]*$\n"
-	 "^[:[@.#]?[_a-z0-9].*$",
-	 /* -- */
-	 /*
-	  * This regex comes from W3C CSS specs. Should theoretically also
-	  * allow ISO 10646 characters U+00A0 and higher,
-	  * but they are not handled in this regex.
-	  */
-	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
-	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
-),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 03/35] userdiff style: declare patterns with consistent style

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:52:59

Change those patterns which were declared with a regex on the same
line as the "PATTERNS()" line to put that regex on the next line, and
add missing "/* -- */" separator comments between the pattern and
word_regex.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c92cbcc0540..c7aaf7094f8 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,7 +44,9 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+PATTERNS("bibtex",
+	 "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 /* -- */
 	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("cpp",
 	 /* Jump targets or access declarations */
@@ -121,7 +123,9 @@ IPATTERN("fortran",
 	  * they would have been matched above as a variable anyway. */
 	 "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
 	 "|//|\\*\\*|::|[/<>=]="),
-IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+IPATTERN("fountain",
+	 "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
@@ -132,7 +136,9 @@ PATTERNS("golang",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"
 	 "|[-+*/<>%&^|=!:]=|--|\\+\\+|<<=?|>>=?|&\\^=?|&&|\\|\\||<-|\\.{3}"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+PATTERNS("html",
+	 "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("java",
 	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
@@ -144,6 +150,7 @@ PATTERNS("java",
 	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
 PATTERNS("markdown",
 	 "^ {0,3}#{1,6}[ \t].*",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("matlab",
 	 /*
@@ -152,6 +159,7 @@ PATTERNS("matlab",
 	  * that is understood by both.
 	  */
 	 "^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$",
+	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
 PATTERNS("objc",
 	 /* Negate C statements that can look like functions */
@@ -212,13 +220,15 @@ PATTERNS("php",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
 	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"),
-PATTERNS("python", "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
+PATTERNS("python",
+	 "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
 	 /* -- */
-PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+PATTERNS("ruby",
+	 "^[ \t]*((class|module|def)[ \t].*)$",
 	 /* -- */
 	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 04/35] userdiff style: normalize pascal regex declaration

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:53:22

Declare the pascal pattern consistently with how we declare the
others, not having "\n" on one line by itself, but as part of the
pattern, and when there are alterations have the "|" at the start, not
end of the line.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c7aaf7094f8..10a02d36209 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -175,9 +175,8 @@ PATTERNS("objc",
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("pascal",
-	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
-		"implementation|initialization|finalization)[ \t]*.*)$"
-	 "\n"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface"
+	 "|implementation|initialization|finalization)[ \t]*.*)$\n"
 	 "^(.*=[ \t]*(class|record).*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 05/35] userdiff: add and use for_each_userdiff_driver()

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:53:24

Refactor the userdiff_find_by_namelen() function so that a new
for_each_userdiff_driver() API function does most of the work.

This will be useful for the same reason we've got other for_each_*()
API functions as part of various APIs, and will be used in a follow-up
commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------
 userdiff.h | 15 ++++++++++++++
 2 files changed, 64 insertions(+), 12 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 10a02d36209..55f4f769bd3 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -259,20 +259,32 @@ static struct userdiff_driver driver_false = {
 	{ NULL, 0 }
 };
 
-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+struct for_each_userdiff_driver_cb {
+	const char *k;
+	size_t len;
+	struct userdiff_driver *driver;
+};
+
+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
+				       enum userdiff_driver_type type, void *priv)
 {
-	int i;
-	for (i = 0; i < ndrivers; i++) {
-		struct userdiff_driver *drv = drivers + i;
-		if (!strncmp(drv->name, k, len) && !drv->name[len])
-			return drv;
-	}
-	for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
-		struct userdiff_driver *drv = builtin_drivers + i;
-		if (!strncmp(drv->name, k, len) && !drv->name[len])
-			return drv;
+	struct for_each_userdiff_driver_cb *cb_data = priv;
+
+	if (!strncmp(driver->name, cb_data->k, cb_data->len) &&
+	    !driver->name[cb_data->len]) {
+		cb_data->driver = driver;
+		return -1; /* found it! */
 	}
-	return NULL;
+	return 0;
+}
+
+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+{
+	struct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };
+
+	for_each_userdiff_driver(userdiff_find_by_namelen_cb,
+				 USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);
+	return udcbdata.driver;
 }
 
 static int parse_funcname(struct userdiff_funcname *f, const char *k,
@@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,
 
 	return driver;
 }
+
+int for_each_userdiff_driver(each_userdiff_driver_fn fn,
+			     enum userdiff_driver_type type, void *cb_data)
+{
+	int i, ret;
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {
+
+		for (i = 0; i < ndrivers; i++) {
+			struct userdiff_driver *drv = drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {
+
+		for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+			struct userdiff_driver *drv = builtin_drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+}
diff --git a/userdiff.h b/userdiff.h
index 203057e13e5..fe14014a775 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -21,6 +21,13 @@ struct userdiff_driver {
 	struct notes_cache *textconv_cache;
 	int textconv_want_cache;
 };
+enum userdiff_driver_type {
+	USERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,
+	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,
+	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,
+};
+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,
+				       enum userdiff_driver_type, void *);
 
 int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 struct userdiff_driver *userdiff_get_textconv(struct repository *r,
 					      struct userdiff_driver *driver);
 
+/*
+ * Iterate over each driver of type userdiff_driver_type, or
+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero
+ * to exit from the loop.
+ */
+int for_each_userdiff_driver(each_userdiff_driver_fn,
+			     enum userdiff_driver_type, void *);
+
 #endif /* USERDIFF */
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 06/35] userdiff tests: explicitly test "default" pattern

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:53:43

Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)
the internals of the userdiff.c code have understood a "default" name,
which is invoked as userdiff_find_by_name("default") and present in
the "builtin_drivers" struct. Let's test for this special case.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 1 +
 1 file changed, 1 insertion(+)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 9675bc17db2..cefe329aea7 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -49,6 +49,7 @@ diffpatterns="
 	ruby
 	rust
 	tex
+	default
 	custom1
 	custom2
 	custom3
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 07/35] userdiff tests: list builtin drivers via test-tool

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:53:46

Change the userdiff test to list the builtin drivers via the
test-tool, using the new for_each_userdiff_driver() API function.

This gets rid of the need to modify this part of the test every time a
new pattern is added, see 2ff6c34612 (userdiff: support Bash,
2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)
for two recent examples.

I only need the "list-builtin-drivers "argument here, but let's add
"list-custom-drivers" and "list-drivers" too, just because it's easy.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 Makefile                 |  1 +
 t/helper/test-tool.c     |  1 +
 t/helper/test-tool.h     |  1 +
 t/helper/test-userdiff.c | 31 +++++++++++++++++++++++++++++++
 t/t4018-diff-funcname.sh | 32 ++++++++------------------------
 5 files changed, 42 insertions(+), 24 deletions(-)
 create mode 100644 t/helper/test-userdiff.c
diff --git a/Makefile b/Makefile
index 5a239cac20e..710a0deaed0 100644
--- a/Makefile
+++ b/Makefile
@@ -741,6 +741,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
+TEST_BUILTINS_OBJS += test-userdiff.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index f97cd9f48a6..dcb05ca6e5e 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {
 	{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
 	{ "subprocess", cmd__subprocess },
 	{ "trace2", cmd__trace2 },
+	{ "userdiff", cmd__userdiff },
 	{ "urlmatch-normalization", cmd__urlmatch_normalization },
 	{ "xml-encode", cmd__xml_encode },
 	{ "wildmatch", cmd__wildmatch },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 28072c0ad5a..589f2e8ac67 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);
 int cmd__submodule_nested_repo_config(int argc, const char **argv);
 int cmd__subprocess(int argc, const char **argv);
 int cmd__trace2(int argc, const char **argv);
+int cmd__userdiff(int argc, const char **argv);
 int cmd__urlmatch_normalization(int argc, const char **argv);
 int cmd__xml_encode(int argc, const char **argv);
 int cmd__wildmatch(int argc, const char **argv);
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
new file mode 100644
index 00000000000..f173a4f18af
--- /dev/null
+++ b/t/helper/test-userdiff.c
@@ -0,0 +1,31 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "userdiff.h"
+
+static int driver_cb(struct userdiff_driver *driver,
+		     enum userdiff_driver_type type, void *priv)
+{
+	if (driver->funcname.pattern)
+		puts(driver->name);
+	return 0;
+}
+
+static int list_what(enum userdiff_driver_type type)
+{
+	return for_each_userdiff_driver(driver_cb, type, NULL);
+}
+
+int cmd__userdiff(int argc, const char **argv)
+{
+	if (argc != 2)
+		return 1;
+
+	if (!strcmp(argv[1], "list-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);
+	else if (!strcmp(argv[1], "list-builtin-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_BUILTIN);
+	else if (!strcmp(argv[1], "list-custom-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_CUSTOM);
+	else
+		return error("unknown argument %s", argv[1]);
+}
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index cefe329aea7..5bd82e09ab3 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -8,6 +8,13 @@ test_description='Test custom diff function name patterns'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	# Make sure additions to builtin_drivers are sorted
+	test_when_finished "rm builtin-drivers.sorted" &&
+	test-tool userdiff list-builtin-drivers >builtin-drivers &&
+	test_file_not_empty builtin-drivers &&
+	sort <builtin-drivers >builtin-drivers.sorted &&
+	test_cmp builtin-drivers.sorted builtin-drivers &&
+
 	# a non-trivial custom pattern
 	git config diff.custom1.funcname "!static
 !String
@@ -26,30 +33,7 @@ test_expect_success 'setup' '
 '
 
 diffpatterns="
-	ada
-	bash
-	bibtex
-	cpp
-	csharp
-	css
-	dts
-	elixir
-	fortran
-	fountain
-	golang
-	html
-	java
-	markdown
-	matlab
-	objc
-	pascal
-	perl
-	php
-	python
-	ruby
-	rust
-	tex
-	default
+	$(cat builtin-drivers)
 	custom1
 	custom2
 	custom3
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 08/35] userdiff: remove support for "broken" tests

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:08

There have been no "broken" tests since 75c3b6b2e8 (userdiff: improve
Fortran xfuncname regex, 2020-08-12). Let's remove the test support
for them, this is in preparation for a more general refactoring of the
tests.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 8 +-------
 t/t4018/README           | 3 ---
 2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5bd82e09ab3..9aec9f8e6de 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -86,13 +86,7 @@ test_expect_success 'setup hunk header tests' '
 # check each individual file
 for i in $(git ls-files)
 do
-	if grep broken "$i" >/dev/null 2>&1
-	then
-		result=failure
-	else
-		result=success
-	fi
-	test_expect_$result "hunk header: $i" "
+	test_expect_success "hunk header: $i" "
 		git diff -U1 $i >actual &&
 		grep '@@ .* @@.*RIGHT' actual
 	"
diff --git a/t/t4018/README b/t/t4018/README
index 283e01cca1a..2d25b2b4fc9 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.
 The text that must appear in the hunk header must contain the word
 "right", but in all upper-case, like in the title above.
 
-To mark a test case that highlights a malfunction, insert the word
-BROKEN in all lower-case somewhere in the file.
-
 This text is a bit twisted and out of order, but it is itself a
 test case for the default hunk header pattern. Know what you are doing
 if you change it.
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 09/35] blame tests: don't rely on t/t4018/ directory

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:08

Refactor a test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) so that the blame tests don't rely
on stealing the contents of "t/t4018/fortran-external-function".

I'm about to change that file in a subsequent commit. Just moving the
relevant test file here inline is the easiest solution, and I think
also the most readable.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/annotate-tests.sh | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 29ce89090d8..04a2c58594c 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
 test_expect_success 'setup -L :funcname with userdiff driver' '
 	echo "fortran-* diff=fortran" >.gitattributes &&
 	fortran_file=fortran-external-function &&
-	orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
-	cp "$orig_file" . &&
+	cat >$fortran_file <<-\EOF &&
+	function RIGHT(a, b) result(c)
+
+	integer, intent(in) :: ChangeMe
+	integer, intent(in) :: b
+	integer, intent(out) :: c
+
+	c = a+b
+
+	end function RIGHT
+	EOF
 	git add "$fortran_file" &&
 	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
 	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >"$fortran_file" &&
+	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
+	mv "$fortran_file".tmp "$fortran_file" &&
 	git add "$fortran_file" &&
 	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
 	git commit -m "change fortran file"
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 10/35] blame tests: simplify userdiff driver test

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:08

Simplify the test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) to use the --author support recently
added in 999cfc4f45 (test-lib functions: add --author support to
test_commit, 2021-01-12).

We also did not need the full fortran-external-function content. Let's
cut it down to just the important parts, and further modify it to
demonstrate that the fortran-specific userdiff function is in effect
by adding "DO NOT MATCH ..." and "AS THE ..." lines surrounding the
"RIGHT" one. This is to check that we're using the userdiff "fortran"
driver, as opposed to the default driver.

The test also left behind a .gitattributes files, let's clean it up
with "test_when_finished".

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/annotate-tests.sh | 36 +++++++++++++++---------------------
 1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 04a2c58594c..d3b299e75cb 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
 	check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
 '
 
-test_expect_success 'setup -L :funcname with userdiff driver' '
-	echo "fortran-* diff=fortran" >.gitattributes &&
-	fortran_file=fortran-external-function &&
-	cat >$fortran_file <<-\EOF &&
+test_expect_success 'blame -L :funcname with userdiff driver' '
+	cat >file.template <<-\EOF &&
+	DO NOT MATCH THIS LINE
 	function RIGHT(a, b) result(c)
+	AS THE DEFAULT DRIVER WOULD
 
 	integer, intent(in) :: ChangeMe
-	integer, intent(in) :: b
-	integer, intent(out) :: c
-
-	c = a+b
-
-	end function RIGHT
 	EOF
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
-	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
-	mv "$fortran_file".tmp "$fortran_file" &&
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
-	git commit -m "change fortran file"
-'
 
-test_expect_success 'blame -L :funcname with userdiff driver' '
-	check_count -f fortran-external-function -L:RIGHT A 7 B 1
+	fortran_file=file.f03 &&
+	test_when_finished "rm .gitattributes" &&
+	echo "$fortran_file diff=fortran" >.gitattributes &&
+
+	test_commit --author "A <A@test.git>" \
+		"add" "$fortran_file" \
+		"$(cat file.template)" &&
+	test_commit --author "B <B@test.git>" \
+		"change" "$fortran_file" \
+		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+	check_count -f "$fortran_file" -L:RIGHT A 3 B 1
 '
 
 test_expect_success 'setup incremental' '
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 12/35] userdiff tests: change setup loop to individual test setup

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:14

Change the recently amended setup loop in "setup hunk header tests" to
instead set up the test data as we test each individual hunk header
test.

This means we can get rid of the "|| return 1" case and the previous
for-loop, and won't spend time on setting up all the data only to
e.g. fail on the 1st test when running under "-i".

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 15dcbe735ca..2365f0e361e 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -72,24 +72,23 @@ test_expect_success 'setup hunk header tests' '
 
 	cp -R "$TEST_DIRECTORY"/t4018 . &&
 	git init t4018 &&
-	git -C t4018 add . &&
+	git -C t4018 add .
+'
 
-	for i in $(git -C t4018 ls-files)
-	do
-		grep -v "^t4018" "t4018/$i" >"t4018/$i.content" &&
-		sed -n -e "s/^t4018 header: //p" <"t4018/$i" >"t4018/$i.header" &&
-		cp "t4018/$i.content" "$i" &&
+# check each individual file
+for i in $(git -C t4018 ls-files)
+do
+	test_expect_success "setup hunk header: $i" "
+		grep -v '^t4018' \"t4018/$i\" >\"t4018/$i.content\" &&
+		sed -n -e 's/^t4018 header: //p' <\"t4018/$i\" >\"t4018/$i.header\" &&
+		cp \"t4018/$i.content\" \"$i\" &&
 
 		# add test file to the index
-		git add "$i" &&
+		git add \"$i\" &&
 		# place modified file in the worktree
-		sed -e "s/ChangeMe/IWasChanged/" <"t4018/$i.content" >"$i" || return 1
-	done
-'
+		sed -e 's/ChangeMe/IWasChanged/' <\"t4018/$i.content\" >\"$i\"
+	"
 
-# check each individual file
-for i in $(git ls-files)
-do
 	test_expect_success "hunk header: $i" "
 		git diff -U1 $i >diff &&
 		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 15/35] userdiff tests: add a test with multiple tests in a LANG file

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:45

Demonstrate that we can now have tests with multiple tests in a given
LANG file. This is useful to show rules that don't match, follow-up
commits will add some tests like that.

Let's move the "golang" test, which I'm going to be modifying soon
over to to this new convention.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh        |  2 +-
 t/t4018/README                  |  8 +++++++
 t/t4018/golang                  | 39 +++++++++++++++++++++++++++++++++
 t/t4018/golang-complex-function |  9 --------
 t/t4018/golang-func             |  5 -----
 t/t4018/golang-interface        |  5 -----
 t/t4018/golang-long-func        |  6 -----
 t/t4018/golang-struct           |  5 -----
 8 files changed, 48 insertions(+), 31 deletions(-)
 create mode 100644 t/t4018/golang
 delete mode 100644 t/t4018/golang-complex-function
 delete mode 100644 t/t4018/golang-func
 delete mode 100644 t/t4018/golang-interface
 delete mode 100644 t/t4018/golang-long-func
 delete mode 100644 t/t4018/golang-struct
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 6fd3dce1364..7fc4291f4be 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -67,7 +67,7 @@ test_expect_success 'last regexp must not be negated' '
 test_expect_success 'setup hunk header tests' '
 	for i in $diffpatterns
 	do
-		echo "$i-* diff=$i"
+		echo "$i* diff=$i"
 	done > .gitattributes &&
 
 	cp -R "$TEST_DIRECTORY"/t4018 . &&
diff --git a/t/t4018/README b/t/t4018/README
index 54ae735d5f8..a3220dd6374 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -41,6 +41,9 @@ below) are:
 Create test cases called "LANG-whatever" in this directory, where
 "whatever" is a brief description of the test.
 
+You can also stick all the tests into one "LANG" file. See "t4018
+description" below.
+
 Any line starting with "t4018" is a control line for the test:
 
  - The "t4018 header:" line above specifies what text must appear in
@@ -48,6 +51,11 @@ Any line starting with "t4018" is a control line for the test:
    the line for ease of not having to hardcode the line numbers and
    offsets.
 
+ - The "t4018 description:" line above the test is a convention to add
+   a human-readable description for the test. Unlike in the case of
+   the LANG.sh test cases these descriptions don't make it to
+   "test_expect_success", and won't be seen in the test output.
+
 In many of the test cases the header line includes the token "RIGHT",
 this used to be part of the test syntax, but isn't anymore. Now we
 care about the "t4018 header:" line, not whatever line contains a
diff --git a/t/t4018/golang b/t/t4018/golang
new file mode 100644
index 00000000000..000e66b1c7b
--- /dev/null
+++ b/t/t4018/golang
@@ -0,0 +1,39 @@
+t4018 description: complex function
+t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
+type Test struct {
+	a Type
+}
+
+func (t *Test) RIGHT(a Type) (Type, error) {
+	t.a = a
+	return ChangeMe, nil
+}
+
+t4018 description: func
+t4018 header: func RIGHT() {
+func RIGHT() {
+	a := 5
+	b := ChangeMe
+}
+
+t4018 description: interface
+t4018 header: type RIGHT interface {
+type RIGHT interface {
+	a() Type
+	b() ChangeMe
+}
+
+t4018 description: long func
+t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+	anotherLongVariableName AnotherLongType) {
+	a := 5
+	b := ChangeMe
+}
+
+t4018 description: struct
+t4018 header: type RIGHT struct {
+type RIGHT struct {
+	a Type
+	b ChangeMe
+}
diff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function
deleted file mode 100644
index 0574ba912e6..00000000000
--- a/t/t4018/golang-complex-function
+++ /dev/null
@@ -1,9 +0,0 @@
-t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
-type Test struct {
-	a Type
-}
-
-func (t *Test) RIGHT(a Type) (Type, error) {
-	t.a = a
-	return ChangeMe, nil
-}
diff --git a/t/t4018/golang-func b/t/t4018/golang-func
deleted file mode 100644
index 0472cfd9798..00000000000
--- a/t/t4018/golang-func
+++ /dev/null
@@ -1,5 +0,0 @@
-t4018 header: func RIGHT() {
-func RIGHT() {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-interface b/t/t4018/golang-interface
deleted file mode 100644
index 3160a1d4524..00000000000
--- a/t/t4018/golang-interface
+++ /dev/null
@@ -1,5 +0,0 @@
-t4018 header: type RIGHT interface {
-type RIGHT interface {
-	a() Type
-	b() ChangeMe
-}
diff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func
deleted file mode 100644
index de83aaafca5..00000000000
--- a/t/t4018/golang-long-func
+++ /dev/null
@@ -1,6 +0,0 @@
-t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
-	anotherLongVariableName AnotherLongType) {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-struct b/t/t4018/golang-struct
deleted file mode 100644
index fc8022537b2..00000000000
--- a/t/t4018/golang-struct
+++ /dev/null
@@ -1,5 +0,0 @@
-t4018 header: type RIGHT struct {
-type RIGHT struct {
-	a Type
-	b ChangeMe
-}
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 11/35] userdiff tests: match full hunk headers

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:45

Fix a regression in the test framework for userdiff added in
bfa7d01413 (t4018: an infrastructure to test hunk headers,
2014-03-21).

The testing infrastructure added in that change went overboard with
simplifying the tests, to the point where we lost test coverage.

Before that we'd been able to test the full context line, or ever
since the feature was originally added in f258475a6e (Per-path
attribute based hunk header selection., 2007-07-06).

After bfa7d01413 all we cared about was whether "RIGHT" appeared on
the line. We thus lost the information about whether or not "RIGHT"
was extracted from the line for the hunk header, or the line appeared
in full (or other subset of the line).

Let's bring back coverage for that by extending the custom test syntax
here to understand "t4018" control lines, we grep those out before
doing the add/diff, and understand "t4018 header: " lines to declare
the full context line we're expecting.

Now when we have failures we'll benefit from the full test_cmp output,
instead of just getting a non-zero exit code from "grep".

The "sed -n -e" here was originally a single 's/^.*@@\( \|$\)//p'
pattern, but the '\( \|$\)' part had portability issues on OSX and
AIX.

The one-off addition of headers here to the test files was done by
instrumenting the test itself:

    grep '@@ .* @@.*RIGHT' actual >header.right &&
    sed 's/^@@.*@@ //' header.right >$i.header.small &&
    mv \"$TEST_DIRECTORY\"/t4018/$i tmp &&
    printf 't4018 header: ' >\"$TEST_DIRECTORY\"/t4018/$i &&
    cat $i.header.small >> \"$TEST_DIRECTORY\"/t4018/$i &&
    cat tmp >>\"$TEST_DIRECTORY\"/t4018/$i

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh                   | 27 +++++++++++++---------
 t/t4018/README                             | 25 ++++++++++++--------
 t/t4018/bash-arithmetic-function           |  1 +
 t/t4018/bash-bashism-style-compact         |  1 +
 t/t4018/bash-bashism-style-function        |  1 +
 t/t4018/bash-bashism-style-whitespace      |  1 +
 t/t4018/bash-conditional-function          |  1 +
 t/t4018/bash-missing-parentheses           |  1 +
 t/t4018/bash-mixed-style-compact           |  1 +
 t/t4018/bash-mixed-style-function          |  1 +
 t/t4018/bash-nested-functions              |  1 +
 t/t4018/bash-other-characters              |  1 +
 t/t4018/bash-posix-style-compact           |  1 +
 t/t4018/bash-posix-style-function          |  1 +
 t/t4018/bash-posix-style-whitespace        |  1 +
 t/t4018/bash-subshell-function             |  1 +
 t/t4018/bash-trailing-comment              |  1 +
 t/t4018/cpp-c++-function                   |  1 +
 t/t4018/cpp-class-constructor              |  1 +
 t/t4018/cpp-class-constructor-mem-init     |  1 +
 t/t4018/cpp-class-definition               |  1 +
 t/t4018/cpp-class-definition-derived       |  1 +
 t/t4018/cpp-class-destructor               |  1 +
 t/t4018/cpp-function-returning-global-type |  1 +
 t/t4018/cpp-function-returning-nested      |  1 +
 t/t4018/cpp-function-returning-pointer     |  1 +
 t/t4018/cpp-function-returning-reference   |  1 +
 t/t4018/cpp-gnu-style-function             |  1 +
 t/t4018/cpp-namespace-definition           |  1 +
 t/t4018/cpp-operator-definition            |  1 +
 t/t4018/cpp-skip-access-specifiers         |  1 +
 t/t4018/cpp-skip-comment-block             |  1 +
 t/t4018/cpp-skip-labels                    |  1 +
 t/t4018/cpp-struct-definition              |  1 +
 t/t4018/cpp-struct-single-line             |  1 +
 t/t4018/cpp-template-function-definition   |  1 +
 t/t4018/cpp-union-definition               |  1 +
 t/t4018/cpp-void-c-function                |  1 +
 t/t4018/css-attribute-value-selector       |  1 +
 t/t4018/css-block-level-@-statements       |  1 +
 t/t4018/css-brace-in-col-1                 |  1 +
 t/t4018/css-class-selector                 |  1 +
 t/t4018/css-colon-eol                      |  1 +
 t/t4018/css-colon-selector                 |  1 +
 t/t4018/css-common                         |  1 +
 t/t4018/css-id-selector                    |  1 +
 t/t4018/css-long-selector-list             |  1 +
 t/t4018/css-prop-sans-indent               |  1 +
 t/t4018/css-root-selector                  |  1 +
 t/t4018/css-short-selector-list            |  1 +
 t/t4018/css-trailing-space                 |  1 +
 t/t4018/custom1-pattern                    |  1 +
 t/t4018/custom2-match-to-end-of-line       |  1 +
 t/t4018/custom3-alternation-in-pattern     |  1 +
 t/t4018/dts-labels                         |  1 +
 t/t4018/dts-node-unitless                  |  1 +
 t/t4018/dts-nodes                          |  1 +
 t/t4018/dts-nodes-boolean-prop             |  1 +
 t/t4018/dts-nodes-comment1                 |  1 +
 t/t4018/dts-nodes-comment2                 |  1 +
 t/t4018/dts-nodes-multiline-prop           |  1 +
 t/t4018/dts-reference                      |  1 +
 t/t4018/dts-root                           |  1 +
 t/t4018/dts-root-comment                   |  1 +
 t/t4018/elixir-do-not-pick-end             |  1 +
 t/t4018/elixir-ex-unit-test                |  1 +
 t/t4018/elixir-function                    |  1 +
 t/t4018/elixir-macro                       |  1 +
 t/t4018/elixir-module                      |  1 +
 t/t4018/elixir-module-func                 |  1 +
 t/t4018/elixir-nested-module               |  1 +
 t/t4018/elixir-private-function            |  1 +
 t/t4018/elixir-protocol                    |  1 +
 t/t4018/elixir-protocol-implementation     |  1 +
 t/t4018/fortran-block-data                 |  1 +
 t/t4018/fortran-comment                    |  1 +
 t/t4018/fortran-comment-keyword            |  1 +
 t/t4018/fortran-comment-legacy             |  1 +
 t/t4018/fortran-comment-legacy-star        |  1 +
 t/t4018/fortran-external-function          |  1 +
 t/t4018/fortran-external-subroutine        |  1 +
 t/t4018/fortran-module                     |  1 +
 t/t4018/fortran-module-procedure           |  1 +
 t/t4018/fortran-program                    |  1 +
 t/t4018/fountain-scene                     |  1 +
 t/t4018/golang-complex-function            |  1 +
 t/t4018/golang-func                        |  1 +
 t/t4018/golang-interface                   |  1 +
 t/t4018/golang-long-func                   |  1 +
 t/t4018/golang-struct                      |  1 +
 t/t4018/java-class-member-function         |  1 +
 t/t4018/markdown-heading-indented          |  1 +
 t/t4018/markdown-heading-non-headings      |  1 +
 t/t4018/matlab-class-definition            |  1 +
 t/t4018/matlab-function                    |  1 +
 t/t4018/matlab-octave-section-1            |  1 +
 t/t4018/matlab-octave-section-2            |  1 +
 t/t4018/matlab-section                     |  1 +
 t/t4018/perl-skip-end-of-heredoc           |  1 +
 t/t4018/perl-skip-forward-decl             |  1 +
 t/t4018/perl-skip-sub-in-pod               |  1 +
 t/t4018/perl-sub-definition                |  1 +
 t/t4018/perl-sub-definition-kr-brace       |  1 +
 t/t4018/php-abstract-class                 |  1 +
 t/t4018/php-abstract-method                |  1 +
 t/t4018/php-class                          |  1 +
 t/t4018/php-final-class                    |  1 +
 t/t4018/php-final-method                   |  1 +
 t/t4018/php-function                       |  1 +
 t/t4018/php-interface                      |  1 +
 t/t4018/php-method                         |  1 +
 t/t4018/php-trait                          |  1 +
 t/t4018/python-async-def                   |  1 +
 t/t4018/python-class                       |  1 +
 t/t4018/python-def                         |  1 +
 t/t4018/python-indented-async-def          |  1 +
 t/t4018/python-indented-class              |  1 +
 t/t4018/python-indented-def                |  1 +
 t/t4018/rust-fn                            |  1 +
 t/t4018/rust-impl                          |  1 +
 t/t4018/rust-macro-rules                   |  1 +
 t/t4018/rust-struct                        |  1 +
 t/t4018/rust-trait                         |  1 +
 123 files changed, 153 insertions(+), 20 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 9aec9f8e6de..15dcbe735ca 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -70,16 +70,20 @@ test_expect_success 'setup hunk header tests' '
 		echo "$i-* diff=$i"
 	done > .gitattributes &&
 
-	# add all test files to the index
-	(
-		cd "$TEST_DIRECTORY"/t4018 &&
-		git --git-dir="$TRASH_DIRECTORY/.git" add .
-	) &&
-
-	# place modified files in the worktree
-	for i in $(git ls-files)
+	cp -R "$TEST_DIRECTORY"/t4018 . &&
+	git init t4018 &&
+	git -C t4018 add . &&
+
+	for i in $(git -C t4018 ls-files)
 	do
-		sed -e "s/ChangeMe/IWasChanged/" <"$TEST_DIRECTORY/t4018/$i" >"$i" || return 1
+		grep -v "^t4018" "t4018/$i" >"t4018/$i.content" &&
+		sed -n -e "s/^t4018 header: //p" <"t4018/$i" >"t4018/$i.header" &&
+		cp "t4018/$i.content" "$i" &&
+
+		# add test file to the index
+		git add "$i" &&
+		# place modified file in the worktree
+		sed -e "s/ChangeMe/IWasChanged/" <"t4018/$i.content" >"$i" || return 1
 	done
 '
 
@@ -87,8 +91,9 @@ test_expect_success 'setup hunk header tests' '
 for i in $(git ls-files)
 do
 	test_expect_success "hunk header: $i" "
-		git diff -U1 $i >actual &&
-		grep '@@ .* @@.*RIGHT' actual
+		git diff -U1 $i >diff &&
+		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
+		test_cmp t4018/$i.header ctx
 	"
 done
 
diff --git a/t/t4018/README b/t/t4018/README
index 2d25b2b4fc9..0a246bbc10e 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -1,15 +1,22 @@
-How to write RIGHT test cases
-=============================
+t4018 header: description of the test.
+How to write test cases
+=======================
+
+Create test cases called "LANG-whatever" in this directory, where
+"LANG" is e.g. the userdiff driver name, where "whatever" is a brief
+description of the test.
 
 Insert the word "ChangeMe" (exactly this form) at a distance of
 at least two lines from the line that must appear in the hunk header.
 
-The text that must appear in the hunk header must contain the word
-"right", but in all upper-case, like in the title above.
+Any line starting with "t4018" is a control line for the test:
 
-This text is a bit twisted and out of order, but it is itself a
-test case for the default hunk header pattern. Know what you are doing
-if you change it.
+ - The "t4018 header:" line above specifies what text must appear in
+   the hunk header. We strip away the starting "@@ [...] @@" part of
+   the line for ease of not having to hardcode the line numbers and
+   offsets.
 
-BTW, this tests that the head line goes to the hunk header, not the line
-of equal signs.
+In many of the test cases the header line includes the token "RIGHT",
+this used to be part of the test syntax, but isn't anymore. Now we
+care about the "t4018 header:" line, not whatever line contains a
+"RIGHT" token.
diff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function
index c0b276cb50f..f5609bf49eb 100644
--- a/t/t4018/bash-arithmetic-function
+++ b/t/t4018/bash-arithmetic-function
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT() ((
 
     ChangeMe = "$x" + "$y"
diff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact
index 1ca3126f611..42611aad166 100644
--- a/t/t4018/bash-bashism-style-compact
+++ b/t/t4018/bash-bashism-style-compact
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT {
 function RIGHT {
     function InvalidSyntax{
         :
diff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function
index f1de4fa831c..823b13a463b 100644
--- a/t/t4018/bash-bashism-style-function
+++ b/t/t4018/bash-bashism-style-function
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT {
 function RIGHT {
     :
     echo 'ChangeMe'
diff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace
index ade85dd3a50..4f1add4a4e5 100644
--- a/t/t4018/bash-bashism-style-whitespace
+++ b/t/t4018/bash-bashism-style-whitespace
@@ -1,3 +1,4 @@
+t4018 header: function 	RIGHT 	( 	) 	{
 	 function 	RIGHT 	( 	) 	{
 
 	    ChangeMe
diff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function
index c5949e829ba..1e71a71a320 100644
--- a/t/t4018/bash-conditional-function
+++ b/t/t4018/bash-conditional-function
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT() [[ \
 
     "$a" > "$ChangeMe"
diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses
index 8c8a05dd7ab..9233042d91f 100644
--- a/t/t4018/bash-missing-parentheses
+++ b/t/t4018/bash-missing-parentheses
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT {
 function RIGHT {
     functionInvalidSyntax {
         :
diff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact
index d9364cba671..0f93c2be55a 100644
--- a/t/t4018/bash-mixed-style-compact
+++ b/t/t4018/bash-mixed-style-compact
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT(){
 function RIGHT(){
     :
     echo 'ChangeMe'
diff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function
index 555f9b24667..b3024cdc6dc 100644
--- a/t/t4018/bash-mixed-style-function
+++ b/t/t4018/bash-mixed-style-function
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT() {
 function RIGHT() {
 
     ChangeMe
diff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions
index 2c9237ead42..1eee6b9a830 100644
--- a/t/t4018/bash-nested-functions
+++ b/t/t4018/bash-nested-functions
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 outer() {
     RIGHT() {
         :
diff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters
index a3f390d525d..02fa1ec822d 100644
--- a/t/t4018/bash-other-characters
+++ b/t/t4018/bash-other-characters
@@ -1,3 +1,4 @@
+t4018 header: _RIGHT_0n()
 _RIGHT_0n() {
 
     ChangeMe
diff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact
index 045bd2029b7..7ba61dea663 100644
--- a/t/t4018/bash-posix-style-compact
+++ b/t/t4018/bash-posix-style-compact
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT(){
 
     ChangeMe
diff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function
index a4d144856e9..8566c3f3838 100644
--- a/t/t4018/bash-posix-style-function
+++ b/t/t4018/bash-posix-style-function
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT() {
 
     ChangeMe
diff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace
index 4d984f0aa4d..dcc06da3f67 100644
--- a/t/t4018/bash-posix-style-whitespace
+++ b/t/t4018/bash-posix-style-whitespace
@@ -1,3 +1,4 @@
+t4018 header: RIGHT 	( 	)
 	 RIGHT 	( 	) 	{
 
 	    ChangeMe
diff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function
index 80baa09484e..f6b188679a2 100644
--- a/t/t4018/bash-subshell-function
+++ b/t/t4018/bash-subshell-function
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT() (
 
     ChangeMe=2
diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment
index f1edbeda319..16ba9701f3f 100644
--- a/t/t4018/bash-trailing-comment
+++ b/t/t4018/bash-trailing-comment
@@ -1,3 +1,4 @@
+t4018 header: RIGHT()
 RIGHT() { # Comment
 
     ChangeMe
diff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function
index 9ee6bbef557..316ad0eb34f 100644
--- a/t/t4018/cpp-c++-function
+++ b/t/t4018/cpp-c++-function
@@ -1,3 +1,4 @@
+t4018 header: Item RIGHT::DoSomething( Args with_spaces )
 Item RIGHT::DoSomething( Args with_spaces )
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor
index ec4f115c250..38552023f00 100644
--- a/t/t4018/cpp-class-constructor
+++ b/t/t4018/cpp-class-constructor
@@ -1,3 +1,4 @@
+t4018 header: Item::Item(int RIGHT)
 Item::Item(int RIGHT)
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init
index 49a69f37e16..dc48c12ac25 100644
--- a/t/t4018/cpp-class-constructor-mem-init
+++ b/t/t4018/cpp-class-constructor-mem-init
@@ -1,3 +1,4 @@
+t4018 header: Item::Item(int RIGHT) :
 Item::Item(int RIGHT) :
 	member(0)
 {
diff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition
index 11b61da3b75..1f258c8f980 100644
--- a/t/t4018/cpp-class-definition
+++ b/t/t4018/cpp-class-definition
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT
 class RIGHT
 {
 	int ChangeMe;
diff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived
index 3b98cd09ab5..1e5ec3b4837 100644
--- a/t/t4018/cpp-class-definition-derived
+++ b/t/t4018/cpp-class-definition-derived
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT :
 class RIGHT :
 	public Baseclass
 {
diff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor
index 54876650965..a06a37169ad 100644
--- a/t/t4018/cpp-class-destructor
+++ b/t/t4018/cpp-class-destructor
@@ -1,3 +1,4 @@
+t4018 header: RIGHT::~RIGHT()
 RIGHT::~RIGHT()
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type
index 1084d5990ef..9bb8f57474f 100644
--- a/t/t4018/cpp-function-returning-global-type
+++ b/t/t4018/cpp-function-returning-global-type
@@ -1,3 +1,4 @@
+t4018 header: ::Item get::it::RIGHT()
 ::Item get::it::RIGHT()
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested
index d9750aa61a5..3a35a39816a 100644
--- a/t/t4018/cpp-function-returning-nested
+++ b/t/t4018/cpp-function-returning-nested
@@ -1,3 +1,4 @@
+t4018 header: get::Item get::it::RIGHT()
 get::Item get::it::RIGHT()
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer
index ef15657ea8f..9890c5488db 100644
--- a/t/t4018/cpp-function-returning-pointer
+++ b/t/t4018/cpp-function-returning-pointer
@@ -1,3 +1,4 @@
+t4018 header: const char *get_it_RIGHT(char *ptr)
 const char *get_it_RIGHT(char *ptr)
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference
index 01b051df701..7147ab9a74d 100644
--- a/t/t4018/cpp-function-returning-reference
+++ b/t/t4018/cpp-function-returning-reference
@@ -1,3 +1,4 @@
+t4018 header: string& get::it::RIGHT(char *ptr)
 string& get::it::RIGHT(char *ptr)
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function
index 08c7c7565ae..91e243f3869 100644
--- a/t/t4018/cpp-gnu-style-function
+++ b/t/t4018/cpp-gnu-style-function
@@ -1,3 +1,4 @@
+t4018 header: RIGHT(int arg)
 const char *
 RIGHT(int arg)
 {
diff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition
index 6749980241c..bd32988b3f9 100644
--- a/t/t4018/cpp-namespace-definition
+++ b/t/t4018/cpp-namespace-definition
@@ -1,3 +1,4 @@
+t4018 header: namespace RIGHT
 namespace RIGHT
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition
index 1acd8271592..7e8ba945281 100644
--- a/t/t4018/cpp-operator-definition
+++ b/t/t4018/cpp-operator-definition
@@ -1,3 +1,4 @@
+t4018 header: Value operator+(Value LEFT, Value RIGHT)
 Value operator+(Value LEFT, Value RIGHT)
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers
index 4d4a9dbb9db..d61c86d3703 100644
--- a/t/t4018/cpp-skip-access-specifiers
+++ b/t/t4018/cpp-skip-access-specifiers
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT : public Baseclass
 class RIGHT : public Baseclass
 {
 public:
diff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block
index 3800b9967a5..ea4d9c25911 100644
--- a/t/t4018/cpp-skip-comment-block
+++ b/t/t4018/cpp-skip-comment-block
@@ -1,3 +1,4 @@
+t4018 header: struct item RIGHT(int i)
 struct item RIGHT(int i)
 // Do not
 // pick up
diff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels
index b9c10aba225..cf508647281 100644
--- a/t/t4018/cpp-skip-labels
+++ b/t/t4018/cpp-skip-labels
@@ -1,3 +1,4 @@
+t4018 header: void RIGHT (void)
 void RIGHT (void)
 {
 repeat:		// C++ comment
diff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition
index 521c59fd151..a0f9a16204d 100644
--- a/t/t4018/cpp-struct-definition
+++ b/t/t4018/cpp-struct-definition
@@ -1,3 +1,4 @@
+t4018 header: struct RIGHT {
 struct RIGHT {
 	unsigned
 	/* this bit field looks like a label and should not be picked up */
diff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line
index a0de5fb800f..0d2378a320a 100644
--- a/t/t4018/cpp-struct-single-line
+++ b/t/t4018/cpp-struct-single-line
@@ -1,3 +1,4 @@
+t4018 header: struct RIGHT_iterator_tag {};
 void wrong()
 {
 }
diff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition
index 0cdf5ba5bd4..f1ea1e54f5a 100644
--- a/t/t4018/cpp-template-function-definition
+++ b/t/t4018/cpp-template-function-definition
@@ -1,3 +1,4 @@
+t4018 header: template<class T> int RIGHT(T arg)
 template<class T> int RIGHT(T arg)
 {
 	ChangeMe;
diff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition
index 7ec94df6973..6c00ab3b430 100644
--- a/t/t4018/cpp-union-definition
+++ b/t/t4018/cpp-union-definition
@@ -1,3 +1,4 @@
+t4018 header: union RIGHT {
 union RIGHT {
 	double v;
 	int ChangeMe;
diff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function
index 153081e872c..08765096c96 100644
--- a/t/t4018/cpp-void-c-function
+++ b/t/t4018/cpp-void-c-function
@@ -1,3 +1,4 @@
+t4018 header: void RIGHT (void)
 void RIGHT (void)
 {
 	ChangeMe;
diff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector
index 918256b20c5..8fe8429eb02 100644
--- a/t/t4018/css-attribute-value-selector
+++ b/t/t4018/css-attribute-value-selector
@@ -1,3 +1,4 @@
+t4018 header: [class*="RIGHT"] {
 [class*="RIGHT"] {
     background : #000;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements
index d6755f2f3db..0ea1b9eb37c 100644
--- a/t/t4018/css-block-level-@-statements
+++ b/t/t4018/css-block-level-@-statements
@@ -1,3 +1,4 @@
+t4018 header: @keyframes RIGHT {
 @keyframes RIGHT {
     from {
         background : #000;
diff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1
index 7831577506a..8bc58ce7098 100644
--- a/t/t4018/css-brace-in-col-1
+++ b/t/t4018/css-brace-in-col-1
@@ -1,3 +1,4 @@
+t4018 header: RIGHT label.control-label
 RIGHT label.control-label
 {
     margin-top: 10px!important;
diff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector
index f790a0062f4..9b7a2b80e72 100644
--- a/t/t4018/css-class-selector
+++ b/t/t4018/css-class-selector
@@ -1,3 +1,4 @@
+t4018 header: .RIGHT {
 .RIGHT {
     background : #000;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol
index 5a30553d291..665c9f6af5b 100644
--- a/t/t4018/css-colon-eol
+++ b/t/t4018/css-colon-eol
@@ -1,3 +1,4 @@
+t4018 header: RIGHT h1 {
 RIGHT h1 {
 color:
 ChangeMe;
diff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector
index c6d71fb42de..dd0e897155f 100644
--- a/t/t4018/css-colon-selector
+++ b/t/t4018/css-colon-selector
@@ -1,3 +1,4 @@
+t4018 header: RIGHT a:hover {
 RIGHT a:hover {
     margin-top:
     10px!important;
diff --git a/t/t4018/css-common b/t/t4018/css-common
index 84ed754b33b..8351eac792b 100644
--- a/t/t4018/css-common
+++ b/t/t4018/css-common
@@ -1,3 +1,4 @@
+t4018 header: RIGHT label.control-label {
 RIGHT label.control-label {
     margin-top: 10px!important;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector
index 17c5111052d..2a4351a4f0b 100644
--- a/t/t4018/css-id-selector
+++ b/t/t4018/css-id-selector
@@ -1,3 +1,4 @@
+t4018 header: #RIGHT {
 #RIGHT {
     background : #000;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list
index 7ccd25d9ed6..e5fba459290 100644
--- a/t/t4018/css-long-selector-list
+++ b/t/t4018/css-long-selector-list
@@ -1,3 +1,4 @@
+t4018 header: div ul#RIGHT {
 p.header,
 label.control-label,
 div ul#RIGHT {
diff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent
index a9e3c86b3c9..8ca742c91f8 100644
--- a/t/t4018/css-prop-sans-indent
+++ b/t/t4018/css-prop-sans-indent
@@ -1,3 +1,4 @@
+t4018 header: RIGHT, label.control-label {
 RIGHT, label.control-label {
 margin-top: 10px!important;
 padding: 0;
diff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector
index 22b958e3694..327d7630649 100644
--- a/t/t4018/css-root-selector
+++ b/t/t4018/css-root-selector
@@ -1,3 +1,4 @@
+t4018 header: :RIGHT {
 :RIGHT {
     background : #000;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list
index 6a0bdee336b..8a8937a5bd8 100644
--- a/t/t4018/css-short-selector-list
+++ b/t/t4018/css-short-selector-list
@@ -1,3 +1,4 @@
+t4018 header: label.control, div ul#RIGHT {
 label.control, div ul#RIGHT {
     margin-top: 10px!important;
     border : 10px ChangeMe #C6C6C6;
diff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space
index 32b5606c70f..68956baab70 100644
--- a/t/t4018/css-trailing-space
+++ b/t/t4018/css-trailing-space
@@ -1,3 +1,4 @@
+t4018 header: RIGHT label.control-label {
 RIGHT label.control-label {
     margin:10px;   
     padding:10px;
diff --git a/t/t4018/custom1-pattern b/t/t4018/custom1-pattern
index e8fd59f884d..37a3422384b 100644
--- a/t/t4018/custom1-pattern
+++ b/t/t4018/custom1-pattern
@@ -1,3 +1,4 @@
+t4018 header: int special, RIGHT;
 public class Beer
 {
 	int special, RIGHT;
diff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line
index f88ac318b79..4800bb1c568 100644
--- a/t/t4018/custom2-match-to-end-of-line
+++ b/t/t4018/custom2-match-to-end-of-line
@@ -1,3 +1,4 @@
+t4018 header: RIGHT_Beer
 public class RIGHT_Beer
 {
 	int special;
diff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3-alternation-in-pattern
index 5f3769c64fc..bf7df3d9a73 100644
--- a/t/t4018/custom3-alternation-in-pattern
+++ b/t/t4018/custom3-alternation-in-pattern
@@ -1,3 +1,4 @@
+t4018 header: public static void main(String RIGHT[])
 public class Beer
 {
 	int special;
diff --git a/t/t4018/dts-labels b/t/t4018/dts-labels
index b21ef8737bb..6b907dcdedf 100644
--- a/t/t4018/dts-labels
+++ b/t/t4018/dts-labels
@@ -1,3 +1,4 @@
+t4018 header: label2: RIGHT {
 / {
 	label_1: node1@ff00 {
 		label2: RIGHT {
diff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless
index c5287d91416..990730ef6f4 100644
--- a/t/t4018/dts-node-unitless
+++ b/t/t4018/dts-node-unitless
@@ -1,3 +1,4 @@
+t4018 header: RIGHT {
 / {
 	label_1: node1 {
 		RIGHT {
diff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes
index 5a4334bb164..4bcebc85e60 100644
--- a/t/t4018/dts-nodes
+++ b/t/t4018/dts-nodes
@@ -1,3 +1,4 @@
+t4018 header: RIGHT@deadf00,4000 {
 / {
 	label_1: node1@ff00 {
 		RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop
index afc6b5b404e..404cab6f0e1 100644
--- a/t/t4018/dts-nodes-boolean-prop
+++ b/t/t4018/dts-nodes-boolean-prop
@@ -1,3 +1,4 @@
+t4018 header: RIGHT@deadf00,4000 {
 / {
 	label_1: node1@ff00 {
 		RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1
index 559dfce9b30..2e5cea26818 100644
--- a/t/t4018/dts-nodes-comment1
+++ b/t/t4018/dts-nodes-comment1
@@ -1,3 +1,4 @@
+t4018 header: RIGHT@deadf00,4000 /* &a comment */ {
 / {
 	label_1: node1@ff00 {
 		RIGHT@deadf00,4000 /* &a comment */ {
diff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2
index 27e9718b31c..f6cc3177958 100644
--- a/t/t4018/dts-nodes-comment2
+++ b/t/t4018/dts-nodes-comment2
@@ -1,3 +1,4 @@
+t4018 header: RIGHT@deadf00,4000 { /* a trailing comment */
 / {
 	label_1: node1@ff00 {
 		RIGHT@deadf00,4000 { /* a trailing comment */ 
diff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop
index 072d58b69dc..ac07b404975 100644
--- a/t/t4018/dts-nodes-multiline-prop
+++ b/t/t4018/dts-nodes-multiline-prop
@@ -1,3 +1,4 @@
+t4018 header: RIGHT@deadf00,4000 {
 / {
 	label_1: node1@ff00 {
 		RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-reference b/t/t4018/dts-reference
index 8f0c87d8637..bf141229e93 100644
--- a/t/t4018/dts-reference
+++ b/t/t4018/dts-reference
@@ -1,3 +1,4 @@
+t4018 header: &RIGHT {
 &label_1 {
 	TEST = <455>;
 };
diff --git a/t/t4018/dts-root b/t/t4018/dts-root
index 4353b8220c9..4105b0bb48a 100644
--- a/t/t4018/dts-root
+++ b/t/t4018/dts-root
@@ -1,3 +1,4 @@
+t4018 header: / { RIGHT /* Technically just supposed to be a slash and brace */
 / { RIGHT /* Technically just supposed to be a slash and brace */
 	#size-cells = <1>;
 
diff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment
index 333a625c700..cdf2cee98db 100644
--- a/t/t4018/dts-root-comment
+++ b/t/t4018/dts-root-comment
@@ -1,3 +1,4 @@
+t4018 header: / { RIGHT /* Technically just supposed to be a slash and brace */
 / { RIGHT /* Technically just supposed to be a slash and brace */
 	#size-cells = <1>;
 
diff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end
index fae08ba7e8c..cf861f81e9a 100644
--- a/t/t4018/elixir-do-not-pick-end
+++ b/t/t4018/elixir-do-not-pick-end
@@ -1,3 +1,4 @@
+t4018 header: defmodule RIGHT do
 defmodule RIGHT do
 end
 #
diff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test
index 0560a2b6971..469f1a03b71 100644
--- a/t/t4018/elixir-ex-unit-test
+++ b/t/t4018/elixir-ex-unit-test
@@ -1,3 +1,4 @@
+t4018 header: test "RIGHT" do
 defmodule Test do
   test "RIGHT" do
     assert true == true
diff --git a/t/t4018/elixir-function b/t/t4018/elixir-function
index d452f495a7e..139a4e1da8e 100644
--- a/t/t4018/elixir-function
+++ b/t/t4018/elixir-function
@@ -1,3 +1,4 @@
+t4018 header: def function(RIGHT, arg) do
 def function(RIGHT, arg) do
   # comment
   # comment
diff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro
index 4f925e9ad46..0b0f7d721bb 100644
--- a/t/t4018/elixir-macro
+++ b/t/t4018/elixir-macro
@@ -1,3 +1,4 @@
+t4018 header: defmacro foo(RIGHT) do
 defmacro foo(RIGHT) do
   # Code
   # Code
diff --git a/t/t4018/elixir-module b/t/t4018/elixir-module
index 91a4e7aa200..a4773f4abae 100644
--- a/t/t4018/elixir-module
+++ b/t/t4018/elixir-module
@@ -1,3 +1,4 @@
+t4018 header: defmodule RIGHT do
 defmodule RIGHT do
   @moduledoc """
   Foo bar
diff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func
index c9910d06751..138493829b6 100644
--- a/t/t4018/elixir-module-func
+++ b/t/t4018/elixir-module-func
@@ -1,3 +1,4 @@
+t4018 header: def fun(RIGHT) do
 defmodule Foo do
   def fun(RIGHT) do
      # Code
diff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module
index 771ebc5c42a..296990dd2db 100644
--- a/t/t4018/elixir-nested-module
+++ b/t/t4018/elixir-nested-module
@@ -1,3 +1,4 @@
+t4018 header: defmodule MyApp.RIGHT do
 defmodule MyApp.RIGHT do
   @moduledoc """
   Foo bar
diff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function
index 1aabe33b7a9..94428e775b5 100644
--- a/t/t4018/elixir-private-function
+++ b/t/t4018/elixir-private-function
@@ -1,3 +1,4 @@
+t4018 header: defp function(RIGHT, arg) do
 defp function(RIGHT, arg) do
   # comment
   # comment
diff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol
index 7d9173691e3..1f1dc8cb199 100644
--- a/t/t4018/elixir-protocol
+++ b/t/t4018/elixir-protocol
@@ -1,3 +1,4 @@
+t4018 header: defprotocol RIGHT do
 defprotocol RIGHT do
   @doc """
   Calculates the size (and not the length!) of a data structure
diff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation
index f9234bbfc48..973226206d7 100644
--- a/t/t4018/elixir-protocol-implementation
+++ b/t/t4018/elixir-protocol-implementation
@@ -1,3 +1,4 @@
+t4018 header: defimpl RIGHT do
 defimpl RIGHT do
   # Docs
   # Docs
diff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data
index 63d4e21d0ad..eb0a77afade 100644
--- a/t/t4018/fortran-block-data
+++ b/t/t4018/fortran-block-data
@@ -1,3 +1,4 @@
+t4018 header: BLOCK DATA RIGHT
        BLOCK DATA RIGHT
        
        COMMON /B/ C, ChangeMe
diff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment
index 7b10d176588..e5e82331ebc 100644
--- a/t/t4018/fortran-comment
+++ b/t/t4018/fortran-comment
@@ -1,3 +1,4 @@
+t4018 header: subroutine RIGHT
       module a
 
       contains
diff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword
index e9206a53799..143aff89d73 100644
--- a/t/t4018/fortran-comment-keyword
+++ b/t/t4018/fortran-comment-keyword
@@ -1,3 +1,4 @@
+t4018 header: subroutine RIGHT (funcA, funcB)
       module a
 
       contains
diff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy
index 53cd062c1e5..edbac0b4148 100644
--- a/t/t4018/fortran-comment-legacy
+++ b/t/t4018/fortran-comment-legacy
@@ -1,3 +1,4 @@
+t4018 header: subroutine RIGHT
       module a
 
       contains
diff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star
index 2cbcdc3d8ab..be918f7b030 100644
--- a/t/t4018/fortran-comment-legacy-star
+++ b/t/t4018/fortran-comment-legacy-star
@@ -1,3 +1,4 @@
+t4018 header: subroutine RIGHT
       module a
 
       contains
diff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function
index 5a2d85d3aa4..ea4d8bcedc4 100644
--- a/t/t4018/fortran-external-function
+++ b/t/t4018/fortran-external-function
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT(a, b) result(c)
 function RIGHT(a, b) result(c)
 
 integer, intent(in) :: ChangeMe
diff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine
index 4ce85fea132..9f662dd26b4 100644
--- a/t/t4018/fortran-external-subroutine
+++ b/t/t4018/fortran-external-subroutine
@@ -1,3 +1,4 @@
+t4018 header: subroutine RIGHT
 subroutine RIGHT
 
 real ChangeMe
diff --git a/t/t4018/fortran-module b/t/t4018/fortran-module
index c4b737dac3f..1882b3cd338 100644
--- a/t/t4018/fortran-module
+++ b/t/t4018/fortran-module
@@ -1,3 +1,4 @@
+t4018 header: module RIGHT
 module RIGHT
 
 use ChangeMe
diff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure
index 1ce6d854c22..51d368ba9be 100644
--- a/t/t4018/fortran-module-procedure
+++ b/t/t4018/fortran-module-procedure
@@ -1,3 +1,4 @@
+t4018 header: module RIGHT
  module RIGHT
 
    implicit none
diff --git a/t/t4018/fortran-program b/t/t4018/fortran-program
index 4616895e4b5..1c84e1ff0f8 100644
--- a/t/t4018/fortran-program
+++ b/t/t4018/fortran-program
@@ -1,3 +1,4 @@
+t4018 header: program RIGHT
 program RIGHT
 
 call ChangeMe
diff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene
index 6b3257d6803..2ffcf799087 100644
--- a/t/t4018/fountain-scene
+++ b/t/t4018/fountain-scene
@@ -1,3 +1,4 @@
+t4018 header: EXT. STREET RIGHT OUTSIDE - DAY
 EXT. STREET RIGHT OUTSIDE - DAY
 
 CHARACTER
diff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function
index e057dcefed6..0574ba912e6 100644
--- a/t/t4018/golang-complex-function
+++ b/t/t4018/golang-complex-function
@@ -1,3 +1,4 @@
+t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
 type Test struct {
 	a Type
 }
diff --git a/t/t4018/golang-func b/t/t4018/golang-func
index 8e9c9ac7c3f..0472cfd9798 100644
--- a/t/t4018/golang-func
+++ b/t/t4018/golang-func
@@ -1,3 +1,4 @@
+t4018 header: func RIGHT() {
 func RIGHT() {
 	a := 5
 	b := ChangeMe
diff --git a/t/t4018/golang-interface b/t/t4018/golang-interface
index 553bedec962..3160a1d4524 100644
--- a/t/t4018/golang-interface
+++ b/t/t4018/golang-interface
@@ -1,3 +1,4 @@
+t4018 header: type RIGHT interface {
 type RIGHT interface {
 	a() Type
 	b() ChangeMe
diff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func
index ac3a77b5c41..de83aaafca5 100644
--- a/t/t4018/golang-long-func
+++ b/t/t4018/golang-long-func
@@ -1,3 +1,4 @@
+t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
 func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
 	anotherLongVariableName AnotherLongType) {
 	a := 5
diff --git a/t/t4018/golang-struct b/t/t4018/golang-struct
index 5deda77feec..fc8022537b2 100644
--- a/t/t4018/golang-struct
+++ b/t/t4018/golang-struct
@@ -1,3 +1,4 @@
+t4018 header: type RIGHT struct {
 type RIGHT struct {
 	a Type
 	b ChangeMe
diff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function
index 298bc7a71b2..df77bd71f79 100644
--- a/t/t4018/java-class-member-function
+++ b/t/t4018/java-class-member-function
@@ -1,3 +1,4 @@
+t4018 header: public static void main(String RIGHT[])
 public class Beer
 {
 	int special;
diff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented
index 1991c2bd456..e68801c7c06 100644
--- a/t/t4018/markdown-heading-indented
+++ b/t/t4018/markdown-heading-indented
@@ -1,3 +1,4 @@
+t4018 header:    ### RIGHT
 Indented headings are allowed, as long as the indent is no more than 3 spaces.
 
    ### RIGHT
diff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings
index c479c1a3f1e..00d677ebe95 100644
--- a/t/t4018/markdown-heading-non-headings
+++ b/t/t4018/markdown-heading-non-headings
@@ -1,3 +1,4 @@
+t4018 header: # RIGHT
 Headings can be right next to other lines of the file:
 # RIGHT
 Indents of four or more spaces make a code block:
diff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition
index 84daedfb4e5..86b48eebe0a 100644
--- a/t/t4018/matlab-class-definition
+++ b/t/t4018/matlab-class-definition
@@ -1,3 +1,4 @@
+t4018 header: classdef RIGHT
 classdef RIGHT
     properties
         ChangeMe
diff --git a/t/t4018/matlab-function b/t/t4018/matlab-function
index 897a9b13ff4..9a93efffb99 100644
--- a/t/t4018/matlab-function
+++ b/t/t4018/matlab-function
@@ -1,3 +1,4 @@
+t4018 header: function y = RIGHT()
 function y = RIGHT()
 x = 5;
 y = ChangeMe + x;
diff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1
index 3bb6c4670e2..b896cb07ff1 100644
--- a/t/t4018/matlab-octave-section-1
+++ b/t/t4018/matlab-octave-section-1
@@ -1,3 +1,4 @@
+t4018 header: %%% RIGHT section
 %%% RIGHT section
 # this is octave script
 ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2
index ab2980f7f29..4e75942678f 100644
--- a/t/t4018/matlab-octave-section-2
+++ b/t/t4018/matlab-octave-section-2
@@ -1,3 +1,4 @@
+t4018 header: ## RIGHT section
 ## RIGHT section
 # this is octave script
 ChangeMe = 1;
diff --git a/t/t4018/matlab-section b/t/t4018/matlab-section
index 5ea59a5de00..6b45ae20009 100644
--- a/t/t4018/matlab-section
+++ b/t/t4018/matlab-section
@@ -1,3 +1,4 @@
+t4018 header: %% RIGHT section
 %% RIGHT section
 % this is understood by both matlab and octave
 ChangeMe = 1;
diff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc
index c22d39b2567..8f90cca7314 100644
--- a/t/t4018/perl-skip-end-of-heredoc
+++ b/t/t4018/perl-skip-end-of-heredoc
@@ -1,3 +1,4 @@
+t4018 header: sub RIGHTwithheredocument {
 sub RIGHTwithheredocument {
 	print <<"EOF"
 decoy here-doc
diff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl
index a98cb8bdad0..ff1f6d14735 100644
--- a/t/t4018/perl-skip-forward-decl
+++ b/t/t4018/perl-skip-forward-decl
@@ -1,3 +1,4 @@
+t4018 header: package RIGHT;
 package RIGHT;
 
 use strict;
diff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod
index e39f02462e2..ff1c65b28fc 100644
--- a/t/t4018/perl-skip-sub-in-pod
+++ b/t/t4018/perl-skip-sub-in-pod
@@ -1,3 +1,4 @@
+t4018 header: =head1 SYNOPSIS_RIGHT
 =head1 NAME
 
 Beer - subroutine to output fragment of a drinking song
diff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition
index a507d1f6452..22e16ad5363 100644
--- a/t/t4018/perl-sub-definition
+++ b/t/t4018/perl-sub-definition
@@ -1,3 +1,4 @@
+t4018 header: sub RIGHT {
 sub RIGHT {
 	my ($n) = @_;
 	print "ChangeMe";
diff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace
index 330b3df1142..6c94e6a62dd 100644
--- a/t/t4018/perl-sub-definition-kr-brace
+++ b/t/t4018/perl-sub-definition-kr-brace
@@ -1,3 +1,4 @@
+t4018 header: sub RIGHT
 sub RIGHT
 {
 	print "ChangeMe\n";
diff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class
index 5213e124946..fbc97843c6b 100644
--- a/t/t4018/php-abstract-class
+++ b/t/t4018/php-abstract-class
@@ -1,3 +1,4 @@
+t4018 header: abstract class RIGHT
 abstract class RIGHT
 {
     const FOO = 'ChangeMe';
diff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method
index ce215df75a4..22f5120d3b7 100644
--- a/t/t4018/php-abstract-method
+++ b/t/t4018/php-abstract-method
@@ -1,3 +1,4 @@
+t4018 header: abstract public function RIGHT(): ?string
 abstract class Klass
 {
     abstract public function RIGHT(): ?string
diff --git a/t/t4018/php-class b/t/t4018/php-class
index 7785b6303c7..42456cda768 100644
--- a/t/t4018/php-class
+++ b/t/t4018/php-class
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT
 class RIGHT
 {
     const FOO = 'ChangeMe';
diff --git a/t/t4018/php-final-class b/t/t4018/php-final-class
index 69f57105529..ccf2d28a2d8 100644
--- a/t/t4018/php-final-class
+++ b/t/t4018/php-final-class
@@ -1,3 +1,4 @@
+t4018 header: final class RIGHT
 final class RIGHT
 {
     const FOO = 'ChangeMe';
diff --git a/t/t4018/php-final-method b/t/t4018/php-final-method
index 537fb8ad9ae..55107faef98 100644
--- a/t/t4018/php-final-method
+++ b/t/t4018/php-final-method
@@ -1,3 +1,4 @@
+t4018 header: final public function RIGHT(): string
 class Klass
 {
     final public function RIGHT(): string
diff --git a/t/t4018/php-function b/t/t4018/php-function
index 35717c51c3b..f021285e385 100644
--- a/t/t4018/php-function
+++ b/t/t4018/php-function
@@ -1,3 +1,4 @@
+t4018 header: function RIGHT()
 function RIGHT()
 {
     return 'ChangeMe';
diff --git a/t/t4018/php-interface b/t/t4018/php-interface
index 86b49ad5d9e..ef48244eaa8 100644
--- a/t/t4018/php-interface
+++ b/t/t4018/php-interface
@@ -1,3 +1,4 @@
+t4018 header: interface RIGHT
 interface RIGHT
 {
     public function foo($ChangeMe);
diff --git a/t/t4018/php-method b/t/t4018/php-method
index 03af1a6d9d7..cbc171a99b9 100644
--- a/t/t4018/php-method
+++ b/t/t4018/php-method
@@ -1,3 +1,4 @@
+t4018 header: public static function RIGHT()
 class Klass
 {
     public static function RIGHT()
diff --git a/t/t4018/php-trait b/t/t4018/php-trait
index 65b8c82a616..cba65191d39 100644
--- a/t/t4018/php-trait
+++ b/t/t4018/php-trait
@@ -1,3 +1,4 @@
+t4018 header: trait RIGHT
 trait RIGHT
 {
     public function foo($ChangeMe)
diff --git a/t/t4018/python-async-def b/t/t4018/python-async-def
index 87640e03d21..facfaeef373 100644
--- a/t/t4018/python-async-def
+++ b/t/t4018/python-async-def
@@ -1,3 +1,4 @@
+t4018 header: async def RIGHT(pi: int = 3.14):
 async def RIGHT(pi: int = 3.14):
     while True:
         break
diff --git a/t/t4018/python-class b/t/t4018/python-class
index ba9e741430f..87153873b4f 100644
--- a/t/t4018/python-class
+++ b/t/t4018/python-class
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT(int, str):
 class RIGHT(int, str):
     # comment
     # another comment
diff --git a/t/t4018/python-def b/t/t4018/python-def
index e50b31b0ad5..08fb9a6b4ec 100644
--- a/t/t4018/python-def
+++ b/t/t4018/python-def
@@ -1,3 +1,4 @@
+t4018 header: def RIGHT(pi: int = 3.14):
 def RIGHT(pi: int = 3.14):
     while True:
         break
diff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def
index f5d03258af4..f604d08028f 100644
--- a/t/t4018/python-indented-async-def
+++ b/t/t4018/python-indented-async-def
@@ -1,3 +1,4 @@
+t4018 header: async def RIGHT(self, x: int):
 class Foo:
     async def RIGHT(self, x: int):
         return [
diff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class
index 19b4f35c4ca..65c07f74f6f 100644
--- a/t/t4018/python-indented-class
+++ b/t/t4018/python-indented-class
@@ -1,3 +1,4 @@
+t4018 header: class RIGHT:
 if TYPE_CHECKING:
     class RIGHT:
         # comment
diff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def
index 208fbadd2be..be87764e31b 100644
--- a/t/t4018/python-indented-def
+++ b/t/t4018/python-indented-def
@@ -1,3 +1,4 @@
+t4018 header: def RIGHT(self, x: int):
 class Foo:
     def RIGHT(self, x: int):
         return [
diff --git a/t/t4018/rust-fn b/t/t4018/rust-fn
index cbe02155f11..939b131ea80 100644
--- a/t/t4018/rust-fn
+++ b/t/t4018/rust-fn
@@ -1,3 +1,4 @@
+t4018 header: pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
 pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
     let _ = x;
     // a comment
diff --git a/t/t4018/rust-impl b/t/t4018/rust-impl
index 09df3cd93b2..1f798a43d38 100644
--- a/t/t4018/rust-impl
+++ b/t/t4018/rust-impl
@@ -1,3 +1,4 @@
+t4018 header: impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
 impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
 
     pub fn ChangeMe(&self) -> () {
diff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules
index ec610c5b62b..3990daf0e90 100644
--- a/t/t4018/rust-macro-rules
+++ b/t/t4018/rust-macro-rules
@@ -1,3 +1,4 @@
+t4018 header: macro_rules! RIGHT {
 macro_rules! RIGHT {
     () => {
         // a comment
diff --git a/t/t4018/rust-struct b/t/t4018/rust-struct
index 76aff1c0d8e..8c901a437cb 100644
--- a/t/t4018/rust-struct
+++ b/t/t4018/rust-struct
@@ -1,3 +1,4 @@
+t4018 header: pub(super) struct RIGHT<'a> {
 #[derive(Debug)]
 pub(super) struct RIGHT<'a> {
     name: &'a str,
diff --git a/t/t4018/rust-trait b/t/t4018/rust-trait
index ea397f09ed1..4cc9714e8f0 100644
--- a/t/t4018/rust-trait
+++ b/t/t4018/rust-trait
@@ -1,3 +1,4 @@
+t4018 header: unsafe trait RIGHT<T> {
 unsafe trait RIGHT<T> {
     fn len(&self) -> u32;
     fn ChangeMe(&self, n: u32) -> T;
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 16/35] userdiff tests: do config teardown in test_diff_funcname()

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:46

Do a teardown of any custom "diff.<what>.x?funcname" config after a
test_diff_funcname() test runs. Nothing currently uses this, but a
follow-up commit will start setting custom config before certain
tests. Centralizing this teardown makes the tests simpler.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 7fc4291f4be..496313fc900 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -123,6 +123,13 @@ test_diff_funcname () {
 		git diff -U1 "$what" >diff &&
 		last_diff_context_line diff >actual &&
 		test_cmp expected actual
+	' &&
+
+	test_expect_success "teardown: $desc" '
+		# In case any custom config was set immediately before
+		# the test itself in the test file
+		test_unconfig "diff.$what.funcname" &&
+		test_unconfig "diff.$what.xfuncname"
 	'
 }
 
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 19/35] userdiff tests: do not do compile tests on "custom" pattern

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:54:52

Since f1b75fbaf1 (t4018: convert custom pattern test to the new
infrastructure, 2014-03-21) we have been doing the basic sanity check
of whether patterns in userdiff.c compile on the "custom" patterns.

That we were doing this was an emergent effect of that change and an
earlier refactoring in bfa7d01413 (t4018: an infrastructure to test
hunk headers, 2014-03-21).

This was never intended by the test added in
e3bf5e43fd (t4018-diff-funcname: test syntax of builtin xfuncname
patterns, 2008-09-22), nor is there any point in doing this. We'll
error out in the custom.sh test itself if those patterns don't
compile.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 71a7b474cd4..b80546b4d7f 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -20,12 +20,7 @@ test_expect_success 'setup' '
 	echo B >B.java
 '
 
-diffpatterns="
-	$(cat builtin-drivers)
-	custom
-"
-
-for p in $diffpatterns
+for p in $(cat builtin-drivers)
 do
 	test_expect_success "builtin $p pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
@@ -50,6 +45,11 @@ test_expect_success 'last regexp must not be negated' '
 	test_i18ngrep ": Last expression must not be negated:" msg
 '
 
+diffpatterns="
+	$(cat builtin-drivers)
+	custom
+"
+
 test_expect_success 'setup hunk header tests' '
 	for i in $diffpatterns
 	do
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 21/35] userdiff tests + docs: document & test "diff.<driver>.x?funcname"

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:55:01

Add the missing documentation for "diff.<driver>.funcname" and test
for how it and "diff.<driver>.xfuncname" interact.

Between the introduction of the "diff.<driver>.xfuncname" form in
45d9414fa5 (diff.*.xfuncname which uses "extended" regex's for hunk
header selection, 2008-09-18) and when this documentation was written
in 90b94c26f7 (Documentation: Add diff.<driver>.* to config,
2011-04-07) we forgot to document the existence of
"diff.<driver>.funcname".

Let's make a mention of it here, we could also partially revert the
former commit and discuss the more verbose form in gitattributes(5),
but let's stop short of that. It makes sense to guide users towards
ERE over BRE whenever possible.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 Documentation/config/diff.txt | 11 +++++++++++
 t/t4018/custom.sh             | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 2d3331f55c2..6f39ef1da93 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -153,10 +153,21 @@ diff.<driver>.command::
 	The custom diff driver command.  See linkgit:gitattributes[5]
 	for details.
 
+diff.<driver>.funcname::
 diff.<driver>.xfuncname::
 	The regular expression that the diff driver should use to
 	recognize the hunk header.  A built-in pattern may also be used.
 	See linkgit:gitattributes[5] for details.
++
+When provided as `diff.<driver>.funcname` the regular expression is
+interpreted as a basic regular expression. With
+`diff.<driver>.xfuncname` it's interpreted as an extended regular
+expression.
++
+The `*.funcname` and `*.xfuncname` variables behave as if though they
+were one configuration variable for the purposes of what value
+eventually gets used. Setting `*.funcname` will override an earlier
+`*.xfuncname` and vice-versa.
 
 diff.<driver>.binary::
 	Set this option to true to make the diff driver treat files as
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index b208a771d28..72d38dad686 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -77,3 +77,37 @@ public class Beer
 	}
 }
 EOF_TEST
+
+test_expect_success 'custom: setup config precedence' '
+	git config diff.custom.funcname "foo" &&
+	git config diff.custom.xfuncname "bar"
+'
+
+test_diff_funcname 'custom: config precedence' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+bar
+EOF_HUNK
+foo
+bar
+
+ChangeMe
+
+baz
+EOF_TEST
+
+test_expect_success 'custom: setup config precedence' '
+	git config diff.custom.xfuncname "bar" &&
+	git config diff.custom.funcname "foo"
+'
+
+test_diff_funcname 'custom: config precedence' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+foo
+EOF_HUNK
+foo
+bar
+
+ChangeMe
+
+baz
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 25/35] gitattributes doc: document multi-line userdiff patterns

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:55:13

Document the multi-line userdiff patterns and how their matching and
the negation syntax works.

These patterns have been supported since f258475a6e (Per-path
attribute based hunk header selection., 2007-07-06), and have had
their current semantics ever since 3d8dccd74a (diff: fix "multiple
regexp" semantics to find hunk header comment, 2008-09-20).

But we had no documentation for them, let's fix that, and also add
tests showing how some of the things being discussed here work.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 Documentation/gitattributes.txt | 38 ++++++++++++++++--
 t/t4018/custom.sh               | 70 +++++++++++++++++++++++++++++++++
 t/t4018/perl.sh                 | 16 ++++++++
 3 files changed, 120 insertions(+), 4 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 62c1147ba97..8082ff1ee73 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -794,12 +794,42 @@ backslashes; the pattern above picks a line that begins with a
 backslash, and zero or more occurrences of `sub` followed by
 `section` followed by open brace, to the end of line.
 
-There are built-in patterns shipped as part of git itself. A more
-advanced version of the `tex` pattern discussed above is one of them.
+Multiple patterns can be supplied by listing them one per line
+separated by `\n`. They will be matched one line at a time, e.g.:
+
+------------------------
+[diff "perl"]
+	xfuncname = "!^=head\n^[^ ]+.*"
+------------------------
+
+Patterns in a list of multiple patterns that begin with "!" are
+negated. A matching negated pattern will cause the matched line to be
+skipped. Use it to skip a later pattern that would otherwise match. It
+is an error if one or more negated patterns aren't followed by a
+non-negated pattern.
+
+To match a literal "!" at the start of a line, use some other regex
+construct that will match a literal "!" without "!" being the first
+character on that line, such as "[!]".
+
+If the last pattern in a list of multiple patterns ends with "\n" it
+will be interpreted as an empty pattern, and will match the first
+empty line. It's almost always a logic error to provide a list of
+multiple patterns ending with "\n", but it's permitted in case you
+genuinely want to match an empty line.
+
+If the pattern contains a `$1` capture it will be used instead of the
+entire matching line (`$0`) to display the hunk header. This can be
+used e.g. to strip whitespace from the beginning of the line, or to
+only display the function name as part of a longer function
+definition.
+
+There are built-in patterns shipped as part of git itself, see the
+full listing below.
 
 For built-in patterns, you do not need `diff.<lang>.xfuncname` in your
-configuration file as discussed above, but if present, it will
-override a built-in pattern.
+configuration file. If present, it will override a built-in pattern,
+as shown in the `diff.perl.xfuncname` example above.
 
 Nevertheless, you need to enable built-in patterns via .gitattributes`
 for the pattern to take effect.
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 72d38dad686..127524afda3 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -111,3 +111,73 @@ ChangeMe
 
 baz
 EOF_TEST
+
+test_expect_success 'custom: setup negation syntax, ! is magic' '
+	git config diff.custom.xfuncname "!negation
+line"
+'
+
+test_diff_funcname 'custom: negation syntax, ! is magic' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+line
+EOF_HUNK
+line
+!negation
+
+ChangeMe
+
+baz
+EOF_TEST
+
+test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '
+	git config diff.custom.xfuncname "[!]negation
+line"
+'
+
+test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+!negation
+EOF_HUNK
+line
+!negation
+
+ChangeMe
+
+baz
+EOF_TEST
+
+test_expect_success 'custom: setup captures in multiple patterns' '
+	git config diff.custom.xfuncname "!^=head
+^format ([^ ]+)
+^sub ([^;]+)"
+'
+
+test_diff_funcname 'custom: captures in multiple patterns' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+foo
+EOF_HUNK
+sub foo;
+
+=head1
+
+ChangeMe
+
+EOF_TEST
+
+test_expect_success 'custom: multiple patterns ending with \n' '
+	git config diff.custom.xfuncname "!^=head
+^sub ([^;]+)
+"
+'
+
+test_diff_funcname 'custom: multiple patterns ending with \n' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+
+EOF_HUNK
+sub foo;
+
+=head1
+
+ChangeMe
+
+EOF_TEST
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
index b53b759353b..ba11241750b 100755
--- a/t/t4018/perl.sh
+++ b/t/t4018/perl.sh
@@ -76,3 +76,19 @@ sub asub
 	print "ChangeMe\n";
 }
 EOF_TEST
+
+
+test_expect_success 'custom: setup config overrides built-in patterns' '
+	git config diff.perl.xfuncname "!^=head
+^[^ ]+.*"
+'
+
+test_diff_funcname 'custom: config overrides built-in patterns' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+sub foo;
+EOF_HUNK
+sub foo;
+=head1
+
+ChangeMe
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 23/35] userdiff tests: move perl tests to perl.sh

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:55:25

Move the perl tests to perl.sh, a follow-up change will piggy-back on
these tests for updating the userdiff documentation. This will require
the new test *.sh test framework.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/perl-skip-end-of-heredoc     |  9 ----
 t/t4018/perl-skip-forward-decl       | 11 ----
 t/t4018/perl-skip-sub-in-pod         | 19 -------
 t/t4018/perl-sub-definition          |  5 --
 t/t4018/perl-sub-definition-kr-brace |  5 --
 t/t4018/perl.sh                      | 78 ++++++++++++++++++++++++++++
 6 files changed, 78 insertions(+), 49 deletions(-)
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc
 delete mode 100644 t/t4018/perl-skip-forward-decl
 delete mode 100644 t/t4018/perl-skip-sub-in-pod
 delete mode 100644 t/t4018/perl-sub-definition
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace
 create mode 100755 t/t4018/perl.sh
diff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc
deleted file mode 100644
index 8f90cca7314..00000000000
--- a/t/t4018/perl-skip-end-of-heredoc
+++ /dev/null
@@ -1,9 +0,0 @@
-t4018 header: sub RIGHTwithheredocument {
-sub RIGHTwithheredocument {
-	print <<"EOF"
-decoy here-doc
-EOF
-	# some lines of context
-	# to pad it out
-	print "ChangeMe\n";
-}
diff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl
deleted file mode 100644
index ff1f6d14735..00000000000
--- a/t/t4018/perl-skip-forward-decl
+++ /dev/null
@@ -1,11 +0,0 @@
-t4018 header: package RIGHT;
-package RIGHT;
-
-use strict;
-use warnings;
-use parent qw(Exporter);
-our @EXPORT_OK = qw(round finalround);
-
-sub other; # forward declaration
-
-# ChangeMe
diff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod
deleted file mode 100644
index ff1c65b28fc..00000000000
--- a/t/t4018/perl-skip-sub-in-pod
+++ /dev/null
@@ -1,19 +0,0 @@
-t4018 header: =head1 SYNOPSIS_RIGHT
-=head1 NAME
-
-Beer - subroutine to output fragment of a drinking song
-
-=head1 SYNOPSIS_RIGHT
-
-	use Beer qw(round finalround);
-
-	sub song {
-		for (my $i = 99; $i > 0; $i--) {
-			round $i;
-		}
-		finalround;
-	}
-
-	ChangeMe;
-
-=cut
diff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition
deleted file mode 100644
index 22e16ad5363..00000000000
--- a/t/t4018/perl-sub-definition
+++ /dev/null
@@ -1,5 +0,0 @@
-t4018 header: sub RIGHT {
-sub RIGHT {
-	my ($n) = @_;
-	print "ChangeMe";
-}
diff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace
deleted file mode 100644
index 6c94e6a62dd..00000000000
--- a/t/t4018/perl-sub-definition-kr-brace
+++ /dev/null
@@ -1,5 +0,0 @@
-t4018 header: sub RIGHT
-sub RIGHT
-{
-	print "ChangeMe\n";
-}
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
new file mode 100755
index 00000000000..ac8fff7417a
--- /dev/null
+++ b/t/t4018/perl.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'perl: skip end of heredoc' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+sub RIGHTwithheredocument {
+EOF_HUNK
+sub RIGHTwithheredocument {
+	print <<"EOF"
+decoy here-doc
+EOF
+	# some lines of context
+	# to pad it out
+	print "ChangeMe\n";
+}
+EOF_TEST
+
+test_diff_funcname 'perl: skip forward decl' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+package RIGHT;
+EOF_HUNK
+package RIGHT;
+
+use strict;
+use warnings;
+use parent qw(Exporter);
+our @EXPORT_OK = qw(round finalround);
+
+sub other; # forward declaration
+
+# ChangeMe
+EOF_TEST
+
+test_diff_funcname 'perl: skip sub in pod' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+=head1 SYNOPSIS_RIGHT
+EOF_HUNK
+=head1 NAME
+
+Beer - subroutine to output fragment of a drinking song
+
+=head1 SYNOPSIS_RIGHT
+
+	use Beer qw(round finalround);
+
+	sub song {
+		for (my $i = 99; $i > 0; $i--) {
+			round $i;
+		}
+		finalround;
+	}
+
+	ChangeMe;
+
+=cut
+EOF_TEST
+
+test_diff_funcname 'perl: sub definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+sub RIGHT {
+EOF_HUNK
+sub RIGHT {
+	my ($n) = @_;
+	print "ChangeMe";
+}
+EOF_TEST
+
+test_diff_funcname 'perl: sub definition kr brace' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+sub RIGHT
+EOF_HUNK
+sub RIGHT
+{
+	print "ChangeMe\n";
+}
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 27/35] userdiff tests: remove "funcname" from custom3 test

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:55:45

We can only have one "funcname" or "xfuncname", any later definition
overrides the earlier one, so this configuration wasn't doing
anything.

When this test was originally added in 3632cfc248 (Use compatibility
regex library for OSX/Darwin, 2008-09-07) we had no such definition of
two patters for this test. Back then this was setting the
"diff.java.funcname" configuration variable.

The stage for that second pattern being set got set later. In
45d9414fa5 (diff.*.xfuncname which uses "extended" regex's for hunk
header selection, 2008-09-18) the pattern got converted from
"funcname" to "xfuncname".

Soon after in b19d288b4d (t4018-diff-funcname: demonstrate end of line
funcname matching flaw, 2008-10-15) another test immediately preceding
this one got added, using "diff.java.funcname" for its configuration.

Then f792a0b88e (t4018 (funcname patterns): make configuration easier
to track, 2011-05-21) came along and codified this whole thing when
converting the two tests from "git config" to "test_config".

Since this was never the intent of the test let's just remove this,
the rationale in f792a0b88e for having some test for the clobbering
behavior makes sense, but I'll do that in another follow-up test, not
as a hard to read side-effect of this one.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/custom.sh | 1 -
 1 file changed, 1 deletion(-)
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 97f310c02fb..58187c2293b 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -52,7 +52,6 @@ public class Beer
 EOF_TEST
 
 test_expect_success 'custom: setup alternation in pattern' '
-	git config diff.custom.funcname "Beer$" &&
 	git config diff.custom.xfuncname "^[ 	]*((public|static).*)$"
 '
 
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 13/35] userdiff tests: factor out test_diff_funcname() logic

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:55:56

Factor out logic in test_diff_funcname() into two helper functions,
these will be useful in a follow-up commit where we'll do this munging
in more than one place.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 2365f0e361e..8a8a7a99c88 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -75,6 +75,17 @@ test_expect_success 'setup hunk header tests' '
 	git -C t4018 add .
 '
 
+do_change_me () {
+	file=$1
+	sed -e "s/ChangeMe/IWasChanged/" <"$file" >tmp &&
+	mv tmp "$file"
+}
+
+last_diff_context_line () {
+	file=$1
+	sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <$file
+}
+
 # check each individual file
 for i in $(git -C t4018 ls-files)
 do
@@ -85,13 +96,12 @@ do
 
 		# add test file to the index
 		git add \"$i\" &&
-		# place modified file in the worktree
-		sed -e 's/ChangeMe/IWasChanged/' <\"t4018/$i.content\" >\"$i\"
+		do_change_me \"$i\"
 	"
 
 	test_expect_success "hunk header: $i" "
 		git diff -U1 $i >diff &&
-		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
+		last_diff_context_line diff >ctx &&
 		test_cmp t4018/$i.header ctx
 	"
 done
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 14/35] userdiff tests: add alternative hunk header test infrastructure

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:56:04

Add an alternative to the hunk header test infrastructure introduced
in bfa7d01413 (t4018: an infrastructure to test hunk headers,
2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',
2014-03-31) for the whole series that commit was part of.

As noted in an earlier commit that change introduced the regression of
not testing for the full hunk line, but just whether "RIGHT" appeared
on it.

A preceding commit fixed that specific issue, but we are still left
with the inflexibility of not being able to do any testing except that
which fits inside the narrow confines of this custom test syntax,
e.g. being unable to setup config variables before specific tests.

So introduce a new "test_diff_funcname()" function which is expected
to be used by tests in t4018/*.sh, and move the "custom" tests over to
that. As will be seen in follow-up changes we'll make use of this new
function infrastructure in the "custom" tests.

There's no reason for not moving all of the tests over to this new
infrastructure as far as test coverage goes, but Junio and Johannes
expressed a desire to keep the existing test mode [1], so I'm only
moving a narrow set of tests over to the new mode.

1. https://lore.kernel.org/git/xmqqsg5vrhha.fsf@gitster.c.googlers.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh                      | 38 +++++++++++++++-
 t/t4018/README                                | 44 ++++++++++++++++---
 t/t4018/{custom1-pattern => custom1.sh}       | 11 ++++-
 t/t4018/custom2-match-to-end-of-line          |  9 ----
 t/t4018/custom2.sh                            | 18 ++++++++
 ...tom3-alternation-in-pattern => custom3.sh} | 11 ++++-
 6 files changed, 113 insertions(+), 18 deletions(-)
 rename t/t4018/{custom1-pattern => custom1.sh} (71%)
 mode change 100644 => 100755
 delete mode 100644 t/t4018/custom2-match-to-end-of-line
 create mode 100755 t/t4018/custom2.sh
 rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)
 mode change 100644 => 100755
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 8a8a7a99c88..6fd3dce1364 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -87,7 +87,7 @@ last_diff_context_line () {
 }
 
 # check each individual file
-for i in $(git -C t4018 ls-files)
+for i in $(git -C t4018 ls-files -- ':!*.sh')
 do
 	test_expect_success "setup hunk header: $i" "
 		grep -v '^t4018' \"t4018/$i\" >\"t4018/$i.content\" &&
@@ -106,4 +106,40 @@ do
 	"
 done
 
+test_diff_funcname () {
+	desc=$1
+	cat <&8 >arg.header &&
+	cat <&9 >arg.test &&
+	what=$(cat arg.what) &&
+
+	test_expect_success "setup: $desc" '
+		cp arg.test "$what" &&
+		cp arg.header expected &&
+		git add "$what" &&
+		do_change_me "$what"
+	' &&
+
+	test_expect_success "$desc" '
+		git diff -U1 "$what" >diff &&
+		last_diff_context_line diff >actual &&
+		test_cmp expected actual
+	'
+}
+
+for what in $diffpatterns
+do
+	test="$TEST_DIRECTORY/t4018/$what.sh"
+	if ! test -e "$test"
+	then
+		continue
+	fi &&
+
+	test_expect_success "setup: hunk header for $what" '
+		echo "$what diff=$what" >.gitattributes &&
+		echo "$what" >arg.what
+	' &&
+
+	. "$test"
+done
+
 test_done
diff --git a/t/t4018/README b/t/t4018/README
index 0a246bbc10e..54ae735d5f8 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -1,13 +1,45 @@
-t4018 header: description of the test.
+t4018 header: There are two ways of writing tests in this directory. In both cases
 How to write test cases
 =======================
 
-Create test cases called "LANG-whatever" in this directory, where
-"LANG" is e.g. the userdiff driver name, where "whatever" is a brief
-description of the test.
+There are two ways of writing tests in this directory. In both cases
+"LANG" is the userdiff driver name, e.g. "perl" or "cpp".
+
+The word "ChangeMe" (exactly this form) should appear at a distance of
+at least two lines from the line that must appear in the hunk
+header. See below sections.
+
+t4018 header: t/README.
+"LANG.sh" test cases
+====================
+
+These tests use the normal test-lib.sh syntax and environment, and are
+sourced by t4018-diff-funcname.sh. The "test_diff_funcname()" function
+is a thin wrapper around the "test_expect_success()" function. See
+t/README.
+
+The content of the "EOF_TEST" argument is used as-is, with the
+exception of the "ChangeMe" token discussed above.
+
+The advantage of using this over the "LANG-whatever" test cases (see
+below) are:
 
-Insert the word "ChangeMe" (exactly this form) at a distance of
-at least two lines from the line that must appear in the hunk header.
+ - The ability to do custom test setup/teardown, e.g. using
+   "test_config" before the test is run.
+
+ - The description of the test is a string, and doesn't need to be a
+   valid filename.
+
+ - All the tests for a given driver are present in one file. As
+   demonstrated with the two "t4018 header" lines in this file this is
+   also possible with the "LANG-whatever" tests, but those N tests in
+   one file won't benefit from different test descriptions.
+
+"LANG-whatever" test cases
+==========================
+
+Create test cases called "LANG-whatever" in this directory, where
+"whatever" is a brief description of the test.
 
 Any line starting with "t4018" is a control line for the test:
 
diff --git a/t/t4018/custom1-pattern b/t/t4018/custom1.sh
old mode 100644
new mode 100755
similarity index 71%
rename from t/t4018/custom1-pattern
rename to t/t4018/custom1.sh
index 37a3422384b..f8bbccadb47
--- a/t/t4018/custom1-pattern
+++ b/t/t4018/custom1.sh
@@ -1,4 +1,12 @@
-t4018 header: int special, RIGHT;
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'custom1: pattern' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+int special, RIGHT;
+EOF_HUNK
 public class Beer
 {
 	int special, RIGHT;
@@ -16,3 +24,4 @@ public class Beer
 			+ "99 bottles of beer on the wall.\n");
 	}
 }
+EOF_TEST
diff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line
deleted file mode 100644
index 4800bb1c568..00000000000
--- a/t/t4018/custom2-match-to-end-of-line
+++ /dev/null
@@ -1,9 +0,0 @@
-t4018 header: RIGHT_Beer
-public class RIGHT_Beer
-{
-	int special;
-	public static void main(String args[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
diff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh
new file mode 100755
index 00000000000..c68421f788e
--- /dev/null
+++ b/t/t4018/custom2.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'custom2: match to end of line' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT_Beer
+EOF_HUNK
+public class RIGHT_Beer
+{
+	int special;
+	public static void main(String args[])
+	{
+		System.out.print("ChangeMe");
+	}
+}
+EOF_TEST
diff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3.sh
old mode 100644
new mode 100755
similarity index 66%
rename from t/t4018/custom3-alternation-in-pattern
rename to t/t4018/custom3.sh
index bf7df3d9a73..07c5c134ffe
--- a/t/t4018/custom3-alternation-in-pattern
+++ b/t/t4018/custom3.sh
@@ -1,4 +1,12 @@
-t4018 header: public static void main(String RIGHT[])
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'custom3: alternation in pattern' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+public static void main(String RIGHT[])
+EOF_HUNK
 public class Beer
 {
 	int special;
@@ -16,3 +24,4 @@ public class Beer
 			+ "99 bottles of beer on the wall.\n");
 	}
 }
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 29/35] userdiff tests: test for a bug in 1dbf0c0ad6c

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:56:40

Add a test for a bug in 1dbf0c0ad6c (userdiff: add built-in pattern
for golang, 2018-03-01), we'd ignore everything after the "{" only for
"type" lines, but not "func".

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
diff --git a/t/t4018/golang b/t/t4018/golang
index 000e66b1c7b..70bf0d936bb 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -16,6 +16,13 @@ func RIGHT() {
 	b := ChangeMe
 }
 
+t4018 description: func with comment after {
+t4018 header: func name() { // comment
+func name() { // comment
+	a := 5
+	b := ChangeMe
+}
+
 t4018 description: interface
 t4018 header: type RIGHT interface {
 type RIGHT interface {
@@ -37,3 +44,10 @@ type RIGHT struct {
 	a Type
 	b ChangeMe
 }
+
+t4018 description: struct with comment after {
+t4018 header: type some struct {
+type some struct { // comment
+	a Type
+	b ChangeMe
+}
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 17/35] userdiff tests: move custom patterns into one test file

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:56:51

In a preceding commit the test infrastructure got rewritten so
"t/t4018/" are now normal test files which can do things like set
config, so let's make it responsible for setting up and tearing down
the config for its tests.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 16 +-------
 t/t4018/custom.sh        | 79 ++++++++++++++++++++++++++++++++++++++++
 t/t4018/custom1.sh       | 27 --------------
 t/t4018/custom2.sh       | 18 ---------
 t/t4018/custom3.sh       | 27 --------------
 5 files changed, 80 insertions(+), 87 deletions(-)
 create mode 100755 t/t4018/custom.sh
 delete mode 100755 t/t4018/custom1.sh
 delete mode 100755 t/t4018/custom2.sh
 delete mode 100755 t/t4018/custom3.sh
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 496313fc900..71a7b474cd4 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -15,18 +15,6 @@ test_expect_success 'setup' '
 	sort <builtin-drivers >builtin-drivers.sorted &&
 	test_cmp builtin-drivers.sorted builtin-drivers &&
 
-	# a non-trivial custom pattern
-	git config diff.custom1.funcname "!static
-!String
-[^ 	].*s.*" &&
-
-	# a custom pattern which matches to end of line
-	git config diff.custom2.funcname "......Beer\$" &&
-
-	# alternation in pattern
-	git config diff.custom3.funcname "Beer$" &&
-	git config diff.custom3.xfuncname "^[ 	]*((public|static).*)$" &&
-
 	# for regexp compilation tests
 	echo A >A.java &&
 	echo B >B.java
@@ -34,9 +22,7 @@ test_expect_success 'setup' '
 
 diffpatterns="
 	$(cat builtin-drivers)
-	custom1
-	custom2
-	custom3
+	custom
 "
 
 for p in $diffpatterns
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
new file mode 100755
index 00000000000..59d855c01c5
--- /dev/null
+++ b/t/t4018/custom.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_expect_success 'custom: setup non-trivial custom' '
+	git config diff.custom.funcname "!static
+!String
+[^ 	].*s.*"
+'
+
+test_diff_funcname 'custom: non-trivial custom pattern' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+int special, RIGHT;
+EOF_HUNK
+public class Beer
+{
+	int special, RIGHT;
+	public static void main(String args[])
+	{
+		String s=" ";
+		for(int x = 99; x > 0; x--)
+		{
+			System.out.print(x + " bottles of beer on the wall "
+				+ x + " bottles of beer\n" // ChangeMe
+				+ "Take one down, pass it around, " + (x - 1)
+				+ " bottles of beer on the wall.\n");
+		}
+		System.out.print("Go to the store, buy some more,\n"
+			+ "99 bottles of beer on the wall.\n");
+	}
+}
+EOF_TEST
+
+test_expect_success 'custom: setup match to end of line' '
+	git config diff.custom.funcname "......Beer\$"
+'
+
+test_diff_funcname 'custom: match to end of line' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT_Beer
+EOF_HUNK
+public class RIGHT_Beer
+{
+	int special;
+	public static void main(String args[])
+	{
+		System.out.print("ChangeMe");
+	}
+}
+EOF_TEST
+
+test_expect_success 'custom: setup alternation in pattern' '
+	git config diff.custom.funcname "Beer$" &&
+	git config diff.custom.xfuncname "^[ 	]*((public|static).*)$"
+'
+
+test_diff_funcname 'custom: alternation in pattern' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+public static void main(String RIGHT[])
+EOF_HUNK
+public class Beer
+{
+	int special;
+	public static void main(String RIGHT[])
+	{
+		String s=" ";
+		for(int x = 99; x > 0; x--)
+		{
+			System.out.print(x + " bottles of beer on the wall "
+				+ x + " bottles of beer\n" // ChangeMe
+				+ "Take one down, pass it around, " + (x - 1)
+				+ " bottles of beer on the wall.\n");
+		}
+		System.out.print("Go to the store, buy some more,\n"
+			+ "99 bottles of beer on the wall.\n");
+	}
+}
+EOF_TEST
diff --git a/t/t4018/custom1.sh b/t/t4018/custom1.sh
deleted file mode 100755
index f8bbccadb47..00000000000
--- a/t/t4018/custom1.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-#
-# See ../t4018-diff-funcname.sh's test_diff_funcname()
-#
-
-test_diff_funcname 'custom1: pattern' \
-	8<<\EOF_HUNK 9<<\EOF_TEST
-int special, RIGHT;
-EOF_HUNK
-public class Beer
-{
-	int special, RIGHT;
-	public static void main(String args[])
-	{
-		String s=" ";
-		for(int x = 99; x > 0; x--)
-		{
-			System.out.print(x + " bottles of beer on the wall "
-				+ x + " bottles of beer\n" // ChangeMe
-				+ "Take one down, pass it around, " + (x - 1)
-				+ " bottles of beer on the wall.\n");
-		}
-		System.out.print("Go to the store, buy some more,\n"
-			+ "99 bottles of beer on the wall.\n");
-	}
-}
-EOF_TEST
diff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh
deleted file mode 100755
index c68421f788e..00000000000
--- a/t/t4018/custom2.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-#
-# See ../t4018-diff-funcname.sh's test_diff_funcname()
-#
-
-test_diff_funcname 'custom2: match to end of line' \
-	8<<\EOF_HUNK 9<<\EOF_TEST
-RIGHT_Beer
-EOF_HUNK
-public class RIGHT_Beer
-{
-	int special;
-	public static void main(String args[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
-EOF_TEST
diff --git a/t/t4018/custom3.sh b/t/t4018/custom3.sh
deleted file mode 100755
index 07c5c134ffe..00000000000
--- a/t/t4018/custom3.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-#
-# See ../t4018-diff-funcname.sh's test_diff_funcname()
-#
-
-test_diff_funcname 'custom3: alternation in pattern' \
-	8<<\EOF_HUNK 9<<\EOF_TEST
-public static void main(String RIGHT[])
-EOF_HUNK
-public class Beer
-{
-	int special;
-	public static void main(String RIGHT[])
-	{
-		String s=" ";
-		for(int x = 99; x > 0; x--)
-		{
-			System.out.print(x + " bottles of beer on the wall "
-				+ x + " bottles of beer\n" // ChangeMe
-				+ "Take one down, pass it around, " + (x - 1)
-				+ " bottles of beer on the wall.\n");
-		}
-		System.out.print("Go to the store, buy some more,\n"
-			+ "99 bottles of beer on the wall.\n");
-	}
-}
-EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 18/35] userdiff tests: remove hack for "RIGHT" token

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:57:01

Now that the "RIGHT" token isn't how we select the desired hunk header
line in the test anymore we can revert a hack added in
f1b75fbaf1 (t4018: convert custom pattern test to the new
infrastructure, 2014-03-21) and go back to the regular expression we
were testing before that change.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/custom.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 59d855c01c5..b208a771d28 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -33,14 +33,14 @@ public class Beer
 EOF_TEST
 
 test_expect_success 'custom: setup match to end of line' '
-	git config diff.custom.funcname "......Beer\$"
+	git config diff.custom.funcname "Beer\$"
 '
 
 test_diff_funcname 'custom: match to end of line' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-RIGHT_Beer
+Beer
 EOF_HUNK
-public class RIGHT_Beer
+public class Beer
 {
 	int special;
 	public static void main(String args[])
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 22/35] gitattributes doc: reword discussion of built-in userdiff patterns

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:57:16

Reword the discussion of the built-in userdiff patterns to make it
more natural to precede it with a discussion about the semantics of
pattern matching, instead of assuming that it follows right after the
"diff.tex.xfuncname" example which now immediately precedes it. This
will make a follow-up commit smaller.

Helped-by: Eric Sunshine [off-list ref]
Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 Documentation/gitattributes.txt | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index e84e104f932..62c1147ba97 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a
 backslash, and zero or more occurrences of `sub` followed by
 `section` followed by open brace, to the end of line.
 
-There are a few built-in patterns to make this easier, and `tex`
-is one of them, so you do not have to write the above in your
-configuration file (you still need to enable this with the
-attribute mechanism, via `.gitattributes`).  The following built in
-patterns are available:
+There are built-in patterns shipped as part of git itself. A more
+advanced version of the `tex` pattern discussed above is one of them.
+
+For built-in patterns, you do not need `diff.<lang>.xfuncname` in your
+configuration file as discussed above, but if present, it will
+override a built-in pattern.
+
+Nevertheless, you need to enable built-in patterns via .gitattributes`
+for the pattern to take effect.
+
+The following built-in patterns are available:
 
 - `ada` suitable for source code in the Ada language.
 
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:57:24

Add an assertion to the userdiff test framework to check that
everything except a narrow whitelist of existing built-in patterns has
tests.

Since this test framework was added we've added new patterns without
any tests. Let's make it obvious in the future in the diff for such
patches that they should have those tests.

For anything with tests we can skip the "does the pattern compile?"
test, as the actual tests will check that for us.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b80546b4d7f..a3058fda130 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -15,6 +15,19 @@ test_expect_success 'setup' '
 	sort <builtin-drivers >builtin-drivers.sorted &&
 	test_cmp builtin-drivers.sorted builtin-drivers &&
 
+	# Do not add anything to this list. New built-in drivers should have
+	# tests
+	cat >drivers-no-tests <<-\EOF &&
+	ada
+	bibtex
+	csharp
+	html
+	objc
+	pascal
+	ruby
+	tex
+	EOF
+
 	# for regexp compilation tests
 	echo A >A.java &&
 	echo B >B.java
@@ -22,7 +35,12 @@ test_expect_success 'setup' '
 
 for p in $(cat builtin-drivers)
 do
-	test_expect_success "builtin $p pattern compiles" '
+	P=$(echo $p | tr 'a-z' 'A-Z')
+	if grep -q $p drivers-no-tests
+	then
+		test_set_prereq NO_TEST_FOR_DRIVER_$P
+	fi
+	test_expect_success NO_TEST_FOR_DRIVER_$P "builtin $p pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index \
 			A.java B.java 2>msg &&
@@ -119,11 +137,17 @@ test_diff_funcname () {
 	'
 }
 
+>drivers-had-no-tests
 for what in $diffpatterns
 do
 	test="$TEST_DIRECTORY/t4018/$what.sh"
 	if ! test -e "$test"
 	then
+		git -C t4018 ls-files ':!*.sh' "$what*" >other-tests &&
+		if ! test -s other-tests
+		then
+			echo $what >>drivers-had-no-tests
+		fi
 		continue
 	fi &&
 
@@ -135,4 +159,8 @@ do
 	. "$test"
 done
 
+test_expect_success 'we should not have new built-in drivers without tests' '
+	test_cmp drivers-no-tests drivers-had-no-tests
+'
+
 test_done
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 26/35] userdiff tests: switch to -U0 by default

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:57:46

Change the -U1 default in the userdiff tests to -U0. There's no reason
to use -U1, it just causes confusion and the need to work around the
test semantics themselves by not having the matching line immediately
precede the changed line.

The -U1 was initially added in f12c66b9bb8 (userdiff/perl: anchor
"sub" and "package" patterns on the left, 2011-05-21), seemingly to
appease a specific test I'd reported as failing with the "perl"
pattern. This was then documented in bfa7d01413b (t4018: an
infrastructure to test hunk headers, 2014-03-21).

There's no special logic in xdiff/xemit.c that would depend on -U1
v.s. -U0 here. So let's use -U0 for simplicity, we're interested in
what lines match most of the time, not at which line we start the
search.

A couple of test cases depended on the -U1, most confusingly one of
the custom.sh test cases.

Let's extend "test_diff_funcname()" to take an argument to "git diff"
and test "-U1" there, both because rewriting the testcase would be
painful to understand in the context of reviewing this change, and so
that we explicitly test that we're using -U0.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 5 +++--
 t/t4018/README           | 7 +++----
 t/t4018/custom.sh        | 1 +
 t/t4018/perl.sh          | 1 -
 4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index a3058fda130..ca23d156666 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -104,7 +104,7 @@ do
 	"
 
 	test_expect_success "hunk header: $i" "
-		git diff -U1 $i >diff &&
+		git diff -U0 $i >diff &&
 		last_diff_context_line diff >ctx &&
 		test_cmp t4018/$i.header ctx
 	"
@@ -112,6 +112,7 @@ done
 
 test_diff_funcname () {
 	desc=$1
+	diff_opts=${2:--U0} &&
 	cat <&8 >arg.header &&
 	cat <&9 >arg.test &&
 	what=$(cat arg.what) &&
@@ -124,7 +125,7 @@ test_diff_funcname () {
 	' &&
 
 	test_expect_success "$desc" '
-		git diff -U1 "$what" >diff &&
+		git diff $diff_opts "$what" >diff &&
 		last_diff_context_line diff >actual &&
 		test_cmp expected actual
 	' &&
diff --git a/t/t4018/README b/t/t4018/README
index a3220dd6374..cef7d3c0e17 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -5,11 +5,10 @@ How to write test cases
 There are two ways of writing tests in this directory. In both cases
 "LANG" is the userdiff driver name, e.g. "perl" or "cpp".
 
-The word "ChangeMe" (exactly this form) should appear at a distance of
-at least two lines from the line that must appear in the hunk
-header. See below sections.
+The word "ChangeMe" below the line that must appear in the hunk header
+(we run the diff with -U0). See below sections.
 
-t4018 header: t/README.
+t4018 header: The content of the "EOF_TEST" argument is used as-is, with the
 "LANG.sh" test cases
 ====================
 
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 127524afda3..97f310c02fb 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -10,6 +10,7 @@ test_expect_success 'custom: setup non-trivial custom' '
 '
 
 test_diff_funcname 'custom: non-trivial custom pattern' \
+	'-U1' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
 int special, RIGHT;
 EOF_HUNK
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
index ba11241750b..63f373d1a43 100755
--- a/t/t4018/perl.sh
+++ b/t/t4018/perl.sh
@@ -89,6 +89,5 @@ sub foo;
 EOF_HUNK
 sub foo;
 =head1
-
 ChangeMe
 EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 24/35] userdiff tests: move away from "RIGHT" in perl.sh

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:57:46

The "RIGHT" token is no longer magical, see recent changes to
t/t4018/README. Let's change it in the recently moved perl.sh
tests. This change was done separately so the earlier commit could
benefit from the "diff --color-moved" detection.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/perl.sh | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
index ac8fff7417a..b53b759353b 100755
--- a/t/t4018/perl.sh
+++ b/t/t4018/perl.sh
@@ -5,9 +5,9 @@
 
 test_diff_funcname 'perl: skip end of heredoc' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-sub RIGHTwithheredocument {
+sub withheredocument {
 EOF_HUNK
-sub RIGHTwithheredocument {
+sub withheredocument {
 	print <<"EOF"
 decoy here-doc
 EOF
@@ -19,9 +19,9 @@ EOF_TEST
 
 test_diff_funcname 'perl: skip forward decl' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-package RIGHT;
+package Some::Package;
 EOF_HUNK
-package RIGHT;
+package Some::Package;
 
 use strict;
 use warnings;
@@ -35,13 +35,13 @@ EOF_TEST
 
 test_diff_funcname 'perl: skip sub in pod' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-=head1 SYNOPSIS_RIGHT
+=head1 SYNOPSIS
 EOF_HUNK
 =head1 NAME
 
 Beer - subroutine to output fragment of a drinking song
 
-=head1 SYNOPSIS_RIGHT
+=head1 SYNOPSIS
 
 	use Beer qw(round finalround);
 
@@ -59,9 +59,9 @@ EOF_TEST
 
 test_diff_funcname 'perl: sub definition' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-sub RIGHT {
+sub asub {
 EOF_HUNK
-sub RIGHT {
+sub asub {
 	my ($n) = @_;
 	print "ChangeMe";
 }
@@ -69,9 +69,9 @@ EOF_TEST
 
 test_diff_funcname 'perl: sub definition kr brace' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
-sub RIGHT
+sub asub
 EOF_HUNK
-sub RIGHT
+sub asub
 {
 	print "ChangeMe\n";
 }
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 28/35] userdiff tests: assert empty hunk header context on -U<large>

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:58:26

Assert the existing behavior that under -U<large> we'll show no hunk
header context, where <large> takes us past the potential hunk header
we'd have extracted.

I'm just picking a number over nine thousand as a really large number
we're unlikely to exceed in these tests.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index ca23d156666..ba10d1f5313 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -130,6 +130,13 @@ test_diff_funcname () {
 		test_cmp expected actual
 	' &&
 
+	test_expect_success "$desc -U9001" '
+		git diff -U9001 "$what" >diff &&
+		last_diff_context_line diff >actual &&
+		echo >blank &&
+		test_cmp blank actual
+	' &&
+
 	test_expect_success "teardown: $desc" '
 		# In case any custom config was set immediately before
 		# the test itself in the test file
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 31/35] userdiff golang: don't over-match intented constructs

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:58:46

Fix a bug introduced when the "golang" driver was added in
1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01).

Unlike the default def_ff() driver in xemit.c it would match "type"
declarations inside functions. Let's make it mandatory that a "func"
or "type" must be at the beginning of the line with no whitespace to
get around this.

Go is such a regularly formatted language that I think this can be
counted on. I think the whitespace matching was probably copy/pasted
from an earlier userdiff.c pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 20 ++++++++++++++++++++
 userdiff.c     |  4 ++--
 2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/t/t4018/golang b/t/t4018/golang
index 72a35d66008..252b6049da4 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -51,3 +51,23 @@ type some struct { // comment
 	a Type
 	b ChangeMe
 }
+
+t4018 description: func combined with type
+t4018 header: func myfunc() {
+func myfunc() {
+	type mystruct struct {
+		a Foo
+		b Bar
+	}
+	ChangeMe
+
+t4018 description: anonymous indented func()
+t4018 header: func SomeThing() bool {
+func SomeThing() bool {
+	func() {
+		defer func() {
+			fmt.Println("hello")
+		}()
+	}()
+
+	ChangeMe
diff --git a/userdiff.c b/userdiff.c
index 698eca5ad35..704af241e44 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -129,9 +129,9 @@ IPATTERN("fountain",
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
-	 "^[ \t]*(func[ \t].*)\n"
+	 "^(func[ \t].*)\n"
 	 /* Structs and interfaces */
-	 "^[ \t]*(type[ \t].*(struct|interface)[ \t].*)",
+	 "^(type[ \t].*[ \t](struct|interface)[ \t].*)",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 33/35] userdiff golang: match multi-line "const" and "import"

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:58:57

Add matching rules for the very common pattern of having a multi-line
"import" or "const" declaration near the start of the "package"
line. Before this change we'd skip this and match whatever came before
it, e.g. the "package" line.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 26 ++++++++++++++++++++++++++
 userdiff.c     |  2 ++
 2 files changed, 28 insertions(+)
diff --git a/t/t4018/golang b/t/t4018/golang
index 38f254cd269..7f51fa02203 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -47,6 +47,32 @@ package camelCase
 import "fmt"
 // ChangeMe
 
+t4018 description: import (
+t4018 header: import (
+package somePackage
+
+import (
+	"os"
+	ChangeMe
+)
+
+t4018 description: const (
+t4018 header: const (
+package somePackage
+
+const (
+	Foo = 1
+	Bar = ChangeMe
+)
+
+t4018 description: const rule is selective
+t4018 header: package main
+package main
+
+const Foo = "Bar"
+
+// ChangeMe
+
 t4018 description: complex function
 t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
 type Test struct {
diff --git a/userdiff.c b/userdiff.c
index bbbbfa33e0a..c4a2bfaed70 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -132,6 +132,8 @@ PATTERNS("golang",
 	 "^(package[ \t][a-z][A-Za-z0-9_]+)[ \t]*\n"
 	 /* Functions */
 	 "^(func[ \t].*)\n"
+	 /* const & import */
+	 "^((import|const)[ \t]*\\([ \t]*)\n"
 	 /* Structs and interfaces */
 	 "^(type[ \t].*[ \t](struct|interface)[ \t].*)",
 	 /* -- */
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 34/35] userdiff tests: add basic test for ada

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:58:57

Add test for the ada userdiff pattern added in e90d065e64 (Add
userdiff patterns for Ada, 2012-09-16).

I don't know the ada language itself, I just stole a couple of
examples of code that used tokens we're matching[1][2]. Both test
examples stress our negative and positive matching rules.

1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada
2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking
---
 t/t4018-diff-funcname.sh |  1 -
 t/t4018/ada.sh           | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100755 t/t4018/ada.sh
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index ba10d1f5313..b0c2782d067 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -18,7 +18,6 @@ test_expect_success 'setup' '
 	# Do not add anything to this list. New built-in drivers should have
 	# tests
 	cat >drivers-no-tests <<-\EOF &&
-	ada
 	bibtex
 	csharp
 	html
diff --git a/t/t4018/ada.sh b/t/t4018/ada.sh
new file mode 100755
index 00000000000..45fc2c7a3b2
--- /dev/null
+++ b/t/t4018/ada.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'ada: "procedure" over "with"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+procedure Bottles is
+EOF_HUNK
+with Ada.Text_Io; use Ada.Text_Io;
+ procedure Bottles is
+ begin
+    for X in reverse 1..99 loop
+       Put_Line(Integer'Image(X) & " bottles of beer on the wall");
+       Put_Line(Integer'Image(X) & " bottles of beer"); -- ChangeMe
+       Put_Line("Take one down, pass it around");
+       Put_Line(Integer'Image(X - 1) & " bottles of beer on the wall");
+       New_Line;
+    end loop;
+ end Bottles;
+EOF_TEST
+
+test_diff_funcname 'ada: "task" over "procedure"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+task body Check_CPU is
+EOF_HUNK
+procedure Housekeeping is
+  task Check_CPU;
+  task Backup_Disk;
+
+  task body Check_CPU is
+    -- Comment for spacing with
+    -- the above "task" for -U1
+    ChangeMe
+  end Check_CPU;
+end Housekeeping;
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 32/35] userdiff golang: add a rule to match "package"

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:58:57

Improve the "golang" built-in pattern to match "package" lines, as
they weren't matched before changing e.g. the imports would commonly
result in an empty hunk header, now we'll instead show the package
name.

I used https://blog.golang.org/package-names as a guide here, but
e.g. "foo_bar" is still valid syntax, so let's let it pass but veer on
the side of not having false positives.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 userdiff.c     |  2 ++
 2 files changed, 51 insertions(+)
diff --git a/t/t4018/golang b/t/t4018/golang
index 252b6049da4..38f254cd269 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -1,3 +1,52 @@
+t4018 description: package
+t4018 header: package main
+package main
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- ALLCAPS
+t4018 header: package main
+package ALLCAPS
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- CamelCase
+t4018 header: package main
+package CamelCase
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- 123
+t4018 header: package main
+package 123
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- x509
+t4018 header: package x509
+package x509
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- underbars
+t4018 header: package not_conventional
+package not_conventional
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- camelCase
+t4018 header: package camelCase
+package camelCase
+
+import "fmt"
+// ChangeMe
+
 t4018 description: complex function
 t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
 type Test struct {
diff --git a/userdiff.c b/userdiff.c
index 704af241e44..bbbbfa33e0a 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -128,6 +128,8 @@ IPATTERN("fountain",
 	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
+	 /* Packages */
+	 "^(package[ \t][a-z][A-Za-z0-9_]+)[ \t]*\n"
 	 /* Functions */
 	 "^(func[ \t].*)\n"
 	 /* Structs and interfaces */
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 35/35] userdiff tests: add basic test for ruby

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:59:00

Add a test for the Ruby pattern added way back in ad8c1d9260 (diff:
add ruby funcname pattern, 2008-07-31).

The "One/Two" picking demonstrates existing behavior, and a general
case where we may not do what the user expects since we're not aware
of the indentation level.

The code is modified from the Ruby code we have in-tree at
Documentation/asciidoctor-extensions.rb.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh |  1 -
 t/t4018/ruby.sh          | 58 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)
 create mode 100755 t/t4018/ruby.sh
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b0c2782d067..7793d7652d5 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -23,7 +23,6 @@ test_expect_success 'setup' '
 	html
 	objc
 	pascal
-	ruby
 	tex
 	EOF
 
diff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh
new file mode 100755
index 00000000000..ef8a154421a
--- /dev/null
+++ b/t/t4018/ruby.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'ruby: "def" over "class/module"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def process(parent)
+EOF_HUNK
+require 'asciidoctor'
+
+module Git
+  module Documentation
+    class SomeClass
+      use_some
+
+      def process(parent)
+        puts("hello")
+	puts(ChangeMe)
+      end
+    end
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'ruby: "class" over "class/module"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class Two
+EOF_HUNK
+module Git
+  module Documentation
+    class One
+    end
+
+    class Two
+      # Spacing for -U1
+      ChangeMe
+    end
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'ruby: picks first "class/module/def" before changed context' \
+	'-U1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class One
+EOF_HUNK
+module Git
+  module Documentation
+    class One
+    end
+
+    class Two
+      ChangeMe
+    end
+  end
+end
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7

[PATCH v3 30/35] userdiff golang: simplify and correct matching regex

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-24 19:59:41

Simplify the regex for the golang driver added in
1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01) to
remove redundant constructs.

There's no point in having a regex like this:

    .*(foo)?

When we can just write:

    .*

In the "func" case, since the "(foo?)" match isn't mandatory it won't
matter for the end result, and in this case we're not using the
capture pattern. Not that it would matter since it's followed by a
greedy .*, so we'd only get the empty string.

In the "type" case we would stop at the "{", since it was not preceded
by a ".*". This was a bug, if we have a comment or something else on
that line we should include it.

I'm also changing the "func[ \t]*.*" rule to "func[ \t].*" while I'm
at it. We should always get whitespace after "func", so this narrows
down our match. Let's do the same in the new "type" rule.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 2 +-
 userdiff.c     | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/t/t4018/golang b/t/t4018/golang
index 70bf0d936bb..72a35d66008 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -46,7 +46,7 @@ type RIGHT struct {
 }
 
 t4018 description: struct with comment after {
-t4018 header: type some struct {
+t4018 header: type some struct { // comment
 type some struct { // comment
 	a Type
 	b ChangeMe
diff --git a/userdiff.c b/userdiff.c
index 55f4f769bd3..698eca5ad35 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -129,9 +129,9 @@ IPATTERN("fountain",
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
-	 "^[ \t]*(func[ \t]*.*(\\{[ \t]*)?)\n"
+	 "^[ \t]*(func[ \t].*)\n"
 	 /* Structs and interfaces */
-	 "^[ \t]*(type[ \t].*(struct|interface)[ \t]*(\\{[ \t]*)?)",
+	 "^[ \t]*(type[ \t].*(struct|interface)[ \t].*)",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"
-- 
2.30.0.284.gd98b1dd5eaa7

Re: [PATCH v3 31/35] userdiff golang: don't over-match intented constructs

From: Johannes Sixt <hidden>
Date: 2021-02-26 07:59:38

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
Fix a bug introduced when the "golang" driver was added in
1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01).

Unlike the default def_ff() driver in xemit.c it would match "type"
declarations inside functions. Let's make it mandatory that a "func"
or "type" must be at the beginning of the line with no whitespace to
get around this.

Go is such a regularly formatted language that I think this can be
counted on. I think the whitespace matching was probably copy/pasted
from an earlier userdiff.c pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 20 ++++++++++++++++++++
 userdiff.c     |  4 ++--
 2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/t/t4018/golang b/t/t4018/golang
index 72a35d66008..252b6049da4 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -51,3 +51,23 @@ type some struct { // comment
 	a Type
 	b ChangeMe
 }
+
+t4018 description: func combined with type
+t4018 header: func myfunc() {
+func myfunc() {
+	type mystruct struct {
+		a Foo
+		b Bar
+	}
+	ChangeMe
+
+t4018 description: anonymous indented func()
+t4018 header: func SomeThing() bool {
+func SomeThing() bool {
+	func() {
Wait! With the unmodified pattern, this indented "func()" cannot match
because it is not followed by a space. So, this test case does not
demonstrate that the modified pattern makes a difference. In the commit
message of 30/35 you say that "we should always get whitespace after
'func'", but here you show a case where that is not true. Did you forget
to write a name after "func"?
quoted hunk
+		defer func() {
+			fmt.Println("hello")
+		}()
+	}()
+
+	ChangeMe
diff --git a/userdiff.c b/userdiff.c
index 698eca5ad35..704af241e44 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -129,9 +129,9 @@ IPATTERN("fountain",
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
-	 "^[ \t]*(func[ \t].*)\n"
+	 "^(func[ \t].*)\n"
 	 /* Structs and interfaces */
-	 "^[ \t]*(type[ \t].*(struct|interface)[ \t].*)",
+	 "^(type[ \t].*[ \t](struct|interface)[ \t].*)",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"

Re: [PATCH v3 32/35] userdiff golang: add a rule to match "package"

From: Johannes Sixt <hidden>
Date: 2021-02-26 08:04:35

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
Improve the "golang" built-in pattern to match "package" lines, as
they weren't matched before changing e.g. the imports would commonly
result in an empty hunk header, now we'll instead show the package
name.

I used https://blog.golang.org/package-names as a guide here, but
e.g. "foo_bar" is still valid syntax, so let's let it pass but veer on
the side of not having false positives.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018/golang | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 userdiff.c     |  2 ++
 2 files changed, 51 insertions(+)
diff --git a/t/t4018/golang b/t/t4018/golang
index 252b6049da4..38f254cd269 100644
--- a/t/t4018/golang
+++ b/t/t4018/golang
@@ -1,3 +1,52 @@
+t4018 description: package
+t4018 header: package main
+package main
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- ALLCAPS
+t4018 header: package main
+package ALLCAPS
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- CamelCase
+t4018 header: package main
+package CamelCase
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is selective -- 123
+t4018 header: package main
+package 123
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- x509
+t4018 header: package x509
+package x509
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- underbars
+t4018 header: package not_conventional
+package not_conventional
+
+import "fmt"
+// ChangeMe
+
+t4018 description: package regex is not overly selective -- camelCase
+t4018 header: package camelCase
+package camelCase
+
+import "fmt"
+// ChangeMe
+
 t4018 description: complex function
 t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {
 type Test struct {
diff --git a/userdiff.c b/userdiff.c
index 704af241e44..bbbbfa33e0a 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -128,6 +128,8 @@ IPATTERN("fountain",
 	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
+	 /* Packages */
+	 "^(package[ \t][a-z][A-Za-z0-9_]+)[ \t]*\n"
Just a single whitespace character is permitted between the keyword and
the name?
 	 /* Functions */
 	 "^(func[ \t].*)\n"
 	 /* Structs and interfaces */

Re: [PATCH v3 34/35] userdiff tests: add basic test for ada

From: Johannes Sixt <hidden>
Date: 2021-02-27 07:26:45

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
Add test for the ada userdiff pattern added in e90d065e64 (Add
userdiff patterns for Ada, 2012-09-16).

I don't know the ada language itself, I just stole a couple of
examples of code that used tokens we're matching[1][2]. Both test
examples stress our negative and positive matching rules.

1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada
2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking
Missing Signed-off-by.
quoted hunk
---
 t/t4018-diff-funcname.sh |  1 -
 t/t4018/ada.sh           | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100755 t/t4018/ada.sh
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index ba10d1f5313..b0c2782d067 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -18,7 +18,6 @@ test_expect_success 'setup' '
 	# Do not add anything to this list. New built-in drivers should have
 	# tests
 	cat >drivers-no-tests <<-\EOF &&
-	ada
 	bibtex
 	csharp
 	html
diff --git a/t/t4018/ada.sh b/t/t4018/ada.sh
new file mode 100755
index 00000000000..45fc2c7a3b2
--- /dev/null
+++ b/t/t4018/ada.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'ada: "procedure" over "with"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+procedure Bottles is
+EOF_HUNK
+with Ada.Text_Io; use Ada.Text_Io;
+ procedure Bottles is
+ begin
+    for X in reverse 1..99 loop
+       Put_Line(Integer'Image(X) & " bottles of beer on the wall");
+       Put_Line(Integer'Image(X) & " bottles of beer"); -- ChangeMe
+       Put_Line("Take one down, pass it around");
+       Put_Line(Integer'Image(X - 1) & " bottles of beer on the wall");
+       New_Line;
+    end loop;
+ end Bottles;
+EOF_TEST
+
+test_diff_funcname 'ada: "task" over "procedure"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+task body Check_CPU is
+EOF_HUNK
+procedure Housekeeping is
+  task Check_CPU;
+  task Backup_Disk;
+
+  task body Check_CPU is
+    -- Comment for spacing with
+    -- the above "task" for -U1
Aren't we using -U0 by now?
+    ChangeMe
+  end Check_CPU;
+end Housekeeping;
+EOF_TEST
I don't see anything special in these tests. Couldn't you just use the
simplified version of tests, which are much easier to read than this
shell script with EOF markers?

-- Hannes

Re: [PATCH v3 35/35] userdiff tests: add basic test for ruby

From: Johannes Sixt <hidden>
Date: 2021-02-27 07:30:31

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
Add a test for the Ruby pattern added way back in ad8c1d9260 (diff:
add ruby funcname pattern, 2008-07-31).

The "One/Two" picking demonstrates existing behavior, and a general
case where we may not do what the user expects since we're not aware
of the indentation level.

The code is modified from the Ruby code we have in-tree at
Documentation/asciidoctor-extensions.rb.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh |  1 -
 t/t4018/ruby.sh          | 58 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)
 create mode 100755 t/t4018/ruby.sh
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b0c2782d067..7793d7652d5 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -23,7 +23,6 @@ test_expect_success 'setup' '
 	html
 	objc
 	pascal
-	ruby
 	tex
 	EOF
 
diff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh
new file mode 100755
index 00000000000..ef8a154421a
--- /dev/null
+++ b/t/t4018/ruby.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'ruby: "def" over "class/module"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def process(parent)
+EOF_HUNK
+require 'asciidoctor'
+
+module Git
+  module Documentation
+    class SomeClass
+      use_some
+
+      def process(parent)
+        puts("hello")
+	puts(ChangeMe)
+      end
+    end
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'ruby: "class" over "class/module"' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class Two
+EOF_HUNK
+module Git
+  module Documentation
+    class One
+    end
+
+    class Two
+      # Spacing for -U1
Again: -U0 is the default by now.
+      ChangeMe
+    end
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'ruby: picks first "class/module/def" before changed context' \
+	'-U1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class One
+EOF_HUNK
+module Git
+  module Documentation
+    class One
+    end
+
+    class Two
+      ChangeMe
+    end
+  end
+end
+EOF_TEST
Why does this test need -U1? Wouldn't it be sufficient to change the
expected text to "class Two"? And if the answer is "yes", I again do not
see the need for a shell script test here; the simplified version would do.

-- Hannes

Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com

From: Johannes Sixt <hidden>
Date: 2021-02-27 07:47:49

Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:
Addresses feedback on v2. Since Junio & Johannes expressed a desire to
keep the existing test scheme in t4018/* it's still there, but it's
also possible to add *.sh tests in that directory to use the more
familiar test framework used elsewhere in the test suite.

The tests added here make use of it to e.g. supply custom -U<n>
arguments, set config before the tests etc.

I also improved that existing test support so you can have N tests in
one file with (mostly) the existing test syntax. See the "userdiff
tests: add a test with multiple tests in a LANG file" patch.
I've read through all patches and had a comment here and there. I like a
lot that we can now put more than one test into a single file.

However, I do not like the shell script version of tests, because the
syntax is so hard to read. Also, it looks to me that they are only
needed for a few tests that could just as well be coded as one-off tests
outside the framework.

I've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your
github repo and will check again if I missed some cruicial points.

-- Hannes

Re: [PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests

From: Johannes Sixt <hidden>
Date: 2021-02-28 10:34:52

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
Add an assertion to the userdiff test framework to check that
everything except a narrow whitelist of existing built-in patterns has
tests.

Since this test framework was added we've added new patterns without
any tests. Let's make it obvious in the future in the diff for such
patches that they should have those tests.

For anything with tests we can skip the "does the pattern compile?"
test, as the actual tests will check that for us.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b80546b4d7f..a3058fda130 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -15,6 +15,19 @@ test_expect_success 'setup' '
 	sort <builtin-drivers >builtin-drivers.sorted &&
 	test_cmp builtin-drivers.sorted builtin-drivers &&
 
+	# Do not add anything to this list. New built-in drivers should have
+	# tests
+	cat >drivers-no-tests <<-\EOF &&
+	ada
+	bibtex
+	csharp
+	html
+	objc
+	pascal
+	ruby
+	tex
+	EOF
+
 	# for regexp compilation tests
 	echo A >A.java &&
 	echo B >B.java
@@ -22,7 +35,12 @@ test_expect_success 'setup' '
 
 for p in $(cat builtin-drivers)
 do
-	test_expect_success "builtin $p pattern compiles" '
+	P=$(echo $p | tr 'a-z' 'A-Z')
+	if grep -q $p drivers-no-tests
+	then
+		test_set_prereq NO_TEST_FOR_DRIVER_$P
+	fi
+	test_expect_success NO_TEST_FOR_DRIVER_$P "builtin $p pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index \
 			A.java B.java 2>msg &&
Please drop this hunk. It is extremly distracting to see, e.g.,

ok 8 # skip builtin cpp pattern compiles (missing NO_TEST_FOR_DRIVER_CPP)
ok 9 - builtin cpp wordRegex pattern compiles

It says "NO_TEST_FOR_DRIVER_CPP", but I know we have tests. I have to
waste time to check that something not related to "we have tests for the
driver" is meant here. It may be just a matter of naming the
prerequisite, but I think we do not need this optimization.
quoted hunk
@@ -119,11 +137,17 @@ test_diff_funcname () {
 	'
 }
 
+>drivers-had-no-tests
 for what in $diffpatterns
 do
 	test="$TEST_DIRECTORY/t4018/$what.sh"
 	if ! test -e "$test"
 	then
+		git -C t4018 ls-files ':!*.sh' "$what*" >other-tests &&
+		if ! test -s other-tests
+		then
+			echo $what >>drivers-had-no-tests
+		fi
 		continue
 	fi &&
 
@@ -135,4 +159,8 @@ do
 	. "$test"
 done
 
+test_expect_success 'we should not have new built-in drivers without tests' '
+	test_cmp drivers-no-tests drivers-had-no-tests
+'
+
 test_done

Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com

From: Johannes Sixt <hidden>
Date: 2021-02-28 11:05:37

Am 27.02.21 um 08:47 schrieb Johannes Sixt:
Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:
quoted
Addresses feedback on v2. Since Junio & Johannes expressed a desire to
keep the existing test scheme in t4018/* it's still there, but it's
also possible to add *.sh tests in that directory to use the more
familiar test framework used elsewhere in the test suite.

The tests added here make use of it to e.g. supply custom -U<n>
arguments, set config before the tests etc.

I also improved that existing test support so you can have N tests in
one file with (mostly) the existing test syntax. See the "userdiff
tests: add a test with multiple tests in a LANG file" patch.
I've read through all patches and had a comment here and there. I like a
lot that we can now put more than one test into a single file.

However, I do not like the shell script version of tests, because the
syntax is so hard to read. Also, it looks to me that they are only
needed for a few tests that could just as well be coded as one-off tests
outside the framework.

I've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your
github repo and will check again if I missed some cruicial points.

I've now looked through the patch series again.

I appreciate that you dug through the history and discovered and fixed a
few deficiencies and loose ends. The way to throw all test cases for a
language driver into a single file I like a lot, too. The way to specify
expected results is manageable (modulo the dependency on the test
number, t4018, that Junio mentioned).

But I do not see the need for the framework provided by the new
test_diff_funcname. At the end of the series, it is only used by Perl
and custom driver tests. (I discount the new ADA and Ruby tests as they
can easily migrated to the simple test scheme.) But then the Perl tests
do not do anything special, either. The multi-line pattern test is just
a nice add-on but not strictly needed. In the end, the Perl test is just
as straight-forward as all others.

That leaves the custom driver tests. I don't see the need for a new
framework just for that. It would be sufficient to integrate them in
main test file t4018-diff-funcname.sh if needed, or leave them in the
existing framework if possible.

-- Hannes

Re: [PATCH v3 14/35] userdiff tests: add alternative hunk header test infrastructure

From: Johannes Sixt <hidden>
Date: 2021-02-28 11:13:47

Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted hunk
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 8a8a7a99c88..6fd3dce1364 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -87,7 +87,7 @@ last_diff_context_line () {
 }
 
 # check each individual file
-for i in $(git -C t4018 ls-files)
+for i in $(git -C t4018 ls-files -- ':!*.sh')
 do
 	test_expect_success "setup hunk header: $i" "
 		grep -v '^t4018' \"t4018/$i\" >\"t4018/$i.content\" &&
@@ -106,4 +106,40 @@ do
 	"
 done
 
+test_diff_funcname () {
+	desc=$1
+	cat <&8 >arg.header &&
+	cat <&9 >arg.test &&
+	what=$(cat arg.what) &&
+
+	test_expect_success "setup: $desc" '
+		cp arg.test "$what" &&
+		cp arg.header expected &&
+		git add "$what" &&
+		do_change_me "$what"
+	' &&
I think we should not chain test_expect_success with && because when one
of the tests early in the chain fails, test case numbers are shifted
compared to when all tests succeed. I don't know, though, whether that
is an important thing (or a thing at all).
+
+	test_expect_success "$desc" '
+		git diff -U1 "$what" >diff &&
+		last_diff_context_line diff >actual &&
+		test_cmp expected actual
+	'
+}
+
+for what in $diffpatterns
+do
+	test="$TEST_DIRECTORY/t4018/$what.sh"
+	if ! test -e "$test"
+	then
+		continue
+	fi &&
+
+	test_expect_success "setup: hunk header for $what" '
+		echo "$what diff=$what" >.gitattributes &&
+		echo "$what" >arg.what
+	' &&
Same here. And when this is removed, the && just above is not needed
anymore, either.
+
+	. "$test"
+done
+
 test_done
-- Hannes

Re: [PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-28 15:52:52

On Sun, Feb 28 2021, Johannes Sixt wrote:
Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:
quoted
Add an assertion to the userdiff test framework to check that
everything except a narrow whitelist of existing built-in patterns has
tests.

Since this test framework was added we've added new patterns without
any tests. Let's make it obvious in the future in the diff for such
patches that they should have those tests.

For anything with tests we can skip the "does the pattern compile?"
test, as the actual tests will check that for us.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b80546b4d7f..a3058fda130 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -15,6 +15,19 @@ test_expect_success 'setup' '
 	sort <builtin-drivers >builtin-drivers.sorted &&
 	test_cmp builtin-drivers.sorted builtin-drivers &&
 
+	# Do not add anything to this list. New built-in drivers should have
+	# tests
+	cat >drivers-no-tests <<-\EOF &&
+	ada
+	bibtex
+	csharp
+	html
+	objc
+	pascal
+	ruby
+	tex
+	EOF
+
 	# for regexp compilation tests
 	echo A >A.java &&
 	echo B >B.java
@@ -22,7 +35,12 @@ test_expect_success 'setup' '
 
 for p in $(cat builtin-drivers)
 do
-	test_expect_success "builtin $p pattern compiles" '
+	P=$(echo $p | tr 'a-z' 'A-Z')
+	if grep -q $p drivers-no-tests
+	then
+		test_set_prereq NO_TEST_FOR_DRIVER_$P
+	fi
+	test_expect_success NO_TEST_FOR_DRIVER_$P "builtin $p pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index \
 			A.java B.java 2>msg &&
Please drop this hunk. It is extremly distracting to see, e.g.,

ok 8 # skip builtin cpp pattern compiles (missing NO_TEST_FOR_DRIVER_CPP)
ok 9 - builtin cpp wordRegex pattern compiles

It says "NO_TEST_FOR_DRIVER_CPP", but I know we have tests. I have to
waste time to check that something not related to "we have tests for the
driver" is meant here. It may be just a matter of naming the
prerequisite, but I think we do not need this optimization.
Perhaps some other way to do this is better. I did the prerequisite just
to avoid indenting that whole part and I figured it was more readable,
but apparently not on the "more readable".

I do think it makes sense to keep this in some form, i.e. not test the
compilation if we know we have later tests to compile the regex. It's
providing self-documenting code to show that once we add tests for the
few missing drivers we can delete that test entirely.

And if a later change does the same check on the t4034/* files the
--word-diff check can also go.
quoted
@@ -119,11 +137,17 @@ test_diff_funcname () {
 	'
 }
 
+>drivers-had-no-tests
 for what in $diffpatterns
 do
 	test="$TEST_DIRECTORY/t4018/$what.sh"
 	if ! test -e "$test"
 	then
+		git -C t4018 ls-files ':!*.sh' "$what*" >other-tests &&
+		if ! test -s other-tests
+		then
+			echo $what >>drivers-had-no-tests
+		fi
 		continue
 	fi &&
 
@@ -135,4 +159,8 @@ do
 	. "$test"
 done
 
+test_expect_success 'we should not have new built-in drivers without tests' '
+	test_cmp drivers-no-tests drivers-had-no-tests
+'
+
 test_done

Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-02-28 16:08:42

On Sun, Feb 28 2021, Johannes Sixt wrote:
Am 27.02.21 um 08:47 schrieb Johannes Sixt:
quoted
Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:
quoted
Addresses feedback on v2. Since Junio & Johannes expressed a desire to
keep the existing test scheme in t4018/* it's still there, but it's
also possible to add *.sh tests in that directory to use the more
familiar test framework used elsewhere in the test suite.

The tests added here make use of it to e.g. supply custom -U<n>
arguments, set config before the tests etc.

I also improved that existing test support so you can have N tests in
one file with (mostly) the existing test syntax. See the "userdiff
tests: add a test with multiple tests in a LANG file" patch.
I've read through all patches and had a comment here and there. I like a
lot that we can now put more than one test into a single file.

However, I do not like the shell script version of tests, because the
syntax is so hard to read. Also, it looks to me that they are only
needed for a few tests that could just as well be coded as one-off tests
outside the framework.

I've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your
github repo and will check again if I missed some cruicial points.

I've now looked through the patch series again.

I appreciate that you dug through the history and discovered and fixed a
few deficiencies and loose ends. The way to throw all test cases for a
language driver into a single file I like a lot, too. The way to specify
expected results is manageable (modulo the dependency on the test
number, t4018, that Junio mentioned).
Yes, maybe just "HEADER:" or something would be better. I was trying to
come up with something guaranteed not to conflict with the language in
question. Maybe:

    => HEADER      = 
    => description = 
But I do not see the need for the framework provided by the new
test_diff_funcname. At the end of the series, it is only used by Perl
and custom driver tests. (I discount the new ADA and Ruby tests as they
can easily migrated to the simple test scheme.) But then the Perl tests
do not do anything special, either. The multi-line pattern test is just
a nice add-on but not strictly needed. In the end, the Perl test is just
as straight-forward as all others.
The benefit now is:

 1. Unlike the new plain-text "all test cases for a language driver" in
    the same file mechanism you can have test descriptions. The
    "description" in the golang one is just for show, you won't get
    anything informative from test-lib.sh when your test fails.

 2. I think having #1 beats not having test descriptions at all, or
     having to shove a description like the Ruby:

    "picks first "class/module/def" before changed context"

    into something that would make all the FS's we have to support
    happy.

 3. A test in the new perl.sh one sets config. I think in both that case
    and custom.sh it's more readable to carry such config setting with
    the test, rather than at a distance in the main setup of t4018-*.sh.

That being said I'd like to improve the syntax a bit, in particular
instead of having a wrapper for test_expect_success I think it makes
sense just to have the test call test_expect_success, and then provide a
couple of helper functions.

Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com

From: Johannes Sixt <hidden>
Date: 2021-03-01 07:11:20

Am 28.02.21 um 17:07 schrieb Ævar Arnfjörð Bjarmason:
On Sun, Feb 28 2021, Johannes Sixt wrote:
quoted
But I do not see the need for the framework provided by the new
test_diff_funcname. At the end of the series, it is only used by Perl
and custom driver tests. (I discount the new ADA and Ruby tests as they
can easily migrated to the simple test scheme.) But then the Perl tests
do not do anything special, either. The multi-line pattern test is just
a nice add-on but not strictly needed. In the end, the Perl test is just
as straight-forward as all others.
The benefit now is:

 1. Unlike the new plain-text "all test cases for a language driver" in
    the same file mechanism you can have test descriptions. The
    "description" in the golang one is just for show, you won't get
    anything informative from test-lib.sh when your test fails.

 2. I think having #1 beats not having test descriptions at all, or
     having to shove a description like the Ruby:

    "picks first "class/module/def" before changed context"

    into something that would make all the FS's we have to support
    happy.
I missed that the descriptions are gone now that many test cases are
shoved into a single file when the simple framework is used. That is
indeed a disadvantage. But please keep in mind that code is more often
read than written. *If* we have to grow a new framework, then it must
not suffer from unreadability.
 3. A test in the new perl.sh one sets config. I think in both that case
    and custom.sh it's more readable to carry such config setting with
    the test, rather than at a distance in the main setup of t4018-*.sh.
As I said, the config in perl.sh is only used for a multi-line pattern
test. That is dispensable as there is already a similar test with custom
patterns. And the tests for custom patterns can be moved to the main
test file entirely.
That being said I'd like to improve the syntax a bit, in particular
instead of having a wrapper for test_expect_success I think it makes
sense just to have the test call test_expect_success, and then provide a
couple of helper functions.
That sounds like an improvement.

-- Hannes

[PATCH v4 01/10] userdiff: refactor away the parse_bool() function

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Since 6680a0874f (drop odd return value semantics from
userdiff_config, 2012-02-07) we have not cared about the return values
of parse_tristate() or git_config_bool() v.s. falling through in
userdiff_config(), so let's do so in those cases to make the code
easier to read.

Having a wrapper function for git_config_bool() dates back to
d9bae1a178 (diff: cache textconv output, 2010-04-01) and
122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of
which predated the change in 6680a0874f which made their return values
redundant.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c5..c147bcbb173 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,
 	return 0;
 }
 
-static int parse_tristate(int *b, const char *k, const char *v)
+static void parse_tristate(int *b, const char *k, const char *v)
 {
 	if (v && !strcasecmp(v, "auto"))
 		*b = -1;
 	else
 		*b = git_config_bool(k, v);
-	return 0;
-}
-
-static int parse_bool(int *b, const char *k, const char *v)
-{
-	*b = git_config_bool(k, v);
-	return 0;
 }
 
 int userdiff_config(const char *k, const char *v)
@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)
 		return parse_funcname(&drv->funcname, k, v, 0);
 	if (!strcmp(type, "xfuncname"))
 		return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
-	if (!strcmp(type, "binary"))
-		return parse_tristate(&drv->binary, k, v);
 	if (!strcmp(type, "command"))
 		return git_config_string(&drv->external, k, v);
 	if (!strcmp(type, "textconv"))
 		return git_config_string(&drv->textconv, k, v);
-	if (!strcmp(type, "cachetextconv"))
-		return parse_bool(&drv->textconv_want_cache, k, v);
 	if (!strcmp(type, "wordregex"))
 		return git_config_string(&drv->word_regex, k, v);
+	/* Don't care about the parse errors for these, fallthrough */
+	if (!strcmp(type, "cachetextconv"))
+		drv->textconv_want_cache = git_config_bool(k, v);
+	if (!strcmp(type, "binary"))
+		parse_tristate(&drv->binary, k, v);
 
 	return 0;
 }
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 00/10] userdiff: refactor + test improvements

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

This is a restart of the 35-patch v3 of this topic at
https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/

I still plan on submitting the rest of it, but wanted to start with
the early parts of that series that hasn't been controversial or has
outstanding feedback I haven't addressed.

The range-diff to v3 is just for those patches I'm re-rolling here.

Ævar Arnfjörð Bjarmason (10):
  userdiff: refactor away the parse_bool() function
  userdiff style: re-order drivers in alphabetical order
  userdiff style: declare patterns with consistent style
  userdiff style: normalize pascal regex declaration
  userdiff: add and use for_each_userdiff_driver()
  userdiff tests: explicitly test "default" pattern
  userdiff tests: list builtin drivers via test-tool
  userdiff: remove support for "broken" tests
  blame tests: don't rely on t/t4018/ directory
  blame tests: simplify userdiff driver test

 Makefile                 |   1 +
 t/annotate-tests.sh      |  34 ++++----
 t/helper/test-tool.c     |   1 +
 t/helper/test-tool.h     |   1 +
 t/helper/test-userdiff.c |  31 +++++++
 t/t4018-diff-funcname.sh |  39 ++-------
 t/t4018/README           |   3 -
 userdiff.c               | 178 ++++++++++++++++++++++++---------------
 userdiff.h               |  15 ++++
 9 files changed, 186 insertions(+), 117 deletions(-)
 create mode 100644 t/helper/test-userdiff.c

Range-diff:
 1:  0be132b05e2 =  1:  fb7346cd296 userdiff: refactor away the parse_bool() function
 2:  d1e00a739ac =  2:  149387155bc userdiff style: re-order drivers in alphabetical order
 3:  b99bd158d45 =  3:  faf1a824f05 userdiff style: declare patterns with consistent style
 4:  9ce6d47021c =  4:  1e9ddcd1a9a userdiff style: normalize pascal regex declaration
 5:  369fbdcee83 =  5:  64ea5e8443f userdiff: add and use for_each_userdiff_driver()
 6:  70d62a97211 =  6:  862f6ab5d66 userdiff tests: explicitly test "default" pattern
 7:  792421a2f8b =  7:  22a07591b76 userdiff tests: list builtin drivers via test-tool
 8:  9081e2a152e !  8:  7755db95014 userdiff: remove support for "broken" tests
    @@ Commit message
     
         There have been no "broken" tests since 75c3b6b2e8 (userdiff: improve
         Fortran xfuncname regex, 2020-08-12). Let's remove the test support
    -    for them, this is in preparation for a more general refactoring of the
    -    tests.
    +    for them.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
 9:  d3652f95d5e !  9:  4e0b4b42e16 blame tests: don't rely on t/t4018/ directory
    @@ Commit message
         with userdiff driver, 2020-11-01) so that the blame tests don't rely
         on stealing the contents of "t/t4018/fortran-external-function".
     
    -    I'm about to change that file in a subsequent commit. Just moving the
    -    relevant test file here inline is the easiest solution, and I think
    -    also the most readable.
    +    I have another patch series that'll possibly (or not) refactor that
    +    file, but having this test inter-dependency makes things simple in any
    +    case by making this test more readable.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
10:  35d12779ea1 ! 10:  ce98c61bf40 blame tests: simplify userdiff driver test
    @@ Commit message
         test_commit, 2021-01-12).
     
         We also did not need the full fortran-external-function content. Let's
    -    cut it down to just the important parts, and further modify it to
    -    demonstrate that the fortran-specific userdiff function is in effect
    -    by adding "DO NOT MATCH ..." and "AS THE ..." lines surrounding the
    -    "RIGHT" one. This is to check that we're using the userdiff "fortran"
    -    driver, as opposed to the default driver.
    +    cut it down to just the important parts.
     
    -    The test also left behind a .gitattributes files, let's clean it up
    -    with "test_when_finished".
    +    I'm modifying it to demonstrate that the fortran-specific userdiff
    +    function is in effect by adding "DO NOT MATCH ..." and "AS THE ..."
    +    lines surrounding the "RIGHT" one.
    +
    +    This is to check that we're using the userdiff "fortran" driver, as
    +    opposed to the default driver which would match on those lines as part
    +    of the general heuristic of matching a line that doesn't begin with
    +    whitespace.
    +
    +    The test had also been leaving behind a .gitattributes file for later
    +    tests to possibly trip over, let's clean it up with
    +    "test_when_finished".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 02/10] userdiff style: re-order drivers in alphabetical order

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Address some old code smell and move around the built-in userdiff
drivers so they're both in alphabetical order, and now in the same
order they appear in the gitattributes(5) documentation.

The two started drifting in be58e70dba (diff: unify external diff and
funcname parsing code, 2008-10-05), and then even further in
80c49c3de2 (color-words: make regex configurable via attributes,
2009-01-17) when the "cpp" pattern was added.

There are no functional changes here, and as --color-moved will show
only moved existing lines.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 76 +++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c147bcbb173..c92cbcc0540 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,6 +44,44 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 "[={}\"]|[^={}\" \t]+"),
+PATTERNS("cpp",
+	 /* Jump targets or access declarations */
+	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
+	 /* functions/methods, variables, and compounds at top level */
+	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+PATTERNS("csharp",
+	 /* Keywords */
+	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+	 /* Methods and constructors */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+	 /* Properties */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /* Type definitions */
+	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+	 /* Namespace */
+	 "^[ \t]*(namespace[ \t]+.*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+IPATTERN("css",
+	 "![:;][[:space:]]*$\n"
+	 "^[:[@.#]?[_a-z0-9].*$",
+	 /* -- */
+	 /*
+	  * This regex comes from W3C CSS specs. Should theoretically also
+	  * allow ISO 10646 characters U+00A0 and higher,
+	  * but they are not handled in this regex.
+	  */
+	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
+	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
+),
 PATTERNS("dts",
 	 "!;\n"
 	 "!=\n"
@@ -191,46 +229,8 @@ PATTERNS("rust",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
 	 "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-PATTERNS("cpp",
-	 /* Jump targets or access declarations */
-	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
-	 /* functions/methods, variables, and compounds at top level */
-	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
-PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
-	 /* Type definitions */
-	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
-	 /* Namespace */
-	 "^[ \t]*(namespace[ \t]+.*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-IPATTERN("css",
-	 "![:;][[:space:]]*$\n"
-	 "^[:[@.#]?[_a-z0-9].*$",
-	 /* -- */
-	 /*
-	  * This regex comes from W3C CSS specs. Should theoretically also
-	  * allow ISO 10646 characters U+00A0 and higher,
-	  * but they are not handled in this regex.
-	  */
-	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
-	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
-),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 06/10] userdiff tests: explicitly test "default" pattern

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)
the internals of the userdiff.c code have understood a "default" name,
which is invoked as userdiff_find_by_name("default") and present in
the "builtin_drivers" struct. Let's test for this special case.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 1 +
 1 file changed, 1 insertion(+)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 9675bc17db2..cefe329aea7 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -49,6 +49,7 @@ diffpatterns="
 	ruby
 	rust
 	tex
+	default
 	custom1
 	custom2
 	custom3
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 04/10] userdiff style: normalize pascal regex declaration

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Declare the pascal pattern consistently with how we declare the
others, not having "\n" on one line by itself, but as part of the
pattern, and when there are alterations have the "|" at the start, not
end of the line.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c7aaf7094f8..10a02d36209 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -175,9 +175,8 @@ PATTERNS("objc",
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("pascal",
-	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
-		"implementation|initialization|finalization)[ \t]*.*)$"
-	 "\n"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface"
+	 "|implementation|initialization|finalization)[ \t]*.*)$\n"
 	 "^(.*=[ \t]*(class|record).*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 08/10] userdiff: remove support for "broken" tests

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

There have been no "broken" tests since 75c3b6b2e8 (userdiff: improve
Fortran xfuncname regex, 2020-08-12). Let's remove the test support
for them.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/t4018-diff-funcname.sh | 8 +-------
 t/t4018/README           | 3 ---
 2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5bd82e09ab3..9aec9f8e6de 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -86,13 +86,7 @@ test_expect_success 'setup hunk header tests' '
 # check each individual file
 for i in $(git ls-files)
 do
-	if grep broken "$i" >/dev/null 2>&1
-	then
-		result=failure
-	else
-		result=success
-	fi
-	test_expect_$result "hunk header: $i" "
+	test_expect_success "hunk header: $i" "
 		git diff -U1 $i >actual &&
 		grep '@@ .* @@.*RIGHT' actual
 	"
diff --git a/t/t4018/README b/t/t4018/README
index 283e01cca1a..2d25b2b4fc9 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.
 The text that must appear in the hunk header must contain the word
 "right", but in all upper-case, like in the title above.
 
-To mark a test case that highlights a malfunction, insert the word
-BROKEN in all lower-case somewhere in the file.
-
 This text is a bit twisted and out of order, but it is itself a
 test case for the default hunk header pattern. Know what you are doing
 if you change it.
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Refactor the userdiff_find_by_namelen() function so that a new
for_each_userdiff_driver() API function does most of the work.

This will be useful for the same reason we've got other for_each_*()
API functions as part of various APIs, and will be used in a follow-up
commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------
 userdiff.h | 15 ++++++++++++++
 2 files changed, 64 insertions(+), 12 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 10a02d36209..55f4f769bd3 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -259,20 +259,32 @@ static struct userdiff_driver driver_false = {
 	{ NULL, 0 }
 };
 
-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+struct for_each_userdiff_driver_cb {
+	const char *k;
+	size_t len;
+	struct userdiff_driver *driver;
+};
+
+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
+				       enum userdiff_driver_type type, void *priv)
 {
-	int i;
-	for (i = 0; i < ndrivers; i++) {
-		struct userdiff_driver *drv = drivers + i;
-		if (!strncmp(drv->name, k, len) && !drv->name[len])
-			return drv;
-	}
-	for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
-		struct userdiff_driver *drv = builtin_drivers + i;
-		if (!strncmp(drv->name, k, len) && !drv->name[len])
-			return drv;
+	struct for_each_userdiff_driver_cb *cb_data = priv;
+
+	if (!strncmp(driver->name, cb_data->k, cb_data->len) &&
+	    !driver->name[cb_data->len]) {
+		cb_data->driver = driver;
+		return -1; /* found it! */
 	}
-	return NULL;
+	return 0;
+}
+
+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+{
+	struct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };
+
+	for_each_userdiff_driver(userdiff_find_by_namelen_cb,
+				 USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);
+	return udcbdata.driver;
 }
 
 static int parse_funcname(struct userdiff_funcname *f, const char *k,
@@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,
 
 	return driver;
 }
+
+int for_each_userdiff_driver(each_userdiff_driver_fn fn,
+			     enum userdiff_driver_type type, void *cb_data)
+{
+	int i, ret;
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {
+
+		for (i = 0; i < ndrivers; i++) {
+			struct userdiff_driver *drv = drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {
+
+		for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+			struct userdiff_driver *drv = builtin_drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+}
diff --git a/userdiff.h b/userdiff.h
index 203057e13e5..fe14014a775 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -21,6 +21,13 @@ struct userdiff_driver {
 	struct notes_cache *textconv_cache;
 	int textconv_want_cache;
 };
+enum userdiff_driver_type {
+	USERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,
+	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,
+	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,
+};
+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,
+				       enum userdiff_driver_type, void *);
 
 int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 struct userdiff_driver *userdiff_get_textconv(struct repository *r,
 					      struct userdiff_driver *driver);
 
+/*
+ * Iterate over each driver of type userdiff_driver_type, or
+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero
+ * to exit from the loop.
+ */
+int for_each_userdiff_driver(each_userdiff_driver_fn,
+			     enum userdiff_driver_type, void *);
+
 #endif /* USERDIFF */
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 03/10] userdiff style: declare patterns with consistent style

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:04

Change those patterns which were declared with a regex on the same
line as the "PATTERNS()" line to put that regex on the next line, and
add missing "/* -- */" separator comments between the pattern and
word_regex.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index c92cbcc0540..c7aaf7094f8 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,7 +44,9 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+PATTERNS("bibtex",
+	 "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 /* -- */
 	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("cpp",
 	 /* Jump targets or access declarations */
@@ -121,7 +123,9 @@ IPATTERN("fortran",
 	  * they would have been matched above as a variable anyway. */
 	 "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
 	 "|//|\\*\\*|::|[/<>=]="),
-IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+IPATTERN("fountain",
+	 "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
@@ -132,7 +136,9 @@ PATTERNS("golang",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"
 	 "|[-+*/<>%&^|=!:]=|--|\\+\\+|<<=?|>>=?|&\\^=?|&&|\\|\\||<-|\\.{3}"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+PATTERNS("html",
+	 "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("java",
 	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
@@ -144,6 +150,7 @@ PATTERNS("java",
 	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
 PATTERNS("markdown",
 	 "^ {0,3}#{1,6}[ \t].*",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("matlab",
 	 /*
@@ -152,6 +159,7 @@ PATTERNS("matlab",
 	  * that is understood by both.
 	  */
 	 "^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$",
+	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
 PATTERNS("objc",
 	 /* Negate C statements that can look like functions */
@@ -212,13 +220,15 @@ PATTERNS("php",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
 	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"),
-PATTERNS("python", "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
+PATTERNS("python",
+	 "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
 	 /* -- */
-PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+PATTERNS("ruby",
+	 "^[ \t]*((class|module|def)[ \t].*)$",
 	 /* -- */
 	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 09/10] blame tests: don't rely on t/t4018/ directory

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:05

Refactor a test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) so that the blame tests don't rely
on stealing the contents of "t/t4018/fortran-external-function".

I have another patch series that'll possibly (or not) refactor that
file, but having this test inter-dependency makes things simple in any
case by making this test more readable.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/annotate-tests.sh | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 29ce89090d8..04a2c58594c 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
 test_expect_success 'setup -L :funcname with userdiff driver' '
 	echo "fortran-* diff=fortran" >.gitattributes &&
 	fortran_file=fortran-external-function &&
-	orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
-	cp "$orig_file" . &&
+	cat >$fortran_file <<-\EOF &&
+	function RIGHT(a, b) result(c)
+
+	integer, intent(in) :: ChangeMe
+	integer, intent(in) :: b
+	integer, intent(out) :: c
+
+	c = a+b
+
+	end function RIGHT
+	EOF
 	git add "$fortran_file" &&
 	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
 	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >"$fortran_file" &&
+	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
+	mv "$fortran_file".tmp "$fortran_file" &&
 	git add "$fortran_file" &&
 	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
 	git commit -m "change fortran file"
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 07/10] userdiff tests: list builtin drivers via test-tool

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:05

Change the userdiff test to list the builtin drivers via the
test-tool, using the new for_each_userdiff_driver() API function.

This gets rid of the need to modify this part of the test every time a
new pattern is added, see 2ff6c34612 (userdiff: support Bash,
2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)
for two recent examples.

I only need the "list-builtin-drivers "argument here, but let's add
"list-custom-drivers" and "list-drivers" too, just because it's easy.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 Makefile                 |  1 +
 t/helper/test-tool.c     |  1 +
 t/helper/test-tool.h     |  1 +
 t/helper/test-userdiff.c | 31 +++++++++++++++++++++++++++++++
 t/t4018-diff-funcname.sh | 32 ++++++++------------------------
 5 files changed, 42 insertions(+), 24 deletions(-)
 create mode 100644 t/helper/test-userdiff.c
diff --git a/Makefile b/Makefile
index f3dc2178324..3e2568d68da 100644
--- a/Makefile
+++ b/Makefile
@@ -744,6 +744,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
+TEST_BUILTINS_OBJS += test-userdiff.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index f97cd9f48a6..dcb05ca6e5e 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {
 	{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
 	{ "subprocess", cmd__subprocess },
 	{ "trace2", cmd__trace2 },
+	{ "userdiff", cmd__userdiff },
 	{ "urlmatch-normalization", cmd__urlmatch_normalization },
 	{ "xml-encode", cmd__xml_encode },
 	{ "wildmatch", cmd__wildmatch },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 28072c0ad5a..589f2e8ac67 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);
 int cmd__submodule_nested_repo_config(int argc, const char **argv);
 int cmd__subprocess(int argc, const char **argv);
 int cmd__trace2(int argc, const char **argv);
+int cmd__userdiff(int argc, const char **argv);
 int cmd__urlmatch_normalization(int argc, const char **argv);
 int cmd__xml_encode(int argc, const char **argv);
 int cmd__wildmatch(int argc, const char **argv);
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
new file mode 100644
index 00000000000..f173a4f18af
--- /dev/null
+++ b/t/helper/test-userdiff.c
@@ -0,0 +1,31 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "userdiff.h"
+
+static int driver_cb(struct userdiff_driver *driver,
+		     enum userdiff_driver_type type, void *priv)
+{
+	if (driver->funcname.pattern)
+		puts(driver->name);
+	return 0;
+}
+
+static int list_what(enum userdiff_driver_type type)
+{
+	return for_each_userdiff_driver(driver_cb, type, NULL);
+}
+
+int cmd__userdiff(int argc, const char **argv)
+{
+	if (argc != 2)
+		return 1;
+
+	if (!strcmp(argv[1], "list-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);
+	else if (!strcmp(argv[1], "list-builtin-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_BUILTIN);
+	else if (!strcmp(argv[1], "list-custom-drivers"))
+		return list_what(USERDIFF_DRIVER_TYPE_CUSTOM);
+	else
+		return error("unknown argument %s", argv[1]);
+}
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index cefe329aea7..5bd82e09ab3 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -8,6 +8,13 @@ test_description='Test custom diff function name patterns'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	# Make sure additions to builtin_drivers are sorted
+	test_when_finished "rm builtin-drivers.sorted" &&
+	test-tool userdiff list-builtin-drivers >builtin-drivers &&
+	test_file_not_empty builtin-drivers &&
+	sort <builtin-drivers >builtin-drivers.sorted &&
+	test_cmp builtin-drivers.sorted builtin-drivers &&
+
 	# a non-trivial custom pattern
 	git config diff.custom1.funcname "!static
 !String
@@ -26,30 +33,7 @@ test_expect_success 'setup' '
 '
 
 diffpatterns="
-	ada
-	bash
-	bibtex
-	cpp
-	csharp
-	css
-	dts
-	elixir
-	fortran
-	fountain
-	golang
-	html
-	java
-	markdown
-	matlab
-	objc
-	pascal
-	perl
-	php
-	python
-	ruby
-	rust
-	tex
-	default
+	$(cat builtin-drivers)
 	custom1
 	custom2
 	custom3
-- 
2.31.0.366.ga80606b22c1

[PATCH v4 10/10] blame tests: simplify userdiff driver test

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 01:50:05

Simplify the test added in 9466e3809d (blame: enable funcname blaming
with userdiff driver, 2020-11-01) to use the --author support recently
added in 999cfc4f45 (test-lib functions: add --author support to
test_commit, 2021-01-12).

We also did not need the full fortran-external-function content. Let's
cut it down to just the important parts.

I'm modifying it to demonstrate that the fortran-specific userdiff
function is in effect by adding "DO NOT MATCH ..." and "AS THE ..."
lines surrounding the "RIGHT" one.

This is to check that we're using the userdiff "fortran" driver, as
opposed to the default driver which would match on those lines as part
of the general heuristic of matching a line that doesn't begin with
whitespace.

The test had also been leaving behind a .gitattributes file for later
tests to possibly trip over, let's clean it up with
"test_when_finished".

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 t/annotate-tests.sh | 36 +++++++++++++++---------------------
 1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 04a2c58594c..d3b299e75cb 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
 	check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
 '
 
-test_expect_success 'setup -L :funcname with userdiff driver' '
-	echo "fortran-* diff=fortran" >.gitattributes &&
-	fortran_file=fortran-external-function &&
-	cat >$fortran_file <<-\EOF &&
+test_expect_success 'blame -L :funcname with userdiff driver' '
+	cat >file.template <<-\EOF &&
+	DO NOT MATCH THIS LINE
 	function RIGHT(a, b) result(c)
+	AS THE DEFAULT DRIVER WOULD
 
 	integer, intent(in) :: ChangeMe
-	integer, intent(in) :: b
-	integer, intent(out) :: c
-
-	c = a+b
-
-	end function RIGHT
 	EOF
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
-	git commit -m "add fortran file" &&
-	sed -e "s/ChangeMe/IWasChanged/" <"$fortran_file" >"$fortran_file".tmp &&
-	mv "$fortran_file".tmp "$fortran_file" &&
-	git add "$fortran_file" &&
-	GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
-	git commit -m "change fortran file"
-'
 
-test_expect_success 'blame -L :funcname with userdiff driver' '
-	check_count -f fortran-external-function -L:RIGHT A 7 B 1
+	fortran_file=file.f03 &&
+	test_when_finished "rm .gitattributes" &&
+	echo "$fortran_file diff=fortran" >.gitattributes &&
+
+	test_commit --author "A <A@test.git>" \
+		"add" "$fortran_file" \
+		"$(cat file.template)" &&
+	test_commit --author "B <B@test.git>" \
+		"change" "$fortran_file" \
+		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+	check_count -f "$fortran_file" -L:RIGHT A 3 B 1
 '
 
 test_expect_success 'setup incremental' '
-- 
2.31.0.366.ga80606b22c1

Re: [PATCH v4 00/10] userdiff: refactor + test improvements

From: Johannes Sixt <hidden>
Date: 2021-03-24 18:09:05

Am 24.03.21 um 02:48 schrieb Ævar Arnfjörð Bjarmason:
This is a restart of the 35-patch v3 of this topic at
https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/

I still plan on submitting the rest of it, but wanted to start with
the early parts of that series that hasn't been controversial or has
outstanding feedback I haven't addressed.

The range-diff to v3 is just for those patches I'm re-rolling here.
I'm mostly relying on the interdiff below. I think I had no comments on
these patches in the earlier round, so:

Acked-by: Johannes Sixt <redacted>
Ævar Arnfjörð Bjarmason (10):
  userdiff: refactor away the parse_bool() function
  userdiff style: re-order drivers in alphabetical order
  userdiff style: declare patterns with consistent style
  userdiff style: normalize pascal regex declaration
  userdiff: add and use for_each_userdiff_driver()
  userdiff tests: explicitly test "default" pattern
  userdiff tests: list builtin drivers via test-tool
  userdiff: remove support for "broken" tests
  blame tests: don't rely on t/t4018/ directory
  blame tests: simplify userdiff driver test

 Makefile                 |   1 +
 t/annotate-tests.sh      |  34 ++++----
 t/helper/test-tool.c     |   1 +
 t/helper/test-tool.h     |   1 +
 t/helper/test-userdiff.c |  31 +++++++
 t/t4018-diff-funcname.sh |  39 ++-------
 t/t4018/README           |   3 -
 userdiff.c               | 178 ++++++++++++++++++++++++---------------
 userdiff.h               |  15 ++++
 9 files changed, 186 insertions(+), 117 deletions(-)
 create mode 100644 t/helper/test-userdiff.c

Range-diff:
 1:  0be132b05e2 =  1:  fb7346cd296 userdiff: refactor away the parse_bool() function
 2:  d1e00a739ac =  2:  149387155bc userdiff style: re-order drivers in alphabetical order
 3:  b99bd158d45 =  3:  faf1a824f05 userdiff style: declare patterns with consistent style
 4:  9ce6d47021c =  4:  1e9ddcd1a9a userdiff style: normalize pascal regex declaration
 5:  369fbdcee83 =  5:  64ea5e8443f userdiff: add and use for_each_userdiff_driver()
 6:  70d62a97211 =  6:  862f6ab5d66 userdiff tests: explicitly test "default" pattern
 7:  792421a2f8b =  7:  22a07591b76 userdiff tests: list builtin drivers via test-tool
 8:  9081e2a152e !  8:  7755db95014 userdiff: remove support for "broken" tests
    @@ Commit message
     
         There have been no "broken" tests since 75c3b6b2e8 (userdiff: improve
         Fortran xfuncname regex, 2020-08-12). Let's remove the test support
    -    for them, this is in preparation for a more general refactoring of the
    -    tests.
    +    for them.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
 9:  d3652f95d5e !  9:  4e0b4b42e16 blame tests: don't rely on t/t4018/ directory
    @@ Commit message
         with userdiff driver, 2020-11-01) so that the blame tests don't rely
         on stealing the contents of "t/t4018/fortran-external-function".
     
    -    I'm about to change that file in a subsequent commit. Just moving the
    -    relevant test file here inline is the easiest solution, and I think
    -    also the most readable.
    +    I have another patch series that'll possibly (or not) refactor that
    +    file, but having this test inter-dependency makes things simple in any
    +    case by making this test more readable.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     
10:  35d12779ea1 ! 10:  ce98c61bf40 blame tests: simplify userdiff driver test
    @@ Commit message
         test_commit, 2021-01-12).
     
         We also did not need the full fortran-external-function content. Let's
    -    cut it down to just the important parts, and further modify it to
    -    demonstrate that the fortran-specific userdiff function is in effect
    -    by adding "DO NOT MATCH ..." and "AS THE ..." lines surrounding the
    -    "RIGHT" one. This is to check that we're using the userdiff "fortran"
    -    driver, as opposed to the default driver.
    +    cut it down to just the important parts.
     
    -    The test also left behind a .gitattributes files, let's clean it up
    -    with "test_when_finished".
    +    I'm modifying it to demonstrate that the fortran-specific userdiff
    +    function is in effect by adding "DO NOT MATCH ..." and "AS THE ..."
    +    lines surrounding the "RIGHT" one.
    +
    +    This is to check that we're using the userdiff "fortran" driver, as
    +    opposed to the default driver which would match on those lines as part
    +    of the general heuristic of matching a line that doesn't begin with
    +    whitespace.
    +
    +    The test had also been leaving behind a .gitattributes file for later
    +    tests to possibly trip over, let's clean it up with
    +    "test_when_finished".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason [off-list ref]
     

Re: [PATCH v4 01/10] userdiff: refactor away the parse_bool() function

From: Jeff King <hidden>
Date: 2021-03-24 18:48:05

On Wed, Mar 24, 2021 at 02:48:43AM +0100, Ævar Arnfjörð Bjarmason wrote:
Since 6680a0874f (drop odd return value semantics from
userdiff_config, 2012-02-07) we have not cared about the return values
of parse_tristate() or git_config_bool() v.s. falling through in
userdiff_config(), so let's do so in those cases to make the code
easier to read.

Having a wrapper function for git_config_bool() dates back to
d9bae1a178 (diff: cache textconv output, 2010-04-01) and
122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of
which predated the change in 6680a0874f which made their return values
redundant.
I think this cleanup makes sense.
quoted hunk
 int userdiff_config(const char *k, const char *v)
@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)
 		return parse_funcname(&drv->funcname, k, v, 0);
 	if (!strcmp(type, "xfuncname"))
 		return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
-	if (!strcmp(type, "binary"))
-		return parse_tristate(&drv->binary, k, v);
 	if (!strcmp(type, "command"))
 		return git_config_string(&drv->external, k, v);
 	if (!strcmp(type, "textconv"))
 		return git_config_string(&drv->textconv, k, v);
-	if (!strcmp(type, "cachetextconv"))
-		return parse_bool(&drv->textconv_want_cache, k, v);
 	if (!strcmp(type, "wordregex"))
 		return git_config_string(&drv->word_regex, k, v);
+	/* Don't care about the parse errors for these, fallthrough */
+	if (!strcmp(type, "cachetextconv"))
+		drv->textconv_want_cache = git_config_bool(k, v);
+	if (!strcmp(type, "binary"))
+		parse_tristate(&drv->binary, k, v);
The original returned early, which short-circuited the rest of the
strcmp(). that probably doesn't matter much in practice (after all, an
unrecognized value is inherently O(n)). But perhaps:

  if (...)
     assign...;
  else if (...)
     assign...;

would make the comment unnecessary. I don't feel strongly either way,
though.

-Peff

Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Jeff King <hidden>
Date: 2021-03-24 19:13:31

On Wed, Mar 24, 2021 at 02:48:47AM +0100, Ævar Arnfjörð Bjarmason wrote:
Refactor the userdiff_find_by_namelen() function so that a new
for_each_userdiff_driver() API function does most of the work.

This will be useful for the same reason we've got other for_each_*()
API functions as part of various APIs, and will be used in a follow-up
commit.
The refactorings up to here all made sense, but TBH this one makes the
code more confusing to follow to me.

Perhaps part of it is just that the diff is messy, but I had to read it
several times to understand what's going on. Here's what I think were
the tricky parts:
-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+struct for_each_userdiff_driver_cb {
+	const char *k;
+	size_t len;
+	struct userdiff_driver *driver;
+};
Our callback function does _one_ type of selection (based on a "type"
parameter), but not another (based on the name). That feels
inconsistent, but is also the reason we have this awkward struct.  Part
of my confusion is the name: this is not something to be generically
used with for_each_userdiff_driver(), but rather a type unique to
find_by_namelen() to be passed through the opaque void pointer.

So "struct find_by_namelen_data" would have been a lot more
enlightening.

The fact that callbacks are awkward in general in C might not be
solvable, at least not without duplicating some iteration code.
+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
+				       enum userdiff_driver_type type, void *priv)
 {
[...]
+	if (!strncmp(driver->name, cb_data->k, cb_data->len) &&
+	    !driver->name[cb_data->len]) {
+		cb_data->driver = driver;
+		return -1; /* found it! */
 	}
This "return -1" took me a while to grok, and the comment didn't help
all that much. The point is to stop traversing the list, but "-1" to me
signals error. I think returning "1" might be a bit more idiomatic, but
also a comment that says "tell the caller to stop iterating" would have
been more clear.
+int for_each_userdiff_driver(each_userdiff_driver_fn fn,
+			     enum userdiff_driver_type type, void *cb_data)
+{
+	int i, ret;
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {
+
+		for (i = 0; i < ndrivers; i++) {
+			struct userdiff_driver *drv = drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {
+
+		for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+			struct userdiff_driver *drv = builtin_drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+}
I spent a while scratching my head at these types, and what they would
be used for, since this caller doesn't introduce any. Looking at patch 7
helped, though it's unclear to me why we need to distinguish between
custom and builtin drivers there. As you note there, nobody calls
list-custom-drivers nor list-drivers. And if we haven't configured
anything, then wouldn't list-drivers be the same as list-builtin-drivers?
Or for the purposes of that test, if we _did_ configure something,

  As an aside, it feels like this is something we ought to be able to
  ask git-config about, rather than having a test-helper. This is
  basically "baked-in" config, and if we represented it as such, and
  parsed it into a struct just like regular config, then probably "git
  config --list --source" could be used to find it (and differentiate it
  from user-provided config). Possible downsides:

    1. Would people find it confusing that "git config --list" suddenly
       gets way bigger? Maybe we'd want an "--include-baked-in" option
       or something.

    2. Is the cost of parsing the config measurably bad? Obviously a
       user could provide the same content and we'd have to parse it,
       but there's a lot more rules here than most users would probably
       provide.
+enum userdiff_driver_type {
+	USERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,
+	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,
+	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,
+};
I was confused by these being bits, because some of them seem mutually
exclusive (e.g., UNSPECIFIED and anything else).

Perhaps it would make more sense as:

  USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,
  USERDIFF_DRIVER_TYPE_CUSTOM = 1<<0,
  USERDIFF_DRIVER_TYPE_ALL = USERDIFF_DRIVER_TYPE_BUILTIN | USERDIFF_DRIVER_TYPE_CUSTOM

Or the one caller who wants "ALL" could even do the OR themselves.

I do kind of wonder if there's much value in having a single function
with a type field at all, though, given that there's no overlap in the
implementation. Would separate "for_each_custom" and "for_each_builtin"
functions make sense? And then the existing caller would just call them
sequentially.

I dunno. I know a lot of this is nit-picking, and I don't think there's
anything incorrect in this patch. I just found it surprisingly hard to
read for something that purports to be refactoring / cleaning the code.

-Peff

Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-24 23:05:56

On Wed, Mar 24 2021, Jeff King wrote:
On Wed, Mar 24, 2021 at 02:48:47AM +0100, Ævar Arnfjörð Bjarmason wrote:
quoted
Refactor the userdiff_find_by_namelen() function so that a new
for_each_userdiff_driver() API function does most of the work.

This will be useful for the same reason we've got other for_each_*()
API functions as part of various APIs, and will be used in a follow-up
commit.
The refactorings up to here all made sense, but TBH this one makes the
code more confusing to follow to me.

Perhaps part of it is just that the diff is messy, but I had to read it
several times to understand what's going on. Here's what I think were
the tricky parts:
quoted
-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
+struct for_each_userdiff_driver_cb {
+	const char *k;
+	size_t len;
+	struct userdiff_driver *driver;
+};
Our callback function does _one_ type of selection (based on a "type"
parameter), but not another (based on the name). That feels
inconsistent, but is also the reason we have this awkward struct.  Part
of my confusion is the name: this is not something to be generically
used with for_each_userdiff_driver(), but rather a type unique to
find_by_namelen() to be passed through the opaque void pointer.

So "struct find_by_namelen_data" would have been a lot more
enlightening.

The fact that callbacks are awkward in general in C might not be
solvable, at least not without duplicating some iteration code.
quoted
+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
+				       enum userdiff_driver_type type, void *priv)
 {
[...]
+	if (!strncmp(driver->name, cb_data->k, cb_data->len) &&
+	    !driver->name[cb_data->len]) {
+		cb_data->driver = driver;
+		return -1; /* found it! */
 	}
This "return -1" took me a while to grok, and the comment didn't help
all that much. The point is to stop traversing the list, but "-1" to me
signals error. I think returning "1" might be a bit more idiomatic, but
also a comment that says "tell the caller to stop iterating" would have
been more clear.
*nod*

Also thanks for all the reviewing so far both, I'm not replying to all
of it point-by-point here, will respond with a re-roll at some point.
quoted
+int for_each_userdiff_driver(each_userdiff_driver_fn fn,
+			     enum userdiff_driver_type type, void *cb_data)
+{
+	int i, ret;
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {
+
+		for (i = 0; i < ndrivers; i++) {
+			struct userdiff_driver *drv = drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {
+
+		for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+			struct userdiff_driver *drv = builtin_drivers + i;
+			ret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+}
I spent a while scratching my head at these types, and what they would
be used for, since this caller doesn't introduce any. Looking at patch 7
helped, though it's unclear to me why we need to distinguish between
custom and builtin drivers there. As you note there, nobody calls
list-custom-drivers nor list-drivers. And if we haven't configured
anything, then wouldn't list-drivers be the same as list-builtin-drivers?
Or for the purposes of that test, if we _did_ configure something,

  As an aside, it feels like this is something we ought to be able to
  ask git-config about, rather than having a test-helper. This is
  basically "baked-in" config, and if we represented it as such, and
  parsed it into a struct just like regular config, then probably "git
  config --list --source" could be used to find it (and differentiate it
  from user-provided config). Possible downsides:

    1. Would people find it confusing that "git config --list" suddenly
       gets way bigger? Maybe we'd want an "--include-baked-in" option
       or something.

    2. Is the cost of parsing the config measurably bad? Obviously a
       user could provide the same content and we'd have to parse it,
       but there's a lot more rules here than most users would probably
       provide.
Also:

 3. Only the PATTERNS() macro translates as-is to config syntax. We
    don't have a way to do what IPATTERN() does in the config syntax
    currently.

    We could add a ifuncname and xifuncname or whatever for it I guess,
    but currently the ICASE behavior in the C code is magic.
quoted
+enum userdiff_driver_type {
+	USERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,
+	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,
+	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,
+};
I was confused by these being bits, because some of them seem mutually
exclusive (e.g., UNSPECIFIED and anything else).

Perhaps it would make more sense as:

  USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,
  USERDIFF_DRIVER_TYPE_CUSTOM = 1<<0,
  USERDIFF_DRIVER_TYPE_ALL = USERDIFF_DRIVER_TYPE_BUILTIN | USERDIFF_DRIVER_TYPE_CUSTOM

Or the one caller who wants "ALL" could even do the OR themselves.

I do kind of wonder if there's much value in having a single function
with a type field at all, though, given that there's no overlap in the
implementation. Would separate "for_each_custom" and "for_each_builtin"
functions make sense? And then the existing caller would just call them
sequentially.

I dunno. I know a lot of this is nit-picking, and I don't think there's
anything incorrect in this patch. I just found it surprisingly hard to
read for something that purports to be refactoring / cleaning the code.

-Peff

Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Jeff King <hidden>
Date: 2021-03-25 00:34:39

On Thu, Mar 25, 2021 at 12:05:09AM +0100, Ævar Arnfjörð Bjarmason wrote:
quoted
  As an aside, it feels like this is something we ought to be able to
  ask git-config about, rather than having a test-helper. This is
  basically "baked-in" config, and if we represented it as such, and
  parsed it into a struct just like regular config, then probably "git
  config --list --source" could be used to find it (and differentiate it
  from user-provided config). Possible downsides:

    1. Would people find it confusing that "git config --list" suddenly
       gets way bigger? Maybe we'd want an "--include-baked-in" option
       or something.

    2. Is the cost of parsing the config measurably bad? Obviously a
       user could provide the same content and we'd have to parse it,
       but there's a lot more rules here than most users would probably
       provide.
Also:

 3. Only the PATTERNS() macro translates as-is to config syntax. We
    don't have a way to do what IPATTERN() does in the config syntax
    currently.

    We could add a ifuncname and xifuncname or whatever for it I guess,
    but currently the ICASE behavior in the C code is magic.
Good point. IMHO that is something we should consider fixing
independently. It was a mistake to add builtins that couldn't be
replicated via the config (though I notice it happened quite a while
ago, and nobody seems to have cared, so perhaps it isn't that
important).

I have also wondered if we should just ship a file which could be
installed as /etc/gitconfig.filetypes and include it the stock
/etc/gitconfig. That is effectively the same as "baked in", but
hopefully makes it more clear to users how they can modify things.  But
all of that is somewhat orthogonal to what you're doing here.

-Peff

Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-03-25 01:27:06

On Thu, Mar 25 2021, Jeff King wrote:
On Thu, Mar 25, 2021 at 12:05:09AM +0100, Ævar Arnfjörð Bjarmason wrote:
quoted
quoted
  As an aside, it feels like this is something we ought to be able to
  ask git-config about, rather than having a test-helper. This is
  basically "baked-in" config, and if we represented it as such, and
  parsed it into a struct just like regular config, then probably "git
  config --list --source" could be used to find it (and differentiate it
  from user-provided config). Possible downsides:

    1. Would people find it confusing that "git config --list" suddenly
       gets way bigger? Maybe we'd want an "--include-baked-in" option
       or something.

    2. Is the cost of parsing the config measurably bad? Obviously a
       user could provide the same content and we'd have to parse it,
       but there's a lot more rules here than most users would probably
       provide.
Also:

 3. Only the PATTERNS() macro translates as-is to config syntax. We
    don't have a way to do what IPATTERN() does in the config syntax
    currently.

    We could add a ifuncname and xifuncname or whatever for it I guess,
    but currently the ICASE behavior in the C code is magic.
Good point. IMHO that is something we should consider fixing
independently. It was a mistake to add builtins that couldn't be
replicated via the config (though I notice it happened quite a while
ago, and nobody seems to have cared, so perhaps it isn't that
important).
I'm conspiring to eventually optimistically replace these ERE patterns
with PCRE if we build with that at some point.

Then you could just prefix your pattern with (?i) here in this and other
things that want icase...
I have also wondered if we should just ship a file which could be
installed as /etc/gitconfig.filetypes and include it the stock
/etc/gitconfig. That is effectively the same as "baked in", but
hopefully makes it more clear to users how they can modify things.  But
all of that is somewhat orthogonal to what you're doing here.
In theory I'm with you on that, in practice this is just the sort of
thing that requires opt-in effort from every person packaging git
(installing system-wide-config).

A good number of those people will decide "default config values? In
some ~200 line file included in /etc/gitconfig?? I don't need that! This
is a minimal install!".

And then their web UI that calls "git diff" under the hood won't do the
right thing with a .gitattributes config that expects these built-in
drivers anymore.

But yeah, there's no reason your earlier suggestion of injecting global
defaults into "git config -l" wouldn't work, and for our own C APIs such
as this one to rely on that happening.

It would even make git more discoverable, as all the "this is set to xyz
by default" docs in git-config(1) could become reflective, you could
just run "git config -l --show-origin | grep ^default:" or something to
see all default values.

Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()

From: Jeff King <hidden>
Date: 2021-03-26 03:28:57

On Thu, Mar 25, 2021 at 02:26:21AM +0100, Ævar Arnfjörð Bjarmason wrote:
quoted
quoted
    We could add a ifuncname and xifuncname or whatever for it I guess,
    but currently the ICASE behavior in the C code is magic.
Good point. IMHO that is something we should consider fixing
independently. It was a mistake to add builtins that couldn't be
replicated via the config (though I notice it happened quite a while
ago, and nobody seems to have cared, so perhaps it isn't that
important).
I'm conspiring to eventually optimistically replace these ERE patterns
with PCRE if we build with that at some point.

Then you could just prefix your pattern with (?i) here in this and other
things that want icase...
I really like PCRE myself, but is it portable/common enough for us to
start using it for baked-in funcname patterns? I sort of assumed there
were exotic platforms where libpcre wouldn't build (or at least it would
be inconvenient or uncommon to have it). And it would be nice to degrade
those gracefully (or at least better than "I guess you don't get any
builtin funcnames. Tough luck").
quoted
I have also wondered if we should just ship a file which could be
installed as /etc/gitconfig.filetypes and include it the stock
/etc/gitconfig. That is effectively the same as "baked in", but
hopefully makes it more clear to users how they can modify things.  But
all of that is somewhat orthogonal to what you're doing here.
In theory I'm with you on that, in practice this is just the sort of
thing that requires opt-in effort from every person packaging git
(installing system-wide-config).
I assumed that we'd install it with "make install", and packaging would
pick it up from there. But you're right that it is likely to create extra
headaches.
But yeah, there's no reason your earlier suggestion of injecting global
defaults into "git config -l" wouldn't work, and for our own C APIs such
as this one to rely on that happening.

It would even make git more discoverable, as all the "this is set to xyz
by default" docs in git-config(1) could become reflective, you could
just run "git config -l --show-origin | grep ^default:" or something to
see all default values.
Yeah, exactly. The discoverability is the real value IMHO. I just worry
about overwhelming the user who runs "git config" with --show-origin. I
guess it could avoid showing baked-in config unless an extra option is
given, but then that makes discoverability slightly harder (though still
better than it is today).

I dunno. This is mostly orthogonal to your patch series, so I don't mind
at all punting on it for now. Though if we _did_ do the baked-in config
then, then a lot of this userdiff code would want to be converted to it.

-Peff

[PATCH v5 1/9] userdiff style: re-order drivers in alphabetical order

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-04-08 15:04:39

Address some old code smell and move around the built-in userdiff
drivers so they're both in alphabetical order, and now in the same
order they appear in the gitattributes(5) documentation.

The two started drifting in be58e70dba (diff: unify external diff and
funcname parsing code, 2008-10-05), and then even further in
80c49c3de2 (color-words: make regex configurable via attributes,
2009-01-17) when the "cpp" pattern was added.

There are no functional changes here, and as --color-moved will show
only moved existing lines.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 76 +++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c..650f421d63 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,6 +44,44 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 "[={}\"]|[^={}\" \t]+"),
+PATTERNS("cpp",
+	 /* Jump targets or access declarations */
+	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
+	 /* functions/methods, variables, and compounds at top level */
+	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+PATTERNS("csharp",
+	 /* Keywords */
+	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+	 /* Methods and constructors */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+	 /* Properties */
+	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /* Type definitions */
+	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+	 /* Namespace */
+	 "^[ \t]*(namespace[ \t]+.*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+IPATTERN("css",
+	 "![:;][[:space:]]*$\n"
+	 "^[:[@.#]?[_a-z0-9].*$",
+	 /* -- */
+	 /*
+	  * This regex comes from W3C CSS specs. Should theoretically also
+	  * allow ISO 10646 characters U+00A0 and higher,
+	  * but they are not handled in this regex.
+	  */
+	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
+	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
+),
 PATTERNS("dts",
 	 "!;\n"
 	 "!=\n"
@@ -191,46 +229,8 @@ PATTERNS("rust",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
 	 "|[-+*\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\|\\||->|=>|\\.{2}=|\\.{3}|::"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 	 "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-PATTERNS("cpp",
-	 /* Jump targets or access declarations */
-	 "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
-	 /* functions/methods, variables, and compounds at top level */
-	 "^((::[[:space:]]*)?[A-Za-z_].*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
-PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
-	 /* Type definitions */
-	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
-	 /* Namespace */
-	 "^[ \t]*(namespace[ \t]+.*)$",
-	 /* -- */
-	 "[a-zA-Z_][a-zA-Z0-9_]*"
-	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-IPATTERN("css",
-	 "![:;][[:space:]]*$\n"
-	 "^[:[@.#]?[_a-z0-9].*$",
-	 /* -- */
-	 /*
-	  * This regex comes from W3C CSS specs. Should theoretically also
-	  * allow ISO 10646 characters U+00A0 and higher,
-	  * but they are not handled in this regex.
-	  */
-	 "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */
-	 "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */
-),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
-- 
2.31.1.527.g9b8f7de2547

[PATCH v5 2/9] userdiff style: declare patterns with consistent style

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-04-08 15:04:41

Change those patterns which were declared with a regex on the same
line as the "PATTERNS()" line to put that regex on the next line, and
add missing "/* -- */" separator comments between the pattern and
word_regex.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 650f421d63..33b0ce4020 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -44,7 +44,9 @@ PATTERNS("bash",
 	 /* -- */
 	 /* Characters not in the default $IFS value */
 	 "[^ \t]+"),
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+PATTERNS("bibtex",
+	 "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+	 /* -- */
 	 "[={}\"]|[^={}\" \t]+"),
 PATTERNS("cpp",
 	 /* Jump targets or access declarations */
@@ -121,7 +123,9 @@ IPATTERN("fortran",
 	  * they would have been matched above as a variable anyway. */
 	 "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
 	 "|//|\\*\\*|::|[/<>=]="),
-IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+IPATTERN("fountain",
+	 "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
+	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
 	 /* Functions */
@@ -132,7 +136,9 @@ PATTERNS("golang",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?"
 	 "|[-+*/<>%&^|=!:]=|--|\\+\\+|<<=?|>>=?|&\\^=?|&&|\\|\\||<-|\\.{3}"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+PATTERNS("html",
+	 "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("java",
 	 "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
@@ -144,6 +150,7 @@ PATTERNS("java",
 	 "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
 PATTERNS("markdown",
 	 "^ {0,3}#{1,6}[ \t].*",
+	 /* -- */
 	 "[^<>= \t]+"),
 PATTERNS("matlab",
 	 /*
@@ -152,6 +159,7 @@ PATTERNS("matlab",
 	  * that is understood by both.
 	  */
 	 "^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$",
+	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
 PATTERNS("objc",
 	 /* Negate C statements that can look like functions */
@@ -212,13 +220,15 @@ PATTERNS("php",
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
 	 "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"),
-PATTERNS("python", "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
+PATTERNS("python",
+	 "^[ \t]*((class|(async[ \t]+)?def)[ \t].*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
 	 /* -- */
-PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+PATTERNS("ruby",
+	 "^[ \t]*((class|module|def)[ \t].*)$",
 	 /* -- */
 	 "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
 	 "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
-- 
2.31.1.527.g9b8f7de2547

[PATCH v5 0/9] userdiff: refactor + test improvements

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-04-08 15:04:42

As noted in v3[1] this is the start of some wider userdiff test
improvements.

Since v3 I've ejected the parse_bool() change at the start of the
series. I split that into another series[2]:

This one applies on master, and doesn't conflict with the tiny
unrelated change in userdiff.c done in that series either textually or
semantically.

Changes since v3:

 * Jeff and Junio suggested refactoring the userdiff.c callback
   facility. I've done that using some union of their suggetions, I
   think it's easier to read this time around.

   Basically, the "for each userdiff" callback doesn't take a "I want
   this type", that's now handled in the user callback, which
   simplifies things a lot.

 * Other minor changes related to the above, e.g. renaming the CB
   struct per Jeff's suggestion.

 * I found that the "list-custom-drivers" I'd added for completeness
   to the helper didn't work, I needed to setup the .git dir and
   config in the helper. That's now done, and the tests check that the
   helper does the right thing.

1. https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/
2. https://lore.kernel.org/git/cover-0.6-0000000000-20210408T133125Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (9):
  userdiff style: re-order drivers in alphabetical order
  userdiff style: declare patterns with consistent style
  userdiff style: normalize pascal regex declaration
  userdiff: add and use for_each_userdiff_driver()
  userdiff tests: explicitly test "default" pattern
  userdiff tests: list builtin drivers via test-tool
  userdiff: remove support for "broken" tests
  blame tests: don't rely on t/t4018/ directory
  blame tests: simplify userdiff driver test

 Makefile                 |   1 +
 t/annotate-tests.sh      |  34 ++++----
 t/helper/test-tool.c     |   1 +
 t/helper/test-tool.h     |   1 +
 t/helper/test-userdiff.c |  46 +++++++++++
 t/t4018-diff-funcname.sh |  53 +++++-------
 t/t4018/README           |   3 -
 userdiff.c               | 169 ++++++++++++++++++++++++++-------------
 userdiff.h               |  13 +++
 9 files changed, 213 insertions(+), 108 deletions(-)
 create mode 100644 t/helper/test-userdiff.c

Range-diff:
 1:  fb7346cd29 <  -:  ---------- userdiff: refactor away the parse_bool() function
 2:  149387155b =  1:  2f7a7b2897 userdiff style: re-order drivers in alphabetical order
 3:  faf1a824f0 =  2:  22e48f33b6 userdiff style: declare patterns with consistent style
 4:  1e9ddcd1a9 =  3:  dff2aa0d38 userdiff style: normalize pascal regex declaration
 5:  64ea5e8443 !  4:  1a3a61e389 userdiff: add and use for_each_userdiff_driver()
    @@ userdiff.c: static struct userdiff_driver driver_false = {
      };
      
     -static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
    -+struct for_each_userdiff_driver_cb {
    -+	const char *k;
    ++struct find_by_namelen_data {
    ++	const char *name;
     +	size_t len;
     +	struct userdiff_driver *driver;
     +};
    @@ userdiff.c: static struct userdiff_driver driver_false = {
     -		struct userdiff_driver *drv = builtin_drivers + i;
     -		if (!strncmp(drv->name, k, len) && !drv->name[len])
     -			return drv;
    -+	struct for_each_userdiff_driver_cb *cb_data = priv;
    ++	struct find_by_namelen_data *cb_data = priv;
     +
    -+	if (!strncmp(driver->name, cb_data->k, cb_data->len) &&
    ++	if (!strncmp(driver->name, cb_data->name, cb_data->len) &&
     +	    !driver->name[cb_data->len]) {
     +		cb_data->driver = driver;
    -+		return -1; /* found it! */
    ++		return 1; /* tell the caller to stop iterating */
      	}
     -	return NULL;
     +	return 0;
     +}
     +
    -+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)
    ++static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)
     +{
    -+	struct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };
    -+
    -+	for_each_userdiff_driver(userdiff_find_by_namelen_cb,
    -+				 USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);
    ++	struct find_by_namelen_data udcbdata = {
    ++		.name = name,
    ++		.len = len,
    ++	};
    ++	for_each_userdiff_driver(userdiff_find_by_namelen_cb, &udcbdata);
     +	return udcbdata.driver;
      }
      
    @@ userdiff.c: struct userdiff_driver *userdiff_get_textconv(struct repository *r,
      	return driver;
      }
     +
    -+int for_each_userdiff_driver(each_userdiff_driver_fn fn,
    -+			     enum userdiff_driver_type type, void *cb_data)
    ++static int for_each_userdiff_driver_list(each_userdiff_driver_fn fn,
    ++					 enum userdiff_driver_type type, void *cb_data,
    ++					 struct userdiff_driver *drv,
    ++					 int drv_size)
     +{
    -+	int i, ret;
    -+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {
    -+
    -+		for (i = 0; i < ndrivers; i++) {
    -+			struct userdiff_driver *drv = drivers + i;
    -+			ret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);
    -+			if (ret)
    -+				return ret;
    -+		}
    ++	int i;
    ++	int ret;
    ++	for (i = 0; i < drv_size; i++) {
    ++		struct userdiff_driver *item = drv + i;
    ++		if ((ret = fn(item, type, cb_data)))
    ++			return ret;
     +	}
    -+	if (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {
    ++	return 0;
    ++}
    ++
    ++int for_each_userdiff_driver(each_userdiff_driver_fn fn, void *cb_data)
    ++{
    ++	int ret;
    ++
    ++	ret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_CUSTOM,
    ++					    cb_data, drivers, ndrivers);
    ++	if (ret)
    ++		return ret;
    ++
    ++	ret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_BUILTIN,
    ++					    cb_data, builtin_drivers,
    ++					    ARRAY_SIZE(builtin_drivers));
    ++	if (ret)
    ++		return ret;
     +
    -+		for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
    -+			struct userdiff_driver *drv = builtin_drivers + i;
    -+			ret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);
    -+			if (ret)
    -+				return ret;
    -+		}
    -+	}
     +	return 0;
     +}
     
    @@ userdiff.h: struct userdiff_driver {
      	int textconv_want_cache;
      };
     +enum userdiff_driver_type {
    -+	USERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,
    -+	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,
    -+	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,
    ++	USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,
    ++	USERDIFF_DRIVER_TYPE_CUSTOM = 1<<1,
     +};
     +typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,
     +				       enum userdiff_driver_type, void *);
    @@ userdiff.h: struct userdiff_driver *userdiff_find_by_path(struct index_state *is
      					      struct userdiff_driver *driver);
      
     +/*
    -+ * Iterate over each driver of type userdiff_driver_type, or
    -+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero
    -+ * to exit from the loop.
    ++ * Iterate over all userdiff drivers. The userdiff_driver_type
    ++ * argument to each_userdiff_driver_fn indicates their type. Return
    ++ * non-zero to exit early from the loop.
     + */
    -+int for_each_userdiff_driver(each_userdiff_driver_fn,
    -+			     enum userdiff_driver_type, void *);
    ++int for_each_userdiff_driver(each_userdiff_driver_fn, void *);
     +
      #endif /* USERDIFF */
 6:  862f6ab5d6 =  5:  3eb7abd121 userdiff tests: explicitly test "default" pattern
 7:  22a07591b7 !  6:  e90758a978 userdiff tests: list builtin drivers via test-tool
    @@ t/helper/test-userdiff.c (new)
     +#include "test-tool.h"
     +#include "cache.h"
     +#include "userdiff.h"
    ++#include "config.h"
     +
     +static int driver_cb(struct userdiff_driver *driver,
     +		     enum userdiff_driver_type type, void *priv)
     +{
    -+	if (driver->funcname.pattern)
    ++	enum userdiff_driver_type *want_type = priv;
    ++	if (type & *want_type && driver->funcname.pattern)
     +		puts(driver->name);
     +	return 0;
     +}
     +
    -+static int list_what(enum userdiff_driver_type type)
    ++static int cmd__userdiff_config(const char *var, const char *value, void *cb)
     +{
    -+	return for_each_userdiff_driver(driver_cb, type, NULL);
    ++	if (userdiff_config(var, value) < 0)
    ++		return -1;
    ++	return 0;
     +}
     +
     +int cmd__userdiff(int argc, const char **argv)
     +{
    ++	enum userdiff_driver_type want = 0;
     +	if (argc != 2)
     +		return 1;
     +
     +	if (!strcmp(argv[1], "list-drivers"))
    -+		return list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);
    ++		want = (USERDIFF_DRIVER_TYPE_BUILTIN |
    ++			USERDIFF_DRIVER_TYPE_CUSTOM);
     +	else if (!strcmp(argv[1], "list-builtin-drivers"))
    -+		return list_what(USERDIFF_DRIVER_TYPE_BUILTIN);
    ++		want = USERDIFF_DRIVER_TYPE_BUILTIN;
     +	else if (!strcmp(argv[1], "list-custom-drivers"))
    -+		return list_what(USERDIFF_DRIVER_TYPE_CUSTOM);
    ++		want = USERDIFF_DRIVER_TYPE_CUSTOM;
     +	else
     +		return error("unknown argument %s", argv[1]);
    ++
    ++	if (want & USERDIFF_DRIVER_TYPE_CUSTOM) {
    ++		setup_git_directory();
    ++		git_config(cmd__userdiff_config, NULL);
    ++	}
    ++
    ++	for_each_userdiff_driver(driver_cb, &want);
    ++
    ++	return 0;
     +}
     
      ## t/t4018-diff-funcname.sh ##
    -@@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patterns'
    - . ./test-lib.sh
    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
    + 	echo B >B.java
    + '
      
    - test_expect_success 'setup' '
    ++test_expect_success 'setup: test-tool userdiff' '
     +	# Make sure additions to builtin_drivers are sorted
     +	test_when_finished "rm builtin-drivers.sorted" &&
     +	test-tool userdiff list-builtin-drivers >builtin-drivers &&
    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte
     +	sort <builtin-drivers >builtin-drivers.sorted &&
     +	test_cmp builtin-drivers.sorted builtin-drivers &&
     +
    - 	# a non-trivial custom pattern
    - 	git config diff.custom1.funcname "!static
    - !String
    -@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
    - '
    - 
    ++	# Ditto, but "custom" requires the .git directory and config
    ++	# to be setup and read.
    ++	test_when_finished "rm custom-drivers.sorted" &&
    ++	test-tool userdiff list-custom-drivers >custom-drivers &&
    ++	test_file_not_empty custom-drivers &&
    ++	sort <custom-drivers >custom-drivers.sorted &&
    ++	test_cmp custom-drivers.sorted custom-drivers
    ++'
    ++
      diffpatterns="
     -	ada
     -	bash
    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '
     -	rust
     -	tex
     -	default
    +-	custom1
    +-	custom2
    +-	custom3
     +	$(cat builtin-drivers)
    - 	custom1
    - 	custom2
    - 	custom3
    ++	$(cat custom-drivers)
    + "
    + 
    + for p in $diffpatterns
 8:  7755db9501 =  7:  04bce275ab userdiff: remove support for "broken" tests
 9:  4e0b4b42e1 =  8:  3583078715 blame tests: don't rely on t/t4018/ directory
10:  ce98c61bf4 =  9:  548673260b blame tests: simplify userdiff driver test
-- 
2.31.1.527.g9b8f7de2547

[PATCH v5 3/9] userdiff style: normalize pascal regex declaration

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-04-08 15:04:43

Declare the pascal pattern consistently with how we declare the
others, not having "\n" on one line by itself, but as part of the
pattern, and when there are alterations have the "|" at the start, not
end of the line.

Signed-off-by: Ævar Arnfjörð Bjarmason <redacted>
---
 userdiff.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/userdiff.c b/userdiff.c
index 33b0ce4020..978ae64155 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -175,9 +175,8 @@ PATTERNS("objc",
 	 "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("pascal",
-	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
-		"implementation|initialization|finalization)[ \t]*.*)$"
-	 "\n"
+	 "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface"
+	 "|implementation|initialization|finalization)[ \t]*.*)$\n"
 	 "^(.*=[ \t]*(class|record).*)$",
 	 /* -- */
 	 "[a-zA-Z_][a-zA-Z0-9_]*"
-- 
2.31.1.527.g9b8f7de2547
Next 8 of 8 remaining

Previous page

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help